bostrat-node 1.10.1 → 1.10.2
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/cli.js +17 -17
- package/dist/index.js +179 -179
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
var
|
|
1
|
+
var vC=Object.defineProperty;var q=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var If=(e,t)=>{for(var n in t)vC(e,n,{get:t[n],enumerable:!0})};import{createHash as SC,randomBytes as AC,timingSafeEqual as TC}from"node:crypto";function mk(){return AC(32).toString("hex")}function Oo(e){let t=typeof e=="string"?e.trim():"";return t?SC("sha256").update(t).digest("hex"):null}function hk(e,t){return typeof e!="string"||typeof t!="string"||e.length!==t.length||!e?!1:TC(Buffer.from(e),Buffer.from(t))}function di(e,t={},{now:n=Date.now(),log:r=console.warn}={}){if(_f.set(e,(_f.get(e)||0)+1),$f.has(e)&&n-$f.get(e)<EC)return!1;$f.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=${_f.get(e)}${o?` ${o}`:""}`),!0}async function wk(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 er,tr,_f,$f,EC,Ns=q(()=>{er="BOSTRAT_HOOK_TOKEN",tr="x-bostrat-hook";_f=new Map,$f=new Map,EC=6e4});import Lt from"node:fs/promises";import gk from"node:os";import Ot from"node:path";import{execFile as IC}from"node:child_process";import{promisify as _C}from"node:util";function Ll(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function $C(){return process.env.CLAUDE_CONFIG_DIR?Ot.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Ot.join(gk.homedir(),".claude.json")}function RC(){return process.env.CLAUDE_CONFIG_DIR?Ot.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Ot.join(gk.homedir(),".claude","settings.json")}async function Cr(e){if(!e||typeof e!="string")return;let t=Ot.resolve(e),n=$C(),r=RC(),o={};try{o=JSON.parse(await Lt.readFile(n,"utf8"))}catch{}o.hasCompletedOnboarding=!0,o.theme||(o.theme="dark"),o.projects=o.projects||{},o.projects[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 Lt.mkdir(Ot.dirname(n),{recursive:!0}),await Lt.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await Lt.readFile(r,"utf8"))}catch{}if(i.skipDangerousModePermissionPrompt=!0,i.remoteControlAtStartup=!1,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 "${tr}: $${er}" --data-binary @- >/dev/null 2>&1 || true`,u=[{hooks:[{type:"command",command:l}]}];i.hooks=i.hooks||{};for(let d of["SessionStart","UserPromptSubmit","Stop"])i.hooks[d]=u;i.hooks.PostToolUse=[{matcher:"*",hooks:[{type:"command",command:l}]}]}await Lt.mkdir(Ot.dirname(r),{recursive:!0}),await Lt.writeFile(r,JSON.stringify(i,null,2)),await OC(t)}async function OC(e){try{let t=Ot.resolve(e),n=Ot.join(t,".bostrat","memory","claude"),r=Ot.join(t,".claude","settings.local.json"),o={};try{o=JSON.parse(await Lt.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(Ot.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await Lt.mkdir(n,{recursive:!0}),await Lt.mkdir(Ot.dirname(r),{recursive:!0}),await Lt.writeFile(r,JSON.stringify(o,null,2)),await MC(t,["/.claude/settings.local.json"])}catch{}}async function MC(e,t){try{let{stdout:n}=await _C(IC)("git",["-C",e,"rev-parse","--git-path","info/exclude"]),r=Ot.resolve(e,n.trim()),o=await Lt.readFile(r,"utf8").catch(()=>""),s=t.filter(a=>!o.split(`
|
|
2
2
|
`).includes(a));if(!s.length)return;await Lt.mkdir(Ot.dirname(r),{recursive:!0}),await Lt.appendFile(r,(o&&!o.endsWith(`
|
|
3
3
|
`)?`
|
|
4
4
|
`:"")+s.join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`)}catch{}}var
|
|
6
|
+
`)}catch{}}var pi=q(()=>{Ns()});import Rf from"node:fs/promises";import CC from"node:os";import Fl from"node:path";function NC(){let e=process.env.CODEX_HOME||Fl.join(CC.homedir(),".codex");return Fl.join(e,"config.toml")}function yk(e){return`[projects."${e}"]`}function PC(e,t){let n=yk(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 Ps(e){if(!e||typeof e!="string")return;let t=Fl.resolve(e),n=NC(),r="";try{r=await Rf.readFile(n,"utf8")}catch{}if(PC(r,t))return;let o=yk(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),d=/trust_level\s*=/.test(u)?u.replace(/trust_level\s*=\s*"[^"]*"/,'trust_level = "trusted"'):`${u.replace(/\s*$/,"")}
|
|
7
7
|
trust_level = "trusted"
|
|
8
8
|
`;r=r.slice(0,a)+d+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 Hl=q(()=>{});import Of from"node:fs/promises";import Nr from"node:path";import{execFile as PC}from"node:child_process";import{promisify as DC}from"node:util";async function HC(e){try{return(await Of.stat(Nr.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function WC(e){let t=i=>Of.readFile(Nr.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 nr(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await HC(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:Nr.basename(o),branch:await WC(o)});return}if(s>=FC)return;let i;try{i=await Of.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||UC.has(c.name)||await t(Nr.join(o,c.name),s+1)}for(let o of LC)await t(Nr.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=await Oe(o.path),a=s?.toLowerCase();if(a){if(n.has(a))continue;n.add(a),o.slug=s}r.push(o)}return r.sort((o,s)=>o.name.localeCompare(s.name))}function qC(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function Oe(e){if(!e)return null;if(Rf.has(e))return Rf.get(e);let t=null;try{let{stdout:n}=await BC("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=qC(n)}catch{t=null}return Rf.set(e,t),t}async function Bs(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await nr())if((await Oe(n.path))?.toLowerCase()===t)return n.path;return null}var BC,yk,jC,LC,FC,UC,Rf,Pr=q(()=>{BC=DC(PC),yk=process.env.HOME||"/home/globo",jC=Nr.join(process.env.BOSTRAT_STATE_DIR||Nr.join(yk,".bostrat"),"repos"),LC=[...(process.env.BOSTRAT_REPOS_ROOT||Nr.join(yk,"Documents")).split(","),jC].map(e=>e.trim()).filter(Boolean),FC=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,UC=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);Rf=new Map});function js(e=Date.now()){if(Co)return{tripped:!1,holdMs:1/0};if(Mo>e)return{tripped:!1,holdMs:Mo-e};if((!mi||e-mi>=Wl.windowMs)&&(mi=e,fi=0),fi++,fi<Wl.trip)return{tripped:!1,holdMs:0};ql++;let t=Math.min(Wl.minMs*2**(ql-1),Wl.maxMs);return Mo=e+t,fi=0,mi=0,{tripped:!0,holdMs:t}}function bk(){fi=0,mi=0,ql=0,Mo=0}function hi(e=Date.now()){return Co?1/0:Mo>e?Mo-e:0}function Ls(e=Date.now()){return hi(e)>0}function Mf(e={}){Co={at:Date.now(),...e},Mo=1/0}function Gl(){return Co}function kk(e=Date.now()){return{held:Ls(e),holdMs:Co?null:hi(e),holds:ql,removed:Co?{...Co}:null}}var GC,Wl,fi,mi,ql,Mo,Co,Cf=q(()=>{GC={trip:3,windowMs:3e5,minMs:3e5,maxMs:216e5},Wl={...GC},fi=0,mi=0,ql=0,Mo=0,Co=null});import KC from"node:fs";import xk from"node:fs/promises";import wi from"node:path";import VC from"node:os";import{fileURLToPath as zC}from"node:url";import{execFile as JC}from"node:child_process";import{promisify as YC}from"node:util";function Ne(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 Kl(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}function Ek(e){return e?.status===404&&/not granted/i.test(String(e?.message||""))}async function Fs(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!Sk.test(n))throw No("repo must be owner/name",400);let r=Nf.get(n);if(r&&r.expiresAt-Date.now()>QC)return r;if(!Kl(t))throw No("node is not plane-linked \u2014 GitHub auth unavailable",503);if(Ls()){if(r&&r.expiresAt>Date.now())return r;throw No(Gl()?"this node was removed from the plane \u2014 GitHub auth unavailable":"plane refused this node's token \u2014 GitHub auth unavailable until it is re-minted",503)}let o;try{o=await Ak(`${Ne(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:Tk(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw No(`plane unreachable \u2014 GitHub auth unavailable (${i?.message||i})`,503)}let s=await o.json().catch(()=>({}));if(!o.ok){if((o.status===401||o.status===403||o.status===410)&&js(),o.status>=500&&r&&r.expiresAt>Date.now())return r;throw No(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 Nf.set(n,a),a}async function gi({env:e=process.env,force:t=!1}={}){if(!Kl(e))return{configured:!1,repos:[],username:null};if(!t&&Dr&&Date.now()-Dr.at<eN)return Dr.data;if(Ls())return Dr?.data??{configured:!0,repos:[],username:null,error:"plane refused this node's token"};try{let n=await Ak(`${Ne(e)}/v1/github/repos`,{headers:Tk(e)});if((n.status===401||n.status===403||n.status===410)&&js(),!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Dr={at:Date.now(),data:r},r}catch(n){return Dr?Dr.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Ik(){Nf.clear(),Dr=null}async function _n({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await Oe(e):null);if(!r)return{};let{token:o}=await Fs(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Pf({env:e=process.env}={}){return(await gi({env:e})).username??null}function oN(e){let[t,n]=String(e).split("/");return wi.join(ZC,t,n)}async function Df(e,{env:t=process.env}={}){let n=(...r)=>vk("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${nN()}" ${rN(t)}`),await n("config","credential.useHttpPath","true")}async function Vl(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!Sk.test(String(e||"")))throw No(`bad repo slug "${e}"`,400);let r=oN(e);if(KC.existsSync(wi.join(r,".git")))return r;let{token:o}=await Fs(e,{env:t});await xk.mkdir(wi.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await vk("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:tN,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await xk.rm(r,{recursive:!0,force:!0}).catch(()=>{}),No(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Df(r,{env:t}),r}var vk,XC,ZC,Sk,QC,eN,tN,Ak,Nf,Dr,Tk,No,nN,rN,yt=q(()=>{Pr();Cf();vk=YC(JC),XC=process.env.BOSTRAT_STATE_DIR||wi.join(process.env.HOME||VC.tmpdir(),".bostrat"),ZC=wi.join(XC,"repos"),Sk=/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?\/[A-Za-z0-9._-]{1,100}$/,QC=5*6e4,eN=6e4,tN=10*6e4,Ak=fetch,Nf=new Map,Dr=null;Tk=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),No=(e,t)=>Object.assign(new Error(e),{status:t});nN=()=>zC(new URL("../bin/bostrat-git-credential",import.meta.url)),rN=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function $k(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Ok(e,t,n,r=_k){let o=Ne(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await aN(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function Mk(e,{env:t=process.env,force:n=!1,timeoutMs:r=_k}={}){if(!$k(t))return{facts:[],source:"unconfigured"};let o=Bf.get(e);if(!n&&o&&Date.now()-o.at<sN)return{facts:o.facts,source:"cache"};try{let s=await Ok(`/v1/memory/${encodeURIComponent(e)}`,{headers:Rk(t)},t,r);if(!s.ok)throw new Error(`plane ${s.status}`);let a=await s.json(),i=Array.isArray(a?.facts)?a.facts:[];return Bf.set(e,{at:Date.now(),facts:i}),{facts:i,source:"plane"}}catch(s){return o?{facts:o.facts,source:"stale",error:s?.message}:{facts:[],source:"unreachable",error:s?.message}}}async function zl(e,t,{env:n=process.env}={}){if(!$k(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await Ok(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...Rk(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 Bf.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 sN,_k,Bf,aN,Rk,Ck=q(()=>{yt();sN=6e4,_k=5e3,Bf=new Map,aN=(...e)=>fetch(...e);Rk=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import jf from"node:fs/promises";import Us from"node:path";import iN from"node:os";import{EventEmitter as cN}from"node:events";import{randomUUID as lN}from"node:crypto";function Hs(e){return e?.cwd?Us.join(e.cwd,".bostrat","events"):Us.join(Lf,"events")}async function fN(e,t){if(!Jl.has(t)){let o=(await G(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);Jl.set(t,o)}let n=Jl.get(t)+1;return Jl.set(t,n),n}function Ff(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 N(e,t){let n=typeof e=="string"?e:e?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!t||!uN.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!dN.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!pN.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?Us.join(Lf,"events"):Hs(e),o=t.epoch;if(o==null&&typeof e=="object"&&t.boxId&&Array.isArray(e?.boxes)){let u=e.boxes.find(d=>d.name===t.boxId);o=Ff(u,Number(t.payload?.ts??t.ts)||Date.now())}let s=await fN(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||lN(),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}:{}},c=JSON.stringify(i),l=i;return Pk(c)&&(c=Dk(c),l=JSON.parse(c)),await jf.mkdir(r,{recursive:!0}),await jf.appendFile(Nk(r,n),c+`
|
|
15
|
-
`),Xe.emit("event",l),Xe.emit(`track:${n}`,l),l}async function G(e,t){let n,r;typeof e=="string"&&t?(n=e,r=t):(r=typeof e=="string"?e:e?.id,n=typeof e=="string"?Us.join(
|
|
16
|
-
`))if(a.trim())try{s.push(JSON.parse(
|
|
13
|
+
`:"")}try{await Rf.mkdir(Fl.dirname(n),{recursive:!0}),await Rf.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var Ul=q(()=>{});import bk from"node:fs/promises";import DC from"node:os";import fi from"node:path";function BC(e){let n=fi.resolve(e).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||fi.join(DC.homedir(),".cursor");return fi.join(r,"projects",n)}async function Ds(e){if(!e||typeof e!="string")return;let t=fi.resolve(e),n=BC(t),r=fi.join(n,".workspace-trusted");await bk.mkdir(n,{recursive:!0}),await bk.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:t},null,2)}
|
|
14
|
+
`,"utf8")}var Hl=q(()=>{});import Mf from"node:fs/promises";import Nr from"node:path";import{execFile as jC}from"node:child_process";import{promisify as LC}from"node:util";async function GC(e){try{return(await Mf.stat(Nr.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function KC(e){let t=i=>Mf.readFile(Nr.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 nr(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await GC(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:Nr.basename(o),branch:await KC(o)});return}if(s>=WC)return;let i;try{i=await Mf.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||qC.has(c.name)||await t(Nr.join(o,c.name),s+1)}for(let o of HC)await t(Nr.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=await Oe(o.path),a=s?.toLowerCase();if(a){if(n.has(a))continue;n.add(a),o.slug=s}r.push(o)}return r.sort((o,s)=>o.name.localeCompare(s.name))}function VC(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function Oe(e){if(!e)return null;if(Of.has(e))return Of.get(e);let t=null;try{let{stdout:n}=await FC("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=VC(n)}catch{t=null}return Of.set(e,t),t}async function Bs(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await nr())if((await Oe(n.path))?.toLowerCase()===t)return n.path;return null}var FC,kk,UC,HC,WC,qC,Of,Pr=q(()=>{FC=LC(jC),kk=process.env.HOME||"/home/globo",UC=Nr.join(process.env.BOSTRAT_STATE_DIR||Nr.join(kk,".bostrat"),"repos"),HC=[...(process.env.BOSTRAT_REPOS_ROOT||Nr.join(kk,"Documents")).split(","),UC].map(e=>e.trim()).filter(Boolean),WC=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,qC=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);Of=new Map});function js(e=Date.now()){if(Co)return{tripped:!1,holdMs:1/0};if(Mo>e)return{tripped:!1,holdMs:Mo-e};if((!hi||e-hi>=Wl.windowMs)&&(hi=e,mi=0),mi++,mi<Wl.trip)return{tripped:!1,holdMs:0};ql++;let t=Math.min(Wl.minMs*2**(ql-1),Wl.maxMs);return Mo=e+t,mi=0,hi=0,{tripped:!0,holdMs:t}}function xk(){mi=0,hi=0,ql=0,Mo=0}function wi(e=Date.now()){return Co?1/0:Mo>e?Mo-e:0}function Ls(e=Date.now()){return wi(e)>0}function Cf(e={}){Co={at:Date.now(),...e},Mo=1/0}function Gl(){return Co}function vk(e=Date.now()){return{held:Ls(e),holdMs:Co?null:wi(e),holds:ql,removed:Co?{...Co}:null}}var zC,Wl,mi,hi,ql,Mo,Co,Nf=q(()=>{zC={trip:3,windowMs:3e5,minMs:3e5,maxMs:216e5},Wl={...zC},mi=0,hi=0,ql=0,Mo=0,Co=null});import JC from"node:fs";import Sk from"node:fs/promises";import gi from"node:path";import YC from"node:os";import{fileURLToPath as XC}from"node:url";import{execFile as ZC}from"node:child_process";import{promisify as QC}from"node:util";function Ne(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 Kl(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}function _k(e){return e?.status===404&&/not granted/i.test(String(e?.message||""))}async function Fs(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!Tk.test(n))throw No("repo must be owner/name",400);let r=Pf.get(n);if(r&&r.expiresAt-Date.now()>nN)return r;if(!Kl(t))throw No("node is not plane-linked \u2014 GitHub auth unavailable",503);if(Ls()){if(r&&r.expiresAt>Date.now())return r;throw No(Gl()?"this node was removed from the plane \u2014 GitHub auth unavailable":"plane refused this node's token \u2014 GitHub auth unavailable until it is re-minted",503)}let o;try{o=await Ek(`${Ne(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:Ik(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw No(`plane unreachable \u2014 GitHub auth unavailable (${i?.message||i})`,503)}let s=await o.json().catch(()=>({}));if(!o.ok){if((o.status===401||o.status===403||o.status===410)&&js(),o.status>=500&&r&&r.expiresAt>Date.now())return r;throw No(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 Pf.set(n,a),a}async function yi({env:e=process.env,force:t=!1}={}){if(!Kl(e))return{configured:!1,repos:[],username:null};if(!t&&Dr&&Date.now()-Dr.at<rN)return Dr.data;if(Ls())return Dr?.data??{configured:!0,repos:[],username:null,error:"plane refused this node's token"};try{let n=await Ek(`${Ne(e)}/v1/github/repos`,{headers:Ik(e)});if((n.status===401||n.status===403||n.status===410)&&js(),!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Dr={at:Date.now(),data:r},r}catch(n){return Dr?Dr.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function $k(){Pf.clear(),Dr=null}async function _n({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await Oe(e):null);if(!r)return{};let{token:o}=await Fs(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Df({env:e=process.env}={}){return(await yi({env:e})).username??null}function iN(e){let[t,n]=String(e).split("/");return gi.join(tN,t,n)}async function Bf(e,{env:t=process.env}={}){let n=(...r)=>Ak("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${sN()}" ${aN(t)}`),await n("config","credential.useHttpPath","true")}async function Vl(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!Tk.test(String(e||"")))throw No(`bad repo slug "${e}"`,400);let r=iN(e);if(JC.existsSync(gi.join(r,".git")))return r;let{token:o}=await Fs(e,{env:t});await Sk.mkdir(gi.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Ak("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:oN,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Sk.rm(r,{recursive:!0,force:!0}).catch(()=>{}),No(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Bf(r,{env:t}),r}var Ak,eN,tN,Tk,nN,rN,oN,Ek,Pf,Dr,Ik,No,sN,aN,yt=q(()=>{Pr();Nf();Ak=QC(ZC),eN=process.env.BOSTRAT_STATE_DIR||gi.join(process.env.HOME||YC.tmpdir(),".bostrat"),tN=gi.join(eN,"repos"),Tk=/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?\/[A-Za-z0-9._-]{1,100}$/,nN=5*6e4,rN=6e4,oN=10*6e4,Ek=fetch,Pf=new Map,Dr=null;Ik=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),No=(e,t)=>Object.assign(new Error(e),{status:t});sN=()=>XC(new URL("../bin/bostrat-git-credential",import.meta.url)),aN=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function Ok(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Ck(e,t,n,r=Rk){let o=Ne(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await lN(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function Nk(e,{env:t=process.env,force:n=!1,timeoutMs:r=Rk}={}){if(!Ok(t))return{facts:[],source:"unconfigured"};let o=jf.get(e);if(!n&&o&&Date.now()-o.at<cN)return{facts:o.facts,source:"cache"};try{let s=await Ck(`/v1/memory/${encodeURIComponent(e)}`,{headers:Mk(t)},t,r);if(!s.ok)throw new Error(`plane ${s.status}`);let a=await s.json(),i=Array.isArray(a?.facts)?a.facts:[];return jf.set(e,{at:Date.now(),facts:i}),{facts:i,source:"plane"}}catch(s){return o?{facts:o.facts,source:"stale",error:s?.message}:{facts:[],source:"unreachable",error:s?.message}}}async function zl(e,t,{env:n=process.env}={}){if(!Ok(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await Ck(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...Mk(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 jf.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 cN,Rk,jf,lN,Mk,Pk=q(()=>{yt();cN=6e4,Rk=5e3,jf=new Map,lN=(...e)=>fetch(...e);Mk=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import Lf from"node:fs/promises";import Us from"node:path";import uN from"node:os";import{EventEmitter as dN}from"node:events";import{randomUUID as pN}from"node:crypto";function Hs(e){return e?.cwd?Us.join(e.cwd,".bostrat","events"):Us.join(Ff,"events")}async function wN(e,t){if(!Jl.has(t)){let o=(await G(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);Jl.set(t,o)}let n=Jl.get(t)+1;return Jl.set(t,n),n}function Uf(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 N(e,t){let n=typeof e=="string"?e:e?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!t||!fN.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!mN.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!hN.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?Us.join(Ff,"events"):Hs(e),o=t.epoch;if(o==null&&typeof e=="object"&&t.boxId&&Array.isArray(e?.boxes)){let u=e.boxes.find(d=>d.name===t.boxId);o=Uf(u,Number(t.payload?.ts??t.ts)||Date.now())}let s=await wN(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||pN(),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}:{}},c=JSON.stringify(i),l=i;return Bk(c)&&(c=jk(c),l=JSON.parse(c)),await Lf.mkdir(r,{recursive:!0}),await Lf.appendFile(Dk(r,n),c+`
|
|
15
|
+
`),Xe.emit("event",l),Xe.emit(`track:${n}`,l),l}async function G(e,t){let n,r;typeof e=="string"&&t?(n=e,r=t):(r=typeof e=="string"?e:e?.id,n=typeof e=="string"?Us.join(Ff,"events"):Hs(e));let o;try{o=await Lf.readFile(Dk(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
16
|
+
`))if(a.trim())try{s.push(JSON.parse(Bk(a)?jk(a):a))}catch{}return s}function Lk(e,t){let n=`track:${e}`;return Xe.on(n,t),()=>Xe.off(n,t)}var fN,mN,hN,Xe,Ff,Dk,Jl,gN,Bk,jk,ye=q(()=>{fN=new Set(["session.ready","turn.user","turn.agent","turn.retracted","peer.message","provider.error","delivery.queued","delivery.settled","turn.interrupted","tool.called","tool.result","prompt.raised","prompt.answered","prompt.dismissed","prompt.autoRejected","box.fullscreen.detected","box.fullscreen.answered","context.usage","cost.recorded","memory.seed","memory.harvested","handoff.artifact","code.pushed","code.committed","code.rework","rework.cause.offered","rework.cause.answered","rework.cause.retracted","handoff.started","handoff.step","handoff.done","handoff.failed","subtask.delegated","subtask.merged","job.started","job.stalled","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","loop.update.offered","loop.update.answered","loop.updated","loop.delete.offered","loop.delete.answered","loop.deleted","loop.paused","followup.armed","followup.armed.cancelled","followup.offered","followup.answered","followup.create.offered","followup.create.answered","followup.created","planning.change.offered","planning.change.answered","planning.changed","notify.raised","usage.cap.hit","usage.cap.warning","provider.connected","track.summary.recorded","track.transferred","track.recovered","track.phase.changed","track.operation.changed","track.obligations.changed","track.circuit.tagged","blueprint.item.bound","decision.recorded","changelog.published","stall.detected","error"]),mN=new Set(["user","agent","orchestrator","system","peer"]),hN=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor","cli"]),Xe=new dN;Xe.setMaxListeners(0);Ff=process.env.BOSTRAT_STATE_DIR||Us.join(process.env.HOME||uN.tmpdir(),".bostrat");Dk=(e,t)=>Us.join(e,`${t}.ndjson`),Jl=new Map;gN=/(?<!\\)((?:\\\\)*)\\u0000/g,Bk=e=>e.includes("\\u0000"),jk=e=>e.replace(gN,"$1\\ufffd")});import le from"node:fs/promises";import yN from"node:os";import Ae from"node:path";import{execFile as bN}from"node:child_process";import{promisify as kN}from"node:util";function Xl(){return Ae.join(Zl,"memory","repos")}function Po(e){return e===rr?Ae.join(Zl,"memory",rr):Ae.join(Xl(),e)}async function ki(e){try{return JSON.parse(await le.readFile(Ae.join(e,Kk),"utf8"))}catch{return{}}}async function Wf(e,t){let n=await ki(e);for(let[r,o]of t)n[r]=Math.max(Number(n[r])||0,o);await le.mkdir(e,{recursive:!0}),await le.writeFile(Ae.join(e,Kk),JSON.stringify(n))}function Uk(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 Hk(e){try{let{stdout:t}=await xN("git",["-C",e,"config","--get","remote.origin.url"]);return t.trim()||null}catch{return null}}async function qf(e){if(!e)return[];let t=Uk(await Hk(e));if(t)return[t];let n=[],r=[];try{r=await le.readdir(e,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=Uk(await Hk(Ae.join(e,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function Ql(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 Br(e,t){let n=new RegExp(`^\\s*${t}:\\s*(.+?)\\s*$`,"m").exec(e||"");return n?n[1].replace(/^"|"$/g,""):null}async function $n(e,t=null){let n=t??await le.readFile(e,"utf8").catch(()=>""),{fm:r}=Ql(n),o=Br(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await le.stat(e)).mtimeMs}catch{return 0}}function Vk(e,t){let n=Br(e,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===rr){let o=Br(e,"trust");if(!o||/^operator/i.test(o))return rr}return t}function Gf(e,t,n,r=new Map){let o=(e||"").split(`
|
|
17
17
|
`),s=new Map;for(let c of(t||"").split(`
|
|
18
18
|
`)){let l=qs(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=qs(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(`
|
|
19
19
|
`).replace(/\n{3,}/g,`
|
|
20
20
|
|
|
21
|
-
`)}function
|
|
21
|
+
`)}function zk(e,t){let n=Br(t,"name")||e.replace(/\.md$/,""),r=Br(t,"description")||"";return`- [${n}](${e})${r?` \u2014 ${r}`:""}`}async function xi(e){let t=[];try{t=await le.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 Jk(e,t){let n=Ae.join(e,".bostrat","memory"),r=await xi(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=Wk.get(e)??0,s=Date.now(),a=new Map,i=new Map,c=0,l=new Map;for(let d of r){let p=Ae.join(n,d);if(o)try{if((await le.stat(p)).mtimeMs<o-SN)continue}catch{continue}let f;try{f=await le.readFile(p,"utf8")}catch{continue}let{fm:w}=Ql(f),m=Vk(w,t);if(!m)continue;let g=Po(m),y=Ae.join(g,d),b=await $n(p,f);if(l.has(g)||l.set(g,await ki(g)),(l.get(g)[d]||0)>=b)continue;let k=await $n(y).catch(()=>0);if(await le.access(y).then(()=>!0).catch(()=>!1)&&k>=b)continue;await le.mkdir(g,{recursive:!0}),await le.writeFile(y,f),c++;let $=a.get(g);$||a.set(g,$={won:new Set,generated:new Map}),$.won.add(d),$.generated.set(d,zk(d,w)),i.has(m)||i.set(m,new Set),i.get(m).add(d)}let u=await le.readFile(Ae.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[d,p]of a){let f=Ae.join(d,"MEMORY.md"),w=await le.readFile(f,"utf8").catch(()=>"");await le.writeFile(f,Gf(w,u,p.won,p.generated))}return Wk.set(e,s),{harvested:c,changedByKey:i}}async function Yk(e){let t=0,n=new Map;for(let r of e||[]){if(!r||!await le.access(Ae.join(r,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[s]=await qf(r),a=await Jk(r,s||null);t+=a.harvested;for(let[i,c]of a.changedByKey){n.has(i)||n.set(i,new Set);for(let l of c)n.get(i).add(l)}}return{harvested:t,changedByKey:n}}async function AN(){let e=Ae.join(Zl,"workspaces"),t=[];try{t=await le.readdir(e,{withFileTypes:!0})}catch{return{harvested:0,changedByKey:new Map}}return Yk(t.filter(n=>n.isDirectory()).map(n=>Ae.join(e,n.name)))}async function IN(e,t){let n=Po(e),r=await ki(n),o=new Set,s=new Map,a=[],i=0;for(let c of t||[]){let l=String(c?.file||"");if(!TN.test(l)||l==="MEMORY.md")continue;let u=Ae.join(n,l),d=await le.access(u).then(()=>!0).catch(()=>!1),p=d?await $n(u):0,f=Number(c.modified)||0;if(c.deleted){f>p&&(d&&await le.rm(u,{force:!0}),await Wf(n,[[l,f]]),d&&(a.push(l),i++));continue}d&&p>=f||(r[l]||0)>=f||(await le.mkdir(n,{recursive:!0}),await le.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=Ae.join(n,"MEMORY.md"),l=await le.readFile(c,"utf8").catch(()=>""),u=Gf(l,[...s.values()].join(`
|
|
22
22
|
`),o);if(a.length){let d=new Set(a);u=u.split(`
|
|
23
23
|
`).filter(p=>!d.has(qs(p))).join(`
|
|
24
|
-
`)}await le.mkdir(n,{recursive:!0}),await le.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function eu(e,t=null){let n=Po(e),r=t?.length?t:await
|
|
25
|
-
`);t.length>
|
|
26
|
-
`);for(;Buffer.byteLength(n,"utf8")>
|
|
27
|
-
`);return n}async function
|
|
24
|
+
`)}await le.mkdir(n,{recursive:!0}),await le.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function eu(e,t=null){let n=Po(e),r=t?.length?t:await xi(n);if(!r.length)return{pushed:0,source:"empty"};let o=EN(await le.readFile(Ae.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=Ae.join(n,a),c;try{c=await le.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await $n(i,c),indexLine:o.get(a)??null})}return zl(e,s)}async function _N(e,{timeoutMs:t}={}){let{facts:n,source:r}=await Nk(e,t?{timeoutMs:t}:{});n.length?await IN(e,n):r==="plane"&&await eu(e)}function $N(e){let t=e.split(`
|
|
25
|
+
`);t.length>Fk&&(t=t.slice(0,Fk));let n=t.join(`
|
|
26
|
+
`);for(;Buffer.byteLength(n,"utf8")>vN&&t.length;)t.pop(),n=t.join(`
|
|
27
|
+
`);return n}async function ON(e,t){if(!e||!t?.length)return{materialized:!1,reason:"no-repo-key"};let n=Ae.join(e,".bostrat","memory"),r=0,o=0,s=[];for(let u of t){let d=Po(u),p=await ki(d);for(let[g,y]of Object.entries(p)){let b=Ae.join(n,g);await le.access(b).then(()=>!0).catch(()=>!1)&&await $n(b)<=y&&(await le.rm(b,{force:!0}),o++)}let f=await xi(d);if(!f.length)continue;await le.mkdir(n,{recursive:!0});for(let g of f){let y=Ae.join(d,g),b=Ae.join(n,g),k=await $n(y),S=await $n(b).catch(()=>0);await le.access(b).then(()=>!0).catch(()=>!1)&&S>=k||(await le.copyFile(y,b),r++)}let w=await le.readFile(Ae.join(d,"MEMORY.md"),"utf8").catch(()=>""),m=u===rr?"## operator memory":`## repo: ${u}`;w.trim()&&s.push(t.length>1?`${m}
|
|
28
28
|
|
|
29
29
|
${w.trim()}`:w.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(`
|
|
30
30
|
|
|
31
31
|
`)+`
|
|
32
|
-
`,c
|
|
32
|
+
`,c=$N(i);await le.mkdir(n,{recursive:!0}),await le.writeFile(Ae.join(n,"MEMORY.md"),c);let l=Ae.join(n,"claude");return await le.mkdir(l,{recursive:!0}),await le.writeFile(Ae.join(l,"MEMORY.md"),RN,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function Xk(e){try{let t=await qf(e);if(!t.length)return{materialized:!1,reason:"no-repo-key"};let n=[...t,rr];return await Promise.all(n.map(r=>_N(r,{timeoutMs:MN}))),await ON(e,n)}catch(t){return{materialized:!1,reason:"error",error:t?.message||String(t)}}}function Zk(){return{...st,pending:Ws.size,queued:Hf.size}}function Qk(e){if(Yl.has(e))return Yl.get(e);Hf.add(e);let t=qk.then(async()=>{Hf.delete(e),Yl.delete(e);let n=await Kf(e);return st.runs++,st.lastRunAt=Date.now(),n.error?(st.errors++,st.lastError=n.error):st.harvested+=n.harvested||0,n}).catch(n=>({harvested:0,error:n?.message||String(n)}));return Yl.set(e,t),qk=t.then(()=>{}),t}function tu(e,{delayMs:t=CN}={}){if(!e||typeof e!="string")return;st.scheduled++;let n=Ws.get(e);n&&clearTimeout(n);let r=setTimeout(()=>{Ws.delete(e),Qk(e)},t);r.unref?.(),Ws.set(e,r)}async function nu(e,{timeoutMs:t=3e3}={}){if(!e)return{harvested:0,skipped:"no-cwd"};if(!await le.access(Ae.join(e,".bostrat","memory")).then(()=>!0,()=>!1))return{harvested:0,skipped:"no-memory-dir"};let r=Ws.get(e);r&&(clearTimeout(r),Ws.delete(e));let o,s=new Promise(a=>{o=setTimeout(()=>a({harvested:0,timedOut:!0}),t),o.unref?.()});try{return await Promise.race([Qk(e),s])}finally{clearTimeout(o)}}async function e0(e){return bi||(bi=(async()=>{try{Gk||(Gk=!0,await PN());let{harvested:t,changedByKey:n}=await Yk(e);for(let[r,o]of n)await eu(r,[...o]);return st.reconciles++,st.lastReconcileAt=Date.now(),st.harvested+=t,{harvested:t,keys:[...n.keys()]}}catch(t){return st.errors++,st.lastError=t?.message||String(t),{harvested:0,error:st.lastError}}finally{bi=null}})(),bi)}function NN(){return Ae.join(Zl,"memory",".full-sweep-done")}async function t0(){let e=NN();try{let t=JSON.parse(await le.readFile(e,"utf8"));return st.backfill={at:Number(t?.at)||0,harvested:Number(t?.harvested)||0,prior:!0},{ran:!1,...st.backfill}}catch{}try{let{harvested:t,changedByKey:n}=await AN();for(let[o,s]of n)await eu(o,[...s]);let r={at:Date.now(),harvested:t};return await le.mkdir(Ae.dirname(e),{recursive:!0}),await le.writeFile(e,JSON.stringify(r)),st.backfill=r,{ran:!0,...r}}catch(t){return st.errors++,st.lastError=t?.message||String(t),{ran:!1,error:st.lastError}}}async function PN(){let e=0,t=[];try{t=await le.readdir(Xl(),{withFileTypes:!0})}catch{return{moved:e}}for(let n of t){if(!n.isDirectory())continue;let r=Ae.join(Xl(),n.name),o=Po(rr),s=new Set,a=new Map;for(let f of await xi(r)){let w=Ae.join(r,f),m;try{m=await le.readFile(w,"utf8")}catch{continue}let{fm:g}=Ql(m);if(Vk(g,n.name)!==rr)continue;let y=Ae.join(o,f),b=await $n(y).catch(()=>0),k=await $n(w,m);await le.mkdir(o,{recursive:!0}),k>b&&await le.writeFile(y,m),await le.rm(w,{force:!0}),s.add(f),a.set(f,zk(f,g)),e++}if(!s.size)continue;let i=Date.now();await Wf(r,[...s].map(f=>[f,i])),await zl(n.name,[...s].map(f=>({file:f,deleted:!0,modified:i})));let c=Ae.join(r,"MEMORY.md"),l=await le.readFile(c,"utf8").catch(()=>""),u=l.split(`
|
|
33
33
|
`).filter(f=>!s.has(qs(f))).join(`
|
|
34
|
-
`);await le.writeFile(c,u).catch(()=>{});let d=Ae.join(o,"MEMORY.md"),p=await le.readFile(d,"utf8").catch(()=>"");await le.writeFile(d,
|
|
34
|
+
`);await le.writeFile(c,u).catch(()=>{});let d=Ae.join(o,"MEMORY.md"),p=await le.readFile(d,"utf8").catch(()=>"");await le.writeFile(d,Gf(p,l,s,a))}return{moved:e}}async function n0(){let e=[],t=[rr];try{for(let n of await le.readdir(Xl(),{withFileTypes:!0}))n.isDirectory()&&t.push(n.name)}catch{}for(let n of t){let r=Po(n),o=await xi(r),s=await ki(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await le.readFile(Ae.join(r,i),"utf8").catch(()=>""),{fm:l}=Ql(c);a.push({file:i,name:Br(l,"name")||i.replace(/\.md$/,""),description:Br(l,"description")||"",scope:Br(l,"scope")||null,modified:await $n(Ae.join(r,i),c)})}e.push({key:n,facts:a,tombstones:Object.keys(s).length})}return e}async function r0(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=Po(e),r=Ae.join(n,t);if(!await le.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${t} not found in ${e}`);let s=Date.now();await le.rm(r,{force:!0}),await Wf(n,[[t,s]]);let a=Ae.join(n,"MEMORY.md"),i=await le.readFile(a,"utf8").catch(()=>"");await le.writeFile(a,i.split(`
|
|
35
35
|
`).filter(l=>qs(l)!==t).join(`
|
|
36
|
-
`)).catch(()=>{});let c=await zl(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function
|
|
37
|
-
`)){let r=qs(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};
|
|
36
|
+
`)).catch(()=>{});let c=await zl(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function Kf(e){try{let t=await qf(e),{harvested:n,changedByKey:r}=await Jk(e,t[0]||null);for(let[o,s]of r)await eu(o,[...s]);return n>0&&await BN(e,r).catch(()=>{}),{harvested:n}}catch(t){return{harvested:0,error:t?.message||String(t)}}}async function BN(e,t){let n=/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1];if(!n)return;let r={},o=0;for(let[s,a]of t){let i=[...a].sort();o+=i.length,r[s]={count:i.length,files:i.slice(0,DN)}}await N({id:n,cwd:e},{type:"memory.harvested",actor:"orchestrator",payload:{harvested:o,keys:r,ts:Date.now()}})}var xN,Zl,Fk,vN,rr,Kk,qs,Wk,SN,TN,EN,RN,MN,CN,Ws,Hf,qk,st,Yl,Gk,bi,DN,vi=q(()=>{Pk();ye();xN=kN(bN),Zl=process.env.BOSTRAT_STATE_DIR||Ae.join(process.env.HOME||yN.tmpdir(),".bostrat"),Fk=200,vN=25*1024;rr="operator";Kk=".tombstones.json";qs=e=>/\]\(([^)]+\.md)\)/.exec(e)?.[1]||null;Wk=new Map,SN=2e3;TN=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,EN=e=>{let t=new Map;for(let n of(e||"").split(`
|
|
37
|
+
`)){let r=qs(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};RN=`# Track memory
|
|
38
38
|
|
|
39
39
|
Shared Track memory (all providers) lives one level up: read \`../MEMORY.md\` for the
|
|
40
40
|
index and the fact files beside it. Facts you save here stay Claude-local but ride the
|
|
41
41
|
same sync; prefer adding durable cross-provider facts to \`../\` following its format.
|
|
42
|
-
|
|
43
|
-
`,n!=null?{mode:n}:void 0),await ru.rename(r,e)}catch(o){throw await ru.unlink(r).catch(()=>{}),o}}var
|
|
44
|
-
`,"utf8");let n=(await
|
|
45
|
-
`).filter(Boolean);n.length>
|
|
42
|
+
`;MN=Number(process.env.BOSTRAT_MEMORY_SPAWN_PULL_MS)||1500;CN=Number(process.env.BOSTRAT_MEMORY_HARVEST_DEBOUNCE_MS)||2e3,Ws=new Map,Hf=new Set,qk=Promise.resolve(),st={scheduled:0,runs:0,harvested:0,errors:0,lastRunAt:null,lastError:null,reconciles:0,lastReconcileAt:null,backfill:null};Yl=new Map;Gk=!1,bi=null;DN=20});import ru from"node:fs/promises";import jN from"node:path";async function Mt(e,t,{mode:n}={}){await ru.mkdir(jN.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${++LN}.tmp`;try{await ru.writeFile(r,JSON.stringify(t,null,2)+`
|
|
43
|
+
`,n!=null?{mode:n}:void 0),await ru.rename(r,e)}catch(o){throw await ru.unlink(r).catch(()=>{}),o}}var LN,jr=q(()=>{LN=0});import c0 from"node:fs/promises";import l0 from"node:os";import u0 from"node:path";function qe(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Ti(e,t={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),FN);try{return await GN(`${Ne(n)}${e}`,{...t,signal:r.signal})}finally{clearTimeout(o)}}async function m0(){return su||(Vf||=c0.readFile(d0(),"utf8").then(e=>JSON.parse(e)).catch(()=>({ops:[]})).then(e=>su={ops:Array.isArray(e?.ops)?e.ops:[],parked:Array.isArray(e?.parked)?e.parked:[]}).finally(()=>Vf=null),Vf)}async function au(){await Mt(d0(),su).catch(()=>{})}function a0(e){return e.op==="blob"?`blob ${e.family} ${e.id} ${e.v} ${e.kind}`:`${e.op} ${e.family} ${e.id}`}async function Jf(e){let t=await m0(),n=a0(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=>a0(o)===n);r>=0?t.ops[r]=e:t.ops.push(e)}t.ops.length>o0&&(t.ops=t.ops.slice(-o0)),await au()}function i0(){ou||(ou=setTimeout(()=>{ou=null,Gs().catch(()=>{})},KN),ou.unref?.())}function zf(e){let t=new Error(`plane ${e}`);return t.refused=!0,t}async function zN(e,t=process.env){if(e.op==="record"){let n=await Ti(`/v1/content/${encodeURIComponent(e.family)}`,{method:"POST",headers:{...Ai(t),"content-type":"application/json"},body:JSON.stringify({records:[{id:e.id,trackId:e.trackId??null,data:e.data,updatedAt:e.updatedAt,originNode:p0()}]})},t);if(!n.ok)throw zf(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 c0.readFile(e.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await Ti(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}/blob?v=${e.v}&kind=${encodeURIComponent(e.kind)}`,{method:"POST",headers:{...Ai(t),"content-type":"application/octet-stream"},body:n},t);if(!r.ok&&r.status!==400)throw zf(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 Ti(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}?deletedAt=${e.deletedAt}`,{method:"DELETE",headers:Ai(t)},t);if(!n.ok)throw zf(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function Gs({env:e=process.env,log:t=console,now:n=Date.now()}={}){return qe(e)?Si||(Si=(async()=>{let r=await m0();JN(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await zN(a,e)}catch(c){if(!c?.refused)return i0(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<HN)return i0(),await au(),{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>s0&&(r.parked=r.parked.slice(-s0)),s++,await au();continue}i.rejected&&t.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await au()}return{flushed:o,parked:s,pending:0}})().finally(()=>{Si=null}),Si):{flushed:0,source:"unconfigured"}}function JN(e,t){if(!e.parked.length||e.ops.length)return;let n=e.parked.filter(r=>t-(r.parkedAt||0)>=VN);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 At(e,t,{trackId:n=null,env:r=process.env}={}){return qe(r)?(await Jf({op:"record",family:e,id:t.id,trackId:n??t.trackId??null,data:t,updatedAt:Number(t.updatedAt)||Date.now()}),Gs({env:r})):{queued:!1,source:"unconfigured"}}async function Lr(e,t,n,r,o,{env:s=process.env}={}){return qe(s)?(await Jf({op:"blob",family:e,id:t,v:Number(n),kind:r,path:o}),Gs({env:s})):{queued:!1,source:"unconfigured"}}async function cn(e,t,{deletedAt:n=Date.now(),env:r=process.env}={}){return qe(r)?(await Jf({op:"delete",family:e,id:t,deletedAt:n}),Gs({env:r})):{queued:!1,source:"unconfigured"}}async function dt(e,{env:t=process.env}={}){if(!qe(t))return{records:[],source:"unconfigured"};try{let n=await Ti(`/v1/content/${encodeURIComponent(e)}?tombstones=1`,{headers:Ai(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 Ei(e,t,n,r,{env:o=process.env}={}){if(!qe(o))return null;try{let s=await Ti(`/v1/content/${encodeURIComponent(e)}/${encodeURIComponent(t)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Ai(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 FN,UN,o0,HN,s0,WN,qN,d0,p0,f0,GN,KN,VN,Ai,su,Vf,ou,Si,Fr=q(()=>{yt();jr();FN=1e4,UN=3e4,o0=2e3,HN=5,s0=100,WN=60*6e4,qN=()=>process.env.BOSTRAT_STATE_DIR||u0.join(process.env.HOME||l0.tmpdir(),".bostrat"),d0=()=>u0.join(qN(),"content-outbox.json"),p0=()=>process.env.BOSTRAT_NODE_ID||l0.hostname(),f0=()=>p0(),GN=(...e)=>fetch(...e),KN=UN,VN=WN;Ai=(e=process.env)=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`});su=null,Vf=null,ou=null,Si=null});import Yf from"node:fs/promises";import h0 from"node:path";async function w0(e,t,{box:n=null,agent:r=null}={}){if(!e||!qe())return{pushed:!1};try{let o=Date.now();return await At("checkpoints",{id:e,trackId:e,v:o,box:n,agent:r,updatedAt:o},{trackId:e}),await Lr("checkpoints",e,o,"md",t),Ii=null,{pushed:!0,v:o}}catch{return{pushed:!1}}}async function eP(){if(Ii&&Date.now()-Ii.at<QN)return{records:Ii.records,source:"plane"};let e=await dt("checkpoints");return e.source==="plane"&&(Ii={at:Date.now(),records:e.records}),e}async function iu(e){try{let t=ZN(e);if(!t||!qe())return{restored:!1};let n=h0.join(e,YN,XN);if(await Yf.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await eP();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 Ei("checkpoints",t,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await Yf.mkdir(h0.dirname(n),{recursive:!0}),await Yf.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var YN,XN,ZN,QN,Ii,cu=q(()=>{Fr();YN=".bostrat/handoff",XN="latest.md",ZN=e=>/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1]??null;QN=6e4,Ii=null});import _i from"node:fs/promises";import tP from"node:os";import y0 from"node:path";function Do(e){let t=String(e??"");for(let n of nP)t=t.replace(n,"\u2039redacted\u203A");return t}function lu(){let e=process.env.BOSTRAT_HANDOFF_DEBUG;return e==="1"||e==="true"}async function rP(e){try{await _i.mkdir(Xf,{recursive:!0}),await _i.appendFile(Ks,`${JSON.stringify(e)}
|
|
44
|
+
`,"utf8");let n=(await _i.readFile(Ks,"utf8")).split(`
|
|
45
|
+
`).filter(Boolean);n.length>g0&&await _i.writeFile(Ks,`${n.slice(-g0).join(`
|
|
46
46
|
`)}
|
|
47
|
-
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${Ks}: ${t?.message||t}`)}}async function fe(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&&lu())try{t.paneTail=Do((await De(t.sessionName,{lines:35})).slice(-4e3))}catch{t.paneTail=null}(lu()||t.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed|launch|submit-|gate-accept|blocked|timeout|dialog)/.test(String(t.phase||t.step||"")))&&await
|
|
48
|
-
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Do(o.paneTail)),o}catch{return{raw:Do(r)}}})}catch{return[]}}var
|
|
49
|
-
`,r=
|
|
47
|
+
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${Ks}: ${t?.message||t}`)}}async function fe(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&&lu())try{t.paneTail=Do((await De(t.sessionName,{lines:35})).slice(-4e3))}catch{t.paneTail=null}(lu()||t.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed|launch|submit-|gate-accept|blocked|timeout|dialog)/.test(String(t.phase||t.step||"")))&&await rP(t)}async function b0({limit:e=80}={}){try{return(await _i.readFile(Ks,"utf8")).split(`
|
|
48
|
+
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Do(o.paneTail)),o}catch{return{raw:Do(r)}}})}catch{return[]}}var nP,Xf,Ks,g0,k0,Rn=q(()=>{ht();nP=[/[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];Xf=process.env.BOSTRAT_DEBUG_DIR||y0.join(tP.homedir(),".bostrat","debug"),Ks=y0.join(Xf,"handoff.jsonl"),g0=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;k0={DEBUG_DIR:Xf,LOG_FILE:Ks}});import uu from"node:fs/promises";import Zf from"node:path";import oP from"node:os";async function v0(e=x0){await uu.mkdir(e,{recursive:!0});for(let t of aP){let n=t==="prepare-commit-msg"?iP:t==="post-commit"?cP:`${Qf}exit 0
|
|
49
|
+
`,r=Zf.join(e,t),o=null;try{o=await uu.readFile(r,"utf8")}catch{}o!==n&&await uu.writeFile(r,n,{mode:493}),await uu.chmod(r,493).catch(()=>{})}return e}function S0({nodeUrl:e,dir:t=x0,env:n=process.env}={}){return String(n.BOSTRAT_GIT_HOOKS||"").toLowerCase()==="off"?[]:["-e","GIT_CONFIG_COUNT=1","-e","GIT_CONFIG_KEY_0=core.hooksPath","-e",`GIT_CONFIG_VALUE_0=${t}`,"-e",`${$i}=${e}`]}function A0(e=process.env){return e.BOSTRAT_NODE_URL||`http://127.0.0.1:${e.PORT||3362}`}var $i,sP,x0,aP,Qf,iP,cP,em=q(()=>{Ns();$i="BOSTRAT_NODE_URL",sP=process.env.BOSTRAT_STATE_DIR||Zf.join(process.env.HOME||oP.tmpdir(),".bostrat"),x0=Zf.join(sP,"git-hooks"),aP=["applypatch-msg","pre-applypatch","post-applypatch","pre-commit","pre-merge-commit","prepare-commit-msg","commit-msg","post-commit","pre-rebase","post-checkout","post-merge","pre-push","post-rewrite","pre-auto-gc"],Qf=`#!/bin/sh
|
|
50
50
|
# bostrat git hook shim (agent/src/git-hooks.js) \u2014 chains to the repo's own hook,
|
|
51
51
|
# then stamps/reports for rework attribution when running inside a bostrat box.
|
|
52
52
|
hook=$(basename "$0")
|
|
@@ -61,75 +61,75 @@ esac
|
|
|
61
61
|
if [ -x "$orig/$hook" ] && [ "$orig/$hook" != "$0" ]; then
|
|
62
62
|
"$orig/$hook" "$@" || exit $?
|
|
63
63
|
fi
|
|
64
|
-
`,
|
|
64
|
+
`,iP=`${Qf}
|
|
65
65
|
# \u2500\u2500 bostrat: append provenance trailers (Layer 0). Skipped for merge commits
|
|
66
66
|
# (nobody "wrote" a merge) and outside a box (no token / no node url).
|
|
67
67
|
[ -z "$${er}" ] && exit 0
|
|
68
|
-
[ -z "$${
|
|
68
|
+
[ -z "$${$i}" ] && exit 0
|
|
69
69
|
[ "$2" = "merge" ] && exit 0
|
|
70
|
-
trailers=$(curl -sS -m 3 -H "${tr}: $${er}" "$${
|
|
70
|
+
trailers=$(curl -sS -m 3 -H "${tr}: $${er}" "$${$i}/api/git/trailers" 2>/dev/null) || exit 0
|
|
71
71
|
[ -z "$trailers" ] && exit 0
|
|
72
72
|
printf '%s\\n' "$trailers" | while IFS= read -r line; do
|
|
73
73
|
[ -n "$line" ] && git interpret-trailers --in-place --if-exists addIfDifferent --trailer "$line" "$1" >/dev/null 2>&1 || true
|
|
74
74
|
done
|
|
75
75
|
exit 0
|
|
76
|
-
`,
|
|
76
|
+
`,cP=`${Qf}
|
|
77
77
|
# \u2500\u2500 bostrat: report the commit (code.committed + rework analysis, node-side).
|
|
78
78
|
[ -z "$${er}" ] && exit 0
|
|
79
|
-
[ -z "$${
|
|
79
|
+
[ -z "$${$i}" ] && exit 0
|
|
80
80
|
sha=$(git rev-parse HEAD 2>/dev/null) || exit 0
|
|
81
81
|
cwd=$(git rev-parse --show-toplevel 2>/dev/null)
|
|
82
|
-
printf '{"sha":"%s","cwd":"%s"}' "$sha" "$cwd" | curl -sS -m 5 -X POST "$${
|
|
82
|
+
printf '{"sha":"%s","cwd":"%s"}' "$sha" "$cwd" | curl -sS -m 5 -X POST "$${$i}/api/git/committed" \\
|
|
83
83
|
-H 'content-type: application/json' -H "${tr}: $${er}" --data-binary @- >/dev/null 2>&1 || true
|
|
84
84
|
exit 0
|
|
85
|
-
`});import{execFile as
|
|
86
|
-
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,p,f,w]=r.split("|"),m=Number(i),g=Number(w);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(m)?n-m*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:d||null,agent:p||(l==="1"?"claude":null),alternateOn:f==="1",createdMs:Number.isFinite(g)&&g>0?g*1e3:null}}).filter(r=>!
|
|
87
|
-
`).map(n=>n.trim()).includes(e)}catch{return!1}}function
|
|
88
|
-
`).map(n=>Number(n.trim())).filter(n=>Number.isFinite(n)&&n>0)}catch{return[]}}async function
|
|
89
|
-
`).filter(Boolean).length}catch{return 0}}async function Lo(e,{cols:t=
|
|
90
|
-
`);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
|
|
91
|
-
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function
|
|
92
|
-
`,n);i!==-1&&(t=t.slice(0,i))}let r=
|
|
93
|
-
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function Wr(e,t=""){let n=t.trim();return n||pm(e)||Qs(e)}function fm(e,t){if(e){if(Array.isArray(e)){for(let n of e)fm(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&mm.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))fm(n,t)}}}async function
|
|
94
|
-
`)){let a=s.trim();if(!a||a[0]!=="{")continue;let i;try{i=JSON.parse(a)}catch{continue}let c=i.message;if(!c)continue;let l=[];if(fm(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:Wr(d.command,d.description),ts:u})}return o.slice(-n)}function
|
|
95
|
-
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=t)break}return o}finally{await n.close()}}async function
|
|
96
|
-
`)){let f=p.trim();if(!f||f[0]!=="{")continue;let w;try{w=JSON.parse(f)}catch{continue}if((w.type==="turn_context"&&w.payload?.model||w.type==="session_meta"&&w.payload?.model)&&(i=w.payload.model),w.type==="event_msg"){let b=w.payload?.type;b==="task_started"?(l=!0,u=!1,d=w.payload?.turn_id||null):b==="task_complete"&&l&&(l=!1,!u&&w.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:Uo,sourceId:w.payload?.turn_id||d||void 0,ts:w.timestamp?Date.parse(w.timestamp):null,model:i}));continue}if(w.type!=="response_item")continue;let m=w.payload;if(!m)continue;if((m.type==="function_call"||m.type==="custom_tool_call")&&m.name){u=!0,gm.has(m.name)||c.push(m.name);continue}if(m.type!=="message"||m.role!=="assistant")continue;u=!0;let g=
|
|
97
|
-
`;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
|
|
98
|
-
`)){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 d=[];if(u.type==="function_call"&&
|
|
99
|
-
`).trim():"";
|
|
100
|
-
`).trim():""}function
|
|
101
|
-
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type==="attachment"){let y=l.attachment;y?.type==="queued_command"&&y.commandMode==="task-notification"&&typeof y.prompt=="string"&&y.prompt.trim()&&o.push({role:"user",text:y.prompt.trim(),ts:l.timestamp?Date.parse(l.timestamp):null,model:null});continue}let u=l.message;if(!u)continue;let d=u.role||l.type;if(d!=="user"&&d!=="assistant"||d==="assistant"&&u.model==="<synthetic>"&&!l.isApiErrorMessage)continue;if(d==="assistant"&&Array.isArray(u.content))for(let y of u.content){let b=y?.type==="tool_use"?y.input?.file_path||y.input?.path:null;typeof b=="string"&&
|
|
102
|
-
|
|
103
|
-
`):""}var
|
|
104
|
-
`);if(!
|
|
105
|
-
`);for(let r=n.length-1;r>=0;r--)if(
|
|
106
|
-
`);return t}function
|
|
107
|
-
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(
|
|
108
|
-
`).pop()||"";return
|
|
109
|
-
`),o=r.findIndex(i=>
|
|
110
|
-
`).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
|
|
111
|
-
`),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
|
|
112
|
-
`),n=-1;for(let c=t.length-1;c>=0;c--)if(t[c].trim()){n=c;break}if(n<0)return!1;let r=t[n];if(
|
|
113
|
-
`).filter(c=>/^\s*›/.test(c)),i=a.some(c=>/^\s*›\s+(?:Ask Codex to do anything|Implement \{feature\})\s*$/.test(c));return a.length>0&&!i&&/esc to interrupt|\bWorking \(/.test(s)}function
|
|
114
|
-
`),r=-1;for(let o=n.length-1;o>=0;o--)if(
|
|
115
|
-
`)}function
|
|
116
|
-
`))
|
|
117
|
-
`).slice(-
|
|
118
|
-
`);return
|
|
85
|
+
`});import{execFile as E0}from"node:child_process";import{promisify as lP}from"node:util";import pu from"node:fs";import{randomUUID as I0}from"node:crypto";function Bo(e){return new Promise(t=>setTimeout(t,e))}function du(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 _0(e){return`/tmp/tmux-${e}/default`}function uP({explicit:e,legacyExists:t,runtimeDir:n,uid:r}){if(e)return e;let o=_0(r);return t?o:n?`${n}/bostrat/tmux-default`:o}function Oi(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${Ri}=1`]}function fP(){return pP.flatMap(e=>["-e",`${e}=`])}function Mi(e,t,n){let r=jo(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&&Be(n)&&(s+=` --name ${n}`),s}function jo(e){return/(^|\/|\s)claude(\s|$)/.test(String(e||""))}function Ur(e){return/(^|\/|\s)claude(\s|$)/.test(e)?"claude":/cursor/.test(e)?"cursor":/codex/.test(e)?"codex":/gemini/.test(e)?"gemini":"agent"}function hP(e){let t=String(e||"").trim();return!t||/fake-|\.sh(\s|$)/.test(t)?!1:jo(t)||/(^|\/|\s)codex(\s|$)/.test(t)||/cursor-agent/.test(t)||/(^|\/|\s)gemini(\s|$)/.test(t)}async function $0(e,{loginShell:t=!1,failOpen:n=!1}={}){let r=await R0(e,{loginShell:t});return r===fu?!0:r===Vs?!1:n}async function R0(e,{loginShell:t=!1,timeoutMs:n=tm}={}){if(!e)return Vs;let r=t?process.env.SHELL||"/bin/sh":"sh",o=t?"-lic":"-c";try{return await Me(r,[o,'command -v "$1" >/dev/null 2>&1',"_",e],{timeout:n}),fu}catch(s){return s?.killed||s?.signal?zs:s?.code===1||s?.code===127?Vs:zs}}async function mu(e,{retries:t=wP,sleep:n=gP,probe:r=R0}={}){let o=String(e||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:tm});for(let a=0;s===zs&&a<t.length;a++)await n(t[a]),s=await r(o,{timeoutMs:tm*2});return s}function se(...e){return["-S",Ft,...e]}async function Hr(){if(!pu.existsSync(Ft))return!1;try{return await Me(te,se("list-sessions")),!0}catch(e){let t=e&&e.stderr?String(e.stderr):"";return!/no server running/i.test(t)}}async function Ci(){if(await Hr())return!0;try{pu.mkdirSync(Ft.replace(/\/[^/]+$/,""),{recursive:!0}),await Me(te,se("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await Hr()}function Be(e){return typeof e=="string"&&yP.test(e)}async function ln(){if(!pu.existsSync(Ft))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}|#{session_created}",{stdout:t}=await Me(te,se("list-sessions","-F",e)),n=Date.now();return t.trim().split(`
|
|
86
|
+
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,p,f,w]=r.split("|"),m=Number(i),g=Number(w);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(m)?n-m*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:d||null,agent:p||(l==="1"?"claude":null),alternateOn:f==="1",createdMs:Number.isFinite(g)&&g>0?g*1e3:null}}).filter(r=>!mP.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function xe(e){return(await ln()).find(n=>n.name===e)||null}function xP(e){if(typeof e!="string")return!1;let t=e.trim();return t.length>=1&&t.length<=kP&&bP.test(t)}function hu(e){return e.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function O0(e){try{let{stdout:t}=await Me(te,se("list-sessions","-F","#{session_name}"));return t.split(`
|
|
87
|
+
`).map(n=>n.trim()).includes(e)}catch{return!1}}function AP(e){let t=String(e||"").trim().split(/\s+/)[0]||"";if(t.includes("/"))return null;let n=[],r=s=>/[A-Za-z0-9_]/.test(s);for(let s=1;s<=t.length-3;s++)r(t[s-1])===r(t[s])&&n.push(t.slice(s));if(!n.length)return null;let o=n.map(s=>s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return new RegExp(`\\b(?:${o.join("|")})`)}async function TP(e,t){let n=Date.now()+Ni(),r=AP(t),o=!1,s=0,a=0;for(;;){if(await Bo(vP),s+=1,!await O0(e))return;let i=await De(e,{lines:15}),c=!o&&SP.test(i)&&!!r?.test(i),l=!o&&!!r?.test(i);if(!c&&!l&&s>1&&!await or(e)){if(a+=1,a>1)return}else c||(a=0);if(c){o=!0,await fe({flow:"spawn",phase:"launch-retry",sessionName:e,agent:Ur(t),promptHead:t.slice(0,120)}).catch(()=>{}),await Me(te,se("send-keys","-t",e,"C-u")),await Me(te,se("send-keys","-t",e,"-l",t)),await Me(te,se("send-keys","-t",e,"Enter"));continue}if(Date.now()>n){await fe({flow:"spawn",phase:"launch-failed",sessionName:e,agent:Ur(t),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function On(e,t=dP,n={}){if(!xP(e))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=e.trim(),o=hu(r);if(!Be(o))throw new Error("resulting tmux session name is invalid");if(!await Ci())throw new Error(`host tmux server not running on ${Ft} and auto-start failed \u2014 start one: ${te} -S ${Ft} new-session -d -s keeper "sleep 2147483647"`);if(await O0(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||nm,a=n.agent||Ur(s),i=!!n.resumeSessionId,c=n.resumeSessionId||I0(),l=i?s:Mi(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),d=a==="claude"&&jo(s),p=n.firstRun===!0;p||(d&&await Cr(t),a==="codex"&&await Ps(t),a==="cursor"&&await Ds(t),await Xk(t),n.freshWorkspace!==!0&&await iu(t));let f=d&&!p?Ll():null,w=mk(),m=["-e",`${er}=${w}`];m.push(...S0({nodeUrl:A0()})),m.push(...fP()),f&&m.push("-e",`ANTHROPIC_API_KEY=${f}`),d&&m.push(...Oi());try{await Me(te,se("new-session","-d","-s",o,"-c",t,...m))}catch(g){throw du(f?du(g,f):g,w)}return await Lo(o),await Me(te,se("set-option","-t",o,"@bostrat-session","1")),await Me(te,se("set-option","-t",o,"@bostrat-title",r)),await Me(te,se("set-option","-t",o,"@bostrat-sid",c)),await Me(te,se("set-option","-t",o,"@bostrat-agent",a)),await Me(te,se("send-keys","-t",o,l,"Enter")),hP(l)&&TP(o,l).catch(()=>{}),{name:o,title:r,cwd:t,sessionId:c,agent:a,pinned:u,hookToken:w}}async function Ct(e){if(!Be(e))throw new Error("invalid session name");await Me(te,se("kill-session","-t",e))}async function M0(e,t){if(Be(e))try{let n=t?se("set-option","-t",e,"@bostrat-sid",t):se("set-option","-u","-t",e,"@bostrat-sid");await Me(te,n)}catch{}}async function De(e,{lines:t=40}={}){if(!Be(e))return"";try{let n=se("capture-pane","-t",e,"-p");Number.isFinite(t)&&t>0&&n.push("-S",`-${Math.floor(t)}`);let{stdout:r}=await Me(te,n);return String(r||"")}catch{return""}}async function C0(e){if(!Be(e))return[];try{let{stdout:t}=await Me(te,se("list-panes","-s","-t",e,"-F","#{pane_pid}"));return String(t||"").trim().split(`
|
|
88
|
+
`).map(n=>Number(n.trim())).filter(n=>Number.isFinite(n)&&n>0)}catch{return[]}}async function N0(e){if(!Be(e))return 0;try{let{stdout:t}=await Me(te,se("list-clients","-t",e,"-F","1"));return String(t||"").trim().split(`
|
|
89
|
+
`).filter(Boolean).length}catch{return 0}}async function Lo(e,{cols:t=_P,rows:n=$P}={}){if(!Be(e))return!0;if(await N0(e))return!1;try{let r=await P0(e);if(r.cols===t&&r.rows===n)return!0;await Me(te,se("resize-window","-t",e,"-x",String(t),"-y",String(n))),await Me(te,se("set-window-option","-t",e,"window-size","latest")),await Bo(80)}catch{}return!0}async function RP(e){if(!Be(e))return 0;try{let{stdout:t}=await Me(te,se("display-message","-t",e,"-p","#{pane_pid}"));return Number(String(t||"").trim())||0}catch{return 0}}async function or(e){let t=await RP(e);if(!t)return!1;try{return await Me("pgrep",["-P",String(t)]),!1}catch(n){return n?.code===1}}async function P0(e){if(!Be(e))return{cols:0,rows:0};try{let{stdout:t}=await Me(te,se("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 wu(e,{cols:t=EP,rows:n=IP}={}){if(Be(e)&&!await N0(e))try{let r=await P0(e),o=Math.max(r.cols,t),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await Me(te,se("resize-window","-t",e,"-x",String(o),"-y",String(s))),await Me(te,se("set-window-option","-t",e,"window-size","latest")),await Bo(80))}catch{}}async function un(e){return await wu(e),De(e,{lines:Qe})}async function Ke(e,t,{settleMs:n=40,kind:r}={}){if(!Be(e))throw new Error("invalid session name");let o=(Array.isArray(t)?t:[t]).filter(s=>D0.has(s));for(let s=0;s<o.length;s++)await Me(te,se("send-keys","-t",e,o[s])),n>0&&s<o.length-1&&await Bo(n);return fe({flow:"tmux",phase:"send-keys",sessionName:e,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function sm(e,t,{settleMs:n=40,kind:r}={}){if(!Be(e))throw new Error("invalid session name");let o=(Array.isArray(t)?t:[]).filter(s=>s&&(typeof s.key=="string"&&D0.has(s.key)||typeof s.text=="string"&&s.text.length>0));for(let s=0;s<o.length;s++){let a=o[s];a.text!=null?await Me(te,se("send-keys","-t",e,"-l",a.text)):await Me(te,se("send-keys","-t",e,a.key)),n>0&&s<o.length-1&&await Bo(n)}return fe({flow:"tmux",phase:"send-sequence",sessionName:e,kind:r||"sequence",steps:o.map(s=>s.text!=null?`\u2039text:${s.text.length}\u203A`:s.key).join(" ")}).catch(()=>{}),o.length}async function Js(e,t,{kind:n}={}){if(!Be(e))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(t)||0));return r>0&&await Me(te,se("send-keys","-t",e,"-N",String(r),"BSpace")),fe({flow:"tmux",phase:"send-keys",sessionName:e,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function yu(e,t,{perCharDelayMs:n=120,kind:r}={}){if(!Be(e))throw new Error("invalid session name");let o=[...String(t)];for(let s of o)await Me(te,se("send-keys","-t",e,"-l",s)),n>0&&await Bo(n);fe({flow:"tmux",phase:"type",sessionName:e,kind:r||"type",textLen:o.length,textHead:String(t).slice(0,120)}).catch(()=>{})}function OP(e,t){return new Promise((n,r)=>{let o=E0(te,se(...e),(s,a,i)=>{s?r(s):n({stdout:a,stderr:i})});o.stdin.on("error",()=>{}),o.stdin.end(t)})}async function MP(e,t){let n=`bostrat-${I0().slice(0,8)}`;await OP(["load-buffer","-b",n,"-"],t),await Me(te,se("paste-buffer","-p","-d","-b",n,"-t",e))}async function at(e,t,{enter:n=!0,submitDelayMs:r=0,kind:o,paste:s=!1}={}){if(!Be(e))throw new Error("invalid session name");if(typeof t=="string"&&t.length>0&&(s?await MP(e,t):await Me(te,se("send-keys","-t",e,"-l",t))),n){let a=Number(r);Number.isFinite(a)&&a>0&&await Bo(a),await Me(te,se("send-keys","-t",e,"Enter"))}fe({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,...s?{paste:!0}:{}}).catch(()=>{})}var Me,te,T0,Ft,dP,nm,Ri,pP,rm,mP,fu,Vs,zs,tm,wP,gP,om,yP,bP,kP,Ni,vP,SP,EP,IP,Qe,_P,$P,D0,gu,ht=q(()=>{pi();Ul();Hl();vi();cu();Rn();Ns();em();Me=lP(E0);te=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";T0=typeof process.getuid=="function"?process.getuid():0,Ft=uP({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return pu.existsSync(_0(T0))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:T0}),dP=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",nm=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",Ri="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";pP=Object.freeze(["BOSTRAT_PLANE_URL","BOSTRAT_PLANE_TOKEN"]);rm="bostrat-usage-probe",mP=new Set([rm]);fu="present",Vs="absent",zs="unknown",tm=5e3,wP=[250,1e3,3e3],gP=e=>new Promise(t=>setTimeout(t,e));om={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"};yP=/^[A-Za-z0-9_.-]{1,64}$/;bP=/^[A-Za-z0-9 ._↻-]+$/,kP=64;Ni=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,vP=1e3,SP=/not found|Unknown command/i;EP=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,IP=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,Qe=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,_P=Number(process.env.BOSTRAT_BOX_COLS)||120,$P=Number(process.env.BOSTRAT_BOX_ROWS)||40;D0=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);gu=1600});import Pi from"node:fs";import jP from"node:fs/promises";import Ys from"node:path";import LP from"node:os";function im(e){return e.replace(/[^A-Za-z0-9]/g,"-")}function cm(e){let t=Ys.join(am,im(e)),n;try{n=Pi.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=Ys.join(t,o);try{let{mtimeMs:a}=Pi.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function dn(e,t){if(!t)return null;let n=Ys.join(am,im(e),`${t}.jsonl`);return Pi.existsSync(n)?n:null}function W0(e){if(!e)return[];let t=Ys.join(am,im(e)),n;try{n=Pi.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=Pi.statSync(Ys.join(t,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function Nt(e,t=131072){let n=await jP.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 UP(e){let t=e.split(`
|
|
90
|
+
`);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 Di(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=dn(e,t),o=!0,!r)return null}else if(n){if(r=cm(e),o=!1,!r)return null}else return null;let s;try{s=UP(await Nt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(H0,s.model),i=H0[s.model]||FP;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 H0,FP,am,pn=q(()=>{H0={"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},FP=2e5,am=process.env.BOSTRAT_CLAUDE_PROJECTS||Ys.join(LP.homedir(),".claude","projects")});function HP(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===`
|
|
91
|
+
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function Bi(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 ji(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 je(e,t=new Set){let n=[];for(let r=1;r<e.length;r++){let o=e[r];if(o==="<<")break;if(K0(o)){t.has(o)&&r++;continue}n.push(o)}return n}function V0(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 GP(e){let t=V0(e),n=bt(je(e).find(r=>!/^\d+(,\d+)?p$/.test(r)));return t&&n?t.from===t.to?`Read line ${t.from} of ${Je(n)}`:`Read lines ${t.from}\u2013${t.to} of ${Je(n)}`:e.includes("-i")&&n?`Edit ${Je(n)}`:null}function lm(e){if(e.includes("--files")){let s=bt(je(e,q0)[0]);return s&&s!=="."?`List files in ${Xs(s)}`:"List files"}let t=je(e,q0),n=t[0];if(!n)return null;let r=bt(t.slice(1).find(s=>s!==".")),o=Zs(n,40);return r?`Search for \u201C${o}\u201D in ${Xs(r)}`:`Search for \u201C${o}\u201D`}function um(e){let t=je(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 dm(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=je(e)[0];return n?`Run ${Je(n)}`:null}function KP(e){let t=je(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 G0(e){return e.find(t=>/^https?:\/\//.test(t))||null}function JP(e){let t=ji(Bi(e));if(!t.length)return null;if(/^(ba|z)?sh$/.test(Je(t[0]))&&(t[1]==="-lc"||t[1]==="-c")&&t[2])return pm(t[2]);let n=Je(t[0]);if(zP.has(n))return null;if(t.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=VP[n];return r?r(t.map((o,s)=>s===0?n:o)):null}function pm(e){let t=String(e||"").trim();if(!t)return null;let n=t.indexOf("<<");if(n!==-1){let i=t.indexOf(`
|
|
92
|
+
`,n);i!==-1&&(t=t.slice(0,i))}let r=HP(t);for(let i=0;i<r.length-1;i++){let c=ji(Bi(r[i])),l=ji(Bi(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(Je(c[0]))&&Je(l[0])==="sed"){let u=V0(l),d=je(c)[0];if(u&&d)return Zs(u.from===u.to?`Read line ${u.from} of ${Je(d)}`:`Read lines ${u.from}\u2013${u.to} of ${Je(d)}`,88)}}let o=[];for(let i of r){let c=JP(i);c&&o.push({d:c,head:Je(ji(Bi(i))[0]||""),sub:je(ji(Bi(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":Zs(s?s.d:o[0].d,88)}var Zs,bt,Je,Xs,K0,q0,WP,qP,VP,zP,z0=q(()=>{Zs=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,bt=e=>e&&!/[$`]/.test(e)?e:null,Je=e=>e.replace(/\/+$/,"").split("/").pop()||e,Xs=e=>{let t=e.replace(/\/+$/,"").split("/").filter(Boolean);return t.length>3?t.slice(-2).join("/"):e},K0=e=>e.startsWith("-"),q0=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);WP={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 ${Je(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"},qP={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"};VP={git:e=>{let t=je(e,new Set(["-C","-c"])),n=WP[t[0]];return n?n(t,e):null},gh:e=>{let t=je(e),n=qP[t[0]];return n?n(t):null},sed:GP,rg:lm,grep:lm,egrep:lm,nl:e=>{let t=bt(je(e)[0]);return t?`Read ${Je(t)}`:null},cat:e=>{let t=bt(je(e)[0]);return t?`Read ${Je(t)}`:null},head:e=>{let t=bt(je(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the start of ${Je(t)}`:null},tail:e=>{let t=bt(je(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the end of ${Je(t)}`:null},wc:e=>{let t=bt(je(e)[0]);return t?`Count lines in ${Je(t)}`:"Count input lines"},ls:e=>{let t=bt(je(e)[0]);return t&&t!=="."?`List files in ${Xs(t)}`:"List files"},find:e=>bt(e[1])&&!K0(e[1])&&e[1]!=="."?`Find files in ${Xs(e[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:e=>{let t=bt(je(e)[0]);return t?`Create directory ${Xs(t)}`:null},touch:e=>{let t=bt(je(e)[0]);return t?`Create ${Je(t)}`:null},rm:e=>{let t=bt(je(e)[0]);return t?`Delete ${Xs(t)}`:null},mv:e=>{let t=bt(je(e)[0]);return t?`Move ${Je(t)}`:null},cp:e=>{let t=bt(je(e)[0]);return t?`Copy ${Je(t)}`:null},chmod:e=>{let t=bt(je(e)[1]);return t?`Change permissions on ${Je(t)}`:null},diff:e=>{let t=je(e);return t.length>=2?`Compare ${Je(t[0])} and ${Je(t[1])}`:null},curl:e=>{let t=G0(e);return t?`Fetch ${Zs(t,56)}`:"Fetch a URL"},wget:e=>{let t=G0(e);return t?`Fetch ${Zs(t,56)}`:"Fetch a URL"},npm:um,pnpm:um,yarn:um,npx:e=>{let t=je(e)[0];return t?`Run ${t}`:null},node:e=>dm(e,"Node"),python:e=>dm(e,"Python"),python3:e=>dm(e,"Python"),make:e=>{let t=je(e)[0];return t?`Run make ${t}`:"Run make"},systemctl:KP,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=je(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=je(e)[0];return t?`Locate ${t}`:null},command:e=>e[1]==="-v"&&e[2]?`Locate ${e[2]}`:null,fly:e=>{let t=je(e)[0];return t==="deploy"?"Deploy to Fly":t?`Run fly ${t}`:null}},zP=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"])});function Qs(e){let t=e.split(`
|
|
93
|
+
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function Wr(e,t=""){let n=t.trim();return n||pm(e)||Qs(e)}function fm(e,t){if(e){if(Array.isArray(e)){for(let n of e)fm(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&mm.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))fm(n,t)}}}async function J0(e,{maxBytes:t=1048576,limit:n=400}={}){let r;try{r=await Nt(e,t)}catch{return[]}let o=[];for(let s of r.split(`
|
|
94
|
+
`)){let a=s.trim();if(!a||a[0]!=="{")continue;let i;try{i=JSON.parse(a)}catch{continue}let c=i.message;if(!c)continue;let l=[];if(fm(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:Wr(d.command,d.description),ts:u})}return o.slice(-n)}function Y0(e,t){let n=a=>a?.box??null,r=new Map;for(let a of t||[]){if(a.role!=="assistant"||!(a.text||"").trim())continue;let i=n(a);r.has(i)||r.set(i,[]),r.get(i).push(a)}let o=new Map;for(let a of e||[]){let i=n(a);o.has(i)||o.set(i,[]),o.get(i).push(a)}let s=[];for(let[a,i]of o)s.push(...YP(i,r.get(a)||[]));return s.sort((a,i)=>(a.ts||0)-(i.ts||0))}function YP(e,t){let n=[...t].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((w,m)=>m>=s&&(w.ts||0)>(l.ts||0)),d=u===-1?r.length:u,p=r.slice(s,d);if(s=d,!p.length)continue;let f=p.map(w=>({...w,n:i(w)}));o.push({id:`batch-${l.ts}-${l.box}`,anchorTs:l.ts,anchorBox:l.box,agent:l.agent,boxRole:l.boxRole,commands:f,ts:Math.max(...f.map(w=>w.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 mm,ea=q(()=>{pn();z0();mm=new Set(["Bash"])});import X0 from"node:fs";import wm from"node:fs/promises";import hm from"node:path";import XP from"node:os";async function Z0(e){try{return await wm.realpath(e)}catch{return e}}function QP(e=ZP){let t=[],n=r=>{let o;try{o=X0.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=hm.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:X0.statSync(a).mtimeMs})}catch{}}}};return n(e),t.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function eD(e,t=1048576){let n;try{n=await wm.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(`
|
|
95
|
+
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=t)break}return o}finally{await n.close()}}async function tD(e){let t=await eD(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 Fo(e,t){if(!e&&!t)return null;let n=QP();if(t){let o=n.find(s=>s.id===t);return o||null}let r=await Z0(e);for(let o of n){let s=await tD(o.file);if(s&&(s===e||await Z0(s)===r))return o}return null}async function Q0(e,t,{limit:n=80,maxBytes:r=524288}={}){let o=await Fo(e,t);if(!o)return[];let s;try{s=await Nt(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,d=null;for(let p of s.split(`
|
|
96
|
+
`)){let f=p.trim();if(!f||f[0]!=="{")continue;let w;try{w=JSON.parse(f)}catch{continue}if((w.type==="turn_context"&&w.payload?.model||w.type==="session_meta"&&w.payload?.model)&&(i=w.payload.model),w.type==="event_msg"){let b=w.payload?.type;b==="task_started"?(l=!0,u=!1,d=w.payload?.turn_id||null):b==="task_complete"&&l&&(l=!1,!u&&w.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:Uo,sourceId:w.payload?.turn_id||d||void 0,ts:w.timestamp?Date.parse(w.timestamp):null,model:i}));continue}if(w.type!=="response_item")continue;let m=w.payload;if(!m)continue;if((m.type==="function_call"||m.type==="custom_tool_call")&&m.name){u=!0,gm.has(m.name)||c.push(m.name);continue}if(m.type!=="message"||m.role!=="assistant")continue;u=!0;let g=rD(m.content),y=c.length?c.slice():null;c=[],!(!g&&!y)&&a.push({role:"assistant",text:g,tools:y||void 0,ts:w.timestamp?Date.parse(w.timestamp):null,model:i})}if(i)for(let p of a)p.model||(p.model=i);return a.slice(-n)}function oD(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 sD(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+=`
|
|
97
|
+
`;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 aD(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=sD(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function ex(e,t,{limit:n=400,maxBytes:r=524288}={}){let o=await Fo(e,t);if(!o)return[];let s;try{s=await Nt(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
|
|
98
|
+
`)){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 d=[];if(u.type==="function_call"&&Li.has(u.name)){let f;try{f=JSON.parse(u.arguments||"{}")}catch{continue}let w=oD(f);w&&(d=[w])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=aD(u.input));if(!d.length)continue;let p=u.call_id||u.id||null;d.forEach((f,w)=>{a.push({id:p?w?`${p}:${w}`:p:null,tool:u.name,command:f,description:"",summary:Wr(f),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function tx(e,t){let n=await Fo(e,t);if(!n)return null;try{return Math.round((await wm.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var ZP,nD,rD,Li,gm,Uo,Ho=q(()=>{pn();ea();ZP=process.env.BOSTRAT_CODEX_SESSIONS||hm.join(process.env.CODEX_HOME||hm.join(XP.homedir(),".codex"),"sessions");nD=new Set(["output_text","text"]),rD=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&typeof t.text=="string"&&nD.has(t.type)).map(t=>t.text).join(`
|
|
99
|
+
`).trim():"";Li=new Set(["exec_command","shell","exec"]),gm=new Set(["wait","write_stdin"]),Uo="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 Fi({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&nx.has(r))return nx.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 rx(e){if(!e||e.role!=="assistant")return null;if(e.apiError){let t=e.apiError;return{cls:t.cls||Fi({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===Uo?"other":Fi({text:t}),code:null,status:null,requestId:null}}return null}function ox(e,{codexFailedText:t=null}={}){let n=String(e?.text||"").trim();return n?e?.model==="<synthetic>"?/^No response requested\.$/.test(n)?null:{cls:Fi({text:n}),code:null,status:iD(n),requestId:null,text:n}:t&&n===t?{cls:"other",code:null,status:null,requestId:null,text:n}:null:null}function iD(e){let t=/^API Error:\s*(\d{3})\b/.exec(e);return t?Number(t[1]):null}var N3,nx,xu=q(()=>{Ho();N3=Object.freeze(["entitlement","auth","rate_limit","overloaded","server","other"]),nx=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 cD(e,t){return!e||!t?null:dn(e,t)}function lD(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(`
|
|
100
|
+
`).trim():""}function uD(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 dD(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&t.type==="tool_result")}function ym(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 sx(e,t){if(!e.uuid)return null;let n=ym(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 bm(e,{maxBytes:t=524288,limit:n=80}={}){let r;try{r=await Nt(e,t)}catch{return[]}let o=[],s=new Map,a=new Map;for(let i of r.split(`
|
|
101
|
+
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type==="attachment"){let y=l.attachment;y?.type==="queued_command"&&y.commandMode==="task-notification"&&typeof y.prompt=="string"&&y.prompt.trim()&&o.push({role:"user",text:y.prompt.trim(),ts:l.timestamp?Date.parse(l.timestamp):null,model:null});continue}let u=l.message;if(!u)continue;let d=u.role||l.type;if(d!=="user"&&d!=="assistant"||d==="assistant"&&u.model==="<synthetic>"&&!l.isApiErrorMessage)continue;if(d==="assistant"&&Array.isArray(u.content))for(let y of u.content){let b=y?.type==="tool_use"?y.input?.file_path||y.input?.path:null;typeof b=="string"&&pD.test(b)&&s.set(y.id,b.split("/").pop()),y?.type==="tool_use"&&y.name==="Skill"&&y.id&&a.set(y.id,String(y.input?.skill||"").trim().replace(/^\//,"")||null)}if(dD(u.content)){let y=sx(l,s);y&&o.push({role:"assistant",text:"",images:y,ts:l.timestamp?Date.parse(l.timestamp):null,model:null});continue}let p=lD(u.content),f=d==="assistant"?uD(u.content):null,w=d==="user"?sx(l,s):null;if(!p&&!f&&!w)continue;let m=d==="user"&&l.isMeta&&typeof l.sourceToolUseID=="string"&&l.sourceToolUseID?{toolUseId:l.sourceToolUseID,skill:a.get(l.sourceToolUseID)??null}:null,g=d==="assistant"&&l.isApiErrorMessage?{code:typeof l.error=="string"?l.error:null,status:Number(l.apiErrorStatus)||null,requestId:l.requestId||null,cls:Fi({error:l.error,status:l.apiErrorStatus,text:p})}:null;o.push({role:d,text:p,tools:f||void 0,images:w||void 0,injected:m||void 0,ts:l.timestamp?Date.parse(l.timestamp):null,model:u.model||null,apiError:g||void 0})}return o.slice(-n)}async function ax(e,t,{turns:n=16}={}){let r=cD(e,t);if(!r)return"";let o=await bm(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}:** ${fD(s.text||c,700)} ${i}`.trim()}).join(`
|
|
102
|
+
|
|
103
|
+
`):""}var pD,fD,vu=q(()=>{xu();pn();pD=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;fD=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e});import mD from"node:fs/promises";import{execFile as hD}from"node:child_process";import{promisify as wD}from"node:util";function ux(e){xm=typeof e=="function"?e:null}function vm(e){let t=String(e||""),n=t.indexOf("/lib/node_modules/");return n>0?t.slice(0,n):null}function dx({sessionName:e,from:t,to:n}={}){let r=String(n||"").trim();!r||ix.has(r)||(ix.add(r),cx=cx.then(()=>yD({sessionName:e,from:t,to:r})).catch(()=>{}))}async function yD({sessionName:e,from:t,to:n}){await fe({flow:"codex-update",phase:"start",sessionName:e,from:t,to:n}).catch(()=>{});try{let r=(await km("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await mD.realpath(r),s=vm(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await km("npm",["install","-g","@openai/codex"],{env:a,timeout:gD});let i=(await km(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await fe({flow:"codex-update",phase:"done",sessionName:e,from:t,to:n,version:i}).catch(()=>{}),await lx({sessionName:e,from:t,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await fe({flow:"codex-update",phase:"failed",sessionName:e,from:t,to:n,error:o}).catch(()=>{}),await lx({sessionName:e,from:t,to:n,status:"failed",error:o})}}async function lx(e){if(xm)try{await xm(e)}catch{}}var km,gD,ix,cx,xm,Su=q(()=>{Rn();km=wD(hD),gD=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,ix=new Set,cx=Promise.resolve(),xm=null});function kD(e,{provider:t,box:n}){let r=String(e).replace(/\r/g,"").split(`
|
|
104
|
+
`);if(!px.test(e))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(bD);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"&&px.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 qr(e){let t=String(e??"").replace(/\r/g,""),n=t.split(`
|
|
105
|
+
`);for(let r=n.length-1;r>=0;r--)if(hx.test(n[r]))return n.slice(r).join(`
|
|
106
|
+
`);return t}function xD(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 vD(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 SD(e,t=""){let n=qr(e);if(Ui.test(n)||!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n))return null;let r=n.split(`
|
|
107
|
+
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(fx);if(!u)continue;s<0&&(s=l);let d="";for(let p=l+1;p<Math.min(l+3,r.length);p++){let f=r[p].trim();if(f){if(fx.test(r[p])||/^Press enter/.test(f))break;d=f;break}}o.push({n:Number(u[2]),cursor:!!u[1],label:u[3].trim(),desc:d})}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&&!hx.test(r[l])&&xD(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=vD(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function ED(e,t=""){let n=qr(e);if(Ui.test(n)||!AD.test(n))return null;let r=n.trimEnd().split(`
|
|
108
|
+
`).pop()||"";return TD.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 _D(e,t=""){let n=qr(e);if(Ui.test(n)||!mx.test(n))return null;let r=n.split(`
|
|
109
|
+
`),o=r.findIndex(i=>mx.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(ID);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 $D(e,t=""){let n=qr(e);return Ui.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 RD(e,t=""){let n=qr(e);return Ui.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 OD(e,t=""){let n=String(e);if(!/Press any key to log in/.test(n)||Sm.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
110
|
+
`).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 CD(e,t=""){let n=String(e);if(!MD.test(n)||Sm.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
111
|
+
`),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 ND(e,t=""){let n=String(e);return!/Scan this QR code to log in/.test(n)||Sm.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 wx(e,t,n=""){if(e!=="cursor"&&e!=="codex")return null;if(e==="codex"){let r=ED(t,n)||_D(t,n)||$D(t,n)||RD(t,n)||SD(t,n);if(r)return r}if(e==="cursor"){let r=CD(t,n)||ND(t,n)||OD(t,n);if(r)return r}return kD(t,{provider:e,box:n})}function gx(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 bD,px,Ui,hx,fx,AD,TD,mx,ID,Sm,MD,Au=q(()=>{bD=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,px=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;Ui=/(^|\n)\s*›/,hx=/^\s*Welcome to Codex\b|^\s*(?:│\s*)?>_ OpenAI Codex\b/;fx=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;AD=/Successfully logged in\./,TD=/^\s*Press Enter to continue\.\s*$/;mx=/Enter this one-time code/,ID=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;Sm=/Run Everything|Add a follow-up|Plan, search, build anything/;MD=/click this link to log in|Signing in with the browser/});function jD(e){let t=String(e||"").replace(/\r/g,"").split(`
|
|
112
|
+
`),n=-1;for(let c=t.length-1;c>=0;c--)if(t[c].trim()){n=c;break}if(n<0)return!1;let r=t[n];if(DD.test(r)||BD.test(r)){for(let c=n-1;c>=0&&n-c<=64;c--)if(/^\s*›/.test(t[c]))return!0}let s=String(e||""),a=s.split(`
|
|
113
|
+
`).filter(c=>/^\s*›/.test(c)),i=a.some(c=>/^\s*›\s+(?:Ask Codex to do anything|Implement \{feature\})\s*$/.test(c));return a.length>0&&!i&&/esc to interrupt|\bWorking \(/.test(s)}function WD(e){let t=String(e||"");if(!et.codex.block.test(t))return null;let n=qr(t);if(!et.codex.block.test(n))return null;let r=n.match(HD);return{from:r?.[1]||null,to:r?.[2]||null}}function Mn(e){return new Promise(t=>setTimeout(t,e))}function Am(e,t){let n=Date.now(),r=qD();for(let[s,a]of Eu)n-a>=r&&Eu.delete(s);let o=`${e}::${t}`;return Eu.has(o)?!1:(Eu.set(o,n),!0)}function xx(e,t){if(e?.busyScope!=="footer")return t;let n=String(t||"").split(`
|
|
114
|
+
`),r=-1;for(let o=n.length-1;o>=0;o--)if(kx.test(n[o])){r=o;break}return r<0?t:n.slice(r+1).join(`
|
|
115
|
+
`)}function KD(e){let t=0;for(let n of String(e).split(`
|
|
116
|
+
`))kx.test(n)&&(t=Math.max(t,n.trimEnd().length));return t}function Tm(e,t){let n=et[e];if(!n?.busy)return{state:"idle",reason:null};if(Im(e,t))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(t))return{state:"idle",reason:null};if(n.busy.test(xx(n,t)))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(t))return{state:"busy",reason:null};let r=KD(t);return e==="claude"&&r>0&&r<GD?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function Em(e,t){return Tm(e,t).state}function vx(e,t){if(e!=="claude")return!1;let n=String(t||"").split(`
|
|
117
|
+
`).slice(-zD).join(`
|
|
118
|
+
`);return VD.test(n)}function na(e,t){return Em(e,t)==="busy"}function Hi(e,t){return Em(e,t)!=="idle"}function Wo(e,t){let n=et[e];return n?.up?n.up.test(t):!0}function JD(e,t){let n=et[e];return n?Im(e,t)?!1:n.up.test(t)&&Em(e,t)==="idle":!0}function Im(e,t){let n=et[e];if(!n)return!1;let r=e==="codex"?qr(t):t;return n.auth?.test(r)&&!n.up.test(r)?!0:n.block?.test(r)?e==="codex"?!0:!n.up.test(r):!1}function _m(e,t){let n=String(t||"").trim();if(!n)return e;let r=String(e||"").split(`
|
|
119
119
|
`);for(let o=r.length-1;o>=0;o--)if(r[o].includes(n))return r.slice(o+1).join(`
|
|
120
|
-
`);return e}function
|
|
121
|
-
`)?!0:Buffer.byteLength(e,"utf8")>=
|
|
122
|
-
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(
|
|
123
|
-
`).map(o=>o.replace(/\s+$/,"")),n=-1;for(let o=t.length-1;o>=0;o--)if(t[o]){(
|
|
124
|
-
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return
|
|
125
|
-
${o}`.trim()||t.slice(0,2e3)}function
|
|
126
|
-
`):""}async function
|
|
120
|
+
`);return e}function Sx(e){let t=String(e||"").trim();return t?t.slice(0,32):null}function yx(e){let t=new Error(`${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.`);return t.code="cli-not-running",t}async function bx(e,t,n,{bailOnDialog:r=!1,launchEcho:o=null,onReady:s}={}){let a=Date.now()+n,i=0,c=null;for(await Lo(e).catch(()=>{});;){let l=_m(await De(e),o);if(JD(t,l))return await s?.(),await Mn(ta),"up";if(et[t]?.up&&!et[t].up.test(l))if(await or(e)){if(c??=Date.now(),Date.now()-c>=Ni()){let u=yx(t);throw fe({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,idleMs:Date.now()-c,error:u.message}).catch(()=>{}),u}}else c=null;else c=null;if(r&&t==="claude"&&UD.test(l)&&!et.claude.up.test(l))return fe({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"resume-confirm"}).catch(()=>{}),"dialog-bail";if(t==="codex"&&FD.test(l)&&!et.codex.up.test(l)&&i<3){if(!Am(e,"codex-trust")){await Mn(Tu);continue}i+=1,await fe({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"codex-trust",attempt:i}).catch(()=>{}),await at(e,"",{enter:!0,kind:"codex-trust-accept"}),await Mn(ta);continue}if(t==="cursor"&&LD.test(l)&&!et.cursor.up.test(l)&&i<3){if(!Am(e,"cursor-trust")){await Mn(Tu);continue}i+=1,await fe({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i}).catch(()=>{}),await at(e,"a",{enter:!1,kind:"cursor-trust-accept"}),await Mn(ta);continue}if(t==="codex"&&i<3){let u=WD(l);if(u){if(!Am(e,`codex-update:${u.from}->${u.to}`)){await Mn(Tu);continue}i+=1,await fe({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i,update:`${u.from} -> ${u.to}`}).catch(()=>{}),await at(e,"3",{enter:!1,kind:"codex-update-skip"}),dx({sessionName:e,from:u.from,to:u.to}),await Mn(ta);continue}}if(Date.now()>a){if(Im(t,l)){let u=`${t} is blocked on an interactive prompt`;throw t==="codex"?u=et.codex.auth.test(l)?"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"&&et.cursor.auth.test(l)?u="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"&&et.claude.auth.test(l)?u="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"&&et.claude.block.test(l)&&(u='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'),fe({flow:"agent-ready",phase:"blocked",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:u}).catch(()=>{}),new Error(u)}if(await or(e)){let u=yx(t);throw fe({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:u.message}).catch(()=>{}),u}return console.warn(`[agent-ready] ${t} readiness unconfirmed in ${e} after ${n}ms \u2014 driving the box anyway.`),fe({flow:"agent-ready",phase:"timeout",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,paneBlocked:!!et[t]?.block?.test(l),paneUp:!!et[t]?.up?.test(l),paneBusy:!!et[t]?.busy?.test(xx(et[t],l))}).catch(()=>{}),await Mn(ta),"timeout"}await Mn(Tu)}}function Wi({agent:e,agentCmd:t}={}){let n=t?Ur(t):Ur(e||"");return{label:n,realClaude:t?jo(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 Ax({agent:e,agentCmd:t}={}){let{realClaude:n,realCodex:r,realCursor:o}=Wi({agent:e,agentCmd:t});return n||r||o||process.env.BOSTRAT_FAKE_POLL_READY==="1"}function qo({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o,onBlocked:s}={}){let{realCodex:a,realCursor:i}=Wi({agent:n,agentCmd:r});return!t||!a&&!i?null:sr({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o}).then(()=>{},c=>{try{YD(c,{onBlocked:s,agent:n,sessionName:t})}catch{}})}function YD(e,{onBlocked:t=null,agent:n=null,sessionName:r=null}={}){if(typeof t!="function"||!e||e.code==="cli-not-running")return!1;let o=String(typeof e=="string"?e:e.message??"").trim();return o?(t({message:o,agent:n??null,sessionName:r??null}),!0):!1}async function sr({cwd:e,sessionId:t,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=PD(),bailOnDialog:a=!1,launchEcho:i=null,onReady:c}={}){let{label:l,realClaude:u,realCodex:d,realCursor:p}=Wi({agent:r,agentCmd:o});if(u&&e&&await Cr(e),d&&e&&await Ps(e),p&&e&&await Ds(e),(u||d||p)&&n)return await bx(n,l,s,{bailOnDialog:a,launchEcho:i,onReady:c});let f=et[r]?r:et[l]?l:null;return process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&f?await bx(n,f,s,{launchEcho:i}):(await Mn(ta),"settle")}var PD,Tu,ta,DD,BD,et,LD,FD,UD,HD,qD,Eu,GD,kx,VD,zD,ra=q(()=>{ht();pi();Ul();Hl();Su();Au();Rn();PD=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Tu=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,ta=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,DD=/^\s{0,4}[^\s›•■◦╭╰│]\S*(?: \S+){0,3} · \S/,BD=/^\s*tab to queue message\b/;et={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,busyScope:"footer",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:{test:jD},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/}},LD=/Trust this workspace|\[a\]\s*Trust/,FD=/Do you trust the contents of this directory\?[\s\S]*›\s*1\. Yes, continue[\s\S]*Press enter to continue/,UD=/Resume from summary/i,HD=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;qD=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,Eu=new Map;GD=68,kx=/^\s*[─━]{10,}(?: [^─━\n]+ [─━]+)?\s*$/;VD=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,zD=15});function e1(e){return typeof e!="string"||!e?!1:e.includes(`
|
|
121
|
+
`)?!0:Buffer.byteLength(e,"utf8")>=QD}function Cn(e,t){return e==="codex"?{enter:!0,submitDelayMs:XD,...e1(t)?{paste:!0}:{}}:e==="claude"?{enter:!0,submitDelayMs:ZD}:{enter:!0}}var XD,ZD,QD,qi=q(()=>{XD=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),ZD=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),QD=(()=>{let e=Number(process.env.BOSTRAT_CODEX_PASTE_MIN_BYTES??512);return Number.isFinite(e)&&e>=0?e:512})()});function Gi(e,t){if(e==="codex")return s1(t);if(e!=="claude")return null;let n=String(t).replace(/\r/g,"").split(`
|
|
122
|
+
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(Tx.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(Tx.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(t1);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 s1(e){let t=String(e).replace(/\r/g,"").split(`
|
|
123
|
+
`).map(o=>o.replace(/\s+$/,"")),n=-1;for(let o=t.length-1;o>=0;o--)if(t[o]){(n1.test(t[o])||r1.test(t[o]))&&!$m.test(t[o])?n=o:$m.test(t[o])&&/\[Pasted Content \d+ chars\]/.test(t[o])&&(n=o+1);break}if(n<=0)return null;let r=[];for(let o=n-1;o>=0&&n-o<=60;o--){let s=t[o],a=s.match($m);if(a){r.unshift(a[1]);let i=r.map(c=>c.trim()).filter(Boolean);return i.length?i.join(" "):null}if(o1.test(s))return null;r.unshift(s)}return null}function Rm(e,t){let n=String(t||"");if(!/\[Pasted Content \d+ chars\]/.test(n))return!1;let r=0,o=Ue(n.replace(a1,(i,c)=>(r+=Number(c)," "))),s=[...String(e||"")].length;return o&&!Ue(e).includes(o)?!1:r+o.length<=s+Math.max(4,Math.round(s*.05))}function Ue(e){return String(e||"").replace(/\s+/g," ").trim()}function Ex(e,t,{slackMs:n=2e4}={}){let r=Ue(t?.text);if(!r||typeof t?.at!="number")return!1;let o=Array.isArray(e)?e:[],s=o.findIndex(a=>a?.role==="user"&&typeof a.ts=="number"&&a.ts>=t.at-n&&Ue(a.text).includes(r));return s>=0&&s<o.length-1}function Ix(e,t,n,{busy:r=!1}={}){let o=Gi(e,n);return o&&Iu(t,o)||o&&e==="codex"&&Rm(t,o)?"stuck":r&&e!=="codex"||o?"submitted":"unknown"}function Iu(e,t){let n=Ue(e),r=Ue(t).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var Tx,t1,$m,n1,r1,o1,a1,Ki=q(()=>{Tx=/^\s*─{8,}(?: [^─\n]+ ─+)?\s*$/,t1=/^❯[ ]?(.*)$/,$m=/^\s*›[ ]?(.*)$/,n1=/\S+ · \S/,r1=/^\s*(?:\? for shortcuts|tab to queue message)\b/,o1=/^\s*(?:[•■◦✶✻⚠]|╭|╰|│)/;a1=/\[Pasted Content (\d+) chars\](?: #\d+)?/g});async function zi({sessionName:e,agent:t,agentCmd:n=null,text:r,trackId:o=null,flow:s="say",stats:a=null},i={}){let c=(i.now||Date.now)(),l=(L,B,A={})=>(a&&typeof a=="object"&&Object.assign(a,{verdict:L,retries:A.retries??0,ms:(i.now||Date.now)()-c,sawUp:!!A.sawUp,reason:A.reason??null}),B);if(!i1.has(t))return l("skipped",!0);let u=i.waitForUp??Ax({agent:t,agentCmd:n}),d=i.capture||(L=>De(L,{lines:Qe})),p=i.pressEnter||(L=>Ke(L,["Enter"],{kind:"submit-retry"})),f=i.sleep||d1,w=i.now||Date.now,m=i.log||(L=>fe(L).catch(()=>{})),g=i.maxMs??u1(),y=w()+g,b={flow:s,trackId:o,sessionName:e,agent:t,textHead:String(r).slice(0,120)},k=0,S=0,$=0,M=!1;for(await f(Vi[0]);;){let L=await d(e).catch(()=>""),B=Wo(t,L),A=na(t,L),I=Ix(t,r,L,{busy:A});if(I==="submitted")return l("submitted",!0,{retries:k,sawUp:!0});if(u&&(!B||I==="stuck"&&A)){if(w()>=y)return I==="stuck"?(m({...b,phase:"submit-failed",reason:"stuck-at-deadline",retries:k}),l("failed",!1,{retries:k,sawUp:M,reason:"stuck-at-deadline"})):(m({...b,phase:"submit-unverified",reason:M?"cli-down":"cli-never-up",retries:k,waitedMs:g}),l("unverified",!0,{retries:k,sawUp:M,reason:M?"cli-down":"cli-never-up"}));await f(c1);continue}if(M=!0,I==="stuck"){if(k>=l1)return m({...b,phase:"submit-failed",retries:k}),l("failed",!1,{retries:k,sawUp:!0,reason:"stuck"});k+=1,S=0,$=0,await p(e),m({...b,phase:"submit-retry",attempt:k}),await f(Vi[0]);continue}if(S+=1,S>=Vi.length||w()>=y)return l("assumed",!0,{retries:k,sawUp:!0});$+=1,await f(Vi[Math.min($,Vi.length-1)])}}var i1,Vi,c1,l1,u1,d1,Om=q(()=>{ht();ra();Ki();Rn();i1=new Set(["claude","codex"]),Vi=[250,450,700],c1=500,l1=2,u1=()=>{let e=Number(process.env.BOSTRAT_SUBMIT_VERIFY_MAX_MS);return Number.isFinite(e)&&e>0?e:3e4},d1=e=>new Promise(t=>setTimeout(t,e))});function _x(e){return String(e||"").replace(/[\u2018\u2019\u201A\u02BC\u0060\u00B4]/g,"'").replace(/[\u201C\u201D\u201E]/g,'"')}function ar(e){let t=_x(e).toLowerCase();if(!t.trim())return null;let n=null;for(let r of p1){let o=0;for(let s of f1[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function $x(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))m1.has(r)&&(t+=o||0),h1.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function Ji(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," ")}function Rx(e){let t=_x(e).trim();return t?g1.test(t)?!0:t.split(/\s+/).length<=w1:!1}var p1,f1,m1,h1,w1,g1,_u=q(()=>{p1=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],f1={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 (it |this |that |them )?out\b/,/\btalk (me |us )?through\b/,/\b(give|show|tell) me\b/,/\bwhat about\b/,/\bwhy\b/,/\btake a look\b/,/\bwalk (me |us )?through\b/,/\bexplain\b/,/\bdo we (still |even )?need\b/,/\bis there (a|an|any)\b/,/\bhow often\b/,/\bdo we (currently|already)\b/,/\bwould it be (valuable|useful|worth)\b/,/\bis it possible\b/,/\bwhat are the odds\b/,/\bdetermine (what|which|whether|how|if)\b/,/\bwhat (does|do|would|could|should|will) .{1,60}\blook like\b/,/\bhow (is|are) (our|the|this|that|your|my) .{1,50}\b(going|doing|performing|trending)\b/,/\bhow (thorough|far along|complete|accurate|reliable|good) are we\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/,/\bkeeps? getting\b/,/\bdoesn'?t (seem|look) right\b/,/\bseems? (wrong|off|broken)\b/,/\b(too slow|slower than|much slower)\b/,/\bfail(ed|ing|s)\b/,/\b(another|one other|one more|next) (thing|issue|bug|problem) i (found|noticed|spotted|saw)\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/,/\bupdate (the |this |that |my |our )?[\w-]+( [\w-]+){0,3} to\b/,/\bset up\b/,/\b(can|could) we (also |maybe )?(tag|allow for|support|enable)\b/,/\bnow needs to be\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/,/\bpropose (the |a |these |those |new )?loops?\b/],"docs-writing":[/\bdocs?\b/,/\bdocument/,/\bmemo\b/,/\breadme\b/,/\bwrite-?up\b/,/\bchangelog\b/,/\bclaude\.md\b/,/\bblog/,/\bproofread/,/\bcopy(edit|writ)/,/\b(linked[ -]?in|social( media)?) post\b/,/\b(framed|phrased|worded) as\b/,/\bwhat'?s a good (response|reply|answer)\b/],"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/,/\b(unzip|decompress|untar)\b/]};m1=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),h1=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);w1=12,g1=/^\s*(ok(ay)?|yes|yep|yeah|sure|go ahead|go|proceed|continue|next|begin|start|do it|do that|try again|try once more|again|ship it|perfect|great|good|nice|thanks|lock(ed)? (it |that |those )?in|option [a-z]\b|where are we|what'?s the status)\b/i});function Nx(e){let t=Ji(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||y1.has(r)||n.push(r);return n}function b1(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(Cx(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(Cx(n,"assistant",o))}}return t}function Cx(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 Yi(e){let t=b1(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=Nx(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 p=n.get(u);p||n.set(u,p=new Map),p.set(i,d)}});let s=t.length;return{chunks:t,postings:n,lengths:r,N:s,avgdl:s?o/s:0}}function $u(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(Nx(t))];if(!i.length)return[];let c=new Map;for(let u of i){let d=e.postings.get(u);if(!d)continue;let p=d.size,f=Math.log(1+(e.N-p+.5)/(p+.5));for(let[w,m]of d){let g=e.lengths[w],y=m+Ox*(1-Mx+Mx*(g/(e.avgdl||1))),b=f*(m*(Ox+1)/y);c.set(w,(c.get(w)||0)+b)}}let l=[];for(let[u,d]of c){let p=e.chunks[u];o&&p.role!==o||s&&!p.tags.includes(s)||a&&p.boxId!==a||l.push({chunk:p,score:d})}return l.sort((u,d)=>d.score-u.score||d.chunk.seq-u.chunk.seq),l.slice(0,r)}var Ox,Mx,y1,Mm=q(()=>{_u();Ox=1.5,Mx=.75,y1=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 Ru from"node:fs/promises";import Px from"node:path";async function k1(e){let t=Px.join(e,".bostrat","events"),n;try{n=await Ru.readdir(t)}catch{return Yi([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await Ru.readFile(Px.join(t,o),"utf8");for(let a of s.split(`
|
|
124
|
+
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return Yi(r)}function x1(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}
|
|
125
|
+
${o}`.trim()||t.slice(0,2e3)}function v1(e,t,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=$u(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(),d=u.length>800?u.slice(0,800)+"\u2026":u,p=`- **${l}:** ${d}`;if(i+p.length>r||(a.push(p),i+=p.length,a.length>=n))break}return a.length?["",`## ${Dx}, 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(`
|
|
126
|
+
`):""}async function Bx(e,t,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await Ru.readFile(t,"utf8");if(a.includes(Dx))return{appended:!1,reason:"already-enriched"};let i=await k1(e);if(!i.N)return{appended:!1,reason:"empty-index"};let c=v1(i,x1(a),{k:r,maxChars:o,exclude:s});return c?(await Ru.appendFile(t,`
|
|
127
127
|
`+c),{appended:!0,chars:c.length,chunks:c.split(`
|
|
128
|
-
- `).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
|
|
129
|
-
`).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
|
|
128
|
+
- `).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 Dx,jx=q(()=>{Mm();Dx="Relevant prior discussion (verbatim, retrieved"});function _1(e){return S1.test(String(e||"").trimStart())}function Ux(e){let t=String(e||"");if(!_1(t.trimStart()))return null;let n=t.replace(A1,"").trim(),r=$1(n),o=[];for(let u of E1){let d=u.from.map(p=>oa(r.get(p))).find(p=>p&&!Lx(p));d&&o.push({h:u.h,text:Xi(d,T1)})}let s=oa(r.get("Current Work"))||oa(r.get("Primary Request and Intent"))||O1(n),a=Mu(s),i=Xi(Ou(Pm(a[0]||"")),I1)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(Xi(Ou(Pm(l)),Nm));for(let u of["Pending Tasks","Optional Next Step"]){let d=Mu(oa(r.get(u)))[0]||"";if(!(!d||Lx(d))){c.push(Xi(`Left to do: ${Ou(Pm(d))}`,Nm));break}}if(!c.length){let u=Mu(oa(r.get("Problem Solving"))||"")[0];u&&c.push(Xi(Ou(u),Nm))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function $1(e){let t=new Map,n=[];Cm.lastIndex=0;for(let r=Cm.exec(e);r;r=Cm.exec(e))n.push({name:R1(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 R1(e){return Fx.find(n=>n.toLowerCase()===e.toLowerCase())||e}function oa(e){return e?String(e).replace(/```[\s\S]*?(?:```|$)/g," ").split(`
|
|
129
|
+
`).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 O1(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return oa(t.split(/\n{2,}/).slice(0,2).join(" "))}function Mu(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 Pm(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 Ou(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function Lx(e){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(e||"").trim())}function Xi(e,t){let n=String(e||"").trim();if(n.length<=t)return n;let r=Mu(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 S1,Fx,Cm,A1,T1,E1,I1,Nm,Hx=q(()=>{S1=/^This session is being continued from a previous conversation that ran out of context\./,Fx=["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"],Cm=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${Fx.join("|")})[:*\t ]*$`,"gim"),A1=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,T1=420,E1=[{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"]}],I1=116,Nm=168});function Gx(e){return Dm.has(String(e||"").replace(/^\//,"").toLowerCase())}function Zi(e){let t=String(e||"").trim();if(!t)return null;let n=Ux(t);if(n)return n;let r=t.match(M1);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(C1);if(o)return{kind:"skill",name:o[1]};let s=t.match(N1);if(s)return{kind:"skill",name:s[1].toLowerCase()};let a=t.match(P1);if(a){let c=a[1].toLowerCase();return Dm.has(c)?{kind:"suppress"}:{kind:"command",command:`/${c}`}}let i=t.match(G1);if(i&&Dm.has(i[1].toLowerCase()))return{kind:"suppress"};if(D1.test(t))return{kind:"suppress"};if(j1.test(t))return{kind:"suppress"};if(B1.test(t))return{kind:"suppress"};if(L1.test(t)){let c=u=>J1(t.match(u)?.[1].trim()||"")||null,l=c(q1);return{kind:"task",summary:c(F1)||"",taskId:c(U1),toolUseId:c(H1),status:c(W1)?.toLowerCase()||null,resultHead:l?l.length>Wx?`${l.slice(0,Wx).trimEnd()}\u2026`:l:null}}return null}function Kx(e){let t=Zi(e?.text);return t||(e?.injected?{kind:"skill",name:e.injected.skill||"skill"}:null)}function Vx(e){let t=String(e||""),n=t.match(K1);if(n)return{kind:"shell-input",command:n[1].trim()};let r=t.match(V1);return r?{kind:"shell-output",stdout:qx(r[1]),stderr:qx(r[2])}:null}function J1(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function zx(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 Jx(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var M1,C1,N1,P1,D1,B1,j1,L1,F1,U1,H1,W1,q1,Wx,Dm,G1,K1,V1,z1,qx,Cu=q(()=>{Hx();M1=/^Base directory for this skill:\s*(\S+)/,C1=/^Run the "([^"]+)" workflow\./,N1=/^#\s*\/([a-z0-9:_-]+)\s*[—–:-]/i,P1=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,D1=/^<local-command-(?:stdout|caveat)>/i,B1=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,j1=/^Continue from where you left off\.$/,L1=/^<task-notification>/i,F1=/<summary>([\s\S]*?)<\/summary>/i,U1=/<task-id>([\s\S]*?)<\/task-id>/i,H1=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,W1=/<status>([\s\S]*?)<\/status>/i,q1=/<result>([\s\S]*?)(?:<\/result>|$)/i,Wx=600,Dm=new Set(["model","clear","compact","login","exit","quit"]);G1=/^\/([a-z0-9:_-]+)$/i;K1=/^\s*<bash-input>([\s\S]*?)<\/bash-input>\s*$/i,V1=/^\s*<bash-stdout>([\s\S]*?)<\/bash-stdout>\s*(?:<bash-stderr>([\s\S]*?)<\/bash-stderr>)?\s*$/i,z1=/\x1b\[[0-9;?]*[ -/]*[@-~]/g,qx=e=>String(e||"").replace(z1,"").replace(/^\n+/,"").replace(/\s+$/,"")});import Y1 from"node:crypto";function eB(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,Xx)){let o=sa(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,X1)){let c=sa(i?.label);if(!c)return null;let l=sa(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<Z1)return null;n.push({question:o,header:sa(r?.header).slice(0,Q1),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function Bm(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;Yx.lastIndex=0;let t=null;for(let o;o=Yx.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=eB(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function jm(e,t,n){let r=Y1.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${e}:ask:${t||"turn"}:${r}`}function Lm(e,{leadBoxId:t,provider:n,isAnswered:r,maxQuestions:o=Xx}={}){if(!Array.isArray(e)||!t||!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||Bm(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=jm(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",custom:!0,dismissable:!0,multiSelect:i[0].multiSelect,questionIndex:0,questionTotal:i.length,questions:i}}function Zx(e,t,n,r){let o=[];(e?.questions||[]).forEach((i,c)=>{let l=(t?.[c]||[]).map(d=>i.options?.[d]?.label).filter(Boolean),u=sa(Array.isArray(r)?r[c]:"");u&&l.push(u),l.length&&o.push({name:i.header||i.question,picks:l.join(", ")})});let s=sa(n),a;return o.length?o.length===1?a=`${o[0].name}: ${o[0].picks}`:a=`Answers:
|
|
130
130
|
${o.map(i=>`- ${i.name}: ${i.picks}`).join(`
|
|
131
131
|
`)}`:a=s,o.length&&s&&(a+=`
|
|
132
|
-
${s}`),a||""}var
|
|
132
|
+
${s}`),a||""}var Xx,X1,Z1,Q1,Yx,sa,Qx,Fm=q(()=>{Xx=4,X1=4,Z1=2,Q1=24,Yx=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,sa=e=>typeof e=="string"?e.trim():"";Qx=`
|
|
133
133
|
When you are BLOCKED on a decision only the operator can make \u2014 an ambiguous
|
|
134
134
|
requirement, an irreversible choice, several valid approaches \u2014 do not bury the
|
|
135
135
|
question in prose. End your message with exactly ONE fenced code block tagged
|
|
@@ -150,89 +150,89 @@ your turn. The operator sees the questions as an interactive card in their chat
|
|
|
150
150
|
They may instead reply in free text \u2014 treat that as the answer. Never use the block for rhetorical questions, progress updates, or
|
|
151
151
|
anything you can decide yourself; keep asking in prose for questions without
|
|
152
152
|
enumerable options.
|
|
153
|
-
`});function Z1(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 Qx(e,t,n){let r=Um(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 Nn(e){let t=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Map,l=new Set,u=new Map,d=new Map,p=(x,h)=>{d.set(x,h),t.push(h)},f=x=>{let h=d.get(x);h&&(h.retired=!0)},w=/^Agent "([\s\S]+)" finished$/,m=new Map;for(let x of e||[]){if(x.type!=="tool.called"||x.source!=="transcript")continue;let h=String(x.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(h))for(let v of h.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let D=v[0].split("/").pop().toLowerCase();m.has(D)||m.set(D,[]),m.get(D).push(x.payload?.ts??x.ts)}}let g=3*6e4,y=20*6e4,b=(x,h)=>!!(x?.name&&m.get(String(x.name).toLowerCase()))?.some(D=>D>=h-g&&D<=h+y);for(let x of e||[])if(x.type==="turn.retracted"){r.add(`${x.boxId}|${x.payload?.saidTs}`);let h=Ue(x.payload?.text);h&&(o.has(x.boxId)||o.set(x.boxId,[]),o.get(x.boxId).push(h))}else if(x.type==="turn.user"&&x.source==="orchestrator"){let h=Ue(x.payload?.text);h&&(s.has(x.boxId)||s.set(x.boxId,new Set),s.get(x.boxId).add(h))}else if(x.type==="job.stalled"&&x.payload?.id)i.set(x.payload.id,{ts:be(x),quietMs:Number(x.payload.quietMs)||null});else if(x.type==="job.started"&&x.payload?.id)a.add(x.payload.id),(x.payload.kind||"agent")==="agent"&&x.payload.name&&l.add(x.payload.name);else if(x.type==="turn.user"&&x.payload?.note?.kind==="task"){let h=x.payload.note,v={status:h.status||null,summary:h.summary||null,resultHead:h.resultHead??null,ts:be(x)};h.toolUseId&&c.set(h.toolUseId,v);let D=String(h.summary||"").match(w);D&&u.set(D[1],v)}let k=new Map,S=new Map,$=null,M=new Map,L=new Map,B=new Map,A=new Map,I=new Map,j=new Map,K=new Map,R=new Map,E=new Map,C=new Map,P=new Map,W=new Map,Y=new Map,me=new Map,_e=new Map,Pe=new Map,ne=(x,h)=>{x.arm.status=h;let v=x.line,D=v.arms.map(Z=>Z.status);v.status=D.includes("armed")?"armed":D.includes("offered")?"offered":D.includes("replaced")?"replaced":"cancelled"},Ve=[],ve=x=>Ve.push(x),ce=x=>{if(!Ve.length)return;let h=be(x);for(let v of Ve){if(v.status!=="open"||v.box&&v.box!==x.boxId||(v.ts??0)>h)continue;let D=t.indexOf(v);D>=0&&t.splice(D,1),v.ts=h,t.push(v)}},re=new Map,Ce=new Map,J=new Map,he=new Map,Fe=new Map,O=new Map,se=new Map,Re=new Map,$t=()=>{for(let x of Re.values())x.questions.length&&t.push(x);Re.clear()},X=null,we=new Map,mt=6e4,nt=new Map,an=(x,h)=>{let v=o.get(x);if(!v?.length)return!1;let D=Ue(h),Z=s.get(x);return v.some(ue=>{if(!D.startsWith(ue))return!1;let ge=Ue(D.slice(ue.length));return!!ge&&!!Z?.has(ge)})};for(let x of e||[]){let h=x.payload||{};switch(x.type){case"turn.user":{if(x.source==="hook"||(Ve.length=0,!String(h.text||"").trim()&&!h.images?.length&&!h.attachments?.length))break;{let v=Gx(h.text);if(v){if(v.kind==="shell-input"){let D={id:x.id,kind:"shell",command:v.command,stdout:null,stderr:null,done:!1,ts:be(x),box:x.boxId,agent:x.provider};t.push(D),we.set(x.boxId,D)}else{let D=we.get(x.boxId);D?(D.stdout=v.stdout||null,D.stderr=v.stderr||null,D.done=!0,we.delete(x.boxId)):t.push({id:x.id,kind:"shell",command:null,stdout:v.stdout||null,stderr:v.stderr||null,done:!0,ts:be(x),box:x.boxId,agent:x.provider})}break}}{let v=h.note||Xi(h.text);if(v){if(v.kind==="task"){if(v.toolUseId&&a.has(v.toolUseId))break;let D=String(v.summary||"").match(w);if(!v.toolUseId&&D&&l.has(D[1]))break}if(v.kind==="command"&&Wx(v.command))break;if(v.kind==="resume-summary"){t.push({id:x.id,kind:"resume-summary",title:v.title||"Context resumed from summary",bullets:v.bullets||[],sections:v.sections||[],rawChars:v.rawChars??null,ts:be(x),box:x.boxId,agent:x.provider});break}v.kind!=="suppress"&&t.push({id:x.id,kind:"event",type:Vx(v),name:v.name??null,command:v.command??null,summary:v.summary??null,status:v.status??null,ts:be(x),box:x.boxId,agent:x.provider});break}}if(r.has(`${x.boxId}|${be(x)}`)||x.source==="transcript"&&an(x.boxId,h.text))break;if(h.kind==="merge-notify"||h.kind==="ship-notify"){t.push({id:x.id,kind:"event",type:h.kind==="ship-notify"?"ship-notified":"merge-notified",text:h.text,ts:be(x),box:x.boxId,agent:x.provider});break}if(h.kind==="mock-picked"||h.kind==="ci-fix"||h.kind==="reconcile"||h.kind==="pr-go"||h.kind==="push-go"||h.kind==="spin-up-view"||h.kind==="prompt-answer"||h.kind==="followup-go"){h.kind==="prompt-answer"&&(X=null);break}for(let v of M.values())v.status="stale";M.clear();for(let v of B.values())v.status="stale";B.clear();for(let v of Y.values())v.rows.some(D=>D.rowStatus==="sent")||(v.status="stale",Y.delete(v.offerId));X&&X.box===x.boxId&&(t.push(Z1(X)),X=null),t.push({id:x.id,kind:"message",role:"user",text:h.text,ts:be(x),box:x.boxId,agent:x.provider,boxRole:h.boxRole,...x.actor==="orchestrator"?{orchestration:h.kind||!0}:{},...h.sourceId?{sourceId:h.sourceId}:{},...h.attachments?.length?{attachments:h.attachments}:{},...h.images?.length?{images:h.images}:{}});break}case"peer.message":{let v=h.from||{};t.push({id:x.id,kind:"event",type:"peer-message",text:h.text,from:v.trackName||v.boxId||v.name||null,fromTrackId:v.trackId||null,ts:be(x),box:x.boxId,agent:x.provider});break}case"turn.agent":{if(h.model==="<synthetic>"&&/^No response requested\.$/.test(String(h.text||"").trim()))break;let v=nx(h,{codexFailedText:Uo});if(v){t.push({id:x.id,kind:"event",type:"provider-error",cls:v.cls,status:v.status??void 0,text:v.text,provider:x.provider,legacy:!0,at:be(x),ts:be(x),box:x.boxId,agent:x.provider});break}$t();let D=(h.images||[]).filter(ze=>!b(ze,be(x)));if(!String(h.text||"").trim()&&!D.length)break;if(x.provider==="cursor"){let ze=`${x.boxId}|${h.text}`;if(n.has(ze))break;n.add(ze)}(!X||X.box===x.boxId)&&(X=null);let Z=x.provider==="codex"?(h.tools||[]).filter(ze=>!gm.has(ze)&&!ji.has(ze)):h.tools||[],ue=h.text,ge=null,ct=Bm(String(h.text||""));ct&&(ue=ct.text,ge={questions:ct.questions}),t.push({id:x.id,kind:"message",role:"assistant",text:ue,ts:be(x),box:x.boxId,agent:x.provider,boxRole:h.boxRole,...ge?{ask:ge}:{},...Z.length?{tools:Z}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},...D.length?{images:D}:{}}),ce(x),ge&&(X={id:x.id,groupId:jm(x.provider,h.sourceId||x.id,ge.questions),provider:x.provider,box:x.boxId,questions:ge.questions,ts:be(x)});break}case"job.started":{if(!h.id)break;let v=be(x),D=c.get(h.id);if(!D&&(h.kind||"agent")==="agent"&&h.name){let lt=u.get(h.name);lt&<.ts>=v&&(D=lt)}let Z=D?null:i.get(h.id)||null,ue={jobId:h.id,jobKind:h.kind||"agent",name:h.name||null,detail:h.detail||null,status:D?D.status==="killed"?"stopped":D.status&&D.status!=="completed"?"failed":"done":Z?"stalled":"running",startedTs:v,finishedTs:D?.ts??Z?.ts??null,quietMs:Z?.quietMs??null,summary:D?.summary??null,resultHead:D?.resultHead??null},ge=nt.get(x.boxId),ct=ge?.jobs[ge.jobs.length-1]?.startedTs;if(ge&&ct!=null&&v-ct<=mt)ge.jobs.push(ue);else{let lt={id:x.id,kind:"job-dock",dockId:h.id,ts:v,box:x.boxId,agent:x.provider,jobs:[ue]};nt.set(x.boxId,lt),t.push(lt)}break}case"handoff.done":t.push(wt(x,"lead-handoff"));break;case"subtask.delegated":t.push({...wt(x,"subtask-delegated"),to:x.boxId,toAgent:x.provider});break;case"subtask.merged":t.push(wt(x,"subtask-merged"));break;case"pr.state.changed":{if(!h.change)break;if(h.change==="opened"){for(let D of M.values())D.status="stale";M.clear();for(let D of B.values())D.status="stale";B.clear()}let v=wt(x,`pr-${h.change}`);if(v.box=x.boxId??null,Zx.has(h.change)&&h.number!=null){let D=Qx(k,S,h);if(k.size>=2){for(let ue of S.values())for(let ge of ue)ge.grouped=!0;v.groupPrs=[...k.values()];let Z=[v];for(let ue of k.keys())S.set(ue,Z)}else{let Z=S.get(D)??[];Z.push(v),S.set(D,Z)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let D=Um(h),Z=$&&!$.complete||k.size>=2;if(k.has(D))k.delete(D),S.delete(D);else if(h.number!=null)for(let ue of[...k.keys()])ue.endsWith(`#${h.number}`)&&(k.delete(ue),S.delete(ue));if(Z&&h.number!=null){(!$||$.complete)&&($={id:x.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:be(x),ts:be(x),box:x.boxId,agent:x.provider},t.push($)),$.prs.push({number:h.number,url:h.url??null,title:h.title??null,repo:h.repo??null,branch:h.branch??null,change:h.change,ts:be(x)}),$.remaining=[...k.values()],$.complete=k.size===0;break}}t.push(v);break}case"pr.offered":{if(!h.offerId)break;for(let D of M.values())D.status="stale";M.clear();for(let D of B.values())D.status="stale";B.clear();let v={id:x.id,kind:"pr-offer",offerId:h.offerId,excerpt:h.excerpt||null,branch:h.branch||null,base:h.base||null,offered:Array.isArray(h.offered)&&h.offered.length?h.offered:null,toPr:h.toPr&&typeof h.toPr=="object"?{number:h.toPr.number??null}:null,available:Array.isArray(h.available)?h.available:null,defaults:Array.isArray(h.defaults)?h.defaults:null,prState:h.prState||null,openPr:h.openPr&&typeof h.openPr=="object"?h.openPr:null,repos:Array.isArray(h.repos)&&h.repos.length?h.repos:null,status:"open",source:x.source==="cli"||h.via==="cli"?"cli":"transcript",ts:be(x),box:x.boxId,agent:x.provider};M.set(h.offerId,v),L.set(h.offerId,v),t.push(v),v.source==="cli"&&ve(v);break}case"pr.offer.answered":{let v=L.get(h.offerId);v&&(v.status=h.action||"dismiss",v.actions=Array.isArray(h.actions)?h.actions:null,v.notify=h.notify===!0,h.existingPr&&typeof h.existingPr=="object"&&(v.existingPr={number:h.existingPr.number??null}),M.delete(h.offerId));break}case"pr.exists.offered":{if(!h.offerId)break;for(let D of B.values())D.status="stale";B.clear();let v={id:x.id,kind:"pr-exists-offer",offerId:h.offerId,srcOfferId:h.srcOfferId??null,branch:h.branch||null,base:h.base||null,pr:h.pr&&typeof h.pr=="object"?{number:h.pr.number??null,url:h.pr.url??null,title:h.pr.title??null}:null,actions:Array.isArray(h.actions)&&h.actions.length?h.actions:null,notify:h.notify===!0,status:"open",ts:be(x),box:x.boxId,agent:x.provider};B.set(h.offerId,v),A.set(h.offerId,v),t.push(v);break}case"pr.exists.answered":{let v=A.get(h.offerId);v&&(v.status=h.choice||"dismiss",B.delete(h.offerId));break}case"box.state.changed":{let v=h.change==="model"?"model-change":h.change;if(!v)break;let D=wt(x,v);if(v==="model-change"&&(D.to=h.model),v==="node-update"&&D.latest){let Z=t.findIndex(ue=>ue.kind==="event"&&ue.type==="node-update"&&ue.latest===D.latest);if(Z>=0){let ue=t[Z];t[Z]={...D,ts:ue.ts,at:ue.at,firstAt:ue.firstAt??ue.ts,updatedAt:D.ts};break}}t.push(D);break}case"track.transferred":case"track.recovered":t.push(wt(x,"track-moved"));break;case"view.state.changed":{if(h.change==="deleted"){f(`view:${h.viewId}`);break}if(h.change!=="created"||h.restarted)break;p(`view:${h.viewId}`,{id:x.id,kind:"view-card",viewId:h.viewId,name:h.name,slug:h.slug,port:h.port,origin:h.origin,ts:be(x),box:x.boxId,agent:x.provider});break}case"view.spinup.failed":{t.push({id:x.id,kind:"view-spinup-failed",name:h.name||null,reason:h.reason||"no-view",ts:be(x),box:x.boxId,agent:x.provider});break}case"mock.state.changed":{if(h.change==="picked"){t.push({id:x.id,kind:"mock-picked",mockId:h.mockId,name:h.name,group:h.group??null,version:h.version??null,siblings:h.siblings??[],ts:be(x),box:x.boxId,agent:x.provider});break}if(h.change==="deleted"){f(`mock:${h.mockId}`);break}if(h.change!=="created")break;p(`mock:${h.mockId}`,{id:x.id,kind:"mock-card",mockId:h.mockId,name:h.name,group:h.group??null,ts:be(x),box:x.boxId,agent:x.provider});break}case"flow.state.changed":{if(h.change==="deleted"){f(`flow:${h.flowId}`);break}if(h.change!=="created")break;p(`flow:${h.flowId}`,{id:x.id,kind:"flow-card",flowId:h.flowId,name:h.name,stepCount:h.stepCount??null,mockIds:h.mockIds??[],ts:be(x),box:x.boxId,agent:x.provider});break}case"runbook.state.changed":{if(h.change==="deleted"){f(`runbook:${h.runbookId}`);break}if(h.change!=="created")break;p(`runbook:${h.runbookId}`,{id:x.id,kind:"runbook-card",runbookId:h.runbookId,name:h.name,ts:be(x),box:x.boxId,agent:x.provider});break}case"ticket.linked":{t.push({id:x.id,kind:"ticket-card",provider:h.provider||"linear",key:h.key,url:h.url,title:h.title??null,linkSource:h.linkSource??null,ts:be(x),box:x.boxId,agent:x.provider});break}case"box.fullscreen.detected":{if(!h.offerId)break;for(let D of K.values())D.box===(h.box||x.boxId)&&(D.status="stale",K.delete(D.offerId));let v={id:x.id,kind:"fullscreen-offer",offerId:h.offerId,status:"open",ts:be(x),box:h.box||x.boxId,agent:h.agent||x.provider};K.set(h.offerId,v),R.set(h.offerId,v),t.push(v);break}case"box.fullscreen.answered":{let v=R.get(h.offerId);v&&(v.status=h.action==="restarted"?"restarted":h.action==="cleared"?"cleared":"dismiss",K.delete(h.offerId));break}case"ticket.move.offered":{if(!h.offerId)break;for(let D of I.values())D.key===h.key&&(D.status="stale",I.delete(D.offerId));let v={id:x.id,kind:"ticket-offer",offerId:h.offerId,provider:h.provider||"linear",key:h.key,url:h.url??null,phase:h.phase??null,from:h.from??null,to:h.to?.name??null,prNumber:h.prNumber??null,status:"open",ts:be(x),box:x.boxId,agent:x.provider};I.set(h.offerId,v),j.set(h.offerId,v),t.push(v);break}case"ticket.move.answered":{let v=j.get(h.offerId);v&&(v.status=h.action==="moved"?"moved":"dismiss",I.delete(h.offerId));break}case"ticket.state.changed":{for(let v of I.values())v.key===h.key&&v.to===h.to&&(v.status="stale",I.delete(v.offerId));if(h.mode==="offer")break;t.push(wt(x,"ticket-state"));break}case"ticket.commented":{t.push({id:x.id,kind:"ticket-comment",key:h.key,url:h.url??null,excerpt:h.excerpt??null,ts:be(x),box:x.boxId,agent:x.provider});break}case"ticket.flow.changed":t.push(wt(x,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let D of E.values())D.status="stale";E.clear();let v={id:x.id,kind:"ticket-create-offer",proposalId:h.proposalId,provider:h.provider||"linear",epic:h.epic??null,issues:Array.isArray(h.issues)?h.issues:[],team:h.team??null,needsTeam:!!h.needsTeam,linkCurrentTrack:!!h.linkCurrentTrack,status:"open",ts:be(x),box:x.boxId,agent:x.provider};E.set(h.proposalId,v),C.set(h.proposalId,v),t.push(v),ve(v);break}case"ticket.create.answered":{let v=C.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",v.createdCount=h.createdCount??null,v.failedCount=h.failedCount??null,E.delete(h.proposalId));break}case"loop.create.offered":{if(!h.proposalId)break;for(let D of P.values())D.status="stale";P.clear();let v={id:x.id,kind:"loop-create-offer",proposalId:h.proposalId,name:h.name??"",schedule:h.schedule??"",prompt:h.prompt??"",expectation:h.expectation??null,notify:h.notify??"off",assignment:h.assignment??{provider:"claude"},repo:h.repo??null,nodeId:h.nodeId??null,nodeName:h.nodeName??null,status:"open",ts:be(x),box:x.boxId,agent:x.provider};P.set(h.proposalId,v),W.set(h.proposalId,v),t.push(v),ve(v);break}case"loop.create.answered":{let v=W.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",P.delete(h.proposalId));break}case"followup.armed":{if(!h.armId)break;let v=h.replaces?_e.get(h.replaces):null;v&&v.arm.status==="armed"&&ne(v,"replaced");let D={armId:h.armId,seq:Number.isInteger(h.seq)?h.seq:0,label:h.label??"",prompt:h.prompt??"",why:h.why??null,standing:h.standing==="never"?"never":"offerable",status:"armed"},Z=h.groupId??null,ue=Z?Pe.get(Z):null;ue||(ue={id:x.id,kind:"followup-armed",groupId:Z,armId:D.armId,trigger:h.trigger??"now",label:D.label,prompt:D.prompt,why:D.why,planWhy:h.planWhy??null,arms:[],status:"armed",ts:be(x),box:x.boxId,agent:x.provider},Z&&Pe.set(Z,ue),ue.trigger!=="now"&&t.push(ue)),ue.arms.push(D),_e.set(h.armId,{arm:D,line:ue});break}case"followup.armed.cancelled":{let v=_e.get(h.armId);v&&v.arm.status==="armed"&&ne(v,"cancelled");break}case"followup.offered":{if(!h.offerId)break;for(let Z of Y.values())Z.trigger===h.trigger&&(Z.status="stale",Y.delete(Z.offerId));let v=Array.isArray(h.rows)?h.rows:[];for(let Z of v){let ue=Z.armId?_e.get(Z.armId):null;ue&&ue.arm.status==="armed"&&ne(ue,"offered")}let D={id:x.id,kind:"followup-offer",offerId:h.offerId,trigger:h.trigger??"now",repo:h.repo??null,context:h.context&&typeof h.context=="object"?h.context:{},groupId:h.groupId??null,why:h.why??null,rows:v.map(Z=>({followupId:Z.followupId??null,armId:Z.armId??null,label:Z.label??"",prompt:Z.prompt??null,promptPreview:Z.promptPreview??null,promptHash:Z.promptHash??null,origin:Z.origin??"operator",why:Z.why??null,groupId:Z.groupId??null,seq:Number.isInteger(Z.seq)?Z.seq:0,standing:Z.standing==="never"?"never":"offerable",promotable:Z.armId?Z.promotable!==!1:!1,promotableReason:Z.promotableReason??null,rowStatus:"open"})),status:"open",answer:null,answers:[],ts:be(x),box:x.boxId,agent:x.provider};Y.set(h.offerId,D),me.set(h.offerId,D),t.push(D),D.trigger==="now"&&ve(D);break}case"followup.answered":{let v=me.get(h.offerId);if(!v)break;let D=new Set;h.armId&&D.add(`arm:${h.armId}`),h.followupId&&D.add(`fu:${h.followupId}`);for(let ge of Array.isArray(h.armIds)?h.armIds:[])D.add(`arm:${ge}`);for(let ge of Array.isArray(h.followupIds)?h.followupIds:[])D.add(`fu:${ge}`);let Z=h.action==="sent"?"sent":h.action==="removed"?"removed":"dismiss";for(let ge of v.rows){if(ge.rowStatus!=="open")continue;let ct=ge.armId?`arm:${ge.armId}`:`fu:${ge.followupId}`;D.size&&!D.has(ct)||(ge.rowStatus=Z)}let ue={followupId:h.followupId??null,armId:h.armId??null,label:h.label??null,promoted:h.promoted??null,revived:h.revived===!0,action:Z,batchId:h.batchId??null};v.answers.push(ue),v.answer=ue,v.rows.some(ge=>ge.rowStatus==="open")?v.status="open":(v.status=v.rows.some(ge=>ge.rowStatus==="sent")?"sent":v.rows.some(ge=>ge.rowStatus==="removed")?"removed":"dismiss",Y.delete(h.offerId));break}case"followup.create.offered":{if(!h.proposalId)break;for(let D of re.values())D.status="stale";re.clear();let v={id:x.id,kind:"followup-create-offer",proposalId:h.proposalId,label:h.label??"",prompt:h.prompt??"",trigger:h.trigger??"pr.merged",repo:h.repo??null,why:h.why??null,status:"open",created:null,ts:be(x),box:x.boxId,agent:x.provider};re.set(h.proposalId,v),Ce.set(h.proposalId,v),t.push(v),ve(v);break}case"followup.create.answered":{let v=Ce.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",re.delete(h.proposalId));break}case"followup.created":{let v=h.proposalId?Ce.get(h.proposalId):null;v&&(v.created={followupId:h.followupId??null});break}case"loop.created":{let v=h.proposalId?W.get(h.proposalId):null;v&&(v.created={loopId:h.loopId??null,nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null}),t.push(wt(x,"loop-created"));break}case"loop.update.offered":case"loop.delete.offered":{if(!h.proposalId||!h.loopId)break;for(let Z of J.values())Z.loopId===h.loopId&&(Z.status="stale",Z.staleReason="superseded",J.delete(Z.proposalId));let v=x.type==="loop.update.offered",D={id:x.id,kind:v?"loop-update-offer":"loop-delete-offer",proposalId:h.proposalId,loopId:String(h.loopId),name:h.name??h.base?.name??"",base:h.base??null,...v?{patch:h.patch??{},changed:Array.isArray(h.changed)?h.changed:Object.keys(h.patch||{})}:{reason:h.reason??""},selfEdit:!!h.selfEdit,status:"open",staleReason:null,ts:be(x),box:x.boxId,agent:x.provider};J.set(h.proposalId,D),he.set(h.proposalId,D),t.push(D),ve(D);break}case"loop.update.answered":{let v=he.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="applied"?"applied":"dismiss",J.delete(h.proposalId));break}case"loop.delete.answered":{let v=he.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="delete"?"deleted":h.action==="pause"?"paused":"keep",J.delete(h.proposalId));break}case"loop.updated":{let v=h.proposalId?he.get(h.proposalId):null;v&&(v.applied={changed:Array.isArray(h.changed)?h.changed:[],nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null}),t.push(wt(x,"loop-updated"));break}case"loop.deleted":t.push(wt(x,"loop-deleted"));break;case"loop.paused":t.push(wt(x,"loop-paused"));break;case"planning.change.offered":{if(!h.proposalId||!Array.isArray(h.ops)||!h.ops.length)break;for(let D of Fe.values())D.status="stale",D.staleReason="superseded",Fe.delete(D.proposalId);let v={id:x.id,kind:"planning-offer",proposalId:h.proposalId,why:h.why??"",ops:h.ops,base:h.base??{},resolved:h.resolved??{},snapped:!!h.snapped,createsMeRow:!!h.createsMeRow,status:"open",staleReason:null,applied:null,ts:be(x),box:x.boxId,agent:x.provider};Fe.set(h.proposalId,v),O.set(h.proposalId,v),t.push(v),ve(v);break}case"planning.change.answered":{let v=O.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="applied"?"applied":"dismiss",Fe.delete(h.proposalId));break}case"planning.changed":{let v=h.proposalId?O.get(h.proposalId):null;v&&(v.applied={results:Array.isArray(h.results)?h.results:[]}),t.push(wt(x,"planning-changed"));break}case"rework.cause.offered":{if(!h.sha||se.has(h.sha))break;let v={id:x.id,kind:"rework-cause-offer",sha:h.sha,subject:h.subject??"",linesReworked:Number(h.linesReworked)||0,origins:Array.isArray(h.origins)?h.origins:[],heuristic:h.heuristic??null,intent:h.intent??null,shas:Array.isArray(h.shas)?h.shas:null,pr:h.pr??null,retracts:0,status:"open",cause:null,ts:be(x),box:x.boxId,agent:x.provider};se.set(h.sha,v),t.push(v);break}case"rework.cause.answered":{let v=h.sha?se.get(h.sha):null;v&&(v.status="answered",v.cause=h.cause??null);break}case"rework.cause.retracted":{let v=h.sha?se.get(h.sha):null;v&&(v.status="open",v.cause=null,v.retracts=(v.retracts??0)+1);break}case"ticket.created":{let v={items:Array.isArray(h.items)?h.items:[],failed:Array.isArray(h.failed)?h.failed:[],team:h.team??null},D=h.proposalId?C.get(h.proposalId):null;if(D){D.created=v;break}t.push({id:x.id,kind:"ticket-created",...v,ts:be(x),box:x.boxId,agent:x.provider});break}case"error":{if(!h.kind)break;t.push(wt(x,h.kind));break}case"provider.error":t.push({...wt(x,"provider-error"),provider:x.provider,box:x.boxId,agent:x.provider});break;case"usage.cap.hit":t.push(wt(x,"usage-cap-hit"));break;case"usage.cap.warning":t.push(wt(x,"usage-cap-warning"));break;case"provider.connected":{t.push({id:x.id,kind:"event",type:"provider-connected",provider:h.provider||x.provider,ts:be(x),at:be(x),box:x.boxId,agent:x.provider});break}case"prompt.answered":{if(X=null,h.control||!h.question)break;let v=h.groupId||h.promptId;if(!v)break;let D=Re.get(v);D||(D={id:x.id,kind:"prompt",groupId:v,provider:x.provider,box:x.boxId,agent:x.provider,questionTotal:h.questionTotal??1,questions:[],ts:be(x)},Re.set(v,D));let Z=h.questionIndex??0,ue={index:Z,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},ge=D.questions.findIndex(ze=>ze.index===Z&&Ue(ze.question)===Ue(ue.question));ge>=0?D.questions[ge]=ue:D.questions.push(ue),D.questions.sort((ze,lt)=>ze.index-lt.index),D.questionTotal=Math.max(D.questionTotal,h.questionTotal??1),D.questions.length>=D.questionTotal&&(t.push(D),Re.delete(v));break}case"prompt.dismissed":{X=null;let v=h.groupId||h.promptId;if(!v||!h.question)break;let Z=Re.get(v)||{id:x.id,kind:"prompt",groupId:v,provider:x.provider,box:x.boxId,agent:x.provider,questionTotal:h.questionTotal??1,questions:[],ts:be(x)},ue=h.questionIndex??0;Z.questions.some(ge=>ge.index===ue&&Ue(ge.question)===Ue(h.question))||(Z.questions.push({index:ue,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:[]}),Z.questions.sort((ge,ct)=>ge.index-ct.index)),Z.answeredVia="chat",t.push(Z),Re.delete(v);break}default:break}}let In=d.size?t.filter(x=>!x.retired):t;return eB(In.sort((x,h)=>(x.ts??0)-(h.ts??0)))}function eB(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<=Q1){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 Hm(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:tB(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??Nn(e)).filter(o=>o.kind==="message");return z0(n,r)}function tB(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==Qs(e.command)?t:Wr(e.command,e.description||"")}function wt(e,t){let{ts:n,change:r,kind:o,...s}=e.payload||{};return{id:e.id,kind:"event",type:t,...s,at:be(e),ts:be(e)}}function ev(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)+nB,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&rB.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function tv(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!sB.has(r.type)||!r.box)continue;let o=(r.ts??0)+oB,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}function Cu(e){return(e||[]).filter(t=>aB.has(t?.kind)&&t.status==="open")}function nv(e){let t=new Map,n=new Map;for(let r of e||[]){if(r.type!=="pr.state.changed")continue;let o=r.payload||{};if(Zx.has(o.change)&&o.number!=null)Qx(t,n,o);else if(o.change==="merged"||o.change==="closed"||o.change==="lost"){let s=Um(o);if(t.has(s))t.delete(s);else if(o.number!=null)for(let a of[...t.keys()])a.endsWith(`#${o.number}`)&&t.delete(a)}}return[...t.values()]}var be,Zx,Um,Q1,nB,rB,oB,sB,aB,Nu=q(()=>{ea();Ho();ku();Gi();Mu();Fm();be=e=>e.payload?.ts??e.ts;Zx=new Set(["opened","ci-fixed","reconciled","pushed"]),Um=e=>`${e.repo??""}#${e.number}`;Q1=2*6e4;nB=12e4,rB=new Set(["archived","unarchived","opened"]);oB=2500,sB=new Set(["pr-opened","pr-merged","pr-auto-armed"]);aB=new Set(["pr-offer","pr-exists-offer","ticket-offer","ticket-create-offer","loop-create-offer","loop-update-offer","loop-delete-offer","planning-offer","fullscreen-offer","followup-create-offer"])});function dB(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 fB(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 mB(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<lB&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<uB&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function av(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=dB(e),s=fB(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=Nn(e??[]).filter(p=>p?.kind==="message"&&typeof p.text=="string"&&p.text.trim()).slice(-iB);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(sv[r]??sv.failover),l.push(""),o.lastPush?.sha){let p=ov(n,o.lastPush.ts),f=p?` (~${p} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${f}`+(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 p=ov(n,i);l.push(`## Last real checkpoint (written by the previous agent${p?`, ~${p} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=mB(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(p=>`\`${p}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(p=>`\`${p}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let p of c)l.push(`**${p.role==="user"?"operator":"agent"}:** ${pB(p.text,cB)}`),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(`
|
|
154
|
-
`);return d.length<=
|
|
155
|
-
|
|
156
|
-
[\u2026 brief truncated]`}var
|
|
157
|
-
|
|
158
|
-
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function AB(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(uv.test(s)){if(r.length>=xv)continue;let a=SB(s.replace(uv,"").replace(/[.;,:]+$/,""),vv,xB);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>lv&&(o=`${o.slice(0,lv-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Sv(e,t,{timeoutMs:n=9e4}={}){let r=Kr.join(t,bB),o=Kr.join(r,kB);await Gr.mkdir(r,{recursive:!0});let s=await Pu(o),a=await xe(e);if(!a)throw new Error(`session "${e}" not found`);return await Du(e,a,vB(o)),await Rv(o,s,n),AB(await Gr.readFile(o,"utf8"))}function Av(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===gv||n===kv;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(wv)||n===wB||r.startsWith(yv)||r.startsWith(bv)}function Tv(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-TB;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 Ev(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 Iv(e,{windows:t=[]}={}){if(Av(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 Bu(e){return Wm.get(e)||null}async function _v(e){let t=Wm.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await xe(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:Kr.join(n.cwd,fv,mv)};return Wm.set(e,r),_B(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function _B(e,t){e.step="summarize",await $v(e.name,t),e.step="clear";let n=await xe(e.name);await Du(e.name,n,"/clear"),await qm(EB),e.step="resume",await Du(e.name,n,yB),e.step="done",e.finishedAt=Date.now()}async function $v(e,t,{timeoutMs:n=IB}={}){let r=Kr.join(t,fv),o=Kr.join(r,mv);await Gr.mkdir(r,{recursive:!0});let s=await Pu(o),a=await xe(e);await fe({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await Du(e,a,gB(o)),await Rv(o,s,n),await fe({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await Pu(o)});let i=await Px(t,o,{exclude:u=>Av(u.role,u.text)});await fe({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(c){await N({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 Gr.readFile(o,"utf8");await N({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...hv(u),ts:Date.now()}})}catch{}}await Gf(t);let l=await $B(r,o);return c&&await m0(c,l||o,{box:e,agent:a?.agent??null}),o}async function ju(e,{timeoutMs:t}={}){let n=await xe(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return $v(e,n.cwd,t?{timeoutMs:t}:void 0)}async function Lu(e,t,{trackName:n="this Track"}={}){let r=await G({id:e,cwd:t}),o=av(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=Kr.join(t,dv);return await Gr.mkdir(Kr.dirname(s),{recursive:!0}),await Gr.writeFile(s,o,"utf8"),await N({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...hv(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),dv}async function Pu(e){try{return(await Gr.stat(e)).mtimeMs}catch{return 0}}async function Rv(e,t,n){let r=Date.now()+n;for(;;){if(await Pu(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await qm(2e3)}}async function $B(e,t){try{if(!hB.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=Kr.join(e,`${n}.md`);return await Gr.copyFile(t,r),r}catch{return null}}function qm(e){return new Promise(t=>setTimeout(t,e))}function pv(e){return e==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":e==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":e==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":""}async function Du(e,t,n){if(t){let o=pv(t.agent);await sr({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await xe(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await Ke(e,["Escape"],{kind:"drive-wipe"}),await qm(150),await Js(e,gu,{kind:"drive-wipe"}),await at(e,n,{kind:"orchestration-drive",...Cn(r.agent,n)}),await Vi({sessionName:e,agent:r.agent,agentCmd:pv(r.agent),text:n,flow:"orchestration-drive"})}var fv,mv,cv,wv,gv,yv,wB,gB,yB,bv,kv,bB,kB,lv,xv,vv,xB,vB,uv,SB,TB,EB,IB,Wm,dv,Gm=q(()=>{ht();ra();Rn();Wi();Om();Dx();xi();ye();cu();iv();fv=".bostrat/handoff",mv="latest.md",cv=32*1024;wv="Checkpoint: write a work-in-progress handoff",gv="HANDOFF WRITTEN",yv="Resume from .bostrat/handoff/latest.md",wB="/clear",gB=e=>`${wv} 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 ${gv} and take no further action.`,yB=`${yv}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,bv="Beat: write a",kv="BEAT WRITTEN",bB=".bostrat/arc",kB="beat.txt",lv=200,xv=4,vv=5,xB=36,vB=e=>`${bv} 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 ${xv} further lines, each starting with "- " and naming ONE piece in at most ${vv} 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 ${kv} and take no further action.`,uv=/^(?:[-–—*•·]|\d+[.)])\s+/,SB=(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};TB=6e5;EB=3500,IB=3e5,Wm=new Map;dv=".bostrat/handoff/status-latest.md"});function zr(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!Fu.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 Cv(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:zr(o.from,"from",n),toMs:zr(o.to,"to",n)}))}return t}function Vm(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 OB(e){if(!e)return null;let t=String(e).trim();return Vr[t]?t:Ov[t]?Ov[t]:Object.keys(Vr).find(r=>t.startsWith(r))||null}function MB(e){if(!e||e===Vr)return RB;let t=Mv.get(e);return t||(t=Cv(e),Mv.set(e,t)),t}function Nv(e,t,n={}){let{table:r,speed:o}=n,s=MB(r),a=r&&r!==Vr?s[e]?e:null:OB(e);if(!a)return null;let i=Vm(t);if(i===null)return null;let c=s[a].find(l=>i>=l.fromMs&&i<=l.toMs)||null;return c?o==null||o==="standard"?c:o==="fast"&&c.fast?{...c.fast,fromMs:c.fromMs,toMs:c.toMs,verifiedAt:c.verifiedAt,note:c.note,speed:"fast"}:null:null}function Pn(e,t={},n,r){let o=Nv(e,n,r);return o?((t.input||0)*o.input+(t.output||0)*o.output+(t.cacheWrite||0)*o.cacheWrite+(t.cacheRead||0)*o.cacheRead)/1e6:0}function CB(e=Vr){let t=[];for(let[n,r]of Object.entries(e)){if(!Array.isArray(r)||r.length===0){t.push(`${n}: must be a non-empty array of windows`);continue}let o;try{o=r.map(a=>({fromMs:zr(a.from,"from",n),toMs:zr(a.to,"to",n)}))}catch(a){t.push(String(a.message));continue}let s=r.filter(a=>a.to==null);s.length!==1?t.push(`${n}: expected exactly 1 open-ended window (to: null), found ${s.length}`):r[r.length-1].to!=null&&t.push(`${n}: the open-ended window must be last`),r[0].from!=null&&t.push(`${n}: the first window must be open at the start (from: null)`);for(let a=0;a<o.length;a++){if(o[a].fromMs>o[a].toMs&&t.push(`${n}: window ${a} ends before it begins (${r[a].from} \u2192 ${r[a].to})`),a===0)continue;let i=o[a-1],c=o[a];c.fromMs<=i.toMs?t.push(`${n}: windows ${a-1} and ${a} overlap (${r[a-1].to} / ${r[a].from})`):c.fromMs-i.toMs>2&&t.push(`${n}: gap between windows ${a-1} and ${a} (${r[a-1].to} \u2192 ${r[a].from})`),r[a].from||t.push(`${n}: window ${a} must declare a from bound`)}for(let a of r){a.note||t.push(`${n}: every window needs a note recording what was verified and when`),Fu.test(String(a.verifiedAt??""))||t.push(`${n}: every window needs a verifiedAt (YYYY-MM-DD) \u2014 the date its rates were checked against the vendor's published page`);for(let i of["input","output","cacheWrite","cacheRead"])(typeof a[i]!="number"||!Number.isFinite(a[i])||a[i]<0)&&t.push(`${n}: window ${i} must be a non-negative number, got ${JSON.stringify(a[i])}`),a.fast&&(typeof a.fast[i]!="number"||!Number.isFinite(a.fast[i])||a.fast[i]<0)&&t.push(`${n}: fast variant ${i} must be a non-negative number, got ${JSON.stringify(a.fast?.[i])}`)}}return t}function PB({now:e=Date.now(),table:t=Vr,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...NB,...n},a=[],i=Vm(e);if(i===null)return[`pricingFreshness: unparseable now ${JSON.stringify(e)}`];for(let[c,l]of Object.entries(t)){if(!Array.isArray(l))continue;let u;try{u=l.map(d=>({...d,fromMs:zr(d.from,"from",c),toMs:zr(d.to,"to",c),verifiedMs:Fu.test(String(d.verifiedAt??""))?Date.parse(`${d.verifiedAt}T00:00:00.000Z`):NaN}))}catch(d){a.push(String(d.message));continue}for(let d of u){if(!Number.isFinite(d.verifiedMs)){a.push(`${c}: window ${d.from??"\u2026"} \u2192 ${d.to??"now"} has no verifiedAt`);continue}let p=i>=d.fromMs&&i<=d.toMs,f=d.fromMs>i&&d.fromMs-i<=o*Km;p&&i-d.verifiedMs>r*Km&&a.push(`${c}: current rate last verified ${d.verifiedAt} (>${r}d) \u2014 re-check the vendor page and bump verifiedAt`),f&&d.fromMs-d.verifiedMs>s*Km&&a.push(`${c}: scheduled rate change on ${d.from} was last verified ${d.verifiedAt} \u2014 confirm it is still happening (vendors cancel; sonnet-5 did) and bump verifiedAt`)}}return a}function aa({now:e=Date.now(),table:t=Vr,...n}={}){let r=Vm(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=zr(l.from,"from",i),d=zr(l.to,"to",i);r>=u&&r<=d&&Fu.test(String(l.verifiedAt??""))&&(!o||l.verifiedAt<o)&&(o=l.verifiedAt),u>r&&s.push({model:i,from:l.from,verifiedAt:l.verifiedAt??null})}s.sort((i,c)=>i.from<c.from?-1:1);let a=[...CB(t),...PB({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var Vr,Ov,Fu,RB,Mv,Pv,NB,Km,ir=q(()=>{Vr={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"GA rates; verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-08-17",note:"$2/$10 is the standard price (intro pricing made permanent; the 2026-09-01 $3/$15 increase was cancelled) \u2014 verified 2026-08-17 against platform.claude.com/docs/en/about-claude/pricing."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-03, re-verified 2026-08-17 (developers.openai.com pricing); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:"2026-08-23",input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL). Superseded 2026-08-24 by the vendor's promotional cut (window below)."},{from:"2026-08-24",to:null,input:4,output:20,cacheWrite:5,cacheRead:.4,verifiedAt:"2026-08-24",note:"promotional pricing verified 2026-08-24 (developers.openai.com pricing), 'at least through November 21, 2026'; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:"2026-08-16",input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:2,output:12,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:"2026-08-16",input:1,output:6,cacheWrite:1.25,cacheRead:.1,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:.2,output:1.2,cacheWrite:.25,cacheRead:.02,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}]},Ov={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"},Fu=/^\d{4}-\d{2}-\d{2}$/;RB=Cv(Vr);Mv=new WeakMap;Pv=(e,t,n)=>Nv(e,t,n)!==null;NB=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),Km=864e5});function DB(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function Bv(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function Xt(e){let t=e.payload?.model||"unknown",n=DB(e.payload?.tokens),r=Number(e.payload?.usd)||0,o=e.payload?.speed||void 0,s=e.source==="vendor",a=r,i=!1;if(n&&!s){let u=Pn(t,n,e.ts,o?{speed:o}:void 0);u>0&&(a=u,i=Math.abs(u-r)>1e-9)}let c=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,l=!a&&c>0&&!Pv(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function Jr(e,{boxLabels:t={}}={}){let n=(e||[]).filter(p=>p.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,d=Dv();for(let p of n){let{usd:f,model:w,tokens:m,repriced:g,unpriced:y,asRecorded:b}=Xt(p);i+=b;let k=p.provider||"unknown",S=p.boxId||"unattributed";if(g&&c++,y&&(l++,u.add(w)),a+=f,Bv(d,m),r[w]||={usd:0,tokens:Dv(),events:0},r[w].usd+=f,r[w].events+=1,Bv(r[w].tokens,m),o[k]||={usd:0,events:0},o[k].usd+=f,o[k].events+=1,s[S]||={usd:0,events:0,provider:k,models:new Set,label:t[S]||S},s[S].usd+=f,s[S].events+=1,w!=="unknown"&&s[S].models.add(w),p.epoch!=null){let $=(s[S].epochs||={})[p.epoch]||={usd:0,events:0,provider:k,models:new Set};$.usd+=f,$.events+=1,$.provider=k,w!=="unknown"&&$.models.add(w)}}return{totalUsd:ia(a),asRecordedUsd:ia(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Uu(r,p=>({...p,usd:ia(p.usd)})),byProvider:Uu(o,p=>({...p,usd:ia(p.usd)})),byBox:Uu(s,p=>({usd:ia(p.usd),events:p.events,provider:p.provider,label:p.label,models:[...p.models],...p.epochs?{epochs:Uu(p.epochs,f=>({usd:ia(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var Dv,ia,Uu,Yr=q(()=>{ir();Dv=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});ia=e=>Math.round(e*1e4)/1e4,Uu=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function BB(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 jv(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 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[p,f]=d.split("#");return`${p.split("/").pop()}#${f}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(zm(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(zm(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(`${zm(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=Jr(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?BB(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var zm,Lv=q(()=>{Yr();zm=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as jB}from"node:child_process";import{promisify as LB}from"node:util";import{randomUUID as FB}from"node:crypto";async function Jm(e,t){let n=Date.now()+t;for(;;){if(await or(e))return!0;if(Date.now()>n)return!1;await Zi(UB)}}async function WB(e,t){let n=Fv[t]||Fv.default;n.escape&&(await Ke(e,["Escape"],{kind:"swap-exit"}),await Zi(150)),n.wipe&&await Js(e,gu,{kind:"swap-exit"}),await yu(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Cn(t).submitDelayMs||0;await Zi(Math.max(r,250)),await Ke(e,["Enter"],{kind:"swap-exit"})}async function Uv(e,t){try{let{stdout:n}=await Go(te,oe("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function qv(e){await Go(te,oe("respawn-pane","-k","-t",e))}async function Ym(e,{exitTimeoutMs:t=Hv,fromAgent:n=null}={}){if(!Be(e))throw new Error("invalid session name");let r=!1;if(await or(e))return{forced:r};let o=n||(await xe(e))?.agent||"agent";if(await WB(e,o),!await Jm(e,t)&&(r=!0,await fe({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await qv(e),!await Jm(e,Wv)))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);return{forced:r}}async function Xm(e,t={}){if(!Be(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await xe(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||Ur(n),a=Number(t.exitTimeoutMs)||Hv;await fe({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let{forced:i}=await Ym(e,{exitTimeoutMs:a,fromAgent:o}),c=s==="claude"&&jo(n),l=c?Ll():null,u=await Uv(e,"ANTHROPIC_API_KEY"),d=!1;try{l?(await Go(te,oe("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await Go(te,oe("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),d=!0)}catch(g){throw l?du(g,l):g}if(d&&!i&&(await qv(e),!await Jm(e,Wv)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let p=!!t.resumeSessionId,f=t.resumeSessionId||FB(),w=p?n:Oi(n,f,e),m=p?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(w);return c&&await Cr(r.cwd),s==="codex"&&await Ps(r.cwd),s==="cursor"&&await Ds(r.cwd),await Lo(e),await Go(te,oe("set-option","-t",e,"@bostrat-sid",f)),await Go(te,oe("set-option","-t",e,"@bostrat-agent",s)),await Zi(HB),c&&Ri().length&&(await Uv(e,$i)||await Go(te,oe("set-environment","-t",e,$i,"1")).catch(()=>{}),await at(e,`export ${$i}=1`,{kind:"swap-env"}),await Zi(150)),await at(e,w,{kind:"swap-launch"}),await fe({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:f,forced:i,envRespawned:d}),{name:e,cwd:r.cwd,sessionId:f,agent:s,pinned:m,forced:i,envRespawned:d,launchCmd:w}}var Go,Zi,Hv,Wv,UB,HB,Fv,Gv=q(()=>{ht();di();Ul();Hl();Wi();Rn();Go=LB(jB),Zi=e=>new Promise(t=>setTimeout(t,e)),Hv=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Wv=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,UB=250,HB=400,Fv={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"}}});function Zm(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Qm({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!Zm(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),Kv);try{let a=await Vv(`${Ne(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 Qi(e,{provider:t=null,cls:n="other",status:r=null,text:o="",note:s=null}={},{env:a=process.env,now:i=Date.now}={}){if(!e?.id||!String(e.name||"").startsWith("\u21BB"))return{ok:!1,source:"not-a-loop-track"};if(!Zm(a))return{ok:!1,source:"unconfigured"};let c=Hu.get(e.id)||0;if(i()-c<qB)return{ok:!1,source:"debounced"};Hu.set(e.id,i());let l=await ec({env:a}),u=l.loops.find(f=>f.trackId===e.id);if(!u)return Hu.delete(e.id),{ok:!1,source:l.source==="plane"?"no-loop":l.source};let d=s?String(s).replace(/\s+/g," ").trim():`${t||"provider"} ${r?r+" ":""}${n}: ${String(o||"").replace(/\s+/g," ").trim()}`,p=await Qm({loopId:u.id,outcome:"errored",note:d.slice(0,500),env:a});return p.ok||Hu.delete(e.id),{...p,loop:{id:u.id,name:u.name}}}async function ec({env:e=process.env}={}){if(!Zm(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),Kv);try{let r=await Vv(`${Ne(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 Kv,Vv,qB,Hu,Wu=q(()=>{yt();Kv=1e4,Vv=(...e)=>fetch(...e);qB=10*6e4,Hu=new Map});import{execFile as GB}from"node:child_process";import qu from"node:fs/promises";import eh from"node:path";import{promisify as KB}from"node:util";function QB(e){let n=String(e||"").trim().match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.+-]+)?/);return n?n[0]:null}async function ej(e,{env:t=process.env}={}){let n=String(e||"").trim();if(!n)return null;let r=n.includes("/")?[eh.resolve(n)]:String(t.PATH||"").split(eh.delimiter).filter(Boolean).map(o=>eh.join(o,n));for(let o of r)try{await qu.access(o,qu.constants.X_OK);let s=await qu.realpath(o),a=await qu.stat(s);if(!a.isFile())continue;return{path:s,mtimeMs:a.mtimeMs}}catch{}return null}async function Ko({agent:e=null,agentCmd:t=null,env:n=process.env}={}){let r=String(t||(e?n[YB[e]]||JB[e]:"")||"").trim();if(!r)return null;let{realClaude:o,realCodex:s,realCursor:a}=Hi({agent:e,agentCmd:r});if(!(o||s||a))return null;let i=await ej(r.split(/\s+/)[0],{env:n});if(!i)return null;let c=`${i.path}@${i.mtimeMs}`;return th.has(c)||th.set(c,Promise.resolve().then(()=>ZB(i.path,{agent:e})).then(l=>QB(l)).catch(()=>null)),th.get(c)}var VB,zB,JB,YB,th,XB,ZB,zv=q(()=>{ra();VB=KB(GB),zB=8e3,JB={claude:"claude",codex:"codex",cursor:"cursor-agent"},YB={claude:"BOSTRAT_AGENT_CMD",codex:"BOSTRAT_CODEX_CMD",cursor:"BOSTRAT_CURSOR_CMD"},th=new Map,XB=async e=>(await VB(e,["--version"],{timeout:zB})).stdout,ZB=XB});function sj(e){if(e.type==="queue-operation"&&e.operation==="enqueue")return Jv.test(String(e.content||""))?String(e.content):null;if(e.type==="attachment"){let n=e.attachment;return n?.type==="queued_command"&&n.commandMode==="task-notification"&&typeof n.prompt=="string"?n.prompt:null}let t=e.message?.content;return typeof t=="string"&&Jv.test(t)?t:null}async function Yv(e,t={}){return(await Zv(e,t)).launches}async function Xv(e,t={}){let{launches:n,done:r}=await Zv(e,t);return n.filter(o=>!r.has(o.id))}async function Zv(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await Nt(e,t)}catch{return{launches:[],done:new Set}}let o=new Map,s=new Set,a=new Set,i=new Map;for(let l of r.split(`
|
|
159
|
-
`)){let u=l.trim();if(!u||u[0]!=="{")continue;let d;try{d=JSON.parse(u)}catch{continue}if(d.isSidechain)continue;let p=
|
|
160
|
-
`).trim():"",
|
|
161
|
-
`),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 d=r[u].match(zo);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let p=r[u].trim();p&&!Qr.test(r[u])&&a.push(p)}}if(s.length<2)return null;let i=s.map(u=>({label:eo(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
|
|
162
|
-
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!
|
|
163
|
-
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(zo);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
|
|
164
|
-
`),o=-1;for(let u=r.length-1;u>=0;u--)if(
|
|
165
|
-
`),o=hh(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:eo(s.label),description:""}))}]}:null}function
|
|
166
|
-
`),o=hh(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:eo(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
|
|
167
|
-
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=
|
|
168
|
-
`)].reverse().find(a=>ph.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
|
|
169
|
-
`),o=hh(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:eo(s.label),description:""}))}]}:null}function
|
|
170
|
-
`),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(
|
|
171
|
-
`),o=
|
|
172
|
-
`).pop()||"")?!0:Jo.test(n)?!1:
|
|
173
|
-
`).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 Dn(e,t){return!e||!t?null:e.agent==="claude"?
|
|
174
|
-
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];
|
|
175
|
-
`).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 Xo(e,t){if(!t)return 0;try{let{stdout:n}=await Ye(e,["rev-list","--count",`${t}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function Zo(e,t,n,r){return await Ye(e,["worktree","add","--no-track","-b",n,t,r]),t}async function Th(e,t){try{return await Ye(e,["rev-parse","--verify","--quiet",`refs/heads/${t}`]),!0}catch{return!1}}async function
|
|
176
|
-
`).map(s=>s.trim()).filter(Boolean)}async function Rh(e,t,n,r){let{stdout:o}=await Ye(e,Ih(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await vh(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await vh(e,o,["--3way","--whitespace=nowarn"]),a=await
|
|
177
|
-
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var Sh,
|
|
178
|
-
`),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
|
|
179
|
-
`),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
|
|
180
|
-
`);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
|
|
181
|
-
`),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 QS(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&&!XS.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=ZS.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!sL.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function oc(e,t,{timeoutMs:n=rL}={}){let r=Date.now()+n;for(;;){let o=await De(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await nd(oL)}}async function cL(e,t){return await at(e,`/model ${t.model}`,Cn("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function lL(e,t){await wu(e),await at(e,"/model",{enter:!1}),await nd(250),await Ke(e,["Enter"],{kind:"model-switch"});let n=await oc(e,VS);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await Ke(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=iL(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await at(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await oc(e,i=>zS(i)||!VS(i)&&o(i)&&{done:o(i)});if(!s)throw await Ke(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?XS.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 Ke(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await at(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await oc(e,i=>!zS(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 uL(){let e=KS.join(process.env.CURSOR_CONFIG_DIR||KS.join(nL.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await tL.readFile(e,"utf8"))?.model||null}catch{return null}}async function dL(e,t){await wu(e),await yu(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await oc(e,JS),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 oc(e,JS,{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 Ke(e,["Down"],{kind:"model-switch"}),await nd(250)}if(!n||!s||!o(s)){await Ke(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await Ke(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 Ke(e,["Enter"],{kind:"model-switch"}),await nd(800);let a=await uL();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 eA(e,t,n){return e==="codex"?lL(t,n):e==="cursor"?dL(t,n):cL(t,n)}var nd,rL,oL,YS,sL,aL,XS,ZS,tA=q(()=>{ht();Wi();nd=e=>new Promise(t=>setTimeout(t,e)),rL=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,oL=300,YS=[{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.1"},{alias:"opusplan",label:"Opus Plan"}],sL=new Set([...YS.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","claude-fable-5-1"]),aL=[{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"}],XS=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),ZS=[{alias:"auto",label:"Auto"},{alias:"composer-2.5",label:"Composer 2.5"},{alias:"claude-opus-5-high",label:"Opus 5"},{alias:"claude-fable-5-1-high",label:"Fable 5.1"},{alias:"claude-sonnet-5-high",label:"Sonnet 5"},{alias:"cursor-grok-4.6-high",label:"Cursor Grok 4.6"},{alias:"gpt-5.6-sol-high",label:"GPT-5.6 Sol High"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}]});function sd(e){let t=e||{},n=pL(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function pL(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{text:e.text,boxRole:e.boxRole,...e.kind?{kind:e.kind}:{},...e.attachments?.length?{attachments:e.attachments}:{},...e.deliveryId?{deliveryId:e.deliveryId}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:e.box,provider:it(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:it(e.agent),payload:{text:e.prompt,kind:e.kind,boxRole:e.boxRole,...e.deliveryId?{deliveryId:e.deliveryId}:{}}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:it(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,cliVersion:e.cliVersion??null}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:it(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:it(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:it(e.agent),payload:{...Bn(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:it(e.agent||e.toAgent),payload:{kind:"subtask-failed",...Bn(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{change:"subtask-resumed",...Bn(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",...Bn(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...Bn(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...Bn(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{change:"rehydrated",...e.fresh?{fresh:!0}:{}}};case"lead-refreshed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{change:"lead-refreshed",box:e.box,agent:e.agent,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,reason:e.reason,cliVersion:e.cliVersion??null}};case"transferred-in":return{type:e.mode==="recover"?"track.recovered":"track.transferred",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{...Bn(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Bn(e)}};case"retag-failed":return{type:"error",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{kind:"retag-failed",action:e.action??"reshape",reason:e.reason,rolledBack:e.rolledBack!==!1,...e.recovered?{recovered:!0}:{}}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};case"delivery-held":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{kind:"delivery-held",...Bn(e)}};case"startup-gate":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{kind:"startup-gate",...Bn(e)}};default:return null}}function oA(e,t){let n=sd(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;ad(()=>N(r,n))}async function sA(e){if(!(!e?.id||nA.has(e.id))){nA.add(e.id);try{let t=await G(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=sd(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await N(e,o)}}catch{}}}function Bn(e){let{type:t,at:n,...r}=e;return r}function aA(e,t,n){!n?.promptId||od.get(e.id)===n.promptId||(od.set(e.id,n.promptId),ad(()=>N(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:it(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 iA(e,t,n,r={}){od.delete(e.id),ad(()=>N(e,{type:"prompt.dismissed",actor:"user",source:"orchestrator",boxId:t?.name,provider:it(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}function Ph(e,t,n,r={}){od.delete(e.id),ad(()=>N(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:it(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var it,rd,ad,rA,nA,od,Dh=q(()=>{ye();it=e=>e||null;rd=Promise.resolve(),ad=e=>(rd=rd.then(e).catch(()=>{}),rd),rA=()=>rd;nA=new Set;od=new Map});function gL(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function fA(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of gL(n).reverse()){if(!fL.test(r))continue;let o=r.endsWith("?")&&r.length<=wL&&hL.test(r);if(mL.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function hA(e){let t=String(e||""),n=[];yL.test(t)&&n.push("commit"),mA.test(t)&&n.push("rebase"),bL.test(t)&&n.push("push");let r=xL.test(t);!r&&kL.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 wA(e){return mA.test(String(e||""))}function wa(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(f=>`${f.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 f=a.has("commit")?"the branch":`this branch${t?` (${t})`:""}`;u.push(i?`rebase ${f} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${f} 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 f=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",w=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${w}${f}`:`push this branch${t?` (${t})`:""} from each changed checkout (${c}) to its remote${f}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${w}${f}`:`push this branch${l} to the remote${f}`)}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 p=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return d.length&&(p+=` ${d.join(", and ").replace(/^./,f=>f.toUpperCase())}.`),a.has("pr")&&(p+=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."),p}function gA(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 yA(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<=vL&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function bA(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+SL||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function kA(e){let t=(Array.isArray(e)?e:[]).filter(r=>cA[r]).map(r=>cA[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 lA,uA,dA,pA,fL,mL,hL,wL,ro,yL,mA,bL,kL,xL,vL,SL,cA,ts=q(()=>{lA=/(?<!\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,uA=/(?<!\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,dA=/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,pA=/(?<!\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,fL=new RegExp(`\\b(?:${lA}|${uA}|${dA}|${pA})\\b`,"i"),mL=/\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,hL=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,wL=90;ro=["commit","rebase","push","pr"],yL=new RegExp(`\\b(?:${lA})\\b`,"i"),mA=new RegExp(`\\b(?:${pA})\\b`,"i"),bL=new RegExp(`\\b(?:${uA})\\b`,"i"),kL=new RegExp(`\\b(?:${dA})\\b`,"i"),xL=/\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;vL=3e4;SL=2500;cA={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import AL from"node:crypto";import id from"node:fs/promises";import xA from"node:path";import TL from"node:os";function TA(e){AA=typeof e=="function"?e:async()=>new Set}function EL(e,t,n){if(e.originNode&&e.originNode===t)return!0;let r=e.data?.trackId;return!!r&&n.has(r)}async function He(){return lr||(Bh??=id.readFile(Lh,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(lr=e,!t&&qe()){let{records:n,source:r}=await dt("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){let o=d0(),s=await AA().catch(()=>new Set),a=0;for(let i of n){if(i.deletedAt||!i.data?.repo||i.data?.number==null)continue;if(!EL(i,o,s)){a++;continue}let{id:c,updatedAt:l,...u}=i.data;lr[ie(u.repo,u.number)]=u}a&&console.log(`[pr-links] restore: ${Object.keys(lr).length} link(s) re-seeded, ${a} other-node record(s) left on the plane`),Object.keys(lr).length&&await Fh().catch(()=>{})}}return lr}).finally(()=>Bh=null),Bh)}async function Fh(){let e=JSON.stringify({links:lr},null,2),t=`${Lh}.${process.pid}.tmp`;await id.mkdir(vA,{recursive:!0}),await id.writeFile(t,e),await id.rename(t,Lh)}async function Ze(e){jh=jh.then(Fh,Fh),await jh.catch(()=>{});let t=lr?.[e];t&&At("prlinks",{id:SA(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function Ut({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 He(),c=ie(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:IL.has(a),firstSeen:Date.now()},await Ze(ie(e,t)),{link:i[c],created:!0})}async function ur(e,t){if(!e||t==null)return null;let n=(await He())[ie(e,t)];return!n||n.announced?null:(n.announced=!0,await Ze(ie(e,t)),n)}async function EA(e,t){if(!e||t==null)return!1;let n=(await He())[ie(e,t)];return n?.notifyOnMerge?(delete n.notifyOnMerge,await Ze(ie(e,t)),!0):!1}async function pt(e,t){return!e||t==null?null:(await He())[ie(e,t)]??null}async function oo(e,t,n){if(!e||t==null)return null;let r=(await He())[ie(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,delete r.followupPending,await Ze(ie(e,t)),r):null}async function IA(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await He())[ie(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Ze(ie(e,t)),o):null}async function _A(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await He())[ie(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Ze(ie(e,t)),o):null}async function cd(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await He())[ie(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Ze(ie(e,t)),r)}async function ld(e,t){if(!e||t==null)return null;let n=(await He())[ie(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Ze(ie(e,t)),n)}async function ud(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await He())[ie(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Ze(ie(e,t)),r)}async function dd(e,t){if(!e||t==null)return null;let n=(await He())[ie(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Ze(ie(e,t)),n)}async function $A(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await He())[ie(e,t)];return r?(r.baseFoldAt=n,await Ze(ie(e,t)),r):null}async function RA(e,t,{misses:n,until:r}){if(!e||t==null)return null;let o=(await He())[ie(e,t)];return o?(o.brokerRefused={misses:Number(n)||0,until:Number(r)||0},await Ze(ie(e,t)),o):null}async function OA(e,t){if(!e||t==null)return null;let n=(await He())[ie(e,t)];return n?.brokerRefused?(delete n.brokerRefused,await Ze(ie(e,t)),n):null}async function MA(e,t){if(!e||t==null)return null;let n=(await He())[ie(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Ze(ie(e,t)),n)}async function CA(e,t,n){if(!e||t==null||!n)return null;let r=(await He())[ie(e,t)];return r?(r.lastHeadSha=n,await Ze(ie(e,t)),r):null}async function NA(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await He())[ie(e,t)];if(!r)return{prev:null,link:null};let o=r.verdict??null;return o&&o.blocked===(n.blocked??null)&&(o.sha??null)===(n.sha??null)||(r.verdict={blocked:n.blocked??null,sha:n.sha??null,at:n.at??Date.now()},await Ze(ie(e,t))),{prev:o,link:r}}async function sc(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await He())[ie(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Ze(ie(e,t)),r):null}async function ns(e,t,n=!0){if(!e||t==null)return null;let r=(await He())[ie(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Ze(ie(e,t)),r):null}async function rs(e,t,n=!0){if(!e||t==null)return null;let r=(await He())[ie(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Ze(ie(e,t)),r):null}async function PA(e,t,n){if(!e||t==null)return null;let r=(await He())[ie(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 Ze(ie(e,t)),r}async function DA(e,t,n){if(!e||t==null)return null;let r=(await He())[ie(e,t)];if(!r)return null;let o=new Map((r.followupPending||[]).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.followupPending=[...o.values()],await Ze(ie(e,t)),r}async function ac(e,t){if(!e||t==null)return null;let n=(await He())[ie(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 Ze(ie(e,t)),n)}async function BA(){return Object.values(await He()).filter(e=>!_L.has(e.state))}async function hn(){return Object.values(await He()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function jA(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await dt("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await He(),r=0;for(let[o,s]of Object.entries(n))await At("prlinks",{id:SA(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var vA,Lh,ie,SA,lr,Bh,jh,AA,IL,_L,jn=q(()=>{Fr();vA=process.env.BOSTRAT_STATE_DIR||xA.join(process.env.HOME||TL.tmpdir(),".bostrat"),Lh=xA.join(vA,"pr-links.json"),ie=(e,t)=>`${e}#${Number(t)}`,SA=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=AL.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},lr=null,Bh=null,jh=Promise.resolve(),AA=async()=>new Set;IL=new Set(["api","observed","reconciled"]);_L=new Set(["merged","closed","lost"])});import $L from"node:fs";import Uh from"node:fs/promises";import HA from"node:path";import RL from"node:os";function GA(e,t=!1){if(!t&&e-FA<OL)return;FA=e;let n=JSON.stringify({windowStart:ga,byCaller:Object.fromEntries(dr),lastHour:so});UA=UA.then(()=>Uh.mkdir(WA,{recursive:!0})).then(()=>Uh.writeFile(`${pd}.tmp`,n)).then(()=>Uh.rename(`${pd}.tmp`,pd)).catch(()=>{})}function KA(e){e-ga<qA||(so={start:ga,total:fd(dr),byCaller:Object.fromEntries(dr)},so.total&&console.log(`[github] ${so.total} calls last hour`,so.byCaller),ga=e,dr=new Map,GA(e,!0))}function pr(e="other",t=Date.now()){KA(t),dr.set(e,(dr.get(e)||0)+1),GA(t)}function VA(e=Date.now()){return KA(e),{windowStart:ga,total:fd(dr),byCaller:Object.fromEntries(dr),lastHour:so,backoffUntil:os>e?os:null}}function ao(e){return ML.test(String(e?.message||e||""))}function io(e,t=Date.now()){return os<=t&&console.warn(`[github] rate limited \u2014 backing off ${LA/6e4} min:`,String(e?.message||e||"").slice(0,200)),os=Math.max(os,t+LA),os}function md(e=Date.now()){return Math.max(0,os-e)}var WA,pd,qA,LA,OL,ga,dr,so,os,FA,fd,UA,ML,ic=q(()=>{WA=process.env.BOSTRAT_STATE_DIR||HA.join(process.env.HOME||RL.tmpdir(),".bostrat"),pd=HA.join(WA,"github-metrics.json"),qA=60*6e4,LA=5*6e4,OL=6e4,ga=Date.now(),dr=new Map,so=null,os=0,FA=0,fd=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse($L.readFileSync(pd,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<qA?(ga=e.windowStart,dr=new Map(Object.entries(e.byCaller||{})),so=e.lastHour||null):fd(new Map(Object.entries(e.byCaller||{})))&&(so={start:e.windowStart,total:fd(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}UA=Promise.resolve();ML=/rate limit|too quickly|abuse detection|HTTP 429/i});import zA from"node:fs/promises";import JA from"node:path";import CL from"node:os";function wd({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};hd.push(l),hd.length>PL&&hd.shift();let u=Hh.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)),Hh.set(e,u),DL(l),l}async function DL(e){try{await zA.mkdir(YA,{recursive:!0}),await zA.appendFile(NL,JSON.stringify(e)+`
|
|
182
|
-
`)}catch{}}function XA(){let e={};for(let[t,n]of Hh)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:hd.slice(-20)}}var YA,NL,PL,hd,Hh,gd=q(()=>{YA=process.env.BOSTRAT_STATE_DIR||JA.join(process.env.HOME||CL.tmpdir(),".bostrat"),NL=JA.join(YA,"github-latency.ndjson"),PL=100,hd=[],Hh=new Map});import{execFile as BL}from"node:child_process";import{promisify as jL}from"node:util";async function LL(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([ba("git",["-C",e,"rev-parse","HEAD"]),ba("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await N({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function Qt(e,t,n={}){let{caller:r="prs",...o}=n,s=await _n({cwd:e});pr(r);try{let{stdout:a}=await ba("gh",t,{cwd:e,...FL,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw ao(a)&&io(a),a}}async function Gh(){if(yd&&Date.now()-yd.at<6e4)return yd.data;let e;try{pr("auth");let t=await ba("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 gi();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return yd={at:Date.now(),data:e},e}async function UL(e){try{let{stdout:t}=await ba("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function QA(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 Wh(e){let t=(e||[]).map(QA),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 Vh(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function tT(e,t){if(!t)return null;let n=await Oe(e);if(!n)return null;try{let r=await Qt(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 nT(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 rT(e,t){let n=await tT(e,t);return n===null?{...Wh([]),unavailable:!0}:Wh(nT(n))}function bd(e,t=Wh(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",state:e.state??null,mergedAt:e.mergedAt??null,closedAt:e.closedAt??null,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 WL(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){ya.delete(s);continue}r.add(s),ya.has(s)||ya.set(s,n),o.mergeabilityPending=n-ya.get(s)<HL}for(let o of ya.keys())o.startsWith(`${e}#`)&&!r.has(o)&&ya.delete(o);return t}async function ka(e,t){let n=await fr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await Qt(n.path,["pr","view",String(r),"--json",Kh]);return bd(JSON.parse(o))}catch(o){if(!Vh(o))throw o;let s=await Qt(n.path,["pr","view",String(r),"--json",eT]),a=JSON.parse(s);return bd(a,await rT(n.path,a.headRefOid))}}async function Ln(e,{caller:t="prs"}={}){try{let n=await Qt(e,["pr","list","--json",Kh,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>bd(r)).sort(ZA)}catch(n){if(!Vh(n))throw n;let r=await Qt(e,["pr","list","--json",eT,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>bd(o,await rT(e,o.headRefOid))))).sort(ZA)}}function en(){cc.clear()}async function KL(e,t){if(!e)return t;for(let n of t){let r=await pt(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 cd(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await ud(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function oT(){let e=await Gh();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await nr())await UL(a.path)&&t.push(a);let n=Date.now(),r=md(n)>0,o=await Promise.all(t.map(async a=>{let i=cc.get(a.path),c=i?.pending?GL:qL;if(i&&(r||n-i.at<c))return i.entry;try{let l=await Oe(a.path).catch(()=>null),u=WL(a.path,await Ln(a.path,{caller:"tab"}),n);if(await KL(l,u),i?.entry?.prs)for(let f of u)i.entry.prs.find(m=>m.number===f.number)?.checks?.pending>0&&f.checks.total>0&&f.checks.pending===0&&wd({change:f.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:f.number,githubAt:f.checks.settledAt,source:"tab"});let d={name:a.name,path:a.path,prs:u},p=u.some(f=>f.mergeabilityPending||f.checks.pending>0||f.autoMergeArmed);return cc.set(a.path,{at:Date.now(),entry:d,pending:p}),d}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:Fn(l)}}})),s=new Set(t.map(a=>a.path));for(let a of cc.keys())s.has(a)||cc.delete(a);return{auth:e,repos:o}}async function fr(e){let t=(await nr()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function ss(e){return Bs(e)}async function xa(e,t,{method:n="squash"}={}){let r=await fr(e);if(!lc.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 _n({cwd:r.path})).GH_TOKEN){let a=await Pf().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Qt(r.path,s)}catch(a){throw new Error(Fn(a))}finally{en()}return{ok:!0,number:o}}async function uc(e,t){let n=await fr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Qt(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Fn(s))}finally{en()}let o=await Oe(n.path).catch(()=>null);return o&&await $A(o,r).catch(()=>{}),{ok:!0,number:r}}async function zh(e,t){let n=await fr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Qt(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Fn(o))}finally{en()}return{ok:!0,number:r}}function Jh(e){return e.green&&!e.isDraft}function YL(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 sT(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:Jh(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function aT(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function dc(e,t){let n=a=>a?.firstSeen!=null&&Number.isFinite(Number(a.firstSeen))?Number(a.firstSeen):Number.MAX_SAFE_INTEGER,r=n(e),o=n(t);if(r!==o)return r-o;let s=String(e?.repo||"").localeCompare(String(t?.repo||""));return s||Number(e?.number||0)-Number(t?.number||0)}function iT(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(dc))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function cT(){let e={};for(let[t,n]of qh)e[t]={...n};return e}async function lT(e,{method:t="merge",onMerged:n=null}={}){let r=await fr(e);if(!lc.has(t))throw new Error(`invalid merge method: ${t}`);if(qh.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Ln(r.path)).filter(Jh).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 qh.set(r.path,s),ZL(s,o,{listPrs:()=>Ln(r.path),merge:a=>xa(r.path,a,{method:t}),sleep:JL,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function XL(e,t){let n=Date.now()+(t.settleMaxMs??zL),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(VL)}}async function ZL(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 XL(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: ${Fn(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(!Jh(a)){e.skipped.push({number:a.number,title:a.title,reason:YL(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:Fn(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),en()}}async function Yh(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await ba("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Fn(l)}`)}await LL(e);let o=n||"";if((await _n({cwd:e})).GH_TOKEN){let l=await Pf().catch(()=>null);l&&(o=`${o?`${o}
|
|
183
|
-
|
|
184
|
-
`:""}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 Qt(e,s)}catch(l){throw new Error(Fn(l))}finally{en()}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
|
|
153
|
+
`});function tB(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 tv(e,t,n){let r=Um(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 Nn(e){let t=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Map,l=new Set,u=new Map,d=new Map,p=(x,h)=>{d.set(x,h),t.push(h)},f=x=>{let h=d.get(x);h&&(h.retired=!0)},w=/^Agent "([\s\S]+)" finished$/,m=new Map;for(let x of e||[]){if(x.type!=="tool.called"||x.source!=="transcript")continue;let h=String(x.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(h))for(let v of h.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let D=v[0].split("/").pop().toLowerCase();m.has(D)||m.set(D,[]),m.get(D).push(x.payload?.ts??x.ts)}}let g=3*6e4,y=20*6e4,b=(x,h)=>!!(x?.name&&m.get(String(x.name).toLowerCase()))?.some(D=>D>=h-g&&D<=h+y);for(let x of e||[])if(x.type==="turn.retracted"){r.add(`${x.boxId}|${x.payload?.saidTs}`);let h=Ue(x.payload?.text);h&&(o.has(x.boxId)||o.set(x.boxId,[]),o.get(x.boxId).push(h))}else if(x.type==="turn.user"&&x.source==="orchestrator"){let h=Ue(x.payload?.text);h&&(s.has(x.boxId)||s.set(x.boxId,new Set),s.get(x.boxId).add(h))}else if(x.type==="job.stalled"&&x.payload?.id)i.set(x.payload.id,{ts:be(x),quietMs:Number(x.payload.quietMs)||null});else if(x.type==="job.started"&&x.payload?.id)a.add(x.payload.id),(x.payload.kind||"agent")==="agent"&&x.payload.name&&l.add(x.payload.name);else if(x.type==="turn.user"&&x.payload?.note?.kind==="task"){let h=x.payload.note,v={status:h.status||null,summary:h.summary||null,resultHead:h.resultHead??null,ts:be(x)};h.toolUseId&&c.set(h.toolUseId,v);let D=String(h.summary||"").match(w);D&&u.set(D[1],v)}let k=new Map,S=new Map,$=null,M=new Map,L=new Map,B=new Map,A=new Map,I=new Map,j=new Map,K=new Map,R=new Map,T=new Map,C=new Map,P=new Map,W=new Map,Y=new Map,me=new Map,_e=new Map,Pe=new Map,ne=(x,h)=>{x.arm.status=h;let v=x.line,D=v.arms.map(Z=>Z.status);v.status=D.includes("armed")?"armed":D.includes("offered")?"offered":D.includes("replaced")?"replaced":"cancelled"},Ve=[],ve=x=>Ve.push(x),ce=x=>{if(!Ve.length)return;let h=be(x);for(let v of Ve){if(v.status!=="open"||v.box&&v.box!==x.boxId||(v.ts??0)>h)continue;let D=t.indexOf(v);D>=0&&t.splice(D,1),v.ts=h,t.push(v)}},re=new Map,Ce=new Map,J=new Map,he=new Map,Fe=new Map,O=new Map,oe=new Map,Re=new Map,$t=()=>{for(let x of Re.values())x.questions.length&&t.push(x);Re.clear()},X=null,we=new Map,mt=6e4,nt=new Map,an=(x,h)=>{let v=o.get(x);if(!v?.length)return!1;let D=Ue(h),Z=s.get(x);return v.some(ue=>{if(!D.startsWith(ue))return!1;let ge=Ue(D.slice(ue.length));return!!ge&&!!Z?.has(ge)})};for(let x of e||[]){let h=x.payload||{};switch(x.type){case"turn.user":{if(x.source==="hook"||(Ve.length=0,!String(h.text||"").trim()&&!h.images?.length&&!h.attachments?.length))break;{let v=Vx(h.text);if(v){if(v.kind==="shell-input"){let D={id:x.id,kind:"shell",command:v.command,stdout:null,stderr:null,done:!1,ts:be(x),box:x.boxId,agent:x.provider};t.push(D),we.set(x.boxId,D)}else{let D=we.get(x.boxId);D?(D.stdout=v.stdout||null,D.stderr=v.stderr||null,D.done=!0,we.delete(x.boxId)):t.push({id:x.id,kind:"shell",command:null,stdout:v.stdout||null,stderr:v.stderr||null,done:!0,ts:be(x),box:x.boxId,agent:x.provider})}break}}{let v=h.note||Zi(h.text);if(v){if(v.kind==="task"){if(v.toolUseId&&a.has(v.toolUseId))break;let D=String(v.summary||"").match(w);if(!v.toolUseId&&D&&l.has(D[1]))break}if(v.kind==="command"&&Gx(v.command))break;if(v.kind==="resume-summary"){t.push({id:x.id,kind:"resume-summary",title:v.title||"Context resumed from summary",bullets:v.bullets||[],sections:v.sections||[],rawChars:v.rawChars??null,ts:be(x),box:x.boxId,agent:x.provider});break}v.kind!=="suppress"&&t.push({id:x.id,kind:"event",type:Jx(v),name:v.name??null,command:v.command??null,summary:v.summary??null,status:v.status??null,ts:be(x),box:x.boxId,agent:x.provider});break}}if(r.has(`${x.boxId}|${be(x)}`)||x.source==="transcript"&&an(x.boxId,h.text))break;if(h.kind==="merge-notify"||h.kind==="ship-notify"){t.push({id:x.id,kind:"event",type:h.kind==="ship-notify"?"ship-notified":"merge-notified",text:h.text,ts:be(x),box:x.boxId,agent:x.provider});break}if(h.kind==="mock-picked"||h.kind==="ci-fix"||h.kind==="reconcile"||h.kind==="pr-go"||h.kind==="push-go"||h.kind==="spin-up-view"||h.kind==="prompt-answer"||h.kind==="followup-go"){h.kind==="prompt-answer"&&(X=null);break}for(let v of M.values())v.status="stale";M.clear();for(let v of B.values())v.status="stale";B.clear();for(let v of Y.values())v.rows.some(D=>D.rowStatus==="sent")||(v.status="stale",Y.delete(v.offerId));X&&X.box===x.boxId&&(t.push(tB(X)),X=null),t.push({id:x.id,kind:"message",role:"user",text:h.text,ts:be(x),box:x.boxId,agent:x.provider,boxRole:h.boxRole,...x.actor==="orchestrator"?{orchestration:h.kind||!0}:{},...h.sourceId?{sourceId:h.sourceId}:{},...h.attachments?.length?{attachments:h.attachments}:{},...h.images?.length?{images:h.images}:{}});break}case"peer.message":{let v=h.from||{};t.push({id:x.id,kind:"event",type:"peer-message",text:h.text,from:v.trackName||v.boxId||v.name||null,fromTrackId:v.trackId||null,ts:be(x),box:x.boxId,agent:x.provider});break}case"turn.agent":{if(h.model==="<synthetic>"&&/^No response requested\.$/.test(String(h.text||"").trim()))break;let v=ox(h,{codexFailedText:Uo});if(v){t.push({id:x.id,kind:"event",type:"provider-error",cls:v.cls,status:v.status??void 0,text:v.text,provider:x.provider,legacy:!0,at:be(x),ts:be(x),box:x.boxId,agent:x.provider});break}$t();let D=(h.images||[]).filter(ze=>!b(ze,be(x)));if(!String(h.text||"").trim()&&!D.length)break;if(x.provider==="cursor"){let ze=`${x.boxId}|${h.text}`;if(n.has(ze))break;n.add(ze)}(!X||X.box===x.boxId)&&(X=null);let Z=x.provider==="codex"?(h.tools||[]).filter(ze=>!gm.has(ze)&&!Li.has(ze)):h.tools||[],ue=h.text,ge=null,ct=Bm(String(h.text||""));ct&&(ue=ct.text,ge={questions:ct.questions}),t.push({id:x.id,kind:"message",role:"assistant",text:ue,ts:be(x),box:x.boxId,agent:x.provider,boxRole:h.boxRole,...ge?{ask:ge}:{},...Z.length?{tools:Z}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},...D.length?{images:D}:{}}),ce(x),ge&&(X={id:x.id,groupId:jm(x.provider,h.sourceId||x.id,ge.questions),provider:x.provider,box:x.boxId,questions:ge.questions,ts:be(x)});break}case"job.started":{if(!h.id)break;let v=be(x),D=c.get(h.id);if(!D&&(h.kind||"agent")==="agent"&&h.name){let lt=u.get(h.name);lt&<.ts>=v&&(D=lt)}let Z=D?null:i.get(h.id)||null,ue={jobId:h.id,jobKind:h.kind||"agent",name:h.name||null,detail:h.detail||null,status:D?D.status==="killed"?"stopped":D.status&&D.status!=="completed"?"failed":"done":Z?"stalled":"running",startedTs:v,finishedTs:D?.ts??Z?.ts??null,quietMs:Z?.quietMs??null,summary:D?.summary??null,resultHead:D?.resultHead??null},ge=nt.get(x.boxId),ct=ge?.jobs[ge.jobs.length-1]?.startedTs;if(ge&&ct!=null&&v-ct<=mt)ge.jobs.push(ue);else{let lt={id:x.id,kind:"job-dock",dockId:h.id,ts:v,box:x.boxId,agent:x.provider,jobs:[ue]};nt.set(x.boxId,lt),t.push(lt)}break}case"handoff.done":t.push(wt(x,"lead-handoff"));break;case"subtask.delegated":t.push({...wt(x,"subtask-delegated"),to:x.boxId,toAgent:x.provider});break;case"subtask.merged":t.push(wt(x,"subtask-merged"));break;case"pr.state.changed":{if(!h.change)break;if(h.change==="opened"){for(let D of M.values())D.status="stale";M.clear();for(let D of B.values())D.status="stale";B.clear()}let v=wt(x,`pr-${h.change}`);if(v.box=x.boxId??null,ev.has(h.change)&&h.number!=null){let D=tv(k,S,h);if(k.size>=2){for(let ue of S.values())for(let ge of ue)ge.grouped=!0;v.groupPrs=[...k.values()];let Z=[v];for(let ue of k.keys())S.set(ue,Z)}else{let Z=S.get(D)??[];Z.push(v),S.set(D,Z)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let D=Um(h),Z=$&&!$.complete||k.size>=2;if(k.has(D))k.delete(D),S.delete(D);else if(h.number!=null)for(let ue of[...k.keys()])ue.endsWith(`#${h.number}`)&&(k.delete(ue),S.delete(ue));if(Z&&h.number!=null){(!$||$.complete)&&($={id:x.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:be(x),ts:be(x),box:x.boxId,agent:x.provider},t.push($)),$.prs.push({number:h.number,url:h.url??null,title:h.title??null,repo:h.repo??null,branch:h.branch??null,change:h.change,ts:be(x)}),$.remaining=[...k.values()],$.complete=k.size===0;break}}t.push(v);break}case"pr.offered":{if(!h.offerId)break;for(let D of M.values())D.status="stale";M.clear();for(let D of B.values())D.status="stale";B.clear();let v={id:x.id,kind:"pr-offer",offerId:h.offerId,excerpt:h.excerpt||null,branch:h.branch||null,base:h.base||null,offered:Array.isArray(h.offered)&&h.offered.length?h.offered:null,toPr:h.toPr&&typeof h.toPr=="object"?{number:h.toPr.number??null}:null,available:Array.isArray(h.available)?h.available:null,defaults:Array.isArray(h.defaults)?h.defaults:null,prState:h.prState||null,openPr:h.openPr&&typeof h.openPr=="object"?h.openPr:null,repos:Array.isArray(h.repos)&&h.repos.length?h.repos:null,status:"open",source:x.source==="cli"||h.via==="cli"?"cli":"transcript",ts:be(x),box:x.boxId,agent:x.provider};M.set(h.offerId,v),L.set(h.offerId,v),t.push(v),v.source==="cli"&&ve(v);break}case"pr.offer.answered":{let v=L.get(h.offerId);v&&(v.status=h.action||"dismiss",v.actions=Array.isArray(h.actions)?h.actions:null,v.notify=h.notify===!0,h.existingPr&&typeof h.existingPr=="object"&&(v.existingPr={number:h.existingPr.number??null}),M.delete(h.offerId));break}case"pr.exists.offered":{if(!h.offerId)break;for(let D of B.values())D.status="stale";B.clear();let v={id:x.id,kind:"pr-exists-offer",offerId:h.offerId,srcOfferId:h.srcOfferId??null,branch:h.branch||null,base:h.base||null,pr:h.pr&&typeof h.pr=="object"?{number:h.pr.number??null,url:h.pr.url??null,title:h.pr.title??null}:null,actions:Array.isArray(h.actions)&&h.actions.length?h.actions:null,notify:h.notify===!0,status:"open",ts:be(x),box:x.boxId,agent:x.provider};B.set(h.offerId,v),A.set(h.offerId,v),t.push(v);break}case"pr.exists.answered":{let v=A.get(h.offerId);v&&(v.status=h.choice||"dismiss",B.delete(h.offerId));break}case"box.state.changed":{let v=h.change==="model"?"model-change":h.change;if(!v)break;let D=wt(x,v);if(v==="model-change"&&(D.to=h.model),v==="node-update"&&D.latest){let Z=t.findIndex(ue=>ue.kind==="event"&&ue.type==="node-update"&&ue.latest===D.latest);if(Z>=0){let ue=t[Z];t[Z]={...D,ts:ue.ts,at:ue.at,firstAt:ue.firstAt??ue.ts,updatedAt:D.ts};break}}t.push(D);break}case"track.transferred":case"track.recovered":t.push(wt(x,"track-moved"));break;case"view.state.changed":{if(h.change==="deleted"){f(`view:${h.viewId}`);break}if(h.change!=="created"||h.restarted)break;p(`view:${h.viewId}`,{id:x.id,kind:"view-card",viewId:h.viewId,name:h.name,slug:h.slug,port:h.port,origin:h.origin,ts:be(x),box:x.boxId,agent:x.provider});break}case"view.spinup.failed":{t.push({id:x.id,kind:"view-spinup-failed",name:h.name||null,reason:h.reason||"no-view",ts:be(x),box:x.boxId,agent:x.provider});break}case"mock.state.changed":{if(h.change==="picked"){t.push({id:x.id,kind:"mock-picked",mockId:h.mockId,name:h.name,group:h.group??null,version:h.version??null,siblings:h.siblings??[],ts:be(x),box:x.boxId,agent:x.provider});break}if(h.change==="deleted"){f(`mock:${h.mockId}`);break}if(h.change!=="created")break;p(`mock:${h.mockId}`,{id:x.id,kind:"mock-card",mockId:h.mockId,name:h.name,group:h.group??null,ts:be(x),box:x.boxId,agent:x.provider});break}case"flow.state.changed":{if(h.change==="deleted"){f(`flow:${h.flowId}`);break}if(h.change!=="created")break;p(`flow:${h.flowId}`,{id:x.id,kind:"flow-card",flowId:h.flowId,name:h.name,stepCount:h.stepCount??null,mockIds:h.mockIds??[],ts:be(x),box:x.boxId,agent:x.provider});break}case"runbook.state.changed":{if(h.change==="deleted"){f(`runbook:${h.runbookId}`);break}if(h.change!=="created")break;p(`runbook:${h.runbookId}`,{id:x.id,kind:"runbook-card",runbookId:h.runbookId,name:h.name,ts:be(x),box:x.boxId,agent:x.provider});break}case"ticket.linked":{t.push({id:x.id,kind:"ticket-card",provider:h.provider||"linear",key:h.key,url:h.url,title:h.title??null,linkSource:h.linkSource??null,ts:be(x),box:x.boxId,agent:x.provider});break}case"box.fullscreen.detected":{if(!h.offerId)break;for(let D of K.values())D.box===(h.box||x.boxId)&&(D.status="stale",K.delete(D.offerId));let v={id:x.id,kind:"fullscreen-offer",offerId:h.offerId,status:"open",ts:be(x),box:h.box||x.boxId,agent:h.agent||x.provider};K.set(h.offerId,v),R.set(h.offerId,v),t.push(v);break}case"box.fullscreen.answered":{let v=R.get(h.offerId);v&&(v.status=h.action==="restarted"?"restarted":h.action==="cleared"?"cleared":"dismiss",K.delete(h.offerId));break}case"ticket.move.offered":{if(!h.offerId)break;for(let D of I.values())D.key===h.key&&(D.status="stale",I.delete(D.offerId));let v={id:x.id,kind:"ticket-offer",offerId:h.offerId,provider:h.provider||"linear",key:h.key,url:h.url??null,phase:h.phase??null,from:h.from??null,to:h.to?.name??null,prNumber:h.prNumber??null,status:"open",ts:be(x),box:x.boxId,agent:x.provider};I.set(h.offerId,v),j.set(h.offerId,v),t.push(v);break}case"ticket.move.answered":{let v=j.get(h.offerId);v&&(v.status=h.action==="moved"?"moved":"dismiss",I.delete(h.offerId));break}case"ticket.state.changed":{for(let v of I.values())v.key===h.key&&v.to===h.to&&(v.status="stale",I.delete(v.offerId));if(h.mode==="offer")break;t.push(wt(x,"ticket-state"));break}case"ticket.commented":{t.push({id:x.id,kind:"ticket-comment",key:h.key,url:h.url??null,excerpt:h.excerpt??null,ts:be(x),box:x.boxId,agent:x.provider});break}case"ticket.flow.changed":t.push(wt(x,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let D of T.values())D.status="stale";T.clear();let v={id:x.id,kind:"ticket-create-offer",proposalId:h.proposalId,provider:h.provider||"linear",epic:h.epic??null,issues:Array.isArray(h.issues)?h.issues:[],team:h.team??null,needsTeam:!!h.needsTeam,linkCurrentTrack:!!h.linkCurrentTrack,status:"open",ts:be(x),box:x.boxId,agent:x.provider};T.set(h.proposalId,v),C.set(h.proposalId,v),t.push(v),ve(v);break}case"ticket.create.answered":{let v=C.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",v.createdCount=h.createdCount??null,v.failedCount=h.failedCount??null,T.delete(h.proposalId));break}case"loop.create.offered":{if(!h.proposalId)break;for(let D of P.values())D.status="stale";P.clear();let v={id:x.id,kind:"loop-create-offer",proposalId:h.proposalId,name:h.name??"",schedule:h.schedule??"",prompt:h.prompt??"",expectation:h.expectation??null,notify:h.notify??"off",assignment:h.assignment??{provider:"claude"},repo:h.repo??null,nodeId:h.nodeId??null,nodeName:h.nodeName??null,status:"open",ts:be(x),box:x.boxId,agent:x.provider};P.set(h.proposalId,v),W.set(h.proposalId,v),t.push(v),ve(v);break}case"loop.create.answered":{let v=W.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",P.delete(h.proposalId));break}case"followup.armed":{if(!h.armId)break;let v=h.replaces?_e.get(h.replaces):null;v&&v.arm.status==="armed"&&ne(v,"replaced");let D={armId:h.armId,seq:Number.isInteger(h.seq)?h.seq:0,label:h.label??"",prompt:h.prompt??"",why:h.why??null,standing:h.standing==="never"?"never":"offerable",status:"armed"},Z=h.groupId??null,ue=Z?Pe.get(Z):null;ue||(ue={id:x.id,kind:"followup-armed",groupId:Z,armId:D.armId,trigger:h.trigger??"now",label:D.label,prompt:D.prompt,why:D.why,planWhy:h.planWhy??null,arms:[],status:"armed",ts:be(x),box:x.boxId,agent:x.provider},Z&&Pe.set(Z,ue),ue.trigger!=="now"&&t.push(ue)),ue.arms.push(D),_e.set(h.armId,{arm:D,line:ue});break}case"followup.armed.cancelled":{let v=_e.get(h.armId);v&&v.arm.status==="armed"&&ne(v,"cancelled");break}case"followup.offered":{if(!h.offerId)break;for(let Z of Y.values())Z.trigger===h.trigger&&(Z.status="stale",Y.delete(Z.offerId));let v=Array.isArray(h.rows)?h.rows:[];for(let Z of v){let ue=Z.armId?_e.get(Z.armId):null;ue&&ue.arm.status==="armed"&&ne(ue,"offered")}let D={id:x.id,kind:"followup-offer",offerId:h.offerId,trigger:h.trigger??"now",repo:h.repo??null,context:h.context&&typeof h.context=="object"?h.context:{},groupId:h.groupId??null,why:h.why??null,rows:v.map(Z=>({followupId:Z.followupId??null,armId:Z.armId??null,label:Z.label??"",prompt:Z.prompt??null,promptPreview:Z.promptPreview??null,promptHash:Z.promptHash??null,origin:Z.origin??"operator",why:Z.why??null,groupId:Z.groupId??null,seq:Number.isInteger(Z.seq)?Z.seq:0,standing:Z.standing==="never"?"never":"offerable",promotable:Z.armId?Z.promotable!==!1:!1,promotableReason:Z.promotableReason??null,rowStatus:"open"})),status:"open",answer:null,answers:[],ts:be(x),box:x.boxId,agent:x.provider};Y.set(h.offerId,D),me.set(h.offerId,D),t.push(D),D.trigger==="now"&&ve(D);break}case"followup.answered":{let v=me.get(h.offerId);if(!v)break;let D=new Set;h.armId&&D.add(`arm:${h.armId}`),h.followupId&&D.add(`fu:${h.followupId}`);for(let ge of Array.isArray(h.armIds)?h.armIds:[])D.add(`arm:${ge}`);for(let ge of Array.isArray(h.followupIds)?h.followupIds:[])D.add(`fu:${ge}`);let Z=h.action==="sent"?"sent":h.action==="removed"?"removed":"dismiss";for(let ge of v.rows){if(ge.rowStatus!=="open")continue;let ct=ge.armId?`arm:${ge.armId}`:`fu:${ge.followupId}`;D.size&&!D.has(ct)||(ge.rowStatus=Z)}let ue={followupId:h.followupId??null,armId:h.armId??null,label:h.label??null,promoted:h.promoted??null,revived:h.revived===!0,action:Z,batchId:h.batchId??null};v.answers.push(ue),v.answer=ue,v.rows.some(ge=>ge.rowStatus==="open")?v.status="open":(v.status=v.rows.some(ge=>ge.rowStatus==="sent")?"sent":v.rows.some(ge=>ge.rowStatus==="removed")?"removed":"dismiss",Y.delete(h.offerId));break}case"followup.create.offered":{if(!h.proposalId)break;for(let D of re.values())D.status="stale";re.clear();let v={id:x.id,kind:"followup-create-offer",proposalId:h.proposalId,label:h.label??"",prompt:h.prompt??"",trigger:h.trigger??"pr.merged",repo:h.repo??null,why:h.why??null,status:"open",created:null,ts:be(x),box:x.boxId,agent:x.provider};re.set(h.proposalId,v),Ce.set(h.proposalId,v),t.push(v),ve(v);break}case"followup.create.answered":{let v=Ce.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",re.delete(h.proposalId));break}case"followup.created":{let v=h.proposalId?Ce.get(h.proposalId):null;v&&(v.created={followupId:h.followupId??null});break}case"loop.created":{let v=h.proposalId?W.get(h.proposalId):null;v&&(v.created={loopId:h.loopId??null,nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null}),t.push(wt(x,"loop-created"));break}case"loop.update.offered":case"loop.delete.offered":{if(!h.proposalId||!h.loopId)break;for(let Z of J.values())Z.loopId===h.loopId&&(Z.status="stale",Z.staleReason="superseded",J.delete(Z.proposalId));let v=x.type==="loop.update.offered",D={id:x.id,kind:v?"loop-update-offer":"loop-delete-offer",proposalId:h.proposalId,loopId:String(h.loopId),name:h.name??h.base?.name??"",base:h.base??null,...v?{patch:h.patch??{},changed:Array.isArray(h.changed)?h.changed:Object.keys(h.patch||{})}:{reason:h.reason??""},selfEdit:!!h.selfEdit,status:"open",staleReason:null,ts:be(x),box:x.boxId,agent:x.provider};J.set(h.proposalId,D),he.set(h.proposalId,D),t.push(D),ve(D);break}case"loop.update.answered":{let v=he.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="applied"?"applied":"dismiss",J.delete(h.proposalId));break}case"loop.delete.answered":{let v=he.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="delete"?"deleted":h.action==="pause"?"paused":"keep",J.delete(h.proposalId));break}case"loop.updated":{let v=h.proposalId?he.get(h.proposalId):null;v&&(v.applied={changed:Array.isArray(h.changed)?h.changed:[],nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null}),t.push(wt(x,"loop-updated"));break}case"loop.deleted":t.push(wt(x,"loop-deleted"));break;case"loop.paused":t.push(wt(x,"loop-paused"));break;case"planning.change.offered":{if(!h.proposalId||!Array.isArray(h.ops)||!h.ops.length)break;for(let D of Fe.values())D.status="stale",D.staleReason="superseded",Fe.delete(D.proposalId);let v={id:x.id,kind:"planning-offer",proposalId:h.proposalId,why:h.why??"",ops:h.ops,base:h.base??{},resolved:h.resolved??{},snapped:!!h.snapped,createsMeRow:!!h.createsMeRow,status:"open",staleReason:null,applied:null,ts:be(x),box:x.boxId,agent:x.provider};Fe.set(h.proposalId,v),O.set(h.proposalId,v),t.push(v),ve(v);break}case"planning.change.answered":{let v=O.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="applied"?"applied":"dismiss",Fe.delete(h.proposalId));break}case"planning.changed":{let v=h.proposalId?O.get(h.proposalId):null;v&&(v.applied={results:Array.isArray(h.results)?h.results:[]}),t.push(wt(x,"planning-changed"));break}case"rework.cause.offered":{if(!h.sha||oe.has(h.sha))break;let v={id:x.id,kind:"rework-cause-offer",sha:h.sha,subject:h.subject??"",linesReworked:Number(h.linesReworked)||0,origins:Array.isArray(h.origins)?h.origins:[],heuristic:h.heuristic??null,intent:h.intent??null,shas:Array.isArray(h.shas)?h.shas:null,pr:h.pr??null,retracts:0,status:"open",cause:null,ts:be(x),box:x.boxId,agent:x.provider};oe.set(h.sha,v),t.push(v);break}case"rework.cause.answered":{let v=h.sha?oe.get(h.sha):null;v&&(v.status="answered",v.cause=h.cause??null);break}case"rework.cause.retracted":{let v=h.sha?oe.get(h.sha):null;v&&(v.status="open",v.cause=null,v.retracts=(v.retracts??0)+1);break}case"ticket.created":{let v={items:Array.isArray(h.items)?h.items:[],failed:Array.isArray(h.failed)?h.failed:[],team:h.team??null},D=h.proposalId?C.get(h.proposalId):null;if(D){D.created=v;break}t.push({id:x.id,kind:"ticket-created",...v,ts:be(x),box:x.boxId,agent:x.provider});break}case"error":{if(!h.kind)break;t.push(wt(x,h.kind));break}case"provider.error":t.push({...wt(x,"provider-error"),provider:x.provider,box:x.boxId,agent:x.provider});break;case"usage.cap.hit":t.push(wt(x,"usage-cap-hit"));break;case"usage.cap.warning":t.push(wt(x,"usage-cap-warning"));break;case"provider.connected":{t.push({id:x.id,kind:"event",type:"provider-connected",provider:h.provider||x.provider,ts:be(x),at:be(x),box:x.boxId,agent:x.provider});break}case"prompt.answered":{if(X=null,h.control||!h.question)break;let v=h.groupId||h.promptId;if(!v)break;let D=Re.get(v);D||(D={id:x.id,kind:"prompt",groupId:v,provider:x.provider,box:x.boxId,agent:x.provider,questionTotal:h.questionTotal??1,questions:[],ts:be(x)},Re.set(v,D));let Z=h.questionIndex??0,ue={index:Z,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},ge=D.questions.findIndex(ze=>ze.index===Z&&Ue(ze.question)===Ue(ue.question));ge>=0?D.questions[ge]=ue:D.questions.push(ue),D.questions.sort((ze,lt)=>ze.index-lt.index),D.questionTotal=Math.max(D.questionTotal,h.questionTotal??1),D.questions.length>=D.questionTotal&&(t.push(D),Re.delete(v));break}case"prompt.dismissed":{X=null;let v=h.groupId||h.promptId;if(!v||!h.question)break;let Z=Re.get(v)||{id:x.id,kind:"prompt",groupId:v,provider:x.provider,box:x.boxId,agent:x.provider,questionTotal:h.questionTotal??1,questions:[],ts:be(x)},ue=h.questionIndex??0;Z.questions.some(ge=>ge.index===ue&&Ue(ge.question)===Ue(h.question))||(Z.questions.push({index:ue,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:[]}),Z.questions.sort((ge,ct)=>ge.index-ct.index)),Z.answeredVia="chat",t.push(Z),Re.delete(v);break}default:break}}let In=d.size?t.filter(x=>!x.retired):t;return rB(In.sort((x,h)=>(x.ts??0)-(h.ts??0)))}function rB(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<=nB){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 Hm(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:oB(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??Nn(e)).filter(o=>o.kind==="message");return Y0(n,r)}function oB(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==Qs(e.command)?t:Wr(e.command,e.description||"")}function wt(e,t){let{ts:n,change:r,kind:o,...s}=e.payload||{};return{id:e.id,kind:"event",type:t,...s,at:be(e),ts:be(e)}}function nv(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)+sB,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&aB.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function rv(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!cB.has(r.type)||!r.box)continue;let o=(r.ts??0)+iB,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}function Nu(e){return(e||[]).filter(t=>lB.has(t?.kind)&&t.status==="open")}function ov(e){let t=new Map,n=new Map;for(let r of e||[]){if(r.type!=="pr.state.changed")continue;let o=r.payload||{};if(ev.has(o.change)&&o.number!=null)tv(t,n,o);else if(o.change==="merged"||o.change==="closed"||o.change==="lost"){let s=Um(o);if(t.has(s))t.delete(s);else if(o.number!=null)for(let a of[...t.keys()])a.endsWith(`#${o.number}`)&&t.delete(a)}}return[...t.values()]}var be,ev,Um,nB,sB,aB,iB,cB,lB,Pu=q(()=>{ea();Ho();xu();Ki();Cu();Fm();be=e=>e.payload?.ts??e.ts;ev=new Set(["opened","ci-fixed","reconciled","pushed"]),Um=e=>`${e.repo??""}#${e.number}`;nB=2*6e4;sB=12e4,aB=new Set(["archived","unarchived","opened"]);iB=2500,cB=new Set(["pr-opened","pr-merged","pr-auto-armed"]);lB=new Set(["pr-offer","pr-exists-offer","ticket-offer","ticket-create-offer","loop-create-offer","loop-update-offer","loop-delete-offer","planning-offer","fullscreen-offer","followup-create-offer"])});function mB(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 wB(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 gB(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<pB&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<fB&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function cv(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=mB(e),s=wB(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=Nn(e??[]).filter(p=>p?.kind==="message"&&typeof p.text=="string"&&p.text.trim()).slice(-uB);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(iv[r]??iv.failover),l.push(""),o.lastPush?.sha){let p=av(n,o.lastPush.ts),f=p?` (~${p} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${f}`+(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 p=av(n,i);l.push(`## Last real checkpoint (written by the previous agent${p?`, ~${p} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=gB(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(p=>`\`${p}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(p=>`\`${p}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let p of c)l.push(`**${p.role==="user"?"operator":"agent"}:** ${hB(p.text,dB)}`),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(`
|
|
154
|
+
`);return d.length<=sv?d:`${d.slice(0,sv)}
|
|
155
|
+
|
|
156
|
+
[\u2026 brief truncated]`}var uB,dB,sv,pB,fB,hB,av,iv,lv=q(()=>{Pu();uB=12,dB=2e3,sv=48e3,pB=30,fB=10;hB=(e,t)=>{let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1)}\u2026`},av=(e,t)=>e&&t?Math.max(1,Math.round((e-t)/6e4)):null;iv={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.",get recover(){return this.failover},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 yB from"node:fs";import Gr from"node:fs/promises";import Kr from"node:path";function gv(e){let t=Do(String(e??"")),n=Buffer.byteLength(t,"utf8");return n<=uv?{content:t,bytes:n,truncated:!1}:{content:`${Buffer.from(t,"utf8").subarray(0,uv).toString("utf8").replace(/�+$/,"")}
|
|
157
|
+
|
|
158
|
+
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function IB(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(pv.test(s)){if(r.length>=Sv)continue;let a=EB(s.replace(pv,"").replace(/[.;,:]+$/,""),Av,AB);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>dv&&(o=`${o.slice(0,dv-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Tv(e,t,{timeoutMs:n=9e4}={}){let r=Kr.join(t,vB),o=Kr.join(r,SB);await Gr.mkdir(r,{recursive:!0});let s=await Du(o),a=await xe(e);if(!a)throw new Error(`session "${e}" not found`);return await Bu(e,a,TB(o)),await Mv(o,s,n),IB(await Gr.readFile(o,"utf8"))}function Ev(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===bv||n===vv;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(yv)||n===bB||r.startsWith(kv)||r.startsWith(xv)}function Iv(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-_B;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 _v(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 $v(e,{windows:t=[]}={}){if(Ev(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 ju(e){return Wm.get(e)||null}async function Rv(e){let t=Wm.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await xe(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:Kr.join(n.cwd,hv,wv)};return Wm.set(e,r),OB(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function OB(e,t){e.step="summarize",await Ov(e.name,t),e.step="clear";let n=await xe(e.name);await Bu(e.name,n,"/clear"),await qm($B),e.step="resume",await Bu(e.name,n,xB),e.step="done",e.finishedAt=Date.now()}async function Ov(e,t,{timeoutMs:n=RB}={}){let r=Kr.join(t,hv),o=Kr.join(r,wv);await Gr.mkdir(r,{recursive:!0});let s=await Du(o),a=await xe(e);await fe({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await Bu(e,a,kB(o)),await Mv(o,s,n),await fe({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await Du(o)});let i=await Bx(t,o,{exclude:u=>Ev(u.role,u.text)});await fe({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(c){await N({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 Gr.readFile(o,"utf8");await N({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...gv(u),ts:Date.now()}})}catch{}}await Kf(t);let l=await MB(r,o);return c&&await w0(c,l||o,{box:e,agent:a?.agent??null}),o}async function Lu(e,{timeoutMs:t}={}){let n=await xe(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return Ov(e,n.cwd,t?{timeoutMs:t}:void 0)}async function Fu(e,t,{trackName:n="this Track"}={}){let r=await G({id:e,cwd:t}),o=cv(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=Kr.join(t,fv);return await Gr.mkdir(Kr.dirname(s),{recursive:!0}),await Gr.writeFile(s,o,"utf8"),await N({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...gv(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),fv}async function Du(e){try{return(await Gr.stat(e)).mtimeMs}catch{return 0}}async function Mv(e,t,n){let r=Date.now()+n;for(;;){if(await Du(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await qm(2e3)}}async function MB(e,t){try{if(!yB.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=Kr.join(e,`${n}.md`);return await Gr.copyFile(t,r),r}catch{return null}}function qm(e){return new Promise(t=>setTimeout(t,e))}function mv(e){return e==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":e==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":e==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":""}async function Bu(e,t,n){if(t){let o=mv(t.agent);await sr({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await xe(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await Ke(e,["Escape"],{kind:"drive-wipe"}),await qm(150),await Js(e,gu,{kind:"drive-wipe"}),await at(e,n,{kind:"orchestration-drive",...Cn(r.agent,n)}),await zi({sessionName:e,agent:r.agent,agentCmd:mv(r.agent),text:n,flow:"orchestration-drive"})}var hv,wv,uv,yv,bv,kv,bB,kB,xB,xv,vv,vB,SB,dv,Sv,Av,AB,TB,pv,EB,_B,$B,RB,Wm,fv,Gm=q(()=>{ht();ra();Rn();qi();Om();jx();vi();ye();cu();lv();hv=".bostrat/handoff",wv="latest.md",uv=32*1024;yv="Checkpoint: write a work-in-progress handoff",bv="HANDOFF WRITTEN",kv="Resume from .bostrat/handoff/latest.md",bB="/clear",kB=e=>`${yv} 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 ${bv} and take no further action.`,xB=`${kv}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,xv="Beat: write a",vv="BEAT WRITTEN",vB=".bostrat/arc",SB="beat.txt",dv=200,Sv=4,Av=5,AB=36,TB=e=>`${xv} 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 ${Sv} further lines, each starting with "- " and naming ONE piece in at most ${Av} 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 ${vv} and take no further action.`,pv=/^(?:[-–—*•·]|\d+[.)])\s+/,EB=(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};_B=6e5;$B=3500,RB=3e5,Wm=new Map;fv=".bostrat/handoff/status-latest.md"});function zr(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!Uu.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 Pv(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:zr(o.from,"from",n),toMs:zr(o.to,"to",n)}))}return t}function Vm(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 NB(e){if(!e)return null;let t=String(e).trim();if(Vr[t])return t;if(Cv[t])return Cv[t];let n=null;for(let r of Object.keys(Vr))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function PB(e){if(!e||e===Vr)return CB;let t=Nv.get(e);return t||(t=Pv(e),Nv.set(e,t)),t}function Dv(e,t,n={}){let{table:r,speed:o}=n,s=PB(r),a=r&&r!==Vr?s[e]?e:null:NB(e);if(!a)return null;let i=Vm(t);if(i===null)return null;let c=s[a].find(l=>i>=l.fromMs&&i<=l.toMs)||null;return c?o==null||o==="standard"?c:o==="fast"&&c.fast?{...c.fast,fromMs:c.fromMs,toMs:c.toMs,verifiedAt:c.verifiedAt,note:c.note,speed:"fast"}:null:null}function Pn(e,t={},n,r){let o=Dv(e,n,r);return o?((t.input||0)*o.input+(t.output||0)*o.output+(t.cacheWrite||0)*o.cacheWrite+(t.cacheRead||0)*o.cacheRead)/1e6:0}function DB(e=Vr){let t=[];for(let[n,r]of Object.entries(e)){if(!Array.isArray(r)||r.length===0){t.push(`${n}: must be a non-empty array of windows`);continue}let o;try{o=r.map(a=>({fromMs:zr(a.from,"from",n),toMs:zr(a.to,"to",n)}))}catch(a){t.push(String(a.message));continue}let s=r.filter(a=>a.to==null);s.length!==1?t.push(`${n}: expected exactly 1 open-ended window (to: null), found ${s.length}`):r[r.length-1].to!=null&&t.push(`${n}: the open-ended window must be last`),r[0].from!=null&&t.push(`${n}: the first window must be open at the start (from: null)`);for(let a=0;a<o.length;a++){if(o[a].fromMs>o[a].toMs&&t.push(`${n}: window ${a} ends before it begins (${r[a].from} \u2192 ${r[a].to})`),a===0)continue;let i=o[a-1],c=o[a];c.fromMs<=i.toMs?t.push(`${n}: windows ${a-1} and ${a} overlap (${r[a-1].to} / ${r[a].from})`):c.fromMs-i.toMs>2&&t.push(`${n}: gap between windows ${a-1} and ${a} (${r[a-1].to} \u2192 ${r[a].from})`),r[a].from||t.push(`${n}: window ${a} must declare a from bound`)}for(let a of r){a.note||t.push(`${n}: every window needs a note recording what was verified and when`),Uu.test(String(a.verifiedAt??""))||t.push(`${n}: every window needs a verifiedAt (YYYY-MM-DD) \u2014 the date its rates were checked against the vendor's published page`);for(let i of["input","output","cacheWrite","cacheRead"])(typeof a[i]!="number"||!Number.isFinite(a[i])||a[i]<0)&&t.push(`${n}: window ${i} must be a non-negative number, got ${JSON.stringify(a[i])}`),a.fast&&(typeof a.fast[i]!="number"||!Number.isFinite(a.fast[i])||a.fast[i]<0)&&t.push(`${n}: fast variant ${i} must be a non-negative number, got ${JSON.stringify(a.fast?.[i])}`)}}return t}function jB({now:e=Date.now(),table:t=Vr,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...BB,...n},a=[],i=Vm(e);if(i===null)return[`pricingFreshness: unparseable now ${JSON.stringify(e)}`];for(let[c,l]of Object.entries(t)){if(!Array.isArray(l))continue;let u;try{u=l.map(d=>({...d,fromMs:zr(d.from,"from",c),toMs:zr(d.to,"to",c),verifiedMs:Uu.test(String(d.verifiedAt??""))?Date.parse(`${d.verifiedAt}T00:00:00.000Z`):NaN}))}catch(d){a.push(String(d.message));continue}for(let d of u){if(!Number.isFinite(d.verifiedMs)){a.push(`${c}: window ${d.from??"\u2026"} \u2192 ${d.to??"now"} has no verifiedAt`);continue}let p=i>=d.fromMs&&i<=d.toMs,f=d.fromMs>i&&d.fromMs-i<=o*Km;p&&i-d.verifiedMs>r*Km&&a.push(`${c}: current rate last verified ${d.verifiedAt} (>${r}d) \u2014 re-check the vendor page and bump verifiedAt`),f&&d.fromMs-d.verifiedMs>s*Km&&a.push(`${c}: scheduled rate change on ${d.from} was last verified ${d.verifiedAt} \u2014 confirm it is still happening (vendors cancel; sonnet-5 did) and bump verifiedAt`)}}return a}function aa({now:e=Date.now(),table:t=Vr,...n}={}){let r=Vm(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=zr(l.from,"from",i),d=zr(l.to,"to",i);r>=u&&r<=d&&Uu.test(String(l.verifiedAt??""))&&(!o||l.verifiedAt<o)&&(o=l.verifiedAt),u>r&&s.push({model:i,from:l.from,verifiedAt:l.verifiedAt??null})}s.sort((i,c)=>i.from<c.from?-1:1);let a=[...DB(t),...jB({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var Vr,Cv,Uu,CB,Nv,Bv,BB,Km,ir=q(()=>{Vr={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"GA rates; verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-08-17",note:"$2/$10 is the standard price (intro pricing made permanent; the 2026-09-01 $3/$15 increase was cancelled) \u2014 verified 2026-08-17 against platform.claude.com/docs/en/about-claude/pricing."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-fable-5-1":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:.25,cacheReadX:.025,verifiedAt:"2026-09-07",note:"verified 2026-09-07 against platform.claude.com/docs/en/about-claude/pricing: $10/$50, 1h cache write $20 (2\xD7), cache hits $0.25 (0.025\xD7 \u2014 the 5.1-only multiplier; footnote 1 on the page)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-03, re-verified 2026-08-17 (developers.openai.com pricing); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:"2026-08-23",input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL). Superseded 2026-08-24 by the vendor's promotional cut (window below)."},{from:"2026-08-24",to:null,input:4,output:20,cacheWrite:5,cacheRead:.4,verifiedAt:"2026-08-24",note:"promotional pricing verified 2026-08-24 (developers.openai.com pricing), 'at least through November 21, 2026'; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:"2026-08-16",input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:2,output:12,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:"2026-08-16",input:1,output:6,cacheWrite:1.25,cacheRead:.1,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:.2,output:1.2,cacheWrite:.25,cacheRead:.02,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-6-astra":[{from:null,to:null,input:10,output:50,cacheWrite:12.5,cacheRead:1,fast:{input:20,output:100,cacheWrite:25,cacheRead:2},verifiedAt:"2026-09-07",note:"verified 2026-09-07 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input; fast tier $20/$100 recorded as the fast variant (codex reports no speed)."}]},Cv={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-1","gpt-5.6":"gpt-5.6-sol"},Uu=/^\d{4}-\d{2}-\d{2}$/;CB=Pv(Vr);Nv=new WeakMap;Bv=(e,t,n)=>Dv(e,t,n)!==null;BB=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),Km=864e5});function LB(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function Lv(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function Xt(e){let t=e.payload?.model||"unknown",n=LB(e.payload?.tokens),r=Number(e.payload?.usd)||0,o=e.payload?.speed||void 0,s=e.source==="vendor",a=r,i=!1;if(n&&!s){let u=Pn(t,n,e.ts,o?{speed:o}:void 0);u>0&&(a=u,i=Math.abs(u-r)>1e-9)}let c=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,l=!a&&c>0&&!Bv(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function Jr(e,{boxLabels:t={}}={}){let n=(e||[]).filter(p=>p.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,d=jv();for(let p of n){let{usd:f,model:w,tokens:m,repriced:g,unpriced:y,asRecorded:b}=Xt(p);i+=b;let k=p.provider||"unknown",S=p.boxId||"unattributed";if(g&&c++,y&&(l++,u.add(w)),a+=f,Lv(d,m),r[w]||={usd:0,tokens:jv(),events:0},r[w].usd+=f,r[w].events+=1,Lv(r[w].tokens,m),o[k]||={usd:0,events:0},o[k].usd+=f,o[k].events+=1,s[S]||={usd:0,events:0,provider:k,models:new Set,label:t[S]||S},s[S].usd+=f,s[S].events+=1,w!=="unknown"&&s[S].models.add(w),p.epoch!=null){let $=(s[S].epochs||={})[p.epoch]||={usd:0,events:0,provider:k,models:new Set};$.usd+=f,$.events+=1,$.provider=k,w!=="unknown"&&$.models.add(w)}}return{totalUsd:ia(a),asRecordedUsd:ia(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Hu(r,p=>({...p,usd:ia(p.usd)})),byProvider:Hu(o,p=>({...p,usd:ia(p.usd)})),byBox:Hu(s,p=>({usd:ia(p.usd),events:p.events,provider:p.provider,label:p.label,models:[...p.models],...p.epochs?{epochs:Hu(p.epochs,f=>({usd:ia(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var jv,ia,Hu,Yr=q(()=>{ir();jv=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});ia=e=>Math.round(e*1e4)/1e4,Hu=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function FB(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 Fv(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 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[p,f]=d.split("#");return`${p.split("/").pop()}#${f}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(zm(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(zm(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(`${zm(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=Jr(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?FB(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var zm,Uv=q(()=>{Yr();zm=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as UB}from"node:child_process";import{promisify as HB}from"node:util";import{randomUUID as WB}from"node:crypto";async function Jm(e,t){let n=Date.now()+t;for(;;){if(await or(e))return!0;if(Date.now()>n)return!1;await Qi(qB)}}async function KB(e,t){let n=Hv[t]||Hv.default;n.escape&&(await Ke(e,["Escape"],{kind:"swap-exit"}),await Qi(150)),n.wipe&&await Js(e,gu,{kind:"swap-exit"}),await yu(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Cn(t).submitDelayMs||0;await Qi(Math.max(r,250)),await Ke(e,["Enter"],{kind:"swap-exit"})}async function Wv(e,t){try{let{stdout:n}=await Go(te,se("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function Kv(e){await Go(te,se("respawn-pane","-k","-t",e))}async function Ym(e,{exitTimeoutMs:t=qv,fromAgent:n=null}={}){if(!Be(e))throw new Error("invalid session name");let r=!1;if(await or(e))return{forced:r};let o=n||(await xe(e))?.agent||"agent";if(await KB(e,o),!await Jm(e,t)&&(r=!0,await fe({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await Kv(e),!await Jm(e,Gv)))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);return{forced:r}}async function Xm(e,t={}){if(!Be(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await xe(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||Ur(n),a=Number(t.exitTimeoutMs)||qv;await fe({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let{forced:i}=await Ym(e,{exitTimeoutMs:a,fromAgent:o}),c=s==="claude"&&jo(n),l=c?Ll():null,u=await Wv(e,"ANTHROPIC_API_KEY"),d=!1;try{l?(await Go(te,se("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await Go(te,se("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),d=!0)}catch(g){throw l?du(g,l):g}if(d&&!i&&(await Kv(e),!await Jm(e,Gv)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let p=!!t.resumeSessionId,f=t.resumeSessionId||WB(),w=p?n:Mi(n,f,e),m=p?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(w);return c&&await Cr(r.cwd),s==="codex"&&await Ps(r.cwd),s==="cursor"&&await Ds(r.cwd),await Lo(e),await Go(te,se("set-option","-t",e,"@bostrat-sid",f)),await Go(te,se("set-option","-t",e,"@bostrat-agent",s)),await Qi(GB),c&&Oi().length&&(await Wv(e,Ri)||await Go(te,se("set-environment","-t",e,Ri,"1")).catch(()=>{}),await at(e,`export ${Ri}=1`,{kind:"swap-env"}),await Qi(150)),await at(e,w,{kind:"swap-launch"}),await fe({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:f,forced:i,envRespawned:d}),{name:e,cwd:r.cwd,sessionId:f,agent:s,pinned:m,forced:i,envRespawned:d,launchCmd:w}}var Go,Qi,qv,Gv,qB,GB,Hv,Vv=q(()=>{ht();pi();Ul();Hl();qi();Rn();Go=HB(UB),Qi=e=>new Promise(t=>setTimeout(t,e)),qv=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Gv=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,qB=250,GB=400,Hv={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"}}});function Zm(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Qm({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!Zm(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),zv);try{let a=await Jv(`${Ne(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 ec(e,{provider:t=null,cls:n="other",status:r=null,text:o="",note:s=null}={},{env:a=process.env,now:i=Date.now}={}){if(!e?.id||!String(e.name||"").startsWith("\u21BB"))return{ok:!1,source:"not-a-loop-track"};if(!Zm(a))return{ok:!1,source:"unconfigured"};let c=Wu.get(e.id)||0;if(i()-c<VB)return{ok:!1,source:"debounced"};Wu.set(e.id,i());let l=await tc({env:a}),u=l.loops.find(f=>f.trackId===e.id);if(!u)return Wu.delete(e.id),{ok:!1,source:l.source==="plane"?"no-loop":l.source};let d=s?String(s).replace(/\s+/g," ").trim():`${t||"provider"} ${r?r+" ":""}${n}: ${String(o||"").replace(/\s+/g," ").trim()}`,p=await Qm({loopId:u.id,outcome:"errored",note:d.slice(0,500),env:a});return p.ok||Wu.delete(e.id),{...p,loop:{id:u.id,name:u.name}}}async function tc({env:e=process.env}={}){if(!Zm(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),zv);try{let r=await Jv(`${Ne(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 zv,Jv,VB,Wu,qu=q(()=>{yt();zv=1e4,Jv=(...e)=>fetch(...e);VB=10*6e4,Wu=new Map});import{execFile as zB}from"node:child_process";import Gu from"node:fs/promises";import eh from"node:path";import{promisify as JB}from"node:util";function nj(e){let n=String(e||"").trim().match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.+-]+)?/);return n?n[0]:null}async function rj(e,{env:t=process.env}={}){let n=String(e||"").trim();if(!n)return null;let r=n.includes("/")?[eh.resolve(n)]:String(t.PATH||"").split(eh.delimiter).filter(Boolean).map(o=>eh.join(o,n));for(let o of r)try{await Gu.access(o,Gu.constants.X_OK);let s=await Gu.realpath(o),a=await Gu.stat(s);if(!a.isFile())continue;return{path:s,mtimeMs:a.mtimeMs}}catch{}return null}async function Ko({agent:e=null,agentCmd:t=null,env:n=process.env}={}){let r=String(t||(e?n[QB[e]]||ZB[e]:"")||"").trim();if(!r)return null;let{realClaude:o,realCodex:s,realCursor:a}=Wi({agent:e,agentCmd:r});if(!(o||s||a))return null;let i=await rj(r.split(/\s+/)[0],{env:n});if(!i)return null;let c=`${i.path}@${i.mtimeMs}`;return th.has(c)||th.set(c,Promise.resolve().then(()=>tj(i.path,{agent:e})).then(l=>nj(l)).catch(()=>null)),th.get(c)}var YB,XB,ZB,QB,th,ej,tj,Yv=q(()=>{ra();YB=JB(zB),XB=8e3,ZB={claude:"claude",codex:"codex",cursor:"cursor-agent"},QB={claude:"BOSTRAT_AGENT_CMD",codex:"BOSTRAT_CODEX_CMD",cursor:"BOSTRAT_CURSOR_CMD"},th=new Map,ej=async e=>(await YB(e,["--version"],{timeout:XB})).stdout,tj=ej});function cj(e){if(e.type==="queue-operation"&&e.operation==="enqueue")return Xv.test(String(e.content||""))?String(e.content):null;if(e.type==="attachment"){let n=e.attachment;return n?.type==="queued_command"&&n.commandMode==="task-notification"&&typeof n.prompt=="string"?n.prompt:null}let t=e.message?.content;return typeof t=="string"&&Xv.test(t)?t:null}async function Zv(e,t={}){return(await eS(e,t)).launches}async function Qv(e,t={}){let{launches:n,done:r}=await eS(e,t);return n.filter(o=>!r.has(o.id))}async function eS(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await Nt(e,t)}catch{return{launches:[],done:new Set}}let o=new Map,s=new Set,a=new Set,i=new Map;for(let l of r.split(`
|
|
159
|
+
`)){let u=l.trim();if(!u||u[0]!=="{")continue;let d;try{d=JSON.parse(u)}catch{continue}if(d.isSidechain)continue;let p=cj(d);if(p){let m=p.match(ij)?.[1]?.trim();m&&a.add(m);continue}let f=d.message?.content;if(!Array.isArray(f))continue;let w=d.timestamp?Date.parse(d.timestamp):null;for(let m of f)if(!(!m||typeof m!="object")){if(m.type==="tool_use"&&m.id){if(m.name==="Agent"){let g=m.input||{};o.set(m.id,{id:m.id,kind:"agent",name:String(g.description||"").trim()||null,detail:String(g.subagent_type||"").trim()||null,ts:w,proven:!1})}else if(m.name==="Bash"){let g=String(m.input?.command||"").trim(),y={id:m.id,kind:"command",name:String(m.input?.description||"").trim()||(g?Qs(g):null),detail:g?Qs(g):null,ts:w,proven:m.input?.run_in_background===!0};y.proven?o.set(m.id,y):i.set(m.id,y)}}else if(m.type==="tool_result"&&m.tool_use_id){let g=lj(m.content);if(oj.test(g)&&s.add(m.tool_use_id),g.match(sj)){let b=i.get(m.tool_use_id);b&&(i.delete(m.tool_use_id),b.proven=!0,b.outputFile=g.match(aj)?.[1]?.replace(/\.$/,"")||null,o.set(b.id,b))}}}}let c=[];for(let l of o.values()){if(!l.proven&&!s.has(l.id))continue;let{proven:u,...d}=l;c.push(d)}return{launches:c.slice(-n),done:a}}var oj,sj,aj,Xv,ij,lj,tS=q(()=>{pn();ea();oj=/^Async (?:agent|task) launched/i,sj=/^Command did not complete within its [^\n]*?moved to the background \(ID: ([^)]+)\)/i,aj=/Output is being written to:\s*(\S+)/i,Xv=/^\s*<task-notification>/i,ij=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i;lj=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 Xr(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 uj(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 nS(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:fn(`read ${Xr(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${ca(r)}`:`Read ${ca(r)}`}}case"Grep":{let r=t?.pattern;if(!r)return null;let o=t?.path?Xr(t.path,n):null,s=t?.glob?` --glob ${nh(t.glob)}`:"",a=fn(String(r),40);return{command:fn(`grep ${nh(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?Xr(t.target_directory,n):null;return{command:fn(`glob ${nh(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${fn(String(r),40)}\u201D`}}case"StrReplace":return t?.path?{command:fn(`edit ${Xr(t.path,n)}`,140),description:`Edit ${ca(t.path)}`}:null;case"Write":return t?.path?{command:fn(`write ${Xr(t.path,n)}`,140),description:`Write ${ca(t.path)}`}:null;case"Delete":return t?.path?{command:fn(`delete ${Xr(t.path,n)}`,140),description:`Delete ${ca(t.path)}`}:null;case"ReadLints":{let o=(Array.isArray(t?.paths)?t.paths:[]).map(s=>Xr(s,n)).filter(Boolean);return{command:fn(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=uj(t);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:fn(`apply_patch ${Xr(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${ca(r[0])}`}}case"Await":{let r=t?.shell_id??t?.task_id;return{command:fn(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var Ku,fn,ca,nh,rS=q(()=>{Ku=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),fn=(e,t=88)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,ca=e=>String(e).replace(/\/+$/,"").split("/").pop()||String(e);nh=e=>`"${String(e).replace(/"/g,'\\"')}"`});import nc from"node:fs";import dj from"node:fs/promises";import Vo from"node:path";import pj from"node:os";import fj from"node:crypto";import mj from"node:zlib";function aS(e){let t=fj.createHash("md5").update(String(e)).digest("hex");return Vo.join(hj,t)}function iS(e){try{let t=JSON.parse(nc.readFileSync(Vo.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 wj(e){let t=aS(e),n;try{n=nc.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=Vo.join(t,o.name),a=Vo.join(s,"store.db");if(!nc.existsSync(a))continue;let i=iS(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 Vu(e,t){if(!e)return null;if(t){let n=Vo.join(aS(e),t),r=Vo.join(n,"store.db");if(nc.existsSync(r))return{id:t,dir:n,store:r,...iS(n)||{createdAtMs:null,updatedAtMs:null}}}return wj(e)}function oS(e,t){let n=e.prepare("SELECT data FROM blobs WHERE id = ?").get(t);return n?Buffer.from(n.data):null}function gj(e){if(!e)return null;let t=e;if(t.length>1&&t[0]===31&&t[1]===139)try{t=mj.gunzipSync(t)}catch{}try{return JSON.parse(t.toString("utf8"))}catch{return null}}function yj(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 bj(e,t){if(!Array.isArray(e))return[];let n=[];for(let r of e){if(!r||r.type!=="tool-call"||!Ku.has(r.toolName))continue;let o=nS(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 rh(e,t){if(!sS)return la;let{DatabaseSync:n}=sS,r;try{r=new n(e,{readOnly:!0})}catch{return la}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 la;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return la}let a=s.lastUsedModel||null,i=s.latestRootBlobId?oS(r,s.latestRootBlobId):null;if(!i)return{...la,model:a};let c=[],l=[],u=[],d=null,p=[];for(let f of yj(i)){let w=gj(oS(r,f));if(!w)continue;let m=w.providerOptions?.cursor;if(m?.requestId&&p.push(m.requestId),m?.modelProviderMessageId&&p.push(m.modelProviderMessageId),w.role!=="assistant")continue;if(Array.isArray(w.content))for(let b of w.content){let k=b?.providerOptions?.cursor?.modelName;k&&(d=k)}l.push(...bj(w.content,t).map((b,k)=>({...b,id:b.id||`${f}:${k}`}))),u.push(...xj(w.content));let g=kj(w.content);if(!g)continue;let y=[...new Set(u)];u=[],c.push({id:f,text:g,tools:y.length?y:void 0,commands:l,model:d}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:p}}catch{return la}finally{try{r.close()}catch{}}}function cS(e,t){let n=e.updatedAtMs;if(!n)try{n=Math.round(nc.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 lS(e,t,{limit:n=80}={}){let r=Vu(e,t);if(!r)return[];let{turns:o,model:s}=await rh(r.store,e);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=cS(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 uS(e,t){let n=Vu(e,t);if(!n)return null;let{turns:r,requestIds:o,model:s}=await rh(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 dS(e,t,{limit:n=80}={}){let r=Vu(e,t);if(!r)return[];let{turns:o,trailingCommands:s}=await rh(r.store,e);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=cS(r,a.length||1),l=[];a.forEach((u,d)=>{for(let p of u.commands)l.push({...p,summary:Wr(p.command,p.description),ts:c(d)})});for(let u of s)l.push({...u,summary:Wr(u.command,u.description),ts:i+1});return l}async function pS(e,t){let n=Vu(e,t);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await dj.stat(n.store)).mtimeMs)}catch{return null}}var hj,kj,xj,sS,la,oh=q(()=>{ea();rS();hj=process.env.BOSTRAT_CURSOR_CHATS||Vo.join(pj.homedir(),".cursor","chats");kj=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(`
|
|
160
|
+
`).trim():"",xj=e=>Array.isArray(e)?e.filter(t=>t&&t.type==="tool-call"&&t.toolName).map(t=>t.toolName):[],sS=process.getBuiltinModule?.("node:sqlite"),la={turns:[],trailingCommands:[],model:null,requestIds:[]}});import vj from"node:fs/promises";function ch(e){return fS.set(e.agent,e),e}function Zr(e){return fS.get(e)||Sj}async function lh(e,t={}){return(await Zr(e.agent).readMessages(e,t)).map(zu(e))}async function mS(e,t={}){return(await Zr(e.agent).readCommands(e,t)).map(zu(e))}async function hS(e,t={}){return(await Zr(e.agent).readJobs(e,t)).map(zu(e))}async function wS(e,t={}){return(await Zr(e.agent).readOpenJobs(e,t)).map(zu(e))}var ua,sh,ah,ih,Sj,fS,zu,uh=q(()=>{pn();vu();ea();tS();oh();Ho();ua=class{constructor(t,n=new Set){this.agent=t,this.cliTools=n}async readMessages(){return[]}async readCommands(){return[]}async readJobs(){return[]}async readOpenJobs(){return[]}async updatedAt(){return null}},sh=class extends ua{transcript(t){return!t?.cwd||!t?.sessionId?null:dn(t.cwd,t.sessionId)}async readMessages(t,n){let r=this.transcript(t);return r?bm(r,n):[]}async readCommands(t,n){let r=this.transcript(t);return r?J0(r,n):[]}async readJobs(t,n){let r=this.transcript(t);return r?Zv(r,n):[]}async readOpenJobs(t,n){let r=this.transcript(t);return r?Qv(r,n):[]}async updatedAt(t){let n=this.transcript(t);if(!n)return null;try{return Math.round((await vj.stat(n)).mtimeMs)}catch{return null}}},ah=class extends ua{async readMessages(t,n){return lS(t.cwd,t.sessionId,n)}async readCommands(t,n){return dS(t.cwd,t.sessionId,n)}async updatedAt(t){return pS(t.cwd,t.sessionId)}},ih=class extends ua{sessionId(t){return t?.pinned?t.sessionId:null}async readMessages(t,n){return Q0(t.cwd,this.sessionId(t),n)}async readCommands(t,n){return ex(t.cwd,this.sessionId(t),n)}async updatedAt(t){return tx(t.cwd,this.sessionId(t))}},Sj=new ua("unknown"),fS=new Map;ch(new sh("claude",mm));ch(new ah("cursor",Ku));ch(new ih("codex",Li));zu=e=>t=>({...t,box:e.name,agent:e.agent,boxRole:e.role})});function eo(e){let t=e.search(mh);return(t>=0?e.slice(0,t):e).trim()}function Aj(e){if(!e)return"";let t=e.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!t||/hidden|\d+\s+lines?/i.test(t)?"":t}function Tj(e){let t=String(e).trim();return!!(!t||Qr.test(e)||mh.test(t)||/hidden|\d+\s+lines?/i.test(t)||/notespress|press n to add/i.test(t)||/^\s*notes?\s*$/i.test(t))}function Ej(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=Ij(n,t);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function Ij(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(p=>p.length>3&&n.includes(p)).length;if(u)return u;let d=l.slice(0,8).trim();return d.length>=4&&n.includes(d)?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 gS(e){return e.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function _j(e,t=""){let n=String(e);if(!bS.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
161
|
+
`),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 d=r[u].match(zo);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let p=r[u].trim();p&&!Qr.test(r[u])&&a.push(p)}}if(s.length<2)return null;let i=s.map(u=>({label:eo(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 Rj(e,t=""){let n=String(e).replace(/\r/g,"").split(`
|
|
162
|
+
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!kS.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(zo);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&&!($j.test(n[c])||Qr.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:eo(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 Oj(e,t=""){let n=String(e);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
163
|
+
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(zo);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 Cj(e,t=""){let n=String(e);if(!dh.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
164
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(Mj.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 d=r[u].match(zo);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let p=r[u].trim();p&&!Qr.test(r[u])&&a.push(p)}}if(s.length<2||!s.some(u=>dh.test(u.label)))return null;let i=s.map(u=>({label:eo(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 hh(e){let t=[];for(let r=0;r<e.length;r++){let o=e[r].match(zo);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 Nj(e,t=""){let n=String(e);if(!xS.test(n)||!vS.test(n)||Jo.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
165
|
+
`),o=hh(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:eo(s.label),description:""}))}]}:null}function Pj(e,t=""){let n=String(e);if(!SS.test(n)||Jo.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
166
|
+
`),o=hh(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:eo(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 Bj(e,t){let n=e[t].trim();for(let r=t+1;r<e.length;r++){let o=e[r].trim();if(!o||!Dj.test(o)||wh.test(e[r]))break;n+=o}return n}function jj(e,t=""){let n=String(e);if(!wh.test(n)||Jo.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
167
|
+
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=Bj(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 Fj(e,t=""){let n=String(e);if(!ph.test(n)||!Lj.test(n)||Jo.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
168
|
+
`)].reverse().find(a=>ph.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 Uj(e,t=""){let n=String(e);if(!AS.test(n)||Jo.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
169
|
+
`),o=hh(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:eo(s.label),description:""}))}]}:null}function Hj(e,t=""){let n=String(e);if(Jo.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
170
|
+
`),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(TS);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(Qr.test(r[l])||mh.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 Wj(e){let t=-1;for(let r=e.length-1;r>=0;r--)if(fh.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 qj(e,t){let n=e.find(s=>/^type something\b/i.test(s.label));if(n)return n;if(e.length<2)return null;let r=e[e.length-1];if(Ju.test(r.label))return null;let o=null;for(let s=r.line+1;s<Math.min(t.length,r.line+3);s++){let a=t[s].replace(/^\s*❯/,"").trim();if(a){o=a;break}}return o&&(Qr.test(o)||/^submit\b/i.test(o))?r:null}function Gj(e,t=""){let n=String(e);if(!yS.test(n)||!fh.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
171
|
+
`),o=Wj(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let I=i;I<c;I++){let j=r[I].match(zo);j&&(u<0&&(u=I),l.push({line:I,cursor:!!j[1],checked:j[3]!=null&&j[3]!==" ",hasCheckbox:j[3]!=null,label:j[4].trim(),desc:""}))}if(!l.length)return null;for(let I of l)for(let j=I.line+1;j<Math.min(r.length,I.line+4)&&!(zo.test(r[j])||Qr.test(r[j]));j++){let K=r[j].trim();if(!(!K||Tj(r[j])||Ju.test(K))){I.desc=Aj(K);break}}let d=qj(l,r),p=l.filter(I=>I!==d&&!Ju.test(I.label));if(!p.length)return null;let f=p.some(I=>I.hasCheckbox),w=d?.cursor?p.length:Math.max(0,p.findIndex(I=>I.cursor)),m=d&&!Ju.test(d.label)?eo(d.label):"",g=s>=0?s+1:0,y=[];for(let I=g;I<u;I++){let j=r[I].trim();j&&!Qr.test(r[I])&&!/[☐☒]/.test(r[I])&&y.push(j)}let b=y.join(" ")||(s>=0?gS(r[s]):"")||"The agent is asking a question",k=s>=0?Ej(r[s],b):null,S=k?.currentTabLabel||(s>=0?gS(r[s]):""),$=p.map(I=>({label:eo(I.label),description:I.desc})),M=$.map(I=>I.label).join("|"),B=`claude:${k?`q${k.questionIndex+1}/${k.questionTotal}`:"q1/1"}:${S}:${b}:${M}`.slice(0,240),A=k?`claude:ask:${k.tabs.map(I=>I.label).join("|")}`.slice(0,240):B;return{provider:"claude",box:t,multiSelect:f,cursorIndex:w,promptId:B,groupId:A,...d?{custom:!0,customIndex:p.length,customText:m}:{},dismissable:!0,...k?{questionIndex:k.questionIndex,questionTotal:k.questionTotal,currentTabLabel:k.currentTabLabel,tabs:k.tabs}:{},questions:[{question:b,header:S,multiSelect:f,options:$}]}}function to(e,t){let n=String(t||"");return n?e==="claude"?yS.test(n)&&fh.test(n)||bS.test(n)||dh.test(n)||kS.test(n.trimEnd().split(`
|
|
172
|
+
`).pop()||"")?!0:Jo.test(n)?!1:xS.test(n)&&vS.test(n)||SS.test(n)||wh.test(n)||ph.test(n)||AS.test(n)||TS.test(n.trimEnd().split(`
|
|
173
|
+
`).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 Dn(e,t){return!e||!t?null:e.agent==="claude"?Oj(t,e.name)||Gj(t,e.name)||_j(t,e.name)||Cj(t,e.name)||Fj(t,e.name)||jj(t,e.name)||Pj(t,e.name)||Nj(t,e.name)||Uj(t,e.name)||Hj(t,e.name)||Rj(t,e.name):e.agent==="cursor"||e.agent==="codex"?wx(e.agent,t,e.name):null}async function gh(e){if(!e)return null;let t=await un(e.name).catch(()=>"");return Dn(e,t)}function yh(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 ES(e,t,n=""){let r=typeof n=="string"?n.trim():"";if(!r||!e?.custom)return{pre:yh(e,t).map(d=>({key:d})),text:"",post:[]};let o=Array.isArray(t?.[0])?t[0].filter(d=>Number.isInteger(d)&&d>=0):[],s=Number.isInteger(e.customIndex)?e.customIndex:e.questions?.[0]?.options?.length??0,a=Number.isInteger(e.cursorIndex)?e.cursorIndex:0,i=(d,p)=>{let f=p-d;return Array.from({length:Math.abs(f)},()=>({key:f>0?"Down":"Up"}))},c=[],l=a,u=typeof e.customText=="string"?e.customText.length:0;if(u>0&&a===s&&c.push(...Array.from({length:u+2},()=>({key:"BSpace"}))),e.multiSelect){for(let d of[...o].sort((p,f)=>p-f))c.push(...i(l,d),{key:"Enter"}),l=d;return c.push(...i(l,s)),{pre:c,text:r,post:[{key:"Down"},{key:"Enter"}]}}return c.push(...i(l,s)),{pre:c,text:r,post:[{key:"Enter"}]}}function IS(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 yS,fh,zo,Ju,Qr,mh,bS,kS,$j,dh,Mj,Jo,xS,vS,SS,wh,Dj,ph,Lj,AS,TS,bh=q(()=>{ht();Au();yS=/to navigate\b.*\bEsc to cancel|Enter to select\b/,fh=/Chat about this/i,zo=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Ju=/^(type something|chat about this|submit)\b/i,Qr=/^[─—-]{6,}\s*$/,mh=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;bS=/^\s*Switch model\?\s*$/m;kS=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,$j=/^\s*[─—▔-]{6,}\s*$/;dh=/Resume from summary/i,Mj=/^\s*This session is\b/;Jo=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;xS=/Choose the text style that looks best with your terminal/,vS=/Let's get started\./;SS=/Select login method:/;wh=/Paste code here if prompted/,Dj=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;ph=/OAuth error:/,Lj=/Press Enter to retry\./;AS=/Quick safety check: Is this a project you created or one you trust/;TS=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function Yu(e,t,n){if(!e||!t)return null;for(let r of Kj)if(r.provider===e.agent)try{if(r.match(t,n))return r}catch{}return null}function zj(e){let t=kh.get(e);return t||(t={sentAt:[],last:0,emitted:new Set},kh.set(e,t)),t}function RS(e,t,n=Date.now()){let r=zj(e);if(r.sentAt=r.sentAt.filter(s=>n-s<$S),r.sentAt.length>=_S)return{action:"give-up"};if(n-r.last<Vj)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 OS(e,t=Date.now()){let n=kh.get(e);return n?n.sentAt.filter(r=>t-r<$S).length>=_S:!1}var Kj,Vj,_S,$S,kh,xh=q(()=>{Kj=[{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||""}
|
|
174
|
+
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];Vj=5e3,_S=5,$S=3600*1e3,kh=new Map});function da(e){return typeof e=="string"?e.trim():""}var MS=q(()=>{});import{execFile as Jj}from"node:child_process";import{promisify as Yj}from"node:util";async function Ye(e,t,{input:n}={}){let r=Sh("git",["-C",e,...t],{maxBuffer:CS});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function pa(e){if(!e)return!1;try{let{stdout:t}=await Ye(e,["rev-parse","--is-inside-work-tree"]);return t.trim()==="true"}catch{return!1}}async function NS(e){let{stdout:t}=await Ye(e,["rev-parse","--show-toplevel"]);return t.trim()}async function Xj(e){try{return await Ye(e,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function fa(e,t="bostrat: subtask snapshot"){let{stdout:n}=await Ye(e,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>Sh("git",["-C",e,...a],{maxBuffer:CS,env:o,...i});try{let a=await Xj(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 Sh("rm",["-f",r])}catch{}}}async function Ah(e,t,{trailers:n=[]}={}){let{stdout:r}=await Ye(e,["status","--porcelain"]);return r.trim()?(await Ye(e,["add","-A"]),await Ye(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",t,...n.flatMap(o=>["--trailer",o])]),!0):!1}async function Yo(e,{ignorePlumbing:t=!1}={}){let{stdout:n}=await Ye(e,["status","--porcelain"]),r=n.split(`
|
|
175
|
+
`).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 Xo(e,t){if(!t)return 0;try{let{stdout:n}=await Ye(e,["rev-list","--count",`${t}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function Zo(e,t,n,r){return await Ye(e,["worktree","add","--no-track","-b",n,t,r]),t}async function Th(e,t){try{return await Ye(e,["rev-parse","--verify","--quiet",`refs/heads/${t}`]),!0}catch{return!1}}async function rc(e,t,n){return await Ye(e,["worktree","add","--end-of-options",t,n]),t}async function Qo(e){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:t}=await Ye(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 Ye(e,["rev-parse","--verify","--quiet",t]),n}catch{}return"HEAD"}async function Eh(e){await Ye(e,["init","-q"]),await Ye(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function Tt(e,t){try{await Ye(e,["worktree","remove","--force",t])}catch{try{await Ye(e,["worktree","prune"])}catch{}}}function Zj(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 Ih(e,t){return!t||!t.length?e:[...e,"--",".",...t.map(n=>`:(exclude)${n}`)]}async function _h(e,t,n,r){let{stdout:o}=await Ye(e,Ih(["diff","--shortstat",t,n],r));return Zj(o)}async function $h(e,t,n,r){let{stdout:o}=await Ye(e,Ih(["diff","--name-only",t,n],r));return o.split(`
|
|
176
|
+
`).map(s=>s.trim()).filter(Boolean)}async function Rh(e,t,n,r){let{stdout:o}=await Ye(e,Ih(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await vh(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await vh(e,o,["--3way","--whitespace=nowarn"]),a=await Qj(e);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await vh(e,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function vh(e,t,n){try{return await Ye(e,["apply",...n,"-"],{input:t}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function Qj(e){try{let{stdout:t}=await Ye(e,["diff","--name-only","--diff-filter=U"]);return t.split(`
|
|
177
|
+
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var Sh,CS,Xu=q(()=>{Sh=Yj(Jj),CS=64*1024*1024});function Zu(e,{at:t=Date.now(),reason:n="node-transfer"}={}){let r=!1;for(let o of e?.boxes||[]){if(!o||typeof o!="object")continue;!eL.has(o.state)&&o.state!=="stopped"&&(o.state="stopped",r=!0);let s=Array.isArray(o.epochs)?o.epochs:[],a=s[s.length-1];a&&typeof a=="object"&&a.endedAt==null&&(a.endedAt=t,a.endReason==null&&(a.endReason=n),r=!0)}return r}var Le,eL,Zt=q(()=>{Le=e=>!!(e&&(e.archivedAt||e.transferredTo)),eL=new Set(["merged","conflict","canceled","failed","handed-off"])});import Oh from"node:fs/promises";import DS from"node:path";import tL from"node:os";function FS(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function US(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function HS(e){let t=new Date(FS(e));return t.setDate(t.getDate()+7),t.getTime()}function WS(e){let t=new Date(US(e));return t.setMonth(t.getMonth()+1),t.getTime()}function ed(e=Date.now()){for(let[t,n]of Object.entries(ma)){let r=n.startOf(e);if(r!==cr[t]){cr[t]=r;for(let o of es.values())o[t]=0;for(let o of ha)o.includes(`:${t}:`)&&!o.endsWith(`:${r}`)&&ha.delete(o)}}}function qS(e,t){let n=mn?.caps?.[e]?.[ma[t].capKey];return Number.isFinite(n)&&n>0?n:null}function no(e,t=Date.now()){if(!e||!Ch||!mn?.caps)return null;ed(t);let n=es.get(e);for(let r of Object.keys(ma)){let o=qS(e,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:e,window:r,limitUsd:o,spentUsd:Qu(s),resetsAt:ma[r].nextStart(t)}}return null}function GS(e,t){let n=`hit:${e}:${t.provider}:${t.window}:${cr[t.window]}`;return ha.has(n)?!1:(ha.add(n),!0)}function PS(e,t,n,r=Date.now()){if(!e||!t)return;ed(r);let o=nL(e);for(let[s,a]of Object.entries(ma))n>=cr[s]&&n<a.nextStart(r)&&(o[s]+=t)}async function rL(e,t,n=Date.now()){if(!mn?.caps||!Mh)return;let r=es.get(e);if(r)for(let[o,s]of Object.entries(ma)){let a=qS(e,o);if(a==null)continue;let i=r[o]||0;if(i<a*LS||i>=a)continue;let c=`warn:${e}:${o}:${cr[o]}`;if(!ha.has(c)){ha.add(c);try{let l=await Mh(t);if(!l)continue;await N(l,{type:"usage.cap.warning",actor:"system",provider:e,payload:{provider:e,window:o,limitUsd:a,spentUsd:Qu(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function KS(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 td(e,{source:t="local",updatedAt:n=Date.now()}={}){mn={caps:KS(e),updatedAt:n,source:t};try{await Oh.mkdir(BS,{recursive:!0}),await Oh.writeFile(jS(),JSON.stringify(mn,null,2))}catch{}return oc()}function oc(e=Date.now()){ed(e);let t=new Set([...es.keys(),...Object.keys(mn?.caps||{})]),n={};for(let r of t){let o=es.get(r);n[r]={weeklyUsd:Qu(o?.weekly||0),monthlyUsd:Qu(o?.monthly||0)}}return{caps:mn?.caps||{},configured:!!mn,seeded:Ch,updatedAt:mn?.updatedAt??null,spent:n,windows:{weekly:{start:cr.weekly,resetsAt:HS(e)},monthly:{start:cr.monthly,resetsAt:WS(e)}},warnPct:LS}}async function VS({listTracks:e,getTrack:t}={}){Mh=t||null,ed();try{let o=JSON.parse(await Oh.readFile(jS(),"utf8"));mn={caps:KS(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{mn=null}let n=Date.now();Xe.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=Xt(o);PS(o.provider||"unknown",s,o.ts??Date.now()),rL(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(cr.weekly,cr.monthly);for(let o of await e?.()||[]){let s;try{s=await G(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||PS(a.provider||"unknown",Xt(a).usd,i,n)}}return Ch=!0,oc()}var BS,jS,LS,ma,mn,Ch,cr,es,ha,Mh,nL,Qu,nd=q(()=>{ye();Yr();BS=process.env.BOSTRAT_STATE_DIR||DS.join(process.env.HOME||tL.tmpdir(),".bostrat"),jS=()=>DS.join(BS,"usage-caps.json"),LS=.8;ma={weekly:{startOf:FS,nextStart:HS,capKey:"weeklyUsd"},monthly:{startOf:US,nextStart:WS,capKey:"monthlyUsd"}},mn=null,Ch=!1,cr={weekly:0,monthly:0},es=new Map,ha=new Set,Mh=null,nL=e=>{let t=es.get(e);return t||(t={weekly:0,monthly:0},es.set(e,t)),t};Qu=e=>Math.round(e*1e4)/1e4});import oL from"node:fs/promises";import sL from"node:os";import zS from"node:path";function Nh(e){return e==="codex"?lL:e==="cursor"?eA:ZS}function JS(e){let t=String(e||"").split(`
|
|
178
|
+
`),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 YS(e){let t=String(e||"").split(`
|
|
179
|
+
`),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 uL(e){let t=String(e||"").split(`
|
|
180
|
+
`);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 XS(e){let t=String(e||"").split(`
|
|
181
|
+
`),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 tA(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&&!QS.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=eA.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!cL.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function sc(e,t,{timeoutMs:n=aL}={}){let r=Date.now()+n;for(;;){let o=await De(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await rd(iL)}}async function dL(e,t){return await at(e,`/model ${t.model}`,Cn("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function pL(e,t){await wu(e),await at(e,"/model",{enter:!1}),await rd(250),await Ke(e,["Enter"],{kind:"model-switch"});let n=await sc(e,JS);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await Ke(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=uL(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await at(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await sc(e,i=>YS(i)||!JS(i)&&o(i)&&{done:o(i)});if(!s)throw await Ke(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?QS.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 Ke(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await at(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await sc(e,i=>!YS(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 fL(){let e=zS.join(process.env.CURSOR_CONFIG_DIR||zS.join(sL.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await oL.readFile(e,"utf8"))?.model||null}catch{return null}}async function mL(e,t){await wu(e),await yu(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await sc(e,XS),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 sc(e,XS,{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 Ke(e,["Down"],{kind:"model-switch"}),await rd(250)}if(!n||!s||!o(s)){await Ke(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await Ke(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 Ke(e,["Enter"],{kind:"model-switch"}),await rd(800);let a=await fL();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 nA(e,t,n){return e==="codex"?pL(t,n):e==="cursor"?mL(t,n):dL(t,n)}var rd,aL,iL,ZS,cL,lL,QS,eA,rA=q(()=>{ht();qi();rd=e=>new Promise(t=>setTimeout(t,e)),aL=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,iL=300,ZS=[{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.1"},{alias:"opusplan",label:"Opus Plan"}],cL=new Set([...ZS.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","claude-fable-5-1"]),lL=[{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"}],QS=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),eA=[{alias:"auto",label:"Auto"},{alias:"composer-2.5",label:"Composer 2.5"},{alias:"claude-opus-5-high",label:"Opus 5"},{alias:"claude-fable-5-1-high",label:"Fable 5.1"},{alias:"claude-sonnet-5-high",label:"Sonnet 5"},{alias:"cursor-grok-4.6-high",label:"Cursor Grok 4.6"},{alias:"gpt-5.6-sol-high",label:"GPT-5.6 Sol High"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}]});function ad(e){let t=e||{},n=hL(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function hL(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{text:e.text,boxRole:e.boxRole,...e.kind?{kind:e.kind}:{},...e.attachments?.length?{attachments:e.attachments}:{},...e.deliveryId?{deliveryId:e.deliveryId}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:e.box,provider:it(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:it(e.agent),payload:{text:e.prompt,kind:e.kind,boxRole:e.boxRole,...e.deliveryId?{deliveryId:e.deliveryId}:{}}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:it(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,cliVersion:e.cliVersion??null}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:it(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:it(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:it(e.agent),payload:{...Bn(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:it(e.agent||e.toAgent),payload:{kind:"subtask-failed",...Bn(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{change:"subtask-resumed",...Bn(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",...Bn(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...Bn(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...Bn(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{change:"rehydrated",...e.fresh?{fresh:!0}:{}}};case"lead-refreshed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{change:"lead-refreshed",box:e.box,agent:e.agent,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,reason:e.reason,cliVersion:e.cliVersion??null}};case"transferred-in":return{type:e.mode==="recover"?"track.recovered":"track.transferred",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{...Bn(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Bn(e)}};case"retag-failed":return{type:"error",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{kind:"retag-failed",action:e.action??"reshape",reason:e.reason,rolledBack:e.rolledBack!==!1,...e.recovered?{recovered:!0}:{}}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};case"delivery-held":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{kind:"delivery-held",...Bn(e)}};case"startup-gate":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{kind:"startup-gate",...Bn(e)}};default:return null}}function aA(e,t){let n=ad(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;id(()=>N(r,n))}async function iA(e){if(!(!e?.id||oA.has(e.id))){oA.add(e.id);try{let t=await G(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=ad(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await N(e,o)}}catch{}}}function Bn(e){let{type:t,at:n,...r}=e;return r}function cA(e,t,n){!n?.promptId||sd.get(e.id)===n.promptId||(sd.set(e.id,n.promptId),id(()=>N(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:it(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 lA(e,t,n,r={}){sd.delete(e.id),id(()=>N(e,{type:"prompt.dismissed",actor:"user",source:"orchestrator",boxId:t?.name,provider:it(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}function Ph(e,t,n,r={}){sd.delete(e.id),id(()=>N(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:it(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var it,od,id,sA,oA,sd,Dh=q(()=>{ye();it=e=>e||null;od=Promise.resolve(),id=e=>(od=od.then(e).catch(()=>{}),od),sA=()=>od;oA=new Set;sd=new Map});function kL(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function hA(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of kL(n).reverse()){if(!wL.test(r))continue;let o=r.endsWith("?")&&r.length<=bL&&yL.test(r);if(gL.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function gA(e){let t=String(e||""),n=[];xL.test(t)&&n.push("commit"),wA.test(t)&&n.push("rebase"),vL.test(t)&&n.push("push");let r=AL.test(t);!r&&SL.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 yA(e){return wA.test(String(e||""))}function wa(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(f=>`${f.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 f=a.has("commit")?"the branch":`this branch${t?` (${t})`:""}`;u.push(i?`rebase ${f} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${f} 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 f=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",w=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${w}${f}`:`push this branch${t?` (${t})`:""} from each changed checkout (${c}) to its remote${f}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${w}${f}`:`push this branch${l} to the remote${f}`)}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 p=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return d.length&&(p+=` ${d.join(", and ").replace(/^./,f=>f.toUpperCase())}.`),a.has("pr")&&(p+=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."),p}function bA(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 kA(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<=TL&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function xA(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+EL||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function vA(e){let t=(Array.isArray(e)?e:[]).filter(r=>uA[r]).map(r=>uA[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 dA,pA,fA,mA,wL,gL,yL,bL,ro,xL,wA,vL,SL,AL,TL,EL,uA,ts=q(()=>{dA=/(?<!\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,pA=/(?<!\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,fA=/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,mA=/(?<!\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,wL=new RegExp(`\\b(?:${dA}|${pA}|${fA}|${mA})\\b`,"i"),gL=/\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,yL=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,bL=90;ro=["commit","rebase","push","pr"],xL=new RegExp(`\\b(?:${dA})\\b`,"i"),wA=new RegExp(`\\b(?:${mA})\\b`,"i"),vL=new RegExp(`\\b(?:${pA})\\b`,"i"),SL=new RegExp(`\\b(?:${fA})\\b`,"i"),AL=/\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;TL=3e4;EL=2500;uA={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import IL from"node:crypto";import cd from"node:fs/promises";import SA from"node:path";import _L from"node:os";function IA(e){EA=typeof e=="function"?e:async()=>new Set}function $L(e,t,n){if(e.originNode&&e.originNode===t)return!0;let r=e.data?.trackId;return!!r&&n.has(r)}async function He(){return lr||(Bh??=cd.readFile(Lh,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(lr=e,!t&&qe()){let{records:n,source:r}=await dt("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){let o=f0(),s=await EA().catch(()=>new Set),a=0;for(let i of n){if(i.deletedAt||!i.data?.repo||i.data?.number==null)continue;if(!$L(i,o,s)){a++;continue}let{id:c,updatedAt:l,...u}=i.data;lr[ie(u.repo,u.number)]=u}a&&console.log(`[pr-links] restore: ${Object.keys(lr).length} link(s) re-seeded, ${a} other-node record(s) left on the plane`),Object.keys(lr).length&&await Fh().catch(()=>{})}}return lr}).finally(()=>Bh=null),Bh)}async function Fh(){let e=JSON.stringify({links:lr},null,2),t=`${Lh}.${process.pid}.tmp`;await cd.mkdir(AA,{recursive:!0}),await cd.writeFile(t,e),await cd.rename(t,Lh)}async function Ze(e){jh=jh.then(Fh,Fh),await jh.catch(()=>{});let t=lr?.[e];t&&At("prlinks",{id:TA(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function Ut({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 He(),c=ie(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:RL.has(a),firstSeen:Date.now()},await Ze(ie(e,t)),{link:i[c],created:!0})}async function ur(e,t){if(!e||t==null)return null;let n=(await He())[ie(e,t)];return!n||n.announced?null:(n.announced=!0,await Ze(ie(e,t)),n)}async function _A(e,t){if(!e||t==null)return!1;let n=(await He())[ie(e,t)];return n?.notifyOnMerge?(delete n.notifyOnMerge,await Ze(ie(e,t)),!0):!1}async function pt(e,t){return!e||t==null?null:(await He())[ie(e,t)]??null}async function oo(e,t,n){if(!e||t==null)return null;let r=(await He())[ie(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,delete r.followupPending,await Ze(ie(e,t)),r):null}async function $A(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await He())[ie(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Ze(ie(e,t)),o):null}async function RA(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await He())[ie(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Ze(ie(e,t)),o):null}async function ld(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await He())[ie(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Ze(ie(e,t)),r)}async function ud(e,t){if(!e||t==null)return null;let n=(await He())[ie(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Ze(ie(e,t)),n)}async function dd(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await He())[ie(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Ze(ie(e,t)),r)}async function pd(e,t){if(!e||t==null)return null;let n=(await He())[ie(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Ze(ie(e,t)),n)}async function OA(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await He())[ie(e,t)];return r?(r.baseFoldAt=n,await Ze(ie(e,t)),r):null}async function MA(e,t,{misses:n,until:r}){if(!e||t==null)return null;let o=(await He())[ie(e,t)];return o?(o.brokerRefused={misses:Number(n)||0,until:Number(r)||0},await Ze(ie(e,t)),o):null}async function CA(e,t){if(!e||t==null)return null;let n=(await He())[ie(e,t)];return n?.brokerRefused?(delete n.brokerRefused,await Ze(ie(e,t)),n):null}async function NA(e,t){if(!e||t==null)return null;let n=(await He())[ie(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Ze(ie(e,t)),n)}async function PA(e,t,n){if(!e||t==null||!n)return null;let r=(await He())[ie(e,t)];return r?(r.lastHeadSha=n,await Ze(ie(e,t)),r):null}async function DA(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await He())[ie(e,t)];if(!r)return{prev:null,link:null};let o=r.verdict??null;return o&&o.blocked===(n.blocked??null)&&(o.sha??null)===(n.sha??null)||(r.verdict={blocked:n.blocked??null,sha:n.sha??null,at:n.at??Date.now()},await Ze(ie(e,t))),{prev:o,link:r}}async function ac(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await He())[ie(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Ze(ie(e,t)),r):null}async function ns(e,t,n=!0){if(!e||t==null)return null;let r=(await He())[ie(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Ze(ie(e,t)),r):null}async function rs(e,t,n=!0){if(!e||t==null)return null;let r=(await He())[ie(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Ze(ie(e,t)),r):null}async function BA(e,t,n){if(!e||t==null)return null;let r=(await He())[ie(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 Ze(ie(e,t)),r}async function jA(e,t,n){if(!e||t==null)return null;let r=(await He())[ie(e,t)];if(!r)return null;let o=new Map((r.followupPending||[]).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.followupPending=[...o.values()],await Ze(ie(e,t)),r}async function ic(e,t){if(!e||t==null)return null;let n=(await He())[ie(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 Ze(ie(e,t)),n)}async function LA(){return Object.values(await He()).filter(e=>!OL.has(e.state))}async function hn(){return Object.values(await He()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function FA(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await dt("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await He(),r=0;for(let[o,s]of Object.entries(n))await At("prlinks",{id:TA(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var AA,Lh,ie,TA,lr,Bh,jh,EA,RL,OL,jn=q(()=>{Fr();AA=process.env.BOSTRAT_STATE_DIR||SA.join(process.env.HOME||_L.tmpdir(),".bostrat"),Lh=SA.join(AA,"pr-links.json"),ie=(e,t)=>`${e}#${Number(t)}`,TA=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=IL.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},lr=null,Bh=null,jh=Promise.resolve(),EA=async()=>new Set;RL=new Set(["api","observed","reconciled"]);OL=new Set(["merged","closed","lost"])});import ML from"node:fs";import Uh from"node:fs/promises";import qA from"node:path";import CL from"node:os";function VA(e,t=!1){if(!t&&e-HA<NL)return;HA=e;let n=JSON.stringify({windowStart:ga,byCaller:Object.fromEntries(dr),lastHour:so});WA=WA.then(()=>Uh.mkdir(GA,{recursive:!0})).then(()=>Uh.writeFile(`${fd}.tmp`,n)).then(()=>Uh.rename(`${fd}.tmp`,fd)).catch(()=>{})}function zA(e){e-ga<KA||(so={start:ga,total:md(dr),byCaller:Object.fromEntries(dr)},so.total&&console.log(`[github] ${so.total} calls last hour`,so.byCaller),ga=e,dr=new Map,VA(e,!0))}function pr(e="other",t=Date.now()){zA(t),dr.set(e,(dr.get(e)||0)+1),VA(t)}function JA(e=Date.now()){return zA(e),{windowStart:ga,total:md(dr),byCaller:Object.fromEntries(dr),lastHour:so,backoffUntil:os>e?os:null}}function ao(e){return PL.test(String(e?.message||e||""))}function io(e,t=Date.now()){return os<=t&&console.warn(`[github] rate limited \u2014 backing off ${UA/6e4} min:`,String(e?.message||e||"").slice(0,200)),os=Math.max(os,t+UA),os}function hd(e=Date.now()){return Math.max(0,os-e)}var GA,fd,KA,UA,NL,ga,dr,so,os,HA,md,WA,PL,cc=q(()=>{GA=process.env.BOSTRAT_STATE_DIR||qA.join(process.env.HOME||CL.tmpdir(),".bostrat"),fd=qA.join(GA,"github-metrics.json"),KA=60*6e4,UA=5*6e4,NL=6e4,ga=Date.now(),dr=new Map,so=null,os=0,HA=0,md=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(ML.readFileSync(fd,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<KA?(ga=e.windowStart,dr=new Map(Object.entries(e.byCaller||{})),so=e.lastHour||null):md(new Map(Object.entries(e.byCaller||{})))&&(so={start:e.windowStart,total:md(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}WA=Promise.resolve();PL=/rate limit|too quickly|abuse detection|HTTP 429/i});import YA from"node:fs/promises";import XA from"node:path";import DL from"node:os";function gd({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};wd.push(l),wd.length>jL&&wd.shift();let u=Hh.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)),Hh.set(e,u),LL(l),l}async function LL(e){try{await YA.mkdir(ZA,{recursive:!0}),await YA.appendFile(BL,JSON.stringify(e)+`
|
|
182
|
+
`)}catch{}}function QA(){let e={};for(let[t,n]of Hh)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:wd.slice(-20)}}var ZA,BL,jL,wd,Hh,yd=q(()=>{ZA=process.env.BOSTRAT_STATE_DIR||XA.join(process.env.HOME||DL.tmpdir(),".bostrat"),BL=XA.join(ZA,"github-latency.ndjson"),jL=100,wd=[],Hh=new Map});import{execFile as FL}from"node:child_process";import{promisify as UL}from"node:util";async function HL(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([ba("git",["-C",e,"rev-parse","HEAD"]),ba("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await N({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function Qt(e,t,n={}){let{caller:r="prs",...o}=n,s=await _n({cwd:e});pr(r);try{let{stdout:a}=await ba("gh",t,{cwd:e,...WL,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw ao(a)&&io(a),a}}async function Gh(){if(bd&&Date.now()-bd.at<6e4)return bd.data;let e;try{pr("auth");let t=await ba("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 yi();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return bd={at:Date.now(),data:e},e}async function qL(e){try{let{stdout:t}=await ba("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function tT(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 Wh(e){let t=(e||[]).map(tT),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 Vh(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function rT(e,t){if(!t)return null;let n=await Oe(e);if(!n)return null;try{let r=await Qt(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 oT(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 sT(e,t){let n=await rT(e,t);return n===null?{...Wh([]),unavailable:!0}:Wh(oT(n))}function kd(e,t=Wh(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",state:e.state??null,mergedAt:e.mergedAt??null,closedAt:e.closedAt??null,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 KL(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){ya.delete(s);continue}r.add(s),ya.has(s)||ya.set(s,n),o.mergeabilityPending=n-ya.get(s)<GL}for(let o of ya.keys())o.startsWith(`${e}#`)&&!r.has(o)&&ya.delete(o);return t}async function ka(e,t){let n=await fr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await Qt(n.path,["pr","view",String(r),"--json",Kh]);return kd(JSON.parse(o))}catch(o){if(!Vh(o))throw o;let s=await Qt(n.path,["pr","view",String(r),"--json",nT]),a=JSON.parse(s);return kd(a,await sT(n.path,a.headRefOid))}}async function Ln(e,{caller:t="prs"}={}){try{let n=await Qt(e,["pr","list","--json",Kh,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>kd(r)).sort(eT)}catch(n){if(!Vh(n))throw n;let r=await Qt(e,["pr","list","--json",nT,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>kd(o,await sT(e,o.headRefOid))))).sort(eT)}}function en(){lc.clear()}async function JL(e,t){if(!e)return t;for(let n of t){let r=await pt(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 ld(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await dd(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function aT(){let e=await Gh();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await nr())await qL(a.path)&&t.push(a);let n=Date.now(),r=hd(n)>0,o=await Promise.all(t.map(async a=>{let i=lc.get(a.path),c=i?.pending?zL:VL;if(i&&(r||n-i.at<c))return i.entry;try{let l=await Oe(a.path).catch(()=>null),u=KL(a.path,await Ln(a.path,{caller:"tab"}),n);if(await JL(l,u),i?.entry?.prs)for(let f of u)i.entry.prs.find(m=>m.number===f.number)?.checks?.pending>0&&f.checks.total>0&&f.checks.pending===0&&gd({change:f.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:f.number,githubAt:f.checks.settledAt,source:"tab"});let d={name:a.name,path:a.path,prs:u},p=u.some(f=>f.mergeabilityPending||f.checks.pending>0||f.autoMergeArmed);return lc.set(a.path,{at:Date.now(),entry:d,pending:p}),d}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:Fn(l)}}})),s=new Set(t.map(a=>a.path));for(let a of lc.keys())s.has(a)||lc.delete(a);return{auth:e,repos:o}}async function fr(e){let t=(await nr()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function ss(e){return Bs(e)}async function xa(e,t,{method:n="squash"}={}){let r=await fr(e);if(!uc.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 _n({cwd:r.path})).GH_TOKEN){let a=await Df().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Qt(r.path,s)}catch(a){throw new Error(Fn(a))}finally{en()}return{ok:!0,number:o}}async function dc(e,t){let n=await fr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Qt(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Fn(s))}finally{en()}let o=await Oe(n.path).catch(()=>null);return o&&await OA(o,r).catch(()=>{}),{ok:!0,number:r}}async function zh(e,t){let n=await fr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Qt(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Fn(o))}finally{en()}return{ok:!0,number:r}}function Jh(e){return e.green&&!e.isDraft}function QL(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 iT(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:Jh(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function cT(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function pc(e,t){let n=a=>a?.firstSeen!=null&&Number.isFinite(Number(a.firstSeen))?Number(a.firstSeen):Number.MAX_SAFE_INTEGER,r=n(e),o=n(t);if(r!==o)return r-o;let s=String(e?.repo||"").localeCompare(String(t?.repo||""));return s||Number(e?.number||0)-Number(t?.number||0)}function lT(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(pc))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function uT(){let e={};for(let[t,n]of qh)e[t]={...n};return e}async function dT(e,{method:t="merge",onMerged:n=null}={}){let r=await fr(e);if(!uc.has(t))throw new Error(`invalid merge method: ${t}`);if(qh.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Ln(r.path)).filter(Jh).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 qh.set(r.path,s),tF(s,o,{listPrs:()=>Ln(r.path),merge:a=>xa(r.path,a,{method:t}),sleep:ZL,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function eF(e,t){let n=Date.now()+(t.settleMaxMs??XL),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(YL)}}async function tF(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 eF(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: ${Fn(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(!Jh(a)){e.skipped.push({number:a.number,title:a.title,reason:QL(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:Fn(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),en()}}async function Yh(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await ba("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Fn(l)}`)}await HL(e);let o=n||"";if((await _n({cwd:e})).GH_TOKEN){let l=await Df().catch(()=>null);l&&(o=`${o?`${o}
|
|
183
|
+
|
|
184
|
+
`:""}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 Qt(e,s)}catch(l){throw new Error(Fn(l))}finally{en()}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 pT(e,t){let n=await fr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await Qt(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(Fn(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!Vh(i))throw i;let c=await Qt(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await rT(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(Fn(i));o=oT(l).map(tT).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=nF(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await Qt(n.path,["run","view",c,"--log-failed"]).then(u=>rF(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function nF(e){return String(e||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function rF(e,{maxLines:t=400,maxBytes:n=64*1024}={}){let r=String(e||""),o=r.split(`
|
|
185
185
|
`);return o.length>t&&(r=`\u2026 (${o.length-t} earlier lines truncated)
|
|
186
186
|
`+o.slice(-t).join(`
|
|
187
187
|
`)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
|
|
188
188
|
`+r.slice(-n)),r}function Fn(e){return String(e?.stderr||e?.message||e||"").trim().split(`
|
|
189
|
-
`).find(n=>n.trim())||"gh failed"}var ba,
|
|
190
|
-
`)){let r=n.trim(),o=/^([A-Za-z-]+):\s*(.+)$/.exec(r);if(!o)continue;let s=o[1].toLowerCase(),a=o[2].trim();if(s===
|
|
191
|
-
`)){if(o.startsWith("diff --git ")){n={oldPath:null,newPath:null,deleted:[],added:0,binary:!1,hunks:[]},r=null,t.push(n);continue}if(n)if(o.startsWith("--- ")&&!r){let s=o.slice(4).trim();n.oldPath=s==="/dev/null"?null:s.replace(/^a\//,"")}else if(o.startsWith("+++ ")&&!r){let s=o.slice(4).trim();n.newPath=s==="/dev/null"?null:s.replace(/^b\//,"")}else if(o.startsWith("Binary files"))n.binary=!0;else if(o.startsWith("@@")){let s=/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/.exec(o);if(!s)continue;let a=Number(s[1]),i=s[2]==null?1:Number(s[2]),c=Number(s[3]),l=s[4]==null?1:Number(s[4]);i>0&&n.deleted.push([a,i]),n.added+=l,r={oldStart:a,oldCount:i,newStart:c,newCount:l,del:[],add:[]},n.hunks.push(r)}else r&&o.startsWith("-")?r.del.push(o.slice(1)):r&&o.startsWith("+")&&r.add.push(o.slice(1))}return t}function
|
|
192
|
-
`)){let o=/^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/.exec(r);if(o){n={sha:o[1],line:Number(o[3]),time:null},t.set(n.line,n);continue}n&&r.startsWith("committer-time ")&&(n.time=Number(r.slice(15))*1e3)}return t}async function
|
|
189
|
+
`).find(n=>n.trim())||"gh failed"}var ba,WL,bd,Kh,nT,GL,ya,eT,VL,zL,lc,uc,YL,XL,ZL,qh,as=q(()=>{Pr();yt();jn();cc();yd();ye();ba=UL(FL),WL={timeout:3e4,maxBuffer:8*1024*1024};bd=null;Kh="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt,state,mergedAt,closedAt",nT=Kh.replace("statusCheckRollup,","");GL=9e4,ya=new Map;eT=(e,t)=>e.number-t.number;VL=3e4,zL=1500,lc=new Map;uc=new Set(["squash","merge","rebase"]);YL=2500,XL=9e4,ZL=e=>new Promise(t=>setTimeout(t,e));qh=new Map});import{execFile as oF}from"node:child_process";import{promisify as sF}from"node:util";async function iF(e,t){try{let{stdout:n}=await aF("git",["-C",e,"rev-list","--count",t]);return Number(n.trim())||0}catch{return null}}async function cF(e,t,n,{rebase:r=!1,listPrs:o=Ln}={}){let s=await Yo(e.dir,{ignorePlumbing:!0}).catch(()=>!1),a=await Xo(e.dir,n).catch(()=>0),i=t?await iF(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(m=>m.headRefName===t)||null,u=l?"open":"none"}catch{}let d=s||a>0,p=[];s&&p.push("commit"),r&&p.push("rebase"),(s||c)&&p.push("push"),d&&(u!=="open"||s||c)&&p.push("pr");let f=p.filter(w=>!(w==="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:xd.filter(w=>p.includes(w)),defaults:xd.filter(w=>f.includes(w))}}async function vd(e,{branch:t,rebase:n=!1,listPrs:r=Ln}={}){let o=e?.workspace,s=o?.kind==="multi"?(o.repos||[]).filter(p=>p?.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 pa(s[0].dir).catch(()=>!1))return null;let a=[];for(let p of s)a.push(await cF(p,t,p.baseRef||o.baseRef,{rebase:n,listPrs:r}));let i=a.filter(p=>p.dirty||p.aheadBase>0||p.openPr),c=i.length?i:a,l=xd.filter(p=>c.some(f=>f.available.includes(p))),u=xd.filter(p=>c.some(f=>f.defaults.includes(p))),d=c.filter(p=>p.openPr);return{available:l,defaults:u,prState:d.length?"open":c.every(p=>p.prState==="none")?"none":"unknown",openPr:c.length===1?c[0].openPr:null,repos:c.map(p=>({name:p.name,slug:p.slug,dirty:p.dirty,unpushed:p.unpushed,prState:p.prState,openPr:p.openPr}))}}var aF,xd,Xh=q(()=>{Xu();as();aF=sF(oF),xd=["commit","rebase","push","pr"]});function Sd(e,t,n){let{state:r,reason:o}=Tm(t,n);if(r!=="unknown"){if(e){let a=Date.now();for(let[i,c]of fc)a-c.at>=fT()&&fc.delete(i);fc.set(e,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!e)return!1;let s=fc.get(e);return s?Date.now()-s.at>=fT()?(fc.delete(e),!1):s.busy:!1}async function Zh(e,t){let n={working:!1,prompted:!1};if(!e)return n;let r=await De(e,{lines:Qe}).catch(()=>"");if(!Sd(e,t,r))return vx(t,r)?{working:!0,prompted:!1}:n;if(to(t,r)){let o=await un(e).catch(()=>r),s=Dn({name:e,agent:t},o);if(s)return!Yu({name:e,agent:t},s,o)||OS(e)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function is(e,t){return(await Zh(e,t)).working}var fT,fc,mc=q(()=>{ht();ra();bh();xh();fT=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,fc=new Map});import{execFile as lF}from"node:child_process";import{promisify as uF}from"node:util";async function co(e){if(!e)return null;let t=mT.get(e);if(t&&Date.now()-t.at<pF)return t.branch;let n=null;try{let{stdout:r}=await dF("git",["-C",e,"symbolic-ref","--short","-q","HEAD"],{timeout:5e3});n=r.trim()||null}catch{n=null}return mT.set(e,{at:Date.now(),branch:n}),n}function hT(e){return String(e||"").match(fF)?.[1]??null}function wT(e,t){let n=hT(e);return!!n&&!!t&&n===t}function gT(e,t){let n=hT(e);return!!n&&n!==t}function mF(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 va(e){let t=[],n=r=>{r&&!t.includes(r)&&t.push(r)};n(e?.workspace?.branch??null);for(let r of mF(e))n(await co(r));return t}async function Ad(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=>wT(t,a.id));if(s)return s;for(let a of r)if(!Le(a)&&(await va(a)).includes(t))return a;return null}function hF(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}function Td(e,{branch:t=null,repo:n=null,slug:r=null}={}){return wT(t,e?.id)?!0:hF(e,n,r)}var dF,pF,mT,fF,cs=q(()=>{Zt();dF=uF(lF),pF=5e3,mT=new Map;fF=/^track\/.+-([0-9a-f]{8})$/});import{execFile as wF}from"node:child_process";import{promisify as gF}from"node:util";function Sa({trackId:e,boxId:t,epoch:n}){return`${e}/${t}/${Number.isFinite(Number(n))?Number(n):0}`}function Id(e){let t=/^([^/\s]+)\/([^/\s]+)\/(\d+)$/.exec(String(e||"").trim());return t?{trackId:t[1],boxId:t[2],epoch:Number(t[3])}:null}function $d({trackId:e,boxId:t,epoch:n=0,provider:r=null,model:o=null}){let s=[],a=r?SF[r]:null;return a&&s.push(a),s.push(`${vT}: ${Sa({trackId:e,boxId:t,epoch:n})}`),o&&s.push(`${ST}: ${String(o).trim()}`),s}function yT(e){let t={epoch:null,model:null,provider:null,cause:null};for(let n of String(e||"").split(`
|
|
190
|
+
`)){let r=n.trim(),o=/^([A-Za-z-]+):\s*(.+)$/.exec(r);if(!o)continue;let s=o[1].toLowerCase(),a=o[2].trim();if(s===vT.toLowerCase()&&!t.epoch)t.epoch=Id(a);else if(s===ST.toLowerCase()&&!t.model)t.model=a;else if(s===AT.toLowerCase()&&!t.cause){let i=a.toLowerCase();hc.includes(i)&&(t.cause=i)}else if(s==="co-authored-by"||s==="made-with"){let i=a.toLowerCase();t.provider||(i.startsWith("claude")?t.provider="claude":i.startsWith("codex")?t.provider="codex":i.startsWith("cursor")&&(t.provider="cursor"))}}return t}function AF(e,t=lo){return e<=t?"rework":"drift"}function TF(e,{window:t=lo,end:n=rw}={}){return!Number.isFinite(e)||e<=t?1:e>=n?0:1-(e-t)/(n-t)}function Rd({subject:e="",trailerCause:t=null,wallReason:n=null,fixerCategory:r=null,ciFix:o=!1}={}){let s=String(e||"");return EF.test(s)?{cause:"defect",level:"A",basis:"revert"}:o?{cause:"defect",level:"A",basis:"ci-fix"}:t?{cause:t,level:"E",basis:`${AT} trailer`}:_F.test(s)&&!bT.test(s)?{cause:"style",level:"C",basis:"subject"}:bT.test(s)?{cause:"defect",level:"C",basis:"subject"}:IF.test(s)?{cause:"design",level:"C",basis:"subject: refactor"}:n==="wrong-approach"?{cause:"design",level:"C",basis:"wall label: wrong-approach"}:r==="bug-fix"?{cause:"defect",level:"C",basis:"fixer prompt: bug-fix"}:null}function sw({heuristic:e=null,subject:t="",ref:n=null,promptKind:r=null,fixerCategory:o=null,ticketKind:s=null}={}){let a=String(t||""),i=$F.exec(a),c=i?i[1].toLowerCase():null;return e&&(e.level==="A"||e.level==="E")?{intent:"fix",basis:e.basis}:s&&/^bug/i.test(s)?{intent:"fix",basis:`linked ticket: ${s}`}:c&&RF.has(c)?{intent:"fix",basis:`subject prefix: ${c}:`}:c&&OF.has(c)?{intent:"planned",basis:`subject prefix: ${c}:`}:s&&/^(story|feature|task)/i.test(s)?{intent:"planned",basis:`linked ticket: ${s}`}:e?{intent:"likely-fix",basis:e.basis}:r==="followup-go"?{intent:"planned",basis:"operator-tapped follow-up"}:kT.test(String(n||""))||kT.test(a)?{intent:"planned",basis:"blueprint item"}:o&&MF.has(o)?{intent:"planned",basis:`prompt: ${o}`}:{intent:"unknown",basis:null}}function DF(e){let t=String(e||"");return CF.some(n=>n.test(t))?"excluded":NF.some(n=>n.test(t))?"docs":PF.some(n=>n.test(t))?"config":"code"}function BF(e){let t=[],n=null,r=null;for(let o of String(e||"").split(`
|
|
191
|
+
`)){if(o.startsWith("diff --git ")){n={oldPath:null,newPath:null,deleted:[],added:0,binary:!1,hunks:[]},r=null,t.push(n);continue}if(n)if(o.startsWith("--- ")&&!r){let s=o.slice(4).trim();n.oldPath=s==="/dev/null"?null:s.replace(/^a\//,"")}else if(o.startsWith("+++ ")&&!r){let s=o.slice(4).trim();n.newPath=s==="/dev/null"?null:s.replace(/^b\//,"")}else if(o.startsWith("Binary files"))n.binary=!0;else if(o.startsWith("@@")){let s=/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/.exec(o);if(!s)continue;let a=Number(s[1]),i=s[2]==null?1:Number(s[2]),c=Number(s[3]),l=s[4]==null?1:Number(s[4]);i>0&&n.deleted.push([a,i]),n.added+=l,r={oldStart:a,oldCount:i,newStart:c,newCount:l,del:[],add:[]},n.hunks.push(r)}else r&&o.startsWith("-")?r.del.push(o.slice(1)):r&&o.startsWith("+")&&r.add.push(o.slice(1))}return t}function jF(e,t){let n=nw(e),r=nw(t);if(n===r)return 1;if(!n.length||!r.length)return 0;n.length>Ed&&(n=n.slice(0,Ed)),r.length>Ed&&(r=r.slice(0,Ed));let o=new Array(r.length+1),s=new Array(r.length+1);for(let a=0;a<=r.length;a++)o[a]=a;for(let a=1;a<=n.length;a++){s[0]=a;let i=n.charCodeAt(a-1);for(let c=1;c<=r.length;c++){let l=i===r.charCodeAt(c-1)?0:1;s[c]=Math.min(o[c]+1,s[c-1]+1,o[c-1]+l)}[o,s]=[s,o]}return 1-o[r.length]/Math.max(n.length,r.length)}function LF(e,t=TT){let n=new Set,r=e?.del||[],o=e?.add||[];for(let s=0;s<r.length;s++){let a=e.oldStart+s;if(!nw(r[s]).length){n.add(a);continue}s<o.length&&jF(r[s],o[s])>=t&&n.add(a)}return n}function FF(e){let t=[...e].sort((r,o)=>r-o),n=[];for(let r of t){let o=n[n.length-1];o&&o[0]+o[1]===r?o[1]+=1:n.push([r,1])}return n}function UF(e){let t=new Map,n=null;for(let r of String(e||"").split(`
|
|
192
|
+
`)){let o=/^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/.exec(r);if(o){n={sha:o[1],line:Number(o[3]),time:null},t.set(n.line,n);continue}n&&r.startsWith("committer-time ")&&(n.time=Number(r.slice(15))*1e3)}return t}async function xT(e,t){let{stdout:n}=await tw(e,["show","-s","--format=%H%n%P%n%ct%n%s%n%B",t]),[r,o,s,a,...i]=n.split(`
|
|
193
193
|
`);return{sha:r.trim(),parents:o.trim()?o.trim().split(" "):[],ts:Number(s)*1e3,subject:a??"",message:i.join(`
|
|
194
|
-
`)}}async function AT(e,t,{fixKey:n=null,window:r=lo,resolveOrigin:o=null,context:s={}}={}){let a=await bT(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=wT(a.message),l=n?Ed(n):c.epoch,u=l?Sa(l):null,{stdout:d}=await tw(e,["diff","-U0","--no-color","-M","-C",i,t]),p=NF(d),f=0,w=0,m=0,g=0,y=0,b=0,k=0,S=new Map;for(let I of p){if(I.binary)continue;let j=I.oldPath||I.newPath||"",K=I.deleted.reduce((W,[,Y])=>W+Y,0),R=CF(j);if(R==="excluded"){m+=K;continue}if(R==="docs"){g+=K,y+=I.added;continue}if(R==="config"){b+=K;continue}f+=I.added;let E=new Set;for(let W of I.hunks||[])for(let Y of DF(W))E.add(Y);k+=E.size;let C=[];for(let[W,Y]of I.deleted)for(let me=W;me<W+Y;me++)E.has(me)||C.push(me);if(w+=C.length,!I.oldPath||!C.length)continue;let P;try{let W=["blame","-w","-M","-C","--line-porcelain"];for(let[Y,me]of BF(C))W.push("-L",`${Y},${Y+me-1}`);W.push(i,"--",I.oldPath),P=jF((await tw(e,W)).stdout)}catch{continue}for(let{sha:W,time:Y}of P.values()){let me=S.get(W)??{lines:0,time:Y};me.lines+=1,S.set(W,me)}}let $=[],M=0,L=new Map;for(let[I,{lines:j,time:K}]of S){let R=L.get(I);if(!R){try{R=await bT(e,I)}catch{R=null}L.set(I,R)}let E=R?wT(R.message):{epoch:null},C=E.epoch,P=E.provider,W=E.model;if(!C&&o){let ne=await o(I,R);ne?.trackId&&ne?.boxId!=null&&(C={trackId:ne.trackId,boxId:ne.boxId,epoch:ne.epoch??0},P=P??ne.provider??null,W=W??ne.model??null)}if(!C){M+=j;continue}let Y=Number.isFinite(K)?K:R?.ts??a.ts,me=Math.max(0,(a.ts-Y)/gF),_e=Sa(C),Pe=_e===u?"self":xF(me,r);$.push({sha:I,key:_e,trackId:C.trackId,boxId:C.boxId,epoch:C.epoch,provider:P??null,model:W??null,lines:j,ageDays:Math.round(me*100)/100,class:Pe,decay:Pe==="rework"?1:Pe==="drift"?Math.round(vF(me,{window:r})*1e3)/1e3:0})}$.sort((I,j)=>j.lines-I.lines);let B=$d({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0}),A=sw({heuristic:B,subject:a.subject,ref:s.ref??null,promptKind:s.promptKind??null,fixerCategory:s.fixerCategory??null,ticketKind:s.ticketKind??null});return{sha:a.sha,parent:i,subject:a.subject.slice(0,200),ts:a.ts,fixKey:u,fixProvider:c.provider??s.provider??null,fixModel:c.model??s.model??null,window:r,added:f,deleted:w,excludedDeleted:m,cosmeticDeleted:k,docsDeleted:g,docsAdded:y,configDeleted:b,codeDelta:f+w+k>0,origins:$,unattributedLines:M,ghost:w===0&&f>0,heuristic:B,intent:A}}function TT(e,{minLines:t=ow}={}){if(!e)return!1;let n=e.intent?.intent??"unknown";return Aa.has(n)?hc(e)>=t:!1}function hc(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function ew(e){let t=e.genUsd;return{...e,genUsd:wn(t),reworkChargedUsd:wn(e.reworkChargedUsd),reworkPendingUsd:wn(e.reworkPendingUsd),trueUsd:wn(t+e.reworkChargedUsd),multiplier:t>0?wn((t+e.reworkChargedUsd)/t):null}}function ET(e,{window:t=lo,boxMeta:n={}}={}){let r=e||[],o=r.filter(A=>A.type==="cost.recorded"),s=r.filter(A=>A.type==="code.committed"),a=r.filter(A=>A.type==="code.rework"),i=A=>Array.isArray(A?.shas)&&A.shas.length?A.shas:A?.sha?[A.sha]:[],c=new Map,l=new Set;for(let A of r)if(A.type==="rework.cause.offered")for(let I of i(A.payload))l.add(I);else if(A.type==="rework.cause.answered"&&A.payload?.cause)for(let I of i(A.payload))c.set(I,A.payload.cause);else if(A.type==="rework.cause.retracted")for(let I of i(A.payload))c.delete(I);let u={},d=(A,I,j)=>Sa({trackId:A,boxId:I,epoch:j??0}),p=(A,I={})=>{if(!u[A]){let K=Ed(A)||{};u[A]={key:A,...K,provider:null,model:null,...Qh()}}let j=u[A];return I.provider&&!j.provider&&(j.provider=I.provider),I.model&&!j.model&&(j.model=I.model),j};for(let A of o){if(!A.boxId)continue;let I=d(A.trackId,A.boxId,A.epoch),{usd:j,model:K}=Xt(A),R=p(I,{provider:A.provider,model:K!=="unknown"?K:null});R.genUsd+=j}let f=new Map;for(let A of s){let I=`${A.trackId}/${A.boxId}`;(f.get(I)??f.set(I,[]).get(I)).push({...A,ts:A.payload?.ts??A.ts})}for(let A of f.values())A.sort((I,j)=>I.ts-j.ts);let w=(A,I,j,K)=>{let R=f.get(`${A}/${I}`)||[],E=-1/0;for(let W of R)W.ts<K&&(E=W.ts);let C=0,P=0;for(let W of o)W.trackId!==A||W.boxId!==I||(W.epoch??0)===(j??0)&&(W.ts<=E||W.ts>K||(C+=Xt(W).usd,P++));return{usd:C,turns:P,from:Number.isFinite(E)?E:null}},m={charged:0,pending:0,self:0,decayed:0,unattributed:0,ghost:0,planned:0},g={A:0,B:0,C:0,E:0,none:0},y={fix:0,"likely-fix":0,planned:0,unknown:0},b=0,k=[],S=[],$={},M={},L=new Set;for(let A of a){let I=A.payload||{};if(!I.sha||L.has(I.sha))continue;if(L.add(I.sha),I.codeDelta===!1){b++;continue}let j=Ed(I.fixKey)||{trackId:A.trackId,boxId:A.boxId,epoch:A.epoch??0},{usd:K,turns:R}=w(j.trackId,j.boxId,j.epoch,I.ts??A.ts),E=bF.includes(I.intent?.intent)?I.intent.intent:"unknown";y[E]+=1;let C=null;c.has(I.sha)?C={cause:c.get(I.sha),level:"B",basis:"operator"}:(I.heuristic?.level&&I.heuristic.level!=="C"||I.heuristic)&&(C=I.heuristic);let P=C?.level??"none";g[P]=(g[P]||0)+1;let W=yF.has(P),Y=Number(I.deleted)||0,me=Array.isArray(I.origins)?I.origins:[],_e=me.filter(ne=>ne.class==="rework").reduce((ne,Ve)=>ne+Ve.lines,0),Pe={sha:I.sha,subject:I.subject??"",ts:I.ts??A.ts,trackId:A.trackId,fixKey:I.fixKey??null,fixProvider:I.fixProvider??A.provider??null,fixUsd:wn(K),fixTurns:R,linesReworked:_e,unattributedLines:Number(I.unattributedLines)||0,ghost:I.ghost===!0,intent:{intent:E,basis:I.intent?.basis??null},verdict:C?{cause:C.cause,level:P,basis:C.basis??null}:null,charged:wn(0),origins:[]};if(E==="planned"||C?.cause===Id){m.planned+=K;for(let ne of me)ne.class==="rework"&&Pe.origins.push({key:ne.key,provider:ne.provider??null,model:ne.model??null,lines:ne.lines,ageDays:ne.ageDays,class:ne.class,usd:0})}else if(I.ghost||Y===0)m.ghost+=K;else{let ne=0;for(let ve of me){let ce=ve.lines/Y,re=K*ce;if(ne+=re,ve.class==="self"){m.self+=re;continue}let Ce=ve.class==="rework"?1:Number(ve.decay)||0,J=re*Ce;if(m.decayed+=re-J,J<=0)continue;let he=p(ve.key,{provider:ve.provider,model:ve.model});he.fixes+=1,he.linesReworked+=ve.class==="rework"?ve.lines:0;let Fe=C?.cause&&mc.includes(C.cause)?C.cause:"unclassified";he.causes[Fe]+=1,W?(he.reworkChargedUsd+=J,m.charged+=J,Pe.charged=wn(Pe.charged+J)):(he.reworkPendingUsd+=J,m.pending+=J),Pe.origins.push({key:ve.key,provider:he.provider,model:he.model,lines:ve.lines,ageDays:ve.ageDays,class:ve.class,usd:wn(J)})}let Ve=Math.max(0,K-ne);m.unattributed+=Ve}k.push(Pe),!c.has(I.sha)&&l.has(I.sha)&&S.push(I.sha)}for(let A of Object.values(u)){let I=A.provider||"unknown",j=A.model||"unknown",K=R=>{R.genUsd+=A.genUsd,R.reworkChargedUsd+=A.reworkChargedUsd,R.reworkPendingUsd+=A.reworkPendingUsd,R.linesReworked+=A.linesReworked,R.fixes+=A.fixes;for(let E of Object.keys(R.causes))R.causes[E]+=A.causes[E]};K($[I]||=Qh()),K(M[j]||=Qh())}k.sort((A,I)=>I.ts-A.ts);let B=Object.values(u).map(A=>{let I=n[`${A.trackId}/${A.boxId}`]||{};return ew({...A,trackName:I.trackName??null,label:I.label??null})}).sort((A,I)=>I.reworkChargedUsd+I.reworkPendingUsd-(A.reworkChargedUsd+A.reworkPendingUsd)||I.genUsd-A.genUsd);return{window:t,basis:`rework = code lines a fix deletes/modifies that blame to another epoch's commit \u2264${t} d old (docs/config churn and in-place updates \u2265${ST} similar are counted, not blamed); fix cost = the fixing box's turns since its previous commit; charged only on an authoritative / operator / self-reported cause; planned work (follow-up, blueprint item, feat:/docs: prefix, non-fix prompt) is generation cost`,intents:y,noCodeDelta:b,byEpoch:Object.fromEntries(B.map(A=>[A.key,A])),epochs:B,byProvider:Object.fromEntries(Object.entries($).map(([A,I])=>[A,ew(I)])),byModel:Object.fromEntries(Object.entries(M).map(([A,I])=>[A,ew(I)])),buckets:Object.fromEntries(Object.entries(m).map(([A,I])=>[A,wn(I)])),fixUsdTotal:wn(Object.values(m).reduce((A,I)=>A+I,0)),fixes:k,verdicts:g,pendingTaps:S,commits:s.length}}var hF,wF,tw,kT,xT,vT,gF,lo,rw,ow,mc,Id,yF,bF,Aa,ST,kF,SF,gT,AF,TF,EF,IF,_F,yT,$F,RF,OF,MF,nw,Td,wn,Qh,Rd=q(()=>{Yr();hF=mF(fF),wF=64*1024*1024,tw=(e,t)=>hF("git",["-C",e,...t],{maxBuffer:wF,timeout:6e4}),kT="Bostrat-Epoch",xT="Bostrat-Model",vT="Bostrat-Fix-Cause",gF=24*36e5,lo=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,rw=90,ow=3,mc=Object.freeze(["defect","design","requirements","style"]),Id="not-a-fix",yF=new Set(["A","B","E"]),bF=Object.freeze(["fix","likely-fix","planned","unknown"]),Aa=new Set(["fix","likely-fix"]),ST=.8;kF={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};SF=/^revert\b|^revert "/i,gT=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,AF=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,TF=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;EF=/^([a-z]+)(\([^)]*\))?!?:\s/i,IF=new Set(["fix","hotfix","bugfix","revert"]),_F=new Set(["feat","feature","docs","doc","test","tests","build","ci","chore"]),yT=/\bBP-[A-Z0-9]+(?:-[A-Z0-9]+)*\b/i,$F=new Set(["feature-build","docs-writing","research","design","ops-infra","orchestration"]);RF=[/(^|\/)\.bostrat\//,/(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|Cargo\.lock|poetry\.lock|Gemfile\.lock|go\.sum|composer\.lock)$/,/\.min\.(js|css)$/,/\.map$/,/\.snap$/,/(^|\/)(node_modules|dist|build|vendor|\.next|target)\//],OF=[/\.(md|mdx|markdown|rst|txt|adoc|asciidoc)$/i,/(^|\/)(docs?|notes|business|design)\//i,/(^|\/)(README|CHANGELOG|CHANGES|LICENSE|CONTRIBUTING|AUTHORS|NOTICE)(\.[a-z]+)?$/i],MF=[/\.(json|jsonc|json5|ya?ml|toml|ini|cfg|conf|properties|env|example)$/i,/(^|\/)\.[^/]+$/,/(^|\/)\.github\//,/(^|\/)(Dockerfile|Makefile|Procfile|fly\.toml)$/];nw=e=>String(e??"").replace(/\s+/g," ").trim(),Td=400;wn=e=>Math.round(e*1e4)/1e4,Qh=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import{execFile as LF}from"node:child_process";import{promisify as FF}from"node:util";function IT(e,t=Date.now()){return Ff(e,t)??0}function HF(e,t,n){for(let r=e.length-1;r>=0;r--){let o=e[r];if(o.type!=="cost.recorded"||o.boxId!==t||n!=null&&o.epoch!=null&&o.epoch!==n)continue;let s=o.payload?.model;if(s&&s!=="unknown")return s}return null}async function _T(e,t,{now:n=Date.now()}={}){let r=IT(t,n),o=await G(e);return _d({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:HF(o,t.name,r)})}function WF(e,t){let n=null;for(let r=e.length-1;r>=0;r--){let o=e[r];if(o.type!=="turn.user"||o.boxId!==t||o.actor!=="user")continue;let s=o.payload||{};if(s.note?.kind!=="task"){if(o.source==="orchestrator"&&typeof s.text=="string"&&s.text.trim())return{text:s.text,kind:s.kind??null};!n&&typeof s.promptHead=="string"&&s.promptHead.trim()&&(n={text:s.promptHead,kind:s.kind??null})}}return n}function qF(e,t,n,r){let o=Array.isArray(t?.epochs)?t.epochs:[],s=r>0?o[r-1]:null,a=s?.endReason?.code??(typeof s?.endReason=="string"?s.endReason:null),i=WF(n,t.name),c=i?ar(i.text)?.category??null:null;return{wallReason:a,fixerCategory:c,promptKind:i?.kind??null}}async function GF(e,t){try{let{stdout:n}=await aw(e,["show","--shortstat","--format=",t]),r=/(\d+) files? changed/.exec(n),o=/(\d+) insertions?\(\+\)/.exec(n),s=/(\d+) deletions?\(-\)/.exec(n);return{files:r?Number(r[1]):0,insertions:o?Number(o[1]):0,deletions:s?Number(s[1]):0}}catch{return{files:0,insertions:0,deletions:0}}}async function KF(e){try{let{stdout:t}=await aw(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function $T(e,t,n,{cwd:r=t?.cwd,log:o=console,now:s=Date.now()}={}){if(!e?.id||!t?.name||!n||!r)return{recorded:!1,reason:"incomplete"};let a=await G(e);if(a.some(m=>m.type==="code.committed"&&m.payload?.sha===n))return{recorded:!1,reason:"duplicate"};let i=s,c=null;try{let{stdout:m}=await aw(r,["show","-s","--format=%ct%n%s",n]),[g,...y]=m.split(`
|
|
194
|
+
`)}}async function ET(e,t,{fixKey:n=null,window:r=lo,resolveOrigin:o=null,context:s={}}={}){let a=await xT(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=yT(a.message),l=n?Id(n):c.epoch,u=l?Sa(l):null,{stdout:d}=await tw(e,["diff","-U0","--no-color","-M","-C",i,t]),p=BF(d),f=0,w=0,m=0,g=0,y=0,b=0,k=0,S=new Map;for(let I of p){if(I.binary)continue;let j=I.oldPath||I.newPath||"",K=I.deleted.reduce((W,[,Y])=>W+Y,0),R=DF(j);if(R==="excluded"){m+=K;continue}if(R==="docs"){g+=K,y+=I.added;continue}if(R==="config"){b+=K;continue}f+=I.added;let T=new Set;for(let W of I.hunks||[])for(let Y of LF(W))T.add(Y);k+=T.size;let C=[];for(let[W,Y]of I.deleted)for(let me=W;me<W+Y;me++)T.has(me)||C.push(me);if(w+=C.length,!I.oldPath||!C.length)continue;let P;try{let W=["blame","-w","-M","-C","--line-porcelain"];for(let[Y,me]of FF(C))W.push("-L",`${Y},${Y+me-1}`);W.push(i,"--",I.oldPath),P=UF((await tw(e,W)).stdout)}catch{continue}for(let{sha:W,time:Y}of P.values()){let me=S.get(W)??{lines:0,time:Y};me.lines+=1,S.set(W,me)}}let $=[],M=0,L=new Map;for(let[I,{lines:j,time:K}]of S){let R=L.get(I);if(!R){try{R=await xT(e,I)}catch{R=null}L.set(I,R)}let T=R?yT(R.message):{epoch:null},C=T.epoch,P=T.provider,W=T.model;if(!C&&o){let ne=await o(I,R);ne?.trackId&&ne?.boxId!=null&&(C={trackId:ne.trackId,boxId:ne.boxId,epoch:ne.epoch??0},P=P??ne.provider??null,W=W??ne.model??null)}if(!C){M+=j;continue}let Y=Number.isFinite(K)?K:R?.ts??a.ts,me=Math.max(0,(a.ts-Y)/kF),_e=Sa(C),Pe=_e===u?"self":AF(me,r);$.push({sha:I,key:_e,trackId:C.trackId,boxId:C.boxId,epoch:C.epoch,provider:P??null,model:W??null,lines:j,ageDays:Math.round(me*100)/100,class:Pe,decay:Pe==="rework"?1:Pe==="drift"?Math.round(TF(me,{window:r})*1e3)/1e3:0})}$.sort((I,j)=>j.lines-I.lines);let B=Rd({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0}),A=sw({heuristic:B,subject:a.subject,ref:s.ref??null,promptKind:s.promptKind??null,fixerCategory:s.fixerCategory??null,ticketKind:s.ticketKind??null});return{sha:a.sha,parent:i,subject:a.subject.slice(0,200),ts:a.ts,fixKey:u,fixProvider:c.provider??s.provider??null,fixModel:c.model??s.model??null,window:r,added:f,deleted:w,excludedDeleted:m,cosmeticDeleted:k,docsDeleted:g,docsAdded:y,configDeleted:b,codeDelta:f+w+k>0,origins:$,unattributedLines:M,ghost:w===0&&f>0,heuristic:B,intent:A}}function IT(e,{minLines:t=ow}={}){if(!e)return!1;let n=e.intent?.intent??"unknown";return Aa.has(n)?wc(e)>=t:!1}function wc(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function ew(e){let t=e.genUsd;return{...e,genUsd:wn(t),reworkChargedUsd:wn(e.reworkChargedUsd),reworkPendingUsd:wn(e.reworkPendingUsd),trueUsd:wn(t+e.reworkChargedUsd),multiplier:t>0?wn((t+e.reworkChargedUsd)/t):null}}function _T(e,{window:t=lo,boxMeta:n={}}={}){let r=e||[],o=r.filter(A=>A.type==="cost.recorded"),s=r.filter(A=>A.type==="code.committed"),a=r.filter(A=>A.type==="code.rework"),i=A=>Array.isArray(A?.shas)&&A.shas.length?A.shas:A?.sha?[A.sha]:[],c=new Map,l=new Set;for(let A of r)if(A.type==="rework.cause.offered")for(let I of i(A.payload))l.add(I);else if(A.type==="rework.cause.answered"&&A.payload?.cause)for(let I of i(A.payload))c.set(I,A.payload.cause);else if(A.type==="rework.cause.retracted")for(let I of i(A.payload))c.delete(I);let u={},d=(A,I,j)=>Sa({trackId:A,boxId:I,epoch:j??0}),p=(A,I={})=>{if(!u[A]){let K=Id(A)||{};u[A]={key:A,...K,provider:null,model:null,...Qh()}}let j=u[A];return I.provider&&!j.provider&&(j.provider=I.provider),I.model&&!j.model&&(j.model=I.model),j};for(let A of o){if(!A.boxId)continue;let I=d(A.trackId,A.boxId,A.epoch),{usd:j,model:K}=Xt(A),R=p(I,{provider:A.provider,model:K!=="unknown"?K:null});R.genUsd+=j}let f=new Map;for(let A of s){let I=`${A.trackId}/${A.boxId}`;(f.get(I)??f.set(I,[]).get(I)).push({...A,ts:A.payload?.ts??A.ts})}for(let A of f.values())A.sort((I,j)=>I.ts-j.ts);let w=(A,I,j,K)=>{let R=f.get(`${A}/${I}`)||[],T=-1/0;for(let W of R)W.ts<K&&(T=W.ts);let C=0,P=0;for(let W of o)W.trackId!==A||W.boxId!==I||(W.epoch??0)===(j??0)&&(W.ts<=T||W.ts>K||(C+=Xt(W).usd,P++));return{usd:C,turns:P,from:Number.isFinite(T)?T:null}},m={charged:0,pending:0,self:0,decayed:0,unattributed:0,ghost:0,planned:0},g={A:0,B:0,C:0,E:0,none:0},y={fix:0,"likely-fix":0,planned:0,unknown:0},b=0,k=[],S=[],$={},M={},L=new Set;for(let A of a){let I=A.payload||{};if(!I.sha||L.has(I.sha))continue;if(L.add(I.sha),I.codeDelta===!1){b++;continue}let j=Id(I.fixKey)||{trackId:A.trackId,boxId:A.boxId,epoch:A.epoch??0},{usd:K,turns:R}=w(j.trackId,j.boxId,j.epoch,I.ts??A.ts),T=vF.includes(I.intent?.intent)?I.intent.intent:"unknown";y[T]+=1;let C=null;c.has(I.sha)?C={cause:c.get(I.sha),level:"B",basis:"operator"}:(I.heuristic?.level&&I.heuristic.level!=="C"||I.heuristic)&&(C=I.heuristic);let P=C?.level??"none";g[P]=(g[P]||0)+1;let W=xF.has(P),Y=Number(I.deleted)||0,me=Array.isArray(I.origins)?I.origins:[],_e=me.filter(ne=>ne.class==="rework").reduce((ne,Ve)=>ne+Ve.lines,0),Pe={sha:I.sha,subject:I.subject??"",ts:I.ts??A.ts,trackId:A.trackId,fixKey:I.fixKey??null,fixProvider:I.fixProvider??A.provider??null,fixUsd:wn(K),fixTurns:R,linesReworked:_e,unattributedLines:Number(I.unattributedLines)||0,ghost:I.ghost===!0,intent:{intent:T,basis:I.intent?.basis??null},verdict:C?{cause:C.cause,level:P,basis:C.basis??null}:null,charged:wn(0),origins:[]};if(T==="planned"||C?.cause===_d){m.planned+=K;for(let ne of me)ne.class==="rework"&&Pe.origins.push({key:ne.key,provider:ne.provider??null,model:ne.model??null,lines:ne.lines,ageDays:ne.ageDays,class:ne.class,usd:0})}else if(I.ghost||Y===0)m.ghost+=K;else{let ne=0;for(let ve of me){let ce=ve.lines/Y,re=K*ce;if(ne+=re,ve.class==="self"){m.self+=re;continue}let Ce=ve.class==="rework"?1:Number(ve.decay)||0,J=re*Ce;if(m.decayed+=re-J,J<=0)continue;let he=p(ve.key,{provider:ve.provider,model:ve.model});he.fixes+=1,he.linesReworked+=ve.class==="rework"?ve.lines:0;let Fe=C?.cause&&hc.includes(C.cause)?C.cause:"unclassified";he.causes[Fe]+=1,W?(he.reworkChargedUsd+=J,m.charged+=J,Pe.charged=wn(Pe.charged+J)):(he.reworkPendingUsd+=J,m.pending+=J),Pe.origins.push({key:ve.key,provider:he.provider,model:he.model,lines:ve.lines,ageDays:ve.ageDays,class:ve.class,usd:wn(J)})}let Ve=Math.max(0,K-ne);m.unattributed+=Ve}k.push(Pe),!c.has(I.sha)&&l.has(I.sha)&&S.push(I.sha)}for(let A of Object.values(u)){let I=A.provider||"unknown",j=A.model||"unknown",K=R=>{R.genUsd+=A.genUsd,R.reworkChargedUsd+=A.reworkChargedUsd,R.reworkPendingUsd+=A.reworkPendingUsd,R.linesReworked+=A.linesReworked,R.fixes+=A.fixes;for(let T of Object.keys(R.causes))R.causes[T]+=A.causes[T]};K($[I]||=Qh()),K(M[j]||=Qh())}k.sort((A,I)=>I.ts-A.ts);let B=Object.values(u).map(A=>{let I=n[`${A.trackId}/${A.boxId}`]||{};return ew({...A,trackName:I.trackName??null,label:I.label??null})}).sort((A,I)=>I.reworkChargedUsd+I.reworkPendingUsd-(A.reworkChargedUsd+A.reworkPendingUsd)||I.genUsd-A.genUsd);return{window:t,basis:`rework = code lines a fix deletes/modifies that blame to another epoch's commit \u2264${t} d old (docs/config churn and in-place updates \u2265${TT} similar are counted, not blamed); fix cost = the fixing box's turns since its previous commit; charged only on an authoritative / operator / self-reported cause; planned work (follow-up, blueprint item, feat:/docs: prefix, non-fix prompt) is generation cost`,intents:y,noCodeDelta:b,byEpoch:Object.fromEntries(B.map(A=>[A.key,A])),epochs:B,byProvider:Object.fromEntries(Object.entries($).map(([A,I])=>[A,ew(I)])),byModel:Object.fromEntries(Object.entries(M).map(([A,I])=>[A,ew(I)])),buckets:Object.fromEntries(Object.entries(m).map(([A,I])=>[A,wn(I)])),fixUsdTotal:wn(Object.values(m).reduce((A,I)=>A+I,0)),fixes:k,verdicts:g,pendingTaps:S,commits:s.length}}var yF,bF,tw,vT,ST,AT,kF,lo,rw,ow,hc,_d,xF,vF,Aa,TT,SF,EF,bT,IF,_F,$F,RF,OF,kT,MF,CF,NF,PF,nw,Ed,wn,Qh,Od=q(()=>{Yr();yF=gF(wF),bF=64*1024*1024,tw=(e,t)=>yF("git",["-C",e,...t],{maxBuffer:bF,timeout:6e4}),vT="Bostrat-Epoch",ST="Bostrat-Model",AT="Bostrat-Fix-Cause",kF=24*36e5,lo=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,rw=90,ow=3,hc=Object.freeze(["defect","design","requirements","style"]),_d="not-a-fix",xF=new Set(["A","B","E"]),vF=Object.freeze(["fix","likely-fix","planned","unknown"]),Aa=new Set(["fix","likely-fix"]),TT=.8;SF={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};EF=/^revert\b|^revert "/i,bT=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,IF=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,_F=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;$F=/^([a-z]+)(\([^)]*\))?!?:\s/i,RF=new Set(["fix","hotfix","bugfix","revert"]),OF=new Set(["feat","feature","docs","doc","test","tests","build","ci","chore"]),kT=/\bBP-[A-Z0-9]+(?:-[A-Z0-9]+)*\b/i,MF=new Set(["feature-build","docs-writing","research","design","ops-infra","orchestration"]);CF=[/(^|\/)\.bostrat\//,/(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|Cargo\.lock|poetry\.lock|Gemfile\.lock|go\.sum|composer\.lock)$/,/\.min\.(js|css)$/,/\.map$/,/\.snap$/,/(^|\/)(node_modules|dist|build|vendor|\.next|target)\//],NF=[/\.(md|mdx|markdown|rst|txt|adoc|asciidoc)$/i,/(^|\/)(docs?|notes|business|design)\//i,/(^|\/)(README|CHANGELOG|CHANGES|LICENSE|CONTRIBUTING|AUTHORS|NOTICE)(\.[a-z]+)?$/i],PF=[/\.(json|jsonc|json5|ya?ml|toml|ini|cfg|conf|properties|env|example)$/i,/(^|\/)\.[^/]+$/,/(^|\/)\.github\//,/(^|\/)(Dockerfile|Makefile|Procfile|fly\.toml)$/];nw=e=>String(e??"").replace(/\s+/g," ").trim(),Ed=400;wn=e=>Math.round(e*1e4)/1e4,Qh=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import{execFile as HF}from"node:child_process";import{promisify as WF}from"node:util";function $T(e,t=Date.now()){return Uf(e,t)??0}function GF(e,t,n){for(let r=e.length-1;r>=0;r--){let o=e[r];if(o.type!=="cost.recorded"||o.boxId!==t||n!=null&&o.epoch!=null&&o.epoch!==n)continue;let s=o.payload?.model;if(s&&s!=="unknown")return s}return null}async function RT(e,t,{now:n=Date.now()}={}){let r=$T(t,n),o=await G(e);return $d({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:GF(o,t.name,r)})}function KF(e,t){let n=null;for(let r=e.length-1;r>=0;r--){let o=e[r];if(o.type!=="turn.user"||o.boxId!==t||o.actor!=="user")continue;let s=o.payload||{};if(s.note?.kind!=="task"){if(o.source==="orchestrator"&&typeof s.text=="string"&&s.text.trim())return{text:s.text,kind:s.kind??null};!n&&typeof s.promptHead=="string"&&s.promptHead.trim()&&(n={text:s.promptHead,kind:s.kind??null})}}return n}function VF(e,t,n,r){let o=Array.isArray(t?.epochs)?t.epochs:[],s=r>0?o[r-1]:null,a=s?.endReason?.code??(typeof s?.endReason=="string"?s.endReason:null),i=KF(n,t.name),c=i?ar(i.text)?.category??null:null;return{wallReason:a,fixerCategory:c,promptKind:i?.kind??null}}async function zF(e,t){try{let{stdout:n}=await aw(e,["show","--shortstat","--format=",t]),r=/(\d+) files? changed/.exec(n),o=/(\d+) insertions?\(\+\)/.exec(n),s=/(\d+) deletions?\(-\)/.exec(n);return{files:r?Number(r[1]):0,insertions:o?Number(o[1]):0,deletions:s?Number(s[1]):0}}catch{return{files:0,insertions:0,deletions:0}}}async function JF(e){try{let{stdout:t}=await aw(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function OT(e,t,n,{cwd:r=t?.cwd,log:o=console,now:s=Date.now()}={}){if(!e?.id||!t?.name||!n||!r)return{recorded:!1,reason:"incomplete"};let a=await G(e);if(a.some(m=>m.type==="code.committed"&&m.payload?.sha===n))return{recorded:!1,reason:"duplicate"};let i=s,c=null;try{let{stdout:m}=await aw(r,["show","-s","--format=%ct%n%s",n]),[g,...y]=m.split(`
|
|
195
195
|
`),b=Number(g.trim())*1e3;Number.isFinite(b)&&b>0&&(i=b),c=y.join(`
|
|
196
|
-
`).trim().slice(0,300)||null}catch{return{recorded:!1,reason:"unknown-sha"}}let l
|
|
197
|
-
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+rU(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 cw(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function FT(e){let t=jT(e),n=t.match(eU);if(!n)return null;let r=cw(t,n.index);return nU.test(r)?null:{repo:r.match(LT)?.[1]??null,head:r.match(oU)?.[1]??null}}function UT(e){let t=jT(e),n=t.match(tU);if(!n)return null;let r=cw(t,n.index),o=cw(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("-")){aU.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(LT)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function cU(e,t,{exclude:n=()=>!1}={}){let r=async i=>{pr("observer");let{stdout:c}=await NT("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",iU],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw ao(l)&&io(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-XF&&!n(c)),s=await _n({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 lU(e,t){if(t)return[t];let n=[];for(let r of HT(e)){let o=await Oe(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await nr().catch(()=>[])){let o=await Oe(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function HT(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 uU(e,t,n){let r=await va(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)||sU(c.headRefName,e.id),i=[];for(let c of await lU(e,t?.repo??null)){let l;try{l=await cU(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await pt(c,u.number);if(s(u.headRefName)){if(d&&d.trackId===e.id&&d.announced)return{settled:!0};if(d&&d.trackId!==e.id)continue;return{slug:c,pr:u}}d&&(d.trackId!==e.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 WT(e){if(!e)return 0;let t=0;for(let n of mr)n.startsWith(`${e}|`)&&t++;return t}function qT(){for(let e of[...lw])e()}function GT(e,t,{delays:n=PT,turnEnd:r}={}){let o=FT(t.command);if(!o)return null;let s=!!UT(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(mr.has(a))return null;mr.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(m=>{let g=()=>{clearTimeout(y),lw.delete(g),m()},y=setTimeout(g,l);lw.add(g)});let u=await uU(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:d,pr:p}=u,{link:f,created:w}=await Ut({repo:d,number:p.number,trackId:e.id,url:p.url,headRef:p.headRefName,title:p.title,source:"observed"});return!w&&!(f?.trackId===e.id&&await ur(d,p.number))?null:(await iw(t.box,t.agent,r),await N({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:p.number,url:p.url,title:p.title,branch:p.headRefName,repo:d,...s?{selfMerge:!0}:{}}}),p)}return null}finally{mr.delete(a)}})();return c.catch(()=>mr.delete(a)),c}function KT(e,t,{delays:n=PT,turnEnd:r,openedWait:o}={}){let s=UT(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(mr.has(a))return null;mr.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 w=HT(e);if(w.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let m of w){let g=await Oe(m).catch(()=>null);g&&!d.includes(g)&&d.push(g)}}if(l||=(await va(e).catch(()=>[])).filter(w=>w!==e.workspace?.branch)[0]||e.workspace?.branch,!d.length||!l)return null;c=d[0];let p=async(w,m)=>{let g=await pt(c,w.number);return g&&g.trackId!==e.id?null:(g||await Ut({repo:c,number:w.number,trackId:e.id,url:w.url,headRef:w.headRefName,title:w.title,source:"merge-observed"}),await sc(c,w.number),m&&((await G(e).catch(()=>[])).some(k=>k.type==="pr.state.changed"&&(k.payload?.change==="auto-armed"||k.payload?.change==="merged")&&k.payload?.number===w.number)||(await iw(t.box,t.agent,r),await N({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:w.number,url:w.url,title:w.title,branch:w.headRefName,repo:c}}))),w)},f=null;for(let w of n){await new Promise(M=>setTimeout(M,w));let m=[];for(let M of d)try{pr("observer");let{stdout:L}=await NT("gh",["pr","view",String(l),"--repo",M,"--json",dU],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await _n({slug:M})}}),B=JSON.parse(L||"null");B?.number&&m.push({slug:M,pr:B})}catch(L){ao(L)&&io(L)}let g=m.find(M=>M.pr.state==="MERGED")??(m.length===1?m[0]:null);if(!g)continue;c=g.slug;let y=g.pr;if(!y?.number)continue;if(y.state==="CLOSED")return null;if(y.state!=="MERGED"){if(f=y,s.auto)return p(y,!0);continue}let b=await pt(c,y.number);if(b&&b.trackId!==e.id)return null;b||await Ut({repo:c,number:y.number,trackId:e.id,url:y.url,headRef:y.headRefName,title:y.title,source:"merge-observed"}),await iw(t.box,t.agent,r);let k=await G(e).catch(()=>[]);if(k.some(M=>M.type==="pr.state.changed"&&M.payload?.change==="merged"&&M.payload?.number===y.number))return null;let S=M=>M.some(L=>L.type==="pr.state.changed"&&L.payload?.change==="opened"&&L.payload?.number===y.number),$=0;if(!S(k)){if(FT(t.command)&&await ur(c,y.number)){let M=Date.now();$=M+1,await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:M,change:"opened",number:y.number,url:y.url,title:y.title,branch:y.headRefName,repo:c,selfMerge:!0}})}else if((await pt(c,y.number))?.announced){let M=o?.capMs??ZF,L=o?.pollMs??QF,B=Date.now()+M;for(;Date.now()<B&&(await new Promise(A=>setTimeout(A,L)),!S(await G(e).catch(()=>[]))););}}return await N({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(),$),change:"merged",number:y.number,url:y.url,title:y.title,branch:y.headRefName,repo:c}}),await Od(e,{repo:c,number:y.number,title:y.title,branch:y.headRefName,url:y.url}).catch(()=>{}),await wr(c,y.number).catch(()=>{}),await hr(c,y.number).catch(()=>{}),await oo(c,y.number,"merged"),y}return f?p(f,!1):null}finally{mr.delete(a)}})();return i.catch(()=>mr.delete(a)),i}var NT,PT,JF,YF,XF,ZF,QF,DT,BT,eU,tU,nU,CT,rU,LT,oU,sU,aU,iU,mr,lw,dU,Cd=q(()=>{ye();Pr();yt();ic();jn();gr();fc();cs();Md();NT=zF(VF),PT=[4e3,15e3,45e3],JF=2500,YF=5*6e4;XF=15e3,ZF=9e4,QF=2e3,DT=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,BT=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,eU=new RegExp(`${DT}${BT}create\\b`),tU=new RegExp(`${DT}${BT}merge\\b`),nU=/\s--web\b/,CT=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,rU=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");LT=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,oU=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,sU=hT;aU=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);iU="number,url,title,headRefName,createdAt";mr=new Set;lw=new Set;dU="number,url,title,headRefName,state"});import pU from"node:fs/promises";import dw from"node:path";import fU from"node:os";function uw(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function Nd(e){let t=mU.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=uw(n,"from"),s=o?o.replace(/^uds:/,""):null,a=VT(s);return{socket:s,pid:a,name:uw(n,"from-name"),mode:uw(n,"from-mode"),text:r.trim()}}function VT(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function hU(){return process.env.CLAUDE_CONFIG_DIR?dw.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):dw.join(fU.homedir(),".claude","sessions")}async function wU(e){let t=VT(e);if(!t)return null;try{let n=await pU.readFile(dw.join(hU(),`${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 zT(e,t){let n=await wU(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 mU,pw=q(()=>{mU=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function JT(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function YT(e,t,n=fw){let r=new AbortController,o=setTimeout(()=>r.abort(),n);try{return await yU(`${Ne(t)}${e}`,{headers:bU(t),signal:r.signal})}finally{clearTimeout(o)}}async function wc({env:e=process.env,force:t=!1,timeoutMs:n=fw}={}){if(!JT(e))return{followups:[],source:"unconfigured"};if(!t&&ls&&Date.now()-ls.at<gU)return{followups:ls.followups,source:"cache"};try{let r=await YT("/v1/followups?deleted=1",e,n);if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json(),s=Array.isArray(o?.followups)?o.followups:[];return ls={at:Date.now(),followups:s},{followups:s,source:"plane"}}catch(r){return ls?{followups:ls.followups,source:"stale",error:r?.message}:{followups:[],source:"unreachable",error:r?.message}}}async function XT(e,{env:t=process.env,timeoutMs:n=fw}={}){if(!e)return{followup:null,source:"unconfigured"};if(!JT(t))return{followup:null,source:"unconfigured"};try{let r=await YT(`/v1/followups/${encodeURIComponent(e)}`,t,n);if(r.status===404)return{followup:null,source:"gone"};if(!r.ok)throw new Error(`plane ${r.status}`);return{followup:(await r.json())?.followup??null,source:"plane"}}catch(r){let o=ls?.followups.find(s=>s.id===e)??null;return o?{followup:o,source:"stale",error:r?.message}:{followup:null,source:"unreachable",error:r?.message}}}var gU,fw,ls,yU,bU,ZT=q(()=>{yt();gU=6e4,fw=5e3,ls=null,yU=(...e)=>fetch(...e);bU=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});async function uo(e,t){if(mw.has(e)){let n=new Error("offer already answered (answer in flight)");throw n.code="answer-in-flight",n}mw.add(e);try{return await t()}finally{mw.delete(e)}}var mw,gc=q(()=>{mw=new Set});import{randomUUID as us,createHash as kU}from"node:crypto";function Pd(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function iE(e){for(let[t,n]of AU)if(t.test(String(e||"")))return`a standing prompt must not embed this Track's specifics (${n}) \u2014 the card supplies them at fire time; arm a one-shot with \`offer\` instead`;return null}function nE(e,{at:t="",promptRequired:n=!0,standing:r=!1}={}){let o=e&&typeof e=="object"?e:{},s=ds(o.label).replace(/\s+/g," ");if(!s)throw new Error(`${t}label required (\u2264${hw} chars, e.g. "Deploy to Pi")`);if(s.length>hw)throw new Error(`${t}label must be \u2264${hw} characters`);let a=ds(o.prompt);if(n&&!a)throw new Error(`${t}prompt required \u2014 the full instruction a fresh session could execute`);if(Buffer.byteLength(a,"utf8")>QT)throw new Error(`${t}prompt must be \u2264${QT} bytes`);if(r){let l=iE(a);if(l)throw new Error(`${t}${l}`)}let i=o.standing==null||o.standing===""?"offerable":String(o.standing).trim().toLowerCase();if(!SU.includes(i))throw new Error(`${t}standing must be "offerable" or "never", got "${o.standing}"`);let c=ds(o.why).slice(0,oE)||null;return{label:s,prompt:a,why:c,standing:i}}function cE(e,{standing:t=!1}={}){let n=e&&typeof e=="object"?e:{},r=ds(n.trigger)||(n.ship?"now":""),o=t?xU:sE.filter(p=>p!=="track.opened");if(!o.includes(r))throw new Error(`trigger must be one of ${o.join(" | ")}${t?" (a standing follow-up can't be `now` \u2014 it would fire every turn)":""}, got "${n.trigger??""}"`);let s=n.ship&&r==="now"?IU(n.ship):null,a=n.items!=null;if(a&&s)throw new Error("a ship offer has its own card \u2014 `items` cannot be combined with a `ship` block");let i,c=null;if(a){if(!Array.isArray(n.items)||!n.items.length)throw new Error("items must be a non-empty array of {label, prompt, why?, standing?}");if(n.items.length>tE)throw new Error(`items must be \u2264${tE} \u2014 one item per step the operator might run, save or skip separately`);if(t&&n.items.length>1)throw new Error("propose one standing follow-up at a time \u2014 a standing follow-up is one prompt");i=n.items.map((f,w)=>nE(f,{at:`items[${w}]: `,standing:t})),c=ds(n.why).slice(0,oE)||null;let p=new Set;for(let f of i){let w=Pd(f.label);if(p.has(w))throw new Error(`items must have distinct labels ("${f.label}" appears twice)`);p.add(w)}}else i=[nE(n,{promptRequired:!s,standing:t})];let l=i[0],u=c??l.why,d=t?EU(n.repo):null;return{label:l.label,prompt:l.prompt,trigger:r,why:u,planWhy:c,items:i,...d?{repo:d}:{},...s?{ship:s}:{}}}function TU(e){return e.standing==="never"?{promotable:!1,promotableReason:"one-off"}:iE(e.prompt)?{promotable:!1,promotableReason:"specifics"}:{promotable:!0,promotableReason:null}}function EU(e){if(e==null||e===""||e==="*")return null;let t=String(e).trim();if(!/^[\w.-]+\/[\w.-]+$/.test(t))throw new Error(`repo must be a forge slug like owner/name, got "${e}"`);return t}function IU(e){let t=e&&typeof e=="object"?e:{},n=Array.isArray(t.actions)?t.actions.map(i=>String(i).trim().toLowerCase()):ww.slice();for(let i of n)if(!ww.includes(i))throw new Error(`ship.actions may only contain ${ww.join(" | ")}, got "${i}"`);if(!n.length)throw new Error("ship.actions must name at least one of commit | push | pr");let r=t.rebase==null?"none":String(t.rebase).trim();if(!["offered","none"].includes(r))throw new Error(`ship.rebase must be "offered" or "none", got "${t.rebase}"`);let o=null;if(t.toPr!=null&&t.toPr!==""){let i=Number(t.toPr);if(!Number.isInteger(i)||i<=0)throw new Error(`ship.toPr must be a PR number, got "${t.toPr}"`);o=i}let s=ds(t.base)||null,a=null;if(t.repos!=null){if(!Array.isArray(t.repos))throw new Error("ship.repos must be an array of checkout names");a=t.repos.map(i=>ds(i)).filter(Boolean),a.length||(a=null)}return{actions:[...new Set(n)],rebase:r,toPr:o,base:s,repos:a}}function Dd(e,{trigger:t=null}={}){let n=new Map;for(let r of e){let o=r.payload||{};if(r.type==="followup.armed"&&o.armId)o.replaces&&n.delete(o.replaces),n.set(o.armId,{...o,ts:o.ts??r.ts,boxId:r.boxId??null,provider:r.provider??null});else if(r.type==="followup.armed.cancelled"&&o.armId)n.delete(o.armId);else if(r.type==="followup.offered")for(let s of o.rows||[])s.armId&&n.delete(s.armId)}return[...n.values()].filter(r=>!t||r.trigger===t)}function lE(e){return{armId:e.armId,label:e.label,prompt:e.prompt,promptHash:aE(e.prompt),origin:"agent",why:e.why??null,groupId:e.groupId??null,seq:Number.isInteger(e.seq)?e.seq:0,standing:e.standing==="never"?"never":"offerable",...TU(e)}}async function uE(e,t,{pull:n=wc,ship:r={}}={}){if(yw(e))throw new Error("follow-ups don't fire on a loop's run Track \u2014 report the outcome with `bostrat-loop report` instead");let o=cE(t);if(o.ship)return{kind:"ship",...await MU(e,o,r)};let s={id:e.id,cwd:e.cwd},a=(e.boxes||[]).find(b=>b.name===e.leadBox)||null,i={boxId:e.leadBox??null,provider:a?.agent??null},c=us(),l=(b,k,S={})=>({ts:Date.now(),armId:us(),trigger:o.trigger,label:b.label,prompt:b.prompt,why:b.why,standing:b.standing,groupId:c,seq:k,...o.planWhy?{planWhy:o.planWhy}:{},...S}),u={groupId:c,trigger:o.trigger,label:o.label,prompt:o.prompt,why:o.why,planWhy:o.planWhy};if(o.trigger==="now"){let b=us(),k=[];for(let[S,$]of o.items.entries()){let M=l($,S);await N(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:M}),k.push(M)}return await N(s,{type:"followup.offered",actor:"system",source:"orchestrator",...i,payload:{ts:Date.now(),offerId:b,trigger:"now",repo:e.workspace?.repo||null,context:{},groupId:c,why:o.planWhy,rows:k.map(lE)}}),{kind:"now",offerId:b,armId:k[0].armId,items:k.map(S=>({armId:S.armId,label:S.label,standing:S.standing})),...u}}let d=e.workspace?.repo||null,{followups:p}=await n().catch(()=>({followups:[]})),f=await G(s),w=Dd(f,{trigger:o.trigger}),m=[],g=0;for(let b of o.items){let k=Pd(b.label),S=p.find(B=>B.status==="active"&&B.mode!=="off"&&B.trigger===o.trigger&&(B.repo==null||B.repo===d)&&B.dedupeKey===k);if(S){m.push({label:b.label,standing:b.standing,covered:{id:S.id,label:S.label}});continue}let $=w.filter(B=>Pd(B.label)===k),M=$.length?$[$.length-1].armId:null,L=l(b,g++,M?{replaces:M}:{});await N(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:L}),m.push({armId:L.armId,label:b.label,standing:b.standing,replaces:M})}let y=m.filter(b=>b.armId);return y.length?{kind:"armed",armId:y[0].armId,replaces:y[0].replaces,items:m,...u}:{kind:"covered",covered:m[0].covered,items:m,...u}}async function dE(e,t){if(!t)throw new Error("armId required");let n={id:e.id,cwd:e.cwd},r=await G(n);if(!Dd(r).some(o=>o.armId===t))throw new Error("unknown or already-settled arm");return await N(n,{type:"followup.armed.cancelled",actor:"user",source:"orchestrator",payload:{ts:Date.now(),armId:t}}),{armId:t}}async function pE(e,t,{pull:n=wc}={}){if(yw(e))throw new Error("propose from a lead Track, not a loop's run Track");let r=cE(t,{standing:!0}),o=r.repo??e.workspace?.repo??null,s={id:e.id,cwd:e.cwd};if((await G(s)).reduce((g,y)=>y.type==="followup.create.offered"?y.payload?.proposalId??g:y.type==="followup.create.answered"&&y.payload?.proposalId===g?null:g,null))throw new Error("a follow-up proposal is already waiting on the operator in this Track \u2014 one at a time");let{followups:c,source:l}=await n().catch(()=>({followups:[],source:"unreachable"}));if(l==="unreachable")throw new Error("plane unreachable \u2014 standing follow-ups live in the plane; try again shortly");let u=Pd(r.label),d=c.filter(g=>g.dedupeKey===u&&g.trigger===r.trigger),p=d.find(g=>g.status==="deleted"),f=d.find(g=>g.status!=="deleted");if(f)throw new Error(`"${f.label}" (${gw[f.trigger]||f.trigger}) already exists as a standing follow-up \u2014 not re-proposed`);if(p){let g=p.deletedAt?new Date(p.deletedAt).toISOString().slice(0,10):"earlier";throw new Error(`the operator deleted "${p.label}" on ${g} \u2014 do not re-propose it`)}let w=us(),m=(e.boxes||[]).find(g=>g.name===e.leadBox)||null;return await N(s,{type:"followup.create.offered",actor:"agent",source:"cli",boxId:e.leadBox??null,provider:m?.agent??null,payload:{ts:Date.now(),proposalId:w,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why,origin:"agent"}}),{proposalId:w,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why}}async function fE(e,t,n,{followupId:r=null}={}){if(!t)throw new Error("proposalId required");let o={id:e.id,cwd:e.cwd},s=await G(o),a=s.find(c=>c.type==="followup.create.offered"&&c.payload?.proposalId===t);if(!a)throw new Error("unknown proposal");if(s.some(c=>c.type==="followup.create.answered"&&c.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await N(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};if(!r)throw new Error("followupId required \u2014 the follow-up is created on the plane before the card is answered");let i=a.payload;return await N(o,{type:"followup.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,followupId:String(r),label:i.label,trigger:i.trigger,repo:i.repo??null}}),await N(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",followupId:String(r)}}),{proposalId:t,action:"created",followupId:String(r)}}async function yc(e,t,n={},{pull:r=wc}={}){if(!e||yw(e))return null;let o={id:e.id,cwd:e.cwd},s=await G(o).catch(()=>[]),a=n.repo??e.workspace?.repo??null;if(t==="pr.merged"){let y=S=>{let $=(S?.prs||[]).filter(M=>Number.isFinite(Number(M?.number))).map(M=>`${M?.repo??S?.repo??""}#${Number(M.number)}`);return Number.isFinite(Number(S?.prNumber))&&$.push(`${S?.repo??""}#${Number(S.prNumber)}`),$},b=new Set(y(n));if(s.some(S=>S.type==="followup.offered"&&S.payload?.trigger==="pr.merged"&&y(S.payload?.context).some($=>b.has($))))return null}let i=Dd(s,{trigger:t}),c=i.length?i[i.length-1].groupId??null:null,l=new Map(i.map((y,b)=>[y.armId,b])),u=y=>y.groupId&&y.groupId===c?0:1,d=i.slice().sort((y,b)=>u(y)-u(b)||l.get(y.armId)-l.get(b.armId)).slice(0,eE).map(lE),p=c?i.find(y=>y.groupId===c&&y.planWhy)?.planWhy??null:null,f=[];if(t!=="now"){let{followups:y}=await r().catch(()=>({followups:[]}));f=y.filter(b=>b.status==="active"&&b.mode!=="off"&&b.trigger===t&&(b.repo==null||b.repo===a)).sort((b,k)=>{let S=$=>$.stats?.offered?($.stats.accepted||0)/$.stats.offered:0;return S(k)-S(b)||(b.origin==="operator"?-1:0)-(k.origin==="operator"?-1:0)||(k.createdAt||0)-(b.createdAt||0)}).slice(0,vU).map(b=>({followupId:b.id,label:b.label,promptPreview:_U(b.prompt),promptHash:aE(b.prompt),origin:b.origin,repo:b.repo??null}))}let w=[...d,...f].slice(0,eE);if(!w.length)return null;let m=us(),g={ts:Date.now(),offerId:m,trigger:t,repo:a,context:{...n,repo:a},groupId:c,why:p,rows:w};return await N(o,{type:"followup.offered",actor:"system",source:"orchestrator",payload:g}),g}function $U(e,t){let n=t?.rows||[],r=a=>a.armId?`arm:${a.armId}`:`fu:${a.followupId}`,o=new Map(n.map(a=>[r(a),null])),s=(a,i)=>{o.has(a)&&o.get(a)==null&&o.set(a,i)};for(let a of e){if(a.type!=="followup.answered"||a.payload?.offerId!==t.offerId)continue;let i=a.payload,c=[];i.armId&&c.push(`arm:${i.armId}`),i.followupId&&c.push(`fu:${i.followupId}`);for(let l of Array.isArray(i.armIds)?i.armIds:[])c.push(`arm:${l}`);for(let l of Array.isArray(i.followupIds)?i.followupIds:[])c.push(`fu:${l}`);if(c.length)for(let l of c)s(l,i.action);else for(let l of o.keys())s(l,i.action)}return o}function rE(e){let t=e?.context||{},n=Array.isArray(t.prs)&&t.prs.length?t.prs:t.prNumber?[{number:t.prNumber,title:t.prTitle,url:t.prUrl,repo:t.repo}]:[],r=o=>`PR #${o.number}${o.title?` "${o.title}"`:""}${o.url?` (${o.url})`:""}${o.repo?` on ${o.repo}`:""}`;switch(e?.trigger){case"pr.merged":return n.length?`Follow-up the operator tapped after ${n.map(r).join(" and ")} merged:`:"Follow-up the operator tapped after this Track's PR merged:";case"pr.opened":return n.length?`Follow-up the operator tapped after ${n.map(r).join(" and ")} opened:`:"Follow-up the operator tapped after this Track's PR opened:";case"checks.green":return n.length?`Follow-up the operator tapped after CI went green on ${n.map(r).join(" and ")}:`:"Follow-up the operator tapped after CI went green:";case"ship.done":return"Follow-up the operator tapped after the ship you just ran completed:";default:return"Follow-up the operator tapped:"}}async function mE(e,{offerId:t,followupId:n=null,armId:r=null,promoted:o=null,all:s=!1}={},a={}){if(!t)throw new Error("offerId required");return uo(`fu:${e.id}:${t}`,()=>OU(e,{offerId:t,followupId:n,armId:r,promoted:o,all:s===!0},a))}async function OU(e,{offerId:t,followupId:n,armId:r,promoted:o,all:s},a){let{sayToLead:i,unarchiveTrack:c,getFollowup:l=XT}=a,u={id:e.id,cwd:e.cwd},d=await G(u),p=d.find(A=>A.type==="followup.offered"&&A.payload?.offerId===t);if(!p)throw new Error("unknown offer");let f=p.payload.rows||[],w=$U(d,p.payload),m=RU(w,f);if(!m.length)throw new Error("offer already answered");let g=A=>N(u,{type:"followup.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,...A}}),y=async()=>{if(!e.archivedAt)return!1;if(!c)throw new Error("this Track is archived");return await c(e.id,{freshSession:!0}),e={...e,archivedAt:null},!0},b=A=>{let j=d.find(K=>K.type==="followup.armed"&&K.payload?.armId===A.armId)?.payload?.prompt??A.prompt;if(!j)throw new Error("the arm's prompt is missing from the log");return j};if(s){let A=p.payload.groupId??null,I=m.filter(W=>W.armId&&(!A||W.groupId===A)).sort((W,Y)=>(W.seq??0)-(Y.seq??0));if(!I.length)throw new Error("nothing to run together \u2014 no open items from this plan remain on the card");let j=I.map(W=>({row:W,prompt:b(W)})),K=await y();if(!i)throw new Error("sayToLead unavailable");let R=`${rE(p.payload).replace(/:$/,"")} \u2014 ${j.length} steps, in order:`,E=j.map((W,Y)=>`${Y+1}. ${W.row.label}
|
|
196
|
+
`).trim().slice(0,300)||null}catch{return{recorded:!1,reason:"unknown-sha"}}let l=$T(t,i),u=await zF(r,n),d=await JF(r),p=await N(e,{type:"code.committed",actor:"agent",source:"hook",boxId:t.name,provider:t.agent??null,payload:{sha:n,ref:d,subject:c,...u,ts:i}}),f=null,w=!1;try{let m=await ET(r,n,{fixKey:Sa({trackId:e.id,boxId:t.name,epoch:l}),window:lo,context:{...VF(e,t,a,l),ref:d,provider:t.agent??null}});if(m&&(f=await N(e,{type:"code.rework",actor:"system",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:p.id,payload:m}),!(e?.workspace?.kind==="worktree"&&!!e?.workspace?.branch)&&IT(m))){let y=wc(m),b=m.origins.filter(k=>k.class==="rework").slice(0,3);await N(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:f.id,payload:{sha:n,subject:m.subject,linesReworked:y,origins:b.map(k=>({key:k.key,provider:k.provider,model:k.model,lines:k.lines,ageDays:k.ageDays})),heuristic:m.heuristic,intent:m.intent,ts:i}}),w=!0}}catch(m){o.warn?.(`[rework] ${e.id}: analysis of ${String(n).slice(0,7)} failed: ${m?.message||m}`)}return{recorded:!0,event:p,rework:f,offered:w}}function MT(e,t){let n=0;for(let r of e)r.payload?.sha===t&&(r.type==="rework.cause.answered"?n++:r.type==="rework.cause.retracted"&&n--);return n>0}async function CT(e,t,n){if(!hc.includes(n)&&n!==_d)throw new Error(`bad cause "${n}" (${hc.join("|")}|${_d})`);let r=await G(e),o=r.find(s=>s.type==="rework.cause.offered"&&s.payload?.sha===t);if(!o)throw new Error("unknown rework offer");if(MT(r,t))throw new Error("already answered");return N(e,{type:"rework.cause.answered",actor:"user",source:"orchestrator",boxId:o.boxId??null,causationId:o.id,payload:{sha:t,...Array.isArray(o.payload?.shas)?{shas:o.payload.shas}:{},cause:n,level:"B",ts:Date.now()}})}async function NT(e,t){let n=await G(e);if(!MT(n,t))throw new Error("no standing answer to retract");let r=[...n].reverse().find(o=>o.type==="rework.cause.answered"&&o.payload?.sha===t);return N(e,{type:"rework.cause.retracted",actor:"user",source:"orchestrator",boxId:r?.boxId??null,causationId:r?.id,payload:{sha:t,...Array.isArray(r?.payload?.shas)?{shas:r.payload.shas}:{},ts:Date.now()}})}async function Md(e,{repo:t=null,number:n,title:r="",branch:o=null,url:s=null}={},{log:a=console,now:i=Date.now()}={}){try{if(!e?.id||n==null)return{offered:!1,reason:"incomplete"};let c=await G(e);if(c.some(b=>b.type==="rework.cause.offered"&&b.payload?.pr?.number===n&&(t==null||b.payload.pr.repo===t)))return{offered:!1,reason:"duplicate"};let l=new Set;for(let b of c)if(b.type==="rework.cause.offered")for(let k of Array.isArray(b.payload?.shas)?b.payload.shas:[b.payload?.sha])k&&l.add(k);let u=new Map;for(let b of c)b.type==="code.committed"&&b.payload?.sha&&u.set(b.payload.sha,b.payload.ref??null);let d=sw({heuristic:Rd({subject:r}),subject:r,ref:o}),p=[];for(let b of c){if(b.type!=="code.rework")continue;let k=b.payload||{};if(!k.sha||l.has(k.sha)||k.codeDelta===!1||o&&u.has(k.sha)&&u.get(k.sha)!==o)continue;let S=k.intent?.intent??"unknown";(Aa.has(S)||S==="unknown"&&Aa.has(d.intent))&&wc(k)!==0&&p.push(k)}let f=p.reduce((b,k)=>b+wc(k),0);if(f<ow)return{offered:!1,reason:"below-threshold"};let w=new Map;for(let b of p)for(let k of b.origins??[]){if(k.class!=="rework")continue;let S=w.get(k.key)??{key:k.key,provider:k.provider??null,model:k.model??null,lines:0,ageDays:k.ageDays};S.lines+=k.lines,S.ageDays=Math.min(S.ageDays,k.ageDays),w.set(k.key,S)}let m=p.map(b=>b.sha),g=p.map(b=>b.heuristic).find(b=>b&&b.level!=="C")??Rd({subject:r})??p.map(b=>b.heuristic).find(Boolean)??null,y=Aa.has(d.intent)?d:p.find(b=>Aa.has(b.intent?.intent))?.intent??d;return await N(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:null,payload:{sha:m[m.length-1],shas:m,pr:{repo:t,number:n,title:String(r||"").slice(0,200),url:s,branch:o},subject:String(r||"").slice(0,200)||p[p.length-1].subject,linesReworked:f,origins:[...w.values()].sort((b,k)=>k.lines-b.lines).slice(0,3),heuristic:g,intent:y,ts:i}}),{offered:!0,shas:m}}catch(c){return a.warn?.(`[rework] ${e?.id}: PR-level offer for #${n} failed: ${c?.message||c}`),{offered:!1,reason:"error"}}}var qF,aw,Cd=q(()=>{ye();_u();Od();qF=WF(HF),aw=(e,t)=>qF("git",["-C",e,...t],{maxBuffer:16*1024*1024,timeout:3e4})});import{execFile as YF}from"node:child_process";import{promisify as XF}from"node:util";async function iw(e,t,{midTurn:n=is,pollMs:r=ZF,capMs:o=QF}={}){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 FT(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){PT.lastIndex=n;let o=PT.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(`
|
|
197
|
+
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+aU(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 cw(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function HT(e){let t=FT(e),n=t.match(rU);if(!n)return null;let r=cw(t,n.index);return sU.test(r)?null:{repo:r.match(UT)?.[1]??null,head:r.match(iU)?.[1]??null}}function WT(e){let t=FT(e),n=t.match(oU);if(!n)return null;let r=cw(t,n.index),o=cw(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("-")){lU.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(UT)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function dU(e,t,{exclude:n=()=>!1}={}){let r=async i=>{pr("observer");let{stdout:c}=await DT("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",uU],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw ao(l)&&io(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-eU&&!n(c)),s=await _n({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 pU(e,t){if(t)return[t];let n=[];for(let r of qT(e)){let o=await Oe(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await nr().catch(()=>[])){let o=await Oe(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function qT(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 fU(e,t,n){let r=await va(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)||cU(c.headRefName,e.id),i=[];for(let c of await pU(e,t?.repo??null)){let l;try{l=await dU(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await pt(c,u.number);if(s(u.headRefName)){if(d&&d.trackId===e.id&&d.announced)return{settled:!0};if(d&&d.trackId!==e.id)continue;return{slug:c,pr:u}}d&&(d.trackId!==e.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 GT(e){if(!e)return 0;let t=0;for(let n of mr)n.startsWith(`${e}|`)&&t++;return t}function KT(){for(let e of[...lw])e()}function VT(e,t,{delays:n=BT,turnEnd:r}={}){let o=HT(t.command);if(!o)return null;let s=!!WT(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(mr.has(a))return null;mr.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(m=>{let g=()=>{clearTimeout(y),lw.delete(g),m()},y=setTimeout(g,l);lw.add(g)});let u=await fU(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:d,pr:p}=u,{link:f,created:w}=await Ut({repo:d,number:p.number,trackId:e.id,url:p.url,headRef:p.headRefName,title:p.title,source:"observed"});return!w&&!(f?.trackId===e.id&&await ur(d,p.number))?null:(await iw(t.box,t.agent,r),await N({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:p.number,url:p.url,title:p.title,branch:p.headRefName,repo:d,...s?{selfMerge:!0}:{}}}),p)}return null}finally{mr.delete(a)}})();return c.catch(()=>mr.delete(a)),c}function zT(e,t,{delays:n=BT,turnEnd:r,openedWait:o}={}){let s=WT(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(mr.has(a))return null;mr.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 w=qT(e);if(w.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let m of w){let g=await Oe(m).catch(()=>null);g&&!d.includes(g)&&d.push(g)}}if(l||=(await va(e).catch(()=>[])).filter(w=>w!==e.workspace?.branch)[0]||e.workspace?.branch,!d.length||!l)return null;c=d[0];let p=async(w,m)=>{let g=await pt(c,w.number);return g&&g.trackId!==e.id?null:(g||await Ut({repo:c,number:w.number,trackId:e.id,url:w.url,headRef:w.headRefName,title:w.title,source:"merge-observed"}),await ac(c,w.number),m&&((await G(e).catch(()=>[])).some(k=>k.type==="pr.state.changed"&&(k.payload?.change==="auto-armed"||k.payload?.change==="merged")&&k.payload?.number===w.number)||(await iw(t.box,t.agent,r),await N({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:w.number,url:w.url,title:w.title,branch:w.headRefName,repo:c}}))),w)},f=null;for(let w of n){await new Promise(M=>setTimeout(M,w));let m=[];for(let M of d)try{pr("observer");let{stdout:L}=await DT("gh",["pr","view",String(l),"--repo",M,"--json",mU],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await _n({slug:M})}}),B=JSON.parse(L||"null");B?.number&&m.push({slug:M,pr:B})}catch(L){ao(L)&&io(L)}let g=m.find(M=>M.pr.state==="MERGED")??(m.length===1?m[0]:null);if(!g)continue;c=g.slug;let y=g.pr;if(!y?.number)continue;if(y.state==="CLOSED")return null;if(y.state!=="MERGED"){if(f=y,s.auto)return p(y,!0);continue}let b=await pt(c,y.number);if(b&&b.trackId!==e.id)return null;b||await Ut({repo:c,number:y.number,trackId:e.id,url:y.url,headRef:y.headRefName,title:y.title,source:"merge-observed"}),await iw(t.box,t.agent,r);let k=await G(e).catch(()=>[]);if(k.some(M=>M.type==="pr.state.changed"&&M.payload?.change==="merged"&&M.payload?.number===y.number))return null;let S=M=>M.some(L=>L.type==="pr.state.changed"&&L.payload?.change==="opened"&&L.payload?.number===y.number),$=0;if(!S(k)){if(HT(t.command)&&await ur(c,y.number)){let M=Date.now();$=M+1,await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:M,change:"opened",number:y.number,url:y.url,title:y.title,branch:y.headRefName,repo:c,selfMerge:!0}})}else if((await pt(c,y.number))?.announced){let M=o?.capMs??tU,L=o?.pollMs??nU,B=Date.now()+M;for(;Date.now()<B&&(await new Promise(A=>setTimeout(A,L)),!S(await G(e).catch(()=>[]))););}}return await N({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(),$),change:"merged",number:y.number,url:y.url,title:y.title,branch:y.headRefName,repo:c}}),await Md(e,{repo:c,number:y.number,title:y.title,branch:y.headRefName,url:y.url}).catch(()=>{}),await wr(c,y.number).catch(()=>{}),await hr(c,y.number).catch(()=>{}),await oo(c,y.number,"merged"),y}return f?p(f,!1):null}finally{mr.delete(a)}})();return i.catch(()=>mr.delete(a)),i}var DT,BT,ZF,QF,eU,tU,nU,jT,LT,rU,oU,sU,PT,aU,UT,iU,cU,lU,uU,mr,lw,mU,Nd=q(()=>{ye();Pr();yt();cc();jn();gr();mc();cs();Cd();DT=XF(YF),BT=[4e3,15e3,45e3],ZF=2500,QF=5*6e4;eU=15e3,tU=9e4,nU=2e3,jT=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,LT=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,rU=new RegExp(`${jT}${LT}create\\b`),oU=new RegExp(`${jT}${LT}merge\\b`),sU=/\s--web\b/,PT=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,aU=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");UT=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,iU=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,cU=gT;lU=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);uU="number,url,title,headRefName,createdAt";mr=new Set;lw=new Set;mU="number,url,title,headRefName,state"});import hU from"node:fs/promises";import dw from"node:path";import wU from"node:os";function uw(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function Pd(e){let t=gU.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=uw(n,"from"),s=o?o.replace(/^uds:/,""):null,a=JT(s);return{socket:s,pid:a,name:uw(n,"from-name"),mode:uw(n,"from-mode"),text:r.trim()}}function JT(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function yU(){return process.env.CLAUDE_CONFIG_DIR?dw.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):dw.join(wU.homedir(),".claude","sessions")}async function bU(e){let t=JT(e);if(!t)return null;try{let n=await hU.readFile(dw.join(yU(),`${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 YT(e,t){let n=await bU(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 gU,pw=q(()=>{gU=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function XT(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function ZT(e,t,n=fw){let r=new AbortController,o=setTimeout(()=>r.abort(),n);try{return await xU(`${Ne(t)}${e}`,{headers:vU(t),signal:r.signal})}finally{clearTimeout(o)}}async function gc({env:e=process.env,force:t=!1,timeoutMs:n=fw}={}){if(!XT(e))return{followups:[],source:"unconfigured"};if(!t&&ls&&Date.now()-ls.at<kU)return{followups:ls.followups,source:"cache"};try{let r=await ZT("/v1/followups?deleted=1",e,n);if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json(),s=Array.isArray(o?.followups)?o.followups:[];return ls={at:Date.now(),followups:s},{followups:s,source:"plane"}}catch(r){return ls?{followups:ls.followups,source:"stale",error:r?.message}:{followups:[],source:"unreachable",error:r?.message}}}async function QT(e,{env:t=process.env,timeoutMs:n=fw}={}){if(!e)return{followup:null,source:"unconfigured"};if(!XT(t))return{followup:null,source:"unconfigured"};try{let r=await ZT(`/v1/followups/${encodeURIComponent(e)}`,t,n);if(r.status===404)return{followup:null,source:"gone"};if(!r.ok)throw new Error(`plane ${r.status}`);return{followup:(await r.json())?.followup??null,source:"plane"}}catch(r){let o=ls?.followups.find(s=>s.id===e)??null;return o?{followup:o,source:"stale",error:r?.message}:{followup:null,source:"unreachable",error:r?.message}}}var kU,fw,ls,xU,vU,eE=q(()=>{yt();kU=6e4,fw=5e3,ls=null,xU=(...e)=>fetch(...e);vU=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});async function uo(e,t){if(mw.has(e)){let n=new Error("offer already answered (answer in flight)");throw n.code="answer-in-flight",n}mw.add(e);try{return await t()}finally{mw.delete(e)}}var mw,yc=q(()=>{mw=new Set});import{randomUUID as us,createHash as SU}from"node:crypto";function Dd(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function lE(e){for(let[t,n]of IU)if(t.test(String(e||"")))return`a standing prompt must not embed this Track's specifics (${n}) \u2014 the card supplies them at fire time; arm a one-shot with \`offer\` instead`;return null}function oE(e,{at:t="",promptRequired:n=!0,standing:r=!1}={}){let o=e&&typeof e=="object"?e:{},s=ds(o.label).replace(/\s+/g," ");if(!s)throw new Error(`${t}label required (\u2264${hw} chars, e.g. "Deploy to Pi")`);if(s.length>hw)throw new Error(`${t}label must be \u2264${hw} characters`);let a=ds(o.prompt);if(n&&!a)throw new Error(`${t}prompt required \u2014 the full instruction a fresh session could execute`);if(Buffer.byteLength(a,"utf8")>tE)throw new Error(`${t}prompt must be \u2264${tE} bytes`);if(r){let l=lE(a);if(l)throw new Error(`${t}${l}`)}let i=o.standing==null||o.standing===""?"offerable":String(o.standing).trim().toLowerCase();if(!EU.includes(i))throw new Error(`${t}standing must be "offerable" or "never", got "${o.standing}"`);let c=ds(o.why).slice(0,aE)||null;return{label:s,prompt:a,why:c,standing:i}}function uE(e,{standing:t=!1}={}){let n=e&&typeof e=="object"?e:{},r=ds(n.trigger)||(n.ship?"now":""),o=t?AU:iE.filter(p=>p!=="track.opened");if(!o.includes(r))throw new Error(`trigger must be one of ${o.join(" | ")}${t?" (a standing follow-up can't be `now` \u2014 it would fire every turn)":""}, got "${n.trigger??""}"`);let s=n.ship&&r==="now"?RU(n.ship):null,a=n.items!=null;if(a&&s)throw new Error("a ship offer has its own card \u2014 `items` cannot be combined with a `ship` block");let i,c=null;if(a){if(!Array.isArray(n.items)||!n.items.length)throw new Error("items must be a non-empty array of {label, prompt, why?, standing?}");if(n.items.length>rE)throw new Error(`items must be \u2264${rE} \u2014 one item per step the operator might run, save or skip separately`);if(t&&n.items.length>1)throw new Error("propose one standing follow-up at a time \u2014 a standing follow-up is one prompt");i=n.items.map((f,w)=>oE(f,{at:`items[${w}]: `,standing:t})),c=ds(n.why).slice(0,aE)||null;let p=new Set;for(let f of i){let w=Dd(f.label);if(p.has(w))throw new Error(`items must have distinct labels ("${f.label}" appears twice)`);p.add(w)}}else i=[oE(n,{promptRequired:!s,standing:t})];let l=i[0],u=c??l.why,d=t?$U(n.repo):null;return{label:l.label,prompt:l.prompt,trigger:r,why:u,planWhy:c,items:i,...d?{repo:d}:{},...s?{ship:s}:{}}}function _U(e){return e.standing==="never"?{promotable:!1,promotableReason:"one-off"}:lE(e.prompt)?{promotable:!1,promotableReason:"specifics"}:{promotable:!0,promotableReason:null}}function $U(e){if(e==null||e===""||e==="*")return null;let t=String(e).trim();if(!/^[\w.-]+\/[\w.-]+$/.test(t))throw new Error(`repo must be a forge slug like owner/name, got "${e}"`);return t}function RU(e){let t=e&&typeof e=="object"?e:{},n=Array.isArray(t.actions)?t.actions.map(i=>String(i).trim().toLowerCase()):ww.slice();for(let i of n)if(!ww.includes(i))throw new Error(`ship.actions may only contain ${ww.join(" | ")}, got "${i}"`);if(!n.length)throw new Error("ship.actions must name at least one of commit | push | pr");let r=t.rebase==null?"none":String(t.rebase).trim();if(!["offered","none"].includes(r))throw new Error(`ship.rebase must be "offered" or "none", got "${t.rebase}"`);let o=null;if(t.toPr!=null&&t.toPr!==""){let i=Number(t.toPr);if(!Number.isInteger(i)||i<=0)throw new Error(`ship.toPr must be a PR number, got "${t.toPr}"`);o=i}let s=ds(t.base)||null,a=null;if(t.repos!=null){if(!Array.isArray(t.repos))throw new Error("ship.repos must be an array of checkout names");a=t.repos.map(i=>ds(i)).filter(Boolean),a.length||(a=null)}return{actions:[...new Set(n)],rebase:r,toPr:o,base:s,repos:a}}function Bd(e,{trigger:t=null}={}){let n=new Map;for(let r of e){let o=r.payload||{};if(r.type==="followup.armed"&&o.armId)o.replaces&&n.delete(o.replaces),n.set(o.armId,{...o,ts:o.ts??r.ts,boxId:r.boxId??null,provider:r.provider??null});else if(r.type==="followup.armed.cancelled"&&o.armId)n.delete(o.armId);else if(r.type==="followup.offered")for(let s of o.rows||[])s.armId&&n.delete(s.armId)}return[...n.values()].filter(r=>!t||r.trigger===t)}function dE(e){return{armId:e.armId,label:e.label,prompt:e.prompt,promptHash:cE(e.prompt),origin:"agent",why:e.why??null,groupId:e.groupId??null,seq:Number.isInteger(e.seq)?e.seq:0,standing:e.standing==="never"?"never":"offerable",..._U(e)}}async function pE(e,t,{pull:n=gc,ship:r={}}={}){if(yw(e))throw new Error("follow-ups don't fire on a loop's run Track \u2014 report the outcome with `bostrat-loop report` instead");let o=uE(t);if(o.ship)return{kind:"ship",...await PU(e,o,r)};let s={id:e.id,cwd:e.cwd},a=(e.boxes||[]).find(b=>b.name===e.leadBox)||null,i={boxId:e.leadBox??null,provider:a?.agent??null},c=us(),l=(b,k,S={})=>({ts:Date.now(),armId:us(),trigger:o.trigger,label:b.label,prompt:b.prompt,why:b.why,standing:b.standing,groupId:c,seq:k,...o.planWhy?{planWhy:o.planWhy}:{},...S}),u={groupId:c,trigger:o.trigger,label:o.label,prompt:o.prompt,why:o.why,planWhy:o.planWhy};if(o.trigger==="now"){let b=us(),k=[];for(let[S,$]of o.items.entries()){let M=l($,S);await N(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:M}),k.push(M)}return await N(s,{type:"followup.offered",actor:"system",source:"orchestrator",...i,payload:{ts:Date.now(),offerId:b,trigger:"now",repo:e.workspace?.repo||null,context:{},groupId:c,why:o.planWhy,rows:k.map(dE)}}),{kind:"now",offerId:b,armId:k[0].armId,items:k.map(S=>({armId:S.armId,label:S.label,standing:S.standing})),...u}}let d=e.workspace?.repo||null,{followups:p}=await n().catch(()=>({followups:[]})),f=await G(s),w=Bd(f,{trigger:o.trigger}),m=[],g=0;for(let b of o.items){let k=Dd(b.label),S=p.find(B=>B.status==="active"&&B.mode!=="off"&&B.trigger===o.trigger&&(B.repo==null||B.repo===d)&&B.dedupeKey===k);if(S){m.push({label:b.label,standing:b.standing,covered:{id:S.id,label:S.label}});continue}let $=w.filter(B=>Dd(B.label)===k),M=$.length?$[$.length-1].armId:null,L=l(b,g++,M?{replaces:M}:{});await N(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:L}),m.push({armId:L.armId,label:b.label,standing:b.standing,replaces:M})}let y=m.filter(b=>b.armId);return y.length?{kind:"armed",armId:y[0].armId,replaces:y[0].replaces,items:m,...u}:{kind:"covered",covered:m[0].covered,items:m,...u}}async function fE(e,t){if(!t)throw new Error("armId required");let n={id:e.id,cwd:e.cwd},r=await G(n);if(!Bd(r).some(o=>o.armId===t))throw new Error("unknown or already-settled arm");return await N(n,{type:"followup.armed.cancelled",actor:"user",source:"orchestrator",payload:{ts:Date.now(),armId:t}}),{armId:t}}async function mE(e,t,{pull:n=gc}={}){if(yw(e))throw new Error("propose from a lead Track, not a loop's run Track");let r=uE(t,{standing:!0}),o=r.repo??e.workspace?.repo??null,s={id:e.id,cwd:e.cwd};if((await G(s)).reduce((g,y)=>y.type==="followup.create.offered"?y.payload?.proposalId??g:y.type==="followup.create.answered"&&y.payload?.proposalId===g?null:g,null))throw new Error("a follow-up proposal is already waiting on the operator in this Track \u2014 one at a time");let{followups:c,source:l}=await n().catch(()=>({followups:[],source:"unreachable"}));if(l==="unreachable")throw new Error("plane unreachable \u2014 standing follow-ups live in the plane; try again shortly");let u=Dd(r.label),d=c.filter(g=>g.dedupeKey===u&&g.trigger===r.trigger),p=d.find(g=>g.status==="deleted"),f=d.find(g=>g.status!=="deleted");if(f)throw new Error(`"${f.label}" (${gw[f.trigger]||f.trigger}) already exists as a standing follow-up \u2014 not re-proposed`);if(p){let g=p.deletedAt?new Date(p.deletedAt).toISOString().slice(0,10):"earlier";throw new Error(`the operator deleted "${p.label}" on ${g} \u2014 do not re-propose it`)}let w=us(),m=(e.boxes||[]).find(g=>g.name===e.leadBox)||null;return await N(s,{type:"followup.create.offered",actor:"agent",source:"cli",boxId:e.leadBox??null,provider:m?.agent??null,payload:{ts:Date.now(),proposalId:w,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why,origin:"agent"}}),{proposalId:w,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why}}async function hE(e,t,n,{followupId:r=null}={}){if(!t)throw new Error("proposalId required");let o={id:e.id,cwd:e.cwd},s=await G(o),a=s.find(c=>c.type==="followup.create.offered"&&c.payload?.proposalId===t);if(!a)throw new Error("unknown proposal");if(s.some(c=>c.type==="followup.create.answered"&&c.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await N(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};if(!r)throw new Error("followupId required \u2014 the follow-up is created on the plane before the card is answered");let i=a.payload;return await N(o,{type:"followup.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,followupId:String(r),label:i.label,trigger:i.trigger,repo:i.repo??null}}),await N(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",followupId:String(r)}}),{proposalId:t,action:"created",followupId:String(r)}}async function bc(e,t,n={},{pull:r=gc}={}){if(!e||yw(e))return null;let o={id:e.id,cwd:e.cwd},s=await G(o).catch(()=>[]),a=n.repo??e.workspace?.repo??null;if(t==="pr.merged"){let y=S=>{let $=(S?.prs||[]).filter(M=>Number.isFinite(Number(M?.number))).map(M=>`${M?.repo??S?.repo??""}#${Number(M.number)}`);return Number.isFinite(Number(S?.prNumber))&&$.push(`${S?.repo??""}#${Number(S.prNumber)}`),$},b=new Set(y(n));if(s.some(S=>S.type==="followup.offered"&&S.payload?.trigger==="pr.merged"&&y(S.payload?.context).some($=>b.has($))))return null}let i=Bd(s,{trigger:t}),c=i.length?i[i.length-1].groupId??null:null,l=new Map(i.map((y,b)=>[y.armId,b])),u=y=>y.groupId&&y.groupId===c?0:1,d=i.slice().sort((y,b)=>u(y)-u(b)||l.get(y.armId)-l.get(b.armId)).slice(0,nE).map(dE),p=c?i.find(y=>y.groupId===c&&y.planWhy)?.planWhy??null:null,f=[];if(t!=="now"){let{followups:y}=await r().catch(()=>({followups:[]}));f=y.filter(b=>b.status==="active"&&b.mode!=="off"&&b.trigger===t&&(b.repo==null||b.repo===a)).sort((b,k)=>{let S=$=>$.stats?.offered?($.stats.accepted||0)/$.stats.offered:0;return S(k)-S(b)||(b.origin==="operator"?-1:0)-(k.origin==="operator"?-1:0)||(k.createdAt||0)-(b.createdAt||0)}).slice(0,TU).map(b=>({followupId:b.id,label:b.label,promptPreview:OU(b.prompt),promptHash:cE(b.prompt),origin:b.origin,repo:b.repo??null}))}let w=[...d,...f].slice(0,nE);if(!w.length)return null;let m=us(),g={ts:Date.now(),offerId:m,trigger:t,repo:a,context:{...n,repo:a},groupId:c,why:p,rows:w};return await N(o,{type:"followup.offered",actor:"system",source:"orchestrator",payload:g}),g}function MU(e,t){let n=t?.rows||[],r=a=>a.armId?`arm:${a.armId}`:`fu:${a.followupId}`,o=new Map(n.map(a=>[r(a),null])),s=(a,i)=>{o.has(a)&&o.get(a)==null&&o.set(a,i)};for(let a of e){if(a.type!=="followup.answered"||a.payload?.offerId!==t.offerId)continue;let i=a.payload,c=[];i.armId&&c.push(`arm:${i.armId}`),i.followupId&&c.push(`fu:${i.followupId}`);for(let l of Array.isArray(i.armIds)?i.armIds:[])c.push(`arm:${l}`);for(let l of Array.isArray(i.followupIds)?i.followupIds:[])c.push(`fu:${l}`);if(c.length)for(let l of c)s(l,i.action);else for(let l of o.keys())s(l,i.action)}return o}function sE(e){let t=e?.context||{},n=Array.isArray(t.prs)&&t.prs.length?t.prs:t.prNumber?[{number:t.prNumber,title:t.prTitle,url:t.prUrl,repo:t.repo}]:[],r=o=>`PR #${o.number}${o.title?` "${o.title}"`:""}${o.url?` (${o.url})`:""}${o.repo?` on ${o.repo}`:""}`;switch(e?.trigger){case"pr.merged":return n.length?`Follow-up the operator tapped after ${n.map(r).join(" and ")} merged:`:"Follow-up the operator tapped after this Track's PR merged:";case"pr.opened":return n.length?`Follow-up the operator tapped after ${n.map(r).join(" and ")} opened:`:"Follow-up the operator tapped after this Track's PR opened:";case"checks.green":return n.length?`Follow-up the operator tapped after CI went green on ${n.map(r).join(" and ")}:`:"Follow-up the operator tapped after CI went green:";case"ship.done":return"Follow-up the operator tapped after the ship you just ran completed:";default:return"Follow-up the operator tapped:"}}async function wE(e,{offerId:t,followupId:n=null,armId:r=null,promoted:o=null,all:s=!1}={},a={}){if(!t)throw new Error("offerId required");return uo(`fu:${e.id}:${t}`,()=>NU(e,{offerId:t,followupId:n,armId:r,promoted:o,all:s===!0},a))}async function NU(e,{offerId:t,followupId:n,armId:r,promoted:o,all:s},a){let{sayToLead:i,unarchiveTrack:c,getFollowup:l=QT}=a,u={id:e.id,cwd:e.cwd},d=await G(u),p=d.find(A=>A.type==="followup.offered"&&A.payload?.offerId===t);if(!p)throw new Error("unknown offer");let f=p.payload.rows||[],w=MU(d,p.payload),m=CU(w,f);if(!m.length)throw new Error("offer already answered");let g=A=>N(u,{type:"followup.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,...A}}),y=async()=>{if(!e.archivedAt)return!1;if(!c)throw new Error("this Track is archived");return await c(e.id,{freshSession:!0}),e={...e,archivedAt:null},!0},b=A=>{let j=d.find(K=>K.type==="followup.armed"&&K.payload?.armId===A.armId)?.payload?.prompt??A.prompt;if(!j)throw new Error("the arm's prompt is missing from the log");return j};if(s){let A=p.payload.groupId??null,I=m.filter(W=>W.armId&&(!A||W.groupId===A)).sort((W,Y)=>(W.seq??0)-(Y.seq??0));if(!I.length)throw new Error("nothing to run together \u2014 no open items from this plan remain on the card");let j=I.map(W=>({row:W,prompt:b(W)})),K=await y();if(!i)throw new Error("sayToLead unavailable");let R=`${sE(p.payload).replace(/:$/,"")} \u2014 ${j.length} steps, in order:`,T=j.map((W,Y)=>`${Y+1}. ${W.row.label}
|
|
198
198
|
${W.prompt}`).join(`
|
|
199
199
|
|
|
200
200
|
`),C=await i(e.id,`${R}
|
|
201
201
|
|
|
202
|
-
${
|
|
202
|
+
${T}`,{kind:"followup-go"}),P=us();for(let[W,Y]of j.entries())await g({action:"sent",armId:Y.row.armId,label:Y.row.label,batchId:P,...W===0&&K?{revived:!0}:{},source:`arm:${Y.row.armId}`});return{offerId:t,action:"sent",all:!0,batchId:P,armIds:j.map(W=>W.row.armId),revived:K,remaining:m.length-j.length,queued:!!C?.queued}}if(!n&&!r)return await g({action:"dismiss",followupIds:m.map(A=>A.followupId).filter(Boolean),armIds:m.map(A=>A.armId).filter(Boolean)}),{offerId:t,action:"dismiss",remaining:0};let k=null,S=null,$=n?`fu:${n}`:`arm:${r}`;if(n){if(!f.find(K=>K.followupId===n))throw new Error("that follow-up is not on this card");if(w.get($)!=null)throw new Error("that row is already answered");let{followup:I,source:j}=await l(n);if(j==="unreachable")throw new Error("plane unreachable \u2014 the follow-up's current prompt can't be read; try again shortly");if(!I||I.status!=="active"){await g({action:"removed",followupId:n});let K=new Error("removed in Settings since it was offered");throw K.code="removed",K}k=I.prompt,S=I.label}else{let A=f.find(I=>I.armId===r);if(!A)throw new Error("that arm is not on this card");if(w.get($)!=null)throw new Error("that row is already answered");k=b(A),S=A.label}let M=await y();if(!i)throw new Error("sayToLead unavailable");let L=`${sE(p.payload)}
|
|
203
203
|
|
|
204
|
-
${k}`,B=await i(e.id,L,{kind:"followup-go"});return await g({action:"sent",...n?{followupId:n}:{armId:r},label:S,...o?{promoted:String(o)}:{},...M?{revived:!0}:{},source:n?`followup:${n}`:`arm:${r}`}),{offerId:t,action:"sent",followupId:n,armId:r,promoted:o,revived:M,remaining:m.length-1,queued:!!B?.queued}}async function
|
|
204
|
+
${k}`,B=await i(e.id,L,{kind:"followup-go"});return await g({action:"sent",...n?{followupId:n}:{armId:r},label:S,...o?{promoted:String(o)}:{},...M?{revived:!0}:{},source:n?`followup:${n}`:`arm:${r}`}),{offerId:t,action:"sent",followupId:n,armId:r,promoted:o,revived:M,remaining:m.length-1,queued:!!B?.queued}}async function PU(e,t,{changedRepos:n=null,probe:r=vd,branchOf:o=co}={}){let s=t.ship;if(!e.workspace?.branch)throw new Error("this Track has no branch to ship \u2014 a ship offer needs a worktree Track");let a={id:e.id,cwd:e.cwd},i=(e.boxes||[]).find(g=>g.name===e.leadBox)||null,c=e.workspace?.kind==="multi",l;if(c&&n){let g=await n(e).catch(()=>[]),y=s.repos?g.filter(b=>s.repos.includes(b.name)):g;l=y.length?y.map(b=>({name:b.name,slug:b.repo??null})):void 0}let u=(c?null:await o(e.cwd).catch(()=>null))||e.workspace.branch,d=s.rebase==="offered",p=await r(e,{branch:u,rebase:d}).catch(()=>null),f=[...d?["rebase"]:[],...s.actions].filter(g=>ro.includes(g));if(p&&p.available.length===0)return{skipped:!0,reason:"nothing to ship \u2014 the tree is clean, pushed, and its PR is open",shipState:p};let w=us(),m=t.why?`${t.label} \u2014 ${t.why}`:t.label;return await N(a,{type:"pr.offered",actor:"agent",source:"cli",boxId:e.leadBox??null,provider:i?.agent??null,payload:{offerId:w,ts:Date.now(),excerpt:m,via:"cli",branch:u,base:s.base||e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:"lead",available:p?.available,defaults:p?p.defaults.filter(g=>f.includes(g)||g==="rebase"):void 0,prState:p?.prState,openPr:p?.openPr||void 0,repos:p?.repos||l,offered:p?f.filter(g=>p.available.includes(g)):f,toPr:s.toPr?{number:s.toPr}:void 0}}),{offerId:w,branch:u,offered:f,available:p?.available??null,prState:p?.prState??null}}function gE(e){let t=!1;for(let n of e)n.type==="pr.offered"&&(n.source==="cli"||n.payload?.via==="cli")?t=!0:(n.type==="turn.user"&&!n.payload?.kind||n.type==="pr.offer.answered")&&(t=!1);return t}function yE(e){let t=null;for(let n of e){let r=n.payload||{};n.type==="pr.offer.answered"?t=r.action&&r.action!=="dismiss"&&r.action!=="pr-exists"?r.offerId:null:n.type==="pr.exists.answered"?t=r.choice&&r.choice!=="dismiss"?r.offerId??t:null:(n.type==="turn.user"&&!r.kind||n.type==="followup.offered"&&r.trigger==="ship.done"&&r.context?.shipOfferId&&r.context.shipOfferId===t)&&(t=null)}return t}async function bE(e,{pull:t=gc}={}){let{followups:n,source:r,error:o}=await t().catch(a=>({followups:[],source:"unreachable",error:a?.message})),s=e?await G({id:e.id,cwd:e.cwd}).catch(()=>[]):[];return{followups:n,source:r,error:o,arms:e?Bd(s):[]}}var hw,tE,aE,iE,AU,gw,TU,nE,rE,EU,ww,cE,ds,IU,yw,OU,CU,jd=q(()=>{ye();ts();Xh();cs();eE();yc();hw=60,tE=8*1024,aE=200,iE=["now","pr.merged","ship.done","pr.opened","checks.green","track.opened"],AU=iE.filter(e=>e!=="now"&&e!=="track.opened"),gw={now:"now","pr.merged":"after merge","ship.done":"after ship","pr.opened":"after PR opens","checks.green":"after CI goes green","track.opened":"when a Track opens"},TU=3,nE=6,rE=5,EU=["offerable","never"],ww=["commit","push","pr"];cE=e=>SU("sha256").update(String(e)).digest("hex").slice(0,16),ds=e=>typeof e=="string"?e.trim():"",IU=[[/(^|[^\w&])#\d+\b/,"a PR/issue number"],[/https?:\/\/\S+/i,"a URL"],[/\b[0-9a-f]{7,40}\b/i,"a commit SHA"],[/\bv\d+\.\d+\.\d+\b/,"a release tag"]];yw=e=>String(e?.name||"").startsWith("\u21BB");OU=e=>String(e||"").split(`
|
|
205
205
|
`).slice(0,3).join(`
|
|
206
|
-
`).slice(0,400);
|
|
207
|
-
`)){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 PU(e,t){let n=`${e.id}:${t.name}`;if(!xw.has(n)){let r=await G(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);xw.set(n,o)}return xw.get(n)}async function AE(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await Fo(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await PU(e,t),o=NU(await Nt(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||t.model||"gpt-5.5";await N(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:Pn(c,i,a.ts),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await N(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 xw,TE=q(()=>{ye();Ho();pn();ir();xw=new Map});import Ld from"node:fs";import Sw from"node:path";import DU from"node:os";async function LU({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(jU,{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:EE})});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<EE)break}return r}function FU(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 _E(){try{return JSON.parse(Ld.readFileSync(Fd(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function HU(e){try{Ld.mkdirSync(Sw.dirname(Fd()),{recursive:!0});let t=Fd()+".tmp";Ld.writeFileSync(t,JSON.stringify(e)),Ld.renameSync(t,Fd())}catch{}}async function qU({fetchImpl:e,now:t=Date.now()}={}){if(po.rows&&t-po.at<UU)return po.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=_E(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await LU({sinceMs:o,cookie:n,fetchImpl:e});po={at:t,rows:s.map(FU).filter(Boolean),error:null}}catch(s){po={at:t,rows:null,error:String(s?.message||s)}}return po.rows}async function GU(e,t){let n=`${e.id}:${t.name}`;if(!vw.has(n)){let r=await G(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);vw.set(n,o)}return vw.get(n)}async function $E(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!IE())return;let r=await qU({fetchImpl:n});if(!r||!r.length)return;let o=await cS(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await GU(e,t),a=_E(),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",d=l.tokens||null,p=l.vendorCents!=null?l.vendorCents/100:0;!p&&d&&(p=Pn(u,d,l.ts??void 0)),!p&&l.chargedCents!=null&&(p=l.chargedCents/100),await N(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.name,provider:"cursor",payload:{usd:p,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}})}HU({sinceMs:c,seen:[...i].slice(-WU)})}catch{}}var BU,IE,jU,EE,UU,po,Fd,WU,Aw,vw,Tw=q(()=>{ye();oh();ir();BU=process.env.BOSTRAT_STATE_DIR||Sw.join(process.env.HOME||DU.tmpdir(),".bostrat"),IE=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,jU="https://cursor.com/api/dashboard/get-filtered-usage-events",EE=100;UU=5*6e4,po={at:0,rows:null,error:null},Fd=()=>Sw.join(BU,"cursor-usage.json");WU=5e3;Aw=()=>({enabled:IE(),lastPollAt:po.at||null,error:po.error}),vw=new Map});import{randomUUID as KU,createHmac as VU}from"node:crypto";function OE(e){Iw=typeof e=="string"&&e?e:null}function zU(e){if(!Iw)return null;let t=Ue(e);return t?VU("sha256",Buffer.from(Iw,"base64url")).update(t,"utf8").digest("hex").slice(0,16):null}function NE(){let e={};for(let t of CE)e[String(t)]=0;return e.inf=0,{buckets:e,sumMs:0,count:0}}function RE(e,t){let n=Math.max(0,Number(t)||0);for(let r of CE)n<=r&&(e.buckets[String(r)]+=1);e.buckets.inf+=1,e.sumMs+=n,e.count+=1}function PE(){let e={};for(let t of ME)e[t]=0;return e}function YU(){Un={total:new Map,holds:PE(),queueWaitMs:NE(),readyWaitMs:new Map,verify:new Map,retries:new Map}}function DE({queueDepth:e=0,queueOldestAgeMs:t=0}={}){let n=Un;return{total:[...n.total].map(([r,o])=>{let[s,a,i]=r.split("|");return{provider:s,producer:a,outcome:i,n:o}}),holds:{...n.holds},queueWaitMs:{buckets:{...n.queueWaitMs.buckets},sumMs:n.queueWaitMs.sumMs,count:n.queueWaitMs.count},readyWaitMs:[...n.readyWaitMs].map(([r,o])=>{let[s,a]=r.split("|");return{provider:s,outcome:a,buckets:{...o.buckets},sumMs:o.sumMs,count:o.count}}),verify:[...n.verify].map(([r,o])=>{let[s,a,i]=r.split("|");return{provider:s,verdict:a,cliVersion:i||null,n:o}}),retries:[...n.retries].map(([r,o])=>({provider:r,n:o})),queueDepth:Number(e)||0,queueOldestAgeMs:Number(t)||0}}function xc({kind:e=null,producer:t=null,orchestration:n=!1}={}){return n?`orchestration:${e||"orchestration"}`:e?`scripted:${e}`:JU.has(t)?t:"composer"}function vc({text:e="",producer:t="composer",kind:n=null,provider:r=null,attachments:o=0,now:s=Date.now}={}){let a=s();return{id:KU(),producer:t,kind:n||null,provider:r||null,textBytes:Buffer.byteLength(String(e||""),"utf8"),textHmac16:zU(e),attachments:Number(o)||0,paste:null,t0:a,queuedAt:null,dequeuedAt:null,holds:PE(),requeued:0,readyWaitMs:null,readyOutcome:null,verify:null,settled:!1,outcome:null}}async function Sc(e,t,{position:n=null,reason:r="busy"}={}){!t||!e||(t.queuedAt=Date.now(),await N(e,{type:"delivery.queued",actor:"system",source:"orchestrator",boxId:e.leadBox??null,provider:t.provider,payload:{ts:t.queuedAt,deliveryId:t.id,producer:t.producer,...t.kind?{kind:t.kind}:{},reason:r,position:n,textBytes:t.textBytes,textHmac16:t.textHmac16,requeued:t.requeued}}).catch(()=>{}))}function BE(e,t){ME.includes(t)&&(e?.holds&&(e.holds[t]+=1),Un.holds[t]+=1)}function Ac(e,{ms:t,outcome:n}){e&&(e.readyWaitMs=Math.max(0,Number(t)||0),e.readyOutcome=n||null)}function XU(e){let t=e?.boxes?.find?.(n=>n.name===e.leadBox);return typeof t?.cliVersion=="string"&&t.cliVersion?t.cliVersion:null}async function gn(e,t,n,{now:r=Date.now}={}){if(!t||t.settled)return null;t.settled=!0,t.outcome=n;let o=r(),s=t.provider||"unknown",a=t.queuedAt!=null,i=a?Math.max(0,(t.dequeuedAt??o)-t.queuedAt):null;if(Ew(Un.total,`${s}|${t.producer}|${n}`),a&&RE(Un.queueWaitMs,i),t.readyWaitMs!=null){let u=`${s}|${t.readyOutcome||"unknown"}`;Un.readyWaitMs.has(u)||Un.readyWaitMs.set(u,NE()),RE(Un.readyWaitMs.get(u),t.readyWaitMs)}let c=XU(e);t.verify?.verdict&&(Ew(Un.verify,`${s}|${t.verify.verdict}|${c||""}`),t.verify.retries&&Ew(Un.retries,s,t.verify.retries));let l={ts:o,deliveryId:t.id,producer:t.producer,...t.kind?{kind:t.kind}:{},cliVersion:c,textBytes:t.textBytes,textHmac16:t.textHmac16,paste:t.paste,attachments:t.attachments,outcome:n,queued:a,queueWaitMs:i,queueHolds:{...t.holds},requeued:t.requeued,readyWaitMs:t.readyWaitMs,readyOutcome:t.readyOutcome,verify:t.verify?{...t.verify}:null,receiptMs:null,totalMs:Math.max(0,o-t.t0)};return e&&await N(e,{type:"delivery.settled",actor:"system",source:"orchestrator",boxId:e.leadBox??null,provider:t.provider,payload:l}).catch(()=>{}),l}function Ta(e){if(e?.usageCap)return"refused:cap";if(e?.interrupted)return"interrupted";if(e?.code)return`failed:${e.code}`;let t=String(e?.message||"");return/not running/i.test(t)?"failed:not-running":/archived/i.test(t)?"failed:archived":/blocked on an interactive prompt|isn't signed in|is waiting on its|stuck on its update/i.test(t)?"failed:blocked":"failed:error"}var Iw,ME,CE,JU,Un,Ew,_w=q(()=>{ye();Gi();Iw=null;ME=["busy","prompt","handoff","noSession","cap","grace"],CE=[500,2e3,1e4,6e4,6e5],JU=new Set(["composer","voice","loop"]);Un=null;YU();Ew=(e,t,n=1)=>e.set(t,(e.get(t)||0)+n)});import Ic from"node:fs/promises";import Pt from"node:path";import ZU from"node:os";import QU from"node:net";import{randomUUID as e4}from"node:crypto";import{execFile as t4}from"node:child_process";import{promisify as n4}from"node:util";function l4(e){for(let t of Pw(e))if(!WE.test(t))return t;return null}function qE(e){let t=Pw(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function u4(e,t){let n=qE(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Pt.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return Pt.resolve(e,n[r+1])}return e}function p4(e){let t=!1,n=!1;for(let r of Pw(e)){if(!t){if(WE.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&$w(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,$w(r))return`serves the filesystem root ("${r}")`;continue}if($w(r))return`serves the filesystem root ("${r}")`;if(d4(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function m4(e,t){if(Pt.resolve(e)===Pt.resolve(t))return!0;let[n,r]=await Promise.all([Ud(e),Ud(t)]);return n===r}async function h4(e){let n=(await Ic.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 GE(Pt.join(e,o.name,Mw))?`./${o.name}`:null))).filter(Boolean)}async function w4(e,t){let[n,r]=await Promise.all([Ud(e),Ud(t)]),o=Pt.relative(n,r);if(o&&(o.startsWith("..")||Pt.isAbsolute(o)))throw Tc(`serve directory escapes the Track workspace: ${t}`)}async function g4(e,t){let n=p4(t);if(n)throw Tc(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await f4(e))throw Tc(`serve directory does not exist: ${e}`);let r=l4(t);if(!r)return;if(!r.includes("/")&&!await I0(r,{loginShell:!0,failOpen:!0}))throw Tc(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(a4.test(t)||i4.has(r)||!s4.has(r)||!qE(t).includes("run")||c4.test(t))return;let o=u4(e,t);if(await GE(Pt.join(o,Mw)))return;let s=await h4(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Tc(`no ${Mw} in ${o}${a}`)}async function kr(){return Hn||(Rw||=(async()=>{try{Hn=JSON.parse(await Ic.readFile(HE,"utf8"))}catch{Hn={views:[],adoptWatermarks:{}}}finally{Rw=null}return Hn.views||=[],Hn.adoptWatermarks||={},Hn})(),Rw)}async function qn(){await Mt(HE,Hn)}function br(e,t,n={}){return N({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 Bw(e){return Ec.get(e)||null}function VE(e,{name:t,box:n}={}){let r=Ec.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 Ec.set(e,o),o}function Cw(e,t){let n=Ec.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function Hd(e,{error:t=null}={}){let n=Ec.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function zE(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 Nw(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function y4(e,t){let n=Nw(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!FE.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!FE.has(s))return s}}function _c(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=QU.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 jw(e){if(process.platform!=="darwin"){await Wn("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await Wn("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 JE(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function b4(e,t){let n=new Set(e.views.map(o=>o.port)),r=JE();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=jE;o<=LE;o++)if(!(n.has(o)||r.has(o))&&!await _c(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${jE}-${LE}`)}async function k4(e){try{let{stdout:t}=await Wn(te,oe("list-sessions","-F","#{session_name}"));return t.split(`
|
|
208
|
-
`).some(n=>n.trim()===Ea(e))}catch{return!1}}async function
|
|
206
|
+
`).slice(0,400);CU=(e,t)=>t.filter(n=>e.get(n.armId?`arm:${n.armId}`:`fu:${n.followupId}`)==null)});import{randomUUID as DU}from"node:crypto";function kc(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function kE(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function xE(e){return`job|${e.box}|id:${e.id}`}async function kw(e){return bw.has(e.id)?bw.get(e.id):(Ld.has(e.id)||Ld.set(e.id,G(e).then(t=>{let n={keys:new Set,cmdN:new Map};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(kc({role:o,sourceId:r.payload.sourceId})),n.keys.add(kc({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(kc({role:"user",sourceId:r.payload.sourceId})),n.keys.add(kc({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(kE({id:o.id,box:r.boxId,ts:o.ts,command:o.command})),Number(o.n)>(n.cmdN.get(r.boxId)??0)&&n.cmdN.set(r.boxId,Number(o.n))}else r.type==="job.started"&&r.payload?.id&&n.keys.add(xE({id:r.payload.id,box:r.boxId}));return bw.set(e.id,n),n}).finally(()=>Ld.delete(e.id))),Ld.get(e.id))}async function vE(e,t){let n=new Set;try{let r=(await kw(e)).keys;for(let o of t||[]){if(!o||o.ts==null||!String(o.text||"").trim()&&!o.images?.length)continue;let s=kc(o);if(r.has(s))continue;r.add(s);let a=o.role==="user"?Pd(o.text):null;if(a){await N(e,{type:"peer.message",actor:"peer",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{text:a.text,ts:o.ts,keyText:String(o.text||"").slice(0,40),from:await YT(a,xc),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=rx(o);if(i){await N(e,{type:"provider.error",actor:"system",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{cls:i.cls,code:i.code||void 0,status:i.status||void 0,requestId:i.requestId||void 0,text:o.text,ts:o.ts,model:o.model||null,boxRole:o.boxRole,sourceId:o.sourceId||void 0}}),ec(e,{provider:o.agent||null,cls:i.cls,status:i.status,text:o.text}).catch(()=>{});continue}if(o.role!=="user"&&o.box&&n.add(o.box),await N(e,{type:o.role==="user"?"turn.user":"turn.agent",actor:o.role==="user"?"user":"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{text:o.text,ts:o.ts,model:o.model||null,tools:o.tools||void 0,boxRole:o.boxRole,sourceId:o.sourceId||void 0,note:o.note||void 0,images:o.images?.length?o.images:void 0}}),o.role==="assistant"&&o.box===e.leadBox&&e.workspace?.branch){let c=hA(o.text);if(c&&gE(await G(e).catch(()=>[])))continue;if(c){let l=yA(c),u;if(e.workspace?.kind==="multi"){let w=await ps(e).catch(()=>[]);u=w.length?w.map(m=>({name:m.name,slug:m.repo??null})):void 0}let d=(e.workspace?.kind==="multi"?null:await co(e.cwd).catch(()=>null))||e.workspace.branch,p=await vd(e,{branch:d,rebase:l}).catch(()=>null),f=p?null:gA(c);if(p&&p.available.length===0)continue;await N(e,{type:"pr.offered",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{offerId:DU(),ts:o.ts,excerpt:c,branch:d,base:e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:o.boxRole,available:p?.available,defaults:p?.defaults,prState:p?.prState,openPr:p?.openPr||void 0,repos:p?.repos||u,offered:p?.defaults||f?.offered||void 0,toPr:f?.toPr||void 0}})}}}}catch{}return{agentTurnBoxes:n}}async function SE(e,t){try{let n=await kw(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=kE(o);if(n.keys.has(s))continue;n.keys.add(s);let a=(n.cmdN.get(o.box)??0)+1;n.cmdN.set(o.box,a),await N(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:a}}),VT(e,o),zT(e,o)}}catch{}}async function AE(e,t){try{let n=await kw(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=xE(o);n.keys.has(s)||(n.keys.add(s),await N(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 bw,Ld,TE=q(()=>{ye();ts();Xh();Nd();cs();pw();gr();xu();qu();jd();bw=new Map,Ld=new Map});function BU(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
207
|
+
`)){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 jU(e,t){let n=`${e.id}:${t.name}`;if(!xw.has(n)){let r=await G(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);xw.set(n,o)}return xw.get(n)}async function EE(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await Fo(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await jU(e,t),o=BU(await Nt(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||t.model||"gpt-5.5";await N(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:Pn(c,i,a.ts),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await N(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 xw,IE=q(()=>{ye();Ho();pn();ir();xw=new Map});import Fd from"node:fs";import Sw from"node:path";import LU from"node:os";async function HU({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(UU,{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:_E})});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<_E)break}return r}function WU(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 RE(){try{return JSON.parse(Fd.readFileSync(Ud(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function GU(e){try{Fd.mkdirSync(Sw.dirname(Ud()),{recursive:!0});let t=Ud()+".tmp";Fd.writeFileSync(t,JSON.stringify(e)),Fd.renameSync(t,Ud())}catch{}}async function VU({fetchImpl:e,now:t=Date.now()}={}){if(po.rows&&t-po.at<qU)return po.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=RE(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await HU({sinceMs:o,cookie:n,fetchImpl:e});po={at:t,rows:s.map(WU).filter(Boolean),error:null}}catch(s){po={at:t,rows:null,error:String(s?.message||s)}}return po.rows}async function zU(e,t){let n=`${e.id}:${t.name}`;if(!vw.has(n)){let r=await G(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);vw.set(n,o)}return vw.get(n)}async function OE(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!$E())return;let r=await VU({fetchImpl:n});if(!r||!r.length)return;let o=await uS(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await zU(e,t),a=RE(),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",d=l.tokens||null,p=l.vendorCents!=null?l.vendorCents/100:0;!p&&d&&(p=Pn(u,d,l.ts??void 0)),!p&&l.chargedCents!=null&&(p=l.chargedCents/100),await N(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.name,provider:"cursor",payload:{usd:p,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}})}GU({sinceMs:c,seen:[...i].slice(-KU)})}catch{}}var FU,$E,UU,_E,qU,po,Ud,KU,Aw,vw,Tw=q(()=>{ye();oh();ir();FU=process.env.BOSTRAT_STATE_DIR||Sw.join(process.env.HOME||LU.tmpdir(),".bostrat"),$E=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,UU="https://cursor.com/api/dashboard/get-filtered-usage-events",_E=100;qU=5*6e4,po={at:0,rows:null,error:null},Ud=()=>Sw.join(FU,"cursor-usage.json");KU=5e3;Aw=()=>({enabled:$E(),lastPollAt:po.at||null,error:po.error}),vw=new Map});import{randomUUID as JU,createHmac as YU}from"node:crypto";function CE(e){Iw=typeof e=="string"&&e?e:null}function XU(e){if(!Iw)return null;let t=Ue(e);return t?YU("sha256",Buffer.from(Iw,"base64url")).update(t,"utf8").digest("hex").slice(0,16):null}function DE(){let e={};for(let t of PE)e[String(t)]=0;return e.inf=0,{buckets:e,sumMs:0,count:0}}function ME(e,t){let n=Math.max(0,Number(t)||0);for(let r of PE)n<=r&&(e.buckets[String(r)]+=1);e.buckets.inf+=1,e.sumMs+=n,e.count+=1}function BE(){let e={};for(let t of NE)e[t]=0;return e}function QU(){Un={total:new Map,holds:BE(),queueWaitMs:DE(),readyWaitMs:new Map,verify:new Map,retries:new Map}}function jE({queueDepth:e=0,queueOldestAgeMs:t=0}={}){let n=Un;return{total:[...n.total].map(([r,o])=>{let[s,a,i]=r.split("|");return{provider:s,producer:a,outcome:i,n:o}}),holds:{...n.holds},queueWaitMs:{buckets:{...n.queueWaitMs.buckets},sumMs:n.queueWaitMs.sumMs,count:n.queueWaitMs.count},readyWaitMs:[...n.readyWaitMs].map(([r,o])=>{let[s,a]=r.split("|");return{provider:s,outcome:a,buckets:{...o.buckets},sumMs:o.sumMs,count:o.count}}),verify:[...n.verify].map(([r,o])=>{let[s,a,i]=r.split("|");return{provider:s,verdict:a,cliVersion:i||null,n:o}}),retries:[...n.retries].map(([r,o])=>({provider:r,n:o})),queueDepth:Number(e)||0,queueOldestAgeMs:Number(t)||0}}function vc({kind:e=null,producer:t=null,orchestration:n=!1}={}){return n?`orchestration:${e||"orchestration"}`:e?`scripted:${e}`:ZU.has(t)?t:"composer"}function Sc({text:e="",producer:t="composer",kind:n=null,provider:r=null,attachments:o=0,now:s=Date.now}={}){let a=s();return{id:JU(),producer:t,kind:n||null,provider:r||null,textBytes:Buffer.byteLength(String(e||""),"utf8"),textHmac16:XU(e),attachments:Number(o)||0,paste:null,t0:a,queuedAt:null,dequeuedAt:null,holds:BE(),requeued:0,readyWaitMs:null,readyOutcome:null,verify:null,settled:!1,outcome:null}}async function Ac(e,t,{position:n=null,reason:r="busy"}={}){!t||!e||(t.queuedAt=Date.now(),await N(e,{type:"delivery.queued",actor:"system",source:"orchestrator",boxId:e.leadBox??null,provider:t.provider,payload:{ts:t.queuedAt,deliveryId:t.id,producer:t.producer,...t.kind?{kind:t.kind}:{},reason:r,position:n,textBytes:t.textBytes,textHmac16:t.textHmac16,requeued:t.requeued}}).catch(()=>{}))}function LE(e,t){NE.includes(t)&&(e?.holds&&(e.holds[t]+=1),Un.holds[t]+=1)}function Tc(e,{ms:t,outcome:n}){e&&(e.readyWaitMs=Math.max(0,Number(t)||0),e.readyOutcome=n||null)}function e4(e){let t=e?.boxes?.find?.(n=>n.name===e.leadBox);return typeof t?.cliVersion=="string"&&t.cliVersion?t.cliVersion:null}async function gn(e,t,n,{now:r=Date.now}={}){if(!t||t.settled)return null;t.settled=!0,t.outcome=n;let o=r(),s=t.provider||"unknown",a=t.queuedAt!=null,i=a?Math.max(0,(t.dequeuedAt??o)-t.queuedAt):null;if(Ew(Un.total,`${s}|${t.producer}|${n}`),a&&ME(Un.queueWaitMs,i),t.readyWaitMs!=null){let u=`${s}|${t.readyOutcome||"unknown"}`;Un.readyWaitMs.has(u)||Un.readyWaitMs.set(u,DE()),ME(Un.readyWaitMs.get(u),t.readyWaitMs)}let c=e4(e);t.verify?.verdict&&(Ew(Un.verify,`${s}|${t.verify.verdict}|${c||""}`),t.verify.retries&&Ew(Un.retries,s,t.verify.retries));let l={ts:o,deliveryId:t.id,producer:t.producer,...t.kind?{kind:t.kind}:{},cliVersion:c,textBytes:t.textBytes,textHmac16:t.textHmac16,paste:t.paste,attachments:t.attachments,outcome:n,queued:a,queueWaitMs:i,queueHolds:{...t.holds},requeued:t.requeued,readyWaitMs:t.readyWaitMs,readyOutcome:t.readyOutcome,verify:t.verify?{...t.verify}:null,receiptMs:null,totalMs:Math.max(0,o-t.t0)};return e&&await N(e,{type:"delivery.settled",actor:"system",source:"orchestrator",boxId:e.leadBox??null,provider:t.provider,payload:l}).catch(()=>{}),l}function Ta(e){if(e?.usageCap)return"refused:cap";if(e?.interrupted)return"interrupted";if(e?.code)return`failed:${e.code}`;let t=String(e?.message||"");return/not running/i.test(t)?"failed:not-running":/archived/i.test(t)?"failed:archived":/blocked on an interactive prompt|isn't signed in|is waiting on its|stuck on its update/i.test(t)?"failed:blocked":"failed:error"}var Iw,NE,PE,ZU,Un,Ew,_w=q(()=>{ye();Ki();Iw=null;NE=["busy","prompt","handoff","noSession","cap","grace"],PE=[500,2e3,1e4,6e4,6e5],ZU=new Set(["composer","voice","loop"]);Un=null;QU();Ew=(e,t,n=1)=>e.set(t,(e.get(t)||0)+n)});import _c from"node:fs/promises";import Pt from"node:path";import t4 from"node:os";import n4 from"node:net";import{randomUUID as r4}from"node:crypto";import{execFile as o4}from"node:child_process";import{promisify as s4}from"node:util";function p4(e){for(let t of Pw(e))if(!GE.test(t))return t;return null}function KE(e){let t=Pw(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function f4(e,t){let n=KE(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Pt.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return Pt.resolve(e,n[r+1])}return e}function h4(e){let t=!1,n=!1;for(let r of Pw(e)){if(!t){if(GE.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&$w(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,$w(r))return`serves the filesystem root ("${r}")`;continue}if($w(r))return`serves the filesystem root ("${r}")`;if(m4(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function g4(e,t){if(Pt.resolve(e)===Pt.resolve(t))return!0;let[n,r]=await Promise.all([Hd(e),Hd(t)]);return n===r}async function y4(e){let n=(await _c.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 VE(Pt.join(e,o.name,Mw))?`./${o.name}`:null))).filter(Boolean)}async function b4(e,t){let[n,r]=await Promise.all([Hd(e),Hd(t)]),o=Pt.relative(n,r);if(o&&(o.startsWith("..")||Pt.isAbsolute(o)))throw Ec(`serve directory escapes the Track workspace: ${t}`)}async function k4(e,t){let n=h4(t);if(n)throw Ec(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await w4(e))throw Ec(`serve directory does not exist: ${e}`);let r=p4(t);if(!r)return;if(!r.includes("/")&&!await $0(r,{loginShell:!0,failOpen:!0}))throw Ec(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(l4.test(t)||u4.has(r)||!c4.has(r)||!KE(t).includes("run")||d4.test(t))return;let o=f4(e,t);if(await VE(Pt.join(o,Mw)))return;let s=await y4(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Ec(`no ${Mw} in ${o}${a}`)}async function kr(){return Hn||(Rw||=(async()=>{try{Hn=JSON.parse(await _c.readFile(qE,"utf8"))}catch{Hn={views:[],adoptWatermarks:{}}}finally{Rw=null}return Hn.views||=[],Hn.adoptWatermarks||={},Hn})(),Rw)}async function qn(){await Mt(qE,Hn)}function br(e,t,n={}){return N({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 Bw(e){return Ic.get(e)||null}function JE(e,{name:t,box:n}={}){let r=Ic.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 Ic.set(e,o),o}function Cw(e,t){let n=Ic.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function Wd(e,{error:t=null}={}){let n=Ic.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function YE(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 Nw(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function x4(e,t){let n=Nw(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!HE.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!HE.has(s))return s}}function $c(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=n4.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 jw(e){if(process.platform!=="darwin"){await Wn("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await Wn("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 XE(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function v4(e,t){let n=new Set(e.views.map(o=>o.port)),r=XE();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=FE;o<=UE;o++)if(!(n.has(o)||r.has(o))&&!await $c(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${FE}-${UE}`)}async function S4(e){try{let{stdout:t}=await Wn(te,se("list-sessions","-F","#{session_name}"));return t.split(`
|
|
208
|
+
`).some(n=>n.trim()===Ea(e))}catch{return!1}}async function Rc(e){try{await Wn(te,se("kill-session","-t",Ea(e)))}catch{}}async function Lw(e){if(!await Ci())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=Ea(e.slug),n=e.serveCwd??e.cwd,r=Pt.dirname(process.execPath);await Wn(te,se("new-session","-d","-s",t,"-c",n)),await Wn(te,se("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await Wn(te,se("send-keys","-t",t,"Enter")),await Wn(te,se("send-keys","-t",t,"-l",e.cmd)),await Wn(te,se("send-keys","-t",t,"Enter"))}async function WE(e,t=12){try{let{stdout:n}=await Wn(te,se("capture-pane","-t",Ea(e),"-p"));return String(n||"").trimEnd().split(`
|
|
209
209
|
`).filter(Boolean).slice(-t).join(`
|
|
210
|
-
`)}catch{return""}}async function v4(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await k4(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await UE(e.slug)||"server exited before listening"),yr.delete(e.id),await br(e,e.state,e.error?{error:e.error}:{}),!0}if(await _c(e.port))return yr.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await br(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>x4?(e.state="error",e.error=await UE(e.slug)||"never started listening",await br(e,"error",{error:e.error}),!0):!1;let o=(yr.get(e.id)||0)+1;return yr.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function Wd(){if(!Ow){Ow=!0;try{let e=await kr(),t=!1;for(let n of e.views)await v4(n)&&(t=!0);t&&await qn()}finally{Ow=!1}}}async function Fw({trackId:e}={}){await Wd();let t=await kr();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function YE(e){let n=(await kr()).views.find(r=>r.id===e);return n?{...n}:null}function Uw(e){return Hn&&Hn.views.find(t=>t.slug===e)||null}async function Hw({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=Pt.resolve(r,o??"."),d=i==="detected"?null:async()=>{await w4(r,u),await g4(u,n)},p=await kr();if(i!=="detected"){let m=Nw(t),g=String(n).trim(),y=p.views.find(b=>b.trackId===e&&(b.slug===m||Nw(b.name)===m||b.cmd===g&&Pt.resolve(b.serveCwd??b.cwd)===u));if(y)return S4(p,y,{cmd:g,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),p.views.filter(m=>m.state==="live"||m.state==="starting").length>=fo)throw new Error(`view cap reached (${fo} live) \u2014 stop one first`);let w={id:e4().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:y4(p,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await b4(p,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Lw(w),p.views.push(w),await qn(),await br(w,"created",{origin:i}),Cw(e,w.id),i==="detected"&&await _c(w.port)&&(w.state="live",w.lastHealthyAt=Date.now(),await qn(),await br(w,"live")),{...w}}async function S4(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 m4(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await _c(t.port))return await qn(),Cw(t.trackId,t.id),{...t,reused:!0};if(a&&await a(),e.views.filter(d=>d.id!==t.id&&(d.state==="live"||d.state==="starting")).length>=fo)throw new Error(`view cap reached (${fo} live) \u2014 stop one first`);return t.origin==="detected"?(await jw(t.port),t.origin="agent"):await $c(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),yr.delete(t.id),await Lw(t),await qn(),await br(t,"created",{restarted:!0,reused:!0}),Cw(t.trackId,t.id),{...t,reused:!0}}async function XE(e){let n=(await kr()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await jw(n.port):await $c(n.slug),n.state!=="stopped"&&(n.state="stopped",yr.delete(n.id),await qn(),await br(n,"stopped")),{...n}}async function ZE(e){let t=await kr(),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>=fo)throw new Error(`view cap reached (${fo} live) \u2014 stop one first`);return await $c(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),yr.delete(n.id),await Lw(n),await qn(),await br(n,"created",{restarted:!0}),{...n}}async function QE(e){let t=await kr(),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 jw(r.port):await $c(r.slug),t.views.splice(n,1),yr.delete(r.id),await qn(),await br(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function Ww(e){let t=await kr(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await $c(r.slug),yr.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await qn()),n.length}async function eI(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await kr(),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 qn()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(A4)){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(!JE().has(c)&&!n.views.some(l=>l.port===c)&&await _c(c))try{i.push(await Hw({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 Wn,r4,HE,jE,LE,fo,o4,Ea,FE,Tc,s4,Mw,a4,i4,c4,Pw,WE,$w,d4,f4,GE,Ud,Hn,Rw,Ec,Dw,KE,yr,x4,Ow,A4,qd=q(()=>{ht();ye();jr();Wn=n4(t4),r4=process.env.BOSTRAT_STATE_DIR||Pt.join(process.env.HOME||ZU.tmpdir(),".bostrat"),HE=Pt.join(r4,"views.json"),[jE,LE]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),fo=Number(process.env.BOSTRAT_VIEW_MAX)||3,o4="bostrat-view-",Ea=e=>`${o4}${e}`,FE=new Set(["bostrat","www","api","views","app"]),Tc=e=>Object.assign(new Error(e),{status:400}),s4=new Set(["npm","pnpm","yarn","bun"]),Mw="package.json",a4=/[|;&`]|\$\(/,i4=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),c4=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Pw=e=>String(e).trim().split(/\s+/).filter(Boolean),WE=/^[A-Za-z_][A-Za-z0-9_]*=/;$w=e=>e==="/",d4=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");f4=e=>Ic.stat(e).then(t=>t.isDirectory(),()=>!1),GE=e=>Ic.stat(e).then(t=>t.isFile(),()=>!1),Ud=e=>Ic.realpath(e).catch(()=>Pt.resolve(e));Hn=null,Rw=null;Ec=new Map,Dw=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,KE=12e3;yr=new Map,x4=12e4;Ow=!1;A4=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function Ia(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>Gw?null:n}function E4(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function Gd(e,{bypassThrottle:t=!1}={}){let n=e?.workspace?.branch;if(!n)return;let r;try{r=await G(e)}catch{return}let o=Ia(r);if(o==null)return;let s=(e.boxes||[]).find(d=>d.name===e.leadBox);if(await is(e.leadBox,s?.agent).catch(()=>!1))return;let a=Date.now();if(!t&&a-(qw.get(e.id)||0)<T4)return;qw.set(e.id,a);let i;try{i=await Ln(e.cwd,{caller:"reconcile"})}catch{return}let c=await va(e).catch(()=>[n]),l=(i||[]).find(d=>c.includes(d.headRefName));if(!l)return;let u=await Oe(e.cwd).catch(()=>null);if(u){let{link:d,created:p}=await Ut({repo:u,number:l.number,trackId:e.id,url:l.url,headRef:l.headRefName||n,title:l.title,source:"reconciled"});if(!p&&!(d?.trackId===e.id&&await ur(u,l.number))){if(!E4(d,o))return;let f=await G(e).catch(()=>null);if(f&&Ia(f)==null)return;await N(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"pushed",number:l.number,url:l.url,title:l.title,branch:n,repo:u}});return}}await N(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:l.number,url:l.url,title:l.title,branch:n,...u?{repo:u}:{}}}).catch(()=>{}),qw.delete(e.id)}var T4,qw,Gw,Rc=q(()=>{ye();as();Pr();jn();fc();cs();T4=6e3,qw=new Map,Gw=30*6e4});import Kw from"node:os";import{execFile as I4}from"node:child_process";import{promisify as _4}from"node:util";async function Kd(){if(process.platform!=="darwin")return Kw.freemem();try{let{stdout:e}=await $4("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:Kw.freemem()}catch{return Kw.freemem()}}var $4,Vw=q(()=>{$4=_4(I4)});import tt from"node:fs/promises";import rt from"node:path";import oI from"node:os";import{randomUUID as R4}from"node:crypto";import{execFile as O4}from"node:child_process";import{promisify as M4}from"node:util";import{chromium as C4}from"playwright-core";async function Vn(){return fs||(zw||=(async()=>{try{fs=JSON.parse(await tt.readFile(aI,"utf8"))}catch{fs={mocks:[]}}finally{zw=null}return fs.mocks||=[],fs})(),zw)}async function tn(){await Mt(aI,fs)}function uI(e,t){Kn(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&Kn(e,t.v,"png")}function Et(e,t,n={}){return N({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 nn(e,t){let n=Mc(e.cwd);return{html:rt.join(n,`${e.id}.v${t}.html`),png:rt.join(n,`${e.id}.v${t}.png`)}}function Oc(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?rt.join(e.cwd,r):null}function yn(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 Xw(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function Vd(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(F4)}function U4(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 H4(e){let t=Buffer.alloc(24),n=await tt.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function W4({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 q4(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((w,m)=>{r.onload=w,r.onerror=()=>m(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,p=-1,f=-1;for(let w=0;w<s;w++)for(let m=w*o*4,g=0;g<o;g++,m+=4)Math.abs(c[m]-l[0])+Math.abs(c[m+1]-l[1])+Math.abs(c[m+2]-l[2])>24&&(g<u&&(u=g),g>p&&(p=g),w<d&&(d=w),f=w);return p<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:p,bottom:f}}},t.toString("base64"))}async function G4(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((d,p)=>{c.onload=d,c.onerror=()=>p(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 K4({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await C4.launch({executablePath:iI,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Ht("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:_a})).newPage();a.setDefaultTimeout($a),await a.goto(`file://${e}`,{waitUntil:"load",timeout:$a}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:$a});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await q4(a,i),u=l&&W4({...l,pad:D4*_a,align:_a});u&&(i=await G4(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<cI)throw new Ht(`capture produced a ${i.length}-byte image`);let c=Xw(i);if(!c)throw new Ht("output is not a PNG");return await tt.writeFile(t,i),{width:Math.round(c.width/_a),height:Math.round(c.height/_a),dpr:_a,renderMs:Date.now()-r}}catch(s){throw s instanceof Ht?s:new Ht(/timeout/i.test(s?.message||"")?`render timed out after ${$a}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function rI({htmlPath:e,outPath:t,width:n}){let r=await tt.mkdtemp(rt.join(oI.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Yw(tI,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:$a,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new Ht(`firefox not found (${tI})`,""):new Ht(c?.killed?`render timed out after ${$a}ms`:"firefox exited non-zero",s)}let a=await tt.stat(t).catch(()=>null);if(!a||a.size<cI)throw new Ht(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Xw(await H4(t));if(!i)throw new Ht("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await tt.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function V4(e){if(N4==="firefox")return rI(e);try{return await K4(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await rI(e)}catch(n){throw/not found/.test(n?.message||"")?new Ht(`no browser installed \u2014 chromium missing at ${iI} and no firefox on PATH; install chromium (Debian: apt install chromium fonts-dejavu-core fonts-liberation) or point BOSTRAT_CHROMIUM_BIN at a Chrome/Chromium binary`,t?.message||""):n}}}function dI(e){return Jw=Jw.then(e,e),Jw}async function pI(e,t){let n=await Vn(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await tn(),await Et(r,"rendering",{version:t});try{let o=await Kd();if(o<B4)throw new Ht(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=nn(r,t),a=await z4({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await tt.rm(s.png,{force:!0}).catch(()=>{});return}Zw(r,{v:t,source:"html",htmlRel:rt.relative(r.cwd,s.html),pngRel:rt.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await tn(),await Et(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),Gn(r),Kn(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await tn(),await Et(r,"error",{version:t,error:r.error}),Gn(r)}}}function Zw(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 J4({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?P4:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Cc({trackId:e,group:t}={}){let n=await Vn(),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 xr(e){let n=(await Vn()).mocks.find(r=>r.id===e);return n?{...n}:null}function fI({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&&!Vd(t))throw new Error("image must be a PNG");if(n){let o=U4(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>nI)throw new Error(`video must be ${nI/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function mI({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=J4({trackId:e,name:t,cwd:s,width:a}),p=typeof n=="string"&&n.trim(),f=fI({html:n,image:r,video:o}),w=await Vn(),m={id:R4().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:d,height:null,dpr:null,state:"queued",source:p?"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 tt.mkdir(Mc(s),{recursive:!0}),w.mocks.push(m),p?(await tt.writeFile(nn(m,1).html,n),await tn(),await Et(m,"created"),Gn(m),Kn(m,1,"html"),dI(()=>pI(m.id,1))):o?(await wI(m,1,o,f),await tn(),await Et(m,"created"),await Et(m,"ready",{version:1,width:m.width,height:m.height,dpr:m.dpr,durationS:m.versions[0]?.durationS??null}),Gn(m),uI(m,m.versions[0])):(await gI(m,1,r),await tn(),await Et(m,"created"),await Et(m,"ready",{version:1,width:m.width,height:m.height,dpr:m.dpr}),Gn(m),Kn(m,1,"png")),{...m}}async function hI(e,{html:t,image:n,video:r,width:o}={}){let a=(await Vn()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=fI({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 tt.mkdir(Mc(a.cwd),{recursive:!0}),i?(a.state="queued",await tt.writeFile(nn(a,l).html,t),await tn(),await Et(a,"updated",{version:l}),Gn(a),Kn(a,l,"html"),dI(()=>pI(a.id,l))):r?(await wI(a,l,r,c),await tn(),await Et(a,"updated",{version:l}),await Et(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:yn(a)?.durationS??null}),Gn(a),uI(a,a.versions.find(u=>u.v===l))):(await gI(a,l,n),await tn(),await Et(a,"updated",{version:l}),await Et(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Gn(a),Kn(a,l,"png")),{...a}}async function Y4(e){try{let{stdout:t}=await Yw(j4,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:lI,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 X4(e,t){try{await Yw(L4,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:lI,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await tt.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function wI(e,t,n,r){let o=Mc(e.cwd),s=rt.join(o,`${e.id}.v${t}.${r.ext}`),a=rt.join(o,`${e.id}.v${t}.poster.png`);await tt.writeFile(s,n);let i=await Y4(s),c=await X4(s,a);Zw(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:rt.relative(e.cwd,s),posterRel:c?rt.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 gI(e,t,n){let r=Xw(n);if(!r)throw new Error("image must be a PNG");let o=nn(e,t);await tt.writeFile(o.png,n),Zw(e,{v:t,source:"image",htmlRel:null,pngRel:rt.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function yI(e){let t=await Vn(),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 Et(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function bI(e){let t=await Vn(),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=nn(r,o);await tt.rm(s.html,{force:!0}).catch(()=>{}),await tt.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await tt.rm(rt.join(r.cwd,s),{force:!0}).catch(()=>{});return await tn(),await Et(r,"deleted"),cn("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function kI(e){let n=(await Vn()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=rt.join(sI,"mock-archive",e.id),o=Mc(e.cwd),s=rt.join(r,".bostrat","mocks");await tt.mkdir(s,{recursive:!0});let a=await tt.readdir(o).catch(()=>[]);for(let i of a)await tt.copyFile(rt.join(o,i),rt.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await tt.stat(rt.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=e.name||e.id,i.updatedAt=Date.now(),Gn(i)}return await tn(),n.length}async function xI(e){let t=await Vn(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await tn();for(let r of n)cn("mocks",r.id).catch(()=>{})}return n.length}async function vI(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await dt("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Vn();for(let r of n.mocks){await Gn(r);for(let o of r.versions||[])o.htmlRel&&await Kn(r,o.v,"html"),(o.pngRel||o.posterRel)&&await Kn(r,o.v,"png"),o.videoRel&&await Kn(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var Yw,sI,aI,N4,iI,tI,P4,_a,D4,cI,$a,B4,nI,j4,L4,lI,Ht,fs,zw,Gn,Kn,Mc,F4,z4,Jw,Ra=q(()=>{ye();jr();Vw();Fr();Yw=M4(O4),sI=process.env.BOSTRAT_STATE_DIR||rt.join(process.env.HOME||oI.tmpdir(),".bostrat"),aI=rt.join(sI,"mocks.json"),N4=process.env.BOSTRAT_MOCK_ENGINE||"chromium",iI=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",tI=process.env.BOSTRAT_FIREFOX_BIN||"firefox",P4=1300,_a=2,D4=32,cI=1500,$a=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,B4=700*1024*1024,nI=100*1024*1024,j4=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",L4=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",lI=2e4,Ht=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
210
|
+
`)}catch{return""}}async function T4(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await S4(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await WE(e.slug)||"server exited before listening"),yr.delete(e.id),await br(e,e.state,e.error?{error:e.error}:{}),!0}if(await $c(e.port))return yr.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await br(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>A4?(e.state="error",e.error=await WE(e.slug)||"never started listening",await br(e,"error",{error:e.error}),!0):!1;let o=(yr.get(e.id)||0)+1;return yr.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function qd(){if(!Ow){Ow=!0;try{let e=await kr(),t=!1;for(let n of e.views)await T4(n)&&(t=!0);t&&await qn()}finally{Ow=!1}}}async function Fw({trackId:e}={}){await qd();let t=await kr();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function ZE(e){let n=(await kr()).views.find(r=>r.id===e);return n?{...n}:null}function Uw(e){return Hn&&Hn.views.find(t=>t.slug===e)||null}async function Hw({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=Pt.resolve(r,o??"."),d=i==="detected"?null:async()=>{await b4(r,u),await k4(u,n)},p=await kr();if(i!=="detected"){let m=Nw(t),g=String(n).trim(),y=p.views.find(b=>b.trackId===e&&(b.slug===m||Nw(b.name)===m||b.cmd===g&&Pt.resolve(b.serveCwd??b.cwd)===u));if(y)return E4(p,y,{cmd:g,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),p.views.filter(m=>m.state==="live"||m.state==="starting").length>=fo)throw new Error(`view cap reached (${fo} live) \u2014 stop one first`);let w={id:r4().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:x4(p,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await v4(p,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Lw(w),p.views.push(w),await qn(),await br(w,"created",{origin:i}),Cw(e,w.id),i==="detected"&&await $c(w.port)&&(w.state="live",w.lastHealthyAt=Date.now(),await qn(),await br(w,"live")),{...w}}async function E4(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 g4(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await $c(t.port))return await qn(),Cw(t.trackId,t.id),{...t,reused:!0};if(a&&await a(),e.views.filter(d=>d.id!==t.id&&(d.state==="live"||d.state==="starting")).length>=fo)throw new Error(`view cap reached (${fo} live) \u2014 stop one first`);return t.origin==="detected"?(await jw(t.port),t.origin="agent"):await Rc(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),yr.delete(t.id),await Lw(t),await qn(),await br(t,"created",{restarted:!0,reused:!0}),Cw(t.trackId,t.id),{...t,reused:!0}}async function QE(e){let n=(await kr()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await jw(n.port):await Rc(n.slug),n.state!=="stopped"&&(n.state="stopped",yr.delete(n.id),await qn(),await br(n,"stopped")),{...n}}async function eI(e){let t=await kr(),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>=fo)throw new Error(`view cap reached (${fo} live) \u2014 stop one first`);return await Rc(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),yr.delete(n.id),await Lw(n),await qn(),await br(n,"created",{restarted:!0}),{...n}}async function tI(e){let t=await kr(),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 jw(r.port):await Rc(r.slug),t.views.splice(n,1),yr.delete(r.id),await qn(),await br(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function Ww(e){let t=await kr(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Rc(r.slug),yr.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await qn()),n.length}async function nI(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await kr(),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 qn()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(I4)){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(!XE().has(c)&&!n.views.some(l=>l.port===c)&&await $c(c))try{i.push(await Hw({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 Wn,a4,qE,FE,UE,fo,i4,Ea,HE,Ec,c4,Mw,l4,u4,d4,Pw,GE,$w,m4,w4,VE,Hd,Hn,Rw,Ic,Dw,zE,yr,A4,Ow,I4,Gd=q(()=>{ht();ye();jr();Wn=s4(o4),a4=process.env.BOSTRAT_STATE_DIR||Pt.join(process.env.HOME||t4.tmpdir(),".bostrat"),qE=Pt.join(a4,"views.json"),[FE,UE]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),fo=Number(process.env.BOSTRAT_VIEW_MAX)||3,i4="bostrat-view-",Ea=e=>`${i4}${e}`,HE=new Set(["bostrat","www","api","views","app"]),Ec=e=>Object.assign(new Error(e),{status:400}),c4=new Set(["npm","pnpm","yarn","bun"]),Mw="package.json",l4=/[|;&`]|\$\(/,u4=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),d4=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Pw=e=>String(e).trim().split(/\s+/).filter(Boolean),GE=/^[A-Za-z_][A-Za-z0-9_]*=/;$w=e=>e==="/",m4=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");w4=e=>_c.stat(e).then(t=>t.isDirectory(),()=>!1),VE=e=>_c.stat(e).then(t=>t.isFile(),()=>!1),Hd=e=>_c.realpath(e).catch(()=>Pt.resolve(e));Hn=null,Rw=null;Ic=new Map,Dw=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,zE=12e3;yr=new Map,A4=12e4;Ow=!1;I4=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function Ia(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>Gw?null:n}function $4(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function Kd(e,{bypassThrottle:t=!1}={}){let n=e?.workspace?.branch;if(!n)return;let r;try{r=await G(e)}catch{return}let o=Ia(r);if(o==null)return;let s=(e.boxes||[]).find(d=>d.name===e.leadBox);if(await is(e.leadBox,s?.agent).catch(()=>!1))return;let a=Date.now();if(!t&&a-(qw.get(e.id)||0)<_4)return;qw.set(e.id,a);let i;try{i=await Ln(e.cwd,{caller:"reconcile"})}catch{return}let c=await va(e).catch(()=>[n]),l=(i||[]).find(d=>c.includes(d.headRefName));if(!l)return;let u=await Oe(e.cwd).catch(()=>null);if(u){let{link:d,created:p}=await Ut({repo:u,number:l.number,trackId:e.id,url:l.url,headRef:l.headRefName||n,title:l.title,source:"reconciled"});if(!p&&!(d?.trackId===e.id&&await ur(u,l.number))){if(!$4(d,o))return;let f=await G(e).catch(()=>null);if(f&&Ia(f)==null)return;await N(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"pushed",number:l.number,url:l.url,title:l.title,branch:n,repo:u}});return}}await N(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:l.number,url:l.url,title:l.title,branch:n,...u?{repo:u}:{}}}).catch(()=>{}),qw.delete(e.id)}var _4,qw,Gw,Oc=q(()=>{ye();as();Pr();jn();mc();cs();_4=6e3,qw=new Map,Gw=30*6e4});import Kw from"node:os";import{execFile as R4}from"node:child_process";import{promisify as O4}from"node:util";async function Vd(){if(process.platform!=="darwin")return Kw.freemem();try{let{stdout:e}=await M4("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:Kw.freemem()}catch{return Kw.freemem()}}var M4,Vw=q(()=>{M4=O4(R4)});import tt from"node:fs/promises";import rt from"node:path";import aI from"node:os";import{randomUUID as C4}from"node:crypto";import{execFile as N4}from"node:child_process";import{promisify as P4}from"node:util";import{chromium as D4}from"playwright-core";async function Vn(){return fs||(zw||=(async()=>{try{fs=JSON.parse(await tt.readFile(cI,"utf8"))}catch{fs={mocks:[]}}finally{zw=null}return fs.mocks||=[],fs})(),zw)}async function tn(){await Mt(cI,fs)}function pI(e,t){Kn(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&Kn(e,t.v,"png")}function Et(e,t,n={}){return N({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 nn(e,t){let n=Cc(e.cwd);return{html:rt.join(n,`${e.id}.v${t}.html`),png:rt.join(n,`${e.id}.v${t}.png`)}}function Mc(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?rt.join(e.cwd,r):null}function yn(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 Xw(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function zd(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(W4)}function q4(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 G4(e){let t=Buffer.alloc(24),n=await tt.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function K4({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 V4(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((w,m)=>{r.onload=w,r.onerror=()=>m(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,p=-1,f=-1;for(let w=0;w<s;w++)for(let m=w*o*4,g=0;g<o;g++,m+=4)Math.abs(c[m]-l[0])+Math.abs(c[m+1]-l[1])+Math.abs(c[m+2]-l[2])>24&&(g<u&&(u=g),g>p&&(p=g),w<d&&(d=w),f=w);return p<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:p,bottom:f}}},t.toString("base64"))}async function z4(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((d,p)=>{c.onload=d,c.onerror=()=>p(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 J4({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await D4.launch({executablePath:lI,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Ht("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:_a})).newPage();a.setDefaultTimeout($a),await a.goto(`file://${e}`,{waitUntil:"load",timeout:$a}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:$a});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await V4(a,i),u=l&&K4({...l,pad:L4*_a,align:_a});u&&(i=await z4(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<uI)throw new Ht(`capture produced a ${i.length}-byte image`);let c=Xw(i);if(!c)throw new Ht("output is not a PNG");return await tt.writeFile(t,i),{width:Math.round(c.width/_a),height:Math.round(c.height/_a),dpr:_a,renderMs:Date.now()-r}}catch(s){throw s instanceof Ht?s:new Ht(/timeout/i.test(s?.message||"")?`render timed out after ${$a}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function sI({htmlPath:e,outPath:t,width:n}){let r=await tt.mkdtemp(rt.join(aI.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Yw(rI,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:$a,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new Ht(`firefox not found (${rI})`,""):new Ht(c?.killed?`render timed out after ${$a}ms`:"firefox exited non-zero",s)}let a=await tt.stat(t).catch(()=>null);if(!a||a.size<uI)throw new Ht(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Xw(await G4(t));if(!i)throw new Ht("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await tt.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function Y4(e){if(B4==="firefox")return sI(e);try{return await J4(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await sI(e)}catch(n){throw/not found/.test(n?.message||"")?new Ht(`no browser installed \u2014 chromium missing at ${lI} and no firefox on PATH; install chromium (Debian: apt install chromium fonts-dejavu-core fonts-liberation) or point BOSTRAT_CHROMIUM_BIN at a Chrome/Chromium binary`,t?.message||""):n}}}function fI(e){return Jw=Jw.then(e,e),Jw}async function mI(e,t){let n=await Vn(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await tn(),await Et(r,"rendering",{version:t});try{let o=await Vd();if(o<F4)throw new Ht(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=nn(r,t),a=await X4({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await tt.rm(s.png,{force:!0}).catch(()=>{});return}Zw(r,{v:t,source:"html",htmlRel:rt.relative(r.cwd,s.html),pngRel:rt.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await tn(),await Et(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),Gn(r),Kn(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await tn(),await Et(r,"error",{version:t,error:r.error}),Gn(r)}}}function Zw(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 Z4({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?j4:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Nc({trackId:e,group:t}={}){let n=await Vn(),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 xr(e){let n=(await Vn()).mocks.find(r=>r.id===e);return n?{...n}:null}function hI({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&&!zd(t))throw new Error("image must be a PNG");if(n){let o=q4(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>oI)throw new Error(`video must be ${oI/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function wI({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=Z4({trackId:e,name:t,cwd:s,width:a}),p=typeof n=="string"&&n.trim(),f=hI({html:n,image:r,video:o}),w=await Vn(),m={id:C4().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:d,height:null,dpr:null,state:"queued",source:p?"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 tt.mkdir(Cc(s),{recursive:!0}),w.mocks.push(m),p?(await tt.writeFile(nn(m,1).html,n),await tn(),await Et(m,"created"),Gn(m),Kn(m,1,"html"),fI(()=>mI(m.id,1))):o?(await yI(m,1,o,f),await tn(),await Et(m,"created"),await Et(m,"ready",{version:1,width:m.width,height:m.height,dpr:m.dpr,durationS:m.versions[0]?.durationS??null}),Gn(m),pI(m,m.versions[0])):(await bI(m,1,r),await tn(),await Et(m,"created"),await Et(m,"ready",{version:1,width:m.width,height:m.height,dpr:m.dpr}),Gn(m),Kn(m,1,"png")),{...m}}async function gI(e,{html:t,image:n,video:r,width:o}={}){let a=(await Vn()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=hI({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 tt.mkdir(Cc(a.cwd),{recursive:!0}),i?(a.state="queued",await tt.writeFile(nn(a,l).html,t),await tn(),await Et(a,"updated",{version:l}),Gn(a),Kn(a,l,"html"),fI(()=>mI(a.id,l))):r?(await yI(a,l,r,c),await tn(),await Et(a,"updated",{version:l}),await Et(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:yn(a)?.durationS??null}),Gn(a),pI(a,a.versions.find(u=>u.v===l))):(await bI(a,l,n),await tn(),await Et(a,"updated",{version:l}),await Et(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Gn(a),Kn(a,l,"png")),{...a}}async function Q4(e){try{let{stdout:t}=await Yw(U4,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:dI,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 e2(e,t){try{await Yw(H4,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:dI,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await tt.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function yI(e,t,n,r){let o=Cc(e.cwd),s=rt.join(o,`${e.id}.v${t}.${r.ext}`),a=rt.join(o,`${e.id}.v${t}.poster.png`);await tt.writeFile(s,n);let i=await Q4(s),c=await e2(s,a);Zw(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:rt.relative(e.cwd,s),posterRel:c?rt.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 bI(e,t,n){let r=Xw(n);if(!r)throw new Error("image must be a PNG");let o=nn(e,t);await tt.writeFile(o.png,n),Zw(e,{v:t,source:"image",htmlRel:null,pngRel:rt.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function kI(e){let t=await Vn(),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 Et(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function xI(e){let t=await Vn(),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=nn(r,o);await tt.rm(s.html,{force:!0}).catch(()=>{}),await tt.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await tt.rm(rt.join(r.cwd,s),{force:!0}).catch(()=>{});return await tn(),await Et(r,"deleted"),cn("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function vI(e){let n=(await Vn()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=rt.join(iI,"mock-archive",e.id),o=Cc(e.cwd),s=rt.join(r,".bostrat","mocks");await tt.mkdir(s,{recursive:!0});let a=await tt.readdir(o).catch(()=>[]);for(let i of a)await tt.copyFile(rt.join(o,i),rt.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await tt.stat(rt.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=e.name||e.id,i.updatedAt=Date.now(),Gn(i)}return await tn(),n.length}async function SI(e){let t=await Vn(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await tn();for(let r of n)cn("mocks",r.id).catch(()=>{})}return n.length}async function AI(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await dt("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Vn();for(let r of n.mocks){await Gn(r);for(let o of r.versions||[])o.htmlRel&&await Kn(r,o.v,"html"),(o.pngRel||o.posterRel)&&await Kn(r,o.v,"png"),o.videoRel&&await Kn(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var Yw,iI,cI,B4,lI,rI,j4,_a,L4,uI,$a,F4,oI,U4,H4,dI,Ht,fs,zw,Gn,Kn,Cc,W4,X4,Jw,Ra=q(()=>{ye();jr();Vw();Fr();Yw=P4(N4),iI=process.env.BOSTRAT_STATE_DIR||rt.join(process.env.HOME||aI.tmpdir(),".bostrat"),cI=rt.join(iI,"mocks.json"),B4=process.env.BOSTRAT_MOCK_ENGINE||"chromium",lI=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",rI=process.env.BOSTRAT_FIREFOX_BIN||"firefox",j4=1300,_a=2,L4=32,uI=1500,$a=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,F4=700*1024*1024,oI=100*1024*1024,U4=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",H4=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",dI=2e4,Ht=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
211
211
|
${n.trim().split(`
|
|
212
212
|
`).slice(-6).join(`
|
|
213
|
-
`)}`:t),this.name="MockRenderError"}},fs=null,zw=null;Gn=e=>At("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),Kn=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?Oc(e,r,"video"):r&&Oc(e,r,n)||(n==="html"?nn(e,t).html:null);return o?Lr("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Mc=e=>rt.join(e,".bostrat","mocks");F4=Buffer.from([137,80,78,71,13,10,26,10]);z4=V4,Jw=Promise.resolve()});import Z4 from"node:fs/promises";import II from"node:path";import Q4 from"node:os";import{randomUUID as _I}from"node:crypto";async function Sr(){return vr||(Qw||=(async()=>{let e=!0;try{vr=JSON.parse(await Z4.readFile($I,"utf8"))}catch{vr={flows:[]},e=!1}finally{Qw=null}if(vr.flows||=[],!e&&qe()){let{records:t,source:n}=await dt("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&vr.flows.push(r.data);vr.flows.length&&await Oa().catch(()=>{})}}return vr})(),Qw)}async function Oa(){await Mt($I,vr)}function eg(e,t,n={}){return N({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 RI({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>SI)throw new Error(`steps must be an array of 1-${SI}`);let r=await Cc({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>TI)throw new Error(`${l}: caption over ${TI} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>EI)throw new Error(`${l}: title over ${EI} chars`);let p=String(c.mock??"").trim().replace(/^mock:/,"");if(!p)throw new Error(`${l}: mock (name or id) required`);let f=r.filter(b=>b.id===p);if(f.length||(f=r.filter(b=>b.name.toLowerCase()===p.toLowerCase())),!f.length)throw new Error(`${l}: no mock matching "${p}" in this Track \u2014 see \`bostrat-mock ls\``);if(f.length>1)throw new Error(`${l}: "${p}" is ambiguous (${f.map(b=>`mock:${b.id}`).join(", ")}) \u2014 use the id`);let w=f[0],m=null;if(c.v!=null&&(m=Number(c.v),!Number.isInteger(m)||!(w.versions||[]).some(b=>b.v===m)))throw new Error(`${l}: "${w.name}" has no captured v${c.v} to pin`);let g=o.findIndex(b=>b.mockId===w.id),y=g>=0?o.splice(g,1)[0].id:_I().slice(0,8);s.push({id:y,mockId:w.id,caption:u,title:d,v:m})}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 t2(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>AI)throw new Error(`name over ${AI} chars`);return e.trim()}async function OI({trackId:e}={}){let t=await Sr();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function n2(e){let n=(await Sr()).flows.find(r=>r.id===e);return n?{...n}:null}async function MI(e){let t=await n2(e);if(!t)return null;let n=new Map((await Cc({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=yn(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 CI({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=t2(t),i=await Sr(),c=i.flows.find(f=>f.trackId===e&&f.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 RI({trackId:e,steps:n}),d=Date.now(),p={id:_I().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:d}],steps:l,createdAt:d,updatedAt:d};return i.flows.push(p),await Oa(),await eg(p,"created"),zd(p),{...p}}async function NI(e,{steps:t,note:n}={}){let o=(await Sr()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await RI({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 Oa(),await eg(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),zd(o),{...o}}async function PI(e){let t=await Sr(),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 Oa(),await eg(r,"deleted"),cn("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function DI(e){return(await Sr()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function BI(e){let n=(await Sr()).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(),zd(r);return n.length&&await Oa(),n.length}async function jI(e){let t=await Sr(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await Oa();for(let r of n)cn("flows",r.id).catch(()=>{})}return n.length}async function LI(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await dt("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Sr();for(let r of n.flows)await zd(r);return{pushed:n.flows.length,source:"plane"}}var e2,$I,SI,AI,TI,EI,vr,Qw,zd,tg=q(()=>{ye();jr();Ra();Fr();e2=process.env.BOSTRAT_STATE_DIR||II.join(process.env.HOME||Q4.tmpdir(),".bostrat"),$I=II.join(e2,"flows.json"),SI=20,AI=80,TI=500,EI=80,vr=null,Qw=null;zd=e=>At("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import Er from"node:fs/promises";import Ir from"node:path";import r2 from"node:os";import{randomUUID as o2}from"node:crypto";function Nc(e){return s2.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function zn(){return Ar||(ng||=(async()=>{let e=!0;try{Ar=JSON.parse(await Er.readFile(UI,"utf8"))}catch{Ar={runbooks:[]},e=!1}finally{ng=null}return Ar.runbooks||=[],!e&&qe()&&await a2().catch(()=>{}),Ar})(),ng)}async function hs(){await Mt(UI,Ar)}function Yd(e){return Pc(At("runbooks",{...e},{trackId:e.trackId}))}async function a2(){let{records:e,source:t}=await dt("runbooks");if(!(t!=="plane"||!e.length)){await Er.mkdir(Jd,{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 Ti("runbooks",r.id,o.v,"md");typeof s=="string"&&await Er.writeFile(Tr(r,o.v),s).catch(()=>{})}Ar.runbooks.push(r)}Ar.runbooks.length&&await hs()}}function Ma(e,t,n={}){return N({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 Tr(e,t){return Ir.join(Jd,`${e.id}.v${t}.md`)}function HI(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 WI(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>FI)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${FI/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Nc(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 i2(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function c2({trackId:e,status:t}={}){let n=await zn(),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 qI({trackId:e,status:t}={}){let n=await zn(),r=await c2({trackId:e,status:t});if(!qe())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await dt("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 rg(e){let t=await l2(e);if(t)return t;if(!qe())return null;let{records:n,source:r}=await dt("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 GI(e,t){let n=await Ti("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function l2(e){let n=(await zn()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function KI({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=WI(r),c=await zn(),l={id:o2().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 Er.mkdir(Jd,{recursive:!0}),await Er.writeFile(Tr(l,1),r),l.versions.push({v:1,mdRel:Ir.relative(ms,Tr(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await hs(),await Ma(l,"created",{version:1}),Yd(l),Pc(Lr("runbooks",l.id,1,"md",Tr(l,1))),{...l}}async function VI(e,{md:t,note:n}={}){let o=(await zn()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");i2(o,"update");let s=WI(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Er.mkdir(Jd,{recursive:!0}),await Er.writeFile(Tr(o,a),t),o.versions.push({v:a,mdRel:Ir.relative(ms,Tr(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await hs(),await Ma(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Yd(o),Pc(Lr("runbooks",o.id,a,"md",Tr(o,a))),{...o}}async function zI(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await zn()).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 hs(),await Ma(r,t==="done"?"done":"reopened"),Yd(r),{...r})}async function JI(e){let t=await zn(),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 Er.rm(Ir.join(ms,o.mdRel),{force:!0}).catch(()=>{});return await hs(),r.trackDeletedAt||await Ma(r,"deleted"),Pc(cn("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function YI(e){let n=(await zn()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Ma(r,"orphaned"),r.cwd=null,Yd(r);return n.length&&await hs(),n.length}async function XI(e){let t=await zn(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await Ma(r,"deleted");for(let o of r.versions||[])await Er.rm(Ir.join(ms,o.mdRel),{force:!0}).catch(()=>{});Pc(cn("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await hs()),n.length}async function ZI(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await dt("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await zn();for(let r of n.runbooks){await At("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await Lr("runbooks",r.id,o.v,"md",Ir.join(ms,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var ms,UI,Jd,FI,s2,Ar,ng,Pc,Xd=q(()=>{jr();ye();Fr();ms=process.env.BOSTRAT_STATE_DIR||Ir.join(process.env.HOME||r2.tmpdir(),".bostrat"),UI=Ir.join(ms,"runbooks.json"),Jd=Ir.join(ms,"runbooks"),FI=256*1024,s2=[{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/}];Ar=null,ng=null;Pc=e=>Promise.resolve(e).catch(()=>{})});import og from"node:fs/promises";import Dc from"node:path";import{randomUUID as u2}from"node:crypto";function ag(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return d2.has(t)?"image":p2.has(t)?"doc":null}function f2(e){let r=Dc.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 t_(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>QI)throw new Error(`file too large \u2014 max ${Math.floor(QI/(1024*1024))} MB`);let r=f2(t),o=Dc.extname(r).slice(1),s=ag(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=u2().slice(0,8),i=sg(e);await og.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await og.writeFile(Dc.join(i,c),n,{mode:384}),await og.chmod(Dc.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${e_}/${c}`,kind:s,size:n.length}}function n_(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function ig(e){return e.role==="user"&&m2.test((e.text||"").trim())}var e_,sg,QI,d2,p2,m2,cg=q(()=>{e_=".bostrat/uploads",sg=e=>Dc.join(e,e_),QI=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,d2=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),p2=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"]);m2=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function ws(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}function lg(){let e=Number(process.env.BOSTRAT_LOOP_QUEUE_HOLD_MS);return Number.isFinite(e)&&e>0?e:h2}function g2(e,t,n=Date.now()){return e?((e.heldReason!==t||e.heldSince==null)&&(e.heldReason=t,e.heldSince=n),Math.max(0,n-e.heldSince)):0}function r_(e,t,{capMs:n=lg(),now:r=Date.now()}={}){if(!e)return!1;let o=g2(e,t,r);return e.delivery?.producer!=="loop"||!w2.has(t)?!1:o>=n}var h2,w2,o_=q(()=>{h2=5*6e4,w2=new Set(["busy","noSession"])});import{execFile as y2}from"node:child_process";import{promisify as b2}from"node:util";function ug(e){return`refs/bostrat/wip/${e}`}function v2(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function i_(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function S2(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:x2}function c_(e){let t=e?.workspace;return t?.kind==="multi"?(Array.isArray(t.repos)?t.repos:[]).filter(n=>n?.dir).map(n=>({dir:n.dir,name:n.name??null,repo:n.repo??null})):e?.cwd?[{dir:e.cwd,name:null,repo:t?.repo??null}]:[]}async function A2(e,{log:t=console,force:n=!1,checkout:r=null}={}){let o=e?.id,s=r?.dir??e?.cwd;if(!o||!s)return{pushed:!1,reason:"no-workspace"};let a=r?.name?`${o}/${r.name}`:o;try{await mo(s,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await mo(s,["remote","get-url","origin"])}catch{return s_.has(s)||(s_.add(s),t.log?.(`[wip-sync] ${a}: no origin remote \u2014 durable code fidelity is chat/memory-only for this checkout`)),{pushed:!1,reason:"no-origin"}}try{let i=await fa(s,"bostrat: wip snapshot"),{stdout:c}=await mo(s,["rev-parse",`${i}^{tree}`]),l=c.trim();if(!n&&Zd.get(s)===l)return{pushed:!1,reason:"unchanged"};let u=ug(o);return await mo(s,["push","--force","origin",`${i}:${u}`],a_),Zd.set(s,l),await N(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:u,sha:i,wip:!0,...l_(r),ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${a}: pushed ${i.slice(0,7)} \u2192 ${u}`),{pushed:!0,sha:i,ref:u}}catch(i){return t.log?.(`[wip-sync] ${a}: push failed (will retry): ${String(i?.message||i).split(`
|
|
214
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function dg(e,{log:t=console,force:n=!1}={}){let r=[],o=[];for(let s of
|
|
215
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function pg(e,{log:t=console,ts:n=Date.now()}={}){let r=[],o=[];for(let s of
|
|
213
|
+
`)}`:t),this.name="MockRenderError"}},fs=null,zw=null;Gn=e=>At("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),Kn=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?Mc(e,r,"video"):r&&Mc(e,r,n)||(n==="html"?nn(e,t).html:null);return o?Lr("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Cc=e=>rt.join(e,".bostrat","mocks");W4=Buffer.from([137,80,78,71,13,10,26,10]);X4=Y4,Jw=Promise.resolve()});import t2 from"node:fs/promises";import $I from"node:path";import n2 from"node:os";import{randomUUID as RI}from"node:crypto";async function Sr(){return vr||(Qw||=(async()=>{let e=!0;try{vr=JSON.parse(await t2.readFile(OI,"utf8"))}catch{vr={flows:[]},e=!1}finally{Qw=null}if(vr.flows||=[],!e&&qe()){let{records:t,source:n}=await dt("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&vr.flows.push(r.data);vr.flows.length&&await Oa().catch(()=>{})}}return vr})(),Qw)}async function Oa(){await Mt(OI,vr)}function eg(e,t,n={}){return N({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 MI({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>TI)throw new Error(`steps must be an array of 1-${TI}`);let r=await Nc({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>II)throw new Error(`${l}: caption over ${II} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>_I)throw new Error(`${l}: title over ${_I} chars`);let p=String(c.mock??"").trim().replace(/^mock:/,"");if(!p)throw new Error(`${l}: mock (name or id) required`);let f=r.filter(b=>b.id===p);if(f.length||(f=r.filter(b=>b.name.toLowerCase()===p.toLowerCase())),!f.length)throw new Error(`${l}: no mock matching "${p}" in this Track \u2014 see \`bostrat-mock ls\``);if(f.length>1)throw new Error(`${l}: "${p}" is ambiguous (${f.map(b=>`mock:${b.id}`).join(", ")}) \u2014 use the id`);let w=f[0],m=null;if(c.v!=null&&(m=Number(c.v),!Number.isInteger(m)||!(w.versions||[]).some(b=>b.v===m)))throw new Error(`${l}: "${w.name}" has no captured v${c.v} to pin`);let g=o.findIndex(b=>b.mockId===w.id),y=g>=0?o.splice(g,1)[0].id:RI().slice(0,8);s.push({id:y,mockId:w.id,caption:u,title:d,v:m})}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 o2(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>EI)throw new Error(`name over ${EI} chars`);return e.trim()}async function CI({trackId:e}={}){let t=await Sr();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function s2(e){let n=(await Sr()).flows.find(r=>r.id===e);return n?{...n}:null}async function NI(e){let t=await s2(e);if(!t)return null;let n=new Map((await Nc({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=yn(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 PI({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=o2(t),i=await Sr(),c=i.flows.find(f=>f.trackId===e&&f.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 MI({trackId:e,steps:n}),d=Date.now(),p={id:RI().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:d}],steps:l,createdAt:d,updatedAt:d};return i.flows.push(p),await Oa(),await eg(p,"created"),Jd(p),{...p}}async function DI(e,{steps:t,note:n}={}){let o=(await Sr()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await MI({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 Oa(),await eg(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Jd(o),{...o}}async function BI(e){let t=await Sr(),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 Oa(),await eg(r,"deleted"),cn("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function jI(e){return(await Sr()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function LI(e){let n=(await Sr()).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(),Jd(r);return n.length&&await Oa(),n.length}async function FI(e){let t=await Sr(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await Oa();for(let r of n)cn("flows",r.id).catch(()=>{})}return n.length}async function UI(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await dt("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Sr();for(let r of n.flows)await Jd(r);return{pushed:n.flows.length,source:"plane"}}var r2,OI,TI,EI,II,_I,vr,Qw,Jd,tg=q(()=>{ye();jr();Ra();Fr();r2=process.env.BOSTRAT_STATE_DIR||$I.join(process.env.HOME||n2.tmpdir(),".bostrat"),OI=$I.join(r2,"flows.json"),TI=20,EI=80,II=500,_I=80,vr=null,Qw=null;Jd=e=>At("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import Er from"node:fs/promises";import Ir from"node:path";import a2 from"node:os";import{randomUUID as i2}from"node:crypto";function Pc(e){return c2.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function zn(){return Ar||(ng||=(async()=>{let e=!0;try{Ar=JSON.parse(await Er.readFile(WI,"utf8"))}catch{Ar={runbooks:[]},e=!1}finally{ng=null}return Ar.runbooks||=[],!e&&qe()&&await l2().catch(()=>{}),Ar})(),ng)}async function hs(){await Mt(WI,Ar)}function Xd(e){return Dc(At("runbooks",{...e},{trackId:e.trackId}))}async function l2(){let{records:e,source:t}=await dt("runbooks");if(!(t!=="plane"||!e.length)){await Er.mkdir(Yd,{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 Ei("runbooks",r.id,o.v,"md");typeof s=="string"&&await Er.writeFile(Tr(r,o.v),s).catch(()=>{})}Ar.runbooks.push(r)}Ar.runbooks.length&&await hs()}}function Ma(e,t,n={}){return N({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 Tr(e,t){return Ir.join(Yd,`${e.id}.v${t}.md`)}function qI(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 GI(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>HI)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${HI/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Pc(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 u2(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function d2({trackId:e,status:t}={}){let n=await zn(),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 KI({trackId:e,status:t}={}){let n=await zn(),r=await d2({trackId:e,status:t});if(!qe())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await dt("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 rg(e){let t=await p2(e);if(t)return t;if(!qe())return null;let{records:n,source:r}=await dt("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 VI(e,t){let n=await Ei("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function p2(e){let n=(await zn()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function zI({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=GI(r),c=await zn(),l={id:i2().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 Er.mkdir(Yd,{recursive:!0}),await Er.writeFile(Tr(l,1),r),l.versions.push({v:1,mdRel:Ir.relative(ms,Tr(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await hs(),await Ma(l,"created",{version:1}),Xd(l),Dc(Lr("runbooks",l.id,1,"md",Tr(l,1))),{...l}}async function JI(e,{md:t,note:n}={}){let o=(await zn()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");u2(o,"update");let s=GI(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Er.mkdir(Yd,{recursive:!0}),await Er.writeFile(Tr(o,a),t),o.versions.push({v:a,mdRel:Ir.relative(ms,Tr(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await hs(),await Ma(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Xd(o),Dc(Lr("runbooks",o.id,a,"md",Tr(o,a))),{...o}}async function YI(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await zn()).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 hs(),await Ma(r,t==="done"?"done":"reopened"),Xd(r),{...r})}async function XI(e){let t=await zn(),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 Er.rm(Ir.join(ms,o.mdRel),{force:!0}).catch(()=>{});return await hs(),r.trackDeletedAt||await Ma(r,"deleted"),Dc(cn("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function ZI(e){let n=(await zn()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Ma(r,"orphaned"),r.cwd=null,Xd(r);return n.length&&await hs(),n.length}async function QI(e){let t=await zn(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await Ma(r,"deleted");for(let o of r.versions||[])await Er.rm(Ir.join(ms,o.mdRel),{force:!0}).catch(()=>{});Dc(cn("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await hs()),n.length}async function e_(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await dt("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await zn();for(let r of n.runbooks){await At("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await Lr("runbooks",r.id,o.v,"md",Ir.join(ms,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var ms,WI,Yd,HI,c2,Ar,ng,Dc,Zd=q(()=>{jr();ye();Fr();ms=process.env.BOSTRAT_STATE_DIR||Ir.join(process.env.HOME||a2.tmpdir(),".bostrat"),WI=Ir.join(ms,"runbooks.json"),Yd=Ir.join(ms,"runbooks"),HI=256*1024,c2=[{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/}];Ar=null,ng=null;Dc=e=>Promise.resolve(e).catch(()=>{})});import og from"node:fs/promises";import Bc from"node:path";import{randomUUID as f2}from"node:crypto";function ag(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return m2.has(t)?"image":h2.has(t)?"doc":null}function w2(e){let r=Bc.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 r_(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>t_)throw new Error(`file too large \u2014 max ${Math.floor(t_/(1024*1024))} MB`);let r=w2(t),o=Bc.extname(r).slice(1),s=ag(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=f2().slice(0,8),i=sg(e);await og.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await og.writeFile(Bc.join(i,c),n,{mode:384}),await og.chmod(Bc.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${n_}/${c}`,kind:s,size:n.length}}function o_(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function ig(e){return e.role==="user"&&g2.test((e.text||"").trim())}var n_,sg,t_,m2,h2,g2,cg=q(()=>{n_=".bostrat/uploads",sg=e=>Bc.join(e,n_),t_=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,m2=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),h2=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"]);g2=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function ws(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}function lg(){let e=Number(process.env.BOSTRAT_LOOP_QUEUE_HOLD_MS);return Number.isFinite(e)&&e>0?e:y2}function k2(e,t,n=Date.now()){return e?((e.heldReason!==t||e.heldSince==null)&&(e.heldReason=t,e.heldSince=n),Math.max(0,n-e.heldSince)):0}function s_(e,t,{capMs:n=lg(),now:r=Date.now()}={}){if(!e)return!1;let o=k2(e,t,r);return e.delivery?.producer!=="loop"||!b2.has(t)?!1:o>=n}var y2,b2,a_=q(()=>{y2=5*6e4,b2=new Set(["busy","noSession"])});import{execFile as x2}from"node:child_process";import{promisify as v2}from"node:util";function ug(e){return`refs/bostrat/wip/${e}`}function T2(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function l_(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function E2(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:A2}function u_(e){let t=e?.workspace;return t?.kind==="multi"?(Array.isArray(t.repos)?t.repos:[]).filter(n=>n?.dir).map(n=>({dir:n.dir,name:n.name??null,repo:n.repo??null})):e?.cwd?[{dir:e.cwd,name:null,repo:t?.repo??null}]:[]}async function I2(e,{log:t=console,force:n=!1,checkout:r=null}={}){let o=e?.id,s=r?.dir??e?.cwd;if(!o||!s)return{pushed:!1,reason:"no-workspace"};let a=r?.name?`${o}/${r.name}`:o;try{await mo(s,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await mo(s,["remote","get-url","origin"])}catch{return i_.has(s)||(i_.add(s),t.log?.(`[wip-sync] ${a}: no origin remote \u2014 durable code fidelity is chat/memory-only for this checkout`)),{pushed:!1,reason:"no-origin"}}try{let i=await fa(s,"bostrat: wip snapshot"),{stdout:c}=await mo(s,["rev-parse",`${i}^{tree}`]),l=c.trim();if(!n&&Qd.get(s)===l)return{pushed:!1,reason:"unchanged"};let u=ug(o);return await mo(s,["push","--force","origin",`${i}:${u}`],c_),Qd.set(s,l),await N(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:u,sha:i,wip:!0,...d_(r),ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${a}: pushed ${i.slice(0,7)} \u2192 ${u}`),{pushed:!0,sha:i,ref:u}}catch(i){return t.log?.(`[wip-sync] ${a}: push failed (will retry): ${String(i?.message||i).split(`
|
|
214
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function dg(e,{log:t=console,force:n=!1}={}){let r=[],o=[];for(let s of u_(e)){let a=await I2(e,{log:t,force:n,checkout:s});a.pushed?r.push({name:s.name,repo:s.repo,ref:a.ref,sha:a.sha}):o.push({name:s.name,repo:s.repo,reason:a.reason})}return{pushed:r,skipped:o}}async function _2(e,{log:t=console,checkout:n=null,ts:r=Date.now()}={}){let o=e?.id,s=n?.dir??e?.cwd;if(!o||!s)return{pushed:!1,reason:"no-workspace"};let a=n?.name?`${o}/${n.name}`:o;try{await mo(s,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await mo(s,["remote","get-url","origin"])}catch{return{pushed:!1,reason:"no-origin"}}try{let i=await fa(s,"bostrat: transfer rescue snapshot"),{stdout:c}=await mo(s,["rev-parse",`${i}^{tree}`]),l=c.trim();if(Qd.get(s)===l)return{pushed:!1,reason:"unchanged"};let u=T2(o,r);return await mo(s,["push","origin",`${i}:${u}`],c_),Qd.set(s,l),await N(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:u,sha:i,rescue:!0,...d_(n),ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${a}: rescued stranded work ${i.slice(0,7)} \u2192 ${u}`),{pushed:!0,sha:i,ref:u}}catch(i){return t.log?.(`[wip-sync] ${a}: rescue push failed: ${String(i?.message||i).split(`
|
|
215
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function pg(e,{log:t=console,ts:n=Date.now()}={}){let r=[],o=[];for(let s of u_(e)){let a=await _2(e,{log:t,checkout:s,ts:n});a.pushed?r.push({name:s.name,repo:s.repo,ref:a.ref,sha:a.sha}):o.push({name:s.name,repo:s.repo,reason:a.reason})}return{pushed:r,skipped:o}}function p_({listTracks:e,sweepMs:t=E2(),log:n=console}){let r=!1,o=async()=>{if(!r){r=!0;try{let a=await e()??[];for(let i of a)Le(i)||i?.kind==="setup"||await dg(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 S2,A2,c_,mo,Qd,i_,d_,fg=q(()=>{Xu();ye();Zt();S2=v2(x2),A2=12e4,c_=6e4;mo=(e,t,n=15e3)=>S2("git",["-C",e,...t],{timeout:n,maxBuffer:4*1024*1024}),Qd=new Map,i_=new Set,d_=e=>e?.name||e?.repo?{name:e.name??null,repo:e.repo??null}:{}});import $2 from"node:fs";import mg from"node:fs/promises";import R2 from"node:path";function N2(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function f_(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(!N2(t))return{restored:!1,reason:"plane not configured"};let s=Hs(e),a=R2.join(s,`${o}.ndjson`);try{if($2.statSync(a).size>0)return{restored:!1,reason:"log not empty"}}catch{}let i=Ne(t),c={authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`},l=[],u=0;for(;;){let p=new AbortController,f=setTimeout(()=>p.abort(),C2),w;try{w=await n(`${i}/v1/spine/${encodeURIComponent(o)}?after=${u}&limit=${O2}`,{headers:c,signal:p.signal})}finally{clearTimeout(f)}if(w.status===404)return{restored:!1,reason:"unknown to plane"};if(!w.ok)return{restored:!1,reason:`plane ${w.status}`};let m=await w.json();for(let g of m.events||[])l.push(JSON.stringify(g));if(l.length>M2)return{restored:!1,reason:"event count over safety cap"};if(m.next==null)break;u=m.next}if(!l.length)return{restored:!1,reason:"unknown to plane"};await mg.mkdir(s,{recursive:!0});let d=`${a}.pull-${process.pid}-${Date.now()}`;return await mg.writeFile(d,l.join(`
|
|
216
216
|
`)+`
|
|
217
|
-
`),await mg.rename(d,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var _2,$2,R2,p_=q(()=>{yt();ye();_2=1e3,$2=5e5,R2=3e4});function f_(e,t=Date.now()){let n=e.find(s=>s.reconcile||s.ciFix);if(n?.reconcile)return{status:"working",since:n.reconcile.delegatedAt,summary:"reconciling the branch"};if(n?.ciFix)return{status:"working",since:n.ciFix.delegatedAt,summary:"repairing failed checks"};let r=e.find(s=>s.reconcileAwait||s.ciFixAwait);if(r?.reconcileAwait)return{status:"waiting",since:r.reconcileAwait.since,summary:"fix pushed \u2014 waiting for mergeability"};if(r?.ciFixAwait)return{status:"waiting",since:r.ciFixAwait.since,summary:"fix pushed \u2014 waiting for CI"};let o=e.filter(s=>s.autoMerge||s.armedAt);return o.length?{status:"waiting",since:Math.min(...o.map(s=>s.autoMerge?.armedAt||s.armedAt||t)),count:o.length,summary:o.length===1?"auto-merge armed \u2014 lands when checks pass":`auto-merge armed on ${o.length} PRs`}:null}function m_(e){let t=(e||[]).filter(n=>!M2.has(n.state)&&!n.ciFix&&!n.reconcile&&!n.ciFixAwait&&!n.reconcileAwait&&!n.autoMerge&&!n.armedAt);return t.length?{count:t.length,since:Math.min(...t.map(n=>n.announcedAt||n.firstSeen||Date.now())),summary:t.length===1?`PR #${t[0].number} is open \u2014 review, merge or arm it`:`${t.length} open PRs waiting on you`}:null}function h_({archivedAt:e=null,archiving:t=!1,prompted:n=!1,openActions:r=0,openActionSince:o=null,loopAttention:s=!1,prDecisions:a=null,operation:i=null,phase:c=null,working:l=!1,workingSince:u=null,pending:d=null,completedTurnAt:p=null,live:f=!1,now:w=Date.now()}={}){return e&&!t?bn("archived","lifecycle",e):n||r>0||s?bn("needs-you",n?"pane":s?"loop":"obligations",o||w,{count:r||(n||s?1:null)}):t?bn("archiving","lifecycle",e||w):a?.count?bn("needs-you","pr",a.since,{count:a.count,summary:a.summary??null}):i?.status==="working"?bn("working","operation",i.since,{summary:i.summary??null,count:i.count??null}):(l||n)&&c?.phase?bn("working","agent-phase",c.at,{summary:c.summary??null}):l?bn("working","pane",u||w):i?.status==="waiting"?bn("waiting","operation",i.since,{summary:i.summary??null,count:i.count??null}):d?.count?bn("waiting",d.source||"pending",d.since,{summary:d.summary??null,count:d.count}):p&&f?bn("settled","obligations",p,{summary:"no open actions"}):bn("idle","fallback",p||w)}var bn,M2,w_=q(()=>{bn=(e,t,n,r={})=>({kind:e,source:t,since:n||0,...r});M2=new Set(["merged","closed","lost"])});function C2(e,t){let n=0;for(let o of e||[]){if(o?.type!=="track.obligations.changed")continue;let s=Number(o.payload?.open);n=Number.isFinite(s)&&s>0?Math.floor(s):0}let r=Number(t?.openActions)||0;return r===n?null:{open:r,since:r?t.openActionSince??null:null}}async function N2(e,t,n){let r=C2(t,n);return r?N(e,{type:"track.obligations.changed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),open:r.open,since:r.since}}).catch(()=>null):null}async function hg(e){if(Qd.has(e.id))return Qd.get(e.id);let t=await G(e).catch(()=>[]),n=[];try{n=Nn(t)}catch{}let r=Cu(n),o=n.filter(f=>f?.kind==="followup-offer"&&f.status==="open"),s=new Set(t.filter(f=>f.type==="prompt.answered").map(f=>f.payload?.promptId)),a=t.filter(f=>f.type==="prompt.raised"&&f.payload?.promptId&&!s.has(f.payload.promptId)),i=[...r,...o,...a.map(f=>({ts:f.payload?.ts??f.ts}))],c=null;for(let f of t){if(f.type!=="track.phase.changed")continue;let w=f.payload||{};c=w.phase?{phase:w.phase,summary:w.summary??null,scope:w.scope??null,phaseId:w.phaseId??null,at:w.ts??f.ts}:null}let l=n.flatMap(f=>f?.kind==="job-dock"?f.jobs||[]:[]).filter(f=>f?.status==="running"),u=t.reduce((f,w)=>w.type==="turn.agent"?Math.max(f,w.payload?.ts??w.ts??0):f,0)||null,d=t.reduce((f,w)=>w.type==="turn.user"?Math.max(f,w.payload?.ts??w.ts??0):f,0)||null,p={openActions:i.length,openActionSince:i.length?Math.min(...i.map(f=>f.ts||Date.now())):null,phase:c,completedTurnAt:u,currentTurnAt:d,runningJobs:l.length,runningJobsSince:l.length?Math.min(...l.map(f=>f.startedTs||Date.now())):null};return Qd.set(e.id,p),!Le(e)&&e.kind!=="setup"&&N2(e,t,p),p}function g_({trackOf:e,debounceMs:t=P2,log:n=console}={}){if(typeof e!="function")throw new Error("watchObligations: trackOf(id) is required");let r=o=>{let s=o?.trackId;if(!s)return;clearTimeout(Bc.get(s));let a=setTimeout(()=>{Bc.delete(s),Promise.resolve(e(s)).then(i=>i&&!Le(i)&&i.kind!=="setup"?hg(i):null).catch(i=>n.warn?.(`[obligations] fold ${s} failed: ${i?.message||i}`))},t);a.unref?.(),Bc.set(s,a)};return Xe.on("event",r),()=>{Xe.off("event",r);for(let o of Bc.values())clearTimeout(o);Bc.clear()}}var Qd,P2,Bc,wg=q(()=>{ye();Nu();Zt();Qd=new Map;Xe.on("event",e=>{e?.trackId&&Qd.delete(e.trackId)});P2=750,Bc=new Map});import gg from"node:fs/promises";async function B2(e){let t;try{t=await gg.readFile(`/proc/${e}/stat`,"utf8")}catch{return null}let n=t.lastIndexOf(")");if(n<0)return null;let r=t.slice(n+2).split(" "),o=Number(r[1]),s=Number(r[11])+Number(r[12]);return!Number.isFinite(o)||!Number.isFinite(s)?null:{ppid:o,cpuMs:s/D2*1e3,ioBytes:await j2(e)}}async function j2(e){let t;try{t=await gg.readFile(`/proc/${e}/io`,"utf8")}catch{return 0}let n=r=>Number(t.match(new RegExp(`^${r}: (\\d+)$`,"m"))?.[1]||0);return n("rchar")+n("wchar")}async function y_(e){let t=[...new Set((e||[]).map(Number).filter(l=>Number.isFinite(l)&&l>0))];if(!t.length)return{procs:0,cpuMs:0,ioBytes:0};let n;try{n=await gg.readdir("/proc")}catch{return null}let r=new Map;await Promise.all(n.map(async l=>{if(!/^\d+$/.test(l))return;let u=await B2(l);u&&r.set(Number(l),u)}));let o=new Map;for(let[l,u]of r)o.has(u.ppid)||o.set(u.ppid,[]),o.get(u.ppid).push(l);let s=new Set,a=t.filter(l=>r.has(l));for(let l of a)s.add(l);let i=0,c=0;for(let l=0;l<a.length;l++){let u=a[l];i+=r.get(u).cpuMs,c+=r.get(u).ioBytes;for(let d of o.get(u)||[])s.has(d)||(s.add(d),a.push(d))}return{procs:s.size,cpuMs:Math.round(i),ioBytes:c}}var D2,b_=q(()=>{D2=100});import L2 from"node:fs/promises";function xg(e){let t=[];for(let n of e||[])if(!(!n||n.archivedAt&&!n.archiving||n.transferredTo))for(let r of n.boxes||[])r?.state==="live"&&r.name&&t.push({track:n,box:r});return t}async function vg(e,t,n){let r=null;try{r=await n.updatedAt(e)}catch{r=null}let o=k_.get(e.name);if(o&&o.mtime===r&&t-o.at<q2)return o.jobs;let s=[];try{s=await n.readOpenJobs(e)}catch{s=[]}return k_.set(e.name,{at:t,mtime:r,jobs:s}),s}async function G2(e,t,n){let r=await n.panePids(e.name),o=r.length?await n.treeStats(r):null,s=null;try{s=await n.updatedAt(e)}catch{s=null}return{at:t,cpuMs:o?.cpuMs??null,ioBytes:o?.ioBytes??null,procs:o?.procs??null,mtime:s}}async function K2(e,t){return e?.outputFile?await t.statFile(e.outputFile):null}function V2(e,t){if(!e)return!0;let n=Math.max(1,(t.at-e.at)/1e3);return t.mtime!=null&&e.mtime!=null&&t.mtime>e.mtime||t.procs!=null&&e.procs!=null&&t.procs>e.procs||t.cpuMs!=null&&e.cpuMs!=null&&(t.cpuMs-e.cpuMs)/n>=H2||t.ioBytes!=null&&e.ioBytes!=null&&(t.ioBytes-e.ioBytes)/n>=W2}function x_(e,t,n,r){let o=[];for(let s of n){let a=kg(e.id,t.name,s.id),i=kn.get(a);i?i.job=s:kn.set(a,{trackId:e.id,boxName:t.name,agent:t.agent||null,job:s,firstSeenAt:r,quiet:0,stalled:!1}),o.push(a)}return o}async function v_(e,{now:t=Date.now(),deps:n=bg}={}){let o=(await Promise.all(xg([e]).map(({box:s})=>vg(s,t,n)))).flat();return o.length?{source:"jobs",count:o.length,since:Math.min(...o.map(s=>s.ts||t)),summary:o.length===1?"a background job is still running":`${o.length} background jobs are still running`}:null}async function S_(e,{now:t=Date.now(),deps:n=bg}={}){let r=new Map,o=new Set;for(let{track:s,box:a}of xg(e)){let i=await vg(a,t,n);for(let l of i)o.add(kg(s.id,a.name,l.id));let c=x_(s,a,i,t).filter(l=>!kn.get(l).stalled).map(l=>kn.get(l).job);c.length&&r.set(s.id,[...r.get(s.id)??[],...c])}for(let s of[...kn.keys()])o.has(s)||kn.delete(s);return r}async function z2(e,{now:t=Date.now(),deps:n=bg}={}){let r={checked:0,progressing:0,quiet:0,stalled:[]},o=xg(e),s=new Set(o.map(({box:a})=>a.name));for(let[a,i]of[...kn])s.has(i.boxName)||kn.delete(a);for(let{track:a,box:i}of o){let c=await vg(i,t,n),l=new Set(c.map(w=>kg(a.id,i.name,w.id)));for(let[w,m]of[...kn])m.boxName===i.name&&!l.has(w)&&kn.delete(w);if(!c.length){yg.delete(i.name);continue}let u=x_(a,i,c,t),d=await G2(i,t,n),p=yg.get(i.name);yg.set(i.name,d);let f=V2(p,d);for(let w of u){let m=kn.get(w);r.checked++;let g=await K2(m.job,n),y=!!g&&!!m.file&&(g.size>m.file.size||g.mtimeMs>m.file.mtimeMs),b=!!g&&!m.file;if(m.file=g??m.file,f||y||b){m.quiet=0,m.lastProgressAt=t,m.stalled=!1,r.progressing++;continue}if(m.stalled||(m.quiet++,r.quiet++,m.quiet<U2))continue;m.stalled=!0,m.stalledAt=t,r.stalled.push(w);let S=t-(m.lastProgressAt??m.firstSeenAt);try{await n.appendEvent(a,{type:"job.stalled",actor:"orchestrator",source:"orchestrator",boxId:i.name,provider:i.agent||null,payload:{id:m.job.id,kind:m.job.kind||"agent",name:m.job.name||null,detail:m.job.detail||null,startedTs:m.job.ts??m.firstSeenAt,quietMs:S,checks:m.quiet}})}catch{}console.log(`[job-watch] ${a.id}/${i.name} job ${m.job.id} (${m.job.name||m.job.kind}) quiet for ${Math.round(S/6e4)}m over ${m.quiet} check(s) \u2014 released; the node may sleep`)}}return r}function A_({listTracks:e,intervalMs:t=F2}={}){let n=!1,o=setInterval(async()=>{if(!n){n=!0;try{await z2(await e())}catch(s){console.error(`[job-watch] sweep: ${s?.message||s}`)}finally{n=!1}}},t);return o.unref?.(),o}var F2,U2,H2,W2,q2,bg,kn,k_,yg,kg,Sg=q(()=>{ye();uh();ht();b_();F2=Number(process.env.BOSTRAT_JOB_CHECK_MS)||15*6e4,U2=Number(process.env.BOSTRAT_JOB_STALL_CHECKS)||3,H2=Number(process.env.BOSTRAT_JOB_CPU_RATE_MIN)||25,W2=Number(process.env.BOSTRAT_JOB_IO_RATE_MIN)||64*1024,q2=5*6e4,bg={readOpenJobs:mS,statFile:async e=>{try{let t=await L2.stat(e);return{size:t.size,mtimeMs:Math.round(t.mtimeMs)}}catch{return null}},updatedAt:e=>Zr(e.agent).updatedAt(e),panePids:O0,treeStats:y_,appendEvent:N},kn=new Map,k_=new Map,yg=new Map,kg=(e,t,n)=>`${e}|${t}|${n}`});import{randomUUID as J2}from"node:crypto";async function tp(e,t,n={}){let r={operationId:J2(),kind:t,startedAt:Date.now(),...n};return ep.set(e.id,r),await N(e,{type:"track.operation.changed",actor:"orchestrator",source:"orchestrator",payload:{ts:r.startedAt,state:"started",...r}}).catch(()=>{}),r}async function Ag(e,t,n="completed",r=null){ep.get(e.id)?.operationId===t?.operationId&&ep.delete(e.id),await N(e,{type:"track.operation.changed",actor:"orchestrator",source:"orchestrator",payload:{ts:Date.now(),state:"finished",operationId:t?.operationId,kind:t?.kind,outcome:n,...r?{error:String(r)}:{}}}).catch(()=>{})}var ep,T_,Tg=q(()=>{ye();ep=new Map;T_=e=>ep.get(e)||null});var o$={};Ef(o$,{HANDOFF_LATEST_REL:()=>go,KNOWN_AGENTS:()=>Jc,TRACK_NAME_MAX:()=>Bg,addTrackRepo:()=>gp,adoptTrack:()=>by,agentAvailability:()=>pp,announceNodeUpdate:()=>Yn,answerFullscreenOffer:()=>py,answerPrompt:()=>mp,archiveTrack:()=>ks,archiveTrackForMergedPr:()=>hr,augmentFirstPromptAfterHandoff:()=>P_,buildAgentCmd:()=>jg,canResumeSubtask:()=>t$,cancelQueued:()=>Xc,cancelQueuedToDraft:()=>Kg,cancelStopJob:()=>Ua,cancelSubtask:()=>_p,changedRepos:()=>ps,commitTransferOut:()=>Tp,createSetupTrack:()=>oy,createTrack:()=>bp,deleteTrack:()=>ay,demoteTransferredTracks:()=>yy,ensureLeadEpochs:()=>wo,forceMergeSubtask:()=>vy,getHandoffJob:()=>Wa,getReshapeJob:()=>Zc,getSubtaskJob:()=>cW,getTrack:()=>de,getTransferOutJob:()=>wy,healDeadLead:()=>Vc,interruptLead:()=>wp,isArchiving:()=>zH,isHandoffNoteEcho:()=>Mg,isInterruptEcho:()=>Cg,isModelSwitchEcho:()=>Ng,leadCliDead:()=>cy,listSubtaskJobs:()=>vs,listTracks:()=>ke,matchTranscriptsToBoxes:()=>L_,mergeNotifyMessage:()=>X_,navigatePrompt:()=>qg,notifyTrackForMergedPr:()=>wr,notifyTrackForResolvedPr:()=>kp,offerFollowupsForMergedPr:()=>xs,pendingOffers:()=>Hg,queueGauges:()=>Gg,queueShipCompleteNotify:()=>zg,queuedMessages:()=>gs,rawTrack:()=>Ug,reapArchivedOrphans:()=>dy,reconcileDemotedTracks:()=>uy,reconcileHandoffOutcome:()=>Z_,reconcileInterruptedHandoffs:()=>my,reconcileInterruptedReshapes:()=>ny,reconcileInterruptedSubtasks:()=>ky,reconcileNodeUpdateNotices:()=>Fg,reconcileSubtaskOutcome:()=>e$,refreshStandingInstructions:()=>ry,refuseIfTransferred:()=>Ap,rehydrateLead:()=>Qc,removeTrackRepo:()=>yp,renameTrack:()=>ly,reposOf:()=>jt,reshapeTrack:()=>ty,resolveTrackBox:()=>kc,resolveTrackBoxByHookToken:()=>Yc,restartLead:()=>vp,resumeAgentCmd:()=>dp,sayToLead:()=>qt,sendOptionsForAgent:()=>Cn,setTrackDraft:()=>Xg,setTrackModel:()=>Zg,startLeadHandoff:()=>Sp,startSpinUpView:()=>Jg,startSubtask:()=>xy,startTransferOut:()=>gy,stopJobActive:()=>H_,subtaskMessages:()=>Wg,trackMessages:()=>fp,unarchiveTrack:()=>xp});import z from"node:fs/promises";import V from"node:path";import{randomUUID as cp}from"node:crypto";import{execFile as Y2}from"node:child_process";import{promisify as X2}from"node:util";function jg(e,{skipPermissions:t=!0,model:n=null}={}){let r=up[e];if(!r)throw new Error(`unknown agent "${e}"`);let o=process.env[r.envVar];if(o)return o;let s=t&&r.skipFlag?`${r.base} ${r.skipFlag}`:r.base;if(n&&n!=="default"){let a=Nh(e).find(i=>i.alias===n);if(!a)throw new Error(`unknown ${e} launch model "${n}"`);s+=` --model ${a.alias}`}return s}function dp(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=up[e];if(!s)throw new Error(`unknown agent "${e}"`);let a=process.env[s.envVar];if(a)return a;let i=n&&s.skipFlag?` ${s.skipFlag}`:"";switch(e){case"claude":{if(!t)return`${s.base}${i}`;if(r&&!dn(r,t)){let c=o&&Be(o)?` --name ${o}`:"";return`${s.base} --session-id ${t}${c}${i}`}return`${s.base} --resume ${t}${i}`}case"cursor":return`${s.base} --continue${i}`;case"codex":return`${s.base} resume --last${i}`;default:return`${s.base}${i}`}}function bs(e){let t=up[e];if(t)return process.env[t.envVar]||t.base}async function pp({maxAgeMs:e=6e4}={}){if(ho.value&&Date.now()-ho.at<e)return ho.value;let t=ho.value,n=await Promise.all(Jc.map(async r=>{let o=await mu(bs(r),{retries:[]});return o===zs?[r,t?.[r]??!0]:[r,o===fu]}));return ho.value=Object.fromEntries(n),ho.at=Date.now(),ho.value}async function Fa(e,t=!0,n=null){let r=jg(e,{skipPermissions:t,model:n});if(await mu(r)===Vs){ho.at=0;let o=r.split(/\s+/)[0],s=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.":rm[o]?`Install it on this node: ${rm[o]}`:"Its CLI must be installed and on PATH first.";throw new Error(`agent "${e}" isn't available on this box (command not found: ${o}). ${s}`)}return r}function P_(e,t=go){return`${e.trim()}
|
|
217
|
+
`),await mg.rename(d,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var O2,M2,C2,m_=q(()=>{yt();ye();O2=1e3,M2=5e5,C2=3e4});function h_(e,t=Date.now()){let n=e.find(s=>s.reconcile||s.ciFix);if(n?.reconcile)return{status:"working",since:n.reconcile.delegatedAt,summary:"reconciling the branch"};if(n?.ciFix)return{status:"working",since:n.ciFix.delegatedAt,summary:"repairing failed checks"};let r=e.find(s=>s.reconcileAwait||s.ciFixAwait);if(r?.reconcileAwait)return{status:"waiting",since:r.reconcileAwait.since,summary:"fix pushed \u2014 waiting for mergeability"};if(r?.ciFixAwait)return{status:"waiting",since:r.ciFixAwait.since,summary:"fix pushed \u2014 waiting for CI"};let o=e.filter(s=>s.autoMerge||s.armedAt);return o.length?{status:"waiting",since:Math.min(...o.map(s=>s.autoMerge?.armedAt||s.armedAt||t)),count:o.length,summary:o.length===1?"auto-merge armed \u2014 lands when checks pass":`auto-merge armed on ${o.length} PRs`}:null}function w_(e){let t=(e||[]).filter(n=>!P2.has(n.state)&&!n.ciFix&&!n.reconcile&&!n.ciFixAwait&&!n.reconcileAwait&&!n.autoMerge&&!n.armedAt);return t.length?{count:t.length,since:Math.min(...t.map(n=>n.announcedAt||n.firstSeen||Date.now())),summary:t.length===1?`PR #${t[0].number} is open \u2014 review, merge or arm it`:`${t.length} open PRs waiting on you`}:null}function g_({archivedAt:e=null,archiving:t=!1,prompted:n=!1,openActions:r=0,openActionSince:o=null,loopAttention:s=!1,prDecisions:a=null,operation:i=null,phase:c=null,working:l=!1,workingSince:u=null,pending:d=null,completedTurnAt:p=null,live:f=!1,now:w=Date.now()}={}){return e&&!t?bn("archived","lifecycle",e):n||r>0||s?bn("needs-you",n?"pane":s?"loop":"obligations",o||w,{count:r||(n||s?1:null)}):t?bn("archiving","lifecycle",e||w):a?.count?bn("needs-you","pr",a.since,{count:a.count,summary:a.summary??null}):i?.status==="working"?bn("working","operation",i.since,{summary:i.summary??null,count:i.count??null}):(l||n)&&c?.phase?bn("working","agent-phase",c.at,{summary:c.summary??null}):l?bn("working","pane",u||w):i?.status==="waiting"?bn("waiting","operation",i.since,{summary:i.summary??null,count:i.count??null}):d?.count?bn("waiting",d.source||"pending",d.since,{summary:d.summary??null,count:d.count}):p&&f?bn("settled","obligations",p,{summary:"no open actions"}):bn("idle","fallback",p||w)}var bn,P2,y_=q(()=>{bn=(e,t,n,r={})=>({kind:e,source:t,since:n||0,...r});P2=new Set(["merged","closed","lost"])});function D2(e,t){let n=0;for(let o of e||[]){if(o?.type!=="track.obligations.changed")continue;let s=Number(o.payload?.open);n=Number.isFinite(s)&&s>0?Math.floor(s):0}let r=Number(t?.openActions)||0;return r===n?null:{open:r,since:r?t.openActionSince??null:null}}async function B2(e,t,n){let r=D2(t,n);return r?N(e,{type:"track.obligations.changed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),open:r.open,since:r.since}}).catch(()=>null):null}async function hg(e){if(ep.has(e.id))return ep.get(e.id);let t=await G(e).catch(()=>[]),n=[];try{n=Nn(t)}catch{}let r=Nu(n),o=n.filter(f=>f?.kind==="followup-offer"&&f.status==="open"),s=new Set(t.filter(f=>f.type==="prompt.answered").map(f=>f.payload?.promptId)),a=t.filter(f=>f.type==="prompt.raised"&&f.payload?.promptId&&!s.has(f.payload.promptId)),i=[...r,...o,...a.map(f=>({ts:f.payload?.ts??f.ts}))],c=null;for(let f of t){if(f.type!=="track.phase.changed")continue;let w=f.payload||{};c=w.phase?{phase:w.phase,summary:w.summary??null,scope:w.scope??null,phaseId:w.phaseId??null,at:w.ts??f.ts}:null}let l=n.flatMap(f=>f?.kind==="job-dock"?f.jobs||[]:[]).filter(f=>f?.status==="running"),u=t.reduce((f,w)=>w.type==="turn.agent"?Math.max(f,w.payload?.ts??w.ts??0):f,0)||null,d=t.reduce((f,w)=>w.type==="turn.user"?Math.max(f,w.payload?.ts??w.ts??0):f,0)||null,p={openActions:i.length,openActionSince:i.length?Math.min(...i.map(f=>f.ts||Date.now())):null,phase:c,completedTurnAt:u,currentTurnAt:d,runningJobs:l.length,runningJobsSince:l.length?Math.min(...l.map(f=>f.startedTs||Date.now())):null};return ep.set(e.id,p),!Le(e)&&e.kind!=="setup"&&B2(e,t,p),p}function b_({trackOf:e,debounceMs:t=j2,log:n=console}={}){if(typeof e!="function")throw new Error("watchObligations: trackOf(id) is required");let r=o=>{let s=o?.trackId;if(!s)return;clearTimeout(jc.get(s));let a=setTimeout(()=>{jc.delete(s),Promise.resolve(e(s)).then(i=>i&&!Le(i)&&i.kind!=="setup"?hg(i):null).catch(i=>n.warn?.(`[obligations] fold ${s} failed: ${i?.message||i}`))},t);a.unref?.(),jc.set(s,a)};return Xe.on("event",r),()=>{Xe.off("event",r);for(let o of jc.values())clearTimeout(o);jc.clear()}}var ep,j2,jc,wg=q(()=>{ye();Pu();Zt();ep=new Map;Xe.on("event",e=>{e?.trackId&&ep.delete(e.trackId)});j2=750,jc=new Map});import gg from"node:fs/promises";async function F2(e){let t;try{t=await gg.readFile(`/proc/${e}/stat`,"utf8")}catch{return null}let n=t.lastIndexOf(")");if(n<0)return null;let r=t.slice(n+2).split(" "),o=Number(r[1]),s=Number(r[11])+Number(r[12]);return!Number.isFinite(o)||!Number.isFinite(s)?null:{ppid:o,cpuMs:s/L2*1e3,ioBytes:await U2(e)}}async function U2(e){let t;try{t=await gg.readFile(`/proc/${e}/io`,"utf8")}catch{return 0}let n=r=>Number(t.match(new RegExp(`^${r}: (\\d+)$`,"m"))?.[1]||0);return n("rchar")+n("wchar")}async function k_(e){let t=[...new Set((e||[]).map(Number).filter(l=>Number.isFinite(l)&&l>0))];if(!t.length)return{procs:0,cpuMs:0,ioBytes:0};let n;try{n=await gg.readdir("/proc")}catch{return null}let r=new Map;await Promise.all(n.map(async l=>{if(!/^\d+$/.test(l))return;let u=await F2(l);u&&r.set(Number(l),u)}));let o=new Map;for(let[l,u]of r)o.has(u.ppid)||o.set(u.ppid,[]),o.get(u.ppid).push(l);let s=new Set,a=t.filter(l=>r.has(l));for(let l of a)s.add(l);let i=0,c=0;for(let l=0;l<a.length;l++){let u=a[l];i+=r.get(u).cpuMs,c+=r.get(u).ioBytes;for(let d of o.get(u)||[])s.has(d)||(s.add(d),a.push(d))}return{procs:s.size,cpuMs:Math.round(i),ioBytes:c}}var L2,x_=q(()=>{L2=100});import H2 from"node:fs/promises";function xg(e){let t=[];for(let n of e||[])if(!(!n||n.archivedAt&&!n.archiving||n.transferredTo))for(let r of n.boxes||[])r?.state==="live"&&r.name&&t.push({track:n,box:r});return t}async function vg(e,t,n){let r=null;try{r=await n.updatedAt(e)}catch{r=null}let o=v_.get(e.name);if(o&&o.mtime===r&&t-o.at<V2)return o.jobs;let s=[];try{s=await n.readOpenJobs(e)}catch{s=[]}return v_.set(e.name,{at:t,mtime:r,jobs:s}),s}async function z2(e,t,n){let r=await n.panePids(e.name),o=r.length?await n.treeStats(r):null,s=null;try{s=await n.updatedAt(e)}catch{s=null}return{at:t,cpuMs:o?.cpuMs??null,ioBytes:o?.ioBytes??null,procs:o?.procs??null,mtime:s}}async function J2(e,t){return e?.outputFile?await t.statFile(e.outputFile):null}function Y2(e,t){if(!e)return!0;let n=Math.max(1,(t.at-e.at)/1e3);return t.mtime!=null&&e.mtime!=null&&t.mtime>e.mtime||t.procs!=null&&e.procs!=null&&t.procs>e.procs||t.cpuMs!=null&&e.cpuMs!=null&&(t.cpuMs-e.cpuMs)/n>=G2||t.ioBytes!=null&&e.ioBytes!=null&&(t.ioBytes-e.ioBytes)/n>=K2}function S_(e,t,n,r){let o=[];for(let s of n){let a=kg(e.id,t.name,s.id),i=kn.get(a);i?i.job=s:kn.set(a,{trackId:e.id,boxName:t.name,agent:t.agent||null,job:s,firstSeenAt:r,quiet:0,stalled:!1}),o.push(a)}return o}async function A_(e,{now:t=Date.now(),deps:n=bg}={}){let o=(await Promise.all(xg([e]).map(({box:s})=>vg(s,t,n)))).flat();return o.length?{source:"jobs",count:o.length,since:Math.min(...o.map(s=>s.ts||t)),summary:o.length===1?"a background job is still running":`${o.length} background jobs are still running`}:null}async function T_(e,{now:t=Date.now(),deps:n=bg}={}){let r=new Map,o=new Set;for(let{track:s,box:a}of xg(e)){let i=await vg(a,t,n);for(let l of i)o.add(kg(s.id,a.name,l.id));let c=S_(s,a,i,t).filter(l=>!kn.get(l).stalled).map(l=>kn.get(l).job);c.length&&r.set(s.id,[...r.get(s.id)??[],...c])}for(let s of[...kn.keys()])o.has(s)||kn.delete(s);return r}async function X2(e,{now:t=Date.now(),deps:n=bg}={}){let r={checked:0,progressing:0,quiet:0,stalled:[]},o=xg(e),s=new Set(o.map(({box:a})=>a.name));for(let[a,i]of[...kn])s.has(i.boxName)||kn.delete(a);for(let{track:a,box:i}of o){let c=await vg(i,t,n),l=new Set(c.map(w=>kg(a.id,i.name,w.id)));for(let[w,m]of[...kn])m.boxName===i.name&&!l.has(w)&&kn.delete(w);if(!c.length){yg.delete(i.name);continue}let u=S_(a,i,c,t),d=await z2(i,t,n),p=yg.get(i.name);yg.set(i.name,d);let f=Y2(p,d);for(let w of u){let m=kn.get(w);r.checked++;let g=await J2(m.job,n),y=!!g&&!!m.file&&(g.size>m.file.size||g.mtimeMs>m.file.mtimeMs),b=!!g&&!m.file;if(m.file=g??m.file,f||y||b){m.quiet=0,m.lastProgressAt=t,m.stalled=!1,r.progressing++;continue}if(m.stalled||(m.quiet++,r.quiet++,m.quiet<q2))continue;m.stalled=!0,m.stalledAt=t,r.stalled.push(w);let S=t-(m.lastProgressAt??m.firstSeenAt);try{await n.appendEvent(a,{type:"job.stalled",actor:"orchestrator",source:"orchestrator",boxId:i.name,provider:i.agent||null,payload:{id:m.job.id,kind:m.job.kind||"agent",name:m.job.name||null,detail:m.job.detail||null,startedTs:m.job.ts??m.firstSeenAt,quietMs:S,checks:m.quiet}})}catch{}console.log(`[job-watch] ${a.id}/${i.name} job ${m.job.id} (${m.job.name||m.job.kind}) quiet for ${Math.round(S/6e4)}m over ${m.quiet} check(s) \u2014 released; the node may sleep`)}}return r}function E_({listTracks:e,intervalMs:t=W2}={}){let n=!1,o=setInterval(async()=>{if(!n){n=!0;try{await X2(await e())}catch(s){console.error(`[job-watch] sweep: ${s?.message||s}`)}finally{n=!1}}},t);return o.unref?.(),o}var W2,q2,G2,K2,V2,bg,kn,v_,yg,kg,Sg=q(()=>{ye();uh();ht();x_();W2=Number(process.env.BOSTRAT_JOB_CHECK_MS)||15*6e4,q2=Number(process.env.BOSTRAT_JOB_STALL_CHECKS)||3,G2=Number(process.env.BOSTRAT_JOB_CPU_RATE_MIN)||25,K2=Number(process.env.BOSTRAT_JOB_IO_RATE_MIN)||64*1024,V2=5*6e4,bg={readOpenJobs:wS,statFile:async e=>{try{let t=await H2.stat(e);return{size:t.size,mtimeMs:Math.round(t.mtimeMs)}}catch{return null}},updatedAt:e=>Zr(e.agent).updatedAt(e),panePids:C0,treeStats:k_,appendEvent:N},kn=new Map,v_=new Map,yg=new Map,kg=(e,t,n)=>`${e}|${t}|${n}`});import{randomUUID as Z2}from"node:crypto";async function np(e,t,n={}){let r={operationId:Z2(),kind:t,startedAt:Date.now(),...n};return tp.set(e.id,r),await N(e,{type:"track.operation.changed",actor:"orchestrator",source:"orchestrator",payload:{ts:r.startedAt,state:"started",...r}}).catch(()=>{}),r}async function Ag(e,t,n="completed",r=null){tp.get(e.id)?.operationId===t?.operationId&&tp.delete(e.id),await N(e,{type:"track.operation.changed",actor:"orchestrator",source:"orchestrator",payload:{ts:Date.now(),state:"finished",operationId:t?.operationId,kind:t?.kind,outcome:n,...r?{error:String(r)}:{}}}).catch(()=>{})}var tp,I_,Tg=q(()=>{ye();tp=new Map;I_=e=>tp.get(e)||null});var i$={};If(i$,{HANDOFF_LATEST_REL:()=>go,KNOWN_AGENTS:()=>Yc,TRACK_NAME_MAX:()=>jg,addTrackRepo:()=>yp,adoptTrack:()=>xy,agentAvailability:()=>fp,announceNodeUpdate:()=>Yn,answerFullscreenOffer:()=>my,answerPrompt:()=>hp,archiveTrack:()=>ks,archiveTrackForMergedPr:()=>hr,augmentFirstPromptAfterHandoff:()=>B_,buildAgentCmd:()=>Lg,canResumeSubtask:()=>o$,cancelQueued:()=>Zc,cancelQueuedToDraft:()=>Vg,cancelStopJob:()=>Ua,cancelSubtask:()=>$p,changedRepos:()=>ps,commitTransferOut:()=>Ep,createSetupTrack:()=>sy,createTrack:()=>kp,deleteTrack:()=>iy,demoteTransferredTracks:()=>ky,ensureLeadEpochs:()=>wo,forceMergeSubtask:()=>Ay,getHandoffJob:()=>qa,getReshapeJob:()=>Qc,getSubtaskJob:()=>pW,getTrack:()=>pe,getTransferOutJob:()=>yy,healDeadLead:()=>zc,interruptLead:()=>gp,isArchiving:()=>XH,isHandoffNoteEcho:()=>Cg,isInterruptEcho:()=>Ng,isLeadStarting:()=>W_,isModelSwitchEcho:()=>Pg,leadCliDead:()=>ly,listSubtaskJobs:()=>vs,listTracks:()=>ke,matchTranscriptsToBoxes:()=>U_,mergeNotifyMessage:()=>e$,navigatePrompt:()=>Gg,notifyTrackForMergedPr:()=>wr,notifyTrackForResolvedPr:()=>xp,offerFollowupsForMergedPr:()=>xs,pendingOffers:()=>Wg,queueGauges:()=>Kg,queueShipCompleteNotify:()=>Jg,queuedMessages:()=>gs,rawTrack:()=>Hg,reapArchivedOrphans:()=>fy,reconcileDemotedTracks:()=>py,reconcileHandoffOutcome:()=>t$,reconcileInterruptedHandoffs:()=>wy,reconcileInterruptedReshapes:()=>ry,reconcileInterruptedSubtasks:()=>vy,reconcileNodeUpdateNotices:()=>Ug,reconcileSubtaskOutcome:()=>r$,refreshStandingInstructions:()=>oy,refuseIfTransferred:()=>Tp,rehydrateLead:()=>Wa,removeTrackRepo:()=>bp,renameTrack:()=>dy,reposOf:()=>jt,reshapeTrack:()=>ny,resolveTrackBox:()=>xc,resolveTrackBoxByHookToken:()=>Xc,restartLead:()=>Sp,resumeAgentCmd:()=>pp,reviveLeadForSession:()=>uy,sayToLead:()=>qt,sendOptionsForAgent:()=>Cn,setTrackDraft:()=>Zg,setTrackModel:()=>Qg,startLeadHandoff:()=>Ap,startSpinUpView:()=>Yg,startSubtask:()=>Sy,startTransferOut:()=>by,stopJobActive:()=>G_,subtaskMessages:()=>qg,trackMessages:()=>mp,unarchiveTrack:()=>vp});import z from"node:fs/promises";import V from"node:path";import{randomUUID as lp}from"node:crypto";import{execFile as Q2}from"node:child_process";import{promisify as eH}from"node:util";function Lg(e,{skipPermissions:t=!0,model:n=null}={}){let r=dp[e];if(!r)throw new Error(`unknown agent "${e}"`);let o=process.env[r.envVar];if(o)return o;let s=t&&r.skipFlag?`${r.base} ${r.skipFlag}`:r.base;if(n&&n!=="default"){let a=Nh(e).find(i=>i.alias===n);if(!a)throw new Error(`unknown ${e} launch model "${n}"`);s+=` --model ${a.alias}`}return s}function pp(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=dp[e];if(!s)throw new Error(`unknown agent "${e}"`);let a=process.env[s.envVar];if(a)return a;let i=n&&s.skipFlag?` ${s.skipFlag}`:"";switch(e){case"claude":{if(!t)return`${s.base}${i}`;if(r&&!dn(r,t)){let c=o&&Be(o)?` --name ${o}`:"";return`${s.base} --session-id ${t}${c}${i}`}return`${s.base} --resume ${t}${i}`}case"cursor":return`${s.base} --continue${i}`;case"codex":return`${s.base} resume --last${i}`;default:return`${s.base}${i}`}}function bs(e){let t=dp[e];if(t)return process.env[t.envVar]||t.base}async function fp({maxAgeMs:e=6e4}={}){if(ho.value&&Date.now()-ho.at<e)return ho.value;let t=ho.value,n=await Promise.all(Yc.map(async r=>{let o=await mu(bs(r),{retries:[]});return o===zs?[r,t?.[r]??!0]:[r,o===fu]}));return ho.value=Object.fromEntries(n),ho.at=Date.now(),ho.value}async function Fa(e,t=!0,n=null){let r=Lg(e,{skipPermissions:t,model:n});if(await mu(r)===Vs){ho.at=0;let o=r.split(/\s+/)[0],s=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.":om[o]?`Install it on this node: ${om[o]}`:"Its CLI must be installed and on PATH first.";throw new Error(`agent "${e}" isn't available on this box (command not found: ${o}). ${s}`)}return r}function B_(e,t=go){return`${e.trim()}
|
|
218
218
|
|
|
219
|
-
(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
|
|
220
|
-
`;return Ig=Ig.then(()=>z.mkdir(Jn,{recursive:!0})).then(()=>z.appendFile(Z2,n)).catch(()=>{}),Ig}async function D_(e,{box:t,agent:n,message:r}){let o=pe(await Q(),e);!o||o.archivedAt||(Te(o,"startup-gate",{box:t,agent:n??null,message:r}),await ae(),Qi(o,{provider:n??null,cls:"startup-gate",note:r}).catch(()=>{}))}async function Yn({current:e,latest:t,min:n=null,installKind:r=null,required:o=!1,status:s="available",policy:a=null,error:i=null,reason:c=null}={}){if(!t)return{announced:0};if(s==="available"){let d=null;try{d=JSON.parse(await z.readFile(_g,"utf8"))}catch{}if(d?.latest===t&&d?.current===e)return{announced:0,deduped:!0}}let l=await Q(),u=0;for(let d of l.tracks)Le(d)||!d.leadBox||(Te(d,"node-update",{status:s,current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},...a?{policy:a}:{},...i?{error:String(i).slice(0,400)}:{},...c?{reason:c}:{},required:o===!0}),u++);if(await ae().catch(()=>{}),s==="available")try{await z.mkdir(V.dirname(_g),{recursive:!0}),await z.writeFile(_g,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function Fg({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await Q(),o=0,s=new Set;for(let a of r.tracks){if(a.archivedAt||a.transferredTo||!a.leadBox)continue;let i=new Map;for(let c of a.events||[])c.type==="node-update"&&c.latest&&i.set(c.latest,c);for(let[c,l]of i)["done","failed","rolled-back"].includes(l.status)||oH(e,c)||(Te(a,"node-update",{status:"done",current:l.current??null,latest:c,...t?{installKind:t}:{},...n?{policy:n}:{},reason:"version-reconciled",installed:e,required:!1}),o++,s.add(c))}return o&&await ae().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function oH(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!0;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 B_(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=>sH.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 j_(e,t,n,{kind:r}={}){let o=await xe(t);if(!o)throw new Error(`box "${t}" is not running`);let s=bs(o.agent),a=vc({text:n,producer:xc({kind:r,orchestration:!0}),kind:r||"orchestration",provider:o.agent}),i=pe(await Q(),e)||null,c=Date.now(),l=null;try{l=await sr({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s})}catch(g){throw Ac(a,{ms:Date.now()-c,outcome:g?.code==="cli-not-running"?"cli-not-running":"blocked"}),await gn(i,a,Ta(g)),g}Ac(a,{ms:Date.now()-c,outcome:l||"up"});let u=Cn(o.agent,n);a.paste=!!u.paste;try{await at(t,n,{...u,kind:r||"orchestration"})}catch(g){throw await gn(i,a,Ta(g)),g}let d={},p=await Vi({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration",stats:d});a.verify=d,await gn(i,a,p?d.verdict==="unverified"?"unverified":"delivered":"unsubmitted"),await fe({flow:"orchestration",phase:"sent",submitted:p,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let f=await Q(),w=pe(f,e);if(!w)return;let m=w.boxes.find(g=>g.name===t);Te(w,"orchestration-prompt",{box:t,agent:m?.agent||o.agent,boxRole:m?.role,prompt:n,kind:r||"orchestration",deliveryId:a.id}),await ae()}async function aH(e){let t=V.join(Hs(e),`${e.id}.ndjson`),n;try{n=await z.stat(t)}catch{return null}let r=E_.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await G(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return E_.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function iH(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Zh(n.name,n.agent)}async function Wt(e,t=null,n=null){let r=t??new Map((await ln()).map(S=>[S.name,S])),o=!!e.transferredTo,s=e.boxes.map(S=>{let $=o?null:r.get(S.name),M=_r.has(S.state)?S.state:$?S.role==="lead"?"live":S.state:"stopped";return{...S,state:M,cwd:$?.cwd||S.cwd,agent:$?.agent||S.agent,sessionId:$?.sessionId||S.sessionId}}),[a,{working:i,prompted:c},l,u,d]=await Promise.all([aH(e),iH(e,s),hg(e),n??hn().catch(()=>[]),v_({...e,boxes:s})]),p=Kc.has(e.id),f=(u||[]).filter(S=>S.trackId===e.id&&!["merged","closed","lost"].includes(S.state)),w=T_(e.id),m=w&&{status:"waiting",since:w.startedAt,summary:w.summary??null,count:w.count??null}||f_(f),g=s.find(S=>S.name===e.leadBox)?.state==="live",y=g?gs(e.id).filter(S=>S.kind!=="ship-notify"&&S.kind!=="prompt-answer"):[],b=d||(g&&l.runningJobs?{source:"jobs",count:l.runningJobs,since:l.runningJobsSince,summary:l.runningJobs===1?"a background job is still running":`${l.runningJobs} background jobs are still running`}:y.length?{source:"queue",count:y.length,since:Math.min(...y.map(S=>S.ts||Date.now())),summary:"a queued message is about to send"}:null),k=h_({archivedAt:e.archivedAt,archiving:p,prompted:c,openActions:l.openActions,openActionSince:l.openActionSince,prDecisions:m_(f),operation:m,phase:l.phase,working:i,workingSince:l.currentTurnAt,pending:b,completedTurnAt:l.completedTurnAt||a,live:g});return{...e,boxes:s,lastResponseAt:a,working:i,prompted:c,archiving:p,status:k}}function L_(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),d=l.createdAt-cH,p=r[u+1]?.createdAt??1/0,f=o.find(w=>!s.has(w.id)&&w.birthMs>=d&&w.birthMs<p);f&&(s.add(f.id),c.push({box:l.name,sessionId:f.id}))}return c}async function F_(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(p=>p.box.sessionId),a=U0(r),i=new Set(a.map(p=>p.id)),c=s.every(p=>p&&i.has(p)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(p=>p.box),d=L_(u,a,p=>i.has(p));for(let p of d){let f=u.find(w=>w.name===p.box);f&&(f.sessionId=p.sessionId),await R0(p.box,p.sessionId),n=!0}}return n}async function ke(){let e=await Q();await F_(e)&&await ae();let t=new Map((await ln()).map(r=>[r.name,r])),n=await hn().catch(()=>[]);return Promise.all(e.tracks.map(r=>Wt(r,t,n)))}async function kc(e,t){let n=await Q();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 Yc(e){let t=Oo(e);if(!t)return null;let n=await Q();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&fk(o.hookTokenHash,t))return{track:r,box:o};return null}async function Ug(e){return pe(await Q(),e)??null}async function de(e){let t=await Q(),n=pe(t,e);return n?(await F_(t)&&await ae(),Wt(n)):null}async function Hg(e){let t=await G(e).catch(()=>[]),n;try{n=Nn(t)}catch{return[]}let r=Cu(n).map(s=>({kind:s.kind,id:s.offerId??s.proposalId??s.id,ts:s.ts??null})),o=[];try{o=nv(t)}catch{o=[]}return r.concat(o.map(s=>({kind:"pr-open",id:`${s.repo??""}#${s.number}`,ts:null})))}function Cg(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(lH[e.agent]||[]).some(n=>n.test(t))}function Ng(e){let t=(e.text||"").trim();return t?uH.some(n=>n.test(t)):!1}async function fp(e,t={}){let n=await de(e);if(!n)return null;let r=new Set([...n.events.filter(O=>O.type==="orchestration-prompt"&&O.prompt).map(O=>String(O.prompt).trim()),...n.events.filter(O=>O.type==="said"&&O.text).map(O=>String(O.text).trim())]),o=Wa(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=Bw(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...zE(n),...l],p={windows:[...Tv(n),...Ev(n,Bu),...a,...u]},w=(await Promise.all(n.boxes.map(O=>lh(O,t)))).flat(),m=w.map(O=>{let se={kind:"message",...O};if(se.role==="assistant"&&(se.text||"").trim()&&se.tools?.length){let Re=Zr(se.agent).cliTools,$t=se.tools.filter(X=>!Re.has(X));return{...se,tools:$t.length?$t:void 0}}return se}).map(O=>{if(O.role!=="user")return O;let se=qx(O);return se?se.kind==="suppress"?null:{...O,note:se,text:Kx(se)}:O}).filter(Boolean).filter(O=>!(O.role==="user"&&r.has((O.text||"").trim()))).filter(O=>!Cg(O)).filter(O=>!Ng(O)).filter(O=>!Mg(O)).filter(O=>!ig(O)).filter(O=>O.role!=="assistant"||!!(O.text||"").trim()||!!O.images?.length).filter(O=>!Iv(O,p)),g=m.filter(O=>O.failed&&O.text===Uo);if(g.length){let O=new Map;for(let se of g){if(!O.has(se.box)){let X=[...(await De(se.box,{lines:Qe}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(we=>we[1]);O.set(se.box,X.length?X[X.length-1]:null)}let Re=O.get(se.box);Re&&(se.text=`\u25A0 ${Re}`)}}let y=await Promise.all(n.boxes.map(O=>fS(O,t)));await vE(n,y.flat());let{agentTurnBoxes:b}=await kE(n,m);for(let O of n.boxes)b.has(O.name)&&O.cwd&&tu(O.cwd);await sA(n),await eI(n,m).catch(()=>{});for(let O of n.boxes)O.agent==="codex"&&O.state==="live"&&await AE(n,O),O.agent==="cursor"&&O.state==="live"&&await $E(n,O);let k=await Promise.all(n.boxes.map(O=>pS(O,t)));await xE(n,k.flat().filter(O=>!$g(O.box,O.ts,u)));let S=await G(n),$=c&&i.viewId?i.viewId:null,M=yA(S),L=Nn(S).filter(O=>!(O.kind==="message"&&(Cg(O)||Ng(O)||Mg(O)||ig(O)))&&!(O.kind==="view-card"&&$&&O.viewId===$)),B=bA(L,M),A=L.filter(O=>!(O.kind==="message"&&O.role==="assistant"&&!B.has(O)&&$g(O.box,O.ts,M))),I=Hm(S,A).map(O=>({kind:"command-batch",id:O.id,ts:O.pending?O.ts:(O.anchorTs??O.ts)-1,anchorTs:O.anchorTs,anchorBox:O.anchorBox,agent:O.agent,boxRole:O.boxRole,commands:O.commands,pending:O.pending})).filter(O=>!$g(O.anchorBox,O.anchorTs??O.ts,u)),j=ev(tv([...A,...I].filter(O=>O.ts!=null).sort((O,se)=>O.ts-se.ts))),K=pH(n),R=K.size?j.filter(O=>O.kind==="event"||!K.has(O.box??O.anchorBox)):j,E=n.boxes.find(O=>O.name===n.leadBox)||null,C=E?await Pi(E.cwd,E.sessionId,{fallbackNewest:!1}):null;if(!C&&E){let O=[...S].reverse().find(se=>se.type==="context.usage"&&se.boxId===E.name);O?.payload?.window&&(C={pct:O.payload.pct??(O.payload.occupancy!=null?O.payload.occupancy/O.payload.window:null),occupancy:O.payload.occupancy??null,window:O.payload.window,windowKnown:!0,model:O.payload.model||null})}let P=[...w].reverse().find(O=>O.role==="assistant"&&O.model),W=P?.model||null,Y=[...n.events||[]].reverse().find(O=>O.type==="model-change"),me=C?.model||W;Y&&(!P?.ts||Y.at>=P.ts)&&(me=Y.to);let _e=E?await De(E.name,{lines:Qe}).catch(()=>""):"",Pe=E&&to(E.agent,_e)?await un(E.name).catch(()=>_e):_e,ne=E?Dn(E,Pe):null;if(ne&&E){let O=Ju(E,ne,Pe);if(O){let se=_S(E.name,ne.promptId);se.action!=="give-up"&&(se.action==="send"&&(Ke(E.name,O.keys,{kind:"auto-reject"}).catch(()=>{}),se.first&&N(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:E.name,provider:E.agent,payload:{ts:Date.now(),promptId:ne.promptId,rule:O.id}}).catch(()=>{})),ne=null)}}!ne&&E&&!na(E.agent,_e)&&!gs(n.id).length&&(ne=Lm(R,{leadBoxId:E.name,provider:E.agent,isAnswered:O=>S.some(se=>se.type==="prompt.answered"&&se.payload?.promptId===O)})),ne&&aA(n,E,ne);let Ve=R.filter(O=>!(O.kind==="message"&&O.role==="assistant"&&O.ask&&!String(O.text||"").trim()&&!O.images?.length)),ce=!!E&&vd(E.name,E.agent,_e)&&!ne;E&&!E.readyAt&&(ce||ne||_e&&Wo(E.agent,_e))&&(E.readyAt=Date.now(),await ae());let re=!!E&&!E.readyAt&&Date.now()-(n.createdAt||0)<dH;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&E&&Wo(E.agent,_e)&&!ne){let O=await Q(),se=pe(O,n.id);se?.setup&&!se.setup.connectedAt&&(se.setup.connectedAt=Date.now(),n.setup.connectedAt=se.setup.connectedAt,await N(se,{type:"provider.connected",actor:"system",source:"pane",boxId:E.name,provider:E.agent,payload:{provider:E.agent,ts:se.setup.connectedAt}}).catch(()=>{}),await ae())}let Ce=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,J=Zc(n.id),he=J&&!J.finishedAt?{running:!0,step:J.step||null,added:J.added||[],removed:J.removed||[]}:null,Fe=null;if(c){let O=Date.now()-i.startedAt;if(i.viewId&&!ce){let se=Hd(n.id);se&&(Te(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:se.finishedAt}),await ae())}else if(ne||O>KE&&!ce&&!i.viewId||O>Dw){let se=ne?"needs-input":O>Dw?"timeout":"no-view";Hd(n.id,{error:se})&&await N(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:E?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:se}}).catch(()=>{})}else Fe={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:Ve,context:C?{pct:C.pct,occupancy:C.occupancy,window:C.window,windowKnown:C.windowKnown,model:C.model}:null,models:{current:me,options:Nh(E?.agent)},pendingPrompt:ne,working:ce,stopping:H_(n.id),leadStarting:re,handoff:Ce,reshape:he,view:Fe,queued:gs(n.id).filter(O=>O.kind!=="ship-notify"&&O.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function pH(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 Wg(e,t){let n=await de(e);if(!n)return null;let r=vs(e).find(d=>d.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 G(n),a=Nn(s).filter(d=>d.box===o&&!(d.kind==="message"&&d.role==="assistant"&&d.ask&&!String(d.text||"").trim()&&!d.images?.length)),i=Hm(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,p)=>d.ts-p.ts),l=n.boxes.find(d=>d.name===o)||null,u=!1;if(l&&l.state==="live"){let d=await De(o,{lines:Qe}).catch(()=>"");u=vd(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}function fH(e,t){if(e==null)return[];if(!Array.isArray(e))throw new Error("custom answer must be a list of strings");return e.slice(0,Math.max(1,t)).map(n=>{if(n==null||n==="")return"";if(typeof n!="string")throw new Error("custom answer must be text");let r=n.replace(/\s+/g," ").trim();if(/[\x00-\x1f\x7f]/.test(r))throw new Error("custom answer can't contain control characters");if(r.length>I_)throw new Error(`custom answer is too long (max ${I_} characters)`);return r})}function mH(e,t){let n=String(e||"").replace(/\s+/g," ").trim(),r=String(t||"").replace(/\s+/g," ").trim();return!n||!r?!1:n===r||r.startsWith(n)||n.startsWith(r.slice(0,24))}async function mp(e,t={}){let n=jc.get(e);n&&await n.catch(()=>{});let r=hH(e,t);jc.set(e,r);try{return await r}finally{jc.get(e)===r&&jc.delete(e)}}async function hH(e,{promptId:t,answers:n,text:r,custom:o}={}){let s=pe(await Q(),e);if(!s)throw new Error(`track "${e}" not found`);Ua(e,"prompt-answer");let a=s.boxes.find(y=>y.name===s.leadBox);if(!a)throw new Error(`track "${e}" has no lead box`);if(!await xe(s.leadBox))throw new Error(`lead box "${s.leadBox}" is not running`);let c=await gh(a);if(c||(c=await wH(s,a)),!c)throw new Error("no pending prompt to answer");if(t&&t!==c.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let l=fH(o,c.questions?.length??1);if(l.some(Boolean)&&!c.custom)throw new Error("custom answer isn't available for this prompt \u2014 pick one of its options");let u=(n||[]).some(y=>Array.isArray(y)&&y.length>0);if(c.custom&&!u&&!l.some(Boolean)&&c.answerMode!=="text")throw new Error("no answer selected \u2014 pick an option or type your own");l.some(Boolean)&&(n=(c.questions||[]).map((y,b)=>l[b]&&!y.multiSelect?[]:Array.isArray(n?.[b])?n[b]:[]));let d;if(c.answerMode==="text"){let y=typeof r=="string"?r.trim():"";if(!y)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(y)||y.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await at(s.leadBox,y,{enter:!0,kind:"answer-prompt"}),d={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(c.answerMode==="esc")await Ke(s.leadBox,["Escape"],{kind:"answer-prompt"}),d={mode:"keys",value:"Escape",count:1};else if(c.answerMode==="say"){let y=Yx(c,n||[],typeof r=="string"?r:"",l);if(!y)throw new Error("no answer selected \u2014 pick an option or reply in chat");await qt(s.id,y,{kind:"prompt-answer"}),d={mode:"say",value:y,count:1}}else if(l[0]&&c.provider==="claude"){let y=AS(c,n||[],l[0]);await om(s.leadBox,[...y.pre,{text:y.text}],{kind:"answer-prompt"});let b=!1;for(let S of[150,300,600]){await $r(S);let $=await un(s.leadBox).catch(()=>""),M=Dn(a,$);if(M?.customText&&mH(M.customText,y.text)){b=!0;break}}if(!b)throw new Error("custom answer didn't take \u2014 the widget moved; reload and answer again");await om(s.leadBox,y.post,{kind:"answer-prompt"});let k=S=>S.map($=>$.key).join(" ");d={mode:"keys+text",value:`${k(y.pre)} \u2039text\u203A ${k(y.post)}`.trim(),count:y.pre.length+1+y.post.length}}else if(c.provider==="claude"||c.answerMode==="keys"){let y=yh(c,n||[]);await Ke(s.leadBox,y,{kind:"answer-prompt"}),d={mode:"keys",value:y.join(" "),count:y.length}}else{let y=hx(c,n||[]);await at(s.leadBox,y,{enter:!0,kind:"answer-prompt"}),d={mode:"text",value:y,count:1}}if(c.answerMode==="say"){let y=c.questions||[];for(let b=0;b<y.length;b++){let k=y[b],S=new Set(Array.isArray(n?.[b])?n[b]:[]),$=[...S].map(L=>k.options?.[L]).filter(Boolean).map(L=>({label:L.label,description:L.description||""}));l[b]&&$.push({label:l[b],description:"",custom:!0});let M=(k.options||[]).map((L,B)=>S.has(B)?null:L?.label).filter(Boolean);Ph(s,a,c,{...d,groupId:c.groupId||c.promptId,questionIndex:b,questionTotal:c.questionTotal??y.length,header:k.header||"",question:k.question||"",multiSelect:!!k.multiSelect,chosen:$,rejected:M})}return await fe({flow:"prompt",phase:"answered",sessionName:s.leadBox,agent:a.agent,provider:c.provider,promptId:c.promptId,...d}).catch(()=>{}),{ok:!0,promptId:c.promptId,sent:d.count}}let p=c.questions?.[0]||{},f=new Set(Array.isArray(n?.[0])?n[0]:[]),w=[...f].map(y=>p.options?.[y]).filter(Boolean).map(y=>({label:y.label,description:y.description||""}));l[0]&&w.push({label:l[0],description:"",custom:!0});let m=(p.options||[]).map((y,b)=>f.has(b)?null:y?.label).filter(Boolean),g=c.review?"review":/^claude:model-confirm:/.test(c.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(c.promptId||"")?"resume-confirm":void 0;return d={...d,groupId:c.groupId||c.promptId,questionIndex:c.questionIndex??0,questionTotal:c.questionTotal??1,header:p.header||"",question:p.question||"",multiSelect:!!p.multiSelect,chosen:w,rejected:m,...g?{control:g}:{}},await fe({flow:"prompt",phase:"answered",sessionName:s.leadBox,agent:a.agent,provider:c.provider,promptId:c.promptId,...d}).catch(()=>{}),Ph(s,a,c,d),{ok:!0,promptId:c.promptId,sent:d.count}}async function wH(e,t){if(!t)return null;let n=await G(e).catch(()=>[]);return Lm(Nn(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function qg(e,{promptId:t,questionIndex:n}={}){let r=pe(await Q(),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 xe(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await gh(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=TS(a,n);return c.length?(await Ke(r.leadBox,c,{kind:"navigate-prompt"}),await fe({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 hp(e){let t=Ba.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Ba.set(e,t)),t}function gs(e){return(Ba.get(e)?.items??[]).map(({delivery:t,...n})=>({...n}))}function Gg(e=Date.now()){let t=0,n=0;for(let r of Ba.values())for(let o of r.items){t+=1;let s=e-(o.delivery?.queuedAt??o.ts??e);s>n&&(n=s)}return{queueDepth:t,queueOldestAgeMs:n}}function yH(e,t,n){let r=t.filter(o=>o.delivery&&!o.delivery.settled);r.length&&(async()=>{let o=pe(await Q(),e)||null;for(let s of r)await gn(o,s.delivery,`dropped:${n}`)})().catch(()=>{})}function Xc(e,t="cancel"){let n=Ba.get(e);if(!n)return[];let r=n.items.splice(0);return n.idleSince=null,n.timer&&(clearInterval(n.timer),n.timer=null),yH(e,r,t),r}async function Kg(e){let t=Xc(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=pe(await Q(),e);if(o)return r=ja(o,n),await ae(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function Vg(e){let t=hp(e);t.timer||(t.timer=setInterval(()=>bH(e).catch(()=>{}),gH))}async function bH(e){let t=Ba.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=pe(await Q(),e);if(!n||n.archivedAt){Xc(e,"archived");return}let r=async(l,u="")=>{let d=[];for(let g of t.items)BE(g.delivery,l),r_(g,l)&&d.push(g);if(!d.length)return;t.items=t.items.filter(g=>!d.includes(g));let p=Date.now()-Math.min(...d.map(g=>g.heldSince||Date.now())),f=Math.max(1,Math.round(lg()/6e4)),w=String(u||"").split(`
|
|
219
|
+
(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 Cg(e){return e.role==="user"&&rH.test((e.text||"").trim())}async function Q(){return Dt||(Eg||=(async()=>{try{Dt=JSON.parse(await z.readFile(D_,"utf8"))}catch{Dt={tracks:[]}}finally{Eg=null}return Dt})(),Eg)}async function ae(){await Mt(D_,Dt)}function aH(e,t){let n=JSON.stringify({trackId:e,...t})+`
|
|
220
|
+
`;return Ig=Ig.then(()=>z.mkdir(Jn,{recursive:!0})).then(()=>z.appendFile(tH,n)).catch(()=>{}),Ig}async function j_(e,{box:t,agent:n,message:r}){let o=de(await Q(),e);!o||o.archivedAt||(Te(o,"startup-gate",{box:t,agent:n??null,message:r}),await ae(),ec(o,{provider:n??null,cls:"startup-gate",note:r}).catch(()=>{}))}async function Yn({current:e,latest:t,min:n=null,installKind:r=null,required:o=!1,status:s="available",policy:a=null,error:i=null,reason:c=null}={}){if(!t)return{announced:0};if(s==="available"){let d=null;try{d=JSON.parse(await z.readFile(_g,"utf8"))}catch{}if(d?.latest===t&&d?.current===e)return{announced:0,deduped:!0}}let l=await Q(),u=0;for(let d of l.tracks)Le(d)||!d.leadBox||(Te(d,"node-update",{status:s,current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},...a?{policy:a}:{},...i?{error:String(i).slice(0,400)}:{},...c?{reason:c}:{},required:o===!0}),u++);if(await ae().catch(()=>{}),s==="available")try{await z.mkdir(V.dirname(_g),{recursive:!0}),await z.writeFile(_g,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function Ug({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await Q(),o=0,s=new Set;for(let a of r.tracks){if(a.archivedAt||a.transferredTo||!a.leadBox)continue;let i=new Map;for(let c of a.events||[])c.type==="node-update"&&c.latest&&i.set(c.latest,c);for(let[c,l]of i)["done","failed","rolled-back"].includes(l.status)||iH(e,c)||(Te(a,"node-update",{status:"done",current:l.current??null,latest:c,...t?{installKind:t}:{},...n?{policy:n}:{},reason:"version-reconciled",installed:e,required:!1}),o++,s.add(c))}return o&&await ae().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function iH(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!0;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 L_(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=>cH.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 F_(e,t,n,{kind:r}={}){let o=await xe(t);if(!o)throw new Error(`box "${t}" is not running`);let s=bs(o.agent),a=Sc({text:n,producer:vc({kind:r,orchestration:!0}),kind:r||"orchestration",provider:o.agent}),i=de(await Q(),e)||null,c=Date.now(),l=null;try{l=await sr({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s})}catch(g){throw Tc(a,{ms:Date.now()-c,outcome:g?.code==="cli-not-running"?"cli-not-running":"blocked"}),await gn(i,a,Ta(g)),g}Tc(a,{ms:Date.now()-c,outcome:l||"up"});let u=Cn(o.agent,n);a.paste=!!u.paste;try{await at(t,n,{...u,kind:r||"orchestration"})}catch(g){throw await gn(i,a,Ta(g)),g}let d={},p=await zi({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration",stats:d});a.verify=d,await gn(i,a,p?d.verdict==="unverified"?"unverified":"delivered":"unsubmitted"),await fe({flow:"orchestration",phase:"sent",submitted:p,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let f=await Q(),w=de(f,e);if(!w)return;let m=w.boxes.find(g=>g.name===t);Te(w,"orchestration-prompt",{box:t,agent:m?.agent||o.agent,boxRole:m?.role,prompt:n,kind:r||"orchestration",deliveryId:a.id}),await ae()}async function lH(e){let t=V.join(Hs(e),`${e.id}.ndjson`),n;try{n=await z.stat(t)}catch{return null}let r=__.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await G(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return __.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function uH(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Zh(n.name,n.agent)}async function Wt(e,t=null,n=null){let r=t??new Map((await ln()).map(S=>[S.name,S])),o=!!e.transferredTo,s=e.boxes.map(S=>{let $=o?null:r.get(S.name),M=_r.has(S.state)?S.state:$?S.role==="lead"?"live":S.state:"stopped";return{...S,state:M,cwd:$?.cwd||S.cwd,agent:$?.agent||S.agent,sessionId:$?.sessionId||S.sessionId}}),[a,{working:i,prompted:c},l,u,d]=await Promise.all([lH(e),uH(e,s),hg(e),n??hn().catch(()=>[]),A_({...e,boxes:s})]),p=Vc.has(e.id),f=(u||[]).filter(S=>S.trackId===e.id&&!["merged","closed","lost"].includes(S.state)),w=I_(e.id),m=w&&{status:"waiting",since:w.startedAt,summary:w.summary??null,count:w.count??null}||h_(f),g=s.find(S=>S.name===e.leadBox)?.state==="live",y=g?gs(e.id).filter(S=>S.kind!=="ship-notify"&&S.kind!=="prompt-answer"):[],b=d||(g&&l.runningJobs?{source:"jobs",count:l.runningJobs,since:l.runningJobsSince,summary:l.runningJobs===1?"a background job is still running":`${l.runningJobs} background jobs are still running`}:y.length?{source:"queue",count:y.length,since:Math.min(...y.map(S=>S.ts||Date.now())),summary:"a queued message is about to send"}:null),k=g_({archivedAt:e.archivedAt,archiving:p,prompted:c,openActions:l.openActions,openActionSince:l.openActionSince,prDecisions:w_(f),operation:m,phase:l.phase,working:i,workingSince:l.currentTurnAt,pending:b,completedTurnAt:l.completedTurnAt||a,live:g});return{...e,boxes:s,lastResponseAt:a,working:i,prompted:c,archiving:p,status:k}}function U_(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),d=l.createdAt-dH,p=r[u+1]?.createdAt??1/0,f=o.find(w=>!s.has(w.id)&&w.birthMs>=d&&w.birthMs<p);f&&(s.add(f.id),c.push({box:l.name,sessionId:f.id}))}return c}async function H_(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(p=>p.box.sessionId),a=W0(r),i=new Set(a.map(p=>p.id)),c=s.every(p=>p&&i.has(p)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(p=>p.box),d=U_(u,a,p=>i.has(p));for(let p of d){let f=u.find(w=>w.name===p.box);f&&(f.sessionId=p.sessionId),await M0(p.box,p.sessionId),n=!0}}return n}async function ke(){let e=await Q();await H_(e)&&await ae();let t=new Map((await ln()).map(r=>[r.name,r])),n=await hn().catch(()=>[]);return Promise.all(e.tracks.map(r=>Wt(r,t,n)))}async function xc(e,t){let n=await Q();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 Xc(e){let t=Oo(e);if(!t)return null;let n=await Q();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&hk(o.hookTokenHash,t))return{track:r,box:o};return null}async function Hg(e){return de(await Q(),e)??null}async function pe(e){let t=await Q(),n=de(t,e);return n?(await H_(t)&&await ae(),Wt(n)):null}async function Wg(e){let t=await G(e).catch(()=>[]),n;try{n=Nn(t)}catch{return[]}let r=Nu(n).map(s=>({kind:s.kind,id:s.offerId??s.proposalId??s.id,ts:s.ts??null})),o=[];try{o=ov(t)}catch{o=[]}return r.concat(o.map(s=>({kind:"pr-open",id:`${s.repo??""}#${s.number}`,ts:null})))}function Ng(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(pH[e.agent]||[]).some(n=>n.test(t))}function Pg(e){let t=(e.text||"").trim();return t?fH.some(n=>n.test(t)):!1}function W_(e,t,n=Date.now()){return!e||e.readyAt||e.state!=="live"?!1:n-(t||0)<mH}async function mp(e,t={}){let n=await pe(e);if(!n)return null;let r=new Set([...n.events.filter(O=>O.type==="orchestration-prompt"&&O.prompt).map(O=>String(O.prompt).trim()),...n.events.filter(O=>O.type==="said"&&O.text).map(O=>String(O.text).trim())]),o=qa(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=Bw(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...YE(n),...l],p={windows:[...Iv(n),..._v(n,ju),...a,...u]},w=(await Promise.all(n.boxes.map(O=>lh(O,t)))).flat(),m=w.map(O=>{let oe={kind:"message",...O};if(oe.role==="assistant"&&(oe.text||"").trim()&&oe.tools?.length){let Re=Zr(oe.agent).cliTools,$t=oe.tools.filter(X=>!Re.has(X));return{...oe,tools:$t.length?$t:void 0}}return oe}).map(O=>{if(O.role!=="user")return O;let oe=Kx(O);return oe?oe.kind==="suppress"?null:{...O,note:oe,text:zx(oe)}:O}).filter(Boolean).filter(O=>!(O.role==="user"&&r.has((O.text||"").trim()))).filter(O=>!Ng(O)).filter(O=>!Pg(O)).filter(O=>!Cg(O)).filter(O=>!ig(O)).filter(O=>O.role!=="assistant"||!!(O.text||"").trim()||!!O.images?.length).filter(O=>!$v(O,p)),g=m.filter(O=>O.failed&&O.text===Uo);if(g.length){let O=new Map;for(let oe of g){if(!O.has(oe.box)){let X=[...(await De(oe.box,{lines:Qe}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(we=>we[1]);O.set(oe.box,X.length?X[X.length-1]:null)}let Re=O.get(oe.box);Re&&(oe.text=`\u25A0 ${Re}`)}}let y=await Promise.all(n.boxes.map(O=>hS(O,t)));await AE(n,y.flat());let{agentTurnBoxes:b}=await vE(n,m);for(let O of n.boxes)b.has(O.name)&&O.cwd&&tu(O.cwd);await iA(n),await nI(n,m).catch(()=>{});for(let O of n.boxes)O.agent==="codex"&&O.state==="live"&&await EE(n,O),O.agent==="cursor"&&O.state==="live"&&await OE(n,O);let k=await Promise.all(n.boxes.map(O=>mS(O,t)));await SE(n,k.flat().filter(O=>!$g(O.box,O.ts,u)));let S=await G(n),$=c&&i.viewId?i.viewId:null,M=kA(S),L=Nn(S).filter(O=>!(O.kind==="message"&&(Ng(O)||Pg(O)||Cg(O)||ig(O)))&&!(O.kind==="view-card"&&$&&O.viewId===$)),B=xA(L,M),A=L.filter(O=>!(O.kind==="message"&&O.role==="assistant"&&!B.has(O)&&$g(O.box,O.ts,M))),I=Hm(S,A).map(O=>({kind:"command-batch",id:O.id,ts:O.pending?O.ts:(O.anchorTs??O.ts)-1,anchorTs:O.anchorTs,anchorBox:O.anchorBox,agent:O.agent,boxRole:O.boxRole,commands:O.commands,pending:O.pending})).filter(O=>!$g(O.anchorBox,O.anchorTs??O.ts,u)),j=nv(rv([...A,...I].filter(O=>O.ts!=null).sort((O,oe)=>O.ts-oe.ts))),K=hH(n),R=K.size?j.filter(O=>O.kind==="event"||!K.has(O.box??O.anchorBox)):j,T=n.boxes.find(O=>O.name===n.leadBox)||null,C=T?await Di(T.cwd,T.sessionId,{fallbackNewest:!1}):null;if(!C&&T){let O=[...S].reverse().find(oe=>oe.type==="context.usage"&&oe.boxId===T.name);O?.payload?.window&&(C={pct:O.payload.pct??(O.payload.occupancy!=null?O.payload.occupancy/O.payload.window:null),occupancy:O.payload.occupancy??null,window:O.payload.window,windowKnown:!0,model:O.payload.model||null})}let P=[...w].reverse().find(O=>O.role==="assistant"&&O.model),W=P?.model||null,Y=[...n.events||[]].reverse().find(O=>O.type==="model-change"),me=C?.model||W;Y&&(!P?.ts||Y.at>=P.ts)&&(me=Y.to);let _e=T?await De(T.name,{lines:Qe}).catch(()=>""):"",Pe=T&&to(T.agent,_e)?await un(T.name).catch(()=>_e):_e,ne=T?Dn(T,Pe):null;if(ne&&T){let O=Yu(T,ne,Pe);if(O){let oe=RS(T.name,ne.promptId);oe.action!=="give-up"&&(oe.action==="send"&&(Ke(T.name,O.keys,{kind:"auto-reject"}).catch(()=>{}),oe.first&&N(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{ts:Date.now(),promptId:ne.promptId,rule:O.id}}).catch(()=>{})),ne=null)}}!ne&&T&&!na(T.agent,_e)&&!gs(n.id).length&&(ne=Lm(R,{leadBoxId:T.name,provider:T.agent,isAnswered:O=>S.some(oe=>oe.type==="prompt.answered"&&oe.payload?.promptId===O)})),ne&&cA(n,T,ne);let Ve=R.filter(O=>!(O.kind==="message"&&O.role==="assistant"&&O.ask&&!String(O.text||"").trim()&&!O.images?.length)),ce=!!T&&Sd(T.name,T.agent,_e)&&!ne;if(T&&!T.readyAt&&(ce||ne||_e&&Wo(T.agent,_e))){T.readyAt=Date.now();let O=de(await Q(),n.id)?.boxes?.find(oe=>oe.name===T.name);O&&!O.readyAt&&(O.readyAt=T.readyAt,await ae())}let re=W_(T,n.createdAt);if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&T&&Wo(T.agent,_e)&&!ne){let O=await Q(),oe=de(O,n.id);oe?.setup&&!oe.setup.connectedAt&&(oe.setup.connectedAt=Date.now(),n.setup.connectedAt=oe.setup.connectedAt,await N(oe,{type:"provider.connected",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{provider:T.agent,ts:oe.setup.connectedAt}}).catch(()=>{}),await ae())}let Ce=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,J=Qc(n.id),he=J&&!J.finishedAt?{running:!0,step:J.step||null,added:J.added||[],removed:J.removed||[]}:null,Fe=null;if(c){let O=Date.now()-i.startedAt;if(i.viewId&&!ce){let oe=Wd(n.id);oe&&(Te(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:oe.finishedAt}),await ae())}else if(ne||O>zE&&!ce&&!i.viewId||O>Dw){let oe=ne?"needs-input":O>Dw?"timeout":"no-view";Wd(n.id,{error:oe})&&await N(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:T?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:oe}}).catch(()=>{})}else Fe={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:Ve,context:C?{pct:C.pct,occupancy:C.occupancy,window:C.window,windowKnown:C.windowKnown,model:C.model}:null,models:{current:me,options:Nh(T?.agent)},pendingPrompt:ne,working:ce,stopping:G_(n.id),leadStarting:re,handoff:Ce,reshape:he,view:Fe,queued:gs(n.id).filter(O=>O.kind!=="ship-notify"&&O.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function hH(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 qg(e,t){let n=await pe(e);if(!n)return null;let r=vs(e).find(d=>d.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 G(n),a=Nn(s).filter(d=>d.box===o&&!(d.kind==="message"&&d.role==="assistant"&&d.ask&&!String(d.text||"").trim()&&!d.images?.length)),i=Hm(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,p)=>d.ts-p.ts),l=n.boxes.find(d=>d.name===o)||null,u=!1;if(l&&l.state==="live"){let d=await De(o,{lines:Qe}).catch(()=>"");u=Sd(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}function wH(e,t){if(e==null)return[];if(!Array.isArray(e))throw new Error("custom answer must be a list of strings");return e.slice(0,Math.max(1,t)).map(n=>{if(n==null||n==="")return"";if(typeof n!="string")throw new Error("custom answer must be text");let r=n.replace(/\s+/g," ").trim();if(/[\x00-\x1f\x7f]/.test(r))throw new Error("custom answer can't contain control characters");if(r.length>$_)throw new Error(`custom answer is too long (max ${$_} characters)`);return r})}function gH(e,t){let n=String(e||"").replace(/\s+/g," ").trim(),r=String(t||"").replace(/\s+/g," ").trim();return!n||!r?!1:n===r||r.startsWith(n)||n.startsWith(r.slice(0,24))}async function hp(e,t={}){let n=Lc.get(e);n&&await n.catch(()=>{});let r=yH(e,t);Lc.set(e,r);try{return await r}finally{Lc.get(e)===r&&Lc.delete(e)}}async function yH(e,{promptId:t,answers:n,text:r,custom:o}={}){let s=de(await Q(),e);if(!s)throw new Error(`track "${e}" not found`);Ua(e,"prompt-answer");let a=s.boxes.find(y=>y.name===s.leadBox);if(!a)throw new Error(`track "${e}" has no lead box`);if(!await xe(s.leadBox))throw new Error(`lead box "${s.leadBox}" is not running`);let c=await gh(a);if(c||(c=await bH(s,a)),!c)throw new Error("no pending prompt to answer");if(t&&t!==c.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let l=wH(o,c.questions?.length??1);if(l.some(Boolean)&&!c.custom)throw new Error("custom answer isn't available for this prompt \u2014 pick one of its options");let u=(n||[]).some(y=>Array.isArray(y)&&y.length>0);if(c.custom&&!u&&!l.some(Boolean)&&c.answerMode!=="text")throw new Error("no answer selected \u2014 pick an option or type your own");l.some(Boolean)&&(n=(c.questions||[]).map((y,b)=>l[b]&&!y.multiSelect?[]:Array.isArray(n?.[b])?n[b]:[]));let d;if(c.answerMode==="text"){let y=typeof r=="string"?r.trim():"";if(!y)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(y)||y.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await at(s.leadBox,y,{enter:!0,kind:"answer-prompt"}),d={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(c.answerMode==="esc")await Ke(s.leadBox,["Escape"],{kind:"answer-prompt"}),d={mode:"keys",value:"Escape",count:1};else if(c.answerMode==="say"){let y=Zx(c,n||[],typeof r=="string"?r:"",l);if(!y)throw new Error("no answer selected \u2014 pick an option or reply in chat");await qt(s.id,y,{kind:"prompt-answer"}),d={mode:"say",value:y,count:1}}else if(l[0]&&c.provider==="claude"){let y=ES(c,n||[],l[0]);await sm(s.leadBox,[...y.pre,{text:y.text}],{kind:"answer-prompt"});let b=!1;for(let S of[150,300,600]){await $r(S);let $=await un(s.leadBox).catch(()=>""),M=Dn(a,$);if(M?.customText&&gH(M.customText,y.text)){b=!0;break}}if(!b)throw new Error("custom answer didn't take \u2014 the widget moved; reload and answer again");await sm(s.leadBox,y.post,{kind:"answer-prompt"});let k=S=>S.map($=>$.key).join(" ");d={mode:"keys+text",value:`${k(y.pre)} \u2039text\u203A ${k(y.post)}`.trim(),count:y.pre.length+1+y.post.length}}else if(c.provider==="claude"||c.answerMode==="keys"){let y=yh(c,n||[]);await Ke(s.leadBox,y,{kind:"answer-prompt"}),d={mode:"keys",value:y.join(" "),count:y.length}}else{let y=gx(c,n||[]);await at(s.leadBox,y,{enter:!0,kind:"answer-prompt"}),d={mode:"text",value:y,count:1}}if(c.answerMode==="say"){let y=c.questions||[];for(let b=0;b<y.length;b++){let k=y[b],S=new Set(Array.isArray(n?.[b])?n[b]:[]),$=[...S].map(L=>k.options?.[L]).filter(Boolean).map(L=>({label:L.label,description:L.description||""}));l[b]&&$.push({label:l[b],description:"",custom:!0});let M=(k.options||[]).map((L,B)=>S.has(B)?null:L?.label).filter(Boolean);Ph(s,a,c,{...d,groupId:c.groupId||c.promptId,questionIndex:b,questionTotal:c.questionTotal??y.length,header:k.header||"",question:k.question||"",multiSelect:!!k.multiSelect,chosen:$,rejected:M})}return await fe({flow:"prompt",phase:"answered",sessionName:s.leadBox,agent:a.agent,provider:c.provider,promptId:c.promptId,...d}).catch(()=>{}),{ok:!0,promptId:c.promptId,sent:d.count}}let p=c.questions?.[0]||{},f=new Set(Array.isArray(n?.[0])?n[0]:[]),w=[...f].map(y=>p.options?.[y]).filter(Boolean).map(y=>({label:y.label,description:y.description||""}));l[0]&&w.push({label:l[0],description:"",custom:!0});let m=(p.options||[]).map((y,b)=>f.has(b)?null:y?.label).filter(Boolean),g=c.review?"review":/^claude:model-confirm:/.test(c.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(c.promptId||"")?"resume-confirm":void 0;return d={...d,groupId:c.groupId||c.promptId,questionIndex:c.questionIndex??0,questionTotal:c.questionTotal??1,header:p.header||"",question:p.question||"",multiSelect:!!p.multiSelect,chosen:w,rejected:m,...g?{control:g}:{}},await fe({flow:"prompt",phase:"answered",sessionName:s.leadBox,agent:a.agent,provider:c.provider,promptId:c.promptId,...d}).catch(()=>{}),Ph(s,a,c,d),{ok:!0,promptId:c.promptId,sent:d.count}}async function bH(e,t){if(!t)return null;let n=await G(e).catch(()=>[]);return Lm(Nn(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function Gg(e,{promptId:t,questionIndex:n}={}){let r=de(await Q(),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 xe(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await gh(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=IS(a,n);return c.length?(await Ke(r.leadBox,c,{kind:"navigate-prompt"}),await fe({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 wp(e){let t=Ba.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Ba.set(e,t)),t}function gs(e){return(Ba.get(e)?.items??[]).map(({delivery:t,...n})=>({...n}))}function Kg(e=Date.now()){let t=0,n=0;for(let r of Ba.values())for(let o of r.items){t+=1;let s=e-(o.delivery?.queuedAt??o.ts??e);s>n&&(n=s)}return{queueDepth:t,queueOldestAgeMs:n}}function xH(e,t,n){let r=t.filter(o=>o.delivery&&!o.delivery.settled);r.length&&(async()=>{let o=de(await Q(),e)||null;for(let s of r)await gn(o,s.delivery,`dropped:${n}`)})().catch(()=>{})}function Zc(e,t="cancel"){let n=Ba.get(e);if(!n)return[];let r=n.items.splice(0);return n.idleSince=null,n.timer&&(clearInterval(n.timer),n.timer=null),xH(e,r,t),r}async function Vg(e){let t=Zc(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=de(await Q(),e);if(o)return r=ja(o,n),await ae(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function zg(e){let t=wp(e);t.timer||(t.timer=setInterval(()=>vH(e).catch(()=>{}),kH))}async function vH(e){let t=Ba.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=de(await Q(),e);if(!n||n.archivedAt){Zc(e,"archived");return}let r=async(l,u="")=>{let d=[];for(let g of t.items)LE(g.delivery,l),s_(g,l)&&d.push(g);if(!d.length)return;t.items=t.items.filter(g=>!d.includes(g));let p=Date.now()-Math.min(...d.map(g=>g.heldSince||Date.now())),f=Math.max(1,Math.round(lg()/6e4)),w=String(u||"").split(`
|
|
221
221
|
`).map(g=>g.trimEnd()).filter(g=>g.trim()).slice(-3).join(`
|
|
222
|
-
`),m=l==="noSession"?`The scheduled prompt waited ${f} min for the box's session to come back and was dropped \u2014 open the terminal.`:`The scheduled prompt waited ${f} min for the box to become idle and was dropped \u2014 open the terminal.`;for(let g of d)g.delivery&&await gn(n,g.delivery,l==="noSession"?"failed:not-running":"failed:blocked");Te(n,"delivery-held",{box:n.leadBox,agent:o?.agent??null,reason:l,heldMs:p,producer:"loop",deliveryId:d[0]?.delivery?.id??null,message:m,...w?{detail:w}:{}}),await ae(),
|
|
222
|
+
`),m=l==="noSession"?`The scheduled prompt waited ${f} min for the box's session to come back and was dropped \u2014 open the terminal.`:`The scheduled prompt waited ${f} min for the box to become idle and was dropped \u2014 open the terminal.`;for(let g of d)g.delivery&&await gn(n,g.delivery,l==="noSession"?"failed:not-running":"failed:blocked");Te(n,"delivery-held",{box:n.leadBox,agent:o?.agent??null,reason:l,heldMs:p,producer:"loop",deliveryId:d[0]?.delivery?.id??null,message:m,...w?{detail:w}:{}}),await ae(),ec(n,{provider:o?.agent??null,cls:"delivery-held",note:m}).catch(()=>{})},o=n.boxes.find(l=>l.name===n.leadBox),s=qa(e);if(s&&!s.finishedAt&&s.step!=="done"&&!s.error){ws(t,!1),await r("handoff");return}let a=await xe(n.leadBox);if(a&&await ly(a)&&(a=await zc(n,o,"queue")?await xe(n.leadBox):null,await ae()),!a){ws(t,!1),await r("noSession");return}if(no(o?.agent)){ws(t,!1),await r("cap");return}let i=await De(n.leadBox,{lines:Qe}).catch(()=>"");if(Hi(o?.agent,i)){ws(t,!1),await r("busy",i);return}if(to(o?.agent,i)){let l=await un(n.leadBox).catch(()=>i);if(Dn(o,l)){ws(t,!1),await r("prompt");return}}if(!ws(t,!0)){await r("grace");return}let c=t.items.shift();t.idleSince=null,c.delivery&&(c.delivery.dequeuedAt=Date.now());try{await Dg(n,o,a,c.text,c.kind??null,c.attachments??null,c.delivery??null)}catch(l){if(l?.code!=="cli-not-running")throw c.delivery&&await gn(n,c.delivery,Ta(l)),l;t.items.unshift(c),c.delivery&&(c.delivery.requeued+=1,c.delivery.dequeuedAt=null,await Ac(n,c.delivery,{position:1,reason:"requeue"})),await zc(n,o,"queue-gate")}await ae()}finally{t.flushing=!1}}}async function q_(e,t,n,r){let o=no(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(ja(e,[n]),await ae()),GS(e.id,o))try{await N(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 SH(e,t){let n=await De(e.leadBox,{lines:Qe}).catch(()=>"");if(!to(t?.agent,n))return!1;let r=await un(e.leadBox).catch(()=>n);return!!Dn(t,r)}async function Dg(e,t,n,r,o=null,s=null,a=null){a||(a=Sc({text:r,producer:vc({kind:o}),kind:o,provider:t?.agent??null,attachments:Array.isArray(s)?s.length:0})),a.provider??=t?.agent??null,await q_(e,t,r,o);let i=qc.get(e.id)||0,c=r,l=!1,u=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(c=B_(r,e.pendingHandoffHydrate.artifact||go),delete e.pendingHandoffHydrate,l=!0);let d=Array.isArray(s)?s.filter(m=>m&&m.rel):[];if(d.length){let m=o_(d.map(g=>g.rel));c=c.trim()?`${c.trim()}
|
|
223
223
|
|
|
224
|
-
${m}`:m}let p=Te(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,deliveryId:a.id,...o?{kind:o}:{},...d.length?{attachments:d}:{}});o||Da(e,null);try{let m=Date.now(),g=null;try{g=await sr({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:bs(n.agent),bailOnDialog:!0})}catch(b){throw
|
|
224
|
+
${m}`:m}let p=Te(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,deliveryId:a.id,...o?{kind:o}:{},...d.length?{attachments:d}:{}});o||Da(e,null);try{let m=Date.now(),g=null;try{g=await sr({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:bs(n.agent),bailOnDialog:!0})}catch(b){throw Tc(a,{ms:Date.now()-m,outcome:b?.code==="cli-not-running"?"cli-not-running":"blocked"}),b}if(Tc(a,{ms:Date.now()-m,outcome:g||"up"}),(qc.get(e.id)||0)!==i){!o&&r.trim()&&(ja(e,[r]),await ae());let b=new Error("delivery stopped \u2014 the message was handed back as the draft");throw b.interrupted=!0,b}if(await SH(e,t)){let b=wp(e.id);a.requeued+=1,a.dequeuedAt=null;let k={id:a.id,text:r,ts:Date.now(),...o?{kind:o}:{},...d.length?{attachments:d}:{},delivery:a};b.items.unshift(k),zg(e.id),await Ac(e,a,{position:1,reason:"prompt"});let S=new Error("lead is blocked on a prompt \u2014 message queued");throw S.requeued={id:k.id,position:b.items.length},S}let y=Cn(t?.agent,c);a.paste=!!y.paste,await at(e.leadBox,c,{...y,kind:l?"lead-handoff-first-prompt":"say"})}catch(m){if(u&&!e.pendingHandoffHydrate&&(e.pendingHandoffHydrate=u),Te(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:p.at,text:r,...m?.interrupted?{reason:"interrupted"}:{},...m?.requeued?{reason:"requeued"}:{}}),m?.requeued)return{queued:!0,...m.requeued};throw m?.code!=="cli-not-running"&&await gn(e,a,Ta(m)),m}let f={},w=await LH(e,t,c,f);a.verify=f,w?await gn(e,a,f.verdict==="unverified"?"unverified":"delivered"):(await K_(e.leadBox,t?.agent,c.length+8,c).catch(()=>{}),Te(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:p.at,text:r,reason:"unsubmitted"}),o||ja(e,[r]),await gn(e,a,"unsubmitted")),l&&await fe({flow:"lead-handoff",phase:"first-prompt",trackId:e.id,sessionName:e.leadBox,agent:t?.agent,userTextLen:r.length})}function Jg(e,{actions:t}={}){let n=wp(e),r=vA(t),o=Sc({text:r,producer:vc({kind:"ship-notify"}),kind:"ship-notify"});n.items.push({id:o.id,text:r,ts:Date.now(),kind:"ship-notify",delivery:o}),zg(e),(async()=>{let s=de(await Q(),e);s&&(o.provider=s.boxes.find(a=>a.name===s.leadBox)?.agent??null,await Ac(s,o,{position:n.items.length,reason:"queue"}))})().catch(()=>{})}async function AH(e,t,n,r){await Ke(e.leadBox,["Escape"],{kind:"dismiss-prompt"});let o="",s=!1;for(let c of[250,400,600,900]){if(await $r(c),o=await De(e.leadBox,{lines:Qe}).catch(()=>""),!to(t?.agent,o)){s=!0;break}let l=await un(e.leadBox).catch(()=>o);if(!Dn(t,l)){s=!0;break}}let a=n.questions?.[0]||{},i={groupId:n.groupId||n.promptId,questionIndex:n.questionIndex??0,questionTotal:n.questionTotal??1,header:a.header||"",question:a.question||"",multiSelect:!!a.multiSelect,options:(a.options||[]).map(c=>c?.label).filter(Boolean),via:"send",deliveryId:r?.id??null,gone:s};return fe({flow:"prompt",phase:"dismissed",sessionName:e.leadBox,agent:t?.agent,provider:n.provider,promptId:n.promptId,...i}).catch(()=>{}),s&&lA(e,t,n,i),{gone:s,pane:o}}async function qt(e,t,{kind:n=null,attachments:r=null,producer:o=null,dismissPrompt:s=!1}={}){let a=Array.isArray(r)?r.filter(d=>d&&typeof d.rel=="string"):[];if((typeof t!="string"||!da(t))&&!a.length)throw new Error("text (string) or an attachment required");let i=de(await Q(),e);if(!i)throw new Error(`track "${e}" not found`);if(i.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before driving it`);Tp(i);let c=i.boxes.find(d=>d.name===i.leadBox),l=da(t),u=Sc({text:l,producer:vc({kind:n,producer:o}),kind:n,provider:c?.agent??null,attachments:a.length});try{return await TH(e,i,c,l,{kind:n,atts:a,d:u,dismissPrompt:s})}catch(d){throw await gn(i,u,Ta(d)),d}}async function TH(e,t,n,r,{kind:o,atts:s,d:a,dismissPrompt:i=!1}){await q_(t,n,r,o),Ua(e,"say");let c=await xe(t.leadBox);if(c&&await ly(c)?await zc(t,n,"say")?(await ae(),c=await xe(t.leadBox)):c=null:!c&&await Wa(t)&&(await ae(),c=await xe(t.leadBox)),!c)throw new Error(`lead box "${t.leadBox}" is not running`);let l=Lc.get(e);l&&await l.catch(()=>{});let u=wp(e),d=await De(t.leadBox,{lines:Qe}).catch(()=>""),p=u.items.length>0?"queue":Hi(n?.agent,d)?"busy":null,f=p!=null,w=i&&!o;if((!f||p==="queue"&&w)&&to(n?.agent,d)){let g=await un(t.leadBox).catch(()=>d),y=Dn(n,g);if(f||(f=!!y,f&&(p="prompt")),y&&w&&y.dismissable){let b=await AH(t,n,y,a);b.gone&&p==="prompt"&&(d=b.pane,f=Hi(n?.agent,d),p=f?"busy":null)}}if(f){let g={id:a.id,text:r,ts:Date.now(),...o?{kind:o}:{},...s.length?{attachments:s}:{},delivery:a};return u.items.push(g),zg(e),o||Da(t,null),await ae(),await Ac(t,a,{position:u.items.length,reason:p}),{ok:!0,leadBox:t.leadBox,queued:!0,id:g.id,position:u.items.length,deliveryId:a.id}}let m;try{m=await Dg(t,n,c,r,o,s,a)}catch(g){if(g?.code!=="cli-not-running"||!await zc(t,n,"say-gate")||(await ae(),c=await xe(t.leadBox),!c))throw g;m=await Dg(t,n,c,r,o,s,a)}return await ae(),m?.queued?{ok:!0,leadBox:t.leadBox,queued:!0,id:m.id,position:m.position,deliveryId:a.id}:{ok:!0,leadBox:t.leadBox,deliveryId:a.id}}async function Yg(e,{hint:t}={}){let n=de(await Q(),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=Bw(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";JE(n.id,{name:s,box:n.leadBox});let a=String(t||"").trim()?`${R_}
|
|
225
225
|
|
|
226
|
-
What to preview: ${String(t).trim()}`:
|
|
226
|
+
What to preview: ${String(t).trim()}`:R_;try{await qt(e,a,{kind:"spin-up-view"})}catch(i){throw Wd(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function gp(e){let t=de(await Q(),e);if(!t)throw new Error(`track "${e}" not found`);if(t.archivedAt)throw new Error(`track "${e}" is archived`);qc.set(e,(qc.get(e)||0)+1);let n=Zc(e,"interrupt"),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=Pa.get(e);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=ja(t,r),await ae()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:t.draftRev||0}}let s=OH(e,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,$r(EH)]);let a=de(await Q(),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 G_(e){let t=Pa.get(e);return!!t&&!t.canceled}function Ua(e,t){let n=Pa.get(e);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=t||null)}function Xg(e){Ua(e,"track-ended"),qc.delete(e),Zc(e,"track-ended")}function OH(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}),Pa.set(e,r),(async()=>{let s=de(await Q(),e),a=s?.boxes.find(g=>g.name===s.leadBox),i=s?await xe(s.leadBox):null,c=!1,l=!1;if(i){e:for(let g=0;g<IH&&!r.canceled;g++){await Ke(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let y of _H){await $r(y);let b=await De(s.leadBox,{lines:Qe}).catch(()=>"");if(!na(a?.agent,b)){c=!0;break e}}}if(!c){l=!0;for(let g=0;g<RH&&!r.canceled&&(await $r($H),!r.canceled);g++){let y=await De(s.leadBox,{lines:Qe}).catch(()=>"");if(!na(a?.agent,y)){c=!0;break}await Ke(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=de(await Q(),e)||s,d=u?.boxes.find(g=>g.name===u.leadBox)||a,p=null,f=null;u&&d&&i&&c&&!r.canceled&&(p=await PH(u,d).catch(()=>null),p||(f=await jH(u,d).catch(()=>null)));let w=u&&i&&!r.canceled?await CH(u).catch(()=>null):null,m=[p||f,...r.queuedTexts].filter(Boolean);u&&m.length&&(r.restored=ja(u,m),await ae()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await MH(u||s,d,{ts:r.startedAt,result:r.result,dropped:t,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!p,lostRestored:!!f,queuedRestored:r.queuedTexts.length,shipDismissed:!!w,durationMs:Date.now()-r.startedAt,...r.cancelReason?{cancelReason:r.cancelReason}:{}}),await fe({flow:"interrupt",phase:r.result==="gave-up"?"failed":"done",trackId:e,sessionName:u?.leadBox,agent:d?.agent,result:r.result,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!p,lostRestored:!!f,queuedRestored:r.queuedTexts.length,shipDismissed:!!w}).catch(()=>{})})().catch(()=>{}).finally(()=>{r.result||(r.result=r.canceled?"canceled":"gave-up"),Pa.get(e)===r&&Pa.delete(e),o()}),r}async function MH(e,t,n){await N(e,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:e.leadBox,provider:t?.agent||null,payload:n}).catch(()=>{})}async function CH(e){let t=Ia(await G(e));if(!t?.offerId)return null;let n=t.offerType==="exists";return await N(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 PH(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 NH){await $r(r);let o=await De(e.leadBox,{lines:Qe}).catch(()=>""),s=Gi(t.agent,o);if(s&&Iu(n.text,s))return await K_(e.leadBox,t.agent,n.text.length+8),Te(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:n.at,text:n.text}),n.text}return null}async function jH(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>DH||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!Ue(r.text))return null;for(let s of BH){await $r(s);let a;try{a=await lh(t,{})}catch{return null}if(Ex(a,r))return null;let i=await De(e.leadBox,{lines:Qe}).catch(()=>"");if(Gi(t.agent,i))return null}return Te(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}async function LH(e,t,n,r=null){return zi({sessionName:e.leadBox,agent:t?.agent,agentCmd:bs(t?.agent),text:n,trackId:e.id,flow:"say",stats:r})}async function K_(e,t,n,r=null){for(let o=0;o<3;o++){await Js(e,n,{kind:"unsend-wipe"}).catch(()=>{}),await $r(200);let s=await De(e,{lines:Qe}).catch(()=>""),a=Gi(t,s);if(!a||r&&!Iu(r,a)&&!(t==="codex"&&Rm(r,a)))return!0;n=a.length+8}return!1}function Da(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 ja(e,t){let n=[],r=o=>n.some(s=>Ue(s).includes(Ue(o)));for(let o of[e.draft?.text,...t])o&&o.trim()&&!r(o)&&n.push(o);return n.length?Da(e,n.join(`
|
|
227
227
|
|
|
228
|
-
`),{source:"restore"})?.text??null:null}function
|
|
228
|
+
`),{source:"restore"})?.text??null:null}function UH(e,t){let n=Ue(t);if(!n)return!1;if(gs(e.id).some(o=>Ue(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>FH)break;if(!(s.type!=="said"||!s.text||Ue(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function Zg(e,t,{rev:n=null}={}){if(typeof t!="string")throw new Error("text (string) required");let r=de(await Q(),e);if(!r)throw new Error(`track "${e}" not found`);let o=t,s=da(t);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&UH(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;Ue(o).includes(Ue(i))?Da(r,o):Da(r,`${i}
|
|
229
229
|
|
|
230
|
-
${o}`,{source:"restore"})}else Da(r,o);return await ae(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Zg(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=pe(await Q(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await xe(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(d=>d.name===r.leadBox)?.agent||o.agent||"claude",i=QS(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await Pi(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await sr({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:bs(o.agent)});let u=await eA(a,r.leadBox,i);return Te(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await ae(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function q_(e){for(let t of e){if(!t||!await pa(t))continue;let n=await MS(t).catch(()=>null);if(!n)continue;if((await z.stat(V.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function jt(e){let t=e;return t?t.kind==="multi"?Array.isArray(t.repos)?t.repos.filter(Boolean):[]:t.base?[{name:V.basename(t.base),dir:t.dir??null,base:t.base,baseRef:t.baseRef??null,repo:t.repo??null}]:[]:[]}async function ps(e){let t=[];for(let n of jt(e?.workspace)){if(!n?.dir)continue;let r=await Yo(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await Xo(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function Qg(e){let t=e;typeof t=="string"&&!V.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await Bs(t)??await Vl(t));let n=await q_([t]);if(!n){let r=typeof e=="string"&&V.isAbsolute(e)&&!await z.stat(e).catch(()=>null);throw new Error(`tagged repo "${e}" is not a usable base (${r?"that path does not exist on this node \u2014 pick one of this node's repos or use owner/name":"need a primary checkout or a synced owner/name"})`)}return n}async function LH(e,t,n,r){let o=`track/${Ip(t)}-${e}`,s=[];try{await z.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await Qg(i);if(s.some(f=>f.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await Oe(c).catch(()=>null),u=V.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let f=2;a.has(u);f++)u=`${V.basename(c)}-${f}`;a.add(u);let d=V.join(n,u),p=await Qo(c);await Zo(c,d,o,p),s.push({name:u,dir:d,base:c,baseRef:p,repo:l})}return await Ha(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await Tt(i.base,i.dir).catch(()=>{});throw await z.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function gp(e,t){let n=await Q(),r=pe(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 Qg(typeof t=="string"?t.trim():t);if((o.repos??[]).some(p=>p?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await Oe(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(p=>p?.name).filter(Boolean)]),c=V.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let p=2;i.has(c);p++)c=`${V.basename(s)}-${p}`;let l=V.join(o.dir,c),u=await Qo(s);await Th(s,o.branch)?await nc(s,l,o.branch):await Zo(s,l,o.branch,u);let d={name:c,dir:l,base:s,baseRef:u,repo:a};return o.repos=[...(o.repos??[]).filter(Boolean),d],await Ha(o.dir,o.repos,o.branch),Te(r,"retagged",{action:"add",repo:a??s,name:c,workspace:ey(o)}),await ae(),d}async function yp(e,t){let n=await Q(),r=pe(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 Bt.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 Yo(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await Xo(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 Tt(s.base,s.dir).catch(()=>{}),await z.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await Ha(o.dir,o.repos,o.branch),Te(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:ey(o)}),await ae(),{removed:s.name}}async function op(e,t){let n=await Q(),r=pe(n,e);r&&(r.reshape={...r.reshape||{},...t},await ae())}async function FH(e){let t=await z.readdir(e).catch(()=>[]);for(let n of t)if(!(n===".git"||n===".bostrat")){if(n==="AGENTS.md"){let r=await z.readFile(V.join(e,n),"utf8").catch(()=>"");if(Fc(r))continue}return!0}try{let{stdout:n}=await lp("git",["-C",e,"rev-list","--count","HEAD"]);if(Number(n.trim())>1)return!0}catch{}return!1}function $_(e,t,n){let r=V.basename(e);n.has(r)&&t&&(r=t.replace("/","-"));for(let o=2;n.has(r);o++)r=`${V.basename(e)}-${o}`;return n.add(r),r}async function G_(e,{added:t=[]}={}){let{shape:n,dir:r,staging:o,sub:s,base:a,scratch:i}=e;for(let l of t)await Tt(l.base,l.dir).catch(()=>{});let c=async(l,u)=>{await xn(l)&&!await xn(u)&&await z.rename(l,u)};if(n==="worktree"&&s){let l=V.join(r,s);await xn(l)&&(await c(V.join(r,".bostrat"),V.join(l,".bostrat")),await sp(a,l,o))}if(n==="repo"&&i){let l=V.join(r,i);await xn(l)&&(await xn(o)&&await z.rm(o,{recursive:!0,force:!0}),await z.rename(l,o))}return await xn(o)?(await xn(r)&&(await c(V.join(r,".bostrat"),V.join(o,".bostrat")),await z.rm(r,{recursive:!0,force:!0})),n==="worktree"?await sp(a,o,r):await z.rename(o,r),!0):!1}async function ty(e,t,{skipPermissions:n=!0}={}){let r=await Q(),o=pe(r,e);if(!o)throw kt(`track "${e}" not found`,404);if(o.archivedAt)throw kt("track is archived \u2014 unarchive it first");try{Ap(o)}catch(g){throw Object.assign(g,{status:409})}if(o.kind==="setup")throw kt("setup boxes have no repos to reshape");if(String(o.name||"").startsWith("\u21BB"))throw kt("this Track's repos are the loop's \u2014 propose-update the loop instead");let s=Dg.get(e);if(s&&!s.finishedAt)throw kt("a reshape is already running for this Track");let a=ys.get(e);if(a&&!a.finishedAt)throw kt("a lead handoff is running \u2014 retry when it settles");let i=ip.get(e);if(i&&!i.finishedAt)throw kt("a node transfer is running \u2014 retry when it settles");if(np.has(e))throw kt("an adopt is running for this Track \u2014 retry when it settles");for(let g of Bt.values())if(g.trackId===e&&!g.finishedAt)throw kt("a subtask is still running on this Track \u2014 wait for it to merge back before reshaping");let c=(Array.isArray(t)?t:[]).map(g=>typeof g=="string"?g.trim():"").filter(Boolean);if(!c.length)throw kt("repos (non-empty list of paths or owner/name slugs) required",400);let l=[];for(let g of c){let y;try{y=await Qg(g)}catch(b){throw kt(b?.message||String(b),400)}if(l.some(b=>b.base===y))throw kt(`repo tagged twice: ${g}`,400);l.push({sel:g,base:y,slug:await Oe(y).catch(()=>null)})}let u=o.workspace;if(u?.kind==="multi"){let g=(u.repos??[]).filter(Boolean),y=g.filter(S=>!l.some($=>$.base===S.base)),b=l.filter(S=>!g.some($=>$.base===S.base));if(!y.length&&!b.length)return{trackId:e,step:"done",inPlace:!0,added:[],removed:[],startedAt:Date.now(),finishedAt:Date.now(),error:null};for(let S of y){let $=await Yo(S.dir,{ignorePlumbing:!0}).catch(()=>!1),M=await Xo(S.dir,S.baseRef).catch(()=>0);if($||M>0)throw kt(`"${S.name}" has work \u2014 ship or discard it before dropping it`)}let k={trackId:e,step:"retag",inPlace:!0,added:[],removed:[],startedAt:Date.now(),finishedAt:null,error:null};for(let S of b){let $=await gp(e,S.base);k.added.push($.repo??$.base)}for(let S of y)await yp(e,S.name),k.removed.push(S.repo??S.base);return k.step="done",k.finishedAt=Date.now(),k}if(u?.kind!=="worktree"&&u?.kind!=="repo")throw kt(`can't reshape a "${u?.kind??"unknown"}" workspace`);let d=u.kind==="worktree"&&l.some(g=>g.base===u.base);if(u.kind==="worktree"&&!d){let g=await Yo(u.dir??o.cwd,{ignorePlumbing:!0}).catch(()=>!1),y=await Xo(u.dir??o.cwd,u.baseRef).catch(()=>0);if(g||y>0)throw kt(`"${V.basename(u.base)}" has work (${g?"uncommitted changes":`${y} commit${y===1?"":"s"} ahead of ${u.baseRef??"base"}`}) \u2014 ship or discard it before swapping it out`)}Ua(e,"reshape");let p=o.boxes.find(g=>g.name===o.leadBox)||null;if(!p)throw kt("this Track has no lead box");let f=p.agent||"claude",w=await Fa(f,n),m={trackId:e,step:"checkpoint",inPlace:!1,startedAt:Date.now(),finishedAt:null,error:null,added:l.filter(g=>!(u.kind==="worktree"&&g.base===u.base)).map(g=>g.slug??g.base),removed:u.kind==="worktree"&&!d?[u.repo??u.base]:[],scratch:!1,views:0,checkpoint:null};return Dg.set(e,m),await op(e,{step:m.step,startedAt:m.startedAt,finishedAt:null,error:null,added:m.added,removed:m.removed,layout:null}),UH(m,o,{desired:l,keep:d,lead:p,agent:f,agentCmd:w,skipPermissions:n}).catch(async g=>{m.error=g?.message||String(g),m.finishedAt=Date.now(),console.error(`[reshape ${e}] FAILED at step "${m.step}": ${m.error}`),await op(e,{step:m.step,error:m.error,finishedAt:m.finishedAt}).catch(()=>{})}),m}async function UH(e,t,{desired:n,keep:r,lead:o,agent:s,agentCmd:a,skipPermissions:i}){let c=e.trackId,l=t.workspace,u=t.cwd,d=async(E,C={})=>{e.step=E,console.log(`[reshape ${c}] step \u2192 ${E}`),await op(c,{step:E,...C})},p=await xe(o.name).catch(()=>null),f=o.sessionId??p?.sessionId??null,w=o.pinned===!0;if(await d("checkpoint"),e.checkpoint="llm",!p)e.checkpoint="skipped-dead";else if(no(s))e.checkpoint="skipped-capped";else try{await ju(o.name,{timeoutMs:Lg})}catch{e.checkpoint="reconstructed"}let m=null;e.checkpoint!=="llm"&&(m=await Lu(c,u,{trackName:t.name||c}).catch(()=>null)),await d("quiesce"),e.views=await Ww(c).catch(()=>0),await nu(u).catch(()=>{}),p&&(await Ym(o.name,{fromAgent:s}).catch(()=>{}),await Ct(o.name).catch(()=>{}));let g=`${u}.reshape-${Date.now()}`,y=new Set([".bostrat"]),b={shape:l.kind,dir:u,staging:g,base:l.kind==="worktree"?l.base:null,sub:l.kind==="worktree"&&r?$_(l.base,l.repo??null,y):null,scratch:null},k=l.branch||`track/${Ip(t.name||c)}-${c}`,S=[],$=[],M=null;try{if(await d("move",{layout:b}),l.kind==="worktree")if(await sp(l.base,u,g),await z.mkdir(u,{recursive:!0}),r){let C=V.join(u,b.sub);await sp(l.base,g,C),await xn(V.join(C,".bostrat"))&&await z.rename(V.join(C,".bostrat"),V.join(u,".bostrat"));let P=V.join(C,"AGENTS.md");Fc(await z.readFile(P,"utf8").catch(()=>null))&&(await z.rm(P,{force:!0}).catch(()=>{}),await z_(C,"/AGENTS.md").catch(()=>{})),S.push({name:b.sub,dir:C,base:l.base,baseRef:l.baseRef??null,repo:l.repo??null})}else await xn(V.join(g,".bostrat"))&&await z.rename(V.join(g,".bostrat"),V.join(u,".bostrat"));else await z.rename(u,g),await z.mkdir(u,{recursive:!0}),await xn(V.join(g,".bostrat"))&&await z.rename(V.join(g,".bostrat"),V.join(u,".bostrat"));await d("materialize");for(let C of n){if(l.kind==="worktree"&&C.base===l.base)continue;let P=$_(C.base,C.slug,y),W=V.join(u,P),Y=await Qo(C.base);await Th(C.base,k)?await nc(C.base,W,k):await Zo(C.base,W,k,Y);let me={name:P,dir:W,base:C.base,baseRef:Y,repo:C.slug};S.push(me),$.push(me)}if(l.kind==="repo"&&await FH(g)){let C="scratch";for(let P=2;y.has(C);P++)C=`scratch-${P}`;y.add(C),b.scratch=C,await op(c,{layout:b}),await z.rename(g,V.join(u,C)),e.scratch=!0}await Ha(u,S,k,{scratch:b.scratch}),await d("relaunch"),M=await On(o.name,u,{agentCmd:a,agent:s}),await sr({cwd:u,sessionName:o.name,agent:s,agentCmd:bs(s)}).catch(C=>{if(C?.code!=="cli-not-running")throw C});let E=await De(o.name,{lines:Qe}).catch(()=>"");if(!Wo(s,E))throw new Error(`relaunched ${s} CLI never drew its composer in the hub`)}catch(E){await d("rollback",{error:E?.message||String(E)}),await Ct(o.name).catch(()=>{}),await G_(b,{added:$}).catch(Y=>{console.error(`[reshape ${c}] rollback move failed: ${Y?.message||Y}`)});let C=await Q(),P=pe(C,c),W=P?.boxes.find(Y=>Y.name===o.name)||null;try{let Y=dp(s,f,{skipPermissions:i,cwd:u,peerName:o.name}),me=await On(o.name,u,{agentCmd:Y,agent:s,resumeSessionId:f,pinned:w});W&&(W.hookTokenHash=Oo(me.hookToken),W.state="live"),qo({cwd:u,sessionName:o.name,agent:s,agentCmd:Y})}catch(Y){console.error(`[reshape ${c}] rollback relaunch failed: ${Y?.message||Y}`)}throw P&&(Te(P,"retag-failed",{action:"reshape",box:o.name,agent:s,reason:E?.message||String(E),rolledBack:!0}),P.reshape={...P.reshape||{},step:"error",error:E?.message||String(E),finishedAt:Date.now(),recovered:"rolled-back"},await ae()),e.step="error",e.recovered="rolled-back",e.error=E?.message||String(E),e.finishedAt=Date.now(),E}await d("commit");let L=await Q(),B=pe(L,c);if(!B)throw new Error(`track "${c}" vanished mid-reshape`);let A=B.boxes.find(E=>E.name===o.name);if(!A)throw new Error(`lead box row "${o.name}" missing during reshape commit`);let I=Date.now(),j=wo(A),K=j[j.length-1];K.endedAt==null&&(K.endedAt=I),K.endReason==null&&(K.endReason="reshape");let R=await Ko({agent:s,agentCmd:a});j.push({agent:s,sessionId:M.sessionId,pinned:M.pinned===!0,cliVersion:R,startedAt:I,endedAt:null}),A.cwd=u,A.agent=s,A.cliVersion=R,A.sessionId=M.sessionId,A.pinned=M.pinned===!0,A.hookTokenHash=Oo(M.hookToken),A.state="live",A.role="lead",delete A.readyAt,B.cwd=u,B.workspace={dir:u,kind:"multi",branch:k,repos:S},B.pendingHandoffHydrate={artifact:m||go,at:I},Te(B,"retagged",{action:"reshape",added:e.added,removed:e.removed,scratch:e.scratch,checkpoint:e.checkpoint,views:e.views,box:o.name,agent:s,fromSessionId:f,toSessionId:M.sessionId,epoch:j.length-1,workspace:ey(B.workspace)}),B.reshape={...B.reshape||{},step:"done",finishedAt:I,error:null,checkpoint:e.checkpoint,scratch:e.scratch,views:e.views,layout:null},await ae(),qo({cwd:u,sessionName:o.name,agent:s,agentCmd:a,onReady:async()=>{A.readyAt||(A.readyAt=Date.now(),await ae())}}),await xn(g)&&(l.kind==="worktree"&&await Tt(l.base,g).catch(()=>{}),await z.rm(g,{recursive:!0,force:!0}).catch(()=>{})),e.step="done",e.finishedAt=I}async function ny(){let e=await Q(),t=0;for(let n of e.tracks||[]){let r=n.reshape;if(!r||r.finishedAt)continue;t++;let o=n.boxes.find(c=>c.name===n.leadBox)||null;o&&await Ct(o.name).catch(()=>{});let s=!1,a=null;if(r.layout)try{let{dir:c,sub:l,scratch:u}=r.layout,d=new Set([".bostrat",l,u].filter(Boolean)),p=[];for(let f of await z.readdir(c).catch(()=>[])){if(d.has(f))continue;let w=V.join(c,f),m=await z.readFile(V.join(w,".git"),"utf8").catch(()=>null),g=m&&/^gitdir:\s*(.+?)\/\.git\/worktrees\//m.exec(m);g&&p.push({base:g[1],dir:w})}s=await G_(r.layout,{added:p})}catch(c){a=c?.message||String(c),console.error(`[reshape ${n.id}] boot rollback failed: ${a}`)}o&&!_r.has(o.state)&&(o.state="stopped");let i=`interrupted by a node restart during "${r.step}"${s?" \u2014 moved back":""}${a?` (${a})`:""}`;Te(n,"retag-failed",{action:"reshape",box:n.leadBox,agent:o?.agent??null,reason:i,rolledBack:!a,recovered:!0}),n.reshape={...r,step:"error",error:i,finishedAt:Date.now(),recovered:a?"rollback-failed":"rolled-back",layout:null}}return t&&await ae(),t}async function HH(e,t,{repo:n,repos:r,cwd:o}={}){await z.mkdir(Uc,{recursive:!0});let s=V.join(Uc,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return LH(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!V.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await Bs(n)??await Vl(n));let i=await q_([n,o,Q2]);if(i){let c=`track/${Ip(t)}-${e}`;try{let l=await Qo(i);await Zo(i,s,c,l),await Gc(s);let u=await Oe(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await z.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await z.mkdir(s,{recursive:!0}),await Eh(s),await Gc(s),{dir:s,base:null,branch:null,kind:"repo"}}async function Gc(e){try{await z.writeFile(V.join(e,"AGENTS.md"),Lc,{flag:"wx"})}catch{return}await KH(e,["/AGENTS.md"])}function GH(e,t,{scratch:n=null}={}){let r=e.map(s=>`- \`${s.name}/\`${s.repo?` \u2014 github.com/${s.repo}`:""}${s.baseRef?` (forked from ${s.baseRef})`:""}`).join(`
|
|
230
|
+
${o}`,{source:"restore"})}else Da(r,o);return await ae(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Qg(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=de(await Q(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await xe(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(d=>d.name===r.leadBox)?.agent||o.agent||"claude",i=tA(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await Di(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await sr({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:bs(o.agent)});let u=await nA(a,r.leadBox,i);return Te(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await ae(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function V_(e){for(let t of e){if(!t||!await pa(t))continue;let n=await NS(t).catch(()=>null);if(!n)continue;if((await z.stat(V.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function jt(e){let t=e;return t?t.kind==="multi"?Array.isArray(t.repos)?t.repos.filter(Boolean):[]:t.base?[{name:V.basename(t.base),dir:t.dir??null,base:t.base,baseRef:t.baseRef??null,repo:t.repo??null}]:[]:[]}async function ps(e){let t=[];for(let n of jt(e?.workspace)){if(!n?.dir)continue;let r=await Yo(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await Xo(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function ey(e){let t=e;typeof t=="string"&&!V.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await Bs(t)??await Vl(t));let n=await V_([t]);if(!n){let r=typeof e=="string"&&V.isAbsolute(e)&&!await z.stat(e).catch(()=>null);throw new Error(`tagged repo "${e}" is not a usable base (${r?"that path does not exist on this node \u2014 pick one of this node's repos or use owner/name":"need a primary checkout or a synced owner/name"})`)}return n}async function HH(e,t,n,r){let o=`track/${_p(t)}-${e}`,s=[];try{await z.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await ey(i);if(s.some(f=>f.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await Oe(c).catch(()=>null),u=V.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let f=2;a.has(u);f++)u=`${V.basename(c)}-${f}`;a.add(u);let d=V.join(n,u),p=await Qo(c);await Zo(c,d,o,p),s.push({name:u,dir:d,base:c,baseRef:p,repo:l})}return await Ha(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await Tt(i.base,i.dir).catch(()=>{});throw await z.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function yp(e,t){let n=await Q(),r=de(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 ey(typeof t=="string"?t.trim():t);if((o.repos??[]).some(p=>p?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await Oe(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(p=>p?.name).filter(Boolean)]),c=V.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let p=2;i.has(c);p++)c=`${V.basename(s)}-${p}`;let l=V.join(o.dir,c),u=await Qo(s);await Th(s,o.branch)?await rc(s,l,o.branch):await Zo(s,l,o.branch,u);let d={name:c,dir:l,base:s,baseRef:u,repo:a};return o.repos=[...(o.repos??[]).filter(Boolean),d],await Ha(o.dir,o.repos,o.branch),Te(r,"retagged",{action:"add",repo:a??s,name:c,workspace:ty(o)}),await ae(),d}async function bp(e,t){let n=await Q(),r=de(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 Bt.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 Yo(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await Xo(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 Tt(s.base,s.dir).catch(()=>{}),await z.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await Ha(o.dir,o.repos,o.branch),Te(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:ty(o)}),await ae(),{removed:s.name}}async function sp(e,t){let n=await Q(),r=de(n,e);r&&(r.reshape={...r.reshape||{},...t},await ae())}async function WH(e){let t=await z.readdir(e).catch(()=>[]);for(let n of t)if(!(n===".git"||n===".bostrat")){if(n==="AGENTS.md"){let r=await z.readFile(V.join(e,n),"utf8").catch(()=>"");if(Uc(r))continue}return!0}try{let{stdout:n}=await up("git",["-C",e,"rev-list","--count","HEAD"]);if(Number(n.trim())>1)return!0}catch{}return!1}function O_(e,t,n){let r=V.basename(e);n.has(r)&&t&&(r=t.replace("/","-"));for(let o=2;n.has(r);o++)r=`${V.basename(e)}-${o}`;return n.add(r),r}async function z_(e,{added:t=[]}={}){let{shape:n,dir:r,staging:o,sub:s,base:a,scratch:i}=e;for(let l of t)await Tt(l.base,l.dir).catch(()=>{});let c=async(l,u)=>{await xn(l)&&!await xn(u)&&await z.rename(l,u)};if(n==="worktree"&&s){let l=V.join(r,s);await xn(l)&&(await c(V.join(r,".bostrat"),V.join(l,".bostrat")),await ap(a,l,o))}if(n==="repo"&&i){let l=V.join(r,i);await xn(l)&&(await xn(o)&&await z.rm(o,{recursive:!0,force:!0}),await z.rename(l,o))}return await xn(o)?(await xn(r)&&(await c(V.join(r,".bostrat"),V.join(o,".bostrat")),await z.rm(r,{recursive:!0,force:!0})),n==="worktree"?await ap(a,o,r):await z.rename(o,r),!0):!1}async function ny(e,t,{skipPermissions:n=!0}={}){let r=await Q(),o=de(r,e);if(!o)throw kt(`track "${e}" not found`,404);if(o.archivedAt)throw kt("track is archived \u2014 unarchive it first");try{Tp(o)}catch(g){throw Object.assign(g,{status:409})}if(o.kind==="setup")throw kt("setup boxes have no repos to reshape");if(String(o.name||"").startsWith("\u21BB"))throw kt("this Track's repos are the loop's \u2014 propose-update the loop instead");let s=Bg.get(e);if(s&&!s.finishedAt)throw kt("a reshape is already running for this Track");let a=ys.get(e);if(a&&!a.finishedAt)throw kt("a lead handoff is running \u2014 retry when it settles");let i=cp.get(e);if(i&&!i.finishedAt)throw kt("a node transfer is running \u2014 retry when it settles");if(rp.has(e))throw kt("an adopt is running for this Track \u2014 retry when it settles");for(let g of Bt.values())if(g.trackId===e&&!g.finishedAt)throw kt("a subtask is still running on this Track \u2014 wait for it to merge back before reshaping");let c=(Array.isArray(t)?t:[]).map(g=>typeof g=="string"?g.trim():"").filter(Boolean);if(!c.length)throw kt("repos (non-empty list of paths or owner/name slugs) required",400);let l=[];for(let g of c){let y;try{y=await ey(g)}catch(b){throw kt(b?.message||String(b),400)}if(l.some(b=>b.base===y))throw kt(`repo tagged twice: ${g}`,400);l.push({sel:g,base:y,slug:await Oe(y).catch(()=>null)})}let u=o.workspace;if(u?.kind==="multi"){let g=(u.repos??[]).filter(Boolean),y=g.filter(S=>!l.some($=>$.base===S.base)),b=l.filter(S=>!g.some($=>$.base===S.base));if(!y.length&&!b.length)return{trackId:e,step:"done",inPlace:!0,added:[],removed:[],startedAt:Date.now(),finishedAt:Date.now(),error:null};for(let S of y){let $=await Yo(S.dir,{ignorePlumbing:!0}).catch(()=>!1),M=await Xo(S.dir,S.baseRef).catch(()=>0);if($||M>0)throw kt(`"${S.name}" has work \u2014 ship or discard it before dropping it`)}let k={trackId:e,step:"retag",inPlace:!0,added:[],removed:[],startedAt:Date.now(),finishedAt:null,error:null};for(let S of b){let $=await yp(e,S.base);k.added.push($.repo??$.base)}for(let S of y)await bp(e,S.name),k.removed.push(S.repo??S.base);return k.step="done",k.finishedAt=Date.now(),k}if(u?.kind!=="worktree"&&u?.kind!=="repo")throw kt(`can't reshape a "${u?.kind??"unknown"}" workspace`);let d=u.kind==="worktree"&&l.some(g=>g.base===u.base);if(u.kind==="worktree"&&!d){let g=await Yo(u.dir??o.cwd,{ignorePlumbing:!0}).catch(()=>!1),y=await Xo(u.dir??o.cwd,u.baseRef).catch(()=>0);if(g||y>0)throw kt(`"${V.basename(u.base)}" has work (${g?"uncommitted changes":`${y} commit${y===1?"":"s"} ahead of ${u.baseRef??"base"}`}) \u2014 ship or discard it before swapping it out`)}Ua(e,"reshape");let p=o.boxes.find(g=>g.name===o.leadBox)||null;if(!p)throw kt("this Track has no lead box");let f=p.agent||"claude",w=await Fa(f,n),m={trackId:e,step:"checkpoint",inPlace:!1,startedAt:Date.now(),finishedAt:null,error:null,added:l.filter(g=>!(u.kind==="worktree"&&g.base===u.base)).map(g=>g.slug??g.base),removed:u.kind==="worktree"&&!d?[u.repo??u.base]:[],scratch:!1,views:0,checkpoint:null};return Bg.set(e,m),await sp(e,{step:m.step,startedAt:m.startedAt,finishedAt:null,error:null,added:m.added,removed:m.removed,layout:null}),qH(m,o,{desired:l,keep:d,lead:p,agent:f,agentCmd:w,skipPermissions:n}).catch(async g=>{m.error=g?.message||String(g),m.finishedAt=Date.now(),console.error(`[reshape ${e}] FAILED at step "${m.step}": ${m.error}`),await sp(e,{step:m.step,error:m.error,finishedAt:m.finishedAt}).catch(()=>{})}),m}async function qH(e,t,{desired:n,keep:r,lead:o,agent:s,agentCmd:a,skipPermissions:i}){let c=e.trackId,l=t.workspace,u=t.cwd,d=async(T,C={})=>{e.step=T,console.log(`[reshape ${c}] step \u2192 ${T}`),await sp(c,{step:T,...C})},p=await xe(o.name).catch(()=>null),f=o.sessionId??p?.sessionId??null,w=o.pinned===!0;if(await d("checkpoint"),e.checkpoint="llm",!p)e.checkpoint="skipped-dead";else if(no(s))e.checkpoint="skipped-capped";else try{await Lu(o.name,{timeoutMs:Fg})}catch{e.checkpoint="reconstructed"}let m=null;e.checkpoint!=="llm"&&(m=await Fu(c,u,{trackName:t.name||c}).catch(()=>null)),await d("quiesce"),e.views=await Ww(c).catch(()=>0),await nu(u).catch(()=>{}),p&&(await Ym(o.name,{fromAgent:s}).catch(()=>{}),await Ct(o.name).catch(()=>{}));let g=`${u}.reshape-${Date.now()}`,y=new Set([".bostrat"]),b={shape:l.kind,dir:u,staging:g,base:l.kind==="worktree"?l.base:null,sub:l.kind==="worktree"&&r?O_(l.base,l.repo??null,y):null,scratch:null},k=l.branch||`track/${_p(t.name||c)}-${c}`,S=[],$=[],M=null;try{if(await d("move",{layout:b}),l.kind==="worktree")if(await ap(l.base,u,g),await z.mkdir(u,{recursive:!0}),r){let C=V.join(u,b.sub);await ap(l.base,g,C),await xn(V.join(C,".bostrat"))&&await z.rename(V.join(C,".bostrat"),V.join(u,".bostrat"));let P=V.join(C,"AGENTS.md");Uc(await z.readFile(P,"utf8").catch(()=>null))&&(await z.rm(P,{force:!0}).catch(()=>{}),await X_(C,"/AGENTS.md").catch(()=>{})),S.push({name:b.sub,dir:C,base:l.base,baseRef:l.baseRef??null,repo:l.repo??null})}else await xn(V.join(g,".bostrat"))&&await z.rename(V.join(g,".bostrat"),V.join(u,".bostrat"));else await z.rename(u,g),await z.mkdir(u,{recursive:!0}),await xn(V.join(g,".bostrat"))&&await z.rename(V.join(g,".bostrat"),V.join(u,".bostrat"));await d("materialize");for(let C of n){if(l.kind==="worktree"&&C.base===l.base)continue;let P=O_(C.base,C.slug,y),W=V.join(u,P),Y=await Qo(C.base);await Th(C.base,k)?await rc(C.base,W,k):await Zo(C.base,W,k,Y);let me={name:P,dir:W,base:C.base,baseRef:Y,repo:C.slug};S.push(me),$.push(me)}if(l.kind==="repo"&&await WH(g)){let C="scratch";for(let P=2;y.has(C);P++)C=`scratch-${P}`;y.add(C),b.scratch=C,await sp(c,{layout:b}),await z.rename(g,V.join(u,C)),e.scratch=!0}await Ha(u,S,k,{scratch:b.scratch}),await d("relaunch"),M=await On(o.name,u,{agentCmd:a,agent:s}),await sr({cwd:u,sessionName:o.name,agent:s,agentCmd:bs(s)}).catch(C=>{if(C?.code!=="cli-not-running")throw C});let T=await De(o.name,{lines:Qe}).catch(()=>"");if(!Wo(s,T))throw new Error(`relaunched ${s} CLI never drew its composer in the hub`)}catch(T){await d("rollback",{error:T?.message||String(T)}),await Ct(o.name).catch(()=>{}),await z_(b,{added:$}).catch(Y=>{console.error(`[reshape ${c}] rollback move failed: ${Y?.message||Y}`)});let C=await Q(),P=de(C,c),W=P?.boxes.find(Y=>Y.name===o.name)||null;try{let Y=pp(s,f,{skipPermissions:i,cwd:u,peerName:o.name}),me=await On(o.name,u,{agentCmd:Y,agent:s,resumeSessionId:f,pinned:w});W&&(W.hookTokenHash=Oo(me.hookToken),W.state="live"),qo({cwd:u,sessionName:o.name,agent:s,agentCmd:Y})}catch(Y){console.error(`[reshape ${c}] rollback relaunch failed: ${Y?.message||Y}`)}throw P&&(Te(P,"retag-failed",{action:"reshape",box:o.name,agent:s,reason:T?.message||String(T),rolledBack:!0}),P.reshape={...P.reshape||{},step:"error",error:T?.message||String(T),finishedAt:Date.now(),recovered:"rolled-back"},await ae()),e.step="error",e.recovered="rolled-back",e.error=T?.message||String(T),e.finishedAt=Date.now(),T}await d("commit");let L=await Q(),B=de(L,c);if(!B)throw new Error(`track "${c}" vanished mid-reshape`);let A=B.boxes.find(T=>T.name===o.name);if(!A)throw new Error(`lead box row "${o.name}" missing during reshape commit`);let I=Date.now(),j=wo(A),K=j[j.length-1];K.endedAt==null&&(K.endedAt=I),K.endReason==null&&(K.endReason="reshape");let R=await Ko({agent:s,agentCmd:a});j.push({agent:s,sessionId:M.sessionId,pinned:M.pinned===!0,cliVersion:R,startedAt:I,endedAt:null}),A.cwd=u,A.agent=s,A.cliVersion=R,A.sessionId=M.sessionId,A.pinned=M.pinned===!0,A.hookTokenHash=Oo(M.hookToken),A.state="live",A.role="lead",delete A.readyAt,B.cwd=u,B.workspace={dir:u,kind:"multi",branch:k,repos:S},B.pendingHandoffHydrate={artifact:m||go,at:I},Te(B,"retagged",{action:"reshape",added:e.added,removed:e.removed,scratch:e.scratch,checkpoint:e.checkpoint,views:e.views,box:o.name,agent:s,fromSessionId:f,toSessionId:M.sessionId,epoch:j.length-1,workspace:ty(B.workspace)}),B.reshape={...B.reshape||{},step:"done",finishedAt:I,error:null,checkpoint:e.checkpoint,scratch:e.scratch,views:e.views,layout:null},await ae(),qo({cwd:u,sessionName:o.name,agent:s,agentCmd:a,onReady:async()=>{A.readyAt||(A.readyAt=Date.now(),await ae())}}),await xn(g)&&(l.kind==="worktree"&&await Tt(l.base,g).catch(()=>{}),await z.rm(g,{recursive:!0,force:!0}).catch(()=>{})),e.step="done",e.finishedAt=I}async function ry(){let e=await Q(),t=0;for(let n of e.tracks||[]){let r=n.reshape;if(!r||r.finishedAt)continue;t++;let o=n.boxes.find(c=>c.name===n.leadBox)||null;o&&await Ct(o.name).catch(()=>{});let s=!1,a=null;if(r.layout)try{let{dir:c,sub:l,scratch:u}=r.layout,d=new Set([".bostrat",l,u].filter(Boolean)),p=[];for(let f of await z.readdir(c).catch(()=>[])){if(d.has(f))continue;let w=V.join(c,f),m=await z.readFile(V.join(w,".git"),"utf8").catch(()=>null),g=m&&/^gitdir:\s*(.+?)\/\.git\/worktrees\//m.exec(m);g&&p.push({base:g[1],dir:w})}s=await z_(r.layout,{added:p})}catch(c){a=c?.message||String(c),console.error(`[reshape ${n.id}] boot rollback failed: ${a}`)}o&&!_r.has(o.state)&&(o.state="stopped");let i=`interrupted by a node restart during "${r.step}"${s?" \u2014 moved back":""}${a?` (${a})`:""}`;Te(n,"retag-failed",{action:"reshape",box:n.leadBox,agent:o?.agent??null,reason:i,rolledBack:!a,recovered:!0}),n.reshape={...r,step:"error",error:i,finishedAt:Date.now(),recovered:a?"rollback-failed":"rolled-back",layout:null}}return t&&await ae(),t}async function GH(e,t,{repo:n,repos:r,cwd:o}={}){await z.mkdir(Hc,{recursive:!0});let s=V.join(Hc,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return HH(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!V.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await Bs(n)??await Vl(n));let i=await V_([n,o,nH]);if(i){let c=`track/${_p(t)}-${e}`;try{let l=await Qo(i);await Zo(i,s,c,l),await Kc(s);let u=await Oe(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await z.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await z.mkdir(s,{recursive:!0}),await Eh(s),await Kc(s),{dir:s,base:null,branch:null,kind:"repo"}}async function Kc(e){try{await z.writeFile(V.join(e,"AGENTS.md"),Fc,{flag:"wx"})}catch{return}await JH(e,["/AGENTS.md"])}function zH(e,t,{scratch:n=null}={}){let r=e.map(s=>`- \`${s.name}/\`${s.repo?` \u2014 github.com/${s.repo}`:""}${s.baseRef?` (forked from ${s.baseRef})`:""}`).join(`
|
|
231
231
|
`),o=n?`
|
|
232
232
|
\`${n}/\` is this Track's pre-repo workspace, carried over when the
|
|
233
233
|
Track was reshaped onto these repos. It is not part of any tagged repo and
|
|
234
234
|
cannot be shipped \u2014 move anything worth keeping into the repo it belongs to.
|
|
235
|
-
`:"";return`${
|
|
235
|
+
`:"";return`${VH}
|
|
236
236
|
|
|
237
237
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
238
238
|
git worktree per tagged repo, ALL on the same branch \`${t}\`:
|
|
@@ -244,20 +244,20 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
244
244
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
245
245
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
246
246
|
or preview an app, run it from that app's own repo directory.
|
|
247
|
-
${o}`}async function Ha(e,t,n,r={}){let o=
|
|
247
|
+
${o}`}async function Ha(e,t,n,r={}){let o=zH(t,n,r);for(let s of["CLAUDE.md","AGENTS.md"])await z.writeFile(V.join(e,s),o).catch(()=>{})}async function Y_(e){let{stdout:t}=await up("git",["-C",e,"rev-parse","--git-path","info/exclude"]);return V.resolve(e,t.trim())}async function JH(e,t){try{let n=await Y_(e);await z.mkdir(V.dirname(n),{recursive:!0});let r=await z.readFile(n,"utf8").catch(()=>""),o=t.filter(s=>!r.split(`
|
|
248
248
|
`).includes(s));o.length&&await z.appendFile(n,(r.endsWith(`
|
|
249
249
|
`)||!r?"":`
|
|
250
250
|
`)+o.join(`
|
|
251
251
|
`)+`
|
|
252
|
-
`)}catch{}}async function
|
|
252
|
+
`)}catch{}}async function X_(e,t){try{let n=await Y_(e),r=await z.readFile(n,"utf8"),o=r.split(`
|
|
253
253
|
`).filter(s=>s!==t);o.length!==r.split(`
|
|
254
254
|
`).length&&await z.writeFile(n,o.join(`
|
|
255
|
-
`))}catch{}}async function
|
|
255
|
+
`))}catch{}}async function oy(){let e={refreshed:0,removed:0,codexGlobal:null};try{await z.mkdir(Jn,{recursive:!0}),await z.writeFile(V.join(Jn,"CLAUDE.md"),KH).catch(()=>{}),await z.writeFile(V.join(Jn,"AGENTS.md"),Fc).catch(()=>{});let t=process.env.CODEX_HOME||V.join(process.env.HOME||"/tmp",".codex");if((await z.stat(t).catch(()=>null))?.isDirectory()){let r=V.join(t,"AGENTS.md"),o=await z.readFile(r,"utf8").catch(()=>null);o===null?(await z.writeFile(r,Rg,{flag:"wx"}).catch(()=>{}),e.codexGlobal="created"):Uc(o)&&o!==Rg&&(await z.writeFile(r,Rg).catch(()=>{}),e.codexGlobal="refreshed")}for(let r of[Hc,La]){let o=await z.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=V.join(r,s.name),i=V.join(a,"AGENTS.md"),c=await z.readFile(i,"utf8").catch(()=>null);c!==null&&Uc(c)&&c!==Fc&&(await z.writeFile(i,Fc).catch(()=>{}),e.refreshed++);let l=V.join(a,"CLAUDE.md"),u=await z.readFile(l,"utf8").catch(()=>null);u!==null&&Uc(u)&&(await z.rm(l,{force:!0}).catch(()=>{}),await X_(a,"/CLAUDE.md"),e.removed++)}}}catch{}return e}async function Z_(e){let t=e.workspace;if(!(!t||!t.dir))try{if(t.kind==="multi"){for(let n of jt(t))n.base&&n.dir&&await Tt(n.base,n.dir).catch(()=>{});await z.rm(t.dir,{recursive:!0,force:!0})}else t.kind==="worktree"&&t.base?await Tt(t.base,t.dir):await z.rm(t.dir,{recursive:!0,force:!0})}catch{}}async function kp({name:e,cwd:t,repo:n,repos:r,agent:o="claude",model:s=null,skipPermissions:a=!0}){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");let i=await Fa(o,a,s),c=Ko({agent:o,agentCmd:i}),l=await Q(),u=lp().slice(0,8),d=await GH(u,e,{repo:n,repos:r,cwd:t}),p=new Set(l.tracks.flatMap(g=>(g.boxes||[]).map(y=>y.name))),f=`${e.trim()} lead`;p.has(hu(f))&&(f=`${e.trim().slice(0,50).trim()} ${u} lead`);let w=await On(f,d.dir,{agentCmd:i,agent:o,freshWorkspace:!0}),m={id:u,name:e.trim(),cwd:w.cwd,workspace:d,leadBox:w.name,boxes:[hy(w,"lead","live")],events:[],createdAt:Date.now()};return m.boxes[0].cliVersion=await c,Te(m,"opened",{agent:o,...s?{model:s}:{},box:w.name,cliVersion:m.boxes[0].cliVersion,skipPermissions:a,name:m.name,cwd:m.cwd,workspace:{base:d.base??null,branch:d.branch,kind:d.kind,...d.baseRef?{baseRef:d.baseRef}:{},...d.repo?{repo:d.repo}:{},...d.repos?{repos:d.repos.map(({name:g,base:y,baseRef:b,repo:k})=>({name:g,base:y,baseRef:b,repo:k}))}:{}}}),l.tracks.push(m),await ae(),qo({cwd:w.cwd,sessionName:w.name,agent:o,agentCmd:i,onBlocked:({message:g})=>j_(m.id,{box:w.name,agent:o,message:g}).catch(()=>{}),onReady:async()=>{let g=m.boxes.find(y=>y.name===m.leadBox);!g||g.readyAt||(g.readyAt=Date.now(),await ae())}}),Wt(m)}async function sy({provider:e="claude",fresh:t=!1}={}){if(!YH.has(e))throw new Error(`unknown setup provider "${e}" (claude | codex | cursor)`);let r=(await Q()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===e&&!u.archivedAt);if(r){if(!t){let u=await Wt(r);if(u.boxes.find(p=>p.name===u.leadBox)?.state==="live")return u}await ks(r.id).catch(()=>{})}let o=(await Fa(e,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=lp().slice(0,8),a=V.join(Jn,"setup",s);await z.mkdir(a,{recursive:!0});let i=await On(`Connect ${e} ${s}`,a,{agentCmd:o,agent:e,firstRun:!0});e==="codex"&&qo({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:[hy(i,"lead","live")],events:[],createdAt:Date.now()};return Te(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 Q()).tracks.push(c),await ae(),Wt(c)}async function ay(e,t,n){await nu(t.cwd).catch(()=>{}),await Ww(e).catch(()=>{});let r=ys.get(e);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),ys.delete(e),t.handoff&&!t.handoff.finishedAt&&(t.handoff.step="error",t.handoff.error=n,t.handoff.finishedAt=Date.now());for(let a of vs(e))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await Ty(a).catch(()=>{}),Bt.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 Q(),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 iy(e,{cascadeMocks:t=!0,cascadeRunbooks:n=!1}={}){let r=await Q(),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 vI(s),Xg(e),await ay(e,s,"track deleted"),n?await QI(e).catch(()=>{}):await ZI(e).catch(()=>{}),await sA(),await Z_(s),t&&await SI(e).catch(()=>{}),t?await FI(e).catch(()=>{}):await LI(s).catch(()=>{}),Te(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await ae(),{ok:!0,id:e,name:s.name,boxes:s.boxes.length}}async function ks(e){let t=await Q(),n=de(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(),Xg(e),Te(n,"archived",{boxes:n.boxes.length}),await ae(),n.kind==="setup"?(await Q_(e),Wt(n)):(Vc.add(e),QH(e).catch(()=>{}).finally(()=>Vc.delete(e)),{...await Wt(n),archiving:!0})}async function QH(e){try{await eW(e)}catch{}await Q_(e).catch(()=>{})}async function Q_(e){let t=await Q(),n=de(t,e);if(!(!n||!n.archivedAt)){await ay(e,n,"track archived");for(let r of n.boxes)_r.has(r.state)||(r.state="stopped");await ae()}}async function eW(e){let t=await Q(),n=de(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 xe(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await Tv(n.leadBox,i.cwd,{timeoutMs:ZH})),s="lead-cli"}catch{}if(r||(r=Fv(n,await G(n).catch(()=>[]))),!r)return;let c={text:r,source:s,at:Date.now()};o.length&&(c.items=o),n.beat=c,await N(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 ae()}async function hr(e,t){let n=await pt(e,t).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await cy(n);if(r.length){for(let o of r)await ns(o.repo,o.number,!0).catch(()=>{});return!1}try{return await ks(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function cy(e){return(await hn().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 wr(e,t){return xp(e,t,"merged")}async function xs(e,t){let n=await pt(e,t).catch(()=>null);if(!n?.trackId)return null;let r={repo:n.repo??null,number:n.number,title:n.title??null,url:n.url??null},o=await cy(n);if(o.length){let c=[...n.followupPending||[],r];for(let l of o)await jA(l.repo,l.number,c).catch(()=>{});return null}let s=[...n.followupPending||[],r],a=de(await Q(),n.trackId);if(!a)return null;let i=s[0];return bc(await Wt(a),"pr.merged",{repo:n.repo??a.workspace?.repo??null,prs:s,prNumber:i?.number??null,prTitle:i?.title??null,prUrl:i?.url??null}).catch(()=>null)}async function xp(e,t,n="merged"){let r=await pt(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 cy(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await BA(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];if(!await _A(r.repo,r.number).catch(()=>!0))return!1;try{return await qt(r.trackId,e$(a),{kind:"merge-notify"}),!0}catch{return await rs(r.repo,r.number,!0).catch(()=>{}),!1}}function e$(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 ${M_[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 ${M_[i.outcome]??i.outcome}`).join(`
|
|
256
256
|
`),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}
|
|
257
257
|
${s}
|
|
258
|
-
${a}`}async function cy(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<Ci()?!1:or(e.name)}async function Vc(e,t,n){return!t||e.archivedAt||_r.has(t.state)?!1:(await fe({flow:"rehydrate",phase:"resume-dead-cli",trackId:e.id,sessionName:t.name,agent:t.agent,via:n}).catch(()=>{}),console.warn(`[track] ${t.name}: CLI is not running (bare shell) \u2014 relaunching (${n})`),await Ct(t.name).catch(()=>{}),t.state="stopped",Qc(e))}async function Qc(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=Og.get(r.name);if(o)return o;let s=ZH(e,r,{freshSession:t,reason:n}).finally(()=>Og.delete(r.name));return Og.set(r.name,s),s}async function ZH(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||_r.has(t.state)||await xe(t.name))return!1;let o=n===!0,s=o?cp():null,a=o?Oi(jg(t.agent),s,t.name):dp(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await mu(a);if(i===Vs)return Te(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===zs&&console.warn(`[track] ${t.name}: could not confirm "${t.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);let c=o?{sessionId:t.sessionId??null,pinned:t.pinned===!0,epochs:JSON.parse(JSON.stringify(wo(t)))}:null,l=Date.now();if(o){let u=wo(t),d=u[u.length-1];d.endedAt==null&&(d.endedAt=l),d.endReason==null&&(d.endReason=r||"fresh-session"),u.push({agent:t.agent,sessionId:s,pinned:t.agent==="claude",startedAt:l,endedAt:null}),t.sessionId=s,t.pinned=t.agent==="claude"}try{let u=Ko({agent:t.agent,agentCmd:a}),d=await On(t.name,t.cwd,{agentCmd:a,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=Oo(d.hookToken),t.cliVersion=await u,wo(t)[t.epochs.length-1].cliVersion=t.cliVersion,qo({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a,onBlocked:({message:p})=>D_(e.id,{box:t.name,agent:t.agent,message:p}).catch(()=>{})}),t.state="live",o&&Te(e,"lead-refreshed",{box:t.name,agent:t.agent,fromSessionId:c.sessionId,toSessionId:s,epoch:t.epochs.length-1,reason:r||"fresh-session",cliVersion:t.cliVersion??null}),Te(e,"rehydrated",{box:t.name,agent:t.agent,...o?{fresh:!0}:{}}),!0}catch(u){return o&&(t.sessionId=c.sessionId,t.pinned=c.pinned,t.epochs=c.epochs),Te(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function xp(e,{freshSession:t=!1}={}){let n=await Q(),r=pe(n,e);if(!r)throw new Error(`track "${e}" not found`);if(!r.archivedAt)throw new Error(`track "${e}" is not archived`);return delete r.archivedAt,Kc.delete(e),Te(r,"unarchived",{}),await Qc(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await ae(),Wt(r)}async function ly(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>Bg)throw new Error(`name too long (max ${Bg} chars)`);let r=await Q(),o=pe(r,e);if(!o)throw new Error(`track "${e}" not found`);if(String(o.name||"").startsWith("\u21BB"))throw new Error("loop run Tracks are named by their loop \u2014 rename the loop on the Loops tab");if(n===o.name)return Wt(o);let s=o.name;return o.name=n,Te(o,"renamed",{from:s,to:n}),await ae(),Wt(o)}async function uy(){let e=await Q(),t=0;for(let n of e.tracks||[])n.transferredTo&&Xu(n,{at:n.transferredAt??Date.now()})&&t++;return t&&await ae(),t}async function dy(){let e=await Q(),t=new Set((await ln()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!Le(s)).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(Le(s))for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Ct(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):Te(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!_r.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await ae(),r}async function vp(e,{freshSession:t=!1}={}){let n=await Q(),r=pe(n,e);if(!r)throw new Error(`track "${e}" not found`);if(r.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before restarting the lead`);Ap(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(_r.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await xe(r.leadBox)){let i=await De(r.leadBox,{lines:Qe}).catch(()=>"");if(Ui(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Ct(r.leadBox),o.state="stopped"}let a=await Qc(r,t?{freshSession:!0,reason:"loop-fire"}:{});if(await ae(),!a)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Wt(r)}async function py(e,t,n){let r=await de(e);if(!r)throw new Error(`track "${e}" not found`);if(!t)throw new Error("unknown offer");let o=await G(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 vp(e),await N(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 O_(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return _d({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function wo(e){return(!Array.isArray(e.epochs)||e.epochs.length===0)&&(e.epochs=[{agent:e.agent??null,sessionId:e.sessionId??null,pinned:e.pinned===!0,cliVersion:e.cliVersion??null,startedAt:e.createdAt??null,endedAt:null}]),e.epochs}async function ap(e,t){let n=await Q(),r=pe(n,e);r&&(r.handoff={...r.handoff||{},...t},await ae())}function Z_(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 my(){let e=await Q(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=Z_(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await xe(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=wo(l),p=d[d.length-1],f=p.agent,w=p.sessionId??null;p.endedAt=u,r.reason&&(p.endReason=r.reason);let m=c.agent==="claude"&&!!c.sessionId,g=await Ko({agent:r.toAgent});d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:m,cliVersion:g,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.cliVersion=g,l.sessionId=c.sessionId||null,l.pinned=m,l.state="live",n.pendingHandoffHydrate={artifact:go,at:u},Te(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:f,toAgent:r.toAgent,fromSessionId:w,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 fe({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 fe({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 ae(),t}async function Sp(e,t,n=!0,r=null){let o=ys.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=pe(await Q(),e);if(!s)throw new Error(`track "${e}" not found`);Ua(e,"handoff");let a=await Fa(t,n),i=no(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 xe(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:B_(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return ys.set(e,l),await ap(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),QH(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 ap(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),fe({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),fe({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function QH(e,t,n,r,o){let s=async(L,B={})=>{e.step=L,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${L}`),await ap(e.trackId,{step:L,toBox:e.toBox}),await fe({flow:"lead-handoff",phase:L,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:B.sessionName,...B})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await Q(),i=pe(a,e.trackId)?.boxes.find(L=>L.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",no(c))e.checkpoint="skipped-capped";else try{await ju(e.fromBox,{timeoutMs:Lg})}catch(L){e.checkpoint="reconstructed",await fe({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:L?.message||String(L)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await Lu(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 p=Ko({agent:o,agentCmd:r}),f=await Xm(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let w=xx(f.launchCmd);await sr({cwd:n.cwd,sessionId:f.sessionId,sessionName:e.fromBox,agent:o,agentCmd:bs(o),launchEcho:w}).catch(L=>{if(L?.code!=="cli-not-running")throw L});let m=_m(await De(e.fromBox,{lines:Qe}).catch(()=>""),w);if(!Wo(o,m)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let L=dp(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await Xm(e.fromBox,{agentCmd:L,agent:c,resumeSessionId:l,pinned:u});let B=await Q(),A=pe(B,e.trackId);if(A){let I=A.boxes.find(j=>j.name===e.fromBox);I&&(I.state="live"),Te(A,"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 ae()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let g=await Q(),y=pe(g,e.trackId);if(!y)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let b=y.boxes.find(L=>L.name===e.fromBox);if(!b)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let k=Date.now(),S=wo(b),$=S[S.length-1];$.endedAt=k,e.reason&&($.endReason=e.reason);let M=await p;S.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,cliVersion:M,startedAt:k,endedAt:null}),b.agent=o,b.sessionId=f.sessionId,b.pinned=f.pinned,b.cliVersion=M,b.state="live",b.role="lead",y.pendingHandoffHydrate={artifact:e.statusArtifact||go,at:k},Te(y,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:S.length-1,singleBox:!0,forced:f.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,cliVersion:M,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await ae(),await s("done",{sessionName:e.fromBox}),await ap(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function gy(e){let t=ip.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await Q(),r=pe(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=ys.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of Bt.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 ip.set(e,s),eW(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 eW(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await xe(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&no(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await ju(t.leadBox,{timeoutMs:Lg})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await Lu(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 jt(t.workspace)){if(!u?.dir)continue;let d={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await vn(u.dir,["push","origin",o],hy);let{stdout:p}=await vn(u.dir,["rev-parse","HEAD"]);d.baseSha=p.trim(),d.pushed=!0}catch(p){d.error=String(p?.message||p).split(`
|
|
259
|
-
`)[0]}s.push(d)}e.step="wip";let a=await dg(t,{force:!0}),i=await Q(),c=
|
|
260
|
-
`)[0],advanced:null}}}function oW(e){if(typeof e!="string"||!rW.test(e)||e.includes("..")||e.includes("@{")||e.endsWith(".lock"))throw new Error("branch fails the safe-ref check")}async function by(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(e.branch!=null&&oW(e.branch),np.has(t))throw new Error("an adopt is already running for this Track");np.set(t,{startedAt:Date.now()});try{return await sW({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{np.delete(t)}}async function sW({trackId:e,name:t,agent:n,skipPermissions:r,mode:o,fromNode:s=null,toNode:a=null,branch:i=null,repos:c=[],wip:l=null,wips:u=null,brief:d=null,epochs:p=[],createdAt:f=null,leadBox:w=null,reason:m=null,downSince:g=null}){let y=await Fa(n,r),b=await Q(),k=pe(b,e);if(k&&!k.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);k&&(await pg(k).catch(()=>{}),await J_(k),b.tracks.splice(b.tracks.indexOf(k),1),await ae()),await z.mkdir(Uc,{recursive:!0});let S=V.join(Uc,e);await z.rm(S,{recursive:!0,force:!0}).catch(()=>{});let $=null,M=!1,L=[];try{let B=[];if(i)for(let ce of Array.isArray(c)?c:[]){let re=typeof ce=="string"?ce:ce?.repo;if(!re||!/^[^/\s]+\/[^/\s]+$/.test(re))continue;let Ce=await Bs(re)??await Vl(re);await vn(Ce,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],hy),B.push({slug:re,base:Ce,name:typeof ce=="object"?ce?.name??null:null})}if(i&&B.length===1){let{base:ce,slug:re}=B[0];await M_(ce,S,i),L.push({base:ce,dir:S}),await Gc(S);let Ce=await Qo(ce);$={dir:S,base:ce,branch:i,kind:"worktree",baseRef:Ce,repo:re}}else if(i&&B.length>=2){await z.mkdir(S,{recursive:!0});let ce=new Set([".bostrat"]),re=[];for(let Ce of B){let J=Ce.name||V.basename(Ce.base);ce.has(J)&&(J=Ce.slug.replace("/","-"));for(let O=2;ce.has(J);O++)J=`${V.basename(Ce.base)}-${O}`;ce.add(J);let he=V.join(S,J);await M_(Ce.base,he,i),L.push({base:Ce.base,dir:he});let Fe=await Qo(Ce.base);re.push({name:J,dir:he,base:Ce.base,baseRef:Fe,repo:Ce.slug})}await Ha(S,re,i),$={dir:S,kind:"multi",branch:i,repos:re}}else M=!0,await z.mkdir(S,{recursive:!0}),await Eh(S),await Gc(S),$={dir:S,base:null,branch:null,kind:"repo"};let A=[];for(let ce of tW($,{wip:l,wips:u})){let re=await nW(ce.dir,e);A.push({name:ce.name,repo:ce.repo,applied:re.applied,skipped:re.skipped,...re.advanced?{advanced:re.advanced}:{}})}let I=A.some(ce=>ce.applied),j=A.length===1?A[0].skipped:A.filter(ce=>ce.skipped).map(ce=>`${ce.name??ce.repo}: ${ce.skipped}`).join("; ")||null,K=A.length===1?A[0].advanced??null:null;await d_({id:e,cwd:S}).catch(()=>({restored:!1}));let R=V.join(S,go),E=null;(await iu(S)).restored?E="checkpoint":typeof d=="string"&&d.trim()&&(await z.mkdir(V.dirname(R),{recursive:!0}),await z.writeFile(R,d),E="brief");let P=new Set(b.tracks.flatMap(ce=>(ce.boxes||[]).map(re=>re.name))),W=null;w&&!P.has(w)&&!await xe(w).catch(()=>null)?W=w:(W=`${t.trim()} lead`,P.has(hu(W))&&(W=`${t.trim().slice(0,50).trim()} ${e} lead`));let Y=await On(W,S,{agentCmd:y,agent:n});qo({cwd:Y.cwd,sessionName:Y.name,agent:n,agentCmd:y});let me=Date.now(),_e=fy(Y,"lead","live"),Pe=(Array.isArray(p)?p:[]).filter(ce=>ce&&typeof ce=="object").map(ce=>({...ce})),ne=Pe[Pe.length-1];ne&&ne.endedAt==null&&(ne.endedAt=me,ne.endReason==null&&(ne.endReason=o==="recover"?"node-offline":"node-transfer"));let Ve=await Ko({agent:n,agentCmd:y});Pe.push({agent:n,sessionId:Y.sessionId??null,pinned:Y.pinned===!0,cliVersion:Ve,startedAt:me,endedAt:null}),_e.epochs=Pe,_e.cliVersion=Ve;let ve={id:e,name:t.trim(),cwd:Y.cwd,workspace:$,leadBox:Y.name,boxes:[_e],events:[],createdAt:Number.isFinite(f)?f:me,...E?{pendingHandoffHydrate:{artifact:go,at:me}}:{}};return Te(ve,"transferred-in",{box:Y.name,agent:n,fromNode:s,toNode:a,mode:o,reason:m,...o==="recover"?{downSince:Number.isFinite(g)?g:null}:{},branch:$.branch??null,wip:I,...K?{wipAdvanced:K}:{},...$.kind==="multi"&&A.length?{wipRepos:A}:{},epoch:Pe.length-1,name:ve.name,cwd:ve.cwd,workspace:{base:$.base??null,branch:$.branch??null,kind:$.kind,...$.baseRef?{baseRef:$.baseRef}:{},...$.repo?{repo:$.repo}:{},...$.repos?{repos:$.repos.map(({name:ce,base:re,baseRef:Ce,repo:J})=>({name:ce,base:re,baseRef:Ce,repo:J}))}:{}}}),b.tracks.push(ve),await ae(),{...await Wt(ve),adopt:{wipApplied:I,wipSkipped:j,...K?{wipAdvanced:K}:{},...$.kind==="multi"?{wipRepos:A}:{},codeless:M,artifactSource:E}}}catch(B){for(let A of L)await Tt(A.base,A.dir).catch(()=>{});throw await z.rm(S,{recursive:!0,force:!0}).catch(()=>{}),B}}async function Q_(e,t,n){let r=await Q(),o=pe(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await ae())}async function Na(e,t){e.step=t,await el(e)}function e$(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 t$(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function lW(e,t,n){if(!t$(n)||Bt.has(t)||!await xe(n.box).catch(()=>null))return!1;try{await z.access(n.dir)}catch{return!1}let o=await xe(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return Bt.set(t,s),r$(s,o).catch(a=>n$(s,a)),!0}async function ky(){let e=await Q(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Bt.has(r))continue;if(await lW(n,r,o)){t=!0,Te(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await fe({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=e$(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!_r.has(i.state)&&(i.state="canceled"),Te(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 fe({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await C_(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||_r.has(r.state)||Bt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?V.join(La,`${n.id}-${r.subtaskId}`):null;await fe({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await C_(n,r.name,o)}}return t=await uW(e)||t,t&&await ae(),t}async function C_(e,t,n){if(t&&await Ct(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of jt(e.workspace)){let s=V.join(n,o.name);await Tt(o.base,s).then(()=>!0,()=>!1)||await Tt(o.dir,s).catch(()=>{})}await z.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await xe(e.leadBox).catch(()=>null);r?.cwd&&await Tt(r.cwd,n).catch(()=>{})}async function uW(e){let t;try{t=await z.readdir(La)}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 Bt.values())o.dir&&n.add(V.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=V.join(La,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of jt(a.workspace))await Tt(i.base,V.join(s,i.name)).catch(()=>{});await z.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await xe(a.leadBox).catch(()=>null):null;i?.cwd?await Tt(i.cwd,s).catch(()=>{}):await z.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await fe({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function xy(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!da(n))throw new Error("task (string) required");let s=await Fa(t,r),a=no(t);if(a){let f=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 f.usageCap=a,f}let i=pe(await Q(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await xe(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 f=jt(i.workspace);if(!f.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let w of f)if(!w?.dir||!await pa(w.dir))throw new Error(`tagged checkout "${w?.name??"?"}" is missing from the lead workspace (${w?.dir??"no dir"})`);u=f.map(w=>({name:w.name,leadDir:w.dir,base:null}))}else if(!await pa(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=cp().slice(0,8),p={id:d,trackId:e,trackName:i.name,agent:t,task:da(n),skipPermissions:r,reason:B_(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${Ip(i.name)}-${d}`,base:null,repos:u,dir:V.join(La,`${e}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Bt.set(d,p),await el(p),dW(p,c,s).catch(f=>n$(p,f)),p}async function n$(e,t){e.failedStep=e.step,e.error=t instanceof zc?"canceled":t?.message||String(t),e.step=t instanceof zc?"canceled":"error",e.finishedAt=Date.now(),await Sy(e).catch(()=>{}),await el(e).catch(()=>{}),await hW(e).catch(()=>{})}function _p(e){let t=Bt.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 vy(e){let t=Bt.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 dW(e,t,n){if(await Na(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await fa(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await el(e)}else e.base=await fa(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await Na(e,"spawn"),await z.mkdir(La,{recursive:!0}),e.repos){await z.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await Zo(a.leadDir,V.join(e.dir,a.name),e.branch,a.base);let s=jt((pe(await Q(),e.trackId)||{}).workspace);await Ha(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 Zo(t.cwd,e.dir,e.branch,e.base),await Gc(e.dir);await pW(e,t);let r=e.trackName.slice(0,38).trim(),o=await On(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await fW(e,o),await j_(e.trackId,o.name,tH(e.task,!!e.repos),{kind:"subtask"}),await r$(e,t)}async function r$(e,t){await Na(e,"working");let n=V.join(e.dir,rp);if(await gW(n,e),e.canceled)throw new zc;if(await Na(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=V.join(e.dir,l.name);await Ah(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:O_({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await _h(l.leadDir,l.base,e.branch,Ca),l.files=await $h(l.leadDir,l.base,e.branch,Ca);let d=await Rh(l.leadDir,l.base,e.branch,Ca);l.applied=d.applied,l.conflicts=d.conflicts??[],s.files+=l.stat.files,s.insertions+=l.stat.insertions,s.deletions+=l.stat.deletions,a.push(...l.files.map(p=>V.join(l.name,p))),i.push(...l.conflicts.map(p=>p.startsWith("patch did not apply cleanly")?`${l.name}: ${p}`:V.join(l.name,p))),d.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await Ah(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:O_({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await _h(t.cwd,e.base,e.branch,Ca),e.files=await $h(t.cwd,e.base,e.branch,Ca);let s=await Rh(t.cwd,e.base,e.branch,Ca);e.applied=s.applied,e.conflicts=s.conflicts}await el(e);let r=await wW(e,t,n);await Na(e,"resume"),await j_(e.trackId,e.leadBox,nH(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await Na(e,"reap"),await Sy(e),e.step="done";let o=Date.now();await Q_(e.trackId,e.id,{...Ep(e),finishedAt:o}),await mW(e,o),e.finishedAt=o}async function pW(e,t){let n=V.join(e.dir,V.dirname(Hc));await z.mkdir(n,{recursive:!0});let r="";try{r=await ox(t.cwd,t.sessionId)}catch{}if(!r)try{r=await z.readFile(V.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}
|
|
258
|
+
${a}`}async function ly(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<Ni()?!1:or(e.name)}async function zc(e,t,n){return!t||e.archivedAt||_r.has(t.state)?!1:(await fe({flow:"rehydrate",phase:"resume-dead-cli",trackId:e.id,sessionName:t.name,agent:t.agent,via:n}).catch(()=>{}),console.warn(`[track] ${t.name}: CLI is not running (bare shell) \u2014 relaunching (${n})`),await Ct(t.name).catch(()=>{}),t.state="stopped",Wa(e))}async function Wa(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=Og.get(r.name);if(o)return o;let s=nW(e,r,{freshSession:t,reason:n}).finally(()=>Og.delete(r.name));return Og.set(r.name,s),s}async function uy(e){let n=(await Q()).tracks.find(o=>o.leadBox===e);if(!n||n.archivedAt||n.transferredTo||Date.now()-(Mg.get(e)??0)<tW)return!1;let r=await Wa(n,{reason:"attach"}).catch(()=>!1);return r?(Mg.delete(e),await ae()):Mg.set(e,Date.now()),r}async function nW(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||_r.has(t.state)||await xe(t.name))return!1;let o=n===!0,s=o?lp():null,a=o?Mi(Lg(t.agent),s,t.name):pp(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await mu(a);if(i===Vs)return Te(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===zs&&console.warn(`[track] ${t.name}: could not confirm "${t.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);let c=o?{sessionId:t.sessionId??null,pinned:t.pinned===!0,epochs:JSON.parse(JSON.stringify(wo(t)))}:null,l=Date.now();if(o){let u=wo(t),d=u[u.length-1];d.endedAt==null&&(d.endedAt=l),d.endReason==null&&(d.endReason=r||"fresh-session"),u.push({agent:t.agent,sessionId:s,pinned:t.agent==="claude",startedAt:l,endedAt:null}),t.sessionId=s,t.pinned=t.agent==="claude"}try{let u=Ko({agent:t.agent,agentCmd:a}),d=await On(t.name,t.cwd,{agentCmd:a,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=Oo(d.hookToken),t.cliVersion=await u,wo(t)[t.epochs.length-1].cliVersion=t.cliVersion,qo({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a,onBlocked:({message:p})=>j_(e.id,{box:t.name,agent:t.agent,message:p}).catch(()=>{})}),t.state="live",o&&Te(e,"lead-refreshed",{box:t.name,agent:t.agent,fromSessionId:c.sessionId,toSessionId:s,epoch:t.epochs.length-1,reason:r||"fresh-session",cliVersion:t.cliVersion??null}),Te(e,"rehydrated",{box:t.name,agent:t.agent,...o?{fresh:!0}:{}}),!0}catch(u){return o&&(t.sessionId=c.sessionId,t.pinned=c.pinned,t.epochs=c.epochs),Te(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function vp(e,{freshSession:t=!1}={}){let n=await Q(),r=de(n,e);if(!r)throw new Error(`track "${e}" not found`);if(!r.archivedAt)throw new Error(`track "${e}" is not archived`);return delete r.archivedAt,Vc.delete(e),Te(r,"unarchived",{}),await Wa(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await ae(),Wt(r)}async function dy(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>jg)throw new Error(`name too long (max ${jg} chars)`);let r=await Q(),o=de(r,e);if(!o)throw new Error(`track "${e}" not found`);if(String(o.name||"").startsWith("\u21BB"))throw new Error("loop run Tracks are named by their loop \u2014 rename the loop on the Loops tab");if(n===o.name)return Wt(o);let s=o.name;return o.name=n,Te(o,"renamed",{from:s,to:n}),await ae(),Wt(o)}async function py(){let e=await Q(),t=0;for(let n of e.tracks||[])n.transferredTo&&Zu(n,{at:n.transferredAt??Date.now()})&&t++;return t&&await ae(),t}async function fy(){let e=await Q(),t=new Set((await ln()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!Le(s)).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(Le(s))for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Ct(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):Te(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!_r.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await ae(),r}async function Sp(e,{freshSession:t=!1}={}){let n=await Q(),r=de(n,e);if(!r)throw new Error(`track "${e}" not found`);if(r.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before restarting the lead`);Tp(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(_r.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await xe(r.leadBox)){let i=await De(r.leadBox,{lines:Qe}).catch(()=>"");if(Hi(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Ct(r.leadBox),o.state="stopped"}let a=await Wa(r,t?{freshSession:!0,reason:"loop-fire"}:{});if(await ae(),!a)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Wt(r)}async function my(e,t,n){let r=await pe(e);if(!r)throw new Error(`track "${e}" not found`);if(!t)throw new Error("unknown offer");let o=await G(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 Sp(e),await N(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 C_(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return $d({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function wo(e){return(!Array.isArray(e.epochs)||e.epochs.length===0)&&(e.epochs=[{agent:e.agent??null,sessionId:e.sessionId??null,pinned:e.pinned===!0,cliVersion:e.cliVersion??null,startedAt:e.createdAt??null,endedAt:null}]),e.epochs}async function ip(e,t){let n=await Q(),r=de(n,e);r&&(r.handoff={...r.handoff||{},...t},await ae())}function t$(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 wy(){let e=await Q(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=t$(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await xe(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=wo(l),p=d[d.length-1],f=p.agent,w=p.sessionId??null;p.endedAt=u,r.reason&&(p.endReason=r.reason);let m=c.agent==="claude"&&!!c.sessionId,g=await Ko({agent:r.toAgent});d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:m,cliVersion:g,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.cliVersion=g,l.sessionId=c.sessionId||null,l.pinned=m,l.state="live",n.pendingHandoffHydrate={artifact:go,at:u},Te(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:f,toAgent:r.toAgent,fromSessionId:w,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 fe({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 fe({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 ae(),t}async function Ap(e,t,n=!0,r=null){let o=ys.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=de(await Q(),e);if(!s)throw new Error(`track "${e}" not found`);Ua(e,"handoff");let a=await Fa(t,n),i=no(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 xe(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:L_(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return ys.set(e,l),await ip(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),rW(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 ip(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),fe({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),fe({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function rW(e,t,n,r,o){let s=async(L,B={})=>{e.step=L,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${L}`),await ip(e.trackId,{step:L,toBox:e.toBox}),await fe({flow:"lead-handoff",phase:L,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:B.sessionName,...B})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await Q(),i=de(a,e.trackId)?.boxes.find(L=>L.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",no(c))e.checkpoint="skipped-capped";else try{await Lu(e.fromBox,{timeoutMs:Fg})}catch(L){e.checkpoint="reconstructed",await fe({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:L?.message||String(L)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await Fu(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 p=Ko({agent:o,agentCmd:r}),f=await Xm(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let w=Sx(f.launchCmd);await sr({cwd:n.cwd,sessionId:f.sessionId,sessionName:e.fromBox,agent:o,agentCmd:bs(o),launchEcho:w}).catch(L=>{if(L?.code!=="cli-not-running")throw L});let m=_m(await De(e.fromBox,{lines:Qe}).catch(()=>""),w);if(!Wo(o,m)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let L=pp(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await Xm(e.fromBox,{agentCmd:L,agent:c,resumeSessionId:l,pinned:u});let B=await Q(),A=de(B,e.trackId);if(A){let I=A.boxes.find(j=>j.name===e.fromBox);I&&(I.state="live"),Te(A,"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 ae()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let g=await Q(),y=de(g,e.trackId);if(!y)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let b=y.boxes.find(L=>L.name===e.fromBox);if(!b)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let k=Date.now(),S=wo(b),$=S[S.length-1];$.endedAt=k,e.reason&&($.endReason=e.reason);let M=await p;S.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,cliVersion:M,startedAt:k,endedAt:null}),b.agent=o,b.sessionId=f.sessionId,b.pinned=f.pinned,b.cliVersion=M,b.state="live",b.role="lead",y.pendingHandoffHydrate={artifact:e.statusArtifact||go,at:k},Te(y,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:S.length-1,singleBox:!0,forced:f.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,cliVersion:M,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await ae(),await s("done",{sessionName:e.fromBox}),await ip(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function by(e){let t=cp.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await Q(),r=de(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=ys.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of Bt.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 cp.set(e,s),oW(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 oW(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await xe(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&no(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await Lu(t.leadBox,{timeoutMs:Fg})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await Fu(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 jt(t.workspace)){if(!u?.dir)continue;let d={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await vn(u.dir,["push","origin",o],gy);let{stdout:p}=await vn(u.dir,["rev-parse","HEAD"]);d.baseSha=p.trim(),d.pushed=!0}catch(p){d.error=String(p?.message||p).split(`
|
|
259
|
+
`)[0]}s.push(d)}e.step="wip";let a=await dg(t,{force:!0}),i=await Q(),c=de(i,t.id),l=c?.boxes.find(u=>u.name===c.leadBox)||null;l&&wo(l),await ae(),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:d,baseSha:p})=>({name:u,repo:d,baseSha:p})),unpushed:s.filter(u=>!u.pushed).map(({name:u,repo:d,error:p})=>({name:u,repo:d,error:p})),wip:a.pushed[0]?{ref:a.pushed[0].ref,sha:a.pushed[0].sha}:null,wips:a.pushed,wipSkipped:a.skipped,checkpoint:e.checkpoint,epochs:l?.epochs??[],codeless:!o||!s.some(u=>u.pushed)},e.step="ready",e.finishedAt=Date.now()}function Tp(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 Ep(e,{toNode:t,mode:n="transfer"}={}){if(!t||typeof t!="string")throw new Error("toNode required");let r=await Q(),o=de(r,e);if(!o)throw new Error(`track "${e}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};Xg(e),await ay(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();Zu(o,{at:s,reason:n==="recover"?"node-offline":"node-transfer"});let a=await pg(o,{ts:s}),i=a.pushed[0]??null;return o.transferredTo=t,o.transferredAt=s,Te(o,"transferred-out",{toNode:t,mode:n,box:o.leadBox,...i?{rescueRef:i.ref,rescueSha:i.sha}:{},...a.pushed.length?{rescues:a.pushed}:{}}),await ae(),{ok:!0,toNode:t,rescue:i?{ref:i.ref,sha:i.sha}:null,rescues:a.pushed}}async function ky(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await Ep(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 N_(e,t,n){try{await rc(e,t,n)}catch{await vn(e,["worktree","prune"]).catch(()=>{}),await rc(e,t,n)}return t}function sW(e,{wip:t=null,wips:n=null}={}){let r=jt(e).filter(a=>a?.dir),o=Array.isArray(n)?n.filter(a=>a?.sha):[];if(!o.length)return t?.sha&&r.length===1?r:[];let s=r.filter(a=>o.some(i=>i.repo?i.repo===a.repo:i.name!=null&&i.name===a.name));return!s.length&&r.length===1&&o.length===1?r:s}async function aW(e,t){try{await vn(e,["fetch","origin",ug(t)],gy);let{stdout:n}=await vn(e,["rev-parse","FETCH_HEAD^"]),{stdout:r}=await vn(e,["rev-parse","HEAD"]),o=null,s=n.trim()===r.trim();return s||await vn(e,["merge-base","--is-ancestor",r.trim(),n.trim()]).then(()=>!0,()=>!1)&&(await vn(e,["reset","--hard",n.trim()]),o={from:r.trim(),to:n.trim()},s=!0),s?(await vn(e,["read-tree","--reset","-u","FETCH_HEAD"]),await vn(e,["reset","-q"]),{applied:!0,skipped:null,advanced:o}):{applied:!1,skipped:"snapshot does not parent the fetched branch head",advanced:null}}catch(n){return{applied:!1,skipped:String(n?.message||n).split(`
|
|
260
|
+
`)[0],advanced:null}}}function cW(e){if(typeof e!="string"||!iW.test(e)||e.includes("..")||e.includes("@{")||e.endsWith(".lock"))throw new Error("branch fails the safe-ref check")}async function xy(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(e.branch!=null&&cW(e.branch),rp.has(t))throw new Error("an adopt is already running for this Track");rp.set(t,{startedAt:Date.now()});try{return await lW({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{rp.delete(t)}}async function lW({trackId:e,name:t,agent:n,skipPermissions:r,mode:o,fromNode:s=null,toNode:a=null,branch:i=null,repos:c=[],wip:l=null,wips:u=null,brief:d=null,epochs:p=[],createdAt:f=null,leadBox:w=null,reason:m=null,downSince:g=null}){let y=await Fa(n,r),b=await Q(),k=de(b,e);if(k&&!k.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);k&&(await pg(k).catch(()=>{}),await Z_(k),b.tracks.splice(b.tracks.indexOf(k),1),await ae()),await z.mkdir(Hc,{recursive:!0});let S=V.join(Hc,e);await z.rm(S,{recursive:!0,force:!0}).catch(()=>{});let $=null,M=!1,L=[];try{let B=[];if(i)for(let ce of Array.isArray(c)?c:[]){let re=typeof ce=="string"?ce:ce?.repo;if(!re||!/^[^/\s]+\/[^/\s]+$/.test(re))continue;let Ce=await Bs(re)??await Vl(re);await vn(Ce,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],gy),B.push({slug:re,base:Ce,name:typeof ce=="object"?ce?.name??null:null})}if(i&&B.length===1){let{base:ce,slug:re}=B[0];await N_(ce,S,i),L.push({base:ce,dir:S}),await Kc(S);let Ce=await Qo(ce);$={dir:S,base:ce,branch:i,kind:"worktree",baseRef:Ce,repo:re}}else if(i&&B.length>=2){await z.mkdir(S,{recursive:!0});let ce=new Set([".bostrat"]),re=[];for(let Ce of B){let J=Ce.name||V.basename(Ce.base);ce.has(J)&&(J=Ce.slug.replace("/","-"));for(let O=2;ce.has(J);O++)J=`${V.basename(Ce.base)}-${O}`;ce.add(J);let he=V.join(S,J);await N_(Ce.base,he,i),L.push({base:Ce.base,dir:he});let Fe=await Qo(Ce.base);re.push({name:J,dir:he,base:Ce.base,baseRef:Fe,repo:Ce.slug})}await Ha(S,re,i),$={dir:S,kind:"multi",branch:i,repos:re}}else M=!0,await z.mkdir(S,{recursive:!0}),await Eh(S),await Kc(S),$={dir:S,base:null,branch:null,kind:"repo"};let A=[];for(let ce of sW($,{wip:l,wips:u})){let re=await aW(ce.dir,e);A.push({name:ce.name,repo:ce.repo,applied:re.applied,skipped:re.skipped,...re.advanced?{advanced:re.advanced}:{}})}let I=A.some(ce=>ce.applied),j=A.length===1?A[0].skipped:A.filter(ce=>ce.skipped).map(ce=>`${ce.name??ce.repo}: ${ce.skipped}`).join("; ")||null,K=A.length===1?A[0].advanced??null:null;await f_({id:e,cwd:S}).catch(()=>({restored:!1}));let R=V.join(S,go),T=null;(await iu(S)).restored?T="checkpoint":typeof d=="string"&&d.trim()&&(await z.mkdir(V.dirname(R),{recursive:!0}),await z.writeFile(R,d),T="brief");let P=new Set(b.tracks.flatMap(ce=>(ce.boxes||[]).map(re=>re.name))),W=null;w&&!P.has(w)&&!await xe(w).catch(()=>null)?W=w:(W=`${t.trim()} lead`,P.has(hu(W))&&(W=`${t.trim().slice(0,50).trim()} ${e} lead`));let Y=await On(W,S,{agentCmd:y,agent:n});qo({cwd:Y.cwd,sessionName:Y.name,agent:n,agentCmd:y});let me=Date.now(),_e=hy(Y,"lead","live"),Pe=(Array.isArray(p)?p:[]).filter(ce=>ce&&typeof ce=="object").map(ce=>({...ce})),ne=Pe[Pe.length-1];ne&&ne.endedAt==null&&(ne.endedAt=me,ne.endReason==null&&(ne.endReason=o==="recover"?"node-offline":"node-transfer"));let Ve=await Ko({agent:n,agentCmd:y});Pe.push({agent:n,sessionId:Y.sessionId??null,pinned:Y.pinned===!0,cliVersion:Ve,startedAt:me,endedAt:null}),_e.epochs=Pe,_e.cliVersion=Ve;let ve={id:e,name:t.trim(),cwd:Y.cwd,workspace:$,leadBox:Y.name,boxes:[_e],events:[],createdAt:Number.isFinite(f)?f:me,...T?{pendingHandoffHydrate:{artifact:go,at:me}}:{}};return Te(ve,"transferred-in",{box:Y.name,agent:n,fromNode:s,toNode:a,mode:o,reason:m,...o==="recover"?{downSince:Number.isFinite(g)?g:null}:{},branch:$.branch??null,wip:I,...K?{wipAdvanced:K}:{},...$.kind==="multi"&&A.length?{wipRepos:A}:{},epoch:Pe.length-1,name:ve.name,cwd:ve.cwd,workspace:{base:$.base??null,branch:$.branch??null,kind:$.kind,...$.baseRef?{baseRef:$.baseRef}:{},...$.repo?{repo:$.repo}:{},...$.repos?{repos:$.repos.map(({name:ce,base:re,baseRef:Ce,repo:J})=>({name:ce,base:re,baseRef:Ce,repo:J}))}:{}}}),b.tracks.push(ve),await ae(),{...await Wt(ve),adopt:{wipApplied:I,wipSkipped:j,...K?{wipAdvanced:K}:{},...$.kind==="multi"?{wipRepos:A}:{},codeless:M,artifactSource:T}}}catch(B){for(let A of L)await Tt(A.base,A.dir).catch(()=>{});throw await z.rm(S,{recursive:!0,force:!0}).catch(()=>{}),B}}async function n$(e,t,n){let r=await Q(),o=de(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await ae())}async function Na(e,t){e.step=t,await el(e)}function r$(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 o$(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function fW(e,t,n){if(!o$(n)||Bt.has(t)||!await xe(n.box).catch(()=>null))return!1;try{await z.access(n.dir)}catch{return!1}let o=await xe(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return Bt.set(t,s),a$(s,o).catch(a=>s$(s,a)),!0}async function vy(){let e=await Q(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Bt.has(r))continue;if(await fW(n,r,o)){t=!0,Te(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await fe({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=r$(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!_r.has(i.state)&&(i.state="canceled"),Te(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 fe({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await P_(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||_r.has(r.state)||Bt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?V.join(La,`${n.id}-${r.subtaskId}`):null;await fe({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await P_(n,r.name,o)}}return t=await mW(e)||t,t&&await ae(),t}async function P_(e,t,n){if(t&&await Ct(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of jt(e.workspace)){let s=V.join(n,o.name);await Tt(o.base,s).then(()=>!0,()=>!1)||await Tt(o.dir,s).catch(()=>{})}await z.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await xe(e.leadBox).catch(()=>null);r?.cwd&&await Tt(r.cwd,n).catch(()=>{})}async function mW(e){let t;try{t=await z.readdir(La)}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 Bt.values())o.dir&&n.add(V.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=V.join(La,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of jt(a.workspace))await Tt(i.base,V.join(s,i.name)).catch(()=>{});await z.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await xe(a.leadBox).catch(()=>null):null;i?.cwd?await Tt(i.cwd,s).catch(()=>{}):await z.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await fe({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function Sy(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!da(n))throw new Error("task (string) required");let s=await Fa(t,r),a=no(t);if(a){let f=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 f.usageCap=a,f}let i=de(await Q(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await xe(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 f=jt(i.workspace);if(!f.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let w of f)if(!w?.dir||!await pa(w.dir))throw new Error(`tagged checkout "${w?.name??"?"}" is missing from the lead workspace (${w?.dir??"no dir"})`);u=f.map(w=>({name:w.name,leadDir:w.dir,base:null}))}else if(!await pa(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=lp().slice(0,8),p={id:d,trackId:e,trackName:i.name,agent:t,task:da(n),skipPermissions:r,reason:L_(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${_p(i.name)}-${d}`,base:null,repos:u,dir:V.join(La,`${e}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Bt.set(d,p),await el(p),hW(p,c,s).catch(f=>s$(p,f)),p}async function s$(e,t){e.failedStep=e.step,e.error=t instanceof Jc?"canceled":t?.message||String(t),e.step=t instanceof Jc?"canceled":"error",e.finishedAt=Date.now(),await Ty(e).catch(()=>{}),await el(e).catch(()=>{}),await bW(e).catch(()=>{})}function $p(e){let t=Bt.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 Ay(e){let t=Bt.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 hW(e,t,n){if(await Na(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await fa(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await el(e)}else e.base=await fa(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await Na(e,"spawn"),await z.mkdir(La,{recursive:!0}),e.repos){await z.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await Zo(a.leadDir,V.join(e.dir,a.name),e.branch,a.base);let s=jt((de(await Q(),e.trackId)||{}).workspace);await Ha(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 Zo(t.cwd,e.dir,e.branch,e.base),await Kc(e.dir);await wW(e,t);let r=e.trackName.slice(0,38).trim(),o=await On(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await gW(e,o),await F_(e.trackId,o.name,oH(e.task,!!e.repos),{kind:"subtask"}),await a$(e,t)}async function a$(e,t){await Na(e,"working");let n=V.join(e.dir,op);if(await xW(n,e),e.canceled)throw new Jc;if(await Na(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=V.join(e.dir,l.name);await Ah(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:C_({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await _h(l.leadDir,l.base,e.branch,Ca),l.files=await $h(l.leadDir,l.base,e.branch,Ca);let d=await Rh(l.leadDir,l.base,e.branch,Ca);l.applied=d.applied,l.conflicts=d.conflicts??[],s.files+=l.stat.files,s.insertions+=l.stat.insertions,s.deletions+=l.stat.deletions,a.push(...l.files.map(p=>V.join(l.name,p))),i.push(...l.conflicts.map(p=>p.startsWith("patch did not apply cleanly")?`${l.name}: ${p}`:V.join(l.name,p))),d.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await Ah(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:C_({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await _h(t.cwd,e.base,e.branch,Ca),e.files=await $h(t.cwd,e.base,e.branch,Ca);let s=await Rh(t.cwd,e.base,e.branch,Ca);e.applied=s.applied,e.conflicts=s.conflicts}await el(e);let r=await kW(e,t,n);await Na(e,"resume"),await F_(e.trackId,e.leadBox,sH(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await Na(e,"reap"),await Ty(e),e.step="done";let o=Date.now();await n$(e.trackId,e.id,{...Ip(e),finishedAt:o}),await yW(e,o),e.finishedAt=o}async function wW(e,t){let n=V.join(e.dir,V.dirname(Wc));await z.mkdir(n,{recursive:!0});let r="";try{r=await ax(t.cwd,t.sessionId)}catch{}if(!r)try{r=await z.readFile(V.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}
|
|
261
261
|
|
|
262
262
|
**Delegated task:** ${e.task}
|
|
263
263
|
|
|
@@ -268,15 +268,15 @@ ${a}`}async function cy(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<
|
|
|
268
268
|
## Conversation so far (lead \u2192 you)
|
|
269
269
|
|
|
270
270
|
${r}
|
|
271
|
-
`;await z.writeFile(V.join(e.dir,
|
|
271
|
+
`;await z.writeFile(V.join(e.dir,Wc),o)}async function gW(e,t){let n=await Q(),r=de(n,e.trackId);r&&(r.boxes.push({name:t.name,agent:t.agent,role:"subtask",state:"live",cwd:t.cwd,sessionId:t.sessionId,hookTokenHash:Oo(t.hookToken),createdAt:Date.now(),subtaskId:e.id,branch:e.branch,task:e.task}),Te(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]||{},...Ip(e)},await ae())}async function yW(e,t=e.finishedAt){let n=await Q(),r=de(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),Te(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 ae()}async function bW(e){let t=await Q(),n=de(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"),Te(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]||{},...Ip(e)},await ae()}async function kW(e,t,n){let r=`.bostrat/handoff/subtask-${e.id}.md`;try{let o=V.join(t.cwd,r);await z.mkdir(V.dirname(o),{recursive:!0}),await z.copyFile(n,o)}catch{}return r}async function Ty(e){if(e.dir&&await nu(e.dir).catch(()=>{}),e.box&&await Ct(e.box).catch(()=>{}),e.repos&&e.dir){for(let t of e.repos)await Tt(t.leadDir,V.join(e.dir,t.name)).catch(()=>{});await z.rm(e.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(e.dir){let n=(await xe(e.leadBox).catch(()=>null))?.cwd;n&&await Tt(n,e.dir).catch(()=>{})}}async function xW(e,t){let n=(t.startedAt||Date.now())+uW;for(;;){if(t.canceled||t.forceMerge)return;try{await z.access(e);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await $r(dW)}}function $r(e){return new Promise(t=>setTimeout(t,e))}var up,Jn,D_,tH,Hc,nH,dp,Yc,ho,go,Fg,rH,op,Wc,oH,sH,Dt,Eg,de,Ig,Te,_g,$g,cH,_r,__,dH,pH,fH,mH,$_,Lc,kH,Ba,R_,EH,IH,_H,$H,RH,Pa,qc,NH,DH,BH,FH,ty,Bg,Qc,kt,xn,ap,Gc,J_,KH,Fc,Uc,VH,Rg,YH,Vc,XH,ZH,M_,Og,tW,Mg,jg,hy,ys,qa,gy,vn,cp,yy,iW,rp,uW,dW,La,Ca,Bt,pW,vs,Ip,el,_p,Jc,gr=q(()=>{vi();ht();Gm();Uv();ra();jr();Su();Vv();yt();Rn();qu();Yv();Ns();vu();uh();Ho();bh();xh();MS();Ki();Om();Au();Fm();pn();Xu();Pr();Zt();qi();nd();rA();Dh();TE();Pu();Cu();IE();Tw();ye();_w();Od();Gd();mc();ts();Oc();Ra();tg();Zd();cg();jn();jd();a_();fg();m_();cu();y_();wg();Sg();Tg();up=eH(Q2),Jn=process.env.BOSTRAT_STATE_DIR||V.join(process.env.HOME||"/tmp",".bostrat"),D_=V.join(Jn,"tracks.json"),tH=V.join(Jn,"events.jsonl"),Hc=V.join(Jn,"workspaces"),nH=process.env.BOSTRAT_DEFAULT_REPO||null,dp={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"}},Yc=Object.keys(dp);ho={at:0,value:null};go=".bostrat/handoff/latest.md",Fg=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;rH=/\(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\.\)$/;op=".bostrat/handoff/subtask-result.md",Wc=".bostrat/handoff/lead-context.md",oH=(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 ${Wc} \u2014 read that first for context. Your scoped task:
|
|
272
272
|
|
|
273
273
|
${e}
|
|
274
274
|
|
|
275
|
-
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 ${
|
|
275
|
+
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 ${op} (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 ${Wc} \u2014 read that first for context. Your scoped task:
|
|
276
276
|
|
|
277
277
|
${e}
|
|
278
278
|
|
|
279
|
-
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 ${
|
|
279
|
+
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 ${op} 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.`,sH=(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."),Dt=null,Eg=null;de=(e,t)=>e.tracks.find(n=>n.id===t)||null,Ig=Promise.resolve();Te=(e,t,n={})=>{let r={at:Date.now(),type:t,...n};return e.events.push(r),aH(e.id,r),aA(e,r),r};ux(async({sessionName:e,from:t,to:n,version:r,status:o,error:s})=>{let i=(await Q()).tracks.find(c=>c.leadBox===e||(c.boxes||[]).some(l=>l.name===e));i&&(Te(i,"codex-update",{box:e,from:t,to:r||n,status:o,...s?{error:s}:{}}),await ae().catch(()=>{}))});_g=V.join(Jn,"plane-link","update-notice.json");$g=(e,t,n)=>t!=null&&n.some(r=>e===r.box&&t>=r.from&&t<=r.to),cH=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);_r=new Set(["merged","conflict","canceled","failed","handed-off"]),__=new Map;dH=5e3;pH={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};fH=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];mH=10*6e4;$_=400;Lc=new Map;kH=1500,Ba=new Map;R_="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.";EH=4e3,IH=3,_H=[300,350,400],$H=1e3,RH=12,Pa=new Map,qc=new Map;NH=[250,400,500,600];DH=3e4,BH=[350,650,900];FH=12e4;ty=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}))});Bg=new Map,Qc=e=>Bg.get(e)||(Dt?de(Dt,e)?.reshape:null)||null,kt=(e,t=409)=>Object.assign(new Error(e),{status:t});xn=e=>z.access(e).then(()=>!0,()=>!1),ap=(e,t,n)=>up("git",["-C",e,"worktree","move","--end-of-options",t,n],{timeout:6e4});Gc="# bostrat workspace",J_=`${Gc}
|
|
280
280
|
|
|
281
281
|
TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
|
|
282
282
|
before substantive work \u2014 it is the shared cross-provider memory for this repo
|
|
@@ -672,16 +672,16 @@ for the same follow-up at the same moment twice; run bostrat-followup ls first
|
|
|
672
672
|
specifics (no PR numbers, URLs, SHAs, tags \u2014 the card supplies them), and HOLD
|
|
673
673
|
HERE after proposing until the operator answers the card. Bostrat never runs a
|
|
674
674
|
follow-up on its own; the operator's tap is the gate.
|
|
675
|
-
`,
|
|
676
|
-
`)),
|
|
677
|
-
`,`${
|
|
675
|
+
`,KH=J_,Fc=J_+Qx;Uc=e=>typeof e=="string"&&(e===Gc||e.startsWith(`${Gc}
|
|
676
|
+
`)),VH="# bostrat multi-repo workspace";Rg=Fc.replace(`${Gc}
|
|
677
|
+
`,`${Gc}
|
|
678
678
|
|
|
679
679
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
680
680
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
681
681
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
682
682
|
When working anywhere else, ignore everything below.
|
|
683
|
-
`);VH=new Set(["claude","codex","cursor"]);Kc=new Set,zH=e=>Kc.has(e);JH=9e4;R_={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};Og=new Map;Bg=120;fy=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:Oo(e.hookToken),createdAt:Date.now()});ys=new Map,Wa=e=>ys.get(e)||(Dt?pe(Dt,e)?.handoff:null)||null;hy=6e4,vn=(e,t,n=15e3)=>lp("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),ip=new Map,wy=e=>ip.get(e)||null;rW=/^[A-Za-z0-9][A-Za-z0-9._/-]{0,200}$/;np=new Map;aW=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,iW=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,La=V.join(Jn,"worktrees"),Ca=[Hc,rp],Bt=new Map,cW=e=>Bt.get(e)||(Dt?Dt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,vs=e=>{let t=new Map([...Bt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Dt?pe(Dt,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)},Ep=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});el=e=>Q_(e.trackId,e.id,Ep(e));Ip=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",zc=class extends Error{}});ht();import $e from"node:path";import St from"node:fs";import oV from"node:readline";import{fileURLToPath as sV}from"node:url";import Jt from"express";import aV from"node:http";import iV from"node:https";import Ol from"node:os";import{randomUUID as cV}from"node:crypto";import{WebSocketServer as lV}from"ws";ht();import RP from"node-pty";async function D0(e,t,n={}){if(!Be(t)){bu(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await Hr()){bu(e,`host tmux server not running on ${Ft}`),e.close(1011,"tmux server unavailable");return}if(!await xe(t)){bu(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=P0(n.cols,120,B0),o=P0(n.rows,32,j0),s;try{s=RP.spawn(te,["-S",Ft,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){bu(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&sm(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(sm(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"&&NP(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{}CP(t)})}var OP=6,MP=250;async function CP(e,t=OP,n=MP){for(let r=0;r<t;r++){if(await Lo(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var B0=20,j0=6;function NP(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=B0&&Number(t)>=j0}function P0(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function sm(e,t){try{e.send(JSON.stringify(t))}catch{}}function bu(e,t){sm(e,{type:"error",message:t})}function L0(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}}pn();xu();Gm();gr();import{createRequire as uV}from"node:module";var yW=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function Ay({tracks:e=[],views:t=[],queuedOf:n=()=>[],jobsOf:r=()=>[],prPendingOf:o=()=>[]}={}){let s=[];for(let a of e)!a||a.archivedAt&&!a.archiving||a.transferredTo||(a.working&&s.push(`working:${a.id}`),a.prompted&&s.push(`prompted:${a.id}`),(n(a.id)??[]).length&&s.push(`queued:${a.id}`),(a.boxes||[]).some(i=>i?.role==="subtask"&&i?.state==="live")&&s.push(`subtask:${a.id}`),(r(a.id)??[]).length&&s.push(`job:${a.id}`),Object.values(a.subtasks||{}).some(yW)&&s.push(`merge-back:${a.id}`),(o(a.id)??[]).length&&s.push(`pr:${a.id}`));return t.some(a=>a?.state==="live"||a?.state==="starting")&&s.push("views"),{busy:s.length>0,reasons:s}}Sg();ye();Rc();jn();Cd();Zt();var $p=60*6e4,bW=1440*6e4,kW=new Set(["merged","closed","lost"]),tl=(e,t,n)=>Number.isFinite(Number(e))&&n-Number(e)<=t;function xW({trackId:e,shipPending:t=null,links:n=[],observers:r=0,now:o=Date.now()}={}){let s=[];t&&Number.isFinite(Number(t.ts))&&o-Number(t.ts)<=Gw&&s.push("ship"),r>0&&s.push("create-net");for(let a of n||[]){if(!a||kW.has(a.state))continue;let i=[a.ciFix,a.reconcile,a.ciFixAwait,a.reconcileAwait];if(!(a.trackId===e||i.some(d=>d?.trackId===e)))continue;let l=a.number??"?";a.ciFix&&tl(a.ciFix.delegatedAt,$p,o)&&s.push(`ci-fix#${l}`),a.reconcile&&tl(a.reconcile.delegatedAt,$p,o)&&s.push(`reconcile#${l}`),a.ciFixAwait&&tl(a.ciFixAwait.since,$p,o)&&s.push(`ci-fix-await#${l}`),a.reconcileAwait&&tl(a.reconcileAwait.since,$p,o)&&s.push(`reconcile-await#${l}`);let u=a.autoMerge?.armedAt??a.armedAt??null;a.trackId===e&&(a.autoMerge||a.armedAt)&&tl(u,bW,o)&&s.push(`auto-merge#${l}`)}return s}var Rp=new Map;Xe.on("event",e=>{e?.trackId&&Rp.delete(e.trackId)});async function vW(e){if(Rp.has(e.id))return Rp.get(e.id);let t=await G(e).catch(()=>[]),n=Ia(t,-1/0);return Rp.set(e.id,n),n}async function s$(e,{now:t=Date.now()}={}){let n=new Map,r=await hn().catch(()=>[]);for(let o of e||[]){if(!o||Le(o))continue;let s=xW({trackId:o.id,shipPending:await vW(o),links:r,observers:WT(o.id),now:t});s.length&&n.set(o.id,s)}return n}wg();import g$ from"node:fs";import bo from"node:fs/promises";import FW from"node:os";import gt from"node:path";import{spawn as UW}from"node:child_process";import{createRequire as HW}from"node:module";ye();td();import qa from"node:fs";import rl from"node:fs/promises";import TW from"node:http";import Gt from"node:path";import Mp from"node:os";import{createRequire as EW}from"node:module";import Ey from"ws";import SW from"node:path";import AW from"node:os";var nl=process.env.BOSTRAT_STATE_DIR||SW.join(process.env.HOME||AW.tmpdir(),".bostrat");function Ty(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 a$(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=Ty(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function i$(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}Cf();var Sn=(()=>{try{return EW(import.meta.url)("../package.json").version||null}catch{return null}})(),_y=process.env.BOSTRAT_STATE_DIR||Gt.join(process.env.HOME||Mp.tmpdir(),".bostrat"),m$=Gt.join(_y,"plane-link","removed.json");function c$(e){return typeof e!="string"||e.length<8?"\u2022\u2022\u2022\u2022":`${e.slice(0,6)}\u2026${e.slice(-4)}`}function IW(e=m$){try{let t=JSON.parse(qa.readFileSync(e,"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function Ss({env:e=process.env,pkgDir:t=Gt.resolve(Gt.dirname(new URL(import.meta.url).pathname),".."),exists:n=qa.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(Gt.join(t,"..",".git"))?"git-checkout":"dev"}function ol(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 Op(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 _W({home:e=process.env.HOME||Mp.homedir(),env:t=process.env}={}){let n=r=>{try{return qa.statSync(r),!0}catch{return!1}};return{claude:n(Gt.join(e,".claude",".credentials.json")),codex:n(Gt.join(t.CODEX_HOME||Gt.join(e,".codex"),"auth.json"))}}var ft=1,l$=1e3,u$=3e4,$W=6e4,RW=1500,OW=500,yo=100,MW=2e3,CW=25e3,NW=6e4,h$=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},PW=h$(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),DW=3e4,d$=10*6e4,BW=400,p$=250,jW=h$(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),Iy=4,LW=6e4,f$=6e4;function w$({url:e,token:t,nodeId:n=Mp.hostname(),name:r=Mp.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,deliverySample:c=null,onHashKey:l=null,onViewBase:u=null,metricsMs:d=LW,onUpdateAvailable:p=null,onUpdatePolicy:f=null,onHelloRefused:w=null,updatePolicy:m=null,installKind:g=Ss(),selfUrl:y=`http://127.0.0.1:${process.env.PORT||3362}`,apiToken:b=null,removedMarkerFile:k=m$,onRemoved:S=null,onDegraded:$=null,onRecovered:M=null,onConnected:L=null,ingestMode:B=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:A=PW,unackedRetryMs:I=NW,pingMs:j=CW,maxInflightEvents:K=BW,httpTimeoutMs:R=jW,stallBackoffMinMs:E=DW,fetchImpl:C=globalThis.fetch,log:P=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=i$(e);let W=y.replace(/^http/,"ws"),Y=new Set,me=new Map,_e=new Map,Pe=Gt.join(_y,"plane-link","cursor.json"),ne=Gt.join(_y,"plane-link","dead-letter.ndjson"),Ve=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),ve={},ce={},re=new Map,Ce=null,J=null,he=0,Fe=0,O=0,se=Promise.resolve(),Re="ws",$t=0,X=!1,we=0,mt=!1,nt=yo,an=new Set,In=[],x=null,h=null,v=!1,D=!1,Z=l$,ue=null,ge=null,ct=null,ze=null,lt=null,kf=!1,jl=0,ai=!1,Mr=IW(k);if(Mr)if(Mr.tokenPreview===c$(t))ai=!0,Mf({reason:Mr.reason??"removed",at:Mr.at??null,nodeId:Mr.nodeId??n}),P.error(`[plane-link] this node was REMOVED from the plane (${Mr.reason??"removed"}${Mr.at?` at ${new Date(Mr.at).toISOString()}`:""}) \u2014 not dialing. Re-provision it with a new node token, or delete ${k} to force a dial.`);else try{qa.unlinkSync(k),P.log("[plane-link] stale removed-marker discarded \u2014 the node token changed")}catch{}function xf({reason:F="removed",at:H=Date.now(),via:T="ctl"}={}){if(!ai){ai=!0,Mf({reason:F,at:H,nodeId:n,via:T});try{qa.mkdirSync(Gt.dirname(k),{recursive:!0}),qa.writeFileSync(k,JSON.stringify({at:H,reason:F,via:T,nodeId:n,tokenPreview:c$(t)},null,2))}catch(U){P.error(`[plane-link] could not write ${k}: ${U?.message||U}`)}P.error(`[plane-link] the plane REMOVED this node (${F}, via ${T}) \u2014 going dormant: no more dials, ingest or broker calls. Re-provision with a new node token to reconnect; the box and its data are untouched.`);try{S?.({reason:F,at:H,via:T})}catch{}uk({bye:!1})}}async function aC(){try{ve=JSON.parse(await rl.readFile(Pe,"utf8"))}catch{ve={}}}function iC(){kf=!0,!ct&&(ct=setTimeout(async()=>{if(ct=null,!!kf){kf=!1;try{await rl.mkdir(Gt.dirname(Pe),{recursive:!0}),await rl.writeFile(Pe,JSON.stringify(ve))}catch(F){P.error("[plane-link] cursor flush failed:",F?.message||F)}}},MW),ct.unref?.())}function ut(F){return!h||h.readyState!==Ey.OPEN?!1:(h.send(JSON.stringify(F)),!0)}function ii(F){let H=Date.now();for(let{trackId:T,event:U}of F){let ee=re.get(U.id);re.set(U.id,{trackId:T,seq:U.seq,event:U,sentAt:H,firstSentAt:ee?.firstSentAt??H,retryAt:H+I,tries:(ee?.tries??0)+1,nacks:ee?.nacks??0})}return Re==="http"?(dC(F),!0):ut({v:ft,ch:"events",type:"append",events:F})}function cC(F){let H=null;for(let T of re.values())T.trackId===F&&(H==null||T.seq<H)&&(H=T.seq);return H}function vf(F){let H=ce[F];if(H==null)return;let T=cC(F),U=T==null?H:Math.min(H,T-1);U>(ve[F]??0)&&(ve[F]=U,iC())}function ci(){if(Ce=Date.now(),J!=null){let F=Date.now()-J;P.log(`[plane-link] spine sync recovered after ${Math.round(F/1e3)}s degraded (${he} stall redial(s))`);let H=J;J=null,he=0,Fe=0,X=!1,we=0;try{M?.({since:H,downMs:F})}catch(T){P.error(`[plane-link] onRecovered failed: ${T?.message||T}`)}}}function Sf(F){if(!Array.isArray(F)||!F.length)return;let H=new Set;for(let T of F){let U=re.get(T);U&&(re.delete(T),(ce[U.trackId]??0)<U.seq&&(ce[U.trackId]=U.seq),H.add(U.trackId))}for(let T of H)vf(T);ci()}function nk(F,H){O++;let T={at:Date.now(),trackId:F.trackId,seq:F.seq,id:F.event?.id??null,type:F.event?.type??null,reason:H,event:F.event??null};P.error(`[plane-link] DEAD-LETTER ${F.trackId} seq ${F.seq} (${T.type??"?"}): plane rejected it permanently (${H}) \u2014 kept in ${ne}`),se=se.then(async()=>{await rl.mkdir(Gt.dirname(ne),{recursive:!0}),await rl.appendFile(ne,JSON.stringify(T)+`
|
|
684
|
-
`)}).catch(U=>P.error(`[plane-link] dead-letter write failed: ${U?.message||U}`))}function rk(F,H,T,U=null){if(!Array.isArray(F)||!F.length)return;let ee=new Set,Se=Date.now();for(let Ee of F){let Ge=re.get(Ee);Ge&&(T?(re.delete(Ee),nk(Ge,H),(ce[Ge.trackId]??0)<Ge.seq&&(ce[Ge.trackId]=Ge.seq),ee.add(Ge.trackId)):(Ge.nacks++,Ge.retryAt=Se+Math.max(1e3,Number(U)||I)))}for(let Ee of ee)vf(Ee);ci()}function ok(F=Date.now()){let H=1/0;for(let T of re.values())T.firstSentAt<H&&(H=T.firstSentAt);return H===1/0?0:F-H}function sk(){if(!v||!re.size)return;let F=Date.now(),H=ok(F);if(H<A||Ce!=null&&F-Ce<A||Re==="http"&&X||Fe>F)return;he++;let T=Math.min(E*2**(he-1),d$);if(J==null){J=F-H,P.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(H/1e3)}s with ${re.size} event(s) in flight (${Re} door) \u2014 the plane is not answering appends`);try{$?.({since:J,unacked:re.size,oldestAgeMs:H,mode:Re})}catch(U){P.error(`[plane-link] onDegraded failed: ${U?.message||U}`)}}if(Re==="http"){X=!0,we=F+T,P.error(`[plane-link] ingest circuit OPEN (stall #${he}); probing /v1/ingest/health in ${Math.round(T/1e3)}s`);return}Fe=F+T,P.error(`[plane-link] closing the link and redialing in ${Math.round(T/1e3)}s (stall redial #${he})`);try{h?.terminate()}catch{}}function Af(){if(!v)return;let F=Date.now();if(Re==="http"&&X)return;let H=[...re.values()].filter(U=>U.event&&U.retryAt<=F&&!(Re==="http"&&an.has(U.trackId)));if(!H.length)return;H.sort((U,ee)=>U.trackId===ee.trackId?U.seq-ee.seq:U.trackId<ee.trackId?-1:1),P.log(`[plane-link] re-sending ${H.length} unacked event(s) the plane never confirmed`);let T=Re==="http"?nt:yo;if(Re==="http"){let U=new Map;for(let ee of H)(U.get(ee.trackId)??U.set(ee.trackId,[]).get(ee.trackId)).push(ee);for(let[,ee]of U){if($t>=Iy)return;ii(ee.slice(0,T).map(Se=>({trackId:Se.trackId,event:Se.event})))}return}for(let U=0;U<H.length;U+=T){let ee=H.slice(U,U+T).map(Se=>({trackId:Se.trackId,event:Se.event}));if(!ii(ee))return}}async function lC(F=null){for(;;){if(!v)return!1;let H=re.size<K,T=Re!=="http"||$t<Iy&&!X&&(!F||!an.has(F));if(H&&T)return!0;await new Promise(U=>setTimeout(U,100))}}function Tf(){if(x=null,!v||!In.length)return;let F=In;if(In=[],Re!=="http"){for(let T=0;T<F.length;T+=yo)ii(F.slice(T,T+yo));return}let H=new Map;for(let T of F)(H.get(T.trackId)??H.set(T.trackId,[]).get(T.trackId)).push(T);for(let[T,U]of H){if(an.has(T)||$t>=Iy||X){In.push(...U);continue}for(let ee=0;ee<U.length;ee+=nt)ii(U.slice(ee,ee+nt))}In.length&&!x&&(x=setTimeout(Tf,p$),x.unref?.())}let uC=F=>{let H=new Map;for(let{trackId:T,event:U}of F)(H.get(T)??H.set(T,[]).get(T)).push(U);return[...H].map(([T,U])=>({trackId:T,events:U}))},Ro=(F,H,T,{progress:U})=>{let ee=Date.now();for(let{event:Se}of F){let Ee=re.get(Se.id);Ee&&(Ee.nacks++,Ee.retryAt=ee+Math.max(1e3,T||I))}U&&ci()};async function dC(F){let H=new Set(F.map(T=>T.trackId));for(let T of H)an.add(T);$t++;try{let T=await C(`${Ve}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:uC(F)}),signal:AbortSignal.timeout(R)}),U=(Number(T.headers.get("retry-after"))||0)*1e3;if(T.status===200){let ee=await T.json();nt=yo;for(let Se of ee.results??[]){Sf(Se.acked??[]);let Ee=(Ge,Rt)=>{let Yt=new Map;for(let ot of Ge??[])(Yt.get(ot.reason)??Yt.set(ot.reason,[]).get(ot.reason)).push(ot.id);for(let[ot,dk]of Yt)rk(dk,ot,Rt,ee.retryAfterMs??null)};Ee(Se.rejected,!0),Ee(Se.deferred,!1)}Sf(ee.discarded??[]),ci();return}if(T.status===429){Ro(F,"overloaded",U||5e3,{progress:!0});return}if(T.status===413&&F.length>1){nt=Math.max(1,Math.floor(F.length/2)),P.error(`[plane-link] ingest request too large (${F.length} events) \u2014 halving batches to ${nt}`),Ro(F,"too_large",1e3,{progress:!0});return}if(T.status===413){let ee=re.get(F[0].event.id);ee&&(re.delete(ee.event.id),nk(ee,"too_large"),(ce[ee.trackId]??0)<ee.seq&&(ce[ee.trackId]=ee.seq),vf(ee.trackId)),ci();return}if(T.status===410){xf({reason:"removed",via:"410"}),Ro(F,"http_410",6e4,{progress:!1});return}if(T.status===401||T.status===403){let{tripped:ee,holdMs:Se}=js();if(ee&&P.error(`[plane-link] holding every plane call for ${Math.round(Se/6e4)} min \u2014 the ingest door refused this node's token three times in a row`),Date.now()-jl>=f$){jl=Date.now();let Ee="";try{Ee=(await T.json())?.error??""}catch{}P.error(`[plane-link] plane refused ingest (HTTP ${T.status}${Ee?`: ${Ee}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}Ro(F,`http_${T.status}`,Math.max(6e4,hi()),{progress:!1});return}if(T.status===400){P.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${F.length} event(s) for retry; this is a bug, not data`),Ro(F,"http_400",6e4,{progress:!1});return}Ro(F,`http_${T.status}`,U||5e3,{progress:!1})}catch(T){let U=T?.name==="TimeoutError"||T?.name==="AbortError";U&&F.length>1&&(nt=Math.max(1,Math.floor(F.length/2)),P.error(`[plane-link] ingest request of ${F.length} event(s) outlived its ${R} ms deadline \u2014 halving batches to ${nt}`)),Ro(F,U?"timeout":"network",5e3,{progress:!1})}finally{$t--;for(let T of H)an.delete(T)}}async function pC(){if(Re!=="http"||!X||!v||mt)return;let F=Date.now();if(!(we>F||Ls(F))){mt=!0;try{let H=await C(`${Ve}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(R)});if(!H.ok)throw new Error(`HTTP ${H.status}`);P.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),X=!1,we=0,Fe=Date.now()+R+I+1e3;for(let T of re.values())T.retryAt=Math.min(T.retryAt,Date.now());Af()}catch(H){he++;let T=Math.min(E*2**(he-1),d$);we=Date.now()+T,P.error(`[plane-link] ingest door still not answering (${H?.message||H}); next probe in ${Math.round(T/1e3)}s`)}finally{mt=!1}}}function ak(){let F=Date.now();return{mode:Re,unacked:re.size,oldestUnackedAgeMs:ok(F),lastProgressAt:Ce,degradedSince:J,stallRedials:he,deadLettered:O,http:Re==="http"?{inFlight:$t,circuitOpen:X,nextProbeInMs:X?Math.max(0,we-F):0,batchCap:nt}:null}}async function ik(){if(!i)return;let F;try{F=await i()}catch(T){P.error("[plane-link] metrics sample failed:",T?.message||T);return}let H=null;try{H=c?.()??null}catch{}F&&ut({v:ft,ch:"ctl",type:"metrics",metrics:{...F,planeLink:ak(),...H?{delivery:H}:{}}})}function fC(){i&&(lt&&clearInterval(lt),ik(),lt=setInterval(ik,d),lt.unref?.())}async function mC(){let F;try{F=await o()}catch(H){P.error("[plane-link] backfill listTracks failed:",H?.message||H);return}ut({v:ft,ch:"ctl",type:"tracks",ids:F.filter(H=>!H.transferredTo).map(H=>H.id).filter(H=>typeof H=="string")});for(let H of F){if(!v)return;let T;try{T=await G(H)}catch{continue}let U=ve[H.id]??0,ee=T.filter(Ee=>Ee.seq>U&&typeof Ee.id=="string"),Se=Re==="http"?nt:yo;for(let Ee=0;Ee<ee.length;Ee+=Se){if(!await lC(H.id))return;let Ge=ee.slice(Ee,Ee+Math.min(Se,Re==="http"?nt:yo)).map(Rt=>({trackId:H.id,event:Rt}));if(!ii(Ge))return}}}function ck(F){!v||!F?.trackId||typeof F.id!="string"||(In.push({trackId:F.trackId,event:F}),x||(x=setTimeout(Tf,p$),x.unref?.()))}let li=new Map;async function hC(F){let{reqId:H,method:T,path:U,headers:ee={},body:Se}=F,Ee=Object.fromEntries(Object.entries(ee).filter(([,Rt])=>Rt!=null)),Ge=new AbortController;li.set(H,Ge);try{if(F.host){await wC(F,Ee,Ge);return}b&&(Ee["x-bostrat-token"]=b);let Rt=await fetch(y+U,{method:T,headers:Ee,body:Se?Buffer.from(Se,"base64"):void 0,signal:Ge.signal}),Yt=Object.fromEntries(Rt.headers);for(let ot of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Yt[ot];if(Yt["content-type"]||(Yt["content-type"]="application/octet-stream"),ut({v:ft,ch:"control",type:"http_head",reqId:H,status:Rt.status,headers:Yt}),Rt.body){for await(let ot of Rt.body)if(!ut({v:ft,ch:"control",type:"http_data",reqId:H,data:Buffer.from(ot).toString("base64")}))break}ut({v:ft,ch:"control",type:"http_end",reqId:H,error:null})}finally{li.delete(H)}}function wC(F,H,T){let{reqId:U,method:ee,path:Se,body:Ee}=F,Ge=new URL(y);return new Promise(Rt=>{let Yt=TW.request({host:Ge.hostname,port:Ge.port,method:ee,path:Se,headers:{...H,host:F.host},signal:T.signal},ot=>{let{connection:dk,"keep-alive":_V,"transfer-encoding":$V,upgrade:RV,...gC}=ot.headers;ut({v:ft,ch:"control",type:"http_head",reqId:U,status:ot.statusCode||502,headers:gC}),ot.on("data",yC=>{ut({v:ft,ch:"control",type:"http_data",reqId:U,data:Buffer.from(yC).toString("base64")})||ot.destroy()}),ot.on("end",()=>{ut({v:ft,ch:"control",type:"http_end",reqId:U,error:null}),Rt()}),ot.on("error",()=>{ut({v:ft,ch:"control",type:"http_end",reqId:U,error:"view upstream error"}),Rt()})});Yt.on("error",ot=>{ut({v:ft,ch:"control",type:"http_end",reqId:U,error:ot?.message||"view request failed"}),Rt()}),Ee&&Yt.write(Buffer.from(Ee,"base64")),Yt.end()})}function lk(){if(D||ai)return;h=new Ey(e,{headers:{authorization:`Bearer ${t}`}}),h.on("open",async()=>{let H=null;try{H=await s?.()||null}catch{H=null}ut({v:ft,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:Sn,installKind:g,...typeof m=="function"?{updatePolicy:m()}:m?{updatePolicy:m}:{},providers:_W(),...H?{agents:H}:{}})}),h.on("message",H=>{let T;try{T=JSON.parse(H)}catch{return}if(T.ch==="ctl"&&T.type==="hello_ok"){v=!0,Z=l$,ue=null,bk(),P.log(`[plane-link] connected to plane as ${n}`);try{L?.()}catch{}if(typeof T.hashKey=="string"&&T.hashKey)try{l?.(T.hashKey)}catch{}try{u?.(typeof T.viewBase=="string"&&T.viewBase?T.viewBase:null)}catch{}T.minNodeVersion&&Sn&&ol(Sn,T.minNodeVersion)&&P.error(`[plane-link] this node is bostrat-node ${Sn}; the plane asks for \u2265${T.minNodeVersion} \u2014 update with: ${Op(g)}`);try{f?.(typeof T.updatePolicy=="string"?T.updatePolicy:null)}catch{}let U=typeof T.latestNodeVersion=="string"?T.latestNodeVersion:null;if(U&&Sn&&ol(Sn,U)&&!Y.has(U)){Y.add(U),P.log(`[plane-link] bostrat-node ${U} is available (this node runs ${Sn}) \u2014 ${Op(g)}`);try{p?.({current:Sn,latest:U,min:T.minNodeVersion??null,installKind:g,required:!!(T.minNodeVersion&&ol(Sn,T.minNodeVersion))})}catch(Se){P.error(`[plane-link] update notice failed: ${Se?.message||Se}`)}}let ee=T.ingest==="http";Re=B==="ws"?"ws":B==="http"||ee?"http":"ws",B==="http"&&!ee&&P.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),Re==="http"&&P.log("[plane-link] spine rides the HTTPS ingest door"),X=!1,we=0,nt=yo,ze&&clearInterval(ze),ze=setInterval(()=>{ut({v:ft,ch:"ctl",type:"ping"}),sk(),pC().catch(()=>{}),Af(),In.length&&!x&&Tf()},j),ze.unref?.(),fC(),mC().catch(Se=>P.error("[plane-link] backfill failed:",Se?.message||Se));return}if(T.ch==="ctl"&&T.type==="hello_refused"){let U=typeof T.minNodeVersion=="string"?T.minNodeVersion:null,ee=typeof T.latestNodeVersion=="string"?T.latestNodeVersion:null;P.error(`[plane-link] plane REFUSED this node: bostrat-node ${Sn??"?"} is below its minimum ${U??"?"} \u2014 ${T.fix||Op(g)}`),Z=u$,ue=0;try{w?.({current:Sn,min:U,latest:ee,fix:T.fix||Op(g),installKind:g})}catch(Se){P.error(`[plane-link] refusal handler failed: ${Se?.message||Se}`)}return}if(T.ch==="ctl"&&T.type==="removed"){ut({v:ft,ch:"ctl",type:"removed_ack",nodeId:n}),xf({reason:typeof T.reason=="string"?T.reason:"removed",at:Number(T.at)||Date.now(),via:"ctl"});return}if(T.ch==="ctl"&&T.type==="update_policy"){try{f?.(typeof T.mode=="string"?T.mode:null)}catch{}return}if(T.ch==="ctl"&&T.type==="usage_caps"){ed(T.caps??{},{source:"plane",updatedAt:T.updatedAt??Date.now()}).then(U=>P.log(`[plane-link] spend caps ${Object.keys(U.caps).length?`updated (${Object.keys(U.caps).join(", ")})`:"cleared"} from plane`)).catch(U=>P.error("[plane-link] spend-caps push rejected:",U?.message||U));return}if(T.ch==="ctl"&&T.type==="transferred"&&Array.isArray(T.tracks)){Promise.resolve().then(()=>a?.(T.tracks)).catch(U=>P.error("[plane-link] transfer notice failed:",U?.message||U));return}if(T.ch==="events"&&T.type==="ack"&&Array.isArray(T.ids)){Sf(T.ids);return}if(T.ch==="events"&&T.type==="nack"&&Array.isArray(T.ids)){rk(T.ids,T.reason,T.permanent===!0,T.retryAfterMs??null);return}if(T.ch==="control"&&T.type==="http_open"){hC(T).catch(U=>ut({v:ft,ch:"control",type:"http_end",reqId:T.reqId,error:U?.message||"tunnel error"}));return}if(T.ch==="control"&&T.type==="http_cancel"){li.get(T.reqId)?.abort();return}if(T.ch==="attach"&&T.type==="open"){let U=new Ey(W+T.path,Array.isArray(T.protocols)?T.protocols:[],T.host?{headers:{host:T.host}}:void 0);me.set(T.streamId,U),_e.set(T.streamId,[]),U.on("open",()=>{let Se=_e.get(T.streamId)||[];_e.delete(T.streamId);for(let{buf:Ee,bin:Ge}of Se)U.send(Ee,{binary:Ge})}),U.on("message",(Se,Ee)=>{let Ge=Ee?Se:Buffer.from(String(Se));ut({v:ft,ch:"attach",type:"data",streamId:T.streamId,data:Ge.toString("base64"),...Ee?{bin:!0}:{}})});let ee=()=>{_e.delete(T.streamId),me.delete(T.streamId)&&ut({v:ft,ch:"attach",type:"close",streamId:T.streamId})};U.on("close",ee),U.on("error",ee);return}if(T.ch==="attach"&&T.type==="data"){let U=me.get(T.streamId);if(!U)return;let ee=Buffer.from(T.data,"base64"),Se=T.bin===!0,Ee=_e.get(T.streamId);Ee?Ee.push({buf:ee,bin:Se}):U.send(ee,{binary:Se});return}if(T.ch==="attach"&&T.type==="close"){let U=me.get(T.streamId);me.delete(T.streamId),_e.delete(T.streamId),U?.close();return}});let F=()=>{v&&P.log("[plane-link] disconnected from plane"),v=!1,ze&&(clearInterval(ze),ze=null),lt&&(clearInterval(lt),lt=null),re.clear(),ce={},In=[],x&&(clearTimeout(x),x=null),an.clear();for(let U of me.values())U.close();me.clear(),_e.clear();for(let U of li.values())U.abort();if(li.clear(),D||ge)return;ue==null&&(ue=Date.now());let H=Date.now()-ue<$W,T=Math.max(H?RW+Math.random()*OW:Z,Fe-Date.now(),hi());ge=setTimeout(()=>{ge=null,lk()},T),ge.unref?.(),H||(Z=Math.min(Z*2,u$))};h.on("close",F),h.on("error",H=>{let T=/Unexpected server response: (\d+)/.exec(H?.message||"")?.[1];if(T==="410"){xf({reason:"removed",via:"410"});return}if(T==="401"||T==="403"){let{tripped:U,holdMs:ee}=js();U?P.error(`[plane-link] holding every plane call for ${Math.round(ee/6e4)} min \u2014 three credential refusals in a row (HTTP ${T}) mean the token is revoked or unknown, not that the plane is down; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN. Next dial in ${Math.round(ee/6e4)} min.`):Date.now()-jl>=f$&&(jl=Date.now(),P.error(`[plane-link] plane refused this node's credentials (HTTP ${T}) \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)`))}F()})}Xe.on("event",ck),aC().then(lk);function uk({bye:F=!0}={}){D=!0,Xe.off("event",ck),ge&&clearTimeout(ge),ct&&clearTimeout(ct),ze&&clearInterval(ze),lt&&clearInterval(lt),x&&clearTimeout(x),F&&ut({v:ft,ch:"ctl",type:"bye",reason:"shutdown"}),h?.close()}return{stop:()=>uk(),status:()=>({connected:v,nodeId:n,cursor:{...ve},inflight:re.size,...ak(),auth:kk(),dormant:ai,removed:Gl()}),_tick:()=>{sk(),Af()}}}vu();var Cp=(()=>{try{return HW(import.meta.url)("../package.json").version||null}catch{return null}})(),WW=process.env.BOSTRAT_STATE_DIR||gt.join(process.env.HOME||FW.tmpdir(),".bostrat"),y$=gt.join(WW,"self-update"),Np=gt.join(y$,"status.json"),qW=1440*6e4,GW=6e4,sl=new Set(["npm-global","git-checkout"]);function b$({kind:e=Ss(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:sl.has(e)?e==="git-checkout"||KW(t)?"auto":"prompt":"off"}function KW(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var Kt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function VW({kind:e,to:t,from:n=Cp,statusFile:r=Np,logFile:o=gt.join(y$,"last.log"),port:s=Number(process.env.PORT)||3362,npmPrefix:a=null,serviceName:i=process.platform==="darwin"?"com.bostrat.node":"bostrat-node.service",uid:c=typeof process.getuid=="function"?process.getuid():0,liveDir:l=process.env.BOSTRAT_LIVE_CHECKOUT||gt.resolve(gt.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!sl.has(e))throw new Error(`self-update: install kind "${e}" is not actionable here`);if(!/^\d+\.\d+\.\d+$/.test(String(t)))throw new Error(`self-update: bad target version "${t}"`);if(n&&!/^\d+\.\d+\.\d+$/.test(String(n)))throw new Error(`self-update: bad current version "${n}"`);let d=(g,y="")=>`write_status ${Kt(g)} ${y}`,p=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${Kt(i)}`,f=`
|
|
683
|
+
`);YH=new Set(["claude","codex","cursor"]);Vc=new Set,XH=e=>Vc.has(e);ZH=9e4;M_={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};Og=new Map;tW=6e4,Mg=new Map;jg=120;hy=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:Oo(e.hookToken),createdAt:Date.now()});ys=new Map,qa=e=>ys.get(e)||(Dt?de(Dt,e)?.handoff:null)||null;gy=6e4,vn=(e,t,n=15e3)=>up("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),cp=new Map,yy=e=>cp.get(e)||null;iW=/^[A-Za-z0-9][A-Za-z0-9._/-]{0,200}$/;rp=new Map;uW=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,dW=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,La=V.join(Jn,"worktrees"),Ca=[Wc,op],Bt=new Map,pW=e=>Bt.get(e)||(Dt?Dt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,vs=e=>{let t=new Map([...Bt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Dt?de(Dt,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)},Ip=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});el=e=>n$(e.trackId,e.id,Ip(e));_p=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Jc=class extends Error{}});ht();import $e from"node:path";import St from"node:fs";import cV from"node:readline";import{fileURLToPath as lV}from"node:url";import Jt from"express";import uV from"node:http";import dV from"node:https";import Ol from"node:os";import{randomUUID as pV}from"node:crypto";import{WebSocketServer as fV}from"ws";ht();import CP from"node-pty";async function j0(e,t,n={}){if(!Be(t)){bu(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await Hr()){bu(e,`host tmux server not running on ${Ft}`),e.close(1011,"tmux server unavailable");return}if(!await xe(t)){let a=!1;if(typeof n.revive=="function"&&(ku(e,{type:"notice",message:"Resuming the lead box\u2026"}),a=await n.revive().catch(()=>!1)),!a||!await xe(t)){bu(e,`session "${t}" not found`),e.close(1008,"session not found");return}if(e.readyState!==e.OPEN)return}let r=B0(n.cols,120,L0),o=B0(n.rows,32,F0),s;try{s=CP.spawn(te,["-S",Ft,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){bu(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&ku(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(ku(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"&&BP(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{}DP(t)})}var NP=6,PP=250;async function DP(e,t=NP,n=PP){for(let r=0;r<t;r++){if(await Lo(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var L0=20,F0=6;function BP(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=L0&&Number(t)>=F0}function B0(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function ku(e,t){try{e.send(JSON.stringify(t))}catch{}}function bu(e,t){ku(e,{type:"error",message:t})}function U0(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}}pn();vu();Gm();gr();import{createRequire as mV}from"node:module";var vW=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function Ey({tracks:e=[],views:t=[],queuedOf:n=()=>[],jobsOf:r=()=>[],prPendingOf:o=()=>[]}={}){let s=[];for(let a of e)!a||a.archivedAt&&!a.archiving||a.transferredTo||(a.working&&s.push(`working:${a.id}`),a.prompted&&s.push(`prompted:${a.id}`),(n(a.id)??[]).length&&s.push(`queued:${a.id}`),(a.boxes||[]).some(i=>i?.role==="subtask"&&i?.state==="live")&&s.push(`subtask:${a.id}`),(r(a.id)??[]).length&&s.push(`job:${a.id}`),Object.values(a.subtasks||{}).some(vW)&&s.push(`merge-back:${a.id}`),(o(a.id)??[]).length&&s.push(`pr:${a.id}`));return t.some(a=>a?.state==="live"||a?.state==="starting")&&s.push("views"),{busy:s.length>0,reasons:s}}Sg();ye();Oc();jn();Nd();Zt();var Rp=60*6e4,SW=1440*6e4,AW=new Set(["merged","closed","lost"]),tl=(e,t,n)=>Number.isFinite(Number(e))&&n-Number(e)<=t;function TW({trackId:e,shipPending:t=null,links:n=[],observers:r=0,now:o=Date.now()}={}){let s=[];t&&Number.isFinite(Number(t.ts))&&o-Number(t.ts)<=Gw&&s.push("ship"),r>0&&s.push("create-net");for(let a of n||[]){if(!a||AW.has(a.state))continue;let i=[a.ciFix,a.reconcile,a.ciFixAwait,a.reconcileAwait];if(!(a.trackId===e||i.some(d=>d?.trackId===e)))continue;let l=a.number??"?";a.ciFix&&tl(a.ciFix.delegatedAt,Rp,o)&&s.push(`ci-fix#${l}`),a.reconcile&&tl(a.reconcile.delegatedAt,Rp,o)&&s.push(`reconcile#${l}`),a.ciFixAwait&&tl(a.ciFixAwait.since,Rp,o)&&s.push(`ci-fix-await#${l}`),a.reconcileAwait&&tl(a.reconcileAwait.since,Rp,o)&&s.push(`reconcile-await#${l}`);let u=a.autoMerge?.armedAt??a.armedAt??null;a.trackId===e&&(a.autoMerge||a.armedAt)&&tl(u,SW,o)&&s.push(`auto-merge#${l}`)}return s}var Op=new Map;Xe.on("event",e=>{e?.trackId&&Op.delete(e.trackId)});async function EW(e){if(Op.has(e.id))return Op.get(e.id);let t=await G(e).catch(()=>[]),n=Ia(t,-1/0);return Op.set(e.id,n),n}async function c$(e,{now:t=Date.now()}={}){let n=new Map,r=await hn().catch(()=>[]);for(let o of e||[]){if(!o||Le(o))continue;let s=TW({trackId:o.id,shipPending:await EW(o),links:r,observers:GT(o.id),now:t});s.length&&n.set(o.id,s)}return n}wg();import k$ from"node:fs";import bo from"node:fs/promises";import qW from"node:os";import gt from"node:path";import{spawn as GW}from"node:child_process";import{createRequire as KW}from"node:module";ye();nd();import Ga from"node:fs";import rl from"node:fs/promises";import $W from"node:http";import Gt from"node:path";import Cp from"node:os";import{createRequire as RW}from"node:module";import _y from"ws";import IW from"node:path";import _W from"node:os";var nl=process.env.BOSTRAT_STATE_DIR||IW.join(process.env.HOME||_W.tmpdir(),".bostrat");function Iy(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 l$(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=Iy(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function u$(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}Nf();var Sn=(()=>{try{return RW(import.meta.url)("../package.json").version||null}catch{return null}})(),Ry=process.env.BOSTRAT_STATE_DIR||Gt.join(process.env.HOME||Cp.tmpdir(),".bostrat"),g$=Gt.join(Ry,"plane-link","removed.json");function d$(e){return typeof e!="string"||e.length<8?"\u2022\u2022\u2022\u2022":`${e.slice(0,6)}\u2026${e.slice(-4)}`}function OW(e=g$){try{let t=JSON.parse(Ga.readFileSync(e,"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function Ss({env:e=process.env,pkgDir:t=Gt.resolve(Gt.dirname(new URL(import.meta.url).pathname),".."),exists:n=Ga.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(Gt.join(t,"..",".git"))?"git-checkout":"dev"}function ol(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 Mp(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 MW({home:e=process.env.HOME||Cp.homedir(),env:t=process.env}={}){let n=r=>{try{return Ga.statSync(r),!0}catch{return!1}};return{claude:n(Gt.join(e,".claude",".credentials.json")),codex:n(Gt.join(t.CODEX_HOME||Gt.join(e,".codex"),"auth.json"))}}var ft=1,p$=1e3,f$=3e4,CW=6e4,NW=1500,PW=500,yo=100,DW=2e3,BW=25e3,jW=6e4,y$=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},LW=y$(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),FW=3e4,m$=10*6e4,UW=400,h$=250,HW=y$(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),$y=4,WW=6e4,w$=6e4;function b$({url:e,token:t,nodeId:n=Cp.hostname(),name:r=Cp.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,deliverySample:c=null,onHashKey:l=null,onViewBase:u=null,metricsMs:d=WW,onUpdateAvailable:p=null,onUpdatePolicy:f=null,onHelloRefused:w=null,updatePolicy:m=null,installKind:g=Ss(),selfUrl:y=`http://127.0.0.1:${process.env.PORT||3362}`,apiToken:b=null,removedMarkerFile:k=g$,onRemoved:S=null,onDegraded:$=null,onRecovered:M=null,onConnected:L=null,ingestMode:B=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:A=LW,unackedRetryMs:I=jW,pingMs:j=BW,maxInflightEvents:K=UW,httpTimeoutMs:R=HW,stallBackoffMinMs:T=FW,fetchImpl:C=globalThis.fetch,log:P=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=u$(e);let W=y.replace(/^http/,"ws"),Y=new Set,me=new Map,_e=new Map,Pe=Gt.join(Ry,"plane-link","cursor.json"),ne=Gt.join(Ry,"plane-link","dead-letter.ndjson"),Ve=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),ve={},ce={},re=new Map,Ce=null,J=null,he=0,Fe=0,O=0,oe=Promise.resolve(),Re="ws",$t=0,X=!1,we=0,mt=!1,nt=yo,an=new Set,In=[],x=null,h=null,v=!1,D=!1,Z=p$,ue=null,ge=null,ct=null,ze=null,lt=null,xf=!1,jl=0,ii=!1,Mr=OW(k);if(Mr)if(Mr.tokenPreview===d$(t))ii=!0,Cf({reason:Mr.reason??"removed",at:Mr.at??null,nodeId:Mr.nodeId??n}),P.error(`[plane-link] this node was REMOVED from the plane (${Mr.reason??"removed"}${Mr.at?` at ${new Date(Mr.at).toISOString()}`:""}) \u2014 not dialing. Re-provision it with a new node token, or delete ${k} to force a dial.`);else try{Ga.unlinkSync(k),P.log("[plane-link] stale removed-marker discarded \u2014 the node token changed")}catch{}function vf({reason:F="removed",at:H=Date.now(),via:E="ctl"}={}){if(!ii){ii=!0,Cf({reason:F,at:H,nodeId:n,via:E});try{Ga.mkdirSync(Gt.dirname(k),{recursive:!0}),Ga.writeFileSync(k,JSON.stringify({at:H,reason:F,via:E,nodeId:n,tokenPreview:d$(t)},null,2))}catch(U){P.error(`[plane-link] could not write ${k}: ${U?.message||U}`)}P.error(`[plane-link] the plane REMOVED this node (${F}, via ${E}) \u2014 going dormant: no more dials, ingest or broker calls. Re-provision with a new node token to reconnect; the box and its data are untouched.`);try{S?.({reason:F,at:H,via:E})}catch{}pk({bye:!1})}}async function lC(){try{ve=JSON.parse(await rl.readFile(Pe,"utf8"))}catch{ve={}}}function uC(){xf=!0,!ct&&(ct=setTimeout(async()=>{if(ct=null,!!xf){xf=!1;try{await rl.mkdir(Gt.dirname(Pe),{recursive:!0}),await rl.writeFile(Pe,JSON.stringify(ve))}catch(F){P.error("[plane-link] cursor flush failed:",F?.message||F)}}},DW),ct.unref?.())}function ut(F){return!h||h.readyState!==_y.OPEN?!1:(h.send(JSON.stringify(F)),!0)}function ci(F){let H=Date.now();for(let{trackId:E,event:U}of F){let ee=re.get(U.id);re.set(U.id,{trackId:E,seq:U.seq,event:U,sentAt:H,firstSentAt:ee?.firstSentAt??H,retryAt:H+I,tries:(ee?.tries??0)+1,nacks:ee?.nacks??0})}return Re==="http"?(mC(F),!0):ut({v:ft,ch:"events",type:"append",events:F})}function dC(F){let H=null;for(let E of re.values())E.trackId===F&&(H==null||E.seq<H)&&(H=E.seq);return H}function Sf(F){let H=ce[F];if(H==null)return;let E=dC(F),U=E==null?H:Math.min(H,E-1);U>(ve[F]??0)&&(ve[F]=U,uC())}function li(){if(Ce=Date.now(),J!=null){let F=Date.now()-J;P.log(`[plane-link] spine sync recovered after ${Math.round(F/1e3)}s degraded (${he} stall redial(s))`);let H=J;J=null,he=0,Fe=0,X=!1,we=0;try{M?.({since:H,downMs:F})}catch(E){P.error(`[plane-link] onRecovered failed: ${E?.message||E}`)}}}function Af(F){if(!Array.isArray(F)||!F.length)return;let H=new Set;for(let E of F){let U=re.get(E);U&&(re.delete(E),(ce[U.trackId]??0)<U.seq&&(ce[U.trackId]=U.seq),H.add(U.trackId))}for(let E of H)Sf(E);li()}function ok(F,H){O++;let E={at:Date.now(),trackId:F.trackId,seq:F.seq,id:F.event?.id??null,type:F.event?.type??null,reason:H,event:F.event??null};P.error(`[plane-link] DEAD-LETTER ${F.trackId} seq ${F.seq} (${E.type??"?"}): plane rejected it permanently (${H}) \u2014 kept in ${ne}`),oe=oe.then(async()=>{await rl.mkdir(Gt.dirname(ne),{recursive:!0}),await rl.appendFile(ne,JSON.stringify(E)+`
|
|
684
|
+
`)}).catch(U=>P.error(`[plane-link] dead-letter write failed: ${U?.message||U}`))}function sk(F,H,E,U=null){if(!Array.isArray(F)||!F.length)return;let ee=new Set,Se=Date.now();for(let Ee of F){let Ge=re.get(Ee);Ge&&(E?(re.delete(Ee),ok(Ge,H),(ce[Ge.trackId]??0)<Ge.seq&&(ce[Ge.trackId]=Ge.seq),ee.add(Ge.trackId)):(Ge.nacks++,Ge.retryAt=Se+Math.max(1e3,Number(U)||I)))}for(let Ee of ee)Sf(Ee);li()}function ak(F=Date.now()){let H=1/0;for(let E of re.values())E.firstSentAt<H&&(H=E.firstSentAt);return H===1/0?0:F-H}function ik(){if(!v||!re.size)return;let F=Date.now(),H=ak(F);if(H<A||Ce!=null&&F-Ce<A||Re==="http"&&X||Fe>F)return;he++;let E=Math.min(T*2**(he-1),m$);if(J==null){J=F-H,P.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(H/1e3)}s with ${re.size} event(s) in flight (${Re} door) \u2014 the plane is not answering appends`);try{$?.({since:J,unacked:re.size,oldestAgeMs:H,mode:Re})}catch(U){P.error(`[plane-link] onDegraded failed: ${U?.message||U}`)}}if(Re==="http"){X=!0,we=F+E,P.error(`[plane-link] ingest circuit OPEN (stall #${he}); probing /v1/ingest/health in ${Math.round(E/1e3)}s`);return}Fe=F+E,P.error(`[plane-link] closing the link and redialing in ${Math.round(E/1e3)}s (stall redial #${he})`);try{h?.terminate()}catch{}}function Tf(){if(!v)return;let F=Date.now();if(Re==="http"&&X)return;let H=[...re.values()].filter(U=>U.event&&U.retryAt<=F&&!(Re==="http"&&an.has(U.trackId)));if(!H.length)return;H.sort((U,ee)=>U.trackId===ee.trackId?U.seq-ee.seq:U.trackId<ee.trackId?-1:1),P.log(`[plane-link] re-sending ${H.length} unacked event(s) the plane never confirmed`);let E=Re==="http"?nt:yo;if(Re==="http"){let U=new Map;for(let ee of H)(U.get(ee.trackId)??U.set(ee.trackId,[]).get(ee.trackId)).push(ee);for(let[,ee]of U){if($t>=$y)return;ci(ee.slice(0,E).map(Se=>({trackId:Se.trackId,event:Se.event})))}return}for(let U=0;U<H.length;U+=E){let ee=H.slice(U,U+E).map(Se=>({trackId:Se.trackId,event:Se.event}));if(!ci(ee))return}}async function pC(F=null){for(;;){if(!v)return!1;let H=re.size<K,E=Re!=="http"||$t<$y&&!X&&(!F||!an.has(F));if(H&&E)return!0;await new Promise(U=>setTimeout(U,100))}}function Ef(){if(x=null,!v||!In.length)return;let F=In;if(In=[],Re!=="http"){for(let E=0;E<F.length;E+=yo)ci(F.slice(E,E+yo));return}let H=new Map;for(let E of F)(H.get(E.trackId)??H.set(E.trackId,[]).get(E.trackId)).push(E);for(let[E,U]of H){if(an.has(E)||$t>=$y||X){In.push(...U);continue}for(let ee=0;ee<U.length;ee+=nt)ci(U.slice(ee,ee+nt))}In.length&&!x&&(x=setTimeout(Ef,h$),x.unref?.())}let fC=F=>{let H=new Map;for(let{trackId:E,event:U}of F)(H.get(E)??H.set(E,[]).get(E)).push(U);return[...H].map(([E,U])=>({trackId:E,events:U}))},Ro=(F,H,E,{progress:U})=>{let ee=Date.now();for(let{event:Se}of F){let Ee=re.get(Se.id);Ee&&(Ee.nacks++,Ee.retryAt=ee+Math.max(1e3,E||I))}U&&li()};async function mC(F){let H=new Set(F.map(E=>E.trackId));for(let E of H)an.add(E);$t++;try{let E=await C(`${Ve}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:fC(F)}),signal:AbortSignal.timeout(R)}),U=(Number(E.headers.get("retry-after"))||0)*1e3;if(E.status===200){let ee=await E.json();nt=yo;for(let Se of ee.results??[]){Af(Se.acked??[]);let Ee=(Ge,Rt)=>{let Yt=new Map;for(let ot of Ge??[])(Yt.get(ot.reason)??Yt.set(ot.reason,[]).get(ot.reason)).push(ot.id);for(let[ot,fk]of Yt)sk(fk,ot,Rt,ee.retryAfterMs??null)};Ee(Se.rejected,!0),Ee(Se.deferred,!1)}Af(ee.discarded??[]),li();return}if(E.status===429){Ro(F,"overloaded",U||5e3,{progress:!0});return}if(E.status===413&&F.length>1){nt=Math.max(1,Math.floor(F.length/2)),P.error(`[plane-link] ingest request too large (${F.length} events) \u2014 halving batches to ${nt}`),Ro(F,"too_large",1e3,{progress:!0});return}if(E.status===413){let ee=re.get(F[0].event.id);ee&&(re.delete(ee.event.id),ok(ee,"too_large"),(ce[ee.trackId]??0)<ee.seq&&(ce[ee.trackId]=ee.seq),Sf(ee.trackId)),li();return}if(E.status===410){vf({reason:"removed",via:"410"}),Ro(F,"http_410",6e4,{progress:!1});return}if(E.status===401||E.status===403){let{tripped:ee,holdMs:Se}=js();if(ee&&P.error(`[plane-link] holding every plane call for ${Math.round(Se/6e4)} min \u2014 the ingest door refused this node's token three times in a row`),Date.now()-jl>=w$){jl=Date.now();let Ee="";try{Ee=(await E.json())?.error??""}catch{}P.error(`[plane-link] plane refused ingest (HTTP ${E.status}${Ee?`: ${Ee}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}Ro(F,`http_${E.status}`,Math.max(6e4,wi()),{progress:!1});return}if(E.status===400){P.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${F.length} event(s) for retry; this is a bug, not data`),Ro(F,"http_400",6e4,{progress:!1});return}Ro(F,`http_${E.status}`,U||5e3,{progress:!1})}catch(E){let U=E?.name==="TimeoutError"||E?.name==="AbortError";U&&F.length>1&&(nt=Math.max(1,Math.floor(F.length/2)),P.error(`[plane-link] ingest request of ${F.length} event(s) outlived its ${R} ms deadline \u2014 halving batches to ${nt}`)),Ro(F,U?"timeout":"network",5e3,{progress:!1})}finally{$t--;for(let E of H)an.delete(E)}}async function hC(){if(Re!=="http"||!X||!v||mt)return;let F=Date.now();if(!(we>F||Ls(F))){mt=!0;try{let H=await C(`${Ve}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(R)});if(!H.ok)throw new Error(`HTTP ${H.status}`);P.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),X=!1,we=0,Fe=Date.now()+R+I+1e3;for(let E of re.values())E.retryAt=Math.min(E.retryAt,Date.now());Tf()}catch(H){he++;let E=Math.min(T*2**(he-1),m$);we=Date.now()+E,P.error(`[plane-link] ingest door still not answering (${H?.message||H}); next probe in ${Math.round(E/1e3)}s`)}finally{mt=!1}}}function ck(){let F=Date.now();return{mode:Re,unacked:re.size,oldestUnackedAgeMs:ak(F),lastProgressAt:Ce,degradedSince:J,stallRedials:he,deadLettered:O,http:Re==="http"?{inFlight:$t,circuitOpen:X,nextProbeInMs:X?Math.max(0,we-F):0,batchCap:nt}:null}}async function lk(){if(!i)return;let F;try{F=await i()}catch(E){P.error("[plane-link] metrics sample failed:",E?.message||E);return}let H=null;try{H=c?.()??null}catch{}F&&ut({v:ft,ch:"ctl",type:"metrics",metrics:{...F,planeLink:ck(),...H?{delivery:H}:{}}})}function wC(){i&&(lt&&clearInterval(lt),lk(),lt=setInterval(lk,d),lt.unref?.())}async function gC(){let F;try{F=await o()}catch(H){P.error("[plane-link] backfill listTracks failed:",H?.message||H);return}ut({v:ft,ch:"ctl",type:"tracks",ids:F.filter(H=>!H.transferredTo).map(H=>H.id).filter(H=>typeof H=="string")});for(let H of F){if(!v)return;let E;try{E=await G(H)}catch{continue}let U=ve[H.id]??0,ee=E.filter(Ee=>Ee.seq>U&&typeof Ee.id=="string"),Se=Re==="http"?nt:yo;for(let Ee=0;Ee<ee.length;Ee+=Se){if(!await pC(H.id))return;let Ge=ee.slice(Ee,Ee+Math.min(Se,Re==="http"?nt:yo)).map(Rt=>({trackId:H.id,event:Rt}));if(!ci(Ge))return}}}function uk(F){!v||!F?.trackId||typeof F.id!="string"||(In.push({trackId:F.trackId,event:F}),x||(x=setTimeout(Ef,h$),x.unref?.()))}let ui=new Map;async function yC(F){let{reqId:H,method:E,path:U,headers:ee={},body:Se}=F,Ee=Object.fromEntries(Object.entries(ee).filter(([,Rt])=>Rt!=null)),Ge=new AbortController;ui.set(H,Ge);try{if(F.host){await bC(F,Ee,Ge);return}b&&(Ee["x-bostrat-token"]=b);let Rt=await fetch(y+U,{method:E,headers:Ee,body:Se?Buffer.from(Se,"base64"):void 0,signal:Ge.signal}),Yt=Object.fromEntries(Rt.headers);for(let ot of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Yt[ot];if(Yt["content-type"]||(Yt["content-type"]="application/octet-stream"),ut({v:ft,ch:"control",type:"http_head",reqId:H,status:Rt.status,headers:Yt}),Rt.body){for await(let ot of Rt.body)if(!ut({v:ft,ch:"control",type:"http_data",reqId:H,data:Buffer.from(ot).toString("base64")}))break}ut({v:ft,ch:"control",type:"http_end",reqId:H,error:null})}finally{ui.delete(H)}}function bC(F,H,E){let{reqId:U,method:ee,path:Se,body:Ee}=F,Ge=new URL(y);return new Promise(Rt=>{let Yt=$W.request({host:Ge.hostname,port:Ge.port,method:ee,path:Se,headers:{...H,host:F.host},signal:E.signal},ot=>{let{connection:fk,"keep-alive":MV,"transfer-encoding":CV,upgrade:NV,...kC}=ot.headers;ut({v:ft,ch:"control",type:"http_head",reqId:U,status:ot.statusCode||502,headers:kC}),ot.on("data",xC=>{ut({v:ft,ch:"control",type:"http_data",reqId:U,data:Buffer.from(xC).toString("base64")})||ot.destroy()}),ot.on("end",()=>{ut({v:ft,ch:"control",type:"http_end",reqId:U,error:null}),Rt()}),ot.on("error",()=>{ut({v:ft,ch:"control",type:"http_end",reqId:U,error:"view upstream error"}),Rt()})});Yt.on("error",ot=>{ut({v:ft,ch:"control",type:"http_end",reqId:U,error:ot?.message||"view request failed"}),Rt()}),Ee&&Yt.write(Buffer.from(Ee,"base64")),Yt.end()})}function dk(){if(D||ii)return;h=new _y(e,{headers:{authorization:`Bearer ${t}`}}),h.on("open",async()=>{let H=null;try{H=await s?.()||null}catch{H=null}ut({v:ft,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:Sn,installKind:g,...typeof m=="function"?{updatePolicy:m()}:m?{updatePolicy:m}:{},providers:MW(),...H?{agents:H}:{}})}),h.on("message",H=>{let E;try{E=JSON.parse(H)}catch{return}if(E.ch==="ctl"&&E.type==="hello_ok"){v=!0,Z=p$,ue=null,xk(),P.log(`[plane-link] connected to plane as ${n}`);try{L?.()}catch{}if(typeof E.hashKey=="string"&&E.hashKey)try{l?.(E.hashKey)}catch{}try{u?.(typeof E.viewBase=="string"&&E.viewBase?E.viewBase:null)}catch{}E.minNodeVersion&&Sn&&ol(Sn,E.minNodeVersion)&&P.error(`[plane-link] this node is bostrat-node ${Sn}; the plane asks for \u2265${E.minNodeVersion} \u2014 update with: ${Mp(g)}`);try{f?.(typeof E.updatePolicy=="string"?E.updatePolicy:null)}catch{}let U=typeof E.latestNodeVersion=="string"?E.latestNodeVersion:null;if(U&&Sn&&ol(Sn,U)&&!Y.has(U)){Y.add(U),P.log(`[plane-link] bostrat-node ${U} is available (this node runs ${Sn}) \u2014 ${Mp(g)}`);try{p?.({current:Sn,latest:U,min:E.minNodeVersion??null,installKind:g,required:!!(E.minNodeVersion&&ol(Sn,E.minNodeVersion))})}catch(Se){P.error(`[plane-link] update notice failed: ${Se?.message||Se}`)}}let ee=E.ingest==="http";Re=B==="ws"?"ws":B==="http"||ee?"http":"ws",B==="http"&&!ee&&P.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),Re==="http"&&P.log("[plane-link] spine rides the HTTPS ingest door"),X=!1,we=0,nt=yo,ze&&clearInterval(ze),ze=setInterval(()=>{ut({v:ft,ch:"ctl",type:"ping"}),ik(),hC().catch(()=>{}),Tf(),In.length&&!x&&Ef()},j),ze.unref?.(),wC(),gC().catch(Se=>P.error("[plane-link] backfill failed:",Se?.message||Se));return}if(E.ch==="ctl"&&E.type==="hello_refused"){let U=typeof E.minNodeVersion=="string"?E.minNodeVersion:null,ee=typeof E.latestNodeVersion=="string"?E.latestNodeVersion:null;P.error(`[plane-link] plane REFUSED this node: bostrat-node ${Sn??"?"} is below its minimum ${U??"?"} \u2014 ${E.fix||Mp(g)}`),Z=f$,ue=0;try{w?.({current:Sn,min:U,latest:ee,fix:E.fix||Mp(g),installKind:g})}catch(Se){P.error(`[plane-link] refusal handler failed: ${Se?.message||Se}`)}return}if(E.ch==="ctl"&&E.type==="removed"){ut({v:ft,ch:"ctl",type:"removed_ack",nodeId:n}),vf({reason:typeof E.reason=="string"?E.reason:"removed",at:Number(E.at)||Date.now(),via:"ctl"});return}if(E.ch==="ctl"&&E.type==="update_policy"){try{f?.(typeof E.mode=="string"?E.mode:null)}catch{}return}if(E.ch==="ctl"&&E.type==="usage_caps"){td(E.caps??{},{source:"plane",updatedAt:E.updatedAt??Date.now()}).then(U=>P.log(`[plane-link] spend caps ${Object.keys(U.caps).length?`updated (${Object.keys(U.caps).join(", ")})`:"cleared"} from plane`)).catch(U=>P.error("[plane-link] spend-caps push rejected:",U?.message||U));return}if(E.ch==="ctl"&&E.type==="transferred"&&Array.isArray(E.tracks)){Promise.resolve().then(()=>a?.(E.tracks)).catch(U=>P.error("[plane-link] transfer notice failed:",U?.message||U));return}if(E.ch==="events"&&E.type==="ack"&&Array.isArray(E.ids)){Af(E.ids);return}if(E.ch==="events"&&E.type==="nack"&&Array.isArray(E.ids)){sk(E.ids,E.reason,E.permanent===!0,E.retryAfterMs??null);return}if(E.ch==="control"&&E.type==="http_open"){yC(E).catch(U=>ut({v:ft,ch:"control",type:"http_end",reqId:E.reqId,error:U?.message||"tunnel error"}));return}if(E.ch==="control"&&E.type==="http_cancel"){ui.get(E.reqId)?.abort();return}if(E.ch==="attach"&&E.type==="open"){let U=new _y(W+E.path,Array.isArray(E.protocols)?E.protocols:[],E.host?{headers:{host:E.host}}:void 0);me.set(E.streamId,U),_e.set(E.streamId,[]),U.on("open",()=>{let Se=_e.get(E.streamId)||[];_e.delete(E.streamId);for(let{buf:Ee,bin:Ge}of Se)U.send(Ee,{binary:Ge})}),U.on("message",(Se,Ee)=>{let Ge=Ee?Se:Buffer.from(String(Se));ut({v:ft,ch:"attach",type:"data",streamId:E.streamId,data:Ge.toString("base64"),...Ee?{bin:!0}:{}})});let ee=()=>{_e.delete(E.streamId),me.delete(E.streamId)&&ut({v:ft,ch:"attach",type:"close",streamId:E.streamId})};U.on("close",ee),U.on("error",ee);return}if(E.ch==="attach"&&E.type==="data"){let U=me.get(E.streamId);if(!U)return;let ee=Buffer.from(E.data,"base64"),Se=E.bin===!0,Ee=_e.get(E.streamId);Ee?Ee.push({buf:ee,bin:Se}):U.send(ee,{binary:Se});return}if(E.ch==="attach"&&E.type==="close"){let U=me.get(E.streamId);me.delete(E.streamId),_e.delete(E.streamId),U?.close();return}});let F=()=>{v&&P.log("[plane-link] disconnected from plane"),v=!1,ze&&(clearInterval(ze),ze=null),lt&&(clearInterval(lt),lt=null),re.clear(),ce={},In=[],x&&(clearTimeout(x),x=null),an.clear();for(let U of me.values())U.close();me.clear(),_e.clear();for(let U of ui.values())U.abort();if(ui.clear(),D||ge)return;ue==null&&(ue=Date.now());let H=Date.now()-ue<CW,E=Math.max(H?NW+Math.random()*PW:Z,Fe-Date.now(),wi());ge=setTimeout(()=>{ge=null,dk()},E),ge.unref?.(),H||(Z=Math.min(Z*2,f$))};h.on("close",F),h.on("error",H=>{let E=/Unexpected server response: (\d+)/.exec(H?.message||"")?.[1];if(E==="410"){vf({reason:"removed",via:"410"});return}if(E==="401"||E==="403"){let{tripped:U,holdMs:ee}=js();U?P.error(`[plane-link] holding every plane call for ${Math.round(ee/6e4)} min \u2014 three credential refusals in a row (HTTP ${E}) mean the token is revoked or unknown, not that the plane is down; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN. Next dial in ${Math.round(ee/6e4)} min.`):Date.now()-jl>=w$&&(jl=Date.now(),P.error(`[plane-link] plane refused this node's credentials (HTTP ${E}) \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)`))}F()})}Xe.on("event",uk),lC().then(dk);function pk({bye:F=!0}={}){D=!0,Xe.off("event",uk),ge&&clearTimeout(ge),ct&&clearTimeout(ct),ze&&clearInterval(ze),lt&&clearInterval(lt),x&&clearTimeout(x),F&&ut({v:ft,ch:"ctl",type:"bye",reason:"shutdown"}),h?.close()}return{stop:()=>pk(),status:()=>({connected:v,nodeId:n,cursor:{...ve},inflight:re.size,...ck(),auth:vk(),dormant:ii,removed:Gl()}),_tick:()=>{ik(),Tf()}}}Su();var Np=(()=>{try{return KW(import.meta.url)("../package.json").version||null}catch{return null}})(),VW=process.env.BOSTRAT_STATE_DIR||gt.join(process.env.HOME||qW.tmpdir(),".bostrat"),x$=gt.join(VW,"self-update"),Pp=gt.join(x$,"status.json"),zW=1440*6e4,JW=6e4,sl=new Set(["npm-global","git-checkout"]);function v$({kind:e=Ss(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:sl.has(e)?e==="git-checkout"||YW(t)?"auto":"prompt":"off"}function YW(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var Kt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function XW({kind:e,to:t,from:n=Np,statusFile:r=Pp,logFile:o=gt.join(x$,"last.log"),port:s=Number(process.env.PORT)||3362,npmPrefix:a=null,serviceName:i=process.platform==="darwin"?"com.bostrat.node":"bostrat-node.service",uid:c=typeof process.getuid=="function"?process.getuid():0,liveDir:l=process.env.BOSTRAT_LIVE_CHECKOUT||gt.resolve(gt.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!sl.has(e))throw new Error(`self-update: install kind "${e}" is not actionable here`);if(!/^\d+\.\d+\.\d+$/.test(String(t)))throw new Error(`self-update: bad target version "${t}"`);if(n&&!/^\d+\.\d+\.\d+$/.test(String(n)))throw new Error(`self-update: bad current version "${n}"`);let d=(g,y="")=>`write_status ${Kt(g)} ${y}`,p=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${Kt(i)}`,f=`
|
|
685
685
|
health() {
|
|
686
686
|
for i in $(seq 1 45); do
|
|
687
687
|
sleep 1
|
|
@@ -746,66 +746,66 @@ if [ -n "$FROM" ] && ${m("$FROM")}; then
|
|
|
746
746
|
fi
|
|
747
747
|
${d("failed",'"deploy-pi.sh v$TO failed AND the rollback failed"')}
|
|
748
748
|
exit 1
|
|
749
|
-
`}async function al(e=Np){try{return JSON.parse(await bo.readFile(e,"utf8"))}catch{return null}}var zW=new Set(["updating","restarting","rolling-back"]),Pp=e=>!!(e&&zW.has(e.status));async function Dp({to:e,from:t=Cp,kind:n=Ss(),reason:r="manual",statusFile:o=Np,spawnImpl:s=UW,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!sl.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!ol(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await al(o);if(Pp(l))return{ok:!1,reason:`an update to ${l.to} is already in flight`,status:l};if(l&&l.to===e&&(l.status==="failed"||l.status==="rolled-back"))return{ok:!1,reason:`update to ${e} already ${l.status} (${l.error||"no detail"}) \u2014 retried when a newer release exists`,status:l};await bo.mkdir(gt.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?vm(await JW())||gt.resolve(gt.dirname(process.execPath),".."):null,d=VW({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),p=gt.join(gt.dirname(o),`update-${e}.sh`),f=gt.join(gt.dirname(o),"last.log");await bo.writeFile(p,d,{mode:448}),await bo.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let w=g$.existsSync("/run/systemd/system")&&await YW("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",p]]:["setsid",["-f","bash","-c",`exec bash ${Kt(p)} >> ${Kt(f)} 2>&1`]];try{s(w[0],w[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(m){return await bo.writeFile(o,JSON.stringify({status:"failed",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a(),error:`could not spawn updater: ${m?.message||m}`})),{ok:!1,reason:`could not spawn updater: ${m?.message||m}`}}return c.log?.(`[self-update] ${t??"?"} \u2192 ${e} (${n}, ${r}) started \u2014 status ${o}`),{ok:!0,to:e,from:t,kind:n,scriptFile:p,statusFile:o}}async function JW(){try{return await bo.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function YW(e){for(let t of String(process.env.PATH||"").split(gt.delimiter))try{return await bo.access(gt.join(t,e),g$.constants.X_OK),!0}catch{}return!1}var XW=45*6e4;async function k$({statusFile:e=Np,current:t=Cp,now:n=Date.now,staleMs:r=XW}={}){let o=await al(e);if(!o)return null;let s=a=>bo.writeFile(e,JSON.stringify(a)).catch(()=>{});if(Pp(o)){if(t&&o.to===t){let a={...o,status:"done",updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}if(n()-Number(o.updatedAt||o.startedAt||0)>r){let a={...o,status:"failed",error:`updater went silent while ${o.status} (no status change for ${Math.round(r/6e4)} min)`,updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}return null}if(["failed","rolled-back","done"].includes(o.status)&&!o.announced){let a={...o,announced:!0};return await s(a),a}return null}function x$({isBusy:e,start:t=Dp,onStuck:n=null,onStarted:r=null,tickMs:o=GW,stuckMs:s=qW,now:a=Date.now,setIntervalImpl:i=setInterval,clearIntervalImpl:c=clearInterval,log:l=console}={}){let u=null,d=()=>{u?.timer&&c(u.timer),u=null},p=async()=>{if(!u)return;let f=!0;try{f=await e()!==!1}catch{f=!0}if(f){if(!u.stuckFired&&a()-u.since>=s){u.stuckFired=!0;try{await n?.({to:u.to,from:u.from,since:u.since})}catch{}}return}let w=u;d();let m=await t({to:w.to,from:w.from,kind:w.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${m.ok?"started":`not started: ${m.reason}`}`);try{await r?.({...w,result:m})}catch{}};return{arm({to:f,from:w=Cp,kind:m=Ss()}){return u?.to===f?!1:(d(),u={to:f,from:w,kind:m,since:a(),stuckFired:!1,timer:i(p,o)},u.timer.unref?.(),!0)},tick:p,cancel:d,get armed(){return u?{to:u.to,from:u.from,kind:u.kind,since:u.since}:null}}}ye();yt();var v$=(...e)=>fetch(...e);function S$(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function $y(e=process.env){if(!S$(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 v$(`${Ne(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 Ry(e=process.env){if(!S$(e))throw new Error("no plane configured \u2014 the roadmap lives 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 v$(`${Ne(e)}/v1/roadmap`,{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{people:Array.isArray(o.people)?o.people:[],slots:Array.isArray(o.slots)?o.slots:[],reservations:Array.isArray(o.reservations)?o.reservations:[],tray:Array.isArray(o.tray)?o.tray:[],circuits:Array.isArray(o.circuits)?o.circuits:[]}}finally{clearTimeout(n)}}async function Ga(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await $y(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 Bp(e,t,{source:n="chat"}={}){return N(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}ye();yt();var ZW=(...e)=>fetch(...e);async function jp(e,t,n,r=process.env){if(!(Ne(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 ZW(`${Ne(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 QW(e,t=process.env){return jp("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function A$({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await QW(t,o):await Ga(e,o),a=await jp("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function Oy(e,t=process.env){let n=await Ga(e,t),r=await jp("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function My(e,t=process.env){return(await jp("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function T$(e,t,n,r=process.env){let{blueprint:o,items:s}=await Oy(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 N(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}ye();yt();var eq=(...e)=>fetch(...e);async function tq(e,t,n,r=process.env){if(!(Ne(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 eq(`${Ne(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)}}var E$=72,Cy=160;async function I$(e,{title:t,context:n="",decision:r,summary:o,alternatives:s=[],supersedesId:a=null}){let i=String(t??"").trim(),c=String(o??"").trim();if(!i)throw new Error("a title is required");if(i.length>E$)throw new Error(`the title is the choice as a LABEL \u2014 ${E$} chars max (yours is ${i.length}); the full story goes in the decision text`);if(!c)throw new Error(`a summary is required \u2014 one sentence (max ${Cy} chars) a reader can act on without opening the record`);if(c.length>Cy)throw new Error(`the summary is one sentence \u2014 ${Cy} chars max (yours is ${c.length}); the full story goes in the decision text`);if(!String(r??"").trim())throw new Error("the decision itself is required (what was decided)");return await N(e,{type:"decision.recorded",actor:"orchestrator",source:"orchestrator",payload:{title:i,context:String(n??"").trim(),decision:String(r).trim(),summary:c,alternatives:(Array.isArray(s)?s:[]).map(l=>typeof l=="string"?{label:l}:l).filter(l=>l?.label),...a?{supersedesId:a}:{}}}),{title:i}}function _$(e,t=process.env){return tq("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}cg();ye();import fl from"node:fs/promises";import zp from"node:path";import Xq from"node:os";jr();import nq from"node:fs/promises";import $$ from"node:path";import rq from"node:os";var R$=()=>$$.join(process.env.BOSTRAT_STATE_DIR||$$.join(process.env.HOME||rq.tmpdir(),".bostrat"),"integrations.json");async function Rr(){try{return JSON.parse(await nq.readFile(R$(),"utf8"))||{}}catch{return{}}}var Lp=["linked","prOpened","prMerged","create"],Ny=["prompt","auto","off"],oq={create:["prompt","off"]},O$=e=>oq[e]??Ny;function Fp(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of Lp){let s=O$(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 As(){let e=await Rr(),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:Fp(e),defaultTeam:n}:null}async function Up(){let e=await Rr(),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:Fp(e)}}async function Py(){return Fp(await Rr())}async function M$(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${Lp.join(", ")}`);for(let[r,o]of t){if(!Lp.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Lp.join(", ")}`);let s=O$(r);if(!s.includes(o))throw new Error(s.length<Ny.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Ny.join(", ")}`)}let n=await Rr();return n.ticketFlow={...Fp(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Mt(R$(),n,{mode:384}),n.ticketFlow}async function il(){let e=(await Rr()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function sq(){let e=(await Rr()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var aq={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Hp(){let t=(await Rr()).voiceChat||{},n=t.provider||"openai",r=aq[n];if(!r)return null;let o=n==="openai"?await sq():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function C$(){let e=(await Rr()).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 Ts(){let e=(await Rr()).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}}ye();Fr();Yr();Ra();import ll from"node:fs/promises";import X$ from"node:path";import Nq from"node:os";import{randomUUID as Z$}from"node:crypto";var By={};Ef(By,{createComment:()=>hq,findIssueUrl:()=>uq,issueStates:()=>yq,mdToAdf:()=>D$,moveIssue:()=>bq,parseIssueRef:()=>lq,resolveIssue:()=>dq,uploadImage:()=>wq,upsertCard:()=>pq,whoami:()=>Dy});var Ka=e=>String(e.baseUrl||"").replace(/\/$/,""),P$=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function Es(e,t,n,r){let o=await fetch(`${Ka(e)}${n}`,{method:t,headers:{authorization:P$(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 iq=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,cq=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function lq(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(iq);return r?r[1].toUpperCase():null}let n=t.match(cq);return n?n[1]:null}function uq(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 dq(e,t){let n;try{n=await Es(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:`${Ka(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function pq(e,t,{url:n,title:r,subtitle:o}){await Es(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 fq=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function mq(e){let t=[],n=0;for(let r of e.matchAll(fq)){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 D$(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
750
|
-
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...
|
|
749
|
+
`}async function al(e=Pp){try{return JSON.parse(await bo.readFile(e,"utf8"))}catch{return null}}var ZW=new Set(["updating","restarting","rolling-back"]),Dp=e=>!!(e&&ZW.has(e.status));async function Bp({to:e,from:t=Np,kind:n=Ss(),reason:r="manual",statusFile:o=Pp,spawnImpl:s=GW,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!sl.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!ol(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await al(o);if(Dp(l))return{ok:!1,reason:`an update to ${l.to} is already in flight`,status:l};if(l&&l.to===e&&(l.status==="failed"||l.status==="rolled-back"))return{ok:!1,reason:`update to ${e} already ${l.status} (${l.error||"no detail"}) \u2014 retried when a newer release exists`,status:l};await bo.mkdir(gt.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?vm(await QW())||gt.resolve(gt.dirname(process.execPath),".."):null,d=XW({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),p=gt.join(gt.dirname(o),`update-${e}.sh`),f=gt.join(gt.dirname(o),"last.log");await bo.writeFile(p,d,{mode:448}),await bo.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let w=k$.existsSync("/run/systemd/system")&&await eq("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",p]]:["setsid",["-f","bash","-c",`exec bash ${Kt(p)} >> ${Kt(f)} 2>&1`]];try{s(w[0],w[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(m){return await bo.writeFile(o,JSON.stringify({status:"failed",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a(),error:`could not spawn updater: ${m?.message||m}`})),{ok:!1,reason:`could not spawn updater: ${m?.message||m}`}}return c.log?.(`[self-update] ${t??"?"} \u2192 ${e} (${n}, ${r}) started \u2014 status ${o}`),{ok:!0,to:e,from:t,kind:n,scriptFile:p,statusFile:o}}async function QW(){try{return await bo.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function eq(e){for(let t of String(process.env.PATH||"").split(gt.delimiter))try{return await bo.access(gt.join(t,e),k$.constants.X_OK),!0}catch{}return!1}var tq=45*6e4;async function S$({statusFile:e=Pp,current:t=Np,now:n=Date.now,staleMs:r=tq}={}){let o=await al(e);if(!o)return null;let s=a=>bo.writeFile(e,JSON.stringify(a)).catch(()=>{});if(Dp(o)){if(t&&o.to===t){let a={...o,status:"done",updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}if(n()-Number(o.updatedAt||o.startedAt||0)>r){let a={...o,status:"failed",error:`updater went silent while ${o.status} (no status change for ${Math.round(r/6e4)} min)`,updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}return null}if(["failed","rolled-back","done"].includes(o.status)&&!o.announced){let a={...o,announced:!0};return await s(a),a}return null}function A$({isBusy:e,start:t=Bp,onStuck:n=null,onStarted:r=null,tickMs:o=JW,stuckMs:s=zW,now:a=Date.now,setIntervalImpl:i=setInterval,clearIntervalImpl:c=clearInterval,log:l=console}={}){let u=null,d=()=>{u?.timer&&c(u.timer),u=null},p=async()=>{if(!u)return;let f=!0;try{f=await e()!==!1}catch{f=!0}if(f){if(!u.stuckFired&&a()-u.since>=s){u.stuckFired=!0;try{await n?.({to:u.to,from:u.from,since:u.since})}catch{}}return}let w=u;d();let m=await t({to:w.to,from:w.from,kind:w.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${m.ok?"started":`not started: ${m.reason}`}`);try{await r?.({...w,result:m})}catch{}};return{arm({to:f,from:w=Np,kind:m=Ss()}){return u?.to===f?!1:(d(),u={to:f,from:w,kind:m,since:a(),stuckFired:!1,timer:i(p,o)},u.timer.unref?.(),!0)},tick:p,cancel:d,get armed(){return u?{to:u.to,from:u.from,kind:u.kind,since:u.since}:null}}}ye();yt();var T$=(...e)=>fetch(...e);function E$(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Oy(e=process.env){if(!E$(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 T$(`${Ne(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 My(e=process.env){if(!E$(e))throw new Error("no plane configured \u2014 the roadmap lives 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 T$(`${Ne(e)}/v1/roadmap`,{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{people:Array.isArray(o.people)?o.people:[],slots:Array.isArray(o.slots)?o.slots:[],reservations:Array.isArray(o.reservations)?o.reservations:[],tray:Array.isArray(o.tray)?o.tray:[],circuits:Array.isArray(o.circuits)?o.circuits:[]}}finally{clearTimeout(n)}}async function Ka(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await Oy(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 jp(e,t,{source:n="chat"}={}){return N(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}ye();yt();var nq=(...e)=>fetch(...e);async function Lp(e,t,n,r=process.env){if(!(Ne(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 nq(`${Ne(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 rq(e,t=process.env){return Lp("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function I$({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await rq(t,o):await Ka(e,o),a=await Lp("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function Cy(e,t=process.env){let n=await Ka(e,t),r=await Lp("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function Ny(e,t=process.env){return(await Lp("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function _$(e,t,n,r=process.env){let{blueprint:o,items:s}=await Cy(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 N(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}ye();yt();var oq=(...e)=>fetch(...e);async function sq(e,t,n,r=process.env){if(!(Ne(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 oq(`${Ne(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)}}var $$=72,Py=160;async function R$(e,{title:t,context:n="",decision:r,summary:o,alternatives:s=[],supersedesId:a=null}){let i=String(t??"").trim(),c=String(o??"").trim();if(!i)throw new Error("a title is required");if(i.length>$$)throw new Error(`the title is the choice as a LABEL \u2014 ${$$} chars max (yours is ${i.length}); the full story goes in the decision text`);if(!c)throw new Error(`a summary is required \u2014 one sentence (max ${Py} chars) a reader can act on without opening the record`);if(c.length>Py)throw new Error(`the summary is one sentence \u2014 ${Py} chars max (yours is ${c.length}); the full story goes in the decision text`);if(!String(r??"").trim())throw new Error("the decision itself is required (what was decided)");return await N(e,{type:"decision.recorded",actor:"orchestrator",source:"orchestrator",payload:{title:i,context:String(n??"").trim(),decision:String(r).trim(),summary:c,alternatives:(Array.isArray(s)?s:[]).map(l=>typeof l=="string"?{label:l}:l).filter(l=>l?.label),...a?{supersedesId:a}:{}}}),{title:i}}function O$(e,t=process.env){return sq("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}cg();ye();import fl from"node:fs/promises";import Jp from"node:path";import t5 from"node:os";jr();import aq from"node:fs/promises";import M$ from"node:path";import iq from"node:os";var C$=()=>M$.join(process.env.BOSTRAT_STATE_DIR||M$.join(process.env.HOME||iq.tmpdir(),".bostrat"),"integrations.json");async function Rr(){try{return JSON.parse(await aq.readFile(C$(),"utf8"))||{}}catch{return{}}}var Fp=["linked","prOpened","prMerged","create"],Dy=["prompt","auto","off"],cq={create:["prompt","off"]},N$=e=>cq[e]??Dy;function Up(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of Fp){let s=N$(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 As(){let e=await Rr(),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:Up(e),defaultTeam:n}:null}async function Hp(){let e=await Rr(),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:Up(e)}}async function By(){return Up(await Rr())}async function P$(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${Fp.join(", ")}`);for(let[r,o]of t){if(!Fp.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Fp.join(", ")}`);let s=N$(r);if(!s.includes(o))throw new Error(s.length<Dy.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Dy.join(", ")}`)}let n=await Rr();return n.ticketFlow={...Up(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Mt(C$(),n,{mode:384}),n.ticketFlow}async function il(){let e=(await Rr()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function lq(){let e=(await Rr()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var uq={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Wp(){let t=(await Rr()).voiceChat||{},n=t.provider||"openai",r=uq[n];if(!r)return null;let o=n==="openai"?await lq():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function D$(){let e=(await Rr()).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 Ts(){let e=(await Rr()).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}}ye();Fr();Yr();Ra();import ll from"node:fs/promises";import eR from"node:path";import jq from"node:os";import{randomUUID as tR}from"node:crypto";var Ly={};If(Ly,{createComment:()=>bq,findIssueUrl:()=>mq,issueStates:()=>vq,mdToAdf:()=>L$,moveIssue:()=>Sq,parseIssueRef:()=>fq,resolveIssue:()=>hq,uploadImage:()=>kq,upsertCard:()=>wq,whoami:()=>jy});var Va=e=>String(e.baseUrl||"").replace(/\/$/,""),j$=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function Es(e,t,n,r){let o=await fetch(`${Va(e)}${n}`,{method:t,headers:{authorization:j$(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 dq=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,pq=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function fq(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(dq);return r?r[1].toUpperCase():null}let n=t.match(pq);return n?n[1]:null}function mq(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 hq(e,t){let n;try{n=await Es(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:`${Va(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function wq(e,t,{url:n,title:r,subtitle:o}){await Es(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 gq=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function yq(e){let t=[],n=0;for(let r of e.matchAll(gq)){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 L$(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
750
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...yq(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 bq(e,t,n){let r=await Es(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/comment`,{body:L$(n)});return r?.id?`${Va(e)}/browse/${t.key}?focusedCommentId=${r.id}`:null}async function jy(e){let t=await Es(e,"GET","/rest/api/3/myself"),n=null;try{n=new URL(Va(e)).host}catch{n=e.baseUrl??null}return{user:t?.displayName??t?.emailAddress??null,org:n}}async function kq(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(`${Va(e)}/rest/api/3/issue/${encodeURIComponent(t)}/attachments`,{method:"POST",headers:{authorization:j$(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??`${Va(e)}/rest/api/3/attachment/content/${c.id}`}var xq={new:"unstarted",indeterminate:"started",done:"completed"},B$=e=>xq[e?.key]??"started";async function vq(e,t){let n=encodeURIComponent(t.key),[r,o]=await Promise.all([Es(e,"GET",`/rest/api/3/issue/${n}?fields=status`),Es(e,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:B$(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:B$(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function Sq(e,t,n){await Es(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/transitions`,{transition:{id:String(n)}})}var Wy={};If(Wy,{createComment:()=>Oq,createIssue:()=>Hy,createProject:()=>Uy,findIssueUrl:()=>_q,issueStates:()=>Cq,moveIssue:()=>Nq,parseIssueRef:()=>Iq,resolveIssue:()=>$q,teams:()=>za,uploadImage:()=>Mq,upsertCard:()=>Rq,whoami:()=>Fy});var Aq=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function Xn(e,t,n={}){let r=await fetch(Aq(),{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 Tq=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,Eq=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function Iq(e){let t=String(e||"").trim(),n=t.match(Tq);if(n)return n[1].toUpperCase();let r=t.match(Eq);return r?r[1]:null}function _q(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 $q(e,t){let r=(await Xn(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 Rq(e,t,{url:n,title:r,subtitle:o}){let s=await Xn(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 Oq(e,t,n){let r=await Xn(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 Fy(e){let t=await Xn(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 Mq(e,{filename:t,contentType:n,buffer:r}){let o=await Xn(e,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
751
751
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
752
752
|
success
|
|
753
753
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
754
754
|
}
|
|
755
|
-
}`,{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
|
|
756
|
-
`)):Wp.rmSync(r,{force:!0})}catch{}}function Wy(){return F$().viewBase}function qy(e=process.env.BOSTRAT_PUBLIC_HOST){return U$(e,"https")}function W$(){let e=Wy();if(e)return{proto:"https",host:e,source:"learned"};let t=qy();return t?{...t,source:"env"}:null}function qp(e,t){return e?`${e.proto}://${t}.${e.host}/`:null}function Cq(e=process.env.BOSTRAT_PLANE_URL){let t=String(e||"").trim();if(!t)return null;let n=/^(wss?|https?):\/\/([^/?#]+)/i.exec(t);return n?`${/^(wss|https)$/i.test(n[1])?"https":"http"}://${n[2]}`:null}function q$(){let e=String(process.env.BOSTRAT_PUBLIC_URL||"").trim().replace(/\/+$/,"");if(e)return{url:e,source:"env"};let t=Cq();return t?{url:t,source:"plane"}:null}var G$="this node has no public app URL for Track links: link it to a plane (bostrat-node init) or set BOSTRAT_PUBLIC_URL in node.env";var Q$={linear:{api:Uy,config:As},jira:{api:By,config:Up}};async function Xy(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(Q$)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Is(e){let t=Q$[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function Zy(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 eR=()=>process.env.BOSTRAT_STATE_DIR||X$.join(process.env.HOME||Nq.tmpdir(),".bostrat"),zy=()=>X$.join(eR(),"tickets.json"),dl=e=>{let t=q$();if(!t)throw new Error(G$);return`${t.url}/track/${e}`},Pq=e=>`http://bostrat.globopi/track/${e}`,K$=e=>{let t=0;for(let[n,r]of Object.entries(e))r&&typeof r=="object"&&!r.cardUrl&&(r.cardUrl=Pq(n),t++);return t},Zn=null,Gy=null,Ky=Promise.resolve();async function pl(){return Zn||(Gy??=ll.readFile(zy(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{Zn=e;let n=K$(Zn)>0;if(!t&&qe()){let{records:r,source:o}=await dt("tickets").catch(()=>({records:[],source:"error"}));if(o==="plane"){for(let s of r){if(s.deletedAt||!s.data?.id)continue;let{id:a,updatedAt:i,...c}=s.data;Zn[a]=c}K$(Zn),n=n||Object.keys(Zn).length>0}}return n&&await Jy().catch(()=>{}),Zn}).finally(()=>Gy=null),Gy)}async function Jy(){let e=JSON.stringify({tickets:Zn},null,2),t=`${zy()}.${process.pid}.tmp`;await ll.mkdir(eR(),{recursive:!0}),await ll.writeFile(t,e),await ll.rename(t,zy())}async function ko(e=null){if(Ky=Ky.then(Jy,Jy),await Ky.catch(()=>{}),!e)return;let t=Zn?.[e];t?At("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):cn("tickets",e).catch(()=>{})}async function Qn(e){return(await pl())[e]??null}async function tR(){return{...await pl()}}async function nR(){return(await Xy()).length>0}async function Ja(e,t,{source:n="api"}={}){let r=await Xy();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(t);if(d)try{s=await u.api.resolveIssue(u.cfg,d),o=u;break}catch(p){a=p}}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(),cardUrl:dl(e.id),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 pl())[e.id]=c,await ko(e.id),await N({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 ul(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 cl(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await sR(e.id,c,"linked"),c}async function Qy(e){let t=await pl();return t[e]?(delete t[e],await ko(e),!0):!1}var Yy=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function rR(e,t){if(!e.track?.cwd)return null;let n=Jr(await G({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function Dq(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Yy(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${Yy(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function Bq(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(Yy(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function ul(e,t){let n=await Is(t);if(!n)return;let r=await rR(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:t.cardUrl??dl(e),title:`Bostrat Track: ${t.track.name}`,subtitle:Bq(t,r)})}function jq(e){return qp(W$(),e)}var Gp=e=>jq(e)??"(no public URL on this node \u2014 link it to a plane)",Lq={linked:"linked",opened:"prOpened",merged:"prMerged"};function Fq(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 oR(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await ko(e),await N({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 sR(e,t,n){let r=await Is(t);if(!r)return;let o=r.cfg.ticketFlow?.[Lq[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=Zy(s,a),c=Fq(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 oR(e,t,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await N({id:e,cwd:t.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:Z$(),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 aR(e){let t=await Qn(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Is(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=Zy(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await ko(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 eb(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await Qn(e);if(!r)throw new Error("no ticket linked to this Track");let o=await Is(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Zy(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 ko(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await oR(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function iR(e,t){let n=await Qn(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Is(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 N({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 cR(e,t,n){if(!t)throw new Error("offerId required");return uo(`tkt:${e.id}:${t}`,()=>Uq(e,t,n))}async function Uq(e,t,n){let r=await G({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 eb(e.id,o.payload.to?.id,{mode:"offer"})),await N({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 V$=50;async function lR(){let e=await As();if(!e)throw new Error("no ticketing integration configured");return Va(e)}function Hq(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>V$)throw new Error(`too many issues (${n.length}); cap is ${V$} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function Wq(e,t,n,r){let o=r??await Va(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 Qn(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 uR(e,t){let n=await As();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=Hq(t),o=await Va(n),s=await Wq(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=Z$();return await N({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 dR(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await G({id:e.id,cwd:e.cwd}),s=o.find(w=>w.type==="ticket.create.offered"&&w.payload?.proposalId===t);if(!s)throw new Error("unknown proposal");if(o.some(w=>w.type==="ticket.create.answered"&&w.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await N({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 As();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let w=(await Va(a)).find(m=>m.id===r);if(!w)throw new Error("unknown team");c={id:w.id,key:w.key,name:w.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let w=await Ly(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=w.id,l.push({kind:"epic",title:w.name,url:w.url})}for(let w of i.issues||[])try{let m=await Fy(a,{teamId:c.id,title:w.title,description:w.description,projectId:d,priority:w.priority});l.push({kind:"issue",key:m.key,title:m.title,url:m.url})}catch(m){u.push({title:w.title,message:m?.message||String(m)})}await N({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 N({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 p=l.filter(w=>w.kind==="issue"),f=null;return i.linkCurrentTrack&&p.length===1&&!await Qn(e.id)&&(f=await Ja(e,p[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:f?f.key:null}}async function cl(e,t){let n=await Is(e);n&&await n.api.createComment(n.cfg,e,t)}async function qq(e,t,n){let r=await Is(e);if(!r)return null;let o=await xr(t);if(!o)return null;let s=yn(o,n);if(!s)return null;let a=await ll.readFile(nn(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 z$=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),Vy=new Map;function Gq(e,t){if(z$()<=0)return ul(e,t);if(Vy.has(e))return;let n=setTimeout(()=>{Vy.delete(e),Qn(e).then(r=>r?ul(e,r):null).catch(()=>{})},z$());n.unref?.(),Vy.set(e,n)}var J$=new Map;async function Y$(e,t,n){let r=J$.get(e)??0;Date.now()-r<600*1e3||(J$.set(e,Date.now()),await N({id:e,cwd:t},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function Kq(e,{resolveTrack:t=null}={}){let n=await Qn(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await Xy())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 Ja(s,o,{source:"chat"})}catch(a){await Y$(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 ko(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await ko(e.trackId),await ul(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 ko(e.trackId),await ul(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await rR(n,e.trackId).catch(()=>null):null;await cl(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
755
|
+
}`,{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 Cq(e,t){let n=await Xn(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 Nq(e,t,n){if(!(await Xn(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 za(e){return((await Xn(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var F$=255;async function Uy(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>F$,a=s?`${o.slice(0,F$-1).trimEnd()}\u2026`:o,i=await Xn(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 Hy(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await Xn(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}}yc();import qp from"node:fs";import qy from"node:path";import Pq from"node:os";var Dq=()=>process.env.BOSTRAT_STATE_DIR||qy.join(process.env.HOME||Pq.tmpdir(),".bostrat"),U$=()=>qy.join(Dq(),"plane-link","public-host.json"),H$="this node has no public host for Views: link it to a plane that sets PLANE_VIEW_BASE (bostrat-node init), or set BOSTRAT_PUBLIC_HOST in node.env for a plane-less node",Ja=null;function W$(){if(Ja)return Ja;try{let e=JSON.parse(qp.readFileSync(U$(),"utf8"));Ja={viewBase:typeof e?.viewBase=="string"&&e.viewBase?e.viewBase:null}}catch{Ja={viewBase:null}}return Ja}function q$(e,t){let n=String(e||"").trim();if(!n)return null;let r=/^(https?):\/\/(.+?)\/?$/i.exec(n),o=r?r[1].toLowerCase():t,s=(r?r[2]:n).replace(/\/.*$/,"").replace(/:\d+$/,"").toLowerCase();return s?{proto:o,host:s}:null}function G$(e){let t=typeof e=="string"&&e.trim()?q$(e,"https").host:null;if(W$().viewBase===t)return;Ja={viewBase:t};let r=U$();try{t?(qp.mkdirSync(qy.dirname(r),{recursive:!0}),qp.writeFileSync(r,JSON.stringify({viewBase:t,learnedAt:new Date().toISOString()})+`
|
|
756
|
+
`)):qp.rmSync(r,{force:!0})}catch{}}function Gy(){return W$().viewBase}function Ky(e=process.env.BOSTRAT_PUBLIC_HOST){return q$(e,"https")}function K$(){let e=Gy();if(e)return{proto:"https",host:e,source:"learned"};let t=Ky();return t?{...t,source:"env"}:null}function Gp(e,t){return e?`${e.proto}://${t}.${e.host}/`:null}function Bq(e=process.env.BOSTRAT_PLANE_URL){let t=String(e||"").trim();if(!t)return null;let n=/^(wss?|https?):\/\/([^/?#]+)/i.exec(t);return n?`${/^(wss|https)$/i.test(n[1])?"https":"http"}://${n[2]}`:null}function V$(){let e=String(process.env.BOSTRAT_PUBLIC_URL||"").trim().replace(/\/+$/,"");if(e)return{url:e,source:"env"};let t=Bq();return t?{url:t,source:"plane"}:null}var z$="this node has no public app URL for Track links: link it to a plane (bostrat-node init) or set BOSTRAT_PUBLIC_URL in node.env";var nR={linear:{api:Wy,config:As},jira:{api:Ly,config:Hp}};async function Qy(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(nR)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Is(e){let t=nR[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function eb(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 rR=()=>process.env.BOSTRAT_STATE_DIR||eR.join(process.env.HOME||jq.tmpdir(),".bostrat"),Yy=()=>eR.join(rR(),"tickets.json"),dl=e=>{let t=V$();if(!t)throw new Error(z$);return`${t.url}/track/${e}`},Lq=e=>`http://bostrat.globopi/track/${e}`,J$=e=>{let t=0;for(let[n,r]of Object.entries(e))r&&typeof r=="object"&&!r.cardUrl&&(r.cardUrl=Lq(n),t++);return t},Zn=null,Vy=null,zy=Promise.resolve();async function pl(){return Zn||(Vy??=ll.readFile(Yy(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{Zn=e;let n=J$(Zn)>0;if(!t&&qe()){let{records:r,source:o}=await dt("tickets").catch(()=>({records:[],source:"error"}));if(o==="plane"){for(let s of r){if(s.deletedAt||!s.data?.id)continue;let{id:a,updatedAt:i,...c}=s.data;Zn[a]=c}J$(Zn),n=n||Object.keys(Zn).length>0}}return n&&await Xy().catch(()=>{}),Zn}).finally(()=>Vy=null),Vy)}async function Xy(){let e=JSON.stringify({tickets:Zn},null,2),t=`${Yy()}.${process.pid}.tmp`;await ll.mkdir(rR(),{recursive:!0}),await ll.writeFile(t,e),await ll.rename(t,Yy())}async function ko(e=null){if(zy=zy.then(Xy,Xy),await zy.catch(()=>{}),!e)return;let t=Zn?.[e];t?At("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):cn("tickets",e).catch(()=>{})}async function Qn(e){return(await pl())[e]??null}async function oR(){return{...await pl()}}async function sR(){return(await Qy()).length>0}async function Ya(e,t,{source:n="api"}={}){let r=await Qy();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(t);if(d)try{s=await u.api.resolveIssue(u.cfg,d),o=u;break}catch(p){a=p}}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(),cardUrl:dl(e.id),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 pl())[e.id]=c,await ko(e.id),await N({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 ul(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 cl(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await cR(e.id,c,"linked"),c}async function tb(e){let t=await pl();return t[e]?(delete t[e],await ko(e),!0):!1}var Zy=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function aR(e,t){if(!e.track?.cwd)return null;let n=Jr(await G({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function Fq(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Zy(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${Zy(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function Uq(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(Zy(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function ul(e,t){let n=await Is(t);if(!n)return;let r=await aR(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:t.cardUrl??dl(e),title:`Bostrat Track: ${t.track.name}`,subtitle:Uq(t,r)})}function Hq(e){return Gp(K$(),e)}var Kp=e=>Hq(e)??"(no public URL on this node \u2014 link it to a plane)",Wq={linked:"linked",opened:"prOpened",merged:"prMerged"};function qq(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 iR(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await ko(e),await N({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 cR(e,t,n){let r=await Is(t);if(!r)return;let o=r.cfg.ticketFlow?.[Wq[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=eb(s,a),c=qq(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 iR(e,t,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await N({id:e,cwd:t.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:tR(),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 lR(e){let t=await Qn(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Is(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=eb(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await ko(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 nb(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await Qn(e);if(!r)throw new Error("no ticket linked to this Track");let o=await Is(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=eb(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 ko(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await iR(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function uR(e,t){let n=await Qn(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Is(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 N({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 dR(e,t,n){if(!t)throw new Error("offerId required");return uo(`tkt:${e.id}:${t}`,()=>Gq(e,t,n))}async function Gq(e,t,n){let r=await G({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 nb(e.id,o.payload.to?.id,{mode:"offer"})),await N({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 Y$=50;async function pR(){let e=await As();if(!e)throw new Error("no ticketing integration configured");return za(e)}function Kq(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>Y$)throw new Error(`too many issues (${n.length}); cap is ${Y$} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function Vq(e,t,n,r){let o=r??await za(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 Qn(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 fR(e,t){let n=await As();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=Kq(t),o=await za(n),s=await Vq(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=tR();return await N({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 mR(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await G({id:e.id,cwd:e.cwd}),s=o.find(w=>w.type==="ticket.create.offered"&&w.payload?.proposalId===t);if(!s)throw new Error("unknown proposal");if(o.some(w=>w.type==="ticket.create.answered"&&w.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await N({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 As();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let w=(await za(a)).find(m=>m.id===r);if(!w)throw new Error("unknown team");c={id:w.id,key:w.key,name:w.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let w=await Uy(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=w.id,l.push({kind:"epic",title:w.name,url:w.url})}for(let w of i.issues||[])try{let m=await Hy(a,{teamId:c.id,title:w.title,description:w.description,projectId:d,priority:w.priority});l.push({kind:"issue",key:m.key,title:m.title,url:m.url})}catch(m){u.push({title:w.title,message:m?.message||String(m)})}await N({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 N({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 p=l.filter(w=>w.kind==="issue"),f=null;return i.linkCurrentTrack&&p.length===1&&!await Qn(e.id)&&(f=await Ya(e,p[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:f?f.key:null}}async function cl(e,t){let n=await Is(e);n&&await n.api.createComment(n.cfg,e,t)}async function zq(e,t,n){let r=await Is(e);if(!r)return null;let o=await xr(t);if(!o)return null;let s=yn(o,n);if(!s)return null;let a=await ll.readFile(nn(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 X$=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),Jy=new Map;function Jq(e,t){if(X$()<=0)return ul(e,t);if(Jy.has(e))return;let n=setTimeout(()=>{Jy.delete(e),Qn(e).then(r=>r?ul(e,r):null).catch(()=>{})},X$());n.unref?.(),Jy.set(e,n)}var Z$=new Map;async function Q$(e,t,n){let r=Z$.get(e)??0;Date.now()-r<600*1e3||(Z$.set(e,Date.now()),await N({id:e,cwd:t},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function Yq(e,{resolveTrack:t=null}={}){let n=await Qn(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await Qy())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 Ya(s,o,{source:"chat"})}catch(a){await Q$(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 ko(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await ko(e.trackId),await ul(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 ko(e.trackId),await ul(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await aR(n,e.trackId).catch(()=>null):null;await cl(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
757
757
|
|
|
758
|
-
Agent cost for this Track: ${
|
|
758
|
+
Agent cost for this Track: ${Fq(o)}.`:"")),await cR(e.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await zq(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await cl(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
759
759
|
|
|
760
|
-
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await cl(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 cl(n,`**Bostrat** \u2014 live preview **${r.name}**: ${
|
|
761
|
-
_(runs on the Track's branch; a Bostrat-fronted preview, not a public deployment)_`);break}case"cost.recorded":await
|
|
762
|
-
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${dl(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function
|
|
760
|
+
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await cl(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 cl(n,`**Bostrat** \u2014 live preview **${r.name}**: ${Kp(r.slug)}
|
|
761
|
+
_(runs on the Track's branch; a Bostrat-fronted preview, not a public deployment)_`);break}case"cost.recorded":await Jq(e.trackId,n);break;default:break}}catch(o){let s=t?await t(e.trackId).catch(()=>null):null;await Q$(e.trackId,s?.cwd??null,o)}}function hR({resolveTrack:e}){Xe.on("event",t=>{Yq(t,{resolveTrack:e}).catch(()=>{})})}async function wR(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await dt("tickets");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await pl(),r=0;for(let[o,s]of Object.entries(n))await At("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}ts();Ra();import Xq from"ws";var gR=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function Vp(e,t,n={}){let r=await fetch(`${gR()}/${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 rb=(e,t)=>Vp("chat.postMessage",e,t),_s=(e,t)=>Vp("chat.update",e,t);async function It(e,t){await fetch(e,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(t)}).catch(()=>{})}async function Zq(e,t,n={}){let r=await fetch(`${gR()}/${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 yR(e,{filename:t,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await Zq("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 Vp("files.completeUploadExternal",e,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var Qq=2200;function bR({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 Vp("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new Xq(l),o.on("message",u=>{let d;try{d=JSON.parse(u)}catch{return}if(d.type==="hello"){a=1e3,n("socket connected");return}if(d.type==="disconnect"){try{o.close()}catch{}return}if(!d.envelope_id)return;let p=m=>{try{o.send(JSON.stringify(m?{envelope_id:d.envelope_id,payload:m}:{envelope_id:d.envelope_id}))}catch{}},f=!1,w=setTimeout(()=>{f=!0,p()},Qq);Promise.resolve().then(()=>t(d)).then(m=>{clearTimeout(w),!f&&(f=!0,p(d.accepts_response_payload?m:void 0))},m=>{clearTimeout(w),f||(f=!0,p()),n(`envelope handler failed: ${m?.message||m}`)})}),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 vR=()=>process.env.BOSTRAT_STATE_DIR||Jp.join(process.env.HOME||t5.tmpdir(),".bostrat"),cb=()=>Jp.join(vR(),"slack.json"),zp=null,ob=null,sb=Promise.resolve();async function ub(){return zp||(ob??=fl.readFile(cb(),"utf8").then(e=>JSON.parse(e)?.threads??{}).catch(()=>({})).then(e=>zp=e).finally(()=>ob=null),ob)}async function kR(){let e=JSON.stringify({threads:zp},null,2),t=`${cb()}.${process.pid}.tmp`;await fl.mkdir(vR(),{recursive:!0}),await fl.writeFile(t,e),await fl.rename(t,cb())}async function An(){sb=sb.then(kR,kR),await sb.catch(()=>{})}async function db(e){return(await ub())[e]??null}async function n5(e,t){let n=await ub();for(let[r,o]of Object.entries(n))if(o.channel===e&&o.ts===t)return{trackId:r,rec:o};return null}var Ie=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");function ml(e,t){let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function r5(e,t=500){let n=String(e??"").trim().split(/\n\s*\n/)[0]??"";return ml(n.replace(/\n+/g," "),t)}function SR(e){let t=`*${Ie(e.name)}*${e.archivedAt?" \xB7 _archived_":""}`,n=`${Ie(e.repoLabel)} \xB7 \`${Ie(e.branch||"?")}\` \xB7 lead *${Ie(e.lead)}*`,r=e.kickoff?`> ${Ie(ml(e.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${t}
|
|
762
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${dl(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function lb(e,t){await _s(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:SR(t)})}async function vo(e,t,n,r=void 0){return rb(e.botToken,{channel:t.channel,thread_ts:t.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var xR=new Set;function xo(e){xR.add(e),e.catch(()=>{}).finally(()=>xR.delete(e))}var We=e=>({response_type:"ephemeral",text:e}),o5="*/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._",s5=["claude","cursor","codex"];function ab(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 ib=(e,t)=>We(`No live Track matches \u201C${Ie(t)}\u201D. Live: ${e.map(n=>`\`${Ie(n.name)}\``).join(" \xB7 ")||"none"}`);async function AR(e,t,{name:n,repo:r,responseUrl:o}){let s=e.deps;try{let a=(await s.listTracks()).filter(d=>!d.archivedAt);if(a.length>=t.maxConcurrent){await It(o,{...We(`At capacity \u2014 ${a.length} live Tracks (limit ${t.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!t.channel){await It(o,{...We("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?Jp.basename(c.base):"fresh workspace"),lead:"claude"},u=await rb(t.botToken,{channel:t.channel,text:`Track started: ${l.name}`,blocks:SR(l),unfurl_links:!1});l.ts=u.ts,(await ub())[i.id]=l,await An(),await It(o,{...We(`\u2713 *${Ie(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await It(o,{...We(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function a5(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.archiveTrack(n.id);let s=await db(n.id);s&&(s.archivedAt=Date.now(),await An(),await lb(t,s).catch(()=>{})),await It(r,{...We(`\u2713 *${Ie(n.name)}* archived.`),replace_original:!0})}catch(s){await It(r,{...We(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function i5(e,t,{track:n,agent:r,responseUrl:o}){try{await e.deps.startLeadHandoff(n.id,r),await It(o,{...We(`\u2713 Handing *${Ie(n.name)}* to ${Ie(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await It(o,{...We(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function c5(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 It(r,{...We(`\u2713 Stopped *${Ie(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 It(r,{...We(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function l5(e,t){let n=[...t.slice(0,99).map(r=>({text:{type:"plain_text",text:ml(`${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 *${Ie(e)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function u5(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?Jp.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 *${Ie(s.name)}* \u2014 ${Ie(i)}${a.branch?` \`${Ie(a.branch)}\``:""} \xB7 lead ${Ie(c)} \xB7 ${l}`}),o=t.length-n.length;return r.join(`
|
|
763
763
|
`)+(o?`
|
|
764
|
-
_${o} archived_`:"")}async function
|
|
765
|
-
\u2713 *${Ie(a)}*`}}]}).catch(()=>{})}}catch(c){await It(n.response_url,We(`Couldn't answer: ${c?.message||c}`))}}async function
|
|
766
|
-
\u2713 _answered_`}}]})}async function
|
|
767
|
-
_runs on the Track's branch; a Bostrat-fronted preview, not a public deployment_`}}]);return}case"mock.state.changed":return
|
|
768
|
-
`)){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;if(!i)continue;let c=s.cache_creation,l=c&&typeof c=="object"?{m5:c.ephemeral_5m_input_tokens||0,h1:c.ephemeral_1h_input_tokens||0}:null,u=typeof s.speed=="string"&&s.speed!=="standard"?s.speed:null;t.set(i,{messageId:i,tokens:a,cacheWriteTtl:l,speed:u,model:o.message?.model||o.model||null})}return[...t.values()]}function
|
|
769
|
-
`)}ye();Dh();import F5 from"node:fs/promises";import U5 from"node:path";Iu();Mu();Yr();var Yp=e=>e.payload?.ts??e.ts,N5=12e4,rn=e=>Math.round(e*1e4)/1e4,P5=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{},byTrack:new Map});function D5(e,{since:t}){let{track:n,events:r,brief:o}=e,s=k=>Yp(k)>=t,a=ar([zi(n?.name),zi(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let k of r)if(k.type==="subtask.delegated"&&k.boxId&&k.payload?.task){let S=ar(k.payload.task);S&&i.set(k.boxId,S)}let c=new Map,l=k=>{let S=k||"unattributed";return c.has(S)||c.set(S,{segments:[u(null)],lastTs:null,provider:null}),c.get(S)},u=(k,{shape:S="initial",continuation:$=!1,text:M=null,ts:L=null}={})=>({textCat:k,shape:S,continuation:$,text:M,ts:L,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((k,S)=>Yp(k)-Yp(S)),p=0,f=0,w=0,m={turns:0};for(let k of d){let S=l(k.boxId),$=S.segments[S.segments.length-1],M=Yp(k);switch(S.lastTs!=null&&M>S.lastTs&&s(k)&&($.activeMs+=Math.min(M-S.lastTs,N5)),S.lastTs=M,k.provider&&(S.provider=k.provider),k.type){case"turn.user":{let L=k.payload?.text;if(k.source==="hook"||!String(L||"").trim()||k.payload?.note||Xi(L))break;let B=ar(L),A=!!k.payload?.kind,I=A||_x(L),j=u(B,{shape:A?"injected":I?"follow-up":"novel-ask",continuation:!B&&I,text:_R(L),ts:M});S.segments.push(j),s(k)&&(j.turns+=1);break}case"peer.message":{let L=k.payload?.text;if(!String(L||"").trim())break;let B=ar(L),A=u(B,{shape:"peer",continuation:!B,text:_R(L),ts:M});S.segments.push(A),s(k)&&(A.turns+=1);break}case"tool.called":{if(k.source==="transcript")break;let L=k.payload?.tool;L&&($.tools[L]=($.tools[L]||0)+1),s(k)&&($.toolCalls+=1);break}case"cost.recorded":{if(!s(k))break;let{usd:L,tokens:B,model:A,unpriced:I}=Xt(k);I&&(m.turns+=1),$.usd+=L,B&&($.tokens.input+=B.input,$.tokens.output+=B.output,$.tokens.cacheRead+=B.cacheRead,$.tokens.cacheWrite+=B.cacheWrite),$.models[A]=($.models[A]||0)+L,k.provider&&($.provider=k.provider);break}case"handoff.done":s(k)&&(p+=1);break;case"subtask.merged":s(k)&&(f+=1);break;case"error":s(k)&&k.payload?.kind==="subtask-failed"&&(w+=1);break;default:break}}let g=new Map,y={usd:0,turns:0,byShape:{},samples:[]},b=!1;for(let[k,S]of c){let $=null;for(let M of S.segments){let L=null,B=null;if(M.textCat)L=M.textCat.category,B="text";else if(i.get(k))L=i.get(k).category,B="boxTask";else if(M.continuation&&$)L=$,B="inherited";else{let R=Ix(M.tools);R?(L=R.category,B="toolMix"):a&&(L=a.category,B="hint")}if(L&&($=L),!(M.usd>0||M.activeMs>0||M.turns>0||M.toolCalls>0))continue;let I=L??"uncategorized";g.has(I)||g.set(I,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{}});let j=g.get(I);j.usd+=M.usd,j.activeMs+=M.activeMs,j.turns+=M.turns,j.toolCalls+=M.toolCalls,B&&(j.provenance[B]=(j.provenance[B]||0)+M.usd);for(let[R,E]of Object.entries(M.models))j.models[R]=(j.models[R]||0)+E;let K=M.provider||S.provider;K&&(j.providers[K]=(j.providers[K]||0)+M.usd),K==="cursor"&&M.usd===0&&(b=!0),L||(y.usd+=M.usd,y.turns+=M.turns,y.byShape[M.shape]=(y.byShape[M.shape]||0)+M.usd,M.usd>0&&M.text&&y.samples.push({trackId:n?.id,track:n?.name||n?.id,shape:M.shape,usd:rn(M.usd),ts:M.ts,text:M.text}))}}return{perCategory:g,uncategorized:y,handoffs:p,merged:f,failed:w,sawCursorActivity:b,unpricedTurns:m.turns}}function db(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,l={usd:0,turns:0,byShape:{},samples:[]};for(let y of e||[]){let{track:b,events:k}=y;if(!k?.length){a+=1;continue}let{perCategory:S,uncategorized:$,handoffs:M,merged:L,failed:B,sawCursorActivity:A,unpricedTurns:I}=D5(y,{since:r});A&&(i=!0),c+=I,l.usd+=$.usd,l.turns+=$.turns;for(let[K,R]of Object.entries($.byShape))l.byShape[K]=(l.byShape[K]||0)+R;l.samples.push(...$.samples);let j={trackId:b?.id,name:b?.name||b?.id,archived:!!b?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:M,subtasksMerged:L,subtasksFailed:B};for(let[K,R]of S){j.usd+=R.usd,j.activeMs+=R.activeMs,j.turns+=R.turns,j.categories[K]={usd:rn(R.usd),activeMs:R.activeMs};for(let[P,W]of Object.entries(R.models))j.models[P]=rn((j.models[P]||0)+W);for(let[P,W]of Object.entries(R.providers))j.providers[P]=rn((j.providers[P]||0)+W);o.has(K)||o.set(K,P5());let E=o.get(K);E.usd+=R.usd,E.activeMs+=R.activeMs,E.turns+=R.turns,E.toolCalls+=R.toolCalls;for(let[P,W]of Object.entries(R.models))E.models[P]=(E.models[P]||0)+W;for(let[P,W]of Object.entries(R.providers))E.providers[P]=(E.providers[P]||0)+W;for(let[P,W]of Object.entries(R.provenance))E.provenance[P]=(E.provenance[P]||0)+W;let C=E.byTrack.get(b?.id)||{usd:0,activeMs:0};C.usd+=R.usd,C.activeMs+=R.activeMs,E.byTrack.set(b?.id,C)}(j.usd>0||j.activeMs>0||j.turns>0)&&(j.usd=rn(j.usd),j.dominantCategory=L5(j.categories),s.push(j))}let u=[...o.values()].reduce((y,b)=>y+b.usd,0),d=[...o.values()].reduce((y,b)=>y+b.activeMs,0),p=u>0?"usd":"time",f=p==="usd"?u:d,w=new Map((e||[]).map(y=>[y.track?.id,y.track?.name||y.track?.id])),m=[...o.entries()].map(([y,b])=>({category:y,sharePct:f>0?Math.round((p==="usd"?b.usd:b.activeMs)/f*100):0,shareBasis:p,usd:rn(b.usd),activeMs:b.activeMs,turns:b.turns,toolCalls:b.toolCalls,models:wl(b.models),providers:wl(b.providers),provenance:wl(b.provenance),topWorkstreams:[...b.byTrack.entries()].sort((k,S)=>S[1].usd-k[1].usd||S[1].activeMs-k[1].activeMs).slice(0,2).map(([k,S])=>({trackId:k,name:w.get(k),usd:rn(S.usd),activeMs:S.activeMs}))})).sort((y,b)=>b.usd-y.usd||b.activeMs-y.activeMs);s.sort((y,b)=>b.usd-y.usd||b.activeMs-y.activeMs);let g={};for(let y of o.values())for(let[b,k]of Object.entries(y.provenance))g[b]=(g[b]||0)+k;return{since:r,capturedAt:n,totalUsd:rn(u),activeMs:d,turns:[...o.values()].reduce((y,b)=>y+b.turns,0),bullets:m,workstreams:s,provenance:wl(g),gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c,uncategorized:{usd:rn(l.usd),turns:l.turns,sharePct:u>0?Math.round(l.usd/u*100):0,byShape:wl(l.byShape),top:l.samples.sort((y,b)=>b.usd-y.usd).slice(0,B5)}}}}var B5=25,IR=160,_R=e=>{let t=String(e||"").replace(/\s+/g," ").trim();return t.length>IR?`${t.slice(0,IR).trimEnd()}\u2026`:t},j5=10;function $R(e,{thresholdPct:t=j5}={}){let n=e?.gaps?.uncategorized||{usd:0,turns:0},r=e?.totalUsd||0,o=n.usd||0,s=r>0?o/r*100:0,a=s<=t,i=a?[]:[`${s.toFixed(1)}% of the last 7d ($${o.toFixed(2)} of $${r.toFixed(2)}) is uncategorized (threshold ${t}%) \u2014 run \`npm run cost -- --uncategorized\` and add classifier table lines for the top asks`];return{ok:a,sharePct:Math.round(s*10)/10,thresholdPct:t,usd:rn(o),totalUsd:rn(r),turns:n.turns||0,inheritedPct:r>0?Math.round((e?.provenance?.inherited||0)/r*100):0,problems:i}}var L5=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},wl=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,rn(n)]));var Rs={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function H5(e){let t=String(e||"7d");return t in Rs?{key:t,ms:Rs[t]}:{key:"7d",ms:Rs["7d"]}}function W5(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=sd(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 q5(e){if(!e?.cwd)return null;try{let n=(await F5.readFile(U5.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 pb(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}:H5(t),d=[];for(let w of e||[]){let m=await G(w),g=W5(w,m);i&&c<o&&(g=g.filter(y=>(y.ts??0)<=c)),d.push({track:w,events:g,brief:await q5(w)})}let p=db(d,{windowMs:u,now:c}),f=l==="7d"&&c===o?p:db(d,{windowMs:Rs["7d"],now:o});return{window:l,...p,categorization:$R(f)}}var gl=e=>`$${Number(e||0).toFixed(2)}`,RR=e=>{let t=Math.round((e||0)/6e4);return t>=60?`${Math.floor(t/60)}h${String(t%60).padStart(2,"0")}`:`${t}m`},$s=(e,t)=>String(e).padEnd(t);function OR(e){let t=[];t.push(`USAGE ROUNDUP \xB7 ${e.window} \xB7 ${gl(e.totalUsd)} \xB7 ${RR(e.activeMs)} active \xB7 ${e.turns} turn(s) \xB7 as of ${new Date(e.capturedAt).toISOString()}`);let n=e.provenance||{};e.totalUsd>0&&Object.keys(n).length&&t.push("labeled by: "+Object.entries(n).sort((a,i)=>i[1]-a[1]).map(([a,i])=>`${a} ${Math.round(i/e.totalUsd*100)}%`).join(" \xB7 ")),t.push(""),t.push("WHERE IT WENT");for(let a of e.bullets||[])t.push(` ${$s(`${a.shareBasis==="time"?"~":""}${a.sharePct}%`,5)} ${$s(a.category,15)} ${$s(gl(a.usd),10)} ${$s(RR(a.activeMs),6)} ${a.turns} turn(s)`+(a.topWorkstreams?.length?` \xB7 ${a.topWorkstreams.map(i=>i.name).join(", ")}`:""));let r=e.gaps||{};r.tracksWithoutEvents&&t.push(`${r.tracksWithoutEvents} track(s) have no attributed events yet`),r.cursorCostUnknown&&t.push("cursor time counted, cost unavailable"),r.unpricedTurns&&t.push(`WARNING: ${r.unpricedTurns} turn(s) unpriced \u2014 total under-reports`);let o=e.categorization;if(o){t.push(""),t.push((o.ok?"categorization ok: ":"CATEGORIZATION ALARM: ")+`${o.sharePct}% of the last 7d uncategorized (threshold ${o.thresholdPct}%) \xB7 ${o.inheritedPct}% inferred from the previous ask`);for(let a of o.problems||[])t.push(`WARNING: ${a}`)}let s=r.uncategorized;if(s){t.push(""),t.push(`UNCATEGORIZED RESIDUE \xB7 ${gl(s.usd)} (${s.sharePct}%) \xB7 ${s.turns} turn(s) \xB7 by shape: `+(Object.entries(s.byShape||{}).filter(([,a])=>a>0).sort((a,i)=>i[1]-a[1]).map(([a,i])=>`${a} ${gl(i)}`).join(" \xB7 ")||"none")),s.top?.length||t.push(" (nothing unmatched carried cost in this window)");for(let a of s.top||[])t.push(` ${$s(gl(a.usd),8)} ${$s(a.shape,10)} ${$s(String(a.track||"").slice(0,26),26)} | ${a.text}`);s.top?.length&&(t.push(""),t.push("novel-ask rows are vocabulary the classifier lacks: add a rule in agent/src/task-classifier.js (and a line in test/uncategorized-corpus.test.mjs). follow-up rows had no prior ask on their box."))}return t.join(`
|
|
770
|
-
`)}ir();var Ao=36e5,
|
|
771
|
-
`).slice(-3).join(" | ").slice(0,300);d!==
|
|
772
|
-
`).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
|
|
773
|
-
`).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
|
|
774
|
-
`),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 xl(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=xl(r);[c.primary,c.secondary].forEach((f,w)=>{let m=
|
|
775
|
-
`,{mode:384}),
|
|
764
|
+
_${o} archived_`:"")}async function d5(e,t){let n=await Ts();if(!n)return We("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 We("Usage: `/bostrat start <name> [repo]`");let i=await r.listRepos(),c=a[a.length-1]?.toLowerCase(),l=a.length>1?i.find(d=>d.name.toLowerCase()===c):null,u=(l?a.slice(0,-1):a).join(" ");return l?(xo(AR(e,n,{name:u,repo:l.path,responseUrl:t.response_url})),We(`Starting *${Ie(u)}* on ${Ie(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:l5(u,i)}}case"archive":{if(!a.length)return We("Usage: `/bostrat archive <track>`");let{match:i,live:c}=ab(await r.listTracks(),a.join(" "));return i?(xo(a5(e,n,{track:i,responseUrl:t.response_url})),We(`Archiving *${Ie(i.name)}*\u2026`)):ib(c,a.join(" "))}case"handoff":{let i=r.knownAgents??s5,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return We(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:d}=ab(await r.listTracks(),l);return u?(xo(i5(e,n,{track:u,agent:c,responseUrl:t.response_url})),We(`Handing *${Ie(u.name)}* to ${Ie(c)}\u2026`)):ib(d,l)}case"stop":{if(!a.length)return We("Usage: `/bostrat stop <track>`");let{match:i,live:c}=ab(await r.listTracks(),a.join(" "));return i?(xo(c5(e,n,{track:i,responseUrl:t.response_url})),We(`Stopping *${Ie(i.name)}*\u2026`)):ib(c,a.join(" "))}case"list":return We(await u5(r));default:return We(o5)}}async function p5(e,t,n,r){let{trackId:o,promptId:s,label:a}=r,i=await db(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 An(),await _s(t.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Ie(l||"question")}~
|
|
765
|
+
\u2713 *${Ie(a)}*`}}]}).catch(()=>{})}}catch(c){await It(n.response_url,We(`Couldn't answer: ${c?.message||c}`))}}async function f5(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(p=>p.type==="pr.offered"&&p.payload?.offerId===s);if(!u)throw new Error("unknown offer");if(l.some(p=>p.type==="pr.offer.answered"&&p.payload?.offerId===s))throw new Error("offer already answered");let d=Array.isArray(r.actions)?ro.filter(p=>r.actions.includes(p)):a==="pr"?["commit","push","pr"]:a==="push"?["commit","push"]:null;if(d?.includes("pr")&&i.divertShip&&await i.divertShip(c,u,{actions:d}))return;d&&await i.sayToLead(c.id,wa(d,{branch:u.payload.branch,base:u.payload.base}),{kind:d.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:d?a:"dismiss",actions:d}})}catch(c){await It(n.response_url,We(`Couldn't act on the offer: ${c?.message||c}`))}}async function m5(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 It(n.response_url,We(`Couldn't act on the offer: ${i?.message||i}`))}}async function h5(e,t){let n=t.actions?.[0];if(!n?.action_id)return;let r=await Ts();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 It(t.response_url,{...We(`Starting *${Ie(s)}*\u2026`),replace_original:!0}),xo(AR(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"?xo(p5(e,r,t,o)):n.action_id==="bostrat-ship"?xo(f5(e,r,t,o)):n.action_id==="bostrat-pr-exists"&&xo(m5(e,r,t,o))}async function w5(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 n5(t.channel,t.thread_ts);if(!n)return;let r=await Ts();if(r)try{await e.deps.sayToLead(n.trackId,t.text.trim())}catch(o){await vo(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function g5(e){return async t=>{if(t.type==="slash_commands")return d5(e,t.payload);if(t.type==="interactive"){await h5(e,t.payload);return}if(t.type==="events_api"){await w5(e,t.payload?.event);return}}}var y5=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 b5(e,t){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${Ie(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:ml(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 k5(e,t,n){let r=n.payload||{};if(!r.promptId||t.prompt?.promptId===r.promptId)return;t.prompt&&await _s(e.botToken,{channel:t.channel,ts:t.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Ie(t.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await vo(e,t,r.question||"The lead needs an answer",b5(n.trackId,r));t.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await An()}async function x5(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 An(),await _s(e.botToken,{channel:t.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Ie(s||"question")}~
|
|
766
|
+
\u2713 _answered_`}}]})}async function v5(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(d=>l[d]).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 vo(e,t,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${Ie(ml(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await An()}async function S5(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await An();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 _s(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function A5(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 vo(e,t,`PR ${o} already open \u2014 push or new PR?`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${o}${r.branch?` (${Ie(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 An()}async function T5(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await An();let s=r.choice==="dismiss"?"dismissed":r.choice==="new"?"\u2713 opening a new PR":"\u2713 pushing to the open PR";await _s(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function E5(e,t,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??I5)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await yR(e.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:t.channel,threadTs:t.ts,initialComment:`\u{1F3A8} mock *${Ie(o.name)}*${a}${o.group?` (group \`${Ie(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${Ie(a.name)}*`).join(", ");await vo(e,t,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${Ie(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function I5(e,t){let n=await xr(e);if(!n)return null;let r=yn(n,t);if(!r)return null;let o=await fl.readFile(nn(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function _5(e,t){if(!y5.has(t.type))return;let n=await db(t.trackId);if(!n)return;let r=await Ts();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 An(),await lb(r,n);return;case"turn.agent":{if(!o.text)return;let s=r5(o.text);if(!s)return;await vo(r,n,s,[{type:"section",text:{type:"mrkdwn",text:Ie(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${dl(t.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return k5(r,n,t);case"prompt.answered":return x5(r,n,t);case"pr.offered":return v5(r,n,t);case"pr.offer.answered":return S5(r,n,t);case"pr.exists.offered":return A5(r,n,t);case"pr.exists.answered":return T5(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 vo(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 An(),await lb(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await vo(r,n,`Live preview ${o.name}: ${Kp(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${Ie(o.name)}*: ${Kp(o.slug)}
|
|
767
|
+
_runs on the Track's branch; a Bostrat-fronted preview, not a public deployment_`}}]);return}case"mock.state.changed":return E5(r,n,t,e.deps)}}function TR(e){let t={deps:e};Xe.on("event",s=>{_5(t,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await Ts()||(r&&clearInterval(r),r=null,n=bR({getToken:async()=>(await Ts())?.appToken,onEnvelope:g5(t),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}pn();ye();Rn();pw();ir();import $5 from"node:fs/promises";function R5(e){let t=new Map;for(let n of String(e||"").split(`
|
|
768
|
+
`)){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;if(!i)continue;let c=s.cache_creation,l=c&&typeof c=="object"?{m5:c.ephemeral_5m_input_tokens||0,h1:c.ephemeral_1h_input_tokens||0}:null,u=typeof s.speed=="string"&&s.speed!=="standard"?s.speed:null;t.set(i,{messageId:i,tokens:a,cacheWriteTtl:l,speed:u,model:o.message?.model||o.model||null})}return[...t.values()]}function O5(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 M5=new Set(["Write","Edit","NotebookEdit"]),C5=220,N5=160;function P5(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(M5.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,C5)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=Do(o.command.trim().replace(/\s+/g," ")).slice(0,N5));return r}function D5(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?dn(t.cwd,t.sessionId):t?.cwd?cm(t.cwd):null}async function ER(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 N(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=Pd(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:P5(e,o)});break;case"Stop":await B5(r,o,e,i);break;default:break}return a}async function B5(e,t,n,r){let o=[],s=D5(n,t);if(s)try{o=R5(await $5.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=O5(await G(e)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let d of c){let p=`${d.model||"unknown"}\0${d.speed||""}`,f=u.get(p)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},cacheWriteTtl:null,messageIds:[]};for(let w of Object.keys(f.tokens))f.tokens[w]+=d.tokens[w];d.cacheWriteTtl&&(f.cacheWriteTtl??={m5:0,h1:0},f.cacheWriteTtl.m5+=d.cacheWriteTtl.m5,f.cacheWriteTtl.h1+=d.cacheWriteTtl.h1),f.messageIds.push(d.messageId),f.speed=d.speed||null,u.set(p,f)}for(let[d,p]of u){let f=d.split("\0")[0];await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:f,usd:Pn(f,p.tokens,void 0,p.speed?{speed:p.speed}:void 0),tokens:p.tokens,...p.speed?{speed:p.speed}:{},cacheWriteTtl:p.cacheWriteTtl,messageId:p.messageIds[p.messageIds.length-1],messageIds:p.messageIds,apiCalls:p.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}})}Ns();em();Cd();ye();ye();Yr();ye();Yr();ir();function hl(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 IR=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),_R=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},Yp=e=>Math.round(e*1e4)/1e4;async function pb(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=IR(),d=0,p=[];for(let f of e||[]){let w=await G(f),m=Jr(w,{boxLabels:hl(f)});if(m.turns===0){f.archivedAt?d++:p.push(f.name||f.id);continue}o+=m.totalUsd,s+=m.turns,a+=m.repricedTurns||0,i+=m.asRecordedUsd||0,c+=m.unpricedTurns||0;for(let g of m.unpricedModels||[])l.add(g);_R(u,m.totalTokens);for(let[g,y]of Object.entries(m.byModel))n[g]||={usd:0,events:0,tokens:IR()},n[g].usd=Yp(n[g].usd+y.usd),n[g].events+=y.events,_R(n[g].tokens,y.tokens);for(let[g,y]of Object.entries(m.byProvider))r[g]||={usd:0,events:0},r[g].usd=Yp(r[g].usd+y.usd),r[g].events+=y.events;t.push({id:f.id,name:f.name,usd:m.totalUsd,rollup:m})}return t.sort((f,w)=>w.usd-f.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:aa(),fleet:{totalUsd:Yp(o),asRecordedUsd:Yp(i),turns:s,repricedTurns:a,unpricedTurns:c,unpricedModels:[...l],totalTokens:u,byModel:n,byProvider:r,tracksWithCost:t.length,tracksSilent:d+p.length,tracksSilentArchived:d,tracksSilentLive:p},tracks:t}}var So=e=>`$${e.toFixed(4)}`,Xa=(e,t)=>String(e).padEnd(t);function $R(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||[];if(r.push(`fleet total ${So(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 ${So(t.asRecordedUsd)} \xB7 current-rates total ${So(t.totalUsd)} (${t.totalUsd>=t.asRecordedUsd?"+":""}${So(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(", ")}`),e.pricing){let a=e.pricing;r.push(`rates verified against vendor pages: ${a.tableVerifiedAt??"never"}`+(a.upcomingChanges?.length?` \xB7 scheduled: ${a.upcomingChanges.map(i=>`${i.model} ${i.from}`).join(", ")}`:""));for(let i of a.problems||[])r.push(`\u26A0 RATE TABLE: ${i}`)}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(` ${Xa(a,26)} ${Xa(So(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(` ${Xa(a.name||a.id,30)} ${So(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${Xa(c.label,40)} ${Xa(So(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((d,p)=>Number(d[0])-Number(p[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${Xa(So(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
769
|
+
`)}ye();Dh();import q5 from"node:fs/promises";import G5 from"node:path";_u();Cu();Yr();var Xp=e=>e.payload?.ts??e.ts,j5=12e4,rn=e=>Math.round(e*1e4)/1e4,L5=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{},byTrack:new Map});function F5(e,{since:t}){let{track:n,events:r,brief:o}=e,s=k=>Xp(k)>=t,a=ar([Ji(n?.name),Ji(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let k of r)if(k.type==="subtask.delegated"&&k.boxId&&k.payload?.task){let S=ar(k.payload.task);S&&i.set(k.boxId,S)}let c=new Map,l=k=>{let S=k||"unattributed";return c.has(S)||c.set(S,{segments:[u(null)],lastTs:null,provider:null}),c.get(S)},u=(k,{shape:S="initial",continuation:$=!1,text:M=null,ts:L=null}={})=>({textCat:k,shape:S,continuation:$,text:M,ts:L,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((k,S)=>Xp(k)-Xp(S)),p=0,f=0,w=0,m={turns:0};for(let k of d){let S=l(k.boxId),$=S.segments[S.segments.length-1],M=Xp(k);switch(S.lastTs!=null&&M>S.lastTs&&s(k)&&($.activeMs+=Math.min(M-S.lastTs,j5)),S.lastTs=M,k.provider&&(S.provider=k.provider),k.type){case"turn.user":{let L=k.payload?.text;if(k.source==="hook"||!String(L||"").trim()||k.payload?.note||Zi(L))break;let B=ar(L),A=!!k.payload?.kind,I=A||Rx(L),j=u(B,{shape:A?"injected":I?"follow-up":"novel-ask",continuation:!B&&I,text:OR(L),ts:M});S.segments.push(j),s(k)&&(j.turns+=1);break}case"peer.message":{let L=k.payload?.text;if(!String(L||"").trim())break;let B=ar(L),A=u(B,{shape:"peer",continuation:!B,text:OR(L),ts:M});S.segments.push(A),s(k)&&(A.turns+=1);break}case"tool.called":{if(k.source==="transcript")break;let L=k.payload?.tool;L&&($.tools[L]=($.tools[L]||0)+1),s(k)&&($.toolCalls+=1);break}case"cost.recorded":{if(!s(k))break;let{usd:L,tokens:B,model:A,unpriced:I}=Xt(k);I&&(m.turns+=1),$.usd+=L,B&&($.tokens.input+=B.input,$.tokens.output+=B.output,$.tokens.cacheRead+=B.cacheRead,$.tokens.cacheWrite+=B.cacheWrite),$.models[A]=($.models[A]||0)+L,k.provider&&($.provider=k.provider);break}case"handoff.done":s(k)&&(p+=1);break;case"subtask.merged":s(k)&&(f+=1);break;case"error":s(k)&&k.payload?.kind==="subtask-failed"&&(w+=1);break;default:break}}let g=new Map,y={usd:0,turns:0,byShape:{},samples:[]},b=!1;for(let[k,S]of c){let $=null;for(let M of S.segments){let L=null,B=null;if(M.textCat)L=M.textCat.category,B="text";else if(i.get(k))L=i.get(k).category,B="boxTask";else if(M.continuation&&$)L=$,B="inherited";else{let R=$x(M.tools);R?(L=R.category,B="toolMix"):a&&(L=a.category,B="hint")}if(L&&($=L),!(M.usd>0||M.activeMs>0||M.turns>0||M.toolCalls>0))continue;let I=L??"uncategorized";g.has(I)||g.set(I,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{}});let j=g.get(I);j.usd+=M.usd,j.activeMs+=M.activeMs,j.turns+=M.turns,j.toolCalls+=M.toolCalls,B&&(j.provenance[B]=(j.provenance[B]||0)+M.usd);for(let[R,T]of Object.entries(M.models))j.models[R]=(j.models[R]||0)+T;let K=M.provider||S.provider;K&&(j.providers[K]=(j.providers[K]||0)+M.usd),K==="cursor"&&M.usd===0&&(b=!0),L||(y.usd+=M.usd,y.turns+=M.turns,y.byShape[M.shape]=(y.byShape[M.shape]||0)+M.usd,M.usd>0&&M.text&&y.samples.push({trackId:n?.id,track:n?.name||n?.id,shape:M.shape,usd:rn(M.usd),ts:M.ts,text:M.text}))}}return{perCategory:g,uncategorized:y,handoffs:p,merged:f,failed:w,sawCursorActivity:b,unpricedTurns:m.turns}}function fb(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,l={usd:0,turns:0,byShape:{},samples:[]};for(let y of e||[]){let{track:b,events:k}=y;if(!k?.length){a+=1;continue}let{perCategory:S,uncategorized:$,handoffs:M,merged:L,failed:B,sawCursorActivity:A,unpricedTurns:I}=F5(y,{since:r});A&&(i=!0),c+=I,l.usd+=$.usd,l.turns+=$.turns;for(let[K,R]of Object.entries($.byShape))l.byShape[K]=(l.byShape[K]||0)+R;l.samples.push(...$.samples);let j={trackId:b?.id,name:b?.name||b?.id,archived:!!b?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:M,subtasksMerged:L,subtasksFailed:B};for(let[K,R]of S){j.usd+=R.usd,j.activeMs+=R.activeMs,j.turns+=R.turns,j.categories[K]={usd:rn(R.usd),activeMs:R.activeMs};for(let[P,W]of Object.entries(R.models))j.models[P]=rn((j.models[P]||0)+W);for(let[P,W]of Object.entries(R.providers))j.providers[P]=rn((j.providers[P]||0)+W);o.has(K)||o.set(K,L5());let T=o.get(K);T.usd+=R.usd,T.activeMs+=R.activeMs,T.turns+=R.turns,T.toolCalls+=R.toolCalls;for(let[P,W]of Object.entries(R.models))T.models[P]=(T.models[P]||0)+W;for(let[P,W]of Object.entries(R.providers))T.providers[P]=(T.providers[P]||0)+W;for(let[P,W]of Object.entries(R.provenance))T.provenance[P]=(T.provenance[P]||0)+W;let C=T.byTrack.get(b?.id)||{usd:0,activeMs:0};C.usd+=R.usd,C.activeMs+=R.activeMs,T.byTrack.set(b?.id,C)}(j.usd>0||j.activeMs>0||j.turns>0)&&(j.usd=rn(j.usd),j.dominantCategory=W5(j.categories),s.push(j))}let u=[...o.values()].reduce((y,b)=>y+b.usd,0),d=[...o.values()].reduce((y,b)=>y+b.activeMs,0),p=u>0?"usd":"time",f=p==="usd"?u:d,w=new Map((e||[]).map(y=>[y.track?.id,y.track?.name||y.track?.id])),m=[...o.entries()].map(([y,b])=>({category:y,sharePct:f>0?Math.round((p==="usd"?b.usd:b.activeMs)/f*100):0,shareBasis:p,usd:rn(b.usd),activeMs:b.activeMs,turns:b.turns,toolCalls:b.toolCalls,models:wl(b.models),providers:wl(b.providers),provenance:wl(b.provenance),topWorkstreams:[...b.byTrack.entries()].sort((k,S)=>S[1].usd-k[1].usd||S[1].activeMs-k[1].activeMs).slice(0,2).map(([k,S])=>({trackId:k,name:w.get(k),usd:rn(S.usd),activeMs:S.activeMs}))})).sort((y,b)=>b.usd-y.usd||b.activeMs-y.activeMs);s.sort((y,b)=>b.usd-y.usd||b.activeMs-y.activeMs);let g={};for(let y of o.values())for(let[b,k]of Object.entries(y.provenance))g[b]=(g[b]||0)+k;return{since:r,capturedAt:n,totalUsd:rn(u),activeMs:d,turns:[...o.values()].reduce((y,b)=>y+b.turns,0),bullets:m,workstreams:s,provenance:wl(g),gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c,uncategorized:{usd:rn(l.usd),turns:l.turns,sharePct:u>0?Math.round(l.usd/u*100):0,byShape:wl(l.byShape),top:l.samples.sort((y,b)=>b.usd-y.usd).slice(0,U5)}}}}var U5=25,RR=160,OR=e=>{let t=String(e||"").replace(/\s+/g," ").trim();return t.length>RR?`${t.slice(0,RR).trimEnd()}\u2026`:t},H5=10;function MR(e,{thresholdPct:t=H5}={}){let n=e?.gaps?.uncategorized||{usd:0,turns:0},r=e?.totalUsd||0,o=n.usd||0,s=r>0?o/r*100:0,a=s<=t,i=a?[]:[`${s.toFixed(1)}% of the last 7d ($${o.toFixed(2)} of $${r.toFixed(2)}) is uncategorized (threshold ${t}%) \u2014 run \`npm run cost -- --uncategorized\` and add classifier table lines for the top asks`];return{ok:a,sharePct:Math.round(s*10)/10,thresholdPct:t,usd:rn(o),totalUsd:rn(r),turns:n.turns||0,inheritedPct:r>0?Math.round((e?.provenance?.inherited||0)/r*100):0,problems:i}}var W5=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},wl=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,rn(n)]));var Rs={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function K5(e){let t=String(e||"7d");return t in Rs?{key:t,ms:Rs[t]}:{key:"7d",ms:Rs["7d"]}}function V5(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=ad(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 z5(e){if(!e?.cwd)return null;try{let n=(await q5.readFile(G5.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 mb(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}:K5(t),d=[];for(let w of e||[]){let m=await G(w),g=V5(w,m);i&&c<o&&(g=g.filter(y=>(y.ts??0)<=c)),d.push({track:w,events:g,brief:await z5(w)})}let p=fb(d,{windowMs:u,now:c}),f=l==="7d"&&c===o?p:fb(d,{windowMs:Rs["7d"],now:o});return{window:l,...p,categorization:MR(f)}}var gl=e=>`$${Number(e||0).toFixed(2)}`,CR=e=>{let t=Math.round((e||0)/6e4);return t>=60?`${Math.floor(t/60)}h${String(t%60).padStart(2,"0")}`:`${t}m`},$s=(e,t)=>String(e).padEnd(t);function NR(e){let t=[];t.push(`USAGE ROUNDUP \xB7 ${e.window} \xB7 ${gl(e.totalUsd)} \xB7 ${CR(e.activeMs)} active \xB7 ${e.turns} turn(s) \xB7 as of ${new Date(e.capturedAt).toISOString()}`);let n=e.provenance||{};e.totalUsd>0&&Object.keys(n).length&&t.push("labeled by: "+Object.entries(n).sort((a,i)=>i[1]-a[1]).map(([a,i])=>`${a} ${Math.round(i/e.totalUsd*100)}%`).join(" \xB7 ")),t.push(""),t.push("WHERE IT WENT");for(let a of e.bullets||[])t.push(` ${$s(`${a.shareBasis==="time"?"~":""}${a.sharePct}%`,5)} ${$s(a.category,15)} ${$s(gl(a.usd),10)} ${$s(CR(a.activeMs),6)} ${a.turns} turn(s)`+(a.topWorkstreams?.length?` \xB7 ${a.topWorkstreams.map(i=>i.name).join(", ")}`:""));let r=e.gaps||{};r.tracksWithoutEvents&&t.push(`${r.tracksWithoutEvents} track(s) have no attributed events yet`),r.cursorCostUnknown&&t.push("cursor time counted, cost unavailable"),r.unpricedTurns&&t.push(`WARNING: ${r.unpricedTurns} turn(s) unpriced \u2014 total under-reports`);let o=e.categorization;if(o){t.push(""),t.push((o.ok?"categorization ok: ":"CATEGORIZATION ALARM: ")+`${o.sharePct}% of the last 7d uncategorized (threshold ${o.thresholdPct}%) \xB7 ${o.inheritedPct}% inferred from the previous ask`);for(let a of o.problems||[])t.push(`WARNING: ${a}`)}let s=r.uncategorized;if(s){t.push(""),t.push(`UNCATEGORIZED RESIDUE \xB7 ${gl(s.usd)} (${s.sharePct}%) \xB7 ${s.turns} turn(s) \xB7 by shape: `+(Object.entries(s.byShape||{}).filter(([,a])=>a>0).sort((a,i)=>i[1]-a[1]).map(([a,i])=>`${a} ${gl(i)}`).join(" \xB7 ")||"none")),s.top?.length||t.push(" (nothing unmatched carried cost in this window)");for(let a of s.top||[])t.push(` ${$s(gl(a.usd),8)} ${$s(a.shape,10)} ${$s(String(a.track||"").slice(0,26),26)} | ${a.text}`);s.top?.length&&(t.push(""),t.push("novel-ask rows are vocabulary the classifier lacks: add a rule in agent/src/task-classifier.js (and a line in test/uncategorized-corpus.test.mjs). follow-up rows had no prior ask on their box."))}return t.join(`
|
|
770
|
+
`)}ir();var Ao=36e5,jR=24*Ao,J5=400,Y5=12,Os=e=>Math.round(e*1e4)/1e4;function hb({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 Rs?String(e||"7d"):"7d",i=Rs[a];return{key:a,since:i==null?null:r-i,until:r}}var X5=(e,t)=>e!=null&&t-e<=48*Ao?Ao:jR;function PR(e,t){if(t===Ao)return Math.floor(e/Ao)*Ao;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function Z5(e,t){if(t===Ao)return e+Ao;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var DR=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),BR=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},Q5=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Os(n)]));async function LR(e,t={}){let n=t.now??Date.now(),r=hb({...t,now:n}),o=r.since==null?jR:X5(r.since,r.until),s=A=>(r.since==null||A>=r.since)&&A<r.until,a=r.since!=null?r.until-r.since:null,i=A=>a!=null&&A>=r.since-a&&A<r.since,c={},l={},u=DR(),d=0,p=0,f=0,w=0,m=0,g=0,y=new Set,b=new Map,k=[],S=[],$=[],M=null;for(let A of e||[]){let j=(await G(A)).filter(T=>T.type==="cost.recorded");if(!j.length){A.archivedAt||$.push(A.name||A.id);continue}let K=[];for(let T of j){let C=T.ts??0;s(C)?K.push(T):i(C)&&(g+=Xt(T).usd)}if(!K.length)continue;let R=Jr(K,{boxLabels:hl(A)});d+=R.totalUsd,p+=R.turns,f+=R.repricedTurns||0,w+=R.asRecordedUsd||0,m+=R.unpricedTurns||0;for(let T of R.unpricedModels||[])y.add(T);BR(u,R.totalTokens);for(let[T,C]of Object.entries(R.byModel))c[T]||={usd:0,events:0,tokens:DR()},c[T].usd=Os(c[T].usd+C.usd),c[T].events+=C.events,BR(c[T].tokens,C.tokens);for(let[T,C]of Object.entries(R.byProvider))l[T]||={usd:0,events:0},l[T].usd=Os(l[T].usd+C.usd),l[T].events+=C.events;for(let T of K){let{usd:C}=Xt(T);if(!C)continue;let P=PR(T.ts??0,o),W=b.get(P)??{t:P,total:0,byProvider:{}};W.total+=C;let Y=T.provider||"unknown";W.byProvider[Y]=(W.byProvider[Y]||0)+C,b.set(P,W),(M==null||P<M)&&(M=P)}k.push({id:A.id,name:A.name||A.id,archived:!!A.archivedAt,usd:R.totalUsd,turns:R.turns,providers:Object.keys(R.byProvider)});for(let[T,C]of Object.entries(R.byBox))S.push({trackId:A.id,trackName:A.name||A.id,boxId:T,...C})}let L=r.since!=null?PR(r.since,o):M,B=[];for(let A=L,I=0;A!=null&&A<r.until&&I<J5;A=Z5(A,o),I++){let j=b.get(A);B.push(j?{t:A,total:Os(j.total),byProvider:Q5(j.byProvider)}:{t:A,total:0,byProvider:{}})}return k.sort((A,I)=>I.usd-A.usd),S.sort((A,I)=>I.usd-A.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:aa(),window:r.key,since:r.since,until:r.until,bucketMs:o,series:B,fleet:{totalUsd:Os(d),asRecordedUsd:Os(w),turns:p,repricedTurns:f,unpricedTurns:m,unpricedModels:[...y],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:k.length,tracksSilentLive:$},prior:a!=null?{totalUsd:Os(g),since:r.since-a,until:r.since}:null,tracks:k.slice(0,50),boxes:S.slice(0,Y5)}}Od();var e6=new Set(["cost.recorded","code.committed","code.rework","rework.cause.offered","rework.cause.answered"]),t6=20,n6=40;async function FR(e,t={}){let n=t.now??Date.now(),r=hb({...t,now:n}),o=l=>(r.since==null||l>=r.since)&&l<r.until,s=[],a={},i=0;for(let l of e||[]){let u=await G(l),d=0,p=hl(l);for(let f of u){if(!e6.has(f.type))continue;let w=f.type==="code.rework"?f.payload?.ts??f.ts:f.ts;f.type!=="rework.cause.answered"&&!o(w??0)||(f.type==="code.committed"&&d++,s.push(f.trackId?f:{...f,trackId:l.id}))}d&&i++;for(let f of l.boxes||[])a[`${l.id}/${f.name}`]={trackName:l.name||l.id,label:p[f.name]||f.name}}let c=_T(s,{boxMeta:a});return{generatedAt:new Date().toISOString(),window:r.key,since:r.since,until:r.until,reworkWindowDays:lo,decayEndDays:rw,basis:c.basis,commits:c.commits,tracksWithCommits:i,fixesAnalyzed:c.fixes.length,fixUsdTotal:c.fixUsdTotal,buckets:c.buckets,verdicts:c.verdicts,intents:c.intents,noCodeDelta:c.noCodeDelta,pendingTaps:c.pendingTaps.length,byProvider:c.byProvider,byModel:c.byModel,epochs:c.epochs.slice(0,t6),fixes:c.fixes.slice(0,n6)}}import r6 from"node:fs";import UR from"node:fs/promises";import yl from"node:os";import o6 from"node:path";Vw();ht();var s6=1024*1024,Zp=e=>Math.round(e/s6);async function a6(e){try{let t=await UR.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:Zp(r),diskTotalMb:Zp(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function i6(){try{return(await ln()).filter(t=>t.isBostrat).length}catch{return null}}async function c6(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await UR.readdir(o6.join(nl,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function HR({stateDir:e=nl,now:t=Date.now}={}){let n=yl.totalmem(),[r,o,s,a]=await Promise.all([Vd().catch(()=>null),a6(r6.existsSync(e)?e:yl.tmpdir()),i6(),c6()]),i=yl.cpus()?.length||null,c=yl.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?Zp(n-r):null,memTotalMb:Zp(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(yl.uptime())}}_w();ye();Mm();vi();ir();ye();ir();var l6=864e5;function u6(e,{tolerance:t=.005}={}){if(e.type!=="cost.recorded"||e.source==="vendor")return null;let n=e.payload?.tokens,r=Number(e.payload?.usd)||0;if(!(n?(n.input||0)+(n.output||0)+(n.cacheRead||0)+(n.cacheWrite??n.cacheCreation??0):0)||!r)return null;let s=e.payload?.model||"unknown",a=e.payload?.speed||void 0,i={input:n.input||0,output:n.output||0,cacheRead:n.cacheRead||0,cacheWrite:n.cacheWrite??n.cacheCreation??0},c=Pn(s,i,e.ts,a?{speed:a}:void 0);if(!c)return null;let l=r-c,u=Math.abs(l)>Math.max(c*t,.001);return{model:s,speed:a??null,ts:e.ts,asRecorded:r,expected:c,delta:l,divergent:u}}async function WR(e,{days:t=14,tolerance:n=.005,now:r=Date.now()}={}){let o=r-t*l6,s={},a=0,i=0,c=[];for(let u of e||[]){let d=await G(u);for(let p of d){if(!p.ts||p.ts<o)continue;let f=u6(p,{tolerance:n});if(!f)continue;a++;let w=f.speed?`${f.model} (${f.speed})`:f.model,m=s[w]||={rows:0,divergent:0,asRecordedUsd:0,expectedUsd:0};m.rows++,m.asRecordedUsd+=f.asRecorded,m.expectedUsd+=f.expected,f.divergent&&(i++,m.divergent++,c.length<10&&c.push({track:u.name||u.id,...f}))}}let l=u=>Math.round(u*1e4)/1e4;for(let u of Object.values(s))u.asRecordedUsd=l(u.asRecordedUsd),u.expectedUsd=l(u.expectedUsd);return{sinceMs:o,days:t,tolerance:n,rows:a,divergent:i,byModel:s,examples:c,ok:i===0}}Tw();Tg();nd();yt();Zt();import Za from"node:fs/promises";import d6 from"node:fs";import xt from"node:path";import p6 from"node:os";import{execFile as f6}from"node:child_process";import{promisify as m6}from"node:util";var h6=m6(f6),qR=process.env.BOSTRAT_STATE_DIR||xt.join(process.env.HOME||p6.tmpdir(),".bostrat"),Qp=!1,wb=null;async function gb(e,t){let n=await Za.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await Za.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await Za.mkdir(xt.dirname(t),{recursive:!0}),await Za.copyFile(e,t),!0)}var w6=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 ef(e,t,{skip:n=null}={}){let r=0,o=await Za.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(w6.has(s.name)||n?.has(s.name))continue;let a=xt.join(e,s.name);s.isDirectory()?r+=await ef(a,xt.join(t,s.name)):s.isFile()&&(r+=await gb(a,xt.join(t,s.name))?1:0)}return r}async function GR({stateDir:e=qR,dest:t=process.env.BOSTRAT_SYNC_DIR||`${qR}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await Za.mkdir(t,{recursive:!0});let o=0;o+=await gb(xt.join(e,"tracks.json"),xt.join(t,"tracks.json"))?1:0,o+=await gb(xt.join(e,"events.jsonl"),xt.join(t,"events.jsonl"))?1:0,o+=await ef(xt.join(e,"events"),xt.join(t,"events")),o+=await ef(xt.join(e,"memory"),xt.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await ef(xt.join(u.cwd,".bostrat"),xt.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>h6("git",["-C",t,...u]);d6.existsSync(xt.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 Bf(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||Qp))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,Qp&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),Qp=!1,wb=null}catch(u){Qp=!0;let d=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
771
|
+
`).slice(-3).join(" | ").slice(0,300);d!==wb&&(wb=d,console.warn(`[state-sync] PUSH FAILING (retrying each cycle; off-box copy is STALE): ${d}`))}return{staged:o,committed:i,pushed:l}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}fg();pn();Ho();ht();pi();import{execFile as g6}from"node:child_process";import{promisify as y6}from"node:util";import{randomUUID as b6}from"node:crypto";import k6 from"node:fs";import x6 from"node:os";import v6 from"node:path";var Eo=y6(g6),tf=e=>new Promise(t=>setTimeout(t,e)),Io=rm,yb=process.env.BOSTRAT_USAGE_PROBE_CWD||v6.join(x6.homedir(),".bostrat","usage-probe"),S6=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,A6=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,T6=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,KR=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,To=null,bl=null,kl=null;async function E6(){try{return await Eo(te,se("has-session","-t",Io)),!0}catch{return!1}}function I6(){To&&clearTimeout(To),To=setTimeout(()=>_6().catch(()=>{}),S6),To.unref&&To.unref()}async function _6(){To&&(clearTimeout(To),To=null);try{await Eo(te,se("kill-session","-t",Io))}catch{}}async function $6(){let e=Date.now()+T6;for(;;){let t=await bb().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(t)){await tf(500);return}if(Date.now()>e)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await tf(500)}}async function R6(){if(await E6())return;if(!await Hr())throw new Error("host tmux server not running \u2014 start globo-tmux.service");k6.mkdirSync(yb,{recursive:!0}),await Cr(yb);let e=b6();await Eo(te,se("new-session","-d","-s",Io,"-c",yb,"-x","200","-y","60",...Oi()));let t=`${nm} --session-id ${e}`;await Eo(te,se("send-keys","-t",Io,t,"Enter")),await $6()}async function bb(){let{stdout:e}=await Eo(te,se("capture-pane","-t",Io,"-p"));return e}async function O6(){await R6(),await Eo(te,se("send-keys","-t",Io,"-l","/usage")),await Eo(te,se("send-keys","-t",Io,"Enter"));let e=Date.now()+A6,t="";for(;await tf(400),t=await bb(),!(/Current\s+(session|week)/i.test(t)||/\d+%\s*used/i.test(t)||Date.now()>e););return await tf(900),t=await bb(),await Eo(te,se("send-keys","-t",Io,"Escape")).catch(()=>{}),I6(),t}function M6(e){let t=String(e||"").split(`
|
|
772
|
+
`).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 C6(e){let t=String(e||"").split(`
|
|
773
|
+
`).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 VR({fresh:e=!1}={}){return!e&&bl&&Date.now()-bl.at<KR?bl.data:kl||(kl=(async()=>{try{let t=await O6(),n=M6(t),r={...n,contributors:C6(t),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return bl={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 bl={at:Date.now()-(KR-3e3),data:n},n}finally{kl=null}})(),kl)}var N6=262144;function P6(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 D6=new Set([300]),B6=new Set([10080]);function j6(e){let t=Number(e);return!Number.isFinite(t)||t<=0?null:D6.has(t)?"session":B6.has(t)?"week":"other"}function L6(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 F6(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?P6((o-n)/1e3):null,resetsAt:o}}var xl=e=>({session:null,week:null,other:null,planType:null,credits:null,limitReached:null,sawTokenCount:e});function U6(e,t=Date.now()){let n=String(e||"").split(`
|
|
774
|
+
`),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 xl(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=xl(r);[c.primary,c.secondary].forEach((f,w)=>{let m=F6(f,l,t);if(!m)return;let g=j6(f.window_minutes);g===null&&(g=w===0?"session":"week"),g==="session"&&!u.session?u.session=m:g==="week"&&!u.week?u.week=m:(u.other??=[]).push({label:L6(f.window_minutes),...m})}),u.planType=typeof c.plan_type=="string"?c.plan_type:null;let p=c.credits;return p&&(p.has_credits||p.unlimited)&&(u.credits={balance:String(p.balance??""),unlimited:!!p.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{...xl(r),session:l(c.primary_used_percent),week:l(c.secondary_used_percent)}}return xl(r)}return xl(r)}async function kb(e){let t={provider:"codex",supported:!0,contributors:null};try{let n=await Fo(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 Nt(n.file,N6),o=U6(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 zR(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 nf({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 kb(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await kb(null))}return n.has("cursor")&&r.push(await nf({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function nf(e,{fresh:t=!1}={}){let n=e?.agent||"claude";return n==="codex"?kb(e):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await VR({fresh:t})}}Pr();yt();yt();function H6(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}var W6=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),rf=(e,t)=>Object.assign(new Error(e),{status:t});async function JR(e,t){if(!H6(t))throw rf("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${Ne(t)}${e}`,{headers:W6(t)})}catch(o){throw rf(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw rf(r?.error||`plane replied ${n.status}`,n.status);return r}async function YR(e,{max:t=10,env:n=process.env}={}){let r=new URLSearchParams({q:e||""});return t&&r.set("max",String(t)),(await JR(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function XR(e,{env:t=process.env}={}){if(!e)throw rf("message id required",400);return(await JR(`/v1/gmail/messages/${encodeURIComponent(e)}`,t)).message}cc();yd();function xb(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 q6(e){let[t,n]=e.split("/"),r=xb(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 ZR(e){return(e||"").split(",").map(t=>t.trim()).filter(Boolean).map(q6).filter(Boolean)}function G6(e){let t=e.headers["fly-client-ip"];return typeof t=="string"&&t.trim()?t.trim():(e.socket&&e.socket.remoteAddress||"").replace(/^::ffff:/,"")}function QR(e){let t=ZR(e),n=String(e||"").trim().toLowerCase()==="open";return{open:n,clientIp:G6,allowed(r){if(n)return!0;let o=xb((r||"").replace(/^::ffff:/,""));return o==null?!1:t.some(s=>s(o))}}}function eO(e,t){let n=ZR(e);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=xb(o);if(s==null||!n.some(i=>i(s)))return!1;let a=t(r);return!!(a&&a.view)}}}import tO from"node:crypto";import of from"node:fs";import K6 from"node:path";function nO(e=process.env,{stateDir:t=nl,log:n=console}={}){let r=String(e.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=K6.join(t,"api-token");try{let a=of.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=tO.randomBytes(32).toString("base64url");try{of.mkdirSync(t,{recursive:!0}),of.writeFileSync(o,s+`
|
|
775
|
+
`,{mode:384}),of.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function vl(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 rO(e,t){if(!e||!t)return!1;let n=Buffer.from(String(e)),r=Buffer.from(String(t));return n.length!==r.length?!1:tO.timingSafeEqual(n,r)}function oO(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 V6=[/^\/api\/github\/token\b/,/^\/api\/gmail\b/,/^\/api\/tracks\/adopt\b/,/^\/api\/tracks\/[^/]+\/transfer-out\b/,/^\/api\/node\/self-update\b/,/^\/api\/changelog\/(publish|retract)\b/,/^\/api\/tracks\/[^/]+\/loop-(update|delete)-offer\b/],z6=e=>V6.some(t=>t.test(e));function sO({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=oO(o);return rO(c,e)?a():z6(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||vl(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 aO(e,t,{enforce:n=!0}={}){return!n||rO(oO(e),t)?!0:vl(e)}Rn();Gd();Gd();import J6 from"node:http";import Y6 from"node:net";function sf(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 af(e){let t=sf(e.headers.host);return t?{slug:t,view:Uw(t)}:null}var X6=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),Z6=e=>{let t=X6(e.name);return`<!doctype html><meta charset="utf-8">
|
|
776
776
|
<meta http-equiv="refresh" content="3">
|
|
777
777
|
<title>${t} \u2014 starting</title>
|
|
778
778
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
779
779
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
780
|
-
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function
|
|
780
|
+
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function iO(e){if(!/[?&]token=/.test(e))return e;try{let t=new URL(e,"http://view");return t.searchParams.delete("token"),t.pathname+t.search}catch{return e}}function cO(e,t,n){let r=af(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(Z6(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,delete a["x-bostrat-token"];let i=J6.request({host:"127.0.0.1",port:o.port,method:e.method,path:iO(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 lO(e,t,n){let r=af(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
|
|
781
781
|
\r
|
|
782
782
|
bostrat: no live view "${s}"\r
|
|
783
|
-
`),t.destroy(),!0;let a=
|
|
783
|
+
`),t.destroy(),!0;let a=Y6.connect(o.port,"127.0.0.1",()=>{let c=[`${e.method} ${iO(e.url)} HTTP/1.1`];for(let[l,u]of Object.entries(e.headers))l!=="host"&&(l==="authorization"||l==="cookie"||l==="x-bostrat-token"||c.push(`${l}: ${Array.isArray(u)?u.join(", "):u}`));c.push(`host: 127.0.0.1:${o.port}`),a.write(c.join(`\r
|
|
784
784
|
`)+`\r
|
|
785
785
|
\r
|
|
786
|
-
`),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 kb(e){let t=typeof e=="string"?{host:e}:e||{},n=t["x-bostrat-view-base"];if(typeof n=="string"&&n)return{proto:t["x-forwarded-proto"]==="https"?"https":"http",host:n.replace(/:\d+$/,"").toLowerCase(),source:"header"};let r=Wy();if(r)return{proto:"https",host:r,source:"learned"};let o=qy();if(o)return{...o,source:"env"};let s=String(t.host||"").replace(/:\d+$/,"").toLowerCase();if(!s||!s.includes(".")||/^[\d.]+$/.test(s))return null;let a=s.split(".");return{proto:"http",host:of(s)&&Uw(a[0])?a.slice(1).join("."):s,source:"request"}}function iO(e,t){return qp(kb(t),e.slug)}Ra();tg();Xd();Wu();ye();import cO from"node:os";import{randomUUID as vb}from"node:crypto";var J6=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||cO.hostname(),nodeName:e.BOSTRAT_NODE_NAME||cO.hostname()});var lO=32768,uO=120,dO=1e3,pO=new Set(["claude","codex","cursor"]),Y6=new Set(["off","transition","nag"]),X6=[[0,59],[0,23],[1,31],[1,12],[0,6]];function Z6(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=X6[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 Tn=e=>String(e??"").trim(),Ms={name(e){let t=Tn(e);if(!t)throw new Error("a loop needs a name");if(t.length>uO)throw new Error(`name is too long (${t.length}); cap is ${uO}`);return t},schedule(e){let t=Tn(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!Z6(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=Tn(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>lO)throw new Error(`prompt is too long (${t.length} chars); cap is ${lO}`);return t},expectation(e){let t=Tn(e)||null;if(t&&t.length>dO)throw new Error(`expectation is too long (${t.length}); cap is ${dO}`);return t},notify(e){let t=Tn(e)||"off";if(!Y6.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=Tn(typeof t=="string"?t:t?.provider)||"claude";if(!pO.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...pO].join(", ")}`);let r=typeof t=="object"&&t&&Tn(t.model)||null;return r?{provider:n,model:r}:{provider:n}},enabled(e){if(typeof e=="boolean")return e;if(e==="true"||e==="false")return e==="true";throw new Error(`enabled must be true or false, got "${e}"`)}};function Q6(e,t){let n=e&&typeof e=="object"?e:{},r=Ms.name(n.name),o=Ms.schedule(n.schedule),s=Ms.prompt(n.prompt),a=Ms.expectation(n.expectation),i=Ms.notify(n.notify),c=Ms.assignment(n.assignment),l=Tn(t?.workspace?.repo)||Tn(t?.workspace?.base)||null;if(!l)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");let{nodeId:u,nodeName:d}=J6();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:d||null}}async function mO(e,t){let n=Q6(t,e),r=vb();return await N({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 hO(e,t,n,{loopId:r=null,nextFireAt:o=null}={}){if(!t)throw new Error("proposalId required");let s=await G({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 N({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 N({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,nodeId:i.nodeId??null,nextFireAt:Number.isFinite(Number(o))?Number(o):null}}),await N({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)}}var xb=["name","schedule","prompt","expectation","notify","assignment","enabled"],eG=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function wO(e){let t=Array.isArray(e.runs)?e.runs:[],n=t[t.length-1]??null;return{definitionUpdatedAt:Number(e.definitionUpdatedAt??e.updatedAt)||null,name:e.name??"",schedule:e.schedule??"",prompt:e.prompt??"",expectation:e.expectation??null,notify:e.notify??"off",assignment:e.assignment&&typeof e.assignment=="object"?e.assignment:{provider:String(e.assignment||"claude")},enabled:e.enabled!==!1,repo:e.repo??null,nodeId:e.nodeId??null,nodeName:e.nodeName??null,trackId:e.trackId??null,nextFireAt:Number.isFinite(Number(e.nextFireAt))?Number(e.nextFireAt):null,createdAt:Number(e.createdAt)||null,runCount:t.length,lastRun:n?{firedAt:Number(n.firedAt)||null,outcome:n.outcome??(n.error?"errored":null)}:null}}function gO(e,t){let n=Tn(t);if(!n)throw new Error("which loop? give an id or a name \u2014 see `bostrat-loop ls`");let r=e.find(a=>a.id===n)||e.find(a=>a.name===n);if(r)return r;let o=n.toLowerCase(),s=e.filter(a=>String(a.id).toLowerCase().startsWith(o)||String(a.name).toLowerCase().startsWith(o));if(s.length===1)return s[0];throw s.length>1?new Error(`"${n}" matches ${s.length} loops: ${s.map(a=>`${a.name} (${a.id})`).join(", ")} \u2014 use the id`):new Error(`no loop matching "${n}" \u2014 use \`bostrat-loop ls\``)}function tG(e,t){let n=e&&typeof e=="object"&&!Array.isArray(e)?e:null;if(!n)throw new Error("a patch must be a JSON object of the fields to change");let r=Object.keys(n).filter(a=>!xb.includes(a));if(r.length){let a=r.filter(i=>["repo","nodeId","cwd","userId","owner","id","trackId"].includes(i));throw a.length?new Error(`a proposal can't change ${a.join(", ")} \u2014 retargeting a loop is a Loops-tab edit (scope D5)`):new Error(`unknown field(s) ${r.join(", ")} \u2014 patchable: ${xb.join(", ")}`)}let o={};for(let a of xb){if(!(a in n))continue;let i=Ms[a](n[a]);(a==="assignment"?eG(i,t.assignment):i===(t[a]??(a==="notify"?"off":a==="enabled"?!0:null)))||(o[a]=i)}let s=Object.keys(o);if(!s.length)throw new Error("nothing would change \u2014 the patch equals the loop's current definition");return{patch:o,changed:s}}async function yO(e,t,n){let r=wO(t),{patch:o,changed:s}=tG(n,r),a=vb(),i={ts:Date.now(),proposalId:a,loopId:String(t.id),name:r.name,base:r,patch:o,changed:s,selfEdit:!!(r.trackId&&r.trackId===e.id)};return await N({id:e.id,cwd:e.cwd},{type:"loop.update.offered",actor:"system",source:"orchestrator",payload:i}),i}var fO=600;async function bO(e,t,n){let r=Tn(n);if(!r)throw new Error('a delete proposal needs a reason \u2014 bostrat-loop propose-delete <loop> --reason "<why this loop should go>"');if(r.length>fO)throw new Error(`reason is too long (${r.length}); cap is ${fO}`);let o=wO(t),s=vb(),a={ts:Date.now(),proposalId:s,loopId:String(t.id),name:o.name,base:o,reason:r,selfEdit:!!(o.trackId&&o.trackId===e.id)};return await N({id:e.id,cwd:e.cwd},{type:"loop.delete.offered",actor:"system",source:"orchestrator",payload:a}),a}var kO=async(e,t,n)=>{if(!n)throw new Error("proposalId required");let r=await G({id:e.id,cwd:e.cwd}),o=r.find(a=>a.type===t&&a.payload?.proposalId===n);if(!o)throw new Error("unknown proposal");let s=t.replace(".offered",".answered");if(r.some(a=>a.type===s&&a.payload?.proposalId===n))throw new Error("proposal already answered");return o};async function xO(e,t,n,{nextFireAt:r=null}={}){if(!["applied","dismiss","moved"].includes(n))throw new Error("action must be one of applied | dismiss | moved");let s=(await kO(e,"loop.update.offered",t)).payload,a={id:e.id,cwd:e.cwd};return n==="applied"&&await N(a,{type:"loop.updated",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:s.loopId,name:s.patch?.name??s.name,changed:s.changed??Object.keys(s.patch||{}),nextFireAt:Number.isFinite(Number(r))?Number(r):null}}),await N(a,{type:"loop.update.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:s.loopId}}),{proposalId:t,action:n,loopId:s.loopId}}async function vO(e,t,n){if(!["delete","pause","keep","moved"].includes(n))throw new Error("action must be one of delete | pause | keep | moved");let o=(await kO(e,"loop.delete.offered",t)).payload,s={id:e.id,cwd:e.cwd};return(n==="delete"||n==="pause")&&await N(s,{type:n==="delete"?"loop.deleted":"loop.paused",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:o.loopId,name:o.name,trackId:o.base?.trackId??null}}),await N(s,{type:"loop.delete.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:o.loopId}}),{proposalId:t,action:n,loopId:o.loopId}}ye();import{randomUUID as nG}from"node:crypto";var Tb=20,rG=300,Sb=120,SO=120,AO=2e3,af=new Set(["build","ops","debt","spike"]),TO=new Set(["active","done","abandoned"]),Eb=new Set(["circuit.create","circuit.update","circuit.delete"]),oG=new Set(["person.create","slot.create","slot.update","slot.delete","reservation.create","reservation.update","reservation.delete"]),Sl=new Set([...Eb,...oG]),sG=["name","description","status","type"],aG=["person","startDate","endDate","estUsd"],iG=["person","label","startDate","endDate"],cG=/^\d{4}-\d{2}-\d{2}$/,Vt=e=>String(e??"").trim(),Ib=e=>typeof e=="string"&&e.startsWith("@"),_O=864e5;function $O(e){let t=Date.parse(`${e}T00:00:00Z`),n=new Date(t).getUTCDay();return new Date(t-(n+6)%7*_O).toISOString().slice(0,10)}function lG(e){return new Date(Date.parse(`${$O(e)}T00:00:00Z`)+6*_O).toISOString().slice(0,10)}var EO=(e,t)=>{let n=Vt(e);if(!cG.test(n))throw new Error(`${t} must be YYYY-MM-DD, got "${e??""}"`);if(!Number.isFinite(Date.parse(`${n}T00:00:00Z`)))throw new Error(`${t} "${n}" is not a real date`);return n};function uG(e,t){let n=EO(e,"startDate"),r=EO(t,"endDate");if(r<n)throw new Error(`endDate ${r} is before startDate ${n}`);let o=$O(n),s=lG(r);return{startDate:o,endDate:s,snapped:o!==n||s!==r}}var IO=e=>{if(e==null||e==="")return null;let t=Number(e);if(!Number.isFinite(t)||t<0)throw new Error(`estUsd must be a non-negative number, got "${e}"`);return t},_o=(e,t,n)=>{let r=Vt(e);if(r.length>t)throw new Error(`${n} is too long (${r.length}); cap is ${t}`);return r};function Ab(e,t){let n=Vt(t);if(!n)throw new Error("a circuit name or id is required");if(Ib(n))return{ref:n,id:null,name:n.slice(1)};let r=e.find(i=>i.id===n);if(r)return r;let o=n.toLowerCase(),s=e.filter(i=>String(i.name).toLowerCase()===o);if(s.length===1)return s[0];if(s.length>1)throw new Error(`"${n}" names ${s.length} circuits \u2014 use an id (bostrat-circuit ls)`);let a=e.filter(i=>String(i.name).toLowerCase().includes(o)).slice(0,5);throw new Error(a.length?`no circuit named "${n}" \u2014 near matches: ${a.map(i=>`${i.name} (${i.id})`).join(", ")}`:`no circuit named "${n}" \u2014 propose a circuit.create op if it should exist (bostrat-circuit ls lists yours)`)}function dG(e,t){let n=Vt(t);if(!n)throw new Error('a person is required \u2014 "me", a person id, or a row label');if(n==="me"||Ib(n))return{ref:n,id:null,label:n==="me"?"you":n.slice(1)};let r=e.find(a=>a.personId===n);if(r)return{...r,id:r.personId};let o=n.toLowerCase(),s=e.filter(a=>String(a.label).toLowerCase()===o);if(s.length===1)return{...s[0],id:s[0].personId};throw s.length>1?new Error(`"${n}" names ${s.length} roadmap rows \u2014 use a person id`):new Error(e.length?`no roadmap row for "${n}" \u2014 rows: ${e.map(a=>`${a.label} (${a.personId})`).join(", ")}, or propose a person.create op`:`no roadmap rows yet \u2014 use "me", or propose a person.create op for "${n}"`)}function pG(e,t,{allow:n=Sl}={}){let r=e&&typeof e=="object"&&!Array.isArray(e)?e:null;if(!r)throw new Error("a plan must be a JSON object with an `ops` array");let o=Array.isArray(r.ops)?r.ops:null;if(!o?.length)throw new Error("a plan needs at least one op in `ops`");if(o.length>Tb)throw new Error(`too many ops (${o.length}); cap is ${Tb} \u2014 propose one intent at a time`);let s=Array.isArray(t?.circuits)?t.circuits:[],a=Array.isArray(t?.people)?t.people:[],i=Array.isArray(t?.slots)?t.slots:[],c=Array.isArray(t?.reservations)?t.reservations:[],l={circuits:{},slots:{},reservations:{}},u={},d=new Set,p=!1,f=!1,w=k=>{k.id&&(l.circuits[k.id]={name:k.name,status:k.status,type:k.type??"build",updatedAt:k.updatedAt??null},u[k.id]=k.name)},m=k=>{k.id&&(u[k.id]=k.label)},g=(k,S)=>{let $=Vt(k);if(!$)return null;if(d.has($))throw new Error(`ref "${$}" is used by two ops \u2014 each ref names one thing`);return d.add($),u[`@${$}`]=S,$},y=o.map((k,S)=>{let $=k&&typeof k=="object"?k:{},M=Vt($.op),L=`op ${S+1}`;if(!Sl.has(M))throw new Error(`${L}: unknown op "${M}" \u2014 one of ${[...Sl].join(", ")}`);if(!n.has(M))throw new Error(`${L}: "${M}" isn't a circuit op \u2014 roadmap changes go through \`bostrat-roadmap propose\` (one card, either CLI)`);let B=R=>{throw new Error(`${L} (${M}): ${R}`)},A=R=>{let E=$.set&&typeof $.set=="object"&&!Array.isArray($.set)?$.set:null;E||B("needs a `set` object naming the fields to change");let C=Object.keys(E).filter(P=>!R.includes(P));return C.length&&B(`unknown field(s) ${C.join(", ")} \u2014 settable: ${R.join(", ")}`),Object.keys(E).length||B("`set` names no fields"),E},I=R=>{let{startDate:E,endDate:C,snapped:P}=uG(R.startDate,R.endDate);return P&&(p=!0),{startDate:E,endDate:C,snapped:P}},j=R=>{let E=dG(a,R);return Vt(R)==="me"&&(f=!0),m(E),{key:E.id??Vt(R),label:E.label}},K=(R,E,C,P,W)=>{let Y=Vt(R);Y||B(`needs a ${P} id`);let me=E.find(_e=>_e[C]===Y);return me||B(`no ${P} "${Y}" \u2014 see \`bostrat-roadmap ls\``),W[Y]={...me},me};switch(M){case"circuit.create":{let R=_o($.name,SO,"name");R||B("a circuit needs a name");let E=Vt($.type)||"build";af.has(E)||B(`bad type "${E}" (${[...af].join(" | ")})`);let C=s.find(W=>String(W.name).toLowerCase()===R.toLowerCase());C&&B(`a circuit named "${C.name}" already exists (${C.id}) \u2014 update it instead of making a twin`);let P=g($.ref,R);return{op:M,...P?{ref:P}:{},name:R,description:_o($.description,AO,"description"),type:E}}case"circuit.update":{let R=Ab(s,$.circuit);R.id||B("can't update a circuit created in the same plan \u2014 set its fields on the create op"),w(R);let E=A(sG),C={};if(E.name!==void 0){let P=_o(E.name,SO,"name");P||B("name can't be blank"),P!==R.name&&(C.name=P)}return E.description!==void 0&&(C.description=_o(E.description,AO,"description")),E.status!==void 0&&(TO.has(E.status)||B(`bad status "${E.status}" (${[...TO].join(" | ")})`),E.status!==R.status&&(C.status=E.status)),E.type!==void 0&&(af.has(E.type)||B(`bad type "${E.type}" (${[...af].join(" | ")})`),E.type!==(R.type??"build")&&(C.type=E.type)),Object.keys(C).length||B("nothing would change \u2014 the set equals the circuit's current values"),{op:M,circuit:R.id,name:R.name,set:C}}case"circuit.delete":{let R=Ab(s,$.circuit);R.id||B("can't delete a circuit created in the same plan"),w(R);let E=o.some((C,P)=>P<S&&Vt(C?.op)==="circuit.update"&&C?.set?.status&&C.set.status!=="active"&&Vt(C?.circuit)===Vt($.circuit));return R.status==="active"&&!E&&B(`\u201C${R.name}\u201D is active \u2014 an active circuit can't be deleted; propose a circuit.update to done/abandoned first (both can ride one card)`),{op:M,circuit:R.id,name:R.name,status:R.status}}case"person.create":{let R=_o($.label,Sb,"label");R||B("a person row needs a label");let E=a.find(P=>String(P.label).toLowerCase()===R.toLowerCase());E&&B(`a roadmap row labelled "${E.label}" already exists (${E.personId})`);let C=g($.ref,R);return{op:M,...C?{ref:C}:{},label:R}}case"slot.create":{let R=Ab(s,$.circuit);w(R);let E=j($.person),C=I($);return{op:M,circuit:R.id??R.ref,circuitName:R.name,person:E.key,personLabel:E.label,...C,estUsd:IO($.estUsd)}}case"slot.update":{let R=K($.slot,i,"slotId","slot",l.slots),E=A(aG),C={};if(E.person!==void 0){let W=j(E.person);C.person=W.key,C.personLabel=W.label}if(E.startDate!==void 0||E.endDate!==void 0){let W=I({startDate:E.startDate??R.startDate,endDate:E.endDate??R.endDate});W.startDate!==R.startDate&&(C.startDate=W.startDate),W.endDate!==R.endDate&&(C.endDate=W.endDate)}E.estUsd!==void 0&&(C.estUsd=IO(E.estUsd)),Object.keys(C).length||B("nothing would change");let P=s.find(W=>W.id===R.circuitId);return{op:M,slot:R.slotId,circuitName:P?.name??null,set:C}}case"slot.delete":{let R=K($.slot,i,"slotId","slot",l.slots),E=s.find(C=>C.id===R.circuitId);return{op:M,slot:R.slotId,circuitName:E?.name??null,startDate:R.startDate,endDate:R.endDate}}case"reservation.create":{let R=j($.person),E=_o($.label,Sb,"label");return E||B("a reservation needs a label (Leave, Support rotation, \u2026)"),{op:M,person:R.key,personLabel:R.label,label:E,...I($)}}case"reservation.update":{let R=K($.reservation,c,"reservationId","reservation",l.reservations),E=A(iG),C={};if(E.person!==void 0){let P=j(E.person);C.person=P.key,C.personLabel=P.label}if(E.label!==void 0){let P=_o(E.label,Sb,"label");P||B("label can't be blank"),P!==R.label&&(C.label=P)}if(E.startDate!==void 0||E.endDate!==void 0){let P=I({startDate:E.startDate??R.startDate,endDate:E.endDate??R.endDate});P.startDate!==R.startDate&&(C.startDate=P.startDate),P.endDate!==R.endDate&&(C.endDate=P.endDate)}return Object.keys(C).length||B("nothing would change"),{op:M,reservation:R.reservationId,label:R.label,set:C}}case"reservation.delete":{let R=K($.reservation,c,"reservationId","reservation",l.reservations);return{op:M,reservation:R.reservationId,label:R.label,startDate:R.startDate,endDate:R.endDate}}default:return B("unhandled op")}});y.forEach((k,S)=>{for(let $ of[k.circuit,k.person,k.set?.person])if(Ib($)&&!d.has(String($).slice(1)))throw new Error(`op ${S+1} (${k.op}): unknown ref "${$}" \u2014 no earlier op in this plan creates it`)});let b=a.some(k=>k.kind==="user"&&k.userId);return{why:_o(r.why,rG,"why"),ops:y,base:l,resolved:u,snapped:p,createsMeRow:f&&!b}}async function RO(e,t,n,r={}){let o=pG(t,n,r),s=nG(),a={ts:Date.now(),proposalId:s,...o};return await N({id:e.id,cwd:e.cwd},{type:"planning.change.offered",actor:"system",source:"orchestrator",payload:a}),a}var fG=async(e,t)=>{if(!t)throw new Error("proposalId required");let n=await G({id:e.id,cwd:e.cwd}),r=n.find(o=>o.type==="planning.change.offered"&&o.payload?.proposalId===t);if(!r)throw new Error("unknown proposal");if(n.some(o=>o.type==="planning.change.answered"&&o.payload?.proposalId===t))throw new Error("proposal already answered");return r};async function OO(e,t,n,{results:r=[]}={}){if(!["applied","dismiss","moved"].includes(n))throw new Error("action must be one of applied | dismiss | moved");let o=await fG(e,t),s={id:e.id,cwd:e.cwd},a=o.payload;return n==="applied"&&await N(s,{type:"planning.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,why:a.why??"",results:Array.isArray(r)?r.slice(0,Tb+1):[]}}),await N(s,{type:"planning.change.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,ops:a.ops?.length??0}}),{proposalId:t,action:n}}Bd();ye();Fr();ye();jn();Xd();var MO=10,CO=200,mG=120,hG=24,PO=/^\d{4}-\d{2}-\d{2}$/;function _b(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var wG=(e=Date.now())=>new Date(e).toISOString().slice(0,10),vt=(e,t="i")=>new RegExp(e,t),gG=[{re:vt("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:vt("\\bSPC\\b",""),reason:"business (SPC)"},{re:vt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:vt("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:vt("\\bReplicas\\b",""),reason:"competitor research"},{re:vt("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:vt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:vt("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],yG=[vt("\\bSPC\\b",""),vt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),vt("competitor|cloudcli|\\bkiro\\b"),vt("\\bReplicas\\b",""),vt("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),vt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),vt("(^|[^\\w&])#\\d+\\b"),vt("\\btrack/[\\w.-]+")];function bG({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of gG)if(o.test(n)||o.test(r))return s;return null}function NO(e){let t=String(e||"");for(let n of yG){let r=n.exec(t);if(r)return r[0].trim()}return null}function DO(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 kG=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function xG(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Nc(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(`
|
|
787
|
-
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,mG)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${hG}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=kG.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.`);i=`**${u[1].trim().replace(/[:.]\s*$/,"")}** \u2014 ${u[2].trim()}`;let d=NO(i);if(d)throw new Error(`bullet ${r.length+1} mentions "${d}" \u2014 the product changelog never carries business, security/tenancy or internal-ops items, PR numbers, branch names or codenames; drop the bullet or rewrite it as the user-visible change and re-publish.`);if(i.length>CO)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${CO}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=NO(n);if(o)throw new Error(`headline mentions "${o}" \u2014 the product changelog never carries business, security/tenancy or internal-ops items; rewrite it and re-publish.`)}if(!r.length)throw new Error("no `- ` bullets found \u2014 an entry is a bullet list, optionally under a `# headline`");if(r.length>MO)throw new Error(`${r.length} bullets \u2014 over the ${MO}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function vG(e,{repos:t=_b()}={}){let n=new Set(t);return(await hn()).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,headRef:r.headRef||null,mergedAt:r.resolvedAt??null,excluded:bG({title:r.title,headRef:r.headRef})}))}async function BO(e,t){let n=await vG(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function jO(e,{md:t,day:n=null}){let r=xG(t),o=n==null||n===""?wG():String(n);if(!PO.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await N({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}}async function LO(e,{day:t}){let n=String(t||"");if(!PO.test(n)||Number.isNaN(Date.parse(n)))throw new Error(`invalid day "${t}" \u2014 expected YYYY-MM-DD`);let r=await N({id:e.id,cwd:e.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:e.leadBox||null,payload:{ts:Date.now(),day:n,title:null,bullets:[],retract:!0}});return{day:n,retracted:!0,eventId:r.id}}as();jn();cs();ye();as();yt();gr();jn();ic();Md();gd();fc();Zt();import{execFile as NG}from"node:child_process";import{promisify as PG}from"node:util";var SG=new Set(["pull_request","check_suite","workflow_run","push"]),AG=/^[^/\s]+\/[^/\s]+$/,TG=500,$b=new Set,Rb=[],cf=new Map,on={hourStart:0,byEvent:{},duplicates:0,rejected:0};function Ob(e,t,n){let r=Math.floor(e/36e5)*36e5;r!==on.hourStart&&(on.hourStart=r,on.byEvent={},on.duplicates=0,on.rejected=0),t==="accepted"?on.byEvent[n]=(on.byEvent[n]||0)+1:on[t]+=1}function EG(e){if(!e||typeof e!="object"||e.forge!=="github"||typeof e.event!="string"||!SG.has(e.event)||typeof e.repo!="string"||!AG.test(e.repo))return null;let t=(Array.isArray(e.numbers)?e.numbers:[]).map(Number).filter(r=>Number.isInteger(r)&&r>0),n=Number.isInteger(e.number)&&e.number>0?e.number:t[0]??null;return{forge:"github",deliveryId:typeof e.deliveryId=="string"&&e.deliveryId?e.deliveryId.slice(0,128):null,event:e.event,action:typeof e.action=="string"?e.action.slice(0,64):null,repo:e.repo,number:n,numbers:n&&!t.includes(n)?[n,...t]:t,headSha:typeof e.headSha=="string"?e.headSha.slice(0,64):null,headRef:typeof e.headRef=="string"?e.headRef.slice(0,256):null,baseRef:typeof e.baseRef=="string"?e.baseRef.slice(0,256):null,merged:typeof e.merged=="boolean"?e.merged:null,draft:typeof e.draft=="boolean"?e.draft:null,conclusion:typeof e.conclusion=="string"?e.conclusion.slice(0,64):null,deleted:e.deleted===!0,senderLogin:typeof e.senderLogin=="string"?e.senderLogin.slice(0,128):null,ts:Number.isFinite(e.ts)?e.ts:null}}function FO(e,{now:t=Date.now(),onAccept:n=null,log:r=console}={}){let o=EG(e);if(!o)return Ob(t,"rejected"),{ok:!1,error:"malformed github event envelope"};if(o.deliveryId){if($b.has(o.deliveryId))return Ob(t,"duplicates"),{ok:!0,duplicate:!0,envelope:o};for($b.add(o.deliveryId),Rb.push(o.deliveryId);Rb.length>TG;)$b.delete(Rb.shift())}let s=o.repo.toLowerCase(),a=cf.get(s)||{lastDeliveryAt:0,count:0};a.lastDeliveryAt=t,a.count+=1,cf.set(s,a),Ob(t,"accepted",o.event),r.log?.(`[github-event] ${o.event}${o.action?` ${o.action}`:""} ${o.repo}${o.number?` #${o.number}`:o.headRef?` ${o.headRef}`:""}${o.conclusion?` (${o.conclusion})`:""}${o.deliveryId?` delivery ${o.deliveryId.slice(0,8)}`:""}`);try{n?.(o)}catch(i){r.error?.("[github-event] onAccept failed:",i?.message||i)}return{ok:!0,duplicate:!1,envelope:o}}function IG(e,t=Date.now()){let n=cf.get(String(e||"").toLowerCase());return n?{covered:!0,lastDeliveryAt:n.lastDeliveryAt,ageMs:t-n.lastDeliveryAt,count:n.count}:{covered:!1,lastDeliveryAt:null,ageMs:null,count:0}}var UO=1440*6e4;function Mb(e,t=Date.now()){let n=IG(e,t);return n.covered&&n.ageMs<UO}function HO(e=Date.now()){let t={};for(let[n,r]of cf)t[n]={lastDeliveryAt:r.lastDeliveryAt,count:r.count,covered:e-r.lastDeliveryAt<UO};return{hourStart:on.hourStart,byEvent:{...on.byEvent},duplicates:on.duplicates,rejected:on.rejected,repos:t}}ye();jn();Pr();gr();Zt();function WO(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function _G(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var $G=new Set(["merged","closed","lost"]);function RG(e,t){if(!e?.blocked)return null;let n=WO(t);if(e.blocked==="red")return t.checks.failed===0&&t.checks.total>0?{change:"ci-fixed",reason:"red"}:null;if(e.blocked==="conflict")return t.mergeable==="MERGEABLE"&&n!=="red"?{change:"reconciled",reason:"conflict"}:null;if(e.blocked==="behind"){let r=t.headSha??null;return n===null&&r&&e.sha&&r!==e.sha?{change:"reconciled",reason:"behind"}:null}return null}function OG(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function MG(e,t,n,r,o){return(await G(e).catch(()=>[])).some(a=>{if(a.type!=="pr.state.changed"||a.payload?.number!==Number(n))return!1;let i=a.payload?.change;return i===t?a.payload?.sha&&r?a.payload.sha===r:(a.payload?.ts||0)>=o:i==="pushed"&&!!r&&a.payload?.sha===r})}async function CG(e,t,n){await N({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 lf(e,t={}){let{now:n=Date.now(),slugOf:r=Oe,lookup:o=pt,note:s=NA,tracks:a=async()=>(await ke()).filter(d=>!Le(d)),asked:i=MG,emitChange:c=CG}=t,l=[],u=null;for(let d of e??[]){if(!d?.prs?.length)continue;let p=await r(d.path).catch(()=>null);if(p)for(let f of d.prs)try{if(!_G(f))continue;let w=await o(p,f.number).catch(()=>null);if(!w?.trackId||$G.has(w.state))continue;let{prev:m}=await s(p,f.number,{blocked:WO(f),sha:f.headSha??null,at:n});if(!m)continue;let g=RG(m,f);if(!g||OG(w,g.change))continue;u??=new Map((await a()).map(S=>[S.id,S]));let y=u.get(w.trackId);if(!y)continue;let b=f.headSha??null;if(await i(y,g.change,f.number,b,m.at||0))continue;let k={number:Number(f.number),url:f.url||w.url,title:f.title||w.title,branch:f.headRefName||w.headRef,repo:p,sha:b,delegated:!1,reason:g.reason};g.change==="ci-fixed"?k.passed=f.checks.passed:k.base=f.baseRefName||null,await c(y,g.change,k),l.push({change:g.change,number:f.number,reason:g.reason})}catch{}}return l}var Pb=PG(NG),ZO=3e4,DG=30*6e4,BG=ZO,jG=10*6e4,LG=10080*6e4,FG=60*6e4,qO=8,QO=1440*6e4,UG=3,eM="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Nb=60*6e4,HG=15*6e4,WG=9e4,qG=1440*6e4,Al=new Map,Tl=new Map,GG=360*6e4,KG=e=>e.reduce((t,n)=>({misses:Math.max(t.misses,n.brokerRefused?.misses||0),until:Math.max(t.until,n.brokerRefused?.until||0)}),{misses:0,until:0}),VG=/could not resolve|not found|no pull requests?/i,zG=e=>VG.test(String(e?.message||e||""));function JG(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<Nb)}var GO={fast:BG,slow:jG,decay:FG};function KO(e,t,n=Mb){return tM(e,t,{grace:0})||!n(e.repo,t)&&(e.armedAt&&t-e.armedAt<DG||JG(e,t))?"fast":t-(e.firstSeen||0)>LG?"decay":"slow"}function Il(e,t,n,r){let o=e.lanes?.get(ie(t.repo,t.number))||{};return wd({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function YG(e,t){pr("watch");try{let{stdout:n}=await Pb("gh",["pr","view",String(t),"--repo",e,"--json",eM],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await _n({slug:e})}});return JSON.parse(n||"null")}catch(n){throw ao(n)&&io(n),n}}async function XG(e){pr("watch");try{let{stdout:t}=await Pb("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",eM],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await _n({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw ao(t)&&io(t),t}}async function ZG(){let e=new Map;for(let t of await ke().catch(()=>[]))e.set(t.id,t);return e}async function QG(e,t){await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...t}}),await Od(e,t).catch(()=>{})}async function eK(e,t){let n=await ss(e);return n?ka(n,t):null}async function tK(e,t,n){let r=await ss(e);if(!r)throw new Error(`no local checkout for ${e}`);return xa(r,t,{method:n})}async function nK(e,t){let n=await ss(e);if(!n)throw new Error(`no local checkout for ${e}`);return uc(n,t)}async function rK(e,t){await N({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 Za(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 oK=10*6e4;async function sK(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>QO){await Za(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 Za(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await Za(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<oK)return;await Za(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 Za(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 Za(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await nM(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function Qa(e,t,n){await N({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 aK(e,t){try{return await Pb("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function El(e,t,n,r=()=>!0){return(await G(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var ei=(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 tM(e,t,{grace:n=WG}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=qG}async function iK(e,t,n,r){let o=r.now??Date.now();if(!tM(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||Le(s))return!1;let a=(s.boxes||[]).find(d=>d.name===s.leadBox);return await(r.midTurn??is)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await G(s).catch(()=>[])).some(d=>d.type==="pr.state.changed"&&d.payload?.number===Number(e.number))?(await(r.claim??ur)(e.repo,e.number).catch(()=>null),!1):await(r.claim??ur)(e.repo,e.number).catch(()=>null)?(await Qa(s,"opened",{...ei(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 cK(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 cd(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await ud(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<HG;await CA(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await MA(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await El(a,"pushed",e.number,d=>d.payload?.sha===s)&&await Qa(a,"pushed",{...ei(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>Nb)await ld(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 ld(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Il(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await El(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await Qa(a,"ci-fixed",{...ei(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await ld(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>Nb)await dd(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await dd(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Il(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await El(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await Qa(a,"reconciled",{...ei(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await dd(e.repo,e.number).catch(()=>{})}}}}async function lK(e,t,n,r){let o=Il(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await El(s,"closed",e.number)&&await Qa(s,"closed",{...ei(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 VO(e,t,n){Il(n,e,"lost",null);let r=t.get(e.trackId);r&&!await El(r,"lost",e.number)&&await Qa(r,"lost",ei(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function nM(e,t,n,r,o={}){let s=Il(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await G(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.followupsIfMerged(e.repo,e.number).catch(()=>{}),await r.archiveIfArmed(e.repo,e.number).catch(()=>{}),await r.mark(e.repo,e.number,"merged").catch(()=>{})}async function zO(e={}){let{now:t=Date.now(),auth:n=Gh,view:r=YG,list:o=XG,links:s=BA,trackMap:a=ZG,emitMerged:i=QG,mark:c=oo,archiveIfArmed:l=hr,notifyIfArmed:u=wr,notifyIfResolved:d=kp,followupsIfMerged:p=xs,batchThreshold:f=UG,detail:w=eK,merge:m=tK,updateBranch:g=nK,lookup:y=pt,disarm:b=ac,emitDisarmed:k=rK,localHasCommit:S=aK,claim:$=ur,midTurn:M=is,only:L=null,wantDetail:B=null,covered:A=Mb,verdicts:I=null,tokenFor:j=J=>Fs(J),noteRefusal:K=RA,clearRefusal:R=OA}=e;if(!(await n().catch(()=>null))?.authenticated||md()>0)return 0;let C=await s().catch(()=>[]),P=iT(C),W=J=>{try{return!!B?.(J)}catch{return!1}},Y=L?C.filter(J=>{try{return L(J)}catch{return!1}}):C.filter(J=>t-(Al.get(ie(J.repo,J.number))||0)>=GO[KO(J,t,A)]);if(!Y.length)return 0;let me=Date.now(),_e=new Map,Pe=new Map;for(let J of[...Y].sort(dc)){let he=L?"event":KO(J,t,A);_e.set(ie(J.repo,J.number),{lane:he,intervalMs:GO[he]??null}),Al.set(ie(J.repo,J.number),t),Pe.has(J.repo)||Pe.set(J.repo,[]),Pe.get(J.repo).push(J)}let ne=await a().catch(()=>new Map),Ve=new Map,ve=(J,he)=>{let Fe=ie(J,he);if(!Ve.has(Fe)){let O=Promise.resolve().then(()=>w(J,he));O.catch(()=>{}),Ve.set(Fe,O)}return Ve.get(Fe)},ce=I??((J,he)=>lf([{path:J,prs:[he]}],{now:t,slugOf:async()=>J,tracks:async()=>[...ne.values()].filter(Fe=>!Le(Fe))}).catch(()=>{})),re={now:t,lanes:_e,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,followupsIfMerged:p,detail:ve,merge:m,updateBranch:g,lookup:y,disarm:b,emitDisarmed:k,localHasCommit:S,claim:$,midTurn:M};for(let[J,he]of Pe){let Fe=KG(he);if(Fe.until>t)continue;let O=!1;try{if(await j(J),Fe.misses)for(let X of he)await R(X.repo,X.number).catch(()=>{})}catch(X){O=Ek(X)}if(O){let X=Fe.misses+1;if(X>=qO){console.log(`[pr-watch] ${J}: not granted ${X}\xD7 \u2014 retiring ${he.length} link(s) as lost`);for(let we of he){await VO(we,ne,re);let mt=ie(we.repo,we.number);Tl.delete(mt),Al.delete(mt)}}else for(let we of he)await K(we.repo,we.number,{misses:X,until:t+GG}).catch(()=>{});continue}let se,Re=new Set,$t=X=>X.ciFixAwait||X.reconcileAwait||W(X)?!0:!X.autoMerge||X.trackId&&P.get(X.trackId)!==ie(X.repo,X.number)?!1:t-(X.autoMerge.armedAt||0)<=QO;if(he.length>f)se=await o(J).catch(()=>null);else{se=new Map;for(let X of he){if($t(X)){let we=await ve(J,X.number).catch(()=>null);if(we?.state){se.set(Number(X.number),{number:Number(X.number),url:we.url,title:we.title,headRefName:we.headRefName,headRefOid:we.headSha??null,state:we.state,mergedAt:we.mergedAt??null,closedAt:we.closedAt??null});continue}}try{let we=await r(J,X.number);we?.number!=null&&se.set(Number(we.number),we)}catch(we){zG(we)&&Re.add(ie(X.repo,X.number))}}}if(se)for(let X of he){let we=ie(X.repo,X.number),mt=se.get(Number(X.number));if(!mt){if(Re.has(we)){let nt=(Tl.get(we)||0)+1;Tl.set(we,nt),nt>=qO&&(await VO(X,ne,re),Tl.delete(we),Al.delete(we))}continue}if(Tl.delete(we),mt.state==="MERGED"||mt.state==="CLOSED")mt.state==="MERGED"?await nM(X,mt,ne,re):await lK(X,mt,ne,re),Al.delete(we);else{await iK(X,mt,ne,re).catch(()=>{}),await cK(X,mt,ne,re).catch(()=>{});let nt=!X.trackId||P.get(X.trackId)===we;X.autoMerge&&nt&&await sK(X,ne,re).catch(()=>{});try{if(W(X)&&!Ve.has(we)&&ve(X.repo,X.number),Ve.has(we)){let an=await Ve.get(we).catch(()=>null);an?.checks&&await ce(X.repo,an)}}catch{}}}}let Ce={};for(let{lane:J}of _e.values())Ce[J]=(Ce[J]||0)+1;return console.log(`[pr-watch] probed ${Y.length} link(s) (${Object.entries(Ce).map(([J,he])=>`${J} ${he}`).join(", ")}) in ${Date.now()-me}ms`),Y.length}function JO(e,t){return!e||!t?!1:e.all?!0:String(t.repo||"").toLowerCase()!==e.repo?!1:e.numbers?.length?e.numbers.includes(Number(t.number)):e.event==="push"?!!e.branch&&t.headRef===e.branch||!!t.autoMerge:e.branch?t.headRef===e.branch:!0}var Cb=!1,uf=!1,df=[],YO=Promise.resolve();async function uK(){for(;uf||df.length;){if(uf){uf=!1;try{await zO()}catch{}continue}let e=df;df=[];let t=e.map(r=>r.spec),n=Object.assign({},...e.map(r=>r.deps||{}));try{await zO({...n,only:r=>t.some(o=>JO(o,r)),wantDetail:r=>t.some(o=>o.detail&&JO(o,r))})}catch{}}}function pf(e,t){return e===null?uf=!0:df.push({spec:e,deps:t}),Cb||(Cb=!0,YO=uK().finally(()=>{Cb=!1})),YO}function rM(e,t=void 0){let n=String(e?.repo||"").toLowerCase();if(!/^[^/\s]+\/[^/\s]+$/.test(n))return Promise.resolve();let r=(Array.isArray(e.numbers)?e.numbers:[]).map(Number).filter(o=>Number.isInteger(o)&&o>0);return pf({repo:n,numbers:r,branch:e.branch||null,event:e.event||null,detail:e.detail===!0},t)}var dK=6e4,XO=0;function oM(e=void 0){let t=e?.now??Date.now();return t-XO<dK?Promise.resolve():(XO=t,pf({all:!0},e))}function sM(){pf(null),setInterval(()=>pf(null),ZO)}as();Cd();Rc();gr();Zt();var pK=new Set(["opened","reopened","closed","synchronize","ready_for_review","converted_to_draft","edited"]);async function fK({tracks:e=ke,reconcile:t=Gd}={}){for(let n of(await e().catch(()=>[])).filter(r=>!Le(r)))await t(n,{bypassThrottle:!0}).catch(()=>{})}function aM(e,t={}){let{invalidate:n=en,probe:r=rM,poke:o=qT,reconcileOpened:s=fK}=t;try{n()}catch{}let a={probe:null,poked:!1,reconciled:!1};if(!e?.repo)return a;if(e.event==="pull_request"&&pK.has(e.action)){if(e.action==="opened"||e.action==="reopened"){a.poked=!0,a.reconciled=!0;try{o()}catch{}s()}a.probe={repo:e.repo,numbers:e.numbers,branch:e.headRef||null,event:e.event}}else(e.event==="check_suite"||e.event==="workflow_run")&&e.action==="completed"?a.probe={repo:e.repo,numbers:e.numbers,branch:e.headRef||null,event:e.event,detail:!0}:e.event==="push"&&!e.deleted&&(a.probe={repo:e.repo,branch:e.headRef||null,event:"push"});return a.probe&&r(a.probe),a}ht();ye();gr();Zt();import{randomUUID as mK}from"node:crypto";var hK=2e4,iM=3,ti=new Map,Db=new Set;async function cM(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 wK(e={}){let{sessions:t=null,tracks:n=null,append:r=N,read:o=G,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await ln(),i=n??await ke()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(Le(u)||!u.leadBox)continue;let d=(u.boxes||[]).find(f=>f.name===u.leadBox);if(!d||d.agent!=="claude"||d.state!=="live")continue;let p=c.get(d.name);if(p)if(l.add(d.name),p.alternateOn){let f=(ti.get(d.name)||0)+1;ti.set(d.name,f),f===iM&&(Db.add(d.name),await cM(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:mK(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let f=(ti.get(d.name)||0)>=iM;if(ti.delete(d.name),f||!Db.has(d.name)){Db.add(d.name);let w=await cM(u,d.name,o);w&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:d.name,provider:d.agent,payload:{ts:s,offerId:w,action:"cleared"}}).catch(()=>{})}}}for(let u of[...ti.keys()])l.has(u)||ti.delete(u)}var _l=null,Bb=!1;function lM(){return _l?()=>{}:(_l=setInterval(()=>{Bb||(Bb=!0,wK().catch(()=>{}).finally(()=>{Bb=!1}))},hK),_l.unref?.(),()=>{clearInterval(_l),_l=null})}ts();Rc();ye();as();cs();ts();gc();import{randomUUID as gK}from"node:crypto";async function yK(){let e=await Promise.resolve().then(()=>(gr(),o$));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var jb={listRepoPrs:Ln,readEvents:G,appendEvent:N,sayDeps:yK,currentBranch:co};async function bK(e,t=jb,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??co)(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 Lb(e,t,{actions:n,notify:r=!1}={},o=jb){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 bK(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=gK();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 Fb(e,{offerId:t,choice:n}={},r=jb){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');return uo(`prx:${e.id}:${t}`,()=>kK(e,{offerId:t,choice:n},r))}async function kK(e,{offerId:t,choice:n},r){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=ro.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(e.id,wa(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,gA(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}}gc();import Or from"node:fs/promises";import ni from"node:path";import wM from"node:os";import uM from"node:fs/promises";import ff from"node:path";import{execFile as xK}from"node:child_process";import{promisify as vK}from"node:util";import{fileURLToPath as SK}from"node:url";var AK=vK(xK),pM=ff.dirname(SK(import.meta.url)),TK=ff.resolve(pM,"..","..","client","dist"),EK="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",IK=new Map([...EK].map((e,t)=>[e,t]));function _K(e){let t=[],n=0,r=0;for(let o of e){let s=IK.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 $K(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=_K(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 RK(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 dM=new Map;async function OK(e){let t;try{t=await uM.stat(e)}catch{return null}let n=dM.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await uM.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:$K(r.mappings||"")};return dM.set(e,o),o}catch{return null}}var Ub=null;function MK(){return Ub||(Ub=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):AK("git",["rev-parse","--short","HEAD"],{cwd:ff.resolve(pM,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),Ub}var CK=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function NK(e,{distDir:t=TK}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(CK)){let[a,i,c,l]=s,u=await OK(ff.join(t,"assets",`${i}.map`)),d=u?RK(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(e.slice(r,s.index)),d&&u.sources[d[1]]){let p=u.sources[d[1]].replace(/^(\.\.\/)+/,"");n.push(`${p}:${d[2]+1}:${d[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 fM(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await MK();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await NK(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var PK=()=>process.env.BOSTRAT_STATE_DIR||ni.join(process.env.HOME||wM.tmpdir(),".bostrat"),$o=()=>ni.join(PK(),"telemetry"),gM=()=>ni.join($o(),".relay-offsets.json"),DK=50,BK=14,jK=3e4,mM=500,zt=(e,t)=>typeof e=="string"?e.slice(0,t):"";function LK(e,t){if(!e||typeof e!="object")return null;let n=zt(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:zt(e.anonId,64),sessionId:zt(e.sessionId,64),userId:zt(e.userId,64),release:zt(e.release,64),appVersion:zt(e.appVersion,32),route:zt(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:zt(e.error.kind,256),message:zt(e.error.message,8192),stack:zt(e.error.stack,32768),fingerprint:zt(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:zt(s?.n,64),r:zt(s?.r,128)}))),o}var yM=e=>new Date(e).toISOString().slice(0,10),hM=0;async function bM(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,DK):[]).map(s=>LK(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await fM(s);await Or.mkdir($o(),{recursive:!0});let o=ni.join($o(),`${yM(t)}.ndjson`);return await Or.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
788
|
-
`).join(""),"utf8"),t-
|
|
789
|
-
`).filter(Boolean),d=[];for(let p of u)try{d.push(JSON.parse(p))}catch{}for(let p=0;p<d.length;p+=mM){let f=d.slice(p,p+mM).map(m=>UK(m)),w=await e(`${t.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.ingestKey}`},body:JSON.stringify({events:f})}).catch(()=>null);if(!w||!w.ok)return{sent:o,disabled:!1};o+=f.length}r[s]=i+l+1,await vM(r)}return{sent:o,disabled:!1}}var Hb=null,Wb=!1;function SM(){Hb||(Hb=setInterval(async()=>{if(!Wb){Wb=!0;try{await HK()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{Wb=!1}}},jK),Hb.unref?.())}import wf from"node:fs/promises";import _M from"node:path";import WK from"node:os";var $M=()=>process.env.BOSTRAT_STATE_DIR||_M.join(process.env.HOME||WK.tmpdir(),".bostrat"),RM=()=>_M.join($M(),"dictation-glossary.json"),qK=100,GK=500,KK=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],gf=null,qb=null,Gb=Promise.resolve();function AM(){let e={},t=Date.now();for(let n of KK)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function OM(){return gf||(qb??=wf.readFile(RM(),"utf8").then(e=>JSON.parse(e)?.terms??AM()).catch(()=>AM()).then(e=>gf=e).finally(()=>qb=null),qb)}async function TM(){let e=JSON.stringify({terms:gf},null,2),t=RM(),n=`${t}.${process.pid}.tmp`;await wf.mkdir($M(),{recursive:!0}),await wf.writeFile(n,e),await wf.rename(n,t)}function VK(){return Gb=Gb.then(TM,TM),Gb.catch(()=>{})}async function Kb(){return Object.values(await OM()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var zK=e=>Math.max(1,Math.ceil(e.length/4));async function JK(){let e=[],t=0;for(let{term:n}of await Kb()){let r=zK(n);if(!(t+r>GK)&&(e.push(n),t+=r,e.length>=qK))break}return e}async function YK(e,t){let n=await OM(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var EM=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),hf=e=>e.toLowerCase();function IM(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function MM(e,t){let n=EM(e),r=EM(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 d=s-1;d>=0;d--)a[u][d]=hf(n[u])===hf(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;)hf(n[c])===hf(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(IM(r[l])&&i.add(r[l]),l++);for(;l<s;l++)IM(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await YK(u,"learned");return await VK(),[...i]}var $l=null;function XK(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 CM(){if(!$l)return null;let e=await il();if(!e||e.apiKey.slice(-6)!==$l.keyTail)return null;let{keyTail:t,...n}=$l;return n}async function NM(e,t){let n=await il();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 JK())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=XK(o.status,i);c.persistent&&($l={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 $l=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}ye();var Vb={"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 ZK(e){if(!e)return null;let t=String(e).trim();if(Vb[t])return t;let n=null;for(let r of Object.keys(Vb))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function QK(e,t={}){let n=Vb[ZK(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 eV(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 Rl=null;async function tV(){if(!Rl)return null;let e=await Hp();if(!e||e.apiKey.slice(-6)!==Rl.keyTail)return null;let{keyTail:t,...n}=Rl;return n}async function nV(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=eV(t.status,o);s.persistent&&(Rl={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}Rl=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 rV={openai:nV};async function PM(){let e=await Hp();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await tV()}:{configured:!1}}async function DM(){let e=await Hp();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 rV[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function BM(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=QK(t,s),i=await N(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}}Zt();TA(async()=>new Set((await ke()).map(e=>e.id)));var GM=$e.dirname(sV(import.meta.url)),jM=Number(process.env.PORT)||3362,yf=Ty(process.env),KM=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,ri=YR(process.env.BOSTRAT_ALLOW_IPS),Zb=XR(process.env.BOSTRAT_VIEW_FRONT_CIDRS,sf),Qb=QR(),VM=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",_=Jt();_.disable("x-powered-by");_.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),of(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});_.use((e,t,n)=>{if(vl(e)||ri.allowed(ri.clientIp(e))||Zb.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});_.use(sO);_.use(nO({token:Qb,enforce:VM}));_.use("/api/mocks",Jt.json({limit:"1mb"}));_.use("/api/mocks",Jt.raw({type:"image/png",limit:"24mb"}));_.use("/api/mocks",Jt.raw({type:["video/mp4","video/webm"],limit:"105mb"}));_.use("/api/runbooks",Jt.json({limit:"1mb"}));_.use("/api/tracks/adopt",Jt.json({limit:"1mb"}));_.use("/api/telemetry",Jt.json({limit:"256kb"}));_.use("/api/hook",Jt.json({limit:"2mb"}));_.use("/api/dictation",Jt.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));_.use("/api/tracks/:id/upload",Jt.raw({type:()=>!0,limit:"25mb"}));_.use(Jt.json({limit:"64kb"}));_.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:Ft,hostTmuxAlive:await Hr(),autoTriggerPct:KM,plane:oi?oi.status():null})});_.get("/api/sessions",async(e,t)=>{t.json({sessions:await ln()})});_.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 On(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})}});_.delete("/api/sessions/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Be(n))return t.status(400).json({error:"invalid session name"});if(!await xe(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}`})}});_.get("/api/context/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Be(n))return t.status(400).json({error:"invalid session name"});let r=await xe(n);if(!r)return t.status(404).json({error:"session not found"});let o=await Pi(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});_.post("/api/handoff/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Be(n))return t.status(400).json({error:"invalid session name"});try{let r=await _v(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)})}});_.get("/api/handoff/:name",(e,t)=>{let n=decodeURIComponent(e.params.name),r=Bu(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 LM=10;async function zM(){let e=await ke(),[t,n]=await Promise.all([S_(e),s$(e)]);return{tracks:e,views:await Fw(),queuedOf:r=>gs(r),jobsOf:r=>t.get(r)??[],prPendingOf:r=>n.get(r)??[]}}_.get("/api/idle-guard",async(e,t)=>{try{t.json(Ay(await zM()))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var sn=Ss(),dV=b$({kind:sn}),Ml=null,_t=()=>Ml&&["auto","prompt","off"].includes(Ml)?Ml:dV,Xb=null,pV=sl.has(sn),JM=async({to:e,from:t})=>!pV||Pp(await al())?!1:(Nl.arm({to:e,from:t,kind:sn}),!0);async function fV(e){let t=_t();Ml=e??null;let n=_t();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){Nl.cancel();return}Xb&&await JM(Xb)}}var YM=(()=>{try{return uV(import.meta.url)("../package.json").version||null}catch{return null}})(),Nl=x$({isBusy:async()=>Ay(await zM()).busy,onStuck:({to:e,from:t})=>Yn({current:t,latest:e,installKind:sn,policy:_t(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?Yn({current:t,latest:e,installKind:sn,policy:_t(),status:"updating",reason:"auto"}):Yn({current:t,latest:e,installKind:sn,policy:_t(),status:"failed",error:n?.reason})).catch(()=>{})});_.get("/api/node/self-update",async(e,t)=>{t.json({installKind:sn,policy:_t(),policySource:Ml?"plane":"node",current:YM,status:await al(),armed:Nl.armed})});_.post("/api/node/self-update",async(e,t)=>{let n=typeof e.body?.to=="string"?e.body.to.trim():"";if(!/^\d+\.\d+\.\d+$/.test(n))return t.status(400).json({error:"body.to must be a release version (X.Y.Z)"});let r=await Dp({to:n,kind:sn,reason:"manual"});if(r.ok)return Nl.cancel(),Yn({current:r.from,latest:n,installKind:sn,policy:_t(),status:"updating",reason:"manual"}).catch(()=>{}),t.json({ok:!0,...r});t.status(409).json({ok:!1,error:r.reason,status:r.status??null})});async function XM(){try{let e=await k$();e&&await Yn({current:e.from,latest:e.to,installKind:e.kind||sn,policy:_t(),status:e.status,error:e.error,reason:e.reason}),await Fg({current:YM,installKind:sn,policy:_t()})}catch{}}XM();setInterval(XM,3e4).unref();_.get("/api/tracks",async(e,t)=>{let n=(await ke()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>LM){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,LM).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:Jc,agentAvailability:await pp(),tickets:await tR()})});_.get("/api/cost",async(e,t)=>{try{let n=await ke();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await ub(n);if(r.cursorUsage=Aw(),e.query.format==="text")return t.type("text/plain").send(ER(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/usage/roundup",async(e,t)=>{try{let n=await pb(await ke(),{window:e.query.window,from:e.query.from,to:e.query.to});if(e.query.format==="text")return t.type("text/plain").send(OR(n));t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/usage/spend",async(e,t)=>{try{let n=await DR(await ke(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=Aw(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=rc(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/usage/caps",(e,t)=>{t.json(rc())});_.post("/api/usage/caps",async(e,t)=>{try{t.json(await ed(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});_.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await GR(await ke(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await mk(e.get(tr),{byToken:Yc,legacy:()=>kc(e.body?.session_id,e.body?.cwd)});if(n==="refused")return ui("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return ui("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&&ui("mismatch",{track:r.track.id,box:r.box.name}),ui(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 SR(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&tu(r.box.cwd),t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});_.get("/api/git/trailers",async(e,t)=>{t.type("text/plain");try{let n=await Yc(e.get(tr));if(!n?.track||!n?.box)return t.status(204).end();t.send((await _T(n.track,n.box)).join(`
|
|
786
|
+
`),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 vb(e){let t=typeof e=="string"?{host:e}:e||{},n=t["x-bostrat-view-base"];if(typeof n=="string"&&n)return{proto:t["x-forwarded-proto"]==="https"?"https":"http",host:n.replace(/:\d+$/,"").toLowerCase(),source:"header"};let r=Gy();if(r)return{proto:"https",host:r,source:"learned"};let o=Ky();if(o)return{...o,source:"env"};let s=String(t.host||"").replace(/:\d+$/,"").toLowerCase();if(!s||!s.includes(".")||/^[\d.]+$/.test(s))return null;let a=s.split(".");return{proto:"http",host:sf(s)&&Uw(a[0])?a.slice(1).join("."):s,source:"request"}}function uO(e,t){return Gp(vb(t),e.slug)}Ra();tg();Zd();qu();ye();import dO from"node:os";import{randomUUID as Ab}from"node:crypto";var Q6=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||dO.hostname(),nodeName:e.BOSTRAT_NODE_NAME||dO.hostname()});var pO=32768,fO=120,mO=1e3,hO=new Set(["claude","codex","cursor"]),eG=new Set(["off","transition","nag"]),tG=[[0,59],[0,23],[1,31],[1,12],[0,6]];function nG(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=tG[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 Tn=e=>String(e??"").trim(),Ms={name(e){let t=Tn(e);if(!t)throw new Error("a loop needs a name");if(t.length>fO)throw new Error(`name is too long (${t.length}); cap is ${fO}`);return t},schedule(e){let t=Tn(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!nG(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=Tn(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>pO)throw new Error(`prompt is too long (${t.length} chars); cap is ${pO}`);return t},expectation(e){let t=Tn(e)||null;if(t&&t.length>mO)throw new Error(`expectation is too long (${t.length}); cap is ${mO}`);return t},notify(e){let t=Tn(e)||"off";if(!eG.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=Tn(typeof t=="string"?t:t?.provider)||"claude";if(!hO.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...hO].join(", ")}`);let r=typeof t=="object"&&t&&Tn(t.model)||null;return r?{provider:n,model:r}:{provider:n}},enabled(e){if(typeof e=="boolean")return e;if(e==="true"||e==="false")return e==="true";throw new Error(`enabled must be true or false, got "${e}"`)}};function rG(e,t){let n=e&&typeof e=="object"?e:{},r=Ms.name(n.name),o=Ms.schedule(n.schedule),s=Ms.prompt(n.prompt),a=Ms.expectation(n.expectation),i=Ms.notify(n.notify),c=Ms.assignment(n.assignment),l=Tn(t?.workspace?.repo)||Tn(t?.workspace?.base)||null;if(!l)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");let{nodeId:u,nodeName:d}=Q6();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:d||null}}async function gO(e,t){let n=rG(t,e),r=Ab();return await N({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 yO(e,t,n,{loopId:r=null,nextFireAt:o=null}={}){if(!t)throw new Error("proposalId required");let s=await G({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 N({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 N({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,nodeId:i.nodeId??null,nextFireAt:Number.isFinite(Number(o))?Number(o):null}}),await N({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)}}var Sb=["name","schedule","prompt","expectation","notify","assignment","enabled"],oG=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function bO(e){let t=Array.isArray(e.runs)?e.runs:[],n=t[t.length-1]??null;return{definitionUpdatedAt:Number(e.definitionUpdatedAt??e.updatedAt)||null,name:e.name??"",schedule:e.schedule??"",prompt:e.prompt??"",expectation:e.expectation??null,notify:e.notify??"off",assignment:e.assignment&&typeof e.assignment=="object"?e.assignment:{provider:String(e.assignment||"claude")},enabled:e.enabled!==!1,repo:e.repo??null,nodeId:e.nodeId??null,nodeName:e.nodeName??null,trackId:e.trackId??null,nextFireAt:Number.isFinite(Number(e.nextFireAt))?Number(e.nextFireAt):null,createdAt:Number(e.createdAt)||null,runCount:t.length,lastRun:n?{firedAt:Number(n.firedAt)||null,outcome:n.outcome??(n.error?"errored":null)}:null}}function kO(e,t){let n=Tn(t);if(!n)throw new Error("which loop? give an id or a name \u2014 see `bostrat-loop ls`");let r=e.find(a=>a.id===n)||e.find(a=>a.name===n);if(r)return r;let o=n.toLowerCase(),s=e.filter(a=>String(a.id).toLowerCase().startsWith(o)||String(a.name).toLowerCase().startsWith(o));if(s.length===1)return s[0];throw s.length>1?new Error(`"${n}" matches ${s.length} loops: ${s.map(a=>`${a.name} (${a.id})`).join(", ")} \u2014 use the id`):new Error(`no loop matching "${n}" \u2014 use \`bostrat-loop ls\``)}function sG(e,t){let n=e&&typeof e=="object"&&!Array.isArray(e)?e:null;if(!n)throw new Error("a patch must be a JSON object of the fields to change");let r=Object.keys(n).filter(a=>!Sb.includes(a));if(r.length){let a=r.filter(i=>["repo","nodeId","cwd","userId","owner","id","trackId"].includes(i));throw a.length?new Error(`a proposal can't change ${a.join(", ")} \u2014 retargeting a loop is a Loops-tab edit (scope D5)`):new Error(`unknown field(s) ${r.join(", ")} \u2014 patchable: ${Sb.join(", ")}`)}let o={};for(let a of Sb){if(!(a in n))continue;let i=Ms[a](n[a]);(a==="assignment"?oG(i,t.assignment):i===(t[a]??(a==="notify"?"off":a==="enabled"?!0:null)))||(o[a]=i)}let s=Object.keys(o);if(!s.length)throw new Error("nothing would change \u2014 the patch equals the loop's current definition");return{patch:o,changed:s}}async function xO(e,t,n){let r=bO(t),{patch:o,changed:s}=sG(n,r),a=Ab(),i={ts:Date.now(),proposalId:a,loopId:String(t.id),name:r.name,base:r,patch:o,changed:s,selfEdit:!!(r.trackId&&r.trackId===e.id)};return await N({id:e.id,cwd:e.cwd},{type:"loop.update.offered",actor:"system",source:"orchestrator",payload:i}),i}var wO=600;async function vO(e,t,n){let r=Tn(n);if(!r)throw new Error('a delete proposal needs a reason \u2014 bostrat-loop propose-delete <loop> --reason "<why this loop should go>"');if(r.length>wO)throw new Error(`reason is too long (${r.length}); cap is ${wO}`);let o=bO(t),s=Ab(),a={ts:Date.now(),proposalId:s,loopId:String(t.id),name:o.name,base:o,reason:r,selfEdit:!!(o.trackId&&o.trackId===e.id)};return await N({id:e.id,cwd:e.cwd},{type:"loop.delete.offered",actor:"system",source:"orchestrator",payload:a}),a}var SO=async(e,t,n)=>{if(!n)throw new Error("proposalId required");let r=await G({id:e.id,cwd:e.cwd}),o=r.find(a=>a.type===t&&a.payload?.proposalId===n);if(!o)throw new Error("unknown proposal");let s=t.replace(".offered",".answered");if(r.some(a=>a.type===s&&a.payload?.proposalId===n))throw new Error("proposal already answered");return o};async function AO(e,t,n,{nextFireAt:r=null}={}){if(!["applied","dismiss","moved"].includes(n))throw new Error("action must be one of applied | dismiss | moved");let s=(await SO(e,"loop.update.offered",t)).payload,a={id:e.id,cwd:e.cwd};return n==="applied"&&await N(a,{type:"loop.updated",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:s.loopId,name:s.patch?.name??s.name,changed:s.changed??Object.keys(s.patch||{}),nextFireAt:Number.isFinite(Number(r))?Number(r):null}}),await N(a,{type:"loop.update.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:s.loopId}}),{proposalId:t,action:n,loopId:s.loopId}}async function TO(e,t,n){if(!["delete","pause","keep","moved"].includes(n))throw new Error("action must be one of delete | pause | keep | moved");let o=(await SO(e,"loop.delete.offered",t)).payload,s={id:e.id,cwd:e.cwd};return(n==="delete"||n==="pause")&&await N(s,{type:n==="delete"?"loop.deleted":"loop.paused",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:o.loopId,name:o.name,trackId:o.base?.trackId??null}}),await N(s,{type:"loop.delete.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:o.loopId}}),{proposalId:t,action:n,loopId:o.loopId}}ye();import{randomUUID as aG}from"node:crypto";var Ib=20,iG=300,Tb=120,EO=120,IO=2e3,cf=new Set(["build","ops","debt","spike"]),_O=new Set(["active","done","abandoned"]),_b=new Set(["circuit.create","circuit.update","circuit.delete"]),cG=new Set(["person.create","slot.create","slot.update","slot.delete","reservation.create","reservation.update","reservation.delete"]),Sl=new Set([..._b,...cG]),lG=["name","description","status","type"],uG=["person","startDate","endDate","estUsd"],dG=["person","label","startDate","endDate"],pG=/^\d{4}-\d{2}-\d{2}$/,Vt=e=>String(e??"").trim(),$b=e=>typeof e=="string"&&e.startsWith("@"),OO=864e5;function MO(e){let t=Date.parse(`${e}T00:00:00Z`),n=new Date(t).getUTCDay();return new Date(t-(n+6)%7*OO).toISOString().slice(0,10)}function fG(e){return new Date(Date.parse(`${MO(e)}T00:00:00Z`)+6*OO).toISOString().slice(0,10)}var $O=(e,t)=>{let n=Vt(e);if(!pG.test(n))throw new Error(`${t} must be YYYY-MM-DD, got "${e??""}"`);if(!Number.isFinite(Date.parse(`${n}T00:00:00Z`)))throw new Error(`${t} "${n}" is not a real date`);return n};function mG(e,t){let n=$O(e,"startDate"),r=$O(t,"endDate");if(r<n)throw new Error(`endDate ${r} is before startDate ${n}`);let o=MO(n),s=fG(r);return{startDate:o,endDate:s,snapped:o!==n||s!==r}}var RO=e=>{if(e==null||e==="")return null;let t=Number(e);if(!Number.isFinite(t)||t<0)throw new Error(`estUsd must be a non-negative number, got "${e}"`);return t},_o=(e,t,n)=>{let r=Vt(e);if(r.length>t)throw new Error(`${n} is too long (${r.length}); cap is ${t}`);return r};function Eb(e,t){let n=Vt(t);if(!n)throw new Error("a circuit name or id is required");if($b(n))return{ref:n,id:null,name:n.slice(1)};let r=e.find(i=>i.id===n);if(r)return r;let o=n.toLowerCase(),s=e.filter(i=>String(i.name).toLowerCase()===o);if(s.length===1)return s[0];if(s.length>1)throw new Error(`"${n}" names ${s.length} circuits \u2014 use an id (bostrat-circuit ls)`);let a=e.filter(i=>String(i.name).toLowerCase().includes(o)).slice(0,5);throw new Error(a.length?`no circuit named "${n}" \u2014 near matches: ${a.map(i=>`${i.name} (${i.id})`).join(", ")}`:`no circuit named "${n}" \u2014 propose a circuit.create op if it should exist (bostrat-circuit ls lists yours)`)}function hG(e,t){let n=Vt(t);if(!n)throw new Error('a person is required \u2014 "me", a person id, or a row label');if(n==="me"||$b(n))return{ref:n,id:null,label:n==="me"?"you":n.slice(1)};let r=e.find(a=>a.personId===n);if(r)return{...r,id:r.personId};let o=n.toLowerCase(),s=e.filter(a=>String(a.label).toLowerCase()===o);if(s.length===1)return{...s[0],id:s[0].personId};throw s.length>1?new Error(`"${n}" names ${s.length} roadmap rows \u2014 use a person id`):new Error(e.length?`no roadmap row for "${n}" \u2014 rows: ${e.map(a=>`${a.label} (${a.personId})`).join(", ")}, or propose a person.create op`:`no roadmap rows yet \u2014 use "me", or propose a person.create op for "${n}"`)}function wG(e,t,{allow:n=Sl}={}){let r=e&&typeof e=="object"&&!Array.isArray(e)?e:null;if(!r)throw new Error("a plan must be a JSON object with an `ops` array");let o=Array.isArray(r.ops)?r.ops:null;if(!o?.length)throw new Error("a plan needs at least one op in `ops`");if(o.length>Ib)throw new Error(`too many ops (${o.length}); cap is ${Ib} \u2014 propose one intent at a time`);let s=Array.isArray(t?.circuits)?t.circuits:[],a=Array.isArray(t?.people)?t.people:[],i=Array.isArray(t?.slots)?t.slots:[],c=Array.isArray(t?.reservations)?t.reservations:[],l={circuits:{},slots:{},reservations:{}},u={},d=new Set,p=!1,f=!1,w=k=>{k.id&&(l.circuits[k.id]={name:k.name,status:k.status,type:k.type??"build",updatedAt:k.updatedAt??null},u[k.id]=k.name)},m=k=>{k.id&&(u[k.id]=k.label)},g=(k,S)=>{let $=Vt(k);if(!$)return null;if(d.has($))throw new Error(`ref "${$}" is used by two ops \u2014 each ref names one thing`);return d.add($),u[`@${$}`]=S,$},y=o.map((k,S)=>{let $=k&&typeof k=="object"?k:{},M=Vt($.op),L=`op ${S+1}`;if(!Sl.has(M))throw new Error(`${L}: unknown op "${M}" \u2014 one of ${[...Sl].join(", ")}`);if(!n.has(M))throw new Error(`${L}: "${M}" isn't a circuit op \u2014 roadmap changes go through \`bostrat-roadmap propose\` (one card, either CLI)`);let B=R=>{throw new Error(`${L} (${M}): ${R}`)},A=R=>{let T=$.set&&typeof $.set=="object"&&!Array.isArray($.set)?$.set:null;T||B("needs a `set` object naming the fields to change");let C=Object.keys(T).filter(P=>!R.includes(P));return C.length&&B(`unknown field(s) ${C.join(", ")} \u2014 settable: ${R.join(", ")}`),Object.keys(T).length||B("`set` names no fields"),T},I=R=>{let{startDate:T,endDate:C,snapped:P}=mG(R.startDate,R.endDate);return P&&(p=!0),{startDate:T,endDate:C,snapped:P}},j=R=>{let T=hG(a,R);return Vt(R)==="me"&&(f=!0),m(T),{key:T.id??Vt(R),label:T.label}},K=(R,T,C,P,W)=>{let Y=Vt(R);Y||B(`needs a ${P} id`);let me=T.find(_e=>_e[C]===Y);return me||B(`no ${P} "${Y}" \u2014 see \`bostrat-roadmap ls\``),W[Y]={...me},me};switch(M){case"circuit.create":{let R=_o($.name,EO,"name");R||B("a circuit needs a name");let T=Vt($.type)||"build";cf.has(T)||B(`bad type "${T}" (${[...cf].join(" | ")})`);let C=s.find(W=>String(W.name).toLowerCase()===R.toLowerCase());C&&B(`a circuit named "${C.name}" already exists (${C.id}) \u2014 update it instead of making a twin`);let P=g($.ref,R);return{op:M,...P?{ref:P}:{},name:R,description:_o($.description,IO,"description"),type:T}}case"circuit.update":{let R=Eb(s,$.circuit);R.id||B("can't update a circuit created in the same plan \u2014 set its fields on the create op"),w(R);let T=A(lG),C={};if(T.name!==void 0){let P=_o(T.name,EO,"name");P||B("name can't be blank"),P!==R.name&&(C.name=P)}return T.description!==void 0&&(C.description=_o(T.description,IO,"description")),T.status!==void 0&&(_O.has(T.status)||B(`bad status "${T.status}" (${[..._O].join(" | ")})`),T.status!==R.status&&(C.status=T.status)),T.type!==void 0&&(cf.has(T.type)||B(`bad type "${T.type}" (${[...cf].join(" | ")})`),T.type!==(R.type??"build")&&(C.type=T.type)),Object.keys(C).length||B("nothing would change \u2014 the set equals the circuit's current values"),{op:M,circuit:R.id,name:R.name,set:C}}case"circuit.delete":{let R=Eb(s,$.circuit);R.id||B("can't delete a circuit created in the same plan"),w(R);let T=o.some((C,P)=>P<S&&Vt(C?.op)==="circuit.update"&&C?.set?.status&&C.set.status!=="active"&&Vt(C?.circuit)===Vt($.circuit));return R.status==="active"&&!T&&B(`\u201C${R.name}\u201D is active \u2014 an active circuit can't be deleted; propose a circuit.update to done/abandoned first (both can ride one card)`),{op:M,circuit:R.id,name:R.name,status:R.status}}case"person.create":{let R=_o($.label,Tb,"label");R||B("a person row needs a label");let T=a.find(P=>String(P.label).toLowerCase()===R.toLowerCase());T&&B(`a roadmap row labelled "${T.label}" already exists (${T.personId})`);let C=g($.ref,R);return{op:M,...C?{ref:C}:{},label:R}}case"slot.create":{let R=Eb(s,$.circuit);w(R);let T=j($.person),C=I($);return{op:M,circuit:R.id??R.ref,circuitName:R.name,person:T.key,personLabel:T.label,...C,estUsd:RO($.estUsd)}}case"slot.update":{let R=K($.slot,i,"slotId","slot",l.slots),T=A(uG),C={};if(T.person!==void 0){let W=j(T.person);C.person=W.key,C.personLabel=W.label}if(T.startDate!==void 0||T.endDate!==void 0){let W=I({startDate:T.startDate??R.startDate,endDate:T.endDate??R.endDate});W.startDate!==R.startDate&&(C.startDate=W.startDate),W.endDate!==R.endDate&&(C.endDate=W.endDate)}T.estUsd!==void 0&&(C.estUsd=RO(T.estUsd)),Object.keys(C).length||B("nothing would change");let P=s.find(W=>W.id===R.circuitId);return{op:M,slot:R.slotId,circuitName:P?.name??null,set:C}}case"slot.delete":{let R=K($.slot,i,"slotId","slot",l.slots),T=s.find(C=>C.id===R.circuitId);return{op:M,slot:R.slotId,circuitName:T?.name??null,startDate:R.startDate,endDate:R.endDate}}case"reservation.create":{let R=j($.person),T=_o($.label,Tb,"label");return T||B("a reservation needs a label (Leave, Support rotation, \u2026)"),{op:M,person:R.key,personLabel:R.label,label:T,...I($)}}case"reservation.update":{let R=K($.reservation,c,"reservationId","reservation",l.reservations),T=A(dG),C={};if(T.person!==void 0){let P=j(T.person);C.person=P.key,C.personLabel=P.label}if(T.label!==void 0){let P=_o(T.label,Tb,"label");P||B("label can't be blank"),P!==R.label&&(C.label=P)}if(T.startDate!==void 0||T.endDate!==void 0){let P=I({startDate:T.startDate??R.startDate,endDate:T.endDate??R.endDate});P.startDate!==R.startDate&&(C.startDate=P.startDate),P.endDate!==R.endDate&&(C.endDate=P.endDate)}return Object.keys(C).length||B("nothing would change"),{op:M,reservation:R.reservationId,label:R.label,set:C}}case"reservation.delete":{let R=K($.reservation,c,"reservationId","reservation",l.reservations);return{op:M,reservation:R.reservationId,label:R.label,startDate:R.startDate,endDate:R.endDate}}default:return B("unhandled op")}});y.forEach((k,S)=>{for(let $ of[k.circuit,k.person,k.set?.person])if($b($)&&!d.has(String($).slice(1)))throw new Error(`op ${S+1} (${k.op}): unknown ref "${$}" \u2014 no earlier op in this plan creates it`)});let b=a.some(k=>k.kind==="user"&&k.userId);return{why:_o(r.why,iG,"why"),ops:y,base:l,resolved:u,snapped:p,createsMeRow:f&&!b}}async function CO(e,t,n,r={}){let o=wG(t,n,r),s=aG(),a={ts:Date.now(),proposalId:s,...o};return await N({id:e.id,cwd:e.cwd},{type:"planning.change.offered",actor:"system",source:"orchestrator",payload:a}),a}var gG=async(e,t)=>{if(!t)throw new Error("proposalId required");let n=await G({id:e.id,cwd:e.cwd}),r=n.find(o=>o.type==="planning.change.offered"&&o.payload?.proposalId===t);if(!r)throw new Error("unknown proposal");if(n.some(o=>o.type==="planning.change.answered"&&o.payload?.proposalId===t))throw new Error("proposal already answered");return r};async function NO(e,t,n,{results:r=[]}={}){if(!["applied","dismiss","moved"].includes(n))throw new Error("action must be one of applied | dismiss | moved");let o=await gG(e,t),s={id:e.id,cwd:e.cwd},a=o.payload;return n==="applied"&&await N(s,{type:"planning.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,why:a.why??"",results:Array.isArray(r)?r.slice(0,Ib+1):[]}}),await N(s,{type:"planning.change.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,ops:a.ops?.length??0}}),{proposalId:t,action:n}}jd();ye();Fr();ye();jn();Zd();var PO=10,DO=200,yG=120,bG=24,jO=/^\d{4}-\d{2}-\d{2}$/;function Rb(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var kG=(e=Date.now())=>new Date(e).toISOString().slice(0,10),vt=(e,t="i")=>new RegExp(e,t),xG=[{re:vt("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:vt("\\bSPC\\b",""),reason:"business (SPC)"},{re:vt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:vt("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:vt("\\bReplicas\\b",""),reason:"competitor research"},{re:vt("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:vt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:vt("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],vG=[vt("\\bSPC\\b",""),vt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),vt("competitor|cloudcli|\\bkiro\\b"),vt("\\bReplicas\\b",""),vt("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),vt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),vt("(^|[^\\w&])#\\d+\\b"),vt("\\btrack/[\\w.-]+")];function SG({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of xG)if(o.test(n)||o.test(r))return s;return null}function BO(e){let t=String(e||"");for(let n of vG){let r=n.exec(t);if(r)return r[0].trim()}return null}function LO(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 AG=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function TG(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Pc(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(`
|
|
787
|
+
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,yG)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${bG}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=AG.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.`);i=`**${u[1].trim().replace(/[:.]\s*$/,"")}** \u2014 ${u[2].trim()}`;let d=BO(i);if(d)throw new Error(`bullet ${r.length+1} mentions "${d}" \u2014 the product changelog never carries business, security/tenancy or internal-ops items, PR numbers, branch names or codenames; drop the bullet or rewrite it as the user-visible change and re-publish.`);if(i.length>DO)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${DO}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=BO(n);if(o)throw new Error(`headline mentions "${o}" \u2014 the product changelog never carries business, security/tenancy or internal-ops items; rewrite it and re-publish.`)}if(!r.length)throw new Error("no `- ` bullets found \u2014 an entry is a bullet list, optionally under a `# headline`");if(r.length>PO)throw new Error(`${r.length} bullets \u2014 over the ${PO}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function EG(e,{repos:t=Rb()}={}){let n=new Set(t);return(await hn()).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,headRef:r.headRef||null,mergedAt:r.resolvedAt??null,excluded:SG({title:r.title,headRef:r.headRef})}))}async function FO(e,t){let n=await EG(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function UO(e,{md:t,day:n=null}){let r=TG(t),o=n==null||n===""?kG():String(n);if(!jO.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await N({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}}async function HO(e,{day:t}){let n=String(t||"");if(!jO.test(n)||Number.isNaN(Date.parse(n)))throw new Error(`invalid day "${t}" \u2014 expected YYYY-MM-DD`);let r=await N({id:e.id,cwd:e.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:e.leadBox||null,payload:{ts:Date.now(),day:n,title:null,bullets:[],retract:!0}});return{day:n,retracted:!0,eventId:r.id}}as();jn();cs();ye();as();yt();gr();jn();cc();Cd();yd();mc();Zt();import{execFile as jG}from"node:child_process";import{promisify as LG}from"node:util";var IG=new Set(["pull_request","check_suite","workflow_run","push"]),_G=/^[^/\s]+\/[^/\s]+$/,$G=500,Ob=new Set,Mb=[],lf=new Map,on={hourStart:0,byEvent:{},duplicates:0,rejected:0};function Cb(e,t,n){let r=Math.floor(e/36e5)*36e5;r!==on.hourStart&&(on.hourStart=r,on.byEvent={},on.duplicates=0,on.rejected=0),t==="accepted"?on.byEvent[n]=(on.byEvent[n]||0)+1:on[t]+=1}function RG(e){if(!e||typeof e!="object"||e.forge!=="github"||typeof e.event!="string"||!IG.has(e.event)||typeof e.repo!="string"||!_G.test(e.repo))return null;let t=(Array.isArray(e.numbers)?e.numbers:[]).map(Number).filter(r=>Number.isInteger(r)&&r>0),n=Number.isInteger(e.number)&&e.number>0?e.number:t[0]??null;return{forge:"github",deliveryId:typeof e.deliveryId=="string"&&e.deliveryId?e.deliveryId.slice(0,128):null,event:e.event,action:typeof e.action=="string"?e.action.slice(0,64):null,repo:e.repo,number:n,numbers:n&&!t.includes(n)?[n,...t]:t,headSha:typeof e.headSha=="string"?e.headSha.slice(0,64):null,headRef:typeof e.headRef=="string"?e.headRef.slice(0,256):null,baseRef:typeof e.baseRef=="string"?e.baseRef.slice(0,256):null,merged:typeof e.merged=="boolean"?e.merged:null,draft:typeof e.draft=="boolean"?e.draft:null,conclusion:typeof e.conclusion=="string"?e.conclusion.slice(0,64):null,deleted:e.deleted===!0,senderLogin:typeof e.senderLogin=="string"?e.senderLogin.slice(0,128):null,ts:Number.isFinite(e.ts)?e.ts:null}}function WO(e,{now:t=Date.now(),onAccept:n=null,log:r=console}={}){let o=RG(e);if(!o)return Cb(t,"rejected"),{ok:!1,error:"malformed github event envelope"};if(o.deliveryId){if(Ob.has(o.deliveryId))return Cb(t,"duplicates"),{ok:!0,duplicate:!0,envelope:o};for(Ob.add(o.deliveryId),Mb.push(o.deliveryId);Mb.length>$G;)Ob.delete(Mb.shift())}let s=o.repo.toLowerCase(),a=lf.get(s)||{lastDeliveryAt:0,count:0};a.lastDeliveryAt=t,a.count+=1,lf.set(s,a),Cb(t,"accepted",o.event),r.log?.(`[github-event] ${o.event}${o.action?` ${o.action}`:""} ${o.repo}${o.number?` #${o.number}`:o.headRef?` ${o.headRef}`:""}${o.conclusion?` (${o.conclusion})`:""}${o.deliveryId?` delivery ${o.deliveryId.slice(0,8)}`:""}`);try{n?.(o)}catch(i){r.error?.("[github-event] onAccept failed:",i?.message||i)}return{ok:!0,duplicate:!1,envelope:o}}function OG(e,t=Date.now()){let n=lf.get(String(e||"").toLowerCase());return n?{covered:!0,lastDeliveryAt:n.lastDeliveryAt,ageMs:t-n.lastDeliveryAt,count:n.count}:{covered:!1,lastDeliveryAt:null,ageMs:null,count:0}}var qO=1440*6e4;function Nb(e,t=Date.now()){let n=OG(e,t);return n.covered&&n.ageMs<qO}function GO(e=Date.now()){let t={};for(let[n,r]of lf)t[n]={lastDeliveryAt:r.lastDeliveryAt,count:r.count,covered:e-r.lastDeliveryAt<qO};return{hourStart:on.hourStart,byEvent:{...on.byEvent},duplicates:on.duplicates,rejected:on.rejected,repos:t}}ye();jn();Pr();gr();Zt();function KO(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function MG(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var CG=new Set(["merged","closed","lost"]);function NG(e,t){if(!e?.blocked)return null;let n=KO(t);if(e.blocked==="red")return t.checks.failed===0&&t.checks.total>0?{change:"ci-fixed",reason:"red"}:null;if(e.blocked==="conflict")return t.mergeable==="MERGEABLE"&&n!=="red"?{change:"reconciled",reason:"conflict"}:null;if(e.blocked==="behind"){let r=t.headSha??null;return n===null&&r&&e.sha&&r!==e.sha?{change:"reconciled",reason:"behind"}:null}return null}function PG(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function DG(e,t,n,r,o){return(await G(e).catch(()=>[])).some(a=>{if(a.type!=="pr.state.changed"||a.payload?.number!==Number(n))return!1;let i=a.payload?.change;return i===t?a.payload?.sha&&r?a.payload.sha===r:(a.payload?.ts||0)>=o:i==="pushed"&&!!r&&a.payload?.sha===r})}async function BG(e,t,n){await N({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 uf(e,t={}){let{now:n=Date.now(),slugOf:r=Oe,lookup:o=pt,note:s=DA,tracks:a=async()=>(await ke()).filter(d=>!Le(d)),asked:i=DG,emitChange:c=BG}=t,l=[],u=null;for(let d of e??[]){if(!d?.prs?.length)continue;let p=await r(d.path).catch(()=>null);if(p)for(let f of d.prs)try{if(!MG(f))continue;let w=await o(p,f.number).catch(()=>null);if(!w?.trackId||CG.has(w.state))continue;let{prev:m}=await s(p,f.number,{blocked:KO(f),sha:f.headSha??null,at:n});if(!m)continue;let g=NG(m,f);if(!g||PG(w,g.change))continue;u??=new Map((await a()).map(S=>[S.id,S]));let y=u.get(w.trackId);if(!y)continue;let b=f.headSha??null;if(await i(y,g.change,f.number,b,m.at||0))continue;let k={number:Number(f.number),url:f.url||w.url,title:f.title||w.title,branch:f.headRefName||w.headRef,repo:p,sha:b,delegated:!1,reason:g.reason};g.change==="ci-fixed"?k.passed=f.checks.passed:k.base=f.baseRefName||null,await c(y,g.change,k),l.push({change:g.change,number:f.number,reason:g.reason})}catch{}}return l}var Bb=LG(jG),tM=3e4,FG=30*6e4,UG=tM,HG=10*6e4,WG=10080*6e4,qG=60*6e4,VO=8,nM=1440*6e4,GG=3,rM="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Db=60*6e4,KG=15*6e4,VG=9e4,zG=1440*6e4,Al=new Map,Tl=new Map,JG=360*6e4,YG=e=>e.reduce((t,n)=>({misses:Math.max(t.misses,n.brokerRefused?.misses||0),until:Math.max(t.until,n.brokerRefused?.until||0)}),{misses:0,until:0}),XG=/could not resolve|not found|no pull requests?/i,ZG=e=>XG.test(String(e?.message||e||""));function QG(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<Db)}var zO={fast:UG,slow:HG,decay:qG};function JO(e,t,n=Nb){return oM(e,t,{grace:0})||!n(e.repo,t)&&(e.armedAt&&t-e.armedAt<FG||QG(e,t))?"fast":t-(e.firstSeen||0)>WG?"decay":"slow"}function Il(e,t,n,r){let o=e.lanes?.get(ie(t.repo,t.number))||{};return gd({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function eK(e,t){pr("watch");try{let{stdout:n}=await Bb("gh",["pr","view",String(t),"--repo",e,"--json",rM],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await _n({slug:e})}});return JSON.parse(n||"null")}catch(n){throw ao(n)&&io(n),n}}async function tK(e){pr("watch");try{let{stdout:t}=await Bb("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",rM],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await _n({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw ao(t)&&io(t),t}}async function nK(){let e=new Map;for(let t of await ke().catch(()=>[]))e.set(t.id,t);return e}async function rK(e,t){await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...t}}),await Md(e,t).catch(()=>{})}async function oK(e,t){let n=await ss(e);return n?ka(n,t):null}async function sK(e,t,n){let r=await ss(e);if(!r)throw new Error(`no local checkout for ${e}`);return xa(r,t,{method:n})}async function aK(e,t){let n=await ss(e);if(!n)throw new Error(`no local checkout for ${e}`);return dc(n,t)}async function iK(e,t){await N({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 Qa(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 cK=10*6e4;async function lK(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>nM){await Qa(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 Qa(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await Qa(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<cK)return;await Qa(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 Qa(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 Qa(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await sM(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function ei(e,t,n){await N({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 uK(e,t){try{return await Bb("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function El(e,t,n,r=()=>!0){return(await G(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var ti=(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 oM(e,t,{grace:n=VG}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=zG}async function dK(e,t,n,r){let o=r.now??Date.now();if(!oM(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||Le(s))return!1;let a=(s.boxes||[]).find(d=>d.name===s.leadBox);return await(r.midTurn??is)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await G(s).catch(()=>[])).some(d=>d.type==="pr.state.changed"&&d.payload?.number===Number(e.number))?(await(r.claim??ur)(e.repo,e.number).catch(()=>null),!1):await(r.claim??ur)(e.repo,e.number).catch(()=>null)?(await ei(s,"opened",{...ti(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 pK(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 ld(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await dd(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<KG;await PA(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await NA(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await El(a,"pushed",e.number,d=>d.payload?.sha===s)&&await ei(a,"pushed",{...ti(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>Db)await ud(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 ud(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Il(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await El(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await ei(a,"ci-fixed",{...ti(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await ud(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>Db)await pd(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await pd(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Il(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await El(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await ei(a,"reconciled",{...ti(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await pd(e.repo,e.number).catch(()=>{})}}}}async function fK(e,t,n,r){let o=Il(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await El(s,"closed",e.number)&&await ei(s,"closed",{...ti(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 YO(e,t,n){Il(n,e,"lost",null);let r=t.get(e.trackId);r&&!await El(r,"lost",e.number)&&await ei(r,"lost",ti(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function sM(e,t,n,r,o={}){let s=Il(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await G(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.followupsIfMerged(e.repo,e.number).catch(()=>{}),await r.archiveIfArmed(e.repo,e.number).catch(()=>{}),await r.mark(e.repo,e.number,"merged").catch(()=>{})}async function XO(e={}){let{now:t=Date.now(),auth:n=Gh,view:r=eK,list:o=tK,links:s=LA,trackMap:a=nK,emitMerged:i=rK,mark:c=oo,archiveIfArmed:l=hr,notifyIfArmed:u=wr,notifyIfResolved:d=xp,followupsIfMerged:p=xs,batchThreshold:f=GG,detail:w=oK,merge:m=sK,updateBranch:g=aK,lookup:y=pt,disarm:b=ic,emitDisarmed:k=iK,localHasCommit:S=uK,claim:$=ur,midTurn:M=is,only:L=null,wantDetail:B=null,covered:A=Nb,verdicts:I=null,tokenFor:j=J=>Fs(J),noteRefusal:K=MA,clearRefusal:R=CA}=e;if(!(await n().catch(()=>null))?.authenticated||hd()>0)return 0;let C=await s().catch(()=>[]),P=lT(C),W=J=>{try{return!!B?.(J)}catch{return!1}},Y=L?C.filter(J=>{try{return L(J)}catch{return!1}}):C.filter(J=>t-(Al.get(ie(J.repo,J.number))||0)>=zO[JO(J,t,A)]);if(!Y.length)return 0;let me=Date.now(),_e=new Map,Pe=new Map;for(let J of[...Y].sort(pc)){let he=L?"event":JO(J,t,A);_e.set(ie(J.repo,J.number),{lane:he,intervalMs:zO[he]??null}),Al.set(ie(J.repo,J.number),t),Pe.has(J.repo)||Pe.set(J.repo,[]),Pe.get(J.repo).push(J)}let ne=await a().catch(()=>new Map),Ve=new Map,ve=(J,he)=>{let Fe=ie(J,he);if(!Ve.has(Fe)){let O=Promise.resolve().then(()=>w(J,he));O.catch(()=>{}),Ve.set(Fe,O)}return Ve.get(Fe)},ce=I??((J,he)=>uf([{path:J,prs:[he]}],{now:t,slugOf:async()=>J,tracks:async()=>[...ne.values()].filter(Fe=>!Le(Fe))}).catch(()=>{})),re={now:t,lanes:_e,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,followupsIfMerged:p,detail:ve,merge:m,updateBranch:g,lookup:y,disarm:b,emitDisarmed:k,localHasCommit:S,claim:$,midTurn:M};for(let[J,he]of Pe){let Fe=YG(he);if(Fe.until>t)continue;let O=!1;try{if(await j(J),Fe.misses)for(let X of he)await R(X.repo,X.number).catch(()=>{})}catch(X){O=_k(X)}if(O){let X=Fe.misses+1;if(X>=VO){console.log(`[pr-watch] ${J}: not granted ${X}\xD7 \u2014 retiring ${he.length} link(s) as lost`);for(let we of he){await YO(we,ne,re);let mt=ie(we.repo,we.number);Tl.delete(mt),Al.delete(mt)}}else for(let we of he)await K(we.repo,we.number,{misses:X,until:t+JG}).catch(()=>{});continue}let oe,Re=new Set,$t=X=>X.ciFixAwait||X.reconcileAwait||W(X)?!0:!X.autoMerge||X.trackId&&P.get(X.trackId)!==ie(X.repo,X.number)?!1:t-(X.autoMerge.armedAt||0)<=nM;if(he.length>f)oe=await o(J).catch(()=>null);else{oe=new Map;for(let X of he){if($t(X)){let we=await ve(J,X.number).catch(()=>null);if(we?.state){oe.set(Number(X.number),{number:Number(X.number),url:we.url,title:we.title,headRefName:we.headRefName,headRefOid:we.headSha??null,state:we.state,mergedAt:we.mergedAt??null,closedAt:we.closedAt??null});continue}}try{let we=await r(J,X.number);we?.number!=null&&oe.set(Number(we.number),we)}catch(we){ZG(we)&&Re.add(ie(X.repo,X.number))}}}if(oe)for(let X of he){let we=ie(X.repo,X.number),mt=oe.get(Number(X.number));if(!mt){if(Re.has(we)){let nt=(Tl.get(we)||0)+1;Tl.set(we,nt),nt>=VO&&(await YO(X,ne,re),Tl.delete(we),Al.delete(we))}continue}if(Tl.delete(we),mt.state==="MERGED"||mt.state==="CLOSED")mt.state==="MERGED"?await sM(X,mt,ne,re):await fK(X,mt,ne,re),Al.delete(we);else{await dK(X,mt,ne,re).catch(()=>{}),await pK(X,mt,ne,re).catch(()=>{});let nt=!X.trackId||P.get(X.trackId)===we;X.autoMerge&&nt&&await lK(X,ne,re).catch(()=>{});try{if(W(X)&&!Ve.has(we)&&ve(X.repo,X.number),Ve.has(we)){let an=await Ve.get(we).catch(()=>null);an?.checks&&await ce(X.repo,an)}}catch{}}}}let Ce={};for(let{lane:J}of _e.values())Ce[J]=(Ce[J]||0)+1;return console.log(`[pr-watch] probed ${Y.length} link(s) (${Object.entries(Ce).map(([J,he])=>`${J} ${he}`).join(", ")}) in ${Date.now()-me}ms`),Y.length}function ZO(e,t){return!e||!t?!1:e.all?!0:String(t.repo||"").toLowerCase()!==e.repo?!1:e.numbers?.length?e.numbers.includes(Number(t.number)):e.event==="push"?!!e.branch&&t.headRef===e.branch||!!t.autoMerge:e.branch?t.headRef===e.branch:!0}var Pb=!1,df=!1,pf=[],QO=Promise.resolve();async function mK(){for(;df||pf.length;){if(df){df=!1;try{await XO()}catch{}continue}let e=pf;pf=[];let t=e.map(r=>r.spec),n=Object.assign({},...e.map(r=>r.deps||{}));try{await XO({...n,only:r=>t.some(o=>ZO(o,r)),wantDetail:r=>t.some(o=>o.detail&&ZO(o,r))})}catch{}}}function ff(e,t){return e===null?df=!0:pf.push({spec:e,deps:t}),Pb||(Pb=!0,QO=mK().finally(()=>{Pb=!1})),QO}function aM(e,t=void 0){let n=String(e?.repo||"").toLowerCase();if(!/^[^/\s]+\/[^/\s]+$/.test(n))return Promise.resolve();let r=(Array.isArray(e.numbers)?e.numbers:[]).map(Number).filter(o=>Number.isInteger(o)&&o>0);return ff({repo:n,numbers:r,branch:e.branch||null,event:e.event||null,detail:e.detail===!0},t)}var hK=6e4,eM=0;function iM(e=void 0){let t=e?.now??Date.now();return t-eM<hK?Promise.resolve():(eM=t,ff({all:!0},e))}function cM(){ff(null),setInterval(()=>ff(null),tM)}as();Nd();Oc();gr();Zt();var wK=new Set(["opened","reopened","closed","synchronize","ready_for_review","converted_to_draft","edited"]);async function gK({tracks:e=ke,reconcile:t=Kd}={}){for(let n of(await e().catch(()=>[])).filter(r=>!Le(r)))await t(n,{bypassThrottle:!0}).catch(()=>{})}function lM(e,t={}){let{invalidate:n=en,probe:r=aM,poke:o=KT,reconcileOpened:s=gK}=t;try{n()}catch{}let a={probe:null,poked:!1,reconciled:!1};if(!e?.repo)return a;if(e.event==="pull_request"&&wK.has(e.action)){if(e.action==="opened"||e.action==="reopened"){a.poked=!0,a.reconciled=!0;try{o()}catch{}s()}a.probe={repo:e.repo,numbers:e.numbers,branch:e.headRef||null,event:e.event}}else(e.event==="check_suite"||e.event==="workflow_run")&&e.action==="completed"?a.probe={repo:e.repo,numbers:e.numbers,branch:e.headRef||null,event:e.event,detail:!0}:e.event==="push"&&!e.deleted&&(a.probe={repo:e.repo,branch:e.headRef||null,event:"push"});return a.probe&&r(a.probe),a}ht();ye();gr();Zt();import{randomUUID as yK}from"node:crypto";var bK=2e4,uM=3,ni=new Map,jb=new Set;async function dM(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 kK(e={}){let{sessions:t=null,tracks:n=null,append:r=N,read:o=G,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await ln(),i=n??await ke()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(Le(u)||!u.leadBox)continue;let d=(u.boxes||[]).find(f=>f.name===u.leadBox);if(!d||d.agent!=="claude"||d.state!=="live")continue;let p=c.get(d.name);if(p)if(l.add(d.name),p.alternateOn){let f=(ni.get(d.name)||0)+1;ni.set(d.name,f),f===uM&&(jb.add(d.name),await dM(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:yK(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let f=(ni.get(d.name)||0)>=uM;if(ni.delete(d.name),f||!jb.has(d.name)){jb.add(d.name);let w=await dM(u,d.name,o);w&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:d.name,provider:d.agent,payload:{ts:s,offerId:w,action:"cleared"}}).catch(()=>{})}}}for(let u of[...ni.keys()])l.has(u)||ni.delete(u)}var _l=null,Lb=!1;function pM(){return _l?()=>{}:(_l=setInterval(()=>{Lb||(Lb=!0,kK().catch(()=>{}).finally(()=>{Lb=!1}))},bK),_l.unref?.(),()=>{clearInterval(_l),_l=null})}ts();Oc();ye();as();cs();ts();yc();import{randomUUID as xK}from"node:crypto";async function vK(){let e=await Promise.resolve().then(()=>(gr(),i$));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var Fb={listRepoPrs:Ln,readEvents:G,appendEvent:N,sayDeps:vK,currentBranch:co};async function SK(e,t=Fb,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??co)(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 Ub(e,t,{actions:n,notify:r=!1}={},o=Fb){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 SK(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=xK();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 Hb(e,{offerId:t,choice:n}={},r=Fb){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');return uo(`prx:${e.id}:${t}`,()=>AK(e,{offerId:t,choice:n},r))}async function AK(e,{offerId:t,choice:n},r){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=ro.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(e.id,wa(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,bA(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}}yc();import Or from"node:fs/promises";import ri from"node:path";import bM from"node:os";import fM from"node:fs/promises";import mf from"node:path";import{execFile as TK}from"node:child_process";import{promisify as EK}from"node:util";import{fileURLToPath as IK}from"node:url";var _K=EK(TK),hM=mf.dirname(IK(import.meta.url)),$K=mf.resolve(hM,"..","..","client","dist"),RK="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",OK=new Map([...RK].map((e,t)=>[e,t]));function MK(e){let t=[],n=0,r=0;for(let o of e){let s=OK.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 CK(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=MK(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 NK(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 mM=new Map;async function PK(e){let t;try{t=await fM.stat(e)}catch{return null}let n=mM.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await fM.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:CK(r.mappings||"")};return mM.set(e,o),o}catch{return null}}var Wb=null;function DK(){return Wb||(Wb=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):_K("git",["rev-parse","--short","HEAD"],{cwd:mf.resolve(hM,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),Wb}var BK=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function jK(e,{distDir:t=$K}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(BK)){let[a,i,c,l]=s,u=await PK(mf.join(t,"assets",`${i}.map`)),d=u?NK(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(e.slice(r,s.index)),d&&u.sources[d[1]]){let p=u.sources[d[1]].replace(/^(\.\.\/)+/,"");n.push(`${p}:${d[2]+1}:${d[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 wM(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await DK();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await jK(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var LK=()=>process.env.BOSTRAT_STATE_DIR||ri.join(process.env.HOME||bM.tmpdir(),".bostrat"),$o=()=>ri.join(LK(),"telemetry"),kM=()=>ri.join($o(),".relay-offsets.json"),FK=50,UK=14,HK=3e4,gM=500,zt=(e,t)=>typeof e=="string"?e.slice(0,t):"";function WK(e,t){if(!e||typeof e!="object")return null;let n=zt(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:zt(e.anonId,64),sessionId:zt(e.sessionId,64),userId:zt(e.userId,64),release:zt(e.release,64),appVersion:zt(e.appVersion,32),route:zt(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:zt(e.error.kind,256),message:zt(e.error.message,8192),stack:zt(e.error.stack,32768),fingerprint:zt(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:zt(s?.n,64),r:zt(s?.r,128)}))),o}var xM=e=>new Date(e).toISOString().slice(0,10),yM=0;async function vM(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,FK):[]).map(s=>WK(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await wM(s);await Or.mkdir($o(),{recursive:!0});let o=ri.join($o(),`${xM(t)}.ndjson`);return await Or.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
788
|
+
`).join(""),"utf8"),t-yM>6*3600*1e3&&(yM=t,qK(t).catch(()=>{})),r.length}async function qK(e=Date.now()){let t=xM(e-UK*24*3600*1e3),n=[];try{n=await Or.readdir($o())}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 Or.rm(ri.join($o(),s),{force:!0});let o=await AM();for(let s of r)delete o[s];await TM(o)}var hf=new Map;function SM(e,t=Date.now()){let n=e||"unknown",r=hf.get(n);return r||(hf.size>512&&hf.clear(),r={tokens:40,at:t},hf.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 AM(){try{return JSON.parse(await Or.readFile(kM(),"utf8"))||{}}catch{return{}}}async function TM(e){await Or.mkdir($o(),{recursive:!0}),await Or.writeFile(kM(),JSON.stringify(e),"utf8")}function GK(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||bM.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 KK({fetchImpl:e=fetch}={}){let t=await D$();if(!t)return{sent:0,disabled:!0};let n;try{n=(await Or.readdir($o())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await AM(),o=0;for(let s of n){let a=ri.join($o(),s),i=r[s]||0,c;try{let p=await Or.open(a,"r");try{let f=(await p.stat()).size;if(f<=i)continue;c=Buffer.alloc(f-i),await p.read(c,0,c.length,i)}finally{await p.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
|
|
789
|
+
`).filter(Boolean),d=[];for(let p of u)try{d.push(JSON.parse(p))}catch{}for(let p=0;p<d.length;p+=gM){let f=d.slice(p,p+gM).map(m=>GK(m)),w=await e(`${t.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.ingestKey}`},body:JSON.stringify({events:f})}).catch(()=>null);if(!w||!w.ok)return{sent:o,disabled:!1};o+=f.length}r[s]=i+l+1,await TM(r)}return{sent:o,disabled:!1}}var qb=null,Gb=!1;function EM(){qb||(qb=setInterval(async()=>{if(!Gb){Gb=!0;try{await KK()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{Gb=!1}}},HK),qb.unref?.())}import gf from"node:fs/promises";import OM from"node:path";import VK from"node:os";var MM=()=>process.env.BOSTRAT_STATE_DIR||OM.join(process.env.HOME||VK.tmpdir(),".bostrat"),CM=()=>OM.join(MM(),"dictation-glossary.json"),zK=100,JK=500,YK=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],yf=null,Kb=null,Vb=Promise.resolve();function IM(){let e={},t=Date.now();for(let n of YK)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function NM(){return yf||(Kb??=gf.readFile(CM(),"utf8").then(e=>JSON.parse(e)?.terms??IM()).catch(()=>IM()).then(e=>yf=e).finally(()=>Kb=null),Kb)}async function _M(){let e=JSON.stringify({terms:yf},null,2),t=CM(),n=`${t}.${process.pid}.tmp`;await gf.mkdir(MM(),{recursive:!0}),await gf.writeFile(n,e),await gf.rename(n,t)}function XK(){return Vb=Vb.then(_M,_M),Vb.catch(()=>{})}async function zb(){return Object.values(await NM()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var ZK=e=>Math.max(1,Math.ceil(e.length/4));async function QK(){let e=[],t=0;for(let{term:n}of await zb()){let r=ZK(n);if(!(t+r>JK)&&(e.push(n),t+=r,e.length>=zK))break}return e}async function eV(e,t){let n=await NM(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var $M=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),wf=e=>e.toLowerCase();function RM(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function PM(e,t){let n=$M(e),r=$M(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 d=s-1;d>=0;d--)a[u][d]=wf(n[u])===wf(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;)wf(n[c])===wf(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(RM(r[l])&&i.add(r[l]),l++);for(;l<s;l++)RM(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await eV(u,"learned");return await XK(),[...i]}var $l=null;function tV(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 DM(){if(!$l)return null;let e=await il();if(!e||e.apiKey.slice(-6)!==$l.keyTail)return null;let{keyTail:t,...n}=$l;return n}async function BM(e,t){let n=await il();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 QK())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=tV(o.status,i);c.persistent&&($l={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 $l=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}ye();var Jb={"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 nV(e){if(!e)return null;let t=String(e).trim();if(Jb[t])return t;let n=null;for(let r of Object.keys(Jb))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function rV(e,t={}){let n=Jb[nV(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 oV(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 Rl=null;async function sV(){if(!Rl)return null;let e=await Wp();if(!e||e.apiKey.slice(-6)!==Rl.keyTail)return null;let{keyTail:t,...n}=Rl;return n}async function aV(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=oV(t.status,o);s.persistent&&(Rl={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}Rl=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 iV={openai:aV};async function jM(){let e=await Wp();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await sV()}:{configured:!1}}async function LM(){let e=await Wp();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 iV[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function FM(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=rV(t,s),i=await N(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}}Zt();IA(async()=>new Set((await ke()).map(e=>e.id)));var zM=$e.dirname(lV(import.meta.url)),UM=Number(process.env.PORT)||3362,bf=Iy(process.env),JM=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,oi=QR(process.env.BOSTRAT_ALLOW_IPS),ek=eO(process.env.BOSTRAT_VIEW_FRONT_CIDRS,af),tk=nO(),YM=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",_=Jt();_.disable("x-powered-by");_.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),sf(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});_.use((e,t,n)=>{if(vl(e)||oi.allowed(oi.clientIp(e))||ek.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});_.use(cO);_.use(sO({token:tk,enforce:YM}));_.use("/api/mocks",Jt.json({limit:"1mb"}));_.use("/api/mocks",Jt.raw({type:"image/png",limit:"24mb"}));_.use("/api/mocks",Jt.raw({type:["video/mp4","video/webm"],limit:"105mb"}));_.use("/api/runbooks",Jt.json({limit:"1mb"}));_.use("/api/tracks/adopt",Jt.json({limit:"1mb"}));_.use("/api/telemetry",Jt.json({limit:"256kb"}));_.use("/api/hook",Jt.json({limit:"2mb"}));_.use("/api/dictation",Jt.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));_.use("/api/tracks/:id/upload",Jt.raw({type:()=>!0,limit:"25mb"}));_.use(Jt.json({limit:"64kb"}));_.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:Ft,hostTmuxAlive:await Hr(),autoTriggerPct:JM,plane:si?si.status():null})});_.get("/api/sessions",async(e,t)=>{t.json({sessions:await ln()})});_.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 On(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})}});_.delete("/api/sessions/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Be(n))return t.status(400).json({error:"invalid session name"});if(!await xe(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}`})}});_.get("/api/context/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Be(n))return t.status(400).json({error:"invalid session name"});let r=await xe(n);if(!r)return t.status(404).json({error:"session not found"});let o=await Di(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});_.post("/api/handoff/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Be(n))return t.status(400).json({error:"invalid session name"});try{let r=await Rv(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)})}});_.get("/api/handoff/:name",(e,t)=>{let n=decodeURIComponent(e.params.name),r=ju(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 HM=10;async function XM(){let e=await ke(),[t,n]=await Promise.all([T_(e),c$(e)]);return{tracks:e,views:await Fw(),queuedOf:r=>gs(r),jobsOf:r=>t.get(r)??[],prPendingOf:r=>n.get(r)??[]}}_.get("/api/idle-guard",async(e,t)=>{try{t.json(Ey(await XM()))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var sn=Ss(),hV=v$({kind:sn}),Ml=null,_t=()=>Ml&&["auto","prompt","off"].includes(Ml)?Ml:hV,Qb=null,wV=sl.has(sn),ZM=async({to:e,from:t})=>!wV||Dp(await al())?!1:(Nl.arm({to:e,from:t,kind:sn}),!0);async function gV(e){let t=_t();Ml=e??null;let n=_t();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){Nl.cancel();return}Qb&&await ZM(Qb)}}var QM=(()=>{try{return mV(import.meta.url)("../package.json").version||null}catch{return null}})(),Nl=A$({isBusy:async()=>Ey(await XM()).busy,onStuck:({to:e,from:t})=>Yn({current:t,latest:e,installKind:sn,policy:_t(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?Yn({current:t,latest:e,installKind:sn,policy:_t(),status:"updating",reason:"auto"}):Yn({current:t,latest:e,installKind:sn,policy:_t(),status:"failed",error:n?.reason})).catch(()=>{})});_.get("/api/node/self-update",async(e,t)=>{t.json({installKind:sn,policy:_t(),policySource:Ml?"plane":"node",current:QM,status:await al(),armed:Nl.armed})});_.post("/api/node/self-update",async(e,t)=>{let n=typeof e.body?.to=="string"?e.body.to.trim():"";if(!/^\d+\.\d+\.\d+$/.test(n))return t.status(400).json({error:"body.to must be a release version (X.Y.Z)"});let r=await Bp({to:n,kind:sn,reason:"manual"});if(r.ok)return Nl.cancel(),Yn({current:r.from,latest:n,installKind:sn,policy:_t(),status:"updating",reason:"manual"}).catch(()=>{}),t.json({ok:!0,...r});t.status(409).json({ok:!1,error:r.reason,status:r.status??null})});async function eC(){try{let e=await S$();e&&await Yn({current:e.from,latest:e.to,installKind:e.kind||sn,policy:_t(),status:e.status,error:e.error,reason:e.reason}),await Ug({current:QM,installKind:sn,policy:_t()})}catch{}}eC();setInterval(eC,3e4).unref();_.get("/api/tracks",async(e,t)=>{let n=(await ke()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>HM){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,HM).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:Yc,agentAvailability:await fp(),tickets:await oR()})});_.get("/api/cost",async(e,t)=>{try{let n=await ke();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await pb(n);if(r.cursorUsage=Aw(),e.query.format==="text")return t.type("text/plain").send($R(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/usage/roundup",async(e,t)=>{try{let n=await mb(await ke(),{window:e.query.window,from:e.query.from,to:e.query.to});if(e.query.format==="text")return t.type("text/plain").send(NR(n));t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/usage/spend",async(e,t)=>{try{let n=await LR(await ke(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=Aw(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=oc(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/usage/caps",(e,t)=>{t.json(oc())});_.post("/api/usage/caps",async(e,t)=>{try{t.json(await td(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});_.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await zR(await ke(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await wk(e.get(tr),{byToken:Xc,legacy:()=>xc(e.body?.session_id,e.body?.cwd)});if(n==="refused")return di("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return di("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&&di("mismatch",{track:r.track.id,box:r.box.name}),di(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 ER(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&tu(r.box.cwd),t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});_.get("/api/git/trailers",async(e,t)=>{t.type("text/plain");try{let n=await Xc(e.get(tr));if(!n?.track||!n?.box)return t.status(204).end();t.send((await RT(n.track,n.box)).join(`
|
|
790
790
|
`)+`
|
|
791
|
-
`)}catch{t.status(204).end()}});_.post("/api/git/committed",async(e,t)=>{try{let n=await Yc(e.get(tr));if(!n?.track||!n?.box)return t.json({ok:!1,error:"unknown hook token"});let r=String(e.body?.sha||"").trim();if(!/^[0-9a-f]{7,40}$/i.test(r))return t.json({ok:!1,error:"sha required"});t.json({ok:!0});let o=n.box.cwd?$e.resolve(n.box.cwd):null,s=typeof e.body?.cwd=="string"&&e.body.cwd?$e.resolve(e.body.cwd):null,a=o&&s&&(s===o||s.startsWith(o+$e.sep))?s:o;$T(n.track,n.box,r,{cwd:a}).catch(i=>console.warn(`[rework] ${n.track.id}: recordCommit failed: ${i?.message||i}`))}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});_.post("/api/tracks/:id/rework-cause",async(e,t)=>{try{let n=await de(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let{sha:r,cause:o}=e.body||{};t.json({ok:!0,event:await OT(n,String(r||""),String(o||""))})}catch(n){let r=n?.message||String(n);t.status(/unknown rework/i.test(r)?404:/already answered/i.test(r)?409:400).json({error:r})}});_.post("/api/tracks/:id/rework-cause/retract",async(e,t)=>{try{let n=await de(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,event:await MT(n,String(e.body?.sha||""))})}catch(n){let r=n?.message||String(n);t.status(/no standing answer/i.test(r)?409:400).json({error:r})}});_.get("/api/usage/rework",async(e,t)=>{try{t.json(await BR(await ke(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.post("/api/telemetry",async(e,t)=>{try{if(!kM(ri.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await bM(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});_.get("/api/repos",async(e,t)=>{try{let n=await nr(),r=new Set((await Promise.all(n.map(a=>Oe(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await gi()).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)})}});_.get("/api/github/token",async(e,t)=>{try{t.json(await Fs(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});_.get("/api/github/status",(e,t)=>{t.json({configured:Kl(),calls:VA(),latency:XA(),webhook:HO()})});_.post("/api/github/event",Jt.json({limit:"64kb"}),(e,t)=>{let n=FO(e.body,{onAccept:r=>aM(r)});if(!n.ok)return t.status(400).json({error:n.error});t.json({ok:!0,duplicate:n.duplicate})});_.post("/api/github/refresh",(e,t)=>{Ik(),t.json({ok:!0})});_.get("/api/gmail/search",async(e,t)=>{try{let n=await VR(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)})}});_.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await zR(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});async function Cl({repo:e=null,number:t=null,branch:n=null}){let r=e?await Oe(e):null;if(r&&t!=null){let s=await pt(r,t),a=s&&await de(s.trackId);if(a)return a}if(!n)return null;let o=await ke();return Sd(o,n,s=>Ad(s,{branch:n,repo:e,slug:r}))}async function ZM({repo:e=null,number:t=null,branch:n=null}){let r=await Cl({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!Le(r))return r;if(!n)return null;let o=e?await Oe(e).catch(()=>null):null,s=await ke();return Sd(s,n,a=>!Le(a)&&Ad(a,{branch:n,repo:e,slug:o}))}async function En(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Cl({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await Oe(s).catch(()=>null):null);await N({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 mV(e){let t=(await ke()).filter(n=>!Le(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await Oe(n.path);if(r)for(let o of n.prs){if(await pt(r,o.number))continue;let s=await Sd(t,o.headRefName,a=>Ad(a,{branch:o.headRefName,repo:n.path,slug:r}));s&&await Ut({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}_.get("/api/git/prs",async(e,t)=>{try{let n=await oT();await mV(n.repos).catch(()=>{}),await lf(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.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:d}=e.body||{},p=null,f=null,w=null;try{if(s){let b=o||"merge";if(!lc.has(b))throw new Error(`invalid merge method: ${b}`);let k=Number(r);if(!Number.isInteger(k)||k<=0)throw new Error("invalid PR number");let S=await Oe((await fr(n)).path);if(!S)throw new Error("repo has no GitHub remote");let $=await Cl({repo:n,number:k,branch:l}).catch(()=>null);if(!$)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(p=$,f=await tp($,"merge",{count:1,summary:`arming auto-merge on PR #${k}`}),a&&await uc(n,k),await Ut({repo:S,number:k,trackId:$.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await sc(S,k,{merge:{method:b}}))throw new Error("couldn't record the auto-merge arm");return await ns(S,k,i===!0),await rs(S,k,c===!0&&i!==!0),en(),await En("auto-armed",{number:k,url:d,title:u,branch:l,repo:n,track:$,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:k,auto:!0})}p=await Cl({repo:n,number:r,branch:l}).catch(()=>null),p&&(f=await tp(p,"merge",{count:1,summary:`merging PR #${r}`}));let m=await xa(n,r,{method:o||"squash"});await En("merged",{number:m.number,url:d,title:u,branch:l,repo:n});let g=await Oe(n).catch(()=>null),y=!1;if(g&&(i===!0&&await ns(g,m.number,!0).catch(()=>{}),c===!0&&i!==!0&&await rs(g,m.number,!0).catch(()=>{}),await wr(g,m.number).catch(()=>{}),await xs(g,m.number).catch(()=>{}),y=await hr(g,m.number).catch(()=>!1),await oo(g,m.number,"merged").catch(()=>{})),i===!0&&!y){let b=await Cl({repo:n,number:m.number,branch:l}).catch(()=>null);b&&!b.archivedAt&&await ks(b.id).catch(()=>{})}t.json(m)}catch(m){w=m;let g=m?.message||String(m),y=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(g)?400:502;t.status(y).json({error:g})}finally{p&&f&&await Ag(p,f,w?"failed":"completed",w).catch(()=>{})}});_.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 Oe((await fr(n)).path).catch(()=>null),l=c?await ac(c,i).catch(()=>null):null;try{await zh(n,i)}catch(u){if(!l)throw u}en(),await En("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})}});_.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await Oe(n).catch(()=>null),s=await lT(n,{method:r||"merge",onMerged:a=>{En("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&wr(o,a.number).catch(()=>{}).then(()=>xs(o,a.number)).catch(()=>{}).then(()=>hr(o,a.number)).catch(()=>{}).then(()=>oo(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})}});_.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:cT()})});_.post("/api/git/prs/merge-track",async(e,t)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=e.body||{},i=null,c=null,l=null;try{let u=a||"merge";if(!lc.has(u))throw new Error(`invalid merge method: ${u}`);if(!n)return t.status(400).json({error:"trackId required"});let d=await de(n);if(!d)return t.status(404).json({error:`Track ${n} isn't on this node`});i=d;let p=(await hn()).filter(m=>m.trackId===d.id&&m.state!=="merged"&&m.state!=="closed").sort(dc);if(!p.length)return t.status(409).json({error:"no open PRs on this Track"});c=await tp(d,"merge",{count:p.length,summary:p.length===1?"merging 1 PR":`merging ${p.length} PRs`});for(let m of p)await ns(m.repo,m.number,o===!0).catch(()=>{}),await rs(m.repo,m.number,s===!0&&o!==!0).catch(()=>{});let f=[],w=[];for(let m of p){let g={repo:m.repo,number:m.number,ok:!0,action:"armed"};f.push(g);try{let y=await ss(m.repo);if(!y)throw new Error(`no local checkout for ${m.repo}`);let b=await ka(y,m.number);w.push({r:g,l:m,repoPath:y,pr:b,plan:sT(b,{update:r!==!1})})}catch(y){g.ok=!1,g.action="failed",g.reason=y?.message||String(y)}}for(let{r:m,l:g,repoPath:y,pr:b,plan:k}of aT(w))try{if(k.action==="skip")m.action="skipped",m.reason=k.reason;else if(k.action==="merge")await xa(y,g.number,{method:u}),await En("merged",{number:g.number,url:b.url,title:b.title,branch:b.headRefName,repo:y,track:d}),await wr(g.repo,g.number).catch(()=>{}),await xs(g.repo,g.number).catch(()=>{}),await hr(g.repo,g.number).catch(()=>{}),await oo(g.repo,g.number,"merged").catch(()=>{}),m.action="merged";else{if(k.action==="update-arm"&&(await uc(y,g.number).catch(()=>{}),m.action="updated-armed"),!await sc(g.repo,g.number,{merge:{method:u}}))throw new Error("couldn't record the auto-merge arm");await En("auto-armed",{number:g.number,url:b.url,title:b.title,branch:b.headRefName,repo:y,track:d,...m.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(S){m.ok=!1,m.action="failed",m.reason=S?.message||String(S)}if(en(),!f.some(m=>m.ok)){if(o===!0)for(let m of p)await ns(m.repo,m.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let m of p)await rs(m.repo,m.number,!1).catch(()=>{});return t.status(502).json({error:f.map(m=>`${m.repo}#${m.number}: ${m.reason}`).join("; "),results:f})}t.json({ok:!0,trackId:d.id,results:f})}catch(u){l=u;let d=u?.message||String(u),p=/invalid|required/i.test(d)?400:502;t.status(p).json({error:d})}finally{i&&c&&await Ag(i,c,l?"failed":"completed",l).catch(()=>{})}});_.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 de(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await hn()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await ac(a.repo,a.number).catch(()=>null),c=await ss(a.repo).catch(()=>null);c&&await zh(c,a.number).catch(()=>{}),i&&(await En("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}))}en(),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})}});_.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 de(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=jt(i.workspace),d,p=Array.isArray(a)?a.filter(Boolean):[];if(p.length)d=p.map(m=>{let g=u.find(y=>y.name===m||y.repo===m||y.base===m||y.dir===m);if(!g)throw new Error(`invalid repo "${m}" \u2014 not tagged on this Track`);return g});else if(d=await ps(i),!d.length)return t.status(409).json({error:"no tagged repo has work to ship"});let f=[];for(let m of d)try{let g=await Yh(m.dir,{title:r,body:o,base:s}),y=m.repo||await Oe(m.dir).catch(()=>null);y&&g.number&&await Ut({repo:y,number:g.number,trackId:i.id,url:g.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await En("opened",{number:g.number,url:g.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:y??m.name}}),f.push({repo:m.name,slug:y??null,ok:!0,url:g.url,number:g.number})}catch(g){f.push({repo:m.name,slug:m.repo??null,ok:!1,error:g?.message||String(g)})}let w=f.filter(m=>m.ok);return w.length?t.json({ok:!0,multi:!0,results:f,url:w[0].url,number:w[0].number}):t.status(502).json({error:f.map(m=>`${m.repo}: ${m.error}`).join("; "),results:f})}let c=await Yh(i.cwd,{title:r,body:o,base:s}),l=await Oe(i.cwd).catch(()=>null);l&&c.number&&await Ut({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await En("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})}});_.get("/api/tracks/:id/repos",async(e,t)=>{try{let n=await de(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await ps(n),o=new Map(r.map(a=>[a.base,a])),s=jt(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)})}});_.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 gp(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})}});_.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await yp(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})}});_.post("/api/tracks/:id/reshape",async(e,t)=>{let{repos:n,skipPermissions:r}=e.body||{};try{let o=await ty(e.params.id,n,{skipPermissions:r!==!1});t.status(o.finishedAt?200:202).json({ok:!0,...ek(o)})}catch(o){let s=o?.message||String(o),a=o?.status||(/not found/i.test(s)?404:/has work|still running|already|running|archived|last repo/i.test(s)?409:/required|not a usable|tagged twice/i.test(s)?400:502);t.status(a).json({error:s})}});_.get("/api/tracks/:id/reshape",(e,t)=>{t.json(ek(Zc(e.params.id))??{running:!1})});function ek(e){return e?{running:!e.finishedAt,step:e.step,error:e.error??null,inPlace:e.inPlace===!0,added:e.added??[],removed:e.removed??[],scratch:e.scratch===!0,views:e.views??0,checkpoint:e.checkpoint??null,startedAt:e.startedAt??null,finishedAt:e.finishedAt??null}:null}_.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await ka(n,r),s=await Oe(n).catch(()=>null),a=await ZM({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=jt(a.workspace).find(y=>y.base===n||s&&y.repo===s)??null,c=a.workspace?.kind==="multi",l=await uT(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?$e.join("ci",i.name):"ci",d=$e.join(a.cwd,".bostrat",u),p=$e.join(".bostrat",u,`pr-${o.number}-fail.log`);await St.promises.mkdir(d,{recursive:!0}),await St.promises.writeFile($e.join(a.cwd,p),l.map(y=>`\u2550\u2550\u2550\u2550 ${y.name}${y.link?` \xB7 ${y.link}`:""} \u2550\u2550\u2550\u2550
|
|
791
|
+
`)}catch{t.status(204).end()}});_.post("/api/git/committed",async(e,t)=>{try{let n=await Xc(e.get(tr));if(!n?.track||!n?.box)return t.json({ok:!1,error:"unknown hook token"});let r=String(e.body?.sha||"").trim();if(!/^[0-9a-f]{7,40}$/i.test(r))return t.json({ok:!1,error:"sha required"});t.json({ok:!0});let o=n.box.cwd?$e.resolve(n.box.cwd):null,s=typeof e.body?.cwd=="string"&&e.body.cwd?$e.resolve(e.body.cwd):null,a=o&&s&&(s===o||s.startsWith(o+$e.sep))?s:o;OT(n.track,n.box,r,{cwd:a}).catch(i=>console.warn(`[rework] ${n.track.id}: recordCommit failed: ${i?.message||i}`))}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});_.post("/api/tracks/:id/rework-cause",async(e,t)=>{try{let n=await pe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let{sha:r,cause:o}=e.body||{};t.json({ok:!0,event:await CT(n,String(r||""),String(o||""))})}catch(n){let r=n?.message||String(n);t.status(/unknown rework/i.test(r)?404:/already answered/i.test(r)?409:400).json({error:r})}});_.post("/api/tracks/:id/rework-cause/retract",async(e,t)=>{try{let n=await pe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,event:await NT(n,String(e.body?.sha||""))})}catch(n){let r=n?.message||String(n);t.status(/no standing answer/i.test(r)?409:400).json({error:r})}});_.get("/api/usage/rework",async(e,t)=>{try{t.json(await FR(await ke(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.post("/api/telemetry",async(e,t)=>{try{if(!SM(oi.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await vM(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});_.get("/api/repos",async(e,t)=>{try{let n=await nr(),r=new Set((await Promise.all(n.map(a=>Oe(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await yi()).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)})}});_.get("/api/github/token",async(e,t)=>{try{t.json(await Fs(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});_.get("/api/github/status",(e,t)=>{t.json({configured:Kl(),calls:JA(),latency:QA(),webhook:GO()})});_.post("/api/github/event",Jt.json({limit:"64kb"}),(e,t)=>{let n=WO(e.body,{onAccept:r=>lM(r)});if(!n.ok)return t.status(400).json({error:n.error});t.json({ok:!0,duplicate:n.duplicate})});_.post("/api/github/refresh",(e,t)=>{$k(),t.json({ok:!0})});_.get("/api/gmail/search",async(e,t)=>{try{let n=await YR(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)})}});_.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await XR(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});async function Cl({repo:e=null,number:t=null,branch:n=null}){let r=e?await Oe(e):null;if(r&&t!=null){let s=await pt(r,t),a=s&&await pe(s.trackId);if(a)return a}if(!n)return null;let o=await ke();return Ad(o,n,s=>Td(s,{branch:n,repo:e,slug:r}))}async function tC({repo:e=null,number:t=null,branch:n=null}){let r=await Cl({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!Le(r))return r;if(!n)return null;let o=e?await Oe(e).catch(()=>null):null,s=await ke();return Ad(s,n,a=>!Le(a)&&Td(a,{branch:n,repo:e,slug:o}))}async function En(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Cl({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await Oe(s).catch(()=>null):null);await N({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 yV(e){let t=(await ke()).filter(n=>!Le(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await Oe(n.path);if(r)for(let o of n.prs){if(await pt(r,o.number))continue;let s=await Ad(t,o.headRefName,a=>Td(a,{branch:o.headRefName,repo:n.path,slug:r}));s&&await Ut({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}_.get("/api/git/prs",async(e,t)=>{try{let n=await aT();await yV(n.repos).catch(()=>{}),await uf(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.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:d}=e.body||{},p=null,f=null,w=null;try{if(s){let b=o||"merge";if(!uc.has(b))throw new Error(`invalid merge method: ${b}`);let k=Number(r);if(!Number.isInteger(k)||k<=0)throw new Error("invalid PR number");let S=await Oe((await fr(n)).path);if(!S)throw new Error("repo has no GitHub remote");let $=await Cl({repo:n,number:k,branch:l}).catch(()=>null);if(!$)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(p=$,f=await np($,"merge",{count:1,summary:`arming auto-merge on PR #${k}`}),a&&await dc(n,k),await Ut({repo:S,number:k,trackId:$.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await ac(S,k,{merge:{method:b}}))throw new Error("couldn't record the auto-merge arm");return await ns(S,k,i===!0),await rs(S,k,c===!0&&i!==!0),en(),await En("auto-armed",{number:k,url:d,title:u,branch:l,repo:n,track:$,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:k,auto:!0})}p=await Cl({repo:n,number:r,branch:l}).catch(()=>null),p&&(f=await np(p,"merge",{count:1,summary:`merging PR #${r}`}));let m=await xa(n,r,{method:o||"squash"});await En("merged",{number:m.number,url:d,title:u,branch:l,repo:n});let g=await Oe(n).catch(()=>null),y=!1;if(g&&(i===!0&&await ns(g,m.number,!0).catch(()=>{}),c===!0&&i!==!0&&await rs(g,m.number,!0).catch(()=>{}),await wr(g,m.number).catch(()=>{}),await xs(g,m.number).catch(()=>{}),y=await hr(g,m.number).catch(()=>!1),await oo(g,m.number,"merged").catch(()=>{})),i===!0&&!y){let b=await Cl({repo:n,number:m.number,branch:l}).catch(()=>null);b&&!b.archivedAt&&await ks(b.id).catch(()=>{})}t.json(m)}catch(m){w=m;let g=m?.message||String(m),y=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(g)?400:502;t.status(y).json({error:g})}finally{p&&f&&await Ag(p,f,w?"failed":"completed",w).catch(()=>{})}});_.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 Oe((await fr(n)).path).catch(()=>null),l=c?await ic(c,i).catch(()=>null):null;try{await zh(n,i)}catch(u){if(!l)throw u}en(),await En("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})}});_.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await Oe(n).catch(()=>null),s=await dT(n,{method:r||"merge",onMerged:a=>{En("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&wr(o,a.number).catch(()=>{}).then(()=>xs(o,a.number)).catch(()=>{}).then(()=>hr(o,a.number)).catch(()=>{}).then(()=>oo(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})}});_.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:uT()})});_.post("/api/git/prs/merge-track",async(e,t)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=e.body||{},i=null,c=null,l=null;try{let u=a||"merge";if(!uc.has(u))throw new Error(`invalid merge method: ${u}`);if(!n)return t.status(400).json({error:"trackId required"});let d=await pe(n);if(!d)return t.status(404).json({error:`Track ${n} isn't on this node`});i=d;let p=(await hn()).filter(m=>m.trackId===d.id&&m.state!=="merged"&&m.state!=="closed").sort(pc);if(!p.length)return t.status(409).json({error:"no open PRs on this Track"});c=await np(d,"merge",{count:p.length,summary:p.length===1?"merging 1 PR":`merging ${p.length} PRs`});for(let m of p)await ns(m.repo,m.number,o===!0).catch(()=>{}),await rs(m.repo,m.number,s===!0&&o!==!0).catch(()=>{});let f=[],w=[];for(let m of p){let g={repo:m.repo,number:m.number,ok:!0,action:"armed"};f.push(g);try{let y=await ss(m.repo);if(!y)throw new Error(`no local checkout for ${m.repo}`);let b=await ka(y,m.number);w.push({r:g,l:m,repoPath:y,pr:b,plan:iT(b,{update:r!==!1})})}catch(y){g.ok=!1,g.action="failed",g.reason=y?.message||String(y)}}for(let{r:m,l:g,repoPath:y,pr:b,plan:k}of cT(w))try{if(k.action==="skip")m.action="skipped",m.reason=k.reason;else if(k.action==="merge")await xa(y,g.number,{method:u}),await En("merged",{number:g.number,url:b.url,title:b.title,branch:b.headRefName,repo:y,track:d}),await wr(g.repo,g.number).catch(()=>{}),await xs(g.repo,g.number).catch(()=>{}),await hr(g.repo,g.number).catch(()=>{}),await oo(g.repo,g.number,"merged").catch(()=>{}),m.action="merged";else{if(k.action==="update-arm"&&(await dc(y,g.number).catch(()=>{}),m.action="updated-armed"),!await ac(g.repo,g.number,{merge:{method:u}}))throw new Error("couldn't record the auto-merge arm");await En("auto-armed",{number:g.number,url:b.url,title:b.title,branch:b.headRefName,repo:y,track:d,...m.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(S){m.ok=!1,m.action="failed",m.reason=S?.message||String(S)}if(en(),!f.some(m=>m.ok)){if(o===!0)for(let m of p)await ns(m.repo,m.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let m of p)await rs(m.repo,m.number,!1).catch(()=>{});return t.status(502).json({error:f.map(m=>`${m.repo}#${m.number}: ${m.reason}`).join("; "),results:f})}t.json({ok:!0,trackId:d.id,results:f})}catch(u){l=u;let d=u?.message||String(u),p=/invalid|required/i.test(d)?400:502;t.status(p).json({error:d})}finally{i&&c&&await Ag(i,c,l?"failed":"completed",l).catch(()=>{})}});_.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 pe(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await hn()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await ic(a.repo,a.number).catch(()=>null),c=await ss(a.repo).catch(()=>null);c&&await zh(c,a.number).catch(()=>{}),i&&(await En("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}))}en(),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})}});_.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 pe(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=jt(i.workspace),d,p=Array.isArray(a)?a.filter(Boolean):[];if(p.length)d=p.map(m=>{let g=u.find(y=>y.name===m||y.repo===m||y.base===m||y.dir===m);if(!g)throw new Error(`invalid repo "${m}" \u2014 not tagged on this Track`);return g});else if(d=await ps(i),!d.length)return t.status(409).json({error:"no tagged repo has work to ship"});let f=[];for(let m of d)try{let g=await Yh(m.dir,{title:r,body:o,base:s}),y=m.repo||await Oe(m.dir).catch(()=>null);y&&g.number&&await Ut({repo:y,number:g.number,trackId:i.id,url:g.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await En("opened",{number:g.number,url:g.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:y??m.name}}),f.push({repo:m.name,slug:y??null,ok:!0,url:g.url,number:g.number})}catch(g){f.push({repo:m.name,slug:m.repo??null,ok:!1,error:g?.message||String(g)})}let w=f.filter(m=>m.ok);return w.length?t.json({ok:!0,multi:!0,results:f,url:w[0].url,number:w[0].number}):t.status(502).json({error:f.map(m=>`${m.repo}: ${m.error}`).join("; "),results:f})}let c=await Yh(i.cwd,{title:r,body:o,base:s}),l=await Oe(i.cwd).catch(()=>null);l&&c.number&&await Ut({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await En("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})}});_.get("/api/tracks/:id/repos",async(e,t)=>{try{let n=await pe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await ps(n),o=new Map(r.map(a=>[a.base,a])),s=jt(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)})}});_.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 yp(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})}});_.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await bp(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})}});_.post("/api/tracks/:id/reshape",async(e,t)=>{let{repos:n,skipPermissions:r}=e.body||{};try{let o=await ny(e.params.id,n,{skipPermissions:r!==!1});t.status(o.finishedAt?200:202).json({ok:!0,...nk(o)})}catch(o){let s=o?.message||String(o),a=o?.status||(/not found/i.test(s)?404:/has work|still running|already|running|archived|last repo/i.test(s)?409:/required|not a usable|tagged twice/i.test(s)?400:502);t.status(a).json({error:s})}});_.get("/api/tracks/:id/reshape",(e,t)=>{t.json(nk(Qc(e.params.id))??{running:!1})});function nk(e){return e?{running:!e.finishedAt,step:e.step,error:e.error??null,inPlace:e.inPlace===!0,added:e.added??[],removed:e.removed??[],scratch:e.scratch===!0,views:e.views??0,checkpoint:e.checkpoint??null,startedAt:e.startedAt??null,finishedAt:e.finishedAt??null}:null}_.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await ka(n,r),s=await Oe(n).catch(()=>null),a=await tC({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=jt(a.workspace).find(y=>y.base===n||s&&y.repo===s)??null,c=a.workspace?.kind==="multi",l=await pT(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?$e.join("ci",i.name):"ci",d=$e.join(a.cwd,".bostrat",u),p=$e.join(".bostrat",u,`pr-${o.number}-fail.log`);await St.promises.mkdir(d,{recursive:!0}),await St.promises.writeFile($e.join(a.cwd,p),l.map(y=>`\u2550\u2550\u2550\u2550 ${y.name}${y.link?` \xB7 ${y.link}`:""} \u2550\u2550\u2550\u2550
|
|
792
792
|
${y.log??"(no fetchable log \u2014 see link)"}
|
|
793
793
|
`).join(`
|
|
794
|
-
`));let f=l.map(y=>y.name).join(", "),w=c&&i?` in the ${i.name}/ checkout`:"",m=await qt(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${w}. Failing check${l.length===1?"":"s"}: ${f}. The failure log is at ${p} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),g=s;g&&(await Ut({repo:g,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await IA(g,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await En("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(y=>y.name)}}),t.json({ok:!0,leadBox:m.leadBox,queued:!!m.queued,checks:l.map(y=>y.name),log:p})}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})}});_.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await ka(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await Oe(n).catch(()=>null),a=await ZM({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=jt(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 qt(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 Ut({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await _A(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await En("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})}});_.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=ro.filter(a=>e.body.actions.includes(a)),!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;await uo(`pr:${e.params.id}:${n}`,async()=>{let a=await de(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await G(a),c=i.find(d=>d.type==="pr.offered"&&d.payload?.offerId===n);if(!c)return t.status(404).json({error:"unknown offer"});if(i.some(d=>d.type==="pr.offer.answered"&&d.payload?.offerId===n))return t.status(409).json({error:"offer already answered"});if(o?.includes("pr")){let d=await Lb(a,c,{actions:o,notify:s});if(d)return t.json({ok:!0,offerId:n,action:"pr-exists",actions:o,notify:s,existingPr:d.pr})}let l=null;o&&(l=await qt(a.id,wa(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 N({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&&zg(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:/already answered|not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});_.post("/api/tracks/:id/pr-exists",async(e,t)=>{let{offerId:n,choice:r}=e.body||{};try{let o=await de(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await Fb(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 Pl=e=>t=>({...t,url:iO(t,e.headers)});_.get("/api/views",async(e,t)=>{try{let n=await Fw({trackId:e.query.track||void 0});t.json({views:n.map(Pl(e)),maxLive:fo})}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/views/:id",async(e,t)=>{let n=await YE(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:Pl(e)(n),session:Ea(n.slug)})});_.post("/api/views",async(e,t)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=e.body||{};try{let i=n?await de(n):null;if(!i&&s&&(i=(await ke()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+$e.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!kb(e.headers))return t.status(412).json({error:L$});let c=await Hw({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:Pl(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})}});_.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:Pl(e)(await XE(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});_.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:Pl(e)(await ZE(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})}});_.delete("/api/views/:id",async(e,t)=>{try{t.json(await QE(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var hV=[["linear",As,jy],["jira",Up,Dy]],zb=new Map;_.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await Py(),r={};for(let[o,s,a]of hV){let i=await s();if(!i){zb.delete(o),r[o]={configured:!1};continue}let c=zb.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},zb.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)})}});_.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await Py()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await M$(o),i=n?await de(n):null;!i&&r&&(i=(await ke()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+$e.sep)))||null),i&&await N({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 Cs=async(e,t)=>{let n=e?await de(e):null;return!n&&t&&(n=(await ke()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+$e.sep)))||null),n};_.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await $y()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});_.get("/api/roadmap",async(e,t)=>{try{t.json(await Ry())}catch(n){t.status(502).json({error:n?.message||String(n)})}});_.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await Cs(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 Ga(o);await Bp(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})}});_.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await Cs(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await Bp(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var QM=async(e,t,n)=>{if(e)return{ref:e,track:await Cs(t,n)};let r=await Cs(t,n),o=r?await My(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}};_.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 QM(o,n,r),l=await A$({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})}});_.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await QM(e.query.circuit,e.query.track,e.query.cwd);t.json(await Oy(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})}});_.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l}=e.body||{};try{let u=await Cs(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=await I$(u,{title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l});t.json({ok:!0,trackId:u.id,...d})}catch(u){let d=u?.message||String(u);t.status(/required/i.test(d)?400:502).json({error:d})}});_.get("/api/decisions",async(e,t)=>{try{let n=await Cs(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await _$(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})}});_.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 Cs(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await My(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 T$(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})}});_.post("/api/tickets/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=n?await de(n):null;if(!s&&r&&(s=(await ke()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+$e.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 Ja(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})}});_.get("/api/tickets/status",async(e,t)=>{let{cwd:n,track:r}=e.query;try{let o=r?await de(r):null;if(!o&&n&&(o=(await ke()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+$e.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 nR(),ticket:await Qn(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});_.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await Qn(e.params.id)})});_.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await de(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await Ja(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})}});_.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await Qy(e.params.id)})});var bf=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;_.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await aR(e.params.id))}catch(n){let r=n?.message||String(n);t.status(bf(r)).json({error:r})}});_.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await eb(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(bf(r)).json({error:r})}});_.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await iR(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(bf(r)).json({error:r})}});_.post("/api/tracks/:id/ticket-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{let o=await de(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await cR(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(bf(s)).json({error:s})}});_.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await py(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})}});_.post("/api/tickets/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await de(n):null;if(!s&&r&&(s=(await ke()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+$e.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 uR(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})}});_.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await lR()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});_.post("/api/tracks/:id/ticket-create-offer",async(e,t)=>{let{proposalId:n,accept:r,teamId:o}=e.body||{};try{let s=await de(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await dR(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})}});_.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await Cc({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/mocks/:id",async(e,t)=>{let n=await xr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var Dl={dotfiles:"allow"};_.get("/api/mocks/:id/png",async(e,t)=>{let n=await xr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=yn(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Oc(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,Dl,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});_.get("/api/mocks/:id/media",async(e,t)=>{let n=await xr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=yn(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Oc(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,Dl,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});_.get("/api/mocks/:id/html",async(e,t)=>{let n=await xr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=yn(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(nn(n,r.v).html,Dl,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});_.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&Vd(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 d=a?await de(a):null;if(!d&&c&&(d=(await ke()).find(m=>m.cwd&&(c===m.cwd||c.startsWith(m.cwd+$e.sep)))||null),!d)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let p=(d.boxes||[]).find(w=>w.name===d.leadBox),f=await mI({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:p?.agent||null});t.json({ok:!0,mock:f})}catch(d){let p=d?.message||String(d);t.status(/required|invalid|must be/i.test(p)?400:500).json({error:p})}});_.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&Vd(n)?n:null;try{let o=await hI(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})}});_.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await yI(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await qt(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})}});_.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await DI(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await bI(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})}});_.get("/api/flows",async(e,t)=>{try{t.json({flows:await OI({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/flows/:id",async(e,t)=>{let n=await MI(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});_.post("/api/flows",async(e,t)=>{let{trackId:n,name:r,steps:o,cwd:s}=e.body||{};try{let a=n?await de(n):null;if(!a&&s&&(a=(await ke()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+$e.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 CI({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})}});_.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await NI(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})}});_.delete("/api/flows/:id",async(e,t)=>{try{t.json(await PI(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});_.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await ke(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+$e.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await qI({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)})}});_.get("/api/runbooks/:id",async(e,t)=>{let n=await rg(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});_.get("/api/runbooks/:id/md",async(e,t)=>{let n=await rg(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=HI(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 GI(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(Tr(n,r.v),Dl,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});_.get("/api/loops",async(e,t)=>{t.json(await ec())});_.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 ke()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+$e.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await ec();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 Qm({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})});_.post("/api/loops/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await de(n):null;if(!s&&r&&(s=(await ke()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+$e.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 mO(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var si=async(e,t)=>{let n=e?await de(e):null;return!n&&t&&(n=(await ke()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+$e.sep)))||null),n};_.post("/api/followups/offer",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await si(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,result:await uE(s,o,{ship:{changedRepos:ps}})})}catch(s){t.status(400).json({error:s?.message||String(s)})}});_.post("/api/followups/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await si(n,r);if(!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 pE(s,o)})}catch(s){let a=s?.message||String(s);t.status(/unreachable/i.test(a)?503:400).json({error:a})}});_.get("/api/followups",async(e,t)=>{let n=typeof e.query?.cwd=="string"?e.query.cwd:null,r=await si(null,n).catch(()=>null);t.json({ok:!0,trackId:r?.id??null,triggers:gw,...await gE(r)})});var FM=new Set(["reconcile","fixing-ci","rebase","merge"]);_.get("/api/status",async(e,t)=>{let n=await si(null,typeof e.query?.cwd=="string"?e.query.cwd:null).catch(()=>null);if(!n)return t.status(400).json({error:"a cwd inside a Track workspace is required"});t.json({ok:!0,trackId:n.id,status:n.status})});_.post("/api/status",async(e,t)=>{try{let{trackId:n,cwd:r,phase:o,summary:s,scope:a,reason:i}=e.body||{},c=await si(n,r);if(!c)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let l=o==="fix-ci"?"fixing-ci":o;if(l!=null&&!FM.has(l))throw new Error(`phase must be one of ${[...FM].join(" | ")}`);let u=s==null?null:String(s).trim();if(u&&u.length>120)throw new Error("summary is too long (cap is 120 characters)");let d=cV();await N(c,{type:"track.phase.changed",actor:"agent",source:"cli",boxId:c.leadBox,provider:c.boxes?.find(p=>p.name===c.leadBox)?.agent??null,payload:{ts:Date.now(),phase:l||null,phaseId:d,summary:u,scope:a&&typeof a=="object"?a:null,...l?{}:{reason:i||"completed"}}}),t.json({ok:!0,trackId:c.id,phase:l||null,phaseId:d})}catch(n){t.status(400).json({error:n?.message||String(n)})}});_.post("/api/tracks/:id/followup-offer",async(e,t)=>{let{offerId:n,followupId:r,armId:o,promoted:s,all:a}=e.body||{};try{let i=await de(e.params.id);if(!i)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await mE(i,{offerId:n,followupId:r||null,armId:o||null,promoted:s||null,all:a===!0},{sayToLead:qt,unarchiveTrack:xp})})}catch(i){let c=i?.message||String(i),l=i?.code==="removed"?410:/unknown offer/i.test(c)?404:/already answered/i.test(c)?409:/required|not on this card|nothing to run together/i.test(c)?400:/unreachable/i.test(c)?503:502;t.status(l).json({error:c})}});_.post("/api/tracks/:id/followup-arm-cancel",async(e,t)=>{try{let n=await de(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await dE(n,e.body?.armId)})}catch(n){let r=n?.message||String(n);t.status(/unknown/i.test(r)?404:400).json({error:r})}});_.post("/api/tracks/:id/followup-create-offer",async(e,t)=>{let{proposalId:n,accept:r,followupId:o}=e.body||{};try{let s=await de(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await fE(s,n,r===!0,{followupId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal/i.test(a)?404:/already answered/i.test(a)?409:400).json({error:a})}});Xe.on("event",e=>{let t=e?.type==="pr.state.changed"?e.payload?.change:null;if(!t||!e.trackId)return;let n=t==="opened"?"pr.opened":t==="ci-fixed"?"checks.green":null,r=e.payload||{},o={repo:r.repo??null,prNumber:r.number??null,prTitle:r.title??null,prUrl:r.url??null};!n&&t!=="pushed"||de(e.trackId).then(async s=>{if(s&&(n&&await yc(s,n,o),t==="opened"||t==="pushed")){let a=wE(await G(s).catch(()=>[]));a&&await yc(s,"ship.done",{...o,shipOfferId:a})}}).catch(()=>{})});var eC=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await de(n):null;if(!s&&r&&(s=(await ke()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+$e.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"}),null;let a=await ec();if(a.source!=="plane")return t.status(503).json({error:a.source==="unconfigured"?"this node isn't linked to a plane \u2014 loops live in the plane":`plane unreachable (${a.error||"no detail"})`}),null;let i;try{i=gO(a.loops,o)}catch(c){return t.status(/matches \d+ loops/.test(c.message)?409:404).json({error:c.message}),null}return{track:s,loop:i}};_.post("/api/loops/propose-update",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await eC(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await yO(s.track,s.loop,e.body?.patch)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});_.post("/api/loops/propose-delete",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await eC(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await bO(s.track,s.loop,e.body?.reason)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});_.post("/api/planning/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o,scope:s}=e.body||{},a=await si(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i;try{i=await Ry()}catch(c){return t.status(503).json({error:c?.message||String(c)})}try{let c=s==="circuit"?Eb:Sl;t.json({ok:!0,trackId:a.id,proposal:await RO(a,o,i,{allow:c})})}catch(c){t.status(400).json({error:c?.message||String(c)})}});var tk=e=>/unknown proposal/i.test(e)?404:/already answered/i.test(e)?409:400;_.post("/api/tracks/:id/planning-offer",async(e,t)=>{let{proposalId:n,action:r,results:o}=e.body||{};try{let s=await de(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await OO(s,n,r,{results:o||[]})})}catch(s){let a=s?.message||String(s);t.status(tk(a)).json({error:a})}});_.post("/api/tracks/:id/loop-update-offer",async(e,t)=>{let{proposalId:n,action:r,nextFireAt:o}=e.body||{};try{let s=await de(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await xO(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(tk(a)).json({error:a})}});_.post("/api/tracks/:id/loop-delete-offer",async(e,t)=>{let{proposalId:n,action:r}=e.body||{};try{let o=await de(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await vO(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(tk(s)).json({error:s})}});_.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 de(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await hO(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})}});_.post("/api/runbooks",async(e,t)=>{let{trackId:n,name:r,md:o,cwd:s}=e.body||{};try{let a=n?await de(n):null;if(!a&&s&&(a=(await ke()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+$e.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 KI({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})}});_.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await VI(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})}});_.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await zI(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})}});_.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await JI(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});_.get("/api/changelog/draft",async(e,t)=>{try{let n=DO(e.query.since||null);t.json({since:n,repos:_b(),...await BO(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var tC=async({trackId:e,cwd:t})=>{let n=e?await de(e):null;return!n&&t&&(n=(await ke()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+$e.sep)))||null),n};_.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=await tC({trackId:o,cwd:s});if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,entry:await jO(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|no `- `|over the|secret VALUE|mentions "/i.test(i)?400:500).json({error:i})}});_.post("/api/changelog/retract",async(e,t)=>{let{day:n,trackId:r,cwd:o}=e.body||{};try{let s=await tC({trackId:r,cwd:o});if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,entry:await LO(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var wV=120,Jb=400,oi=null,UM=null;_.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>Jb)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${Jb} cap; a notification is a nudge, put the detail in chat`});let c=Nc(`${n||""}
|
|
795
|
-
${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
|
|
794
|
+
`));let f=l.map(y=>y.name).join(", "),w=c&&i?` in the ${i.name}/ checkout`:"",m=await qt(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${w}. Failing check${l.length===1?"":"s"}: ${f}. The failure log is at ${p} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),g=s;g&&(await Ut({repo:g,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await $A(g,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await En("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(y=>y.name)}}),t.json({ok:!0,leadBox:m.leadBox,queued:!!m.queued,checks:l.map(y=>y.name),log:p})}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})}});_.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await ka(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await Oe(n).catch(()=>null),a=await tC({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=jt(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 qt(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 Ut({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await RA(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await En("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})}});_.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=ro.filter(a=>e.body.actions.includes(a)),!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;await uo(`pr:${e.params.id}:${n}`,async()=>{let a=await pe(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await G(a),c=i.find(d=>d.type==="pr.offered"&&d.payload?.offerId===n);if(!c)return t.status(404).json({error:"unknown offer"});if(i.some(d=>d.type==="pr.offer.answered"&&d.payload?.offerId===n))return t.status(409).json({error:"offer already answered"});if(o?.includes("pr")){let d=await Ub(a,c,{actions:o,notify:s});if(d)return t.json({ok:!0,offerId:n,action:"pr-exists",actions:o,notify:s,existingPr:d.pr})}let l=null;o&&(l=await qt(a.id,wa(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 N({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&&Jg(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:/already answered|not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});_.post("/api/tracks/:id/pr-exists",async(e,t)=>{let{offerId:n,choice:r}=e.body||{};try{let o=await pe(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await Hb(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 Pl=e=>t=>({...t,url:uO(t,e.headers)});_.get("/api/views",async(e,t)=>{try{let n=await Fw({trackId:e.query.track||void 0});t.json({views:n.map(Pl(e)),maxLive:fo})}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/views/:id",async(e,t)=>{let n=await ZE(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:Pl(e)(n),session:Ea(n.slug)})});_.post("/api/views",async(e,t)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=e.body||{};try{let i=n?await pe(n):null;if(!i&&s&&(i=(await ke()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+$e.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!vb(e.headers))return t.status(412).json({error:H$});let c=await Hw({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:Pl(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})}});_.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:Pl(e)(await QE(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});_.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:Pl(e)(await eI(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})}});_.delete("/api/views/:id",async(e,t)=>{try{t.json(await tI(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var bV=[["linear",As,Fy],["jira",Hp,jy]],Yb=new Map;_.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await By(),r={};for(let[o,s,a]of bV){let i=await s();if(!i){Yb.delete(o),r[o]={configured:!1};continue}let c=Yb.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},Yb.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)})}});_.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await By()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await P$(o),i=n?await pe(n):null;!i&&r&&(i=(await ke()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+$e.sep)))||null),i&&await N({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 Cs=async(e,t)=>{let n=e?await pe(e):null;return!n&&t&&(n=(await ke()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+$e.sep)))||null),n};_.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await Oy()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});_.get("/api/roadmap",async(e,t)=>{try{t.json(await My())}catch(n){t.status(502).json({error:n?.message||String(n)})}});_.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await Cs(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 Ka(o);await jp(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})}});_.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await Cs(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await jp(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var nC=async(e,t,n)=>{if(e)return{ref:e,track:await Cs(t,n)};let r=await Cs(t,n),o=r?await Ny(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}};_.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 nC(o,n,r),l=await I$({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})}});_.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await nC(e.query.circuit,e.query.track,e.query.cwd);t.json(await Cy(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})}});_.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l}=e.body||{};try{let u=await Cs(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=await R$(u,{title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l});t.json({ok:!0,trackId:u.id,...d})}catch(u){let d=u?.message||String(u);t.status(/required/i.test(d)?400:502).json({error:d})}});_.get("/api/decisions",async(e,t)=>{try{let n=await Cs(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await O$(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})}});_.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 Cs(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await Ny(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 _$(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})}});_.post("/api/tickets/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=n?await pe(n):null;if(!s&&r&&(s=(await ke()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+$e.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 Ya(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})}});_.get("/api/tickets/status",async(e,t)=>{let{cwd:n,track:r}=e.query;try{let o=r?await pe(r):null;if(!o&&n&&(o=(await ke()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+$e.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 sR(),ticket:await Qn(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});_.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await Qn(e.params.id)})});_.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await pe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await Ya(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})}});_.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await tb(e.params.id)})});var kf=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;_.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await lR(e.params.id))}catch(n){let r=n?.message||String(n);t.status(kf(r)).json({error:r})}});_.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await nb(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(kf(r)).json({error:r})}});_.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await uR(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(kf(r)).json({error:r})}});_.post("/api/tracks/:id/ticket-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{let o=await pe(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await dR(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(kf(s)).json({error:s})}});_.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await my(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})}});_.post("/api/tickets/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await pe(n):null;if(!s&&r&&(s=(await ke()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+$e.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 fR(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})}});_.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await pR()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});_.post("/api/tracks/:id/ticket-create-offer",async(e,t)=>{let{proposalId:n,accept:r,teamId:o}=e.body||{};try{let s=await pe(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await mR(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})}});_.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await Nc({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/mocks/:id",async(e,t)=>{let n=await xr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var Dl={dotfiles:"allow"};_.get("/api/mocks/:id/png",async(e,t)=>{let n=await xr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=yn(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Mc(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,Dl,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});_.get("/api/mocks/:id/media",async(e,t)=>{let n=await xr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=yn(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Mc(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,Dl,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});_.get("/api/mocks/:id/html",async(e,t)=>{let n=await xr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=yn(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(nn(n,r.v).html,Dl,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});_.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&zd(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 d=a?await pe(a):null;if(!d&&c&&(d=(await ke()).find(m=>m.cwd&&(c===m.cwd||c.startsWith(m.cwd+$e.sep)))||null),!d)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let p=(d.boxes||[]).find(w=>w.name===d.leadBox),f=await wI({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:p?.agent||null});t.json({ok:!0,mock:f})}catch(d){let p=d?.message||String(d);t.status(/required|invalid|must be/i.test(p)?400:500).json({error:p})}});_.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&zd(n)?n:null;try{let o=await gI(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})}});_.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await kI(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await qt(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})}});_.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await jI(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await xI(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})}});_.get("/api/flows",async(e,t)=>{try{t.json({flows:await CI({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/flows/:id",async(e,t)=>{let n=await NI(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});_.post("/api/flows",async(e,t)=>{let{trackId:n,name:r,steps:o,cwd:s}=e.body||{};try{let a=n?await pe(n):null;if(!a&&s&&(a=(await ke()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+$e.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 PI({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})}});_.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await DI(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})}});_.delete("/api/flows/:id",async(e,t)=>{try{t.json(await BI(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});_.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await ke(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+$e.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await KI({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)})}});_.get("/api/runbooks/:id",async(e,t)=>{let n=await rg(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});_.get("/api/runbooks/:id/md",async(e,t)=>{let n=await rg(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=qI(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 VI(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(Tr(n,r.v),Dl,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});_.get("/api/loops",async(e,t)=>{t.json(await tc())});_.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 ke()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+$e.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await tc();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 Qm({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})});_.post("/api/loops/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await pe(n):null;if(!s&&r&&(s=(await ke()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+$e.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 gO(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var ai=async(e,t)=>{let n=e?await pe(e):null;return!n&&t&&(n=(await ke()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+$e.sep)))||null),n};_.post("/api/followups/offer",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await ai(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,result:await pE(s,o,{ship:{changedRepos:ps}})})}catch(s){t.status(400).json({error:s?.message||String(s)})}});_.post("/api/followups/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await ai(n,r);if(!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 mE(s,o)})}catch(s){let a=s?.message||String(s);t.status(/unreachable/i.test(a)?503:400).json({error:a})}});_.get("/api/followups",async(e,t)=>{let n=typeof e.query?.cwd=="string"?e.query.cwd:null,r=await ai(null,n).catch(()=>null);t.json({ok:!0,trackId:r?.id??null,triggers:gw,...await bE(r)})});var WM=new Set(["reconcile","fixing-ci","rebase","merge"]);_.get("/api/status",async(e,t)=>{let n=await ai(null,typeof e.query?.cwd=="string"?e.query.cwd:null).catch(()=>null);if(!n)return t.status(400).json({error:"a cwd inside a Track workspace is required"});t.json({ok:!0,trackId:n.id,status:n.status})});_.post("/api/status",async(e,t)=>{try{let{trackId:n,cwd:r,phase:o,summary:s,scope:a,reason:i}=e.body||{},c=await ai(n,r);if(!c)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let l=o==="fix-ci"?"fixing-ci":o;if(l!=null&&!WM.has(l))throw new Error(`phase must be one of ${[...WM].join(" | ")}`);let u=s==null?null:String(s).trim();if(u&&u.length>120)throw new Error("summary is too long (cap is 120 characters)");let d=pV();await N(c,{type:"track.phase.changed",actor:"agent",source:"cli",boxId:c.leadBox,provider:c.boxes?.find(p=>p.name===c.leadBox)?.agent??null,payload:{ts:Date.now(),phase:l||null,phaseId:d,summary:u,scope:a&&typeof a=="object"?a:null,...l?{}:{reason:i||"completed"}}}),t.json({ok:!0,trackId:c.id,phase:l||null,phaseId:d})}catch(n){t.status(400).json({error:n?.message||String(n)})}});_.post("/api/tracks/:id/followup-offer",async(e,t)=>{let{offerId:n,followupId:r,armId:o,promoted:s,all:a}=e.body||{};try{let i=await pe(e.params.id);if(!i)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await wE(i,{offerId:n,followupId:r||null,armId:o||null,promoted:s||null,all:a===!0},{sayToLead:qt,unarchiveTrack:vp})})}catch(i){let c=i?.message||String(i),l=i?.code==="removed"?410:/unknown offer/i.test(c)?404:/already answered/i.test(c)?409:/required|not on this card|nothing to run together/i.test(c)?400:/unreachable/i.test(c)?503:502;t.status(l).json({error:c})}});_.post("/api/tracks/:id/followup-arm-cancel",async(e,t)=>{try{let n=await pe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await fE(n,e.body?.armId)})}catch(n){let r=n?.message||String(n);t.status(/unknown/i.test(r)?404:400).json({error:r})}});_.post("/api/tracks/:id/followup-create-offer",async(e,t)=>{let{proposalId:n,accept:r,followupId:o}=e.body||{};try{let s=await pe(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await hE(s,n,r===!0,{followupId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal/i.test(a)?404:/already answered/i.test(a)?409:400).json({error:a})}});Xe.on("event",e=>{let t=e?.type==="pr.state.changed"?e.payload?.change:null;if(!t||!e.trackId)return;let n=t==="opened"?"pr.opened":t==="ci-fixed"?"checks.green":null,r=e.payload||{},o={repo:r.repo??null,prNumber:r.number??null,prTitle:r.title??null,prUrl:r.url??null};!n&&t!=="pushed"||pe(e.trackId).then(async s=>{if(s&&(n&&await bc(s,n,o),t==="opened"||t==="pushed")){let a=yE(await G(s).catch(()=>[]));a&&await bc(s,"ship.done",{...o,shipOfferId:a})}}).catch(()=>{})});var rC=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await pe(n):null;if(!s&&r&&(s=(await ke()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+$e.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"}),null;let a=await tc();if(a.source!=="plane")return t.status(503).json({error:a.source==="unconfigured"?"this node isn't linked to a plane \u2014 loops live in the plane":`plane unreachable (${a.error||"no detail"})`}),null;let i;try{i=kO(a.loops,o)}catch(c){return t.status(/matches \d+ loops/.test(c.message)?409:404).json({error:c.message}),null}return{track:s,loop:i}};_.post("/api/loops/propose-update",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await rC(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await xO(s.track,s.loop,e.body?.patch)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});_.post("/api/loops/propose-delete",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await rC(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await vO(s.track,s.loop,e.body?.reason)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});_.post("/api/planning/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o,scope:s}=e.body||{},a=await ai(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i;try{i=await My()}catch(c){return t.status(503).json({error:c?.message||String(c)})}try{let c=s==="circuit"?_b:Sl;t.json({ok:!0,trackId:a.id,proposal:await CO(a,o,i,{allow:c})})}catch(c){t.status(400).json({error:c?.message||String(c)})}});var rk=e=>/unknown proposal/i.test(e)?404:/already answered/i.test(e)?409:400;_.post("/api/tracks/:id/planning-offer",async(e,t)=>{let{proposalId:n,action:r,results:o}=e.body||{};try{let s=await pe(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await NO(s,n,r,{results:o||[]})})}catch(s){let a=s?.message||String(s);t.status(rk(a)).json({error:a})}});_.post("/api/tracks/:id/loop-update-offer",async(e,t)=>{let{proposalId:n,action:r,nextFireAt:o}=e.body||{};try{let s=await pe(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await AO(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(rk(a)).json({error:a})}});_.post("/api/tracks/:id/loop-delete-offer",async(e,t)=>{let{proposalId:n,action:r}=e.body||{};try{let o=await pe(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await TO(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(rk(s)).json({error:s})}});_.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 pe(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await yO(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})}});_.post("/api/runbooks",async(e,t)=>{let{trackId:n,name:r,md:o,cwd:s}=e.body||{};try{let a=n?await pe(n):null;if(!a&&s&&(a=(await ke()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+$e.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 zI({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})}});_.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await JI(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})}});_.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await YI(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})}});_.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await XI(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});_.get("/api/changelog/draft",async(e,t)=>{try{let n=LO(e.query.since||null);t.json({since:n,repos:Rb(),...await FO(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var oC=async({trackId:e,cwd:t})=>{let n=e?await pe(e):null;return!n&&t&&(n=(await ke()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+$e.sep)))||null),n};_.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=await oC({trackId:o,cwd:s});if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,entry:await UO(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|no `- `|over the|secret VALUE|mentions "/i.test(i)?400:500).json({error:i})}});_.post("/api/changelog/retract",async(e,t)=>{let{day:n,trackId:r,cwd:o}=e.body||{};try{let s=await oC({trackId:r,cwd:o});if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,entry:await HO(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var kV=120,Xb=400,si=null,qM=null;_.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>Xb)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${Xb} cap; a notification is a nudge, put the detail in chat`});let c=Pc(`${n||""}
|
|
795
|
+
${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 pe(a):null;if(!l&&s&&(l=(await ke()).find(w=>w.cwd&&(s===w.cwd||s.startsWith(w.cwd+$e.sep)))||null),!l)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(f=>f.name===l.leadBox),d=(f,w)=>String(f).replace(/\s+/g," ").trim().slice(0,w),p={title:n?d(n,kV):l.name,body:d(i,Xb),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await N(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:p}),t.json({ok:!0,notification:p,delivered:si?si.status().connected:null})}catch(i){t.status(500).json({error:i?.message||String(i)})}});_.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 BM(e.body,e.headers["content-type"]))}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});_.get("/api/dictation/glossary",async(e,t)=>{t.json({configured:!!await il(),terms:await zb(),problem:await DM()})});_.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 PM(n,r)})});_.get("/api/tracks/:id/voice-chat",async(e,t)=>{if(!await pe(e.params.id))return t.status(404).json({error:"track not found"});t.json(await jM())});_.post("/api/tracks/:id/voice-chat/session",async(e,t)=>{try{let n=await pe(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 LM())}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});_.post("/api/tracks/:id/voice-chat/usage",async(e,t)=>{try{let n=await pe(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 FM(n,{model:r,usage:o,seconds:s})})}catch(n){t.status(n?.status||500).json({error:n?.message||String(n)})}});_.post("/api/tracks",async(e,t)=>{let{name:n,cwd:r,repo:o,repos:s,agent:a,model:i,skipPermissions:c,ticket:l,circuit:u}=e.body||{};try{let d=await kp({name:n,cwd:r,repo:o,repos:s,agent:a,model:i,skipPermissions:c!==!1}),p=null;if(l)try{await Ya(d,l,{source:"create"})}catch(w){p=w?.message||String(w)}let f=null;if(u)try{await jp(d,await Ka(u),{source:"picker"})}catch(w){f=w?.message||String(w)}t.json({ok:!0,track:d,...p?{ticketWarning:p}:{},...f?{circuitWarning:f}:{}})}catch(d){let p=d?.message||String(d);t.status(/required|unknown agent|invalid|must be/i.test(p)?400:500).json({error:p})}});_.get("/api/tracks/:id",async(e,t)=>{let n=await pe(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({track:n,handoff:sC(qa(e.params.id)),reshape:nk(Qc(e.params.id)),subtasks:vs(e.params.id).map(Bl),pendingOffers:await Wg(n)})});_.delete("/api/tracks/:id",async(e,t)=>{try{let n=await iy(e.params.id,{cascadeMocks:e.query.mocks!=="0",cascadeRunbooks:e.query.runbooks==="1"});await tb(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})}});_.post("/api/tracks/:id/archive",async(e,t)=>{try{t.json({ok:!0,track:await ks(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})}});_.post("/api/tracks/:id/rename",async(e,t)=>{try{t.json({ok:!0,track:await dy(e.params.id,e.body?.name)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/loop run Tracks/i.test(r)?409:/required|too long/i.test(r)?400:500;t.status(o).json({error:r})}});_.post("/api/tracks/:id/unarchive",async(e,t)=>{try{t.json({ok:!0,track:await vp(e.params.id,{freshSession:e.body?.freshSession===!0})})}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})}});_.get("/api/tracks/:id/messages",async(e,t)=>{let n=await mp(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json(n)});_.get("/api/tracks/:id/events",async(e,t)=>{let n=await pe(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}
|
|
796
796
|
data: ${JSON.stringify(i)}
|
|
797
797
|
|
|
798
|
-
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await G(n))i.seq>o&&r(i);let s=
|
|
798
|
+
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await G(n))i.seq>o&&r(i);let s=Lk(n.id,r),a=setInterval(()=>t.write(`: hb
|
|
799
799
|
|
|
800
|
-
`),25e3);e.on("close",()=>{clearInterval(a),s()})});_.get("/api/tracks/:id/search",async(e,t)=>{let n=await
|
|
801
|
-
`);_.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await qt(e.params.id,
|
|
800
|
+
`),25e3);e.on("close",()=>{clearInterval(a),s()})});_.get("/api/tracks/:id/search",async(e,t)=>{let n=await pe(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=Yi(await G(n)),a=$u(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}))})});_.get("/api/memory",async(e,t)=>{t.json({stores:await n0(),harvest:Zk()})});_.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 r0(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 xV=["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(`
|
|
801
|
+
`);_.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await qt(e.params.id,xV)})}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})}});_.post("/api/tracks/:id/lead/restart",async(e,t)=>{try{t.json({ok:!0,track:await Sp(e.params.id,{freshSession:e.body?.freshSession===!0})})}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}:{}})}});_.post("/api/tracks/:id/say",async(e,t)=>{let{text:n,attachments:r,producer:o}=e.body||{};try{t.json(await qt(e.params.id,n,{attachments:r,producer:typeof o=="string"?o:null,dismissPrompt:!0}))}catch(s){let a=s?.message||String(s);if(s?.usageCap)return t.status(429).json({error:"usage_cap",message:a,cap:s.usageCap});if(s?.transferredTo)return t.status(409).json({error:a,transferredTo:s.transferredTo});t.status(/not found/i.test(a)?404:/archived/i.test(a)?409:/required|not running/i.test(a)?400:500).json({error:a})}});_.post("/api/tracks/:id/upload",async(e,t)=>{try{let n=await pe(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 r_(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})}});_.get("/api/tracks/:id/uploads/:file",async(e,t)=>{try{let n=await pe(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=$e.basename(String(e.params.file||""));if(!r||!ag($e.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=sg(n.cwd),s,a;try{s=await St.promises.realpath(o),a=await St.promises.realpath($e.join(o,r))}catch{return t.status(404).json({error:"upload not found"})}if(!a.startsWith(s+$e.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 vV=/^[0-9a-fA-F-]{8,64}$/;_.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(e,t)=>{try{let n=await pe(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(!vV.test(o)||!Number.isInteger(s)||s<0)return t.status(400).json({error:"bad image ref"});let a=(n.boxes||[]).find(p=>p.name===r);if(!a||a.agent!=="claude"||!a.cwd||!a.sessionId)return t.status(404).json({error:"image source not available"});let i=dn(a.cwd,a.sessionId);if(!i)return t.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=cV.createInterface({input:St.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let p of u){if(!p.includes(c))continue;let f;try{f=JSON.parse(p)}catch{continue}if(f.uuid===o){l=f,u.close();break}}let d=l?ym(l)[s]:null;if(!d||!/^image\//.test(d.mediaType))return t.status(404).json({error:"image not found"});t.set("content-type",d.mediaType),t.set("cache-control","private, max-age=31536000, immutable"),t.set("content-security-policy","sandbox"),t.send(Buffer.from(d.data,"base64"))}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.post("/api/tracks/:id/interrupt",async(e,t)=>{try{t.json(await gp(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})}});_.delete("/api/tracks/:id/queue",async(e,t)=>{try{let{removed:n,restored:r,draftRev:o}=await Vg(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)})}});_.post("/api/tracks/:id/answer-prompt",async(e,t)=>{let{promptId:n,answers:r,text:o,custom:s}=e.body||{};try{t.json(await hp(e.params.id,{promptId:n,answers:r,text:o,custom:s}))}catch(a){let i=a?.message||String(a),c=/not found/i.test(i)?404:/no pending prompt|changed since|not running|not supported|needs a code|look like a sign-in code|no answer selected|custom answer|didn't take/i.test(i)?409:500;t.status(c).json({error:i})}});_.post("/api/setup/:provider/start",async(e,t)=>{try{let n=await sy({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})}});_.post("/api/tracks/:id/navigate-prompt",async(e,t)=>{let{promptId:n,questionIndex:r}=e.body||{};try{t.json(await Gg(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})}});_.put("/api/tracks/:id/draft",async(e,t)=>{let{text:n,rev:r}=e.body||{};try{t.json(await Zg(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})}});_.get("/api/tracks/:id/usage",async(e,t)=>{let n=await pe(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 nf(r,{fresh:o}))});_.post("/api/tracks/:id/model",async(e,t)=>{let{model:n}=e.body||{};try{t.json(await Qg(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})}});_.post("/api/tracks/:id/subtask",async(e,t)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=e.body||{};try{let a=await Sy(e.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});t.json({ok:!0,subtask:Bl(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})}});_.get("/api/tracks/:id/subtasks",(e,t)=>{t.json({subtasks:vs(e.params.id).map(Bl)})});_.get("/api/tracks/:id/subtask/:sid/messages",async(e,t)=>{let n=await qg(e.params.id,e.params.sid);if(!n)return t.status(404).json({error:"subtask not found"});t.json(n)});_.post("/api/tracks/:id/subtask/:sid/cancel",(e,t)=>{try{t.json({ok:!0,subtask:Bl($p(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});_.post("/api/tracks/:id/subtask/:sid/merge-now",(e,t)=>{try{t.json({ok:!0,subtask:Bl(Ay(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});function Bl(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}_.post("/api/tracks/:id/handoff-lead",async(e,t)=>{let{toAgent:n,skipPermissions:r,reason:o}=e.body||{};try{let s=await Ap(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})}});_.post("/api/tracks/:id/spin-up-view",async(e,t)=>{try{t.json(await Yg(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})}});_.get("/api/tracks/:id/handoff-lead",(e,t)=>{t.json(sC(qa(e.params.id))??{running:!1})});_.post("/api/tracks/:id/transfer-out",async(e,t)=>{try{let n=await by(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})}});_.get("/api/tracks/:id/transfer-out",(e,t)=>{let n=yy(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??{}})});_.post("/api/tracks/:id/transfer-out/commit",async(e,t)=>{try{t.json(await Ep(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})}});_.post("/api/tracks/adopt",async(e,t)=>{let{trackId:n}=e.body||{};try{t.json(await xy({...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 sC(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}_.get("/api/debug/handoff",async(e,t)=>{let n=Math.min(Number(e.query.limit)||80,500);t.json({verbose:lu(),paths:k0,trace:await b0({limit:n})})});var Zb=$e.resolve(zM,"..","..","client","dist");St.existsSync(Zb)&&(_.use(Jt.static(Zb)),_.get(/^(?!\/api\/).*/,(e,t)=>t.sendFile($e.join(Zb,"index.html"),Dl)));var aC=uV.createServer(_),SV=new fV({noServer:!0}),iC=(e,t,n)=>{if(!(vl(e)||oi.allowed(oi.clientIp(e))||ek.allows(e))){t.write(`HTTP/1.1 403 Forbidden\r
|
|
802
802
|
\r
|
|
803
|
-
`),t.destroy();return}if(
|
|
803
|
+
`),t.destroy();return}if(lO(e,t,n))return;if(!aO(e,tk,{enforce:YM})){t.write(`HTTP/1.1 401 Unauthorized\r
|
|
804
804
|
\r
|
|
805
|
-
`),t.destroy();return}let r=
|
|
805
|
+
`),t.destroy();return}let r=U0(e.url);if(!r.ok){t.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
806
806
|
\r
|
|
807
807
|
`),t.destroy();return}let o=r.sessionName;if(!Be(o)){t.write(`HTTP/1.1 400 Bad Request\r
|
|
808
808
|
\r
|
|
809
|
-
`),t.destroy();return}let{cols:s,rows:a}=r;
|
|
809
|
+
`),t.destroy();return}let{cols:s,rows:a}=r;SV.handleUpgrade(e,t,n,i=>j0(i,o,{cols:s,rows:a,revive:()=>uy(o)}))};aC.on("upgrade",iC);var cC=process.env.BOSTRAT_CERT_DIR||$e.join(process.env.HOME||Ol.tmpdir(),".bostrat","certs"),GM=Number(process.env.BOSTRAT_TLS_PORT)||3443,KM=$e.join(cC,"bostrat.globopi.key"),VM=$e.join(cC,"bostrat.globopi.crt");if(St.existsSync(KM)&&St.existsSync(VM)){let e=dV.createServer({key:St.readFileSync(KM),cert:St.readFileSync(VM)},_);e.on("upgrade",iC),e.on("error",t=>{console.error(`[bostrat-agent] https listener failed (${t?.code||t?.message}) \u2014 continuing HTTP-only`)}),e.listen(GM,bf,()=>{console.log(`[bostrat-agent] https listening on ${bf||"0.0.0.0"}:${GM}`)})}aC.listen(UM,bf,()=>{console.log(`[bostrat-agent] listening on ${bf||"0.0.0.0"}:${UM}`);let e=l$(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: ${Ft}`),console.log(`[bostrat-agent] auto-trigger default: ${JM}%`),console.log(`[bostrat-agent] ip allowlist: ${oi.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(ek.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),wy().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),ry().then(t=>t&&console.log(`[bostrat-agent] rolled back ${t} interrupted reshape(s)`)).catch(t=>console.error("[bostrat-agent] reshape reconcile failed:",t?.message||t)),vy().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),py().then(t=>t&&console.log(`[bostrat-agent] healed ${t} half-demoted transferred track(s)`)).catch(t=>console.error("[bostrat-agent] demoted-track reconcile failed:",t?.message||t)).then(()=>fy()).then(t=>t&&console.log(`[bostrat-agent] reaped ${t} orphaned box(es) on archived track(s)`)).catch(t=>console.error("[bostrat-agent] archived-orphan sweep failed:",t?.message||t)),oy().then(t=>console.log(`[bostrat-agent] standing instructions refreshed (${t.refreshed} updated, ${t.removed} legacy CLAUDE.md removed${t.codexGlobal?`, codex global ${t.codexGlobal}`:""})`)).catch(t=>console.error("[bostrat-agent] standing-instructions refresh failed:",t?.message||t)),OV(),E_({listTracks:ke}),b_({trackOf:Hg}),EV(),IV(),RV(),VS({listTracks:ke,getTrack:pe}).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)),l_()&&p_({listTracks:ke}),v0().then(t=>console.log(`[bostrat-agent] git hook shims at ${t}`)).catch(t=>console.error("[bostrat-agent] git hook shims failed (commits go unstamped):",t?.message||t)),cM(),pM(),hR({resolveTrack:pe}),TR({listTracks:ke,createTrack:kp,archiveTrack:ks,listRepos:nr,resolveTrack:pe,sayToLead:qt,answerPrompt:hp,startLeadHandoff:Ap,interruptLead:gp,listSubtaskJobs:vs,cancelSubtask:$p,readEvents:G,appendEvent:N,divertShip:Ub,answerPrExists:Hb,knownAgents:Yc}),AV(),Ci().then(t=>{t||console.warn(`[boot] no host tmux server on ${Ft} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),EM(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(si=b$({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,apiToken:tk,nodeId:process.env.BOSTRAT_NODE_ID||Ol.hostname(),name:process.env.BOSTRAT_NODE_NAME||Ol.hostname(),listTracks:ke,detectAgents:fp,sampleMetrics:HR,deliverySample:()=>jE(Kg()),onHashKey:CE,onViewBase:G$,onTracksTransferred:ky,onDegraded:({since:t,unacked:n,oldestAgeMs:r,mode:o})=>{let s=Math.round(r/6e4),a=`${process.env.BOSTRAT_NODE_NAME||Ol.hostname()} \u2192 plane spine sync has stalled: ${n} event(s) unanswered for ${s} min (${o} door). New Tracks won't open from the plane until it recovers; the node is redialing with backoff.`;console.error(`[plane-link] ${a}`),process.env.BOSTRAT_NTFY_URL&&fetch(process.env.BOSTRAT_NTFY_URL,{method:"POST",headers:{Title:"bostrat: plane sync stalled",Tags:"rotating_light"},body:a}).catch(()=>{}),qM=t},onRecovered:async({downMs:t})=>{let n=Math.round(t/6e4);process.env.BOSTRAT_NTFY_URL&&n>=5&&fetch(process.env.BOSTRAT_NTFY_URL,{method:"POST",headers:{Title:"bostrat: plane sync recovered",Tags:"white_check_mark"},body:`Spine sync to the plane recovered after ${n} min; the backlog is delivered.`}).catch(()=>{}),qM=null},onConnected:()=>{iM()},onRemoved:({reason:t,via:n})=>{let r=`${process.env.BOSTRAT_NODE_NAME||Ol.hostname()} was removed from Bostrat (${t}, via ${n}) and is now dormant: it will not redial. The box and its data are untouched; destroy the machine or re-provision it with a new node token.`;console.error(`[plane-link] ${r}`),process.env.BOSTRAT_NTFY_URL&&fetch(process.env.BOSTRAT_NTFY_URL,{method:"POST",headers:{Title:"bostrat: node removed, now dormant",Tags:"zzz"},body:r}).catch(()=>{})},onUpdateAvailable:async t=>{try{Qb={latest:t.latest,current:t.current},await Yn({...t,policy:_t()}),_t()==="auto"&&await ZM({to:t.latest,from:t.current})}catch(n){console.error(`[plane-link] update notice failed: ${n?.message||n}`)}},onUpdatePolicy:t=>gV(t).catch(()=>{}),onHelloRefused:async({current:t,min:n,latest:r,installKind:o,fix:s})=>{try{let a=r||n;if(await Yn({current:t,latest:a,min:n,installKind:o,required:!0,policy:_t(),status:"available",reason:"refused"}),_t()!=="off"&&a){let i=await Bp({to:a,kind:sn,reason:"hard-floor"});i.ok?(Nl.cancel(),await Yn({current:t,latest:a,min:n,installKind:o,required:!0,policy:_t(),status:"updating",reason:"hard-floor"})):console.error(`[self-update] hard-floor update not started: ${i.reason} \u2014 ${s}`)}}catch(a){console.error(`[plane-link] hard-floor handling failed: ${a?.message||a}`)}},updatePolicy:_t}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),Gs().catch(()=>{}),e_().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),wR().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),AI().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),UI().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),FA().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{si?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function AV(){TV(),qd().catch(()=>{}),setInterval(()=>qd().catch(()=>{}),1e4)}function TV(){for(let e of["bostrat-view","bostrat-mock","bostrat-flow","bostrat-ticket","bostrat-circuit","bostrat-roadmap","bostrat-blueprint","bostrat-decision","bostrat-runbook","bostrat-loop","bostrat-followup","bostrat-ship","bostrat-status","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let t=$e.resolve(zM,"..","bin",e),n=$e.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!St.existsSync(t))continue;St.mkdirSync(n,{recursive:!0});let r=$e.join(n,e);(St.existsSync(r)?St.readlinkSync(r):null)!==t&&(St.rmSync(r,{force:!0}),St.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 EV(){let e=!1,t=async()=>{if(!e){e=!0;try{let n=await ke(),r=await GR({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 IV(){let e=null,t=async()=>{try{let n=aa(),r=0,o=[],s=null,a=null;try{let u=await ke(),d=await pb(u);r=d.fleet.unpricedTurns||0,o=d.fleet.unpricedModels||[],s=await WR(u,{days:7}),a=(await mb(u,{window:"7d"})).categorization}catch{}let i=[...n.problems,...a?.problems||[]];s&&!s.ok&&i.push(`${s.divergent} recent row(s) diverge from the table (implied-rate audit) \u2014 run \`npm run cost -- --audit\``);let c=o.filter(u=>u!=="unknown");c.length&&i.push(`${r} unpriced turn(s) \u2014 add rates for: ${c.join(", ")}`);let l=i.join(`
|
|
810
810
|
`);if(!i.length){e!==""&&console.log(`[pricing] table ok \u2014 rates verified ${n.tableVerifiedAt??"never"}`),e="";return}for(let u of i)console.warn(`[pricing] \u26A0 ${u}`);l!==e&&process.env.BOSTRAT_NTFY_URL&&fetch(process.env.BOSTRAT_NTFY_URL,{method:"POST",headers:{Title:"bostrat: cost instrument needs attention",Tags:"warning"},body:i.join(`
|
|
811
|
-
`).slice(0,900)}).catch(()=>{}),e=l}catch(n){console.warn(`[pricing] watch failed: ${n?.message||n}`)}};setInterval(t,1440*60*1e3),setTimeout(t,3e4)}var
|
|
811
|
+
`).slice(0,900)}).catch(()=>{}),e=l}catch(n){console.warn(`[pricing] watch failed: ${n?.message||n}`)}};setInterval(t,1440*60*1e3),setTimeout(t,3e4)}var _V=Number(process.env.BOSTRAT_MEMORY_RECONCILE_MS)||5*6e4,$V=Number(process.env.BOSTRAT_MEMORY_RECONCILE_FIRST_MS)||5*6e4;function RV(){let e=async()=>{try{let n=await t0();n.ran&&console.log(`[memory] one-time full backfill sweep: ${n.harvested} fact(s) harvested`);let o=(await ke()).filter(a=>!Le(a)&&a.kind!=="setup").flatMap(a=>[a.cwd,...(a.boxes||[]).map(i=>i.cwd)]).filter(Boolean),s=await e0([...new Set(o)]);s.harvested&&console.log(`[memory] reconciler harvested ${s.harvested} fact(s) from ${o.length} live workspace(s)`)}catch{}};setTimeout(()=>{e(),setInterval(e,_V).unref()},$V).unref()}function OV(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{let t=await ke();for(let n of t)Le(n)||!n.boxes?.some(r=>r.state==="live")||(await mp(n.id).catch(()=>{}),await Kd(n).catch(()=>{}))}catch{}finally{e=!1}}},4e3)}
|