bostrat-node 1.9.13 → 1.10.0
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/bin/bostrat-followup +2 -2
- package/dist/index.js +220 -200
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
var
|
|
1
|
+
var bC=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 Ef=(e,t)=>{for(var n in t)bC(e,n,{get:t[n],enumerable:!0})};import{createHash as kC,randomBytes as xC,timingSafeEqual as vC}from"node:crypto";function pk(){return xC(32).toString("hex")}function Oo(e){let t=typeof e=="string"?e.trim():"";return t?kC("sha256").update(t).digest("hex"):null}function fk(e,t){return typeof e!="string"||typeof t!="string"||e.length!==t.length||!e?!1:vC(Buffer.from(e),Buffer.from(t))}function ui(e,t={},{now:n=Date.now(),log:r=console.warn}={}){if(If.set(e,(If.get(e)||0)+1),_f.has(e)&&n-_f.get(e)<SC)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=${If.get(e)}${o?` ${o}`:""}`),!0}async function mk(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,If,_f,SC,Ns=q(()=>{er="BOSTRAT_HOOK_TOKEN",tr="x-bostrat-hook";If=new Map,_f=new Map,SC=6e4});import Lt from"node:fs/promises";import hk from"node:os";import Ot from"node:path";import{execFile as AC}from"node:child_process";import{promisify as TC}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 EC(){return process.env.CLAUDE_CONFIG_DIR?Ot.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Ot.join(hk.homedir(),".claude.json")}function IC(){return process.env.CLAUDE_CONFIG_DIR?Ot.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Ot.join(hk.homedir(),".claude","settings.json")}async function Cr(e){if(!e||typeof e!="string")return;let t=Ot.resolve(e),n=EC(),r=IC(),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 _C(t)}async function _C(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 $C(t,["/.claude/settings.local.json"])}catch{}}async function $C(e,t){try{let{stdout:n}=await TC(AC)("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 di=
|
|
6
|
+
`)}catch{}}var di=q(()=>{Ns()});import $f from"node:fs/promises";import RC from"node:os";import Fl from"node:path";function OC(){let e=process.env.CODEX_HOME||Fl.join(RC.homedir(),".codex");return Fl.join(e,"config.toml")}function wk(e){return`[projects."${e}"]`}function MC(e,t){let n=wk(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=OC(),r="";try{r=await $f.readFile(n,"utf8")}catch{}if(MC(r,t))return;let o=wk(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=W(()=>{});import Rf from"node:fs/promises";import Nr from"node:path";import{execFile as TC}from"node:child_process";import{promisify as EC}from"node:util";async function MC(e){try{return(await Rf.stat(Nr.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function CC(e){let t=i=>Rf.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 MC(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:Nr.basename(o),branch:await CC(o)});return}if(s>=RC)return;let i;try{i=await Rf.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||OC.has(c.name)||await t(Nr.join(o,c.name),s+1)}for(let o of $C)await t(Nr.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=await Re(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 NC(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function Re(e){if(!e)return null;if($f.has(e))return $f.get(e);let t=null;try{let{stdout:n}=await _C("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=NC(n)}catch{t=null}return $f.set(e,t),t}async function Ps(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await nr())if((await Re(n.path))?.toLowerCase()===t)return n.path;return null}var _C,fk,IC,$C,RC,OC,$f,Pr=W(()=>{_C=EC(TC),fk=process.env.HOME||"/home/globo",IC=Nr.join(process.env.BOSTRAT_STATE_DIR||Nr.join(fk,".bostrat"),"repos"),$C=[...(process.env.BOSTRAT_REPOS_ROOT||Nr.join(fk,"Documents")).split(","),IC].map(e=>e.trim()).filter(Boolean),RC=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,OC=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);$f=new Map});function Ds(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 mk(){fi=0,mi=0,ql=0,Mo=0}function hi(e=Date.now()){return Co?1/0:Mo>e?Mo-e:0}function Bs(e=Date.now()){return hi(e)>0}function Of(e={}){Co={at:Date.now(),...e},Mo=1/0}function Kl(){return Co}function hk(e=Date.now()){return{held:Bs(e),holdMs:Co?null:hi(e),holds:ql,removed:Co?{...Co}:null}}var PC,Wl,fi,mi,ql,Mo,Co,Mf=W(()=>{PC={trip:3,windowMs:3e5,minMs:3e5,maxMs:216e5},Wl={...PC},fi=0,mi=0,ql=0,Mo=0,Co=null});import DC from"node:fs";import wk from"node:fs/promises";import wi from"node:path";import BC from"node:os";import{fileURLToPath as jC}from"node:url";import{execFile as LC}from"node:child_process";import{promisify as FC}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 Gl(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}function xk(e){return e?.status===404&&/not granted/i.test(String(e?.message||""))}async function js(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!yk.test(n))throw No("repo must be owner/name",400);let r=Cf.get(n);if(r&&r.expiresAt-Date.now()>WC)return r;if(!Gl(t))throw No("node is not plane-linked \u2014 GitHub auth unavailable",503);if(Bs()){if(r&&r.expiresAt>Date.now())return r;throw No(Kl()?"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 bk(`${Ne(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:kk(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)&&Ds(),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 Cf.set(n,a),a}async function gi({env:e=process.env,force:t=!1}={}){if(!Gl(e))return{configured:!1,repos:[],username:null};if(!t&&Dr&&Date.now()-Dr.at<qC)return Dr.data;if(Bs())return Dr?.data??{configured:!0,repos:[],username:null,error:"plane refused this node's token"};try{let n=await bk(`${Ne(e)}/v1/github/repos`,{headers:kk(e)});if((n.status===401||n.status===403||n.status===410)&&Ds(),!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 vk(){Cf.clear(),Dr=null}async function En({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await Re(e):null);if(!r)return{};let{token:o}=await js(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Nf({env:e=process.env}={}){return(await gi({env:e})).username??null}function zC(e){let[t,n]=String(e).split("/");return wi.join(HC,t,n)}async function Pf(e,{env:t=process.env}={}){let n=(...r)=>gk("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${GC()}" ${VC(t)}`),await n("config","credential.useHttpPath","true")}async function Vl(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!yk.test(String(e||"")))throw No(`bad repo slug "${e}"`,400);let r=zC(e);if(DC.existsSync(wi.join(r,".git")))return r;let{token:o}=await js(e,{env:t});await wk.mkdir(wi.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await gk("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:KC,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await wk.rm(r,{recursive:!0,force:!0}).catch(()=>{}),No(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Pf(r,{env:t}),r}var gk,UC,HC,yk,WC,qC,KC,bk,Cf,Dr,kk,No,GC,VC,gt=W(()=>{Pr();Mf();gk=FC(LC),UC=process.env.BOSTRAT_STATE_DIR||wi.join(process.env.HOME||BC.tmpdir(),".bostrat"),HC=wi.join(UC,"repos"),yk=/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?\/[A-Za-z0-9._-]{1,100}$/,WC=5*6e4,qC=6e4,KC=10*6e4,bk=fetch,Cf=new Map,Dr=null;kk=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),No=(e,t)=>Object.assign(new Error(e),{status:t});GC=()=>jC(new URL("../bin/bostrat-git-credential",import.meta.url)),VC=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function Ak(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Ek(e,t,n,r=Sk){let o=Ne(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await YC(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function _k(e,{env:t=process.env,force:n=!1,timeoutMs:r=Sk}={}){if(!Ak(t))return{facts:[],source:"unconfigured"};let o=Df.get(e);if(!n&&o&&Date.now()-o.at<JC)return{facts:o.facts,source:"cache"};try{let s=await Ek(`/v1/memory/${encodeURIComponent(e)}`,{headers:Tk(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 Df.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(!Ak(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await Ek(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...Tk(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 Df.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 JC,Sk,Df,YC,Tk,Ik=W(()=>{gt();JC=6e4,Sk=5e3,Df=new Map,YC=(...e)=>fetch(...e);Tk=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import Bf from"node:fs/promises";import Ls from"node:path";import XC from"node:os";import{EventEmitter as ZC}from"node:events";import{randomUUID as QC}from"node:crypto";function Fs(e){return e?.cwd?Ls.join(e.cwd,".bostrat","events"):Ls.join(jf,"events")}async function rN(e,t){if(!Jl.has(t)){let o=(await q(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 Lf(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 C(e,t){let n=typeof e=="string"?e:e?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!t||!eN.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!tN.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!nN.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?Ls.join(jf,"events"):Fs(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=Lf(u,Number(t.payload?.ts??t.ts)||Date.now())}let s=await rN(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||QC(),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 Rk(c)&&(c=Ok(c),l=JSON.parse(c)),await Bf.mkdir(r,{recursive:!0}),await Bf.appendFile($k(r,n),c+`
|
|
15
|
-
`),
|
|
16
|
-
`))if(a.trim())try{s.push(JSON.parse(
|
|
13
|
+
`:"")}try{await $f.mkdir(Fl.dirname(n),{recursive:!0}),await $f.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var Ul=q(()=>{});import gk from"node:fs/promises";import CC from"node:os";import pi from"node:path";function NC(e){let n=pi.resolve(e).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||pi.join(CC.homedir(),".cursor");return pi.join(r,"projects",n)}async function Ds(e){if(!e||typeof e!="string")return;let t=pi.resolve(e),n=NC(t),r=pi.join(n,".workspace-trusted");await gk.mkdir(n,{recursive:!0}),await gk.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:t},null,2)}
|
|
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(Lf,"events"):Hs(e));let o;try{o=await jf.readFile(Nk(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
16
|
+
`))if(a.trim())try{s.push(JSON.parse(Pk(a)?Dk(a):a))}catch{}return s}function Bk(e,t){let n=`track:${e}`;return Xe.on(n,t),()=>Xe.off(n,t)}var uN,dN,pN,Xe,Lf,Nk,Jl,mN,Pk,Dk,ye=q(()=>{uN=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"]),dN=new Set(["user","agent","orchestrator","system","peer"]),pN=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor","cli"]),Xe=new cN;Xe.setMaxListeners(0);Lf=process.env.BOSTRAT_STATE_DIR||Us.join(process.env.HOME||iN.tmpdir(),".bostrat");Nk=(e,t)=>Us.join(e,`${t}.ndjson`),Jl=new Map;mN=/(?<!\\)((?:\\\\)*)\\u0000/g,Pk=e=>e.includes("\\u0000"),Dk=e=>e.replace(mN,"$1\\ufffd")});import le from"node:fs/promises";import hN from"node:os";import Ae from"node:path";import{execFile as wN}from"node:child_process";import{promisify as gN}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 bi(e){try{return JSON.parse(await le.readFile(Ae.join(e,qk),"utf8"))}catch{return{}}}async function Hf(e,t){let n=await bi(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,qk),JSON.stringify(n))}function Lk(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 Fk(e){try{let{stdout:t}=await yN("git",["-C",e,"config","--get","remote.origin.url"]);return t.trim()||null}catch{return null}}async function Wf(e){if(!e)return[];let t=Lk(await Fk(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=Lk(await Fk(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 Gk(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 qf(e,t,n,r=new Map){let o=(e||"").split(`
|
|
17
17
|
`),s=new Map;for(let c of(t||"").split(`
|
|
18
|
-
`)){let l=
|
|
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 Kk(e,t){let n=Br(t,"name")||e.replace(/\.md$/,""),r=Br(t,"description")||"";return`- [${n}](${e})${r?` \u2014 ${r}`:""}`}async function ki(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 Vk(e,t){let n=Ae.join(e,".bostrat","memory"),r=await ki(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=Uk.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-kN)continue}catch{continue}let f;try{f=await le.readFile(p,"utf8")}catch{continue}let{fm:w}=Ql(f),m=Gk(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 bi(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,Kk(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,qf(w,u,p.won,p.generated))}return Uk.set(e,s),{harvested:c,changedByKey:i}}async function zk(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 Wf(r),a=await Vk(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 xN(){let e=Ae.join(Zl,"workspaces"),t=[];try{t=await le.readdir(e,{withFileTypes:!0})}catch{return{harvested:0,changedByKey:new Map}}return zk(t.filter(n=>n.isDirectory()).map(n=>Ae.join(e,n.name)))}async function AN(e,t){let n=Po(e),r=await bi(n),o=new Set,s=new Map,a=[],i=0;for(let c of t||[]){let l=String(c?.file||"");if(!vN.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 Hf(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=qf(l,[...s.values()].join(`
|
|
22
22
|
`),o);if(a.length){let d=new Set(a);u=u.split(`
|
|
23
|
-
`).filter(p=>!d.has(
|
|
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 ki(n);if(!r.length)return{pushed:0,source:"empty"};let o=
|
|
25
|
-
`);t.length>
|
|
26
|
-
`);for(;Buffer.byteLength(n,"utf8")>
|
|
27
|
-
`);return n}async function
|
|
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 ki(n);if(!r.length)return{pushed:0,source:"empty"};let o=SN(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 TN(e,{timeoutMs:t}={}){let{facts:n,source:r}=await Mk(e,t?{timeoutMs:t}:{});n.length?await AN(e,n):r==="plane"&&await eu(e)}function EN(e){let t=e.split(`
|
|
25
|
+
`);t.length>jk&&(t=t.slice(0,jk));let n=t.join(`
|
|
26
|
+
`);for(;Buffer.byteLength(n,"utf8")>bN&&t.length;)t.pop(),n=t.join(`
|
|
27
|
+
`);return n}async function _N(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 bi(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 ki(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=
|
|
33
|
-
`).filter(f=>!s.has(
|
|
34
|
-
`);await le.writeFile(c,u).catch(()=>{});let d=
|
|
35
|
-
`).filter(l=>
|
|
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=
|
|
32
|
+
`,c=EN(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"),IN,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function Jk(e){try{let t=await Wf(e);if(!t.length)return{materialized:!1,reason:"no-repo-key"};let n=[...t,rr];return await Promise.all(n.map(r=>TN(r,{timeoutMs:$N}))),await _N(e,n)}catch(t){return{materialized:!1,reason:"error",error:t?.message||String(t)}}}function Yk(){return{...st,pending:Ws.size,queued:Uf.size}}function Xk(e){if(Yl.has(e))return Yl.get(e);Uf.add(e);let t=Hk.then(async()=>{Uf.delete(e),Yl.delete(e);let n=await Gf(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),Hk=t.then(()=>{}),t}function tu(e,{delayMs:t=RN}={}){if(!e||typeof e!="string")return;st.scheduled++;let n=Ws.get(e);n&&clearTimeout(n);let r=setTimeout(()=>{Ws.delete(e),Xk(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([Xk(e),s])}finally{clearTimeout(o)}}async function Zk(e){return yi||(yi=(async()=>{try{Wk||(Wk=!0,await MN());let{harvested:t,changedByKey:n}=await zk(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{yi=null}})(),yi)}function ON(){return Ae.join(Zl,"memory",".full-sweep-done")}async function Qk(){let e=ON();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 xN();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 MN(){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 ki(r)){let w=Ae.join(r,f),m;try{m=await le.readFile(w,"utf8")}catch{continue}let{fm:g}=Ql(m);if(Gk(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,Kk(f,g)),e++}if(!s.size)continue;let i=Date.now();await Hf(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
|
+
`).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,qf(p,l,s,a))}return{moved:e}}async function e0(){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 ki(r),s=await bi(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 t0(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 Hf(n,[[t,s]]);let a=Ae.join(n,"MEMORY.md"),i=await le.readFile(a,"utf8").catch(()=>"");await le.writeFile(a,i.split(`
|
|
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 Gf(e){try{let t=await Wf(e),{harvested:n,changedByKey:r}=await Vk(e,t[0]||null);for(let[o,s]of r)await eu(o,[...s]);return n>0&&await NN(e,r).catch(()=>{}),{harvested:n}}catch(t){return{harvested:0,error:t?.message||String(t)}}}async function NN(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,CN)}}await N({id:n,cwd:e},{type:"memory.harvested",actor:"orchestrator",payload:{harvested:o,keys:r,ts:Date.now()}})}var yN,Zl,jk,bN,rr,qk,qs,Uk,kN,vN,SN,IN,$N,RN,Ws,Uf,Hk,st,Yl,Wk,yi,CN,xi=q(()=>{Ck();ye();yN=gN(wN),Zl=process.env.BOSTRAT_STATE_DIR||Ae.join(process.env.HOME||hN.tmpdir(),".bostrat"),jk=200,bN=25*1024;rr="operator";qk=".tombstones.json";qs=e=>/\]\(([^)]+\.md)\)/.exec(e)?.[1]||null;Uk=new Map,kN=2e3;vN=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,SN=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};IN=`# 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
|
+
`;$N=Number(process.env.BOSTRAT_MEMORY_SPAWN_PULL_MS)||1500;RN=Number(process.env.BOSTRAT_MEMORY_HARVEST_DEBOUNCE_MS)||2e3,Ws=new Map,Uf=new Set,Hk=Promise.resolve(),st={scheduled:0,runs:0,harvested:0,errors:0,lastRunAt:null,lastError:null,reconciles:0,lastReconcileAt:null,backfill:null};Yl=new Map;Wk=!1,yi=null;CN=20});import ru from"node:fs/promises";import PN from"node:path";async function Mt(e,t,{mode:n}={}){await ru.mkdir(PN.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${++DN}.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 DN,jr=q(()=>{DN=0});import a0 from"node:fs/promises";import i0 from"node:os";import c0 from"node:path";function qe(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Ai(e,t={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),BN);try{return await HN(`${Ne(n)}${e}`,{...t,signal:r.signal})}finally{clearTimeout(o)}}async function p0(){return su||(Kf||=a0.readFile(l0(),"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(()=>Kf=null),Kf)}async function au(){await Mt(l0(),su).catch(()=>{})}function o0(e){return e.op==="blob"?`blob ${e.family} ${e.id} ${e.v} ${e.kind}`:`${e.op} ${e.family} ${e.id}`}async function zf(e){let t=await p0(),n=o0(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=>o0(o)===n);r>=0?t.ops[r]=e:t.ops.push(e)}t.ops.length>n0&&(t.ops=t.ops.slice(-n0)),await au()}function s0(){ou||(ou=setTimeout(()=>{ou=null,Gs().catch(()=>{})},WN),ou.unref?.())}function Vf(e){let t=new Error(`plane ${e}`);return t.refused=!0,t}async function GN(e,t=process.env){if(e.op==="record"){let n=await Ai(`/v1/content/${encodeURIComponent(e.family)}`,{method:"POST",headers:{...Si(t),"content-type":"application/json"},body:JSON.stringify({records:[{id:e.id,trackId:e.trackId??null,data:e.data,updatedAt:e.updatedAt,originNode:u0()}]})},t);if(!n.ok)throw Vf(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 a0.readFile(e.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await Ai(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}/blob?v=${e.v}&kind=${encodeURIComponent(e.kind)}`,{method:"POST",headers:{...Si(t),"content-type":"application/octet-stream"},body:n},t);if(!r.ok&&r.status!==400)throw Vf(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 Ai(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}?deletedAt=${e.deletedAt}`,{method:"DELETE",headers:Si(t)},t);if(!n.ok)throw Vf(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)?vi||(vi=(async()=>{let r=await p0();KN(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await GN(a,e)}catch(c){if(!c?.refused)return s0(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<LN)return s0(),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>r0&&(r.parked=r.parked.slice(-r0)),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(()=>{vi=null}),vi):{flushed:0,source:"unconfigured"}}function KN(e,t){if(!e.parked.length||e.ops.length)return;let n=e.parked.filter(r=>t-(r.parkedAt||0)>=qN);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 zf({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 zf({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 zf({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 Ai(`/v1/content/${encodeURIComponent(e)}?tombstones=1`,{headers:Si(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 Ti(e,t,n,r,{env:o=process.env}={}){if(!qe(o))return null;try{let s=await Ai(`/v1/content/${encodeURIComponent(e)}/${encodeURIComponent(t)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Si(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 BN,jN,n0,LN,r0,FN,UN,l0,u0,d0,HN,WN,qN,Si,su,Kf,ou,vi,Fr=q(()=>{yt();jr();BN=1e4,jN=3e4,n0=2e3,LN=5,r0=100,FN=60*6e4,UN=()=>process.env.BOSTRAT_STATE_DIR||c0.join(process.env.HOME||i0.tmpdir(),".bostrat"),l0=()=>c0.join(UN(),"content-outbox.json"),u0=()=>process.env.BOSTRAT_NODE_ID||i0.hostname(),d0=()=>u0(),HN=(...e)=>fetch(...e),WN=jN,qN=FN;Si=(e=process.env)=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`});su=null,Kf=null,ou=null,vi=null});import Jf from"node:fs/promises";import f0 from"node:path";async function m0(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),Ei=null,{pushed:!0,v:o}}catch{return{pushed:!1}}}async function XN(){if(Ei&&Date.now()-Ei.at<YN)return{records:Ei.records,source:"plane"};let e=await dt("checkpoints");return e.source==="plane"&&(Ei={at:Date.now(),records:e.records}),e}async function iu(e){try{let t=JN(e);if(!t||!qe())return{restored:!1};let n=f0.join(e,VN,zN);if(await Jf.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await XN();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 Ti("checkpoints",t,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await Jf.mkdir(f0.dirname(n),{recursive:!0}),await Jf.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var VN,zN,JN,YN,Ei,cu=q(()=>{Fr();VN=".bostrat/handoff",zN="latest.md",JN=e=>/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1]??null;YN=6e4,Ei=null});import Ii from"node:fs/promises";import ZN from"node:os";import w0 from"node:path";function Do(e){let t=String(e??"");for(let n of QN)t=t.replace(n,"\u2039redacted\u203A");return t}function lu(){let e=process.env.BOSTRAT_HANDOFF_DEBUG;return e==="1"||e==="true"}async function eP(e){try{await Ii.mkdir(Yf,{recursive:!0}),await Ii.appendFile(Ks,`${JSON.stringify(e)}
|
|
44
|
+
`,"utf8");let n=(await Ii.readFile(Ks,"utf8")).split(`
|
|
45
|
+
`).filter(Boolean);n.length>h0&&await Ii.writeFile(Ks,`${n.slice(-h0).join(`
|
|
46
46
|
`)}
|
|
47
|
-
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${
|
|
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 eP(t)}async function g0({limit:e=80}={}){try{return(await Ii.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 QN,Yf,Ks,h0,y0,Rn=q(()=>{ht();QN=[/[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];Yf=process.env.BOSTRAT_DEBUG_DIR||w0.join(ZN.homedir(),".bostrat","debug"),Ks=w0.join(Yf,"handoff.jsonl"),h0=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;y0={DEBUG_DIR:Yf,LOG_FILE:Ks}});import uu from"node:fs/promises";import Xf from"node:path";import tP from"node:os";async function k0(e=b0){await uu.mkdir(e,{recursive:!0});for(let t of rP){let n=t==="prepare-commit-msg"?oP:t==="post-commit"?sP:`${Zf}exit 0
|
|
49
|
+
`,r=Xf.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 x0({nodeUrl:e,dir:t=b0,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 v0(e=process.env){return e.BOSTRAT_NODE_URL||`http://127.0.0.1:${e.PORT||3362}`}var _i,nP,b0,rP,Zf,oP,sP,Qf=q(()=>{Ns();_i="BOSTRAT_NODE_URL",nP=process.env.BOSTRAT_STATE_DIR||Xf.join(process.env.HOME||tP.tmpdir(),".bostrat"),b0=Xf.join(nP,"git-hooks"),rP=["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"],Zf=`#!/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
|
+
`,oP=`${Zf}
|
|
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
|
+
`,sP=`${Zf}
|
|
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 Pi(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=
|
|
91
|
-
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function Di(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 Bi(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(
|
|
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||
|
|
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(
|
|
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
|
|
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"&&ji.has(u.name)){let f;try{f=JSON.parse(u.arguments||"{}")}catch{continue}let w=
|
|
99
|
-
`).trim():"";ji=new Set(["exec_command","shell","exec"]),
|
|
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
|
|
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 A0}from"node:child_process";import{promisify as aP}from"node:util";import pu from"node:fs";import{randomUUID as T0}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 E0(e){return`/tmp/tmux-${e}/default`}function iP({explicit:e,legacyExists:t,runtimeDir:n,uid:r}){if(e)return e;let o=E0(r);return t?o:n?`${n}/bostrat/tmux-default`:o}function Ri(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${$i}=1`]}function uP(){return lP.flatMap(e=>["-e",`${e}=`])}function Oi(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 pP(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 I0(e,{loginShell:t=!1,failOpen:n=!1}={}){let r=await _0(e,{loginShell:t});return r===fu?!0:r===Vs?!1:n}async function _0(e,{loginShell:t=!1,timeoutMs:n=em}={}){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=fP,sleep:n=mP,probe:r=_0}={}){let o=String(e||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:em});for(let a=0;s===zs&&a<t.length;a++)await n(t[a]),s=await r(o,{timeoutMs:em*2});return s}function oe(...e){return["-S",Ft,...e]}async function Hr(){if(!pu.existsSync(Ft))return!1;try{return await Me(te,oe("list-sessions")),!0}catch(e){let t=e&&e.stderr?String(e.stderr):"";return!/no server running/i.test(t)}}async function Mi(){if(await Hr())return!0;try{pu.mkdirSync(Ft.replace(/\/[^/]+$/,""),{recursive:!0}),await Me(te,oe("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await Hr()}function Be(e){return typeof e=="string"&&hP.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,oe("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=>!dP.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function xe(e){return(await ln()).find(n=>n.name===e)||null}function yP(e){if(typeof e!="string")return!1;let t=e.trim();return t.length>=1&&t.length<=gP&&wP.test(t)}function hu(e){return e.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function $0(e){try{let{stdout:t}=await Me(te,oe("list-sessions","-F","#{session_name}"));return t.split(`
|
|
87
|
+
`).map(n=>n.trim()).includes(e)}catch{return!1}}function xP(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 vP(e,t){let n=Date.now()+Ci(),r=xP(t),o=!1,s=0,a=0;for(;;){if(await Bo(bP),s+=1,!await $0(e))return;let i=await De(e,{lines:15}),c=!o&&kP.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,oe("send-keys","-t",e,"C-u")),await Me(te,oe("send-keys","-t",e,"-l",t)),await Me(te,oe("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=cP,n={}){if(!yP(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 Mi())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 $0(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||tm,a=n.agent||Ur(s),i=!!n.resumeSessionId,c=n.resumeSessionId||T0(),l=i?s:Oi(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 Jk(t),n.freshWorkspace!==!0&&await iu(t));let f=d&&!p?Ll():null,w=pk(),m=["-e",`${er}=${w}`];m.push(...x0({nodeUrl:v0()})),m.push(...uP()),f&&m.push("-e",`ANTHROPIC_API_KEY=${f}`),d&&m.push(...Ri());try{await Me(te,oe("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,oe("set-option","-t",o,"@bostrat-session","1")),await Me(te,oe("set-option","-t",o,"@bostrat-title",r)),await Me(te,oe("set-option","-t",o,"@bostrat-sid",c)),await Me(te,oe("set-option","-t",o,"@bostrat-agent",a)),await Me(te,oe("send-keys","-t",o,l,"Enter")),pP(l)&&vP(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,oe("kill-session","-t",e))}async function R0(e,t){if(Be(e))try{let n=t?oe("set-option","-t",e,"@bostrat-sid",t):oe("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=oe("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 O0(e){if(!Be(e))return[];try{let{stdout:t}=await Me(te,oe("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 M0(e){if(!Be(e))return 0;try{let{stdout:t}=await Me(te,oe("list-clients","-t",e,"-F","1"));return String(t||"").trim().split(`
|
|
89
|
+
`).filter(Boolean).length}catch{return 0}}async function Lo(e,{cols:t=TP,rows:n=EP}={}){if(!Be(e))return!0;if(await M0(e))return!1;try{let r=await C0(e);if(r.cols===t&&r.rows===n)return!0;await Me(te,oe("resize-window","-t",e,"-x",String(t),"-y",String(n))),await Me(te,oe("set-window-option","-t",e,"window-size","latest")),await Bo(80)}catch{}return!0}async function IP(e){if(!Be(e))return 0;try{let{stdout:t}=await Me(te,oe("display-message","-t",e,"-p","#{pane_pid}"));return Number(String(t||"").trim())||0}catch{return 0}}async function or(e){let t=await IP(e);if(!t)return!1;try{return await Me("pgrep",["-P",String(t)]),!1}catch(n){return n?.code===1}}async function C0(e){if(!Be(e))return{cols:0,rows:0};try{let{stdout:t}=await Me(te,oe("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=SP,rows:n=AP}={}){if(Be(e)&&!await M0(e))try{let r=await C0(e),o=Math.max(r.cols,t),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await Me(te,oe("resize-window","-t",e,"-x",String(o),"-y",String(s))),await Me(te,oe("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=>N0.has(s));for(let s=0;s<o.length;s++)await Me(te,oe("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 om(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"&&N0.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,oe("send-keys","-t",e,"-l",a.text)):await Me(te,oe("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,oe("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,oe("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 _P(e,t){return new Promise((n,r)=>{let o=A0(te,oe(...e),(s,a,i)=>{s?r(s):n({stdout:a,stderr:i})});o.stdin.on("error",()=>{}),o.stdin.end(t)})}async function $P(e,t){let n=`bostrat-${T0().slice(0,8)}`;await _P(["load-buffer","-b",n,"-"],t),await Me(te,oe("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 $P(e,t):await Me(te,oe("send-keys","-t",e,"-l",t))),n){let a=Number(r);Number.isFinite(a)&&a>0&&await Bo(a),await Me(te,oe("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,S0,Ft,cP,tm,$i,lP,nm,dP,fu,Vs,zs,em,fP,mP,rm,hP,wP,gP,Ci,bP,kP,SP,AP,Qe,TP,EP,N0,gu,ht=q(()=>{di();Ul();Hl();xi();cu();Rn();Ns();Qf();Me=aP(A0);te=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";S0=typeof process.getuid=="function"?process.getuid():0,Ft=iP({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return pu.existsSync(E0(S0))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:S0}),cP=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",tm=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",$i="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";lP=Object.freeze(["BOSTRAT_PLANE_URL","BOSTRAT_PLANE_TOKEN"]);nm="bostrat-usage-probe",dP=new Set([nm]);fu="present",Vs="absent",zs="unknown",em=5e3,fP=[250,1e3,3e3],mP=e=>new Promise(t=>setTimeout(t,e));rm={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"};hP=/^[A-Za-z0-9_.-]{1,64}$/;wP=/^[A-Za-z0-9 ._↻-]+$/,gP=64;Ci=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,bP=1e3,kP=/not found|Unknown command/i;SP=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,AP=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,Qe=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,TP=Number(process.env.BOSTRAT_BOX_COLS)||120,EP=Number(process.env.BOSTRAT_BOX_ROWS)||40;N0=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);gu=1600});import Ni from"node:fs";import PP from"node:fs/promises";import Ys from"node:path";import DP 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=Ni.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}=Ni.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 Ni.existsSync(n)?n:null}function U0(e){if(!e)return[];let t=Ys.join(am,im(e)),n;try{n=Ni.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=Ni.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 PP.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 jP(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 Pi(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=jP(await Nt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(F0,s.model),i=F0[s.model]||BP;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 F0,BP,am,pn=q(()=>{F0={"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},BP=2e5,am=process.env.BOSTRAT_CLAUDE_PROJECTS||Ys.join(DP.homedir(),".claude","projects")});function LP(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 Di(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 Bi(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(q0(o)){t.has(o)&&r++;continue}n.push(o)}return n}function G0(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 HP(e){let t=G0(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,H0)[0]);return s&&s!=="."?`List files in ${Xs(s)}`:"List files"}let t=je(e,H0),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 WP(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 W0(e){return e.find(t=>/^https?:\/\//.test(t))||null}function KP(e){let t=Bi(Di(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(GP.has(n))return null;if(t.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=qP[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=LP(t);for(let i=0;i<r.length-1;i++){let c=Bi(Di(r[i])),l=Bi(Di(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(Je(c[0]))&&Je(l[0])==="sed"){let u=G0(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=KP(i);c&&o.push({d:c,head:Je(Bi(Di(i))[0]||""),sub:je(Bi(Di(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,q0,H0,FP,UP,qP,GP,K0=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},q0=e=>e.startsWith("-"),H0=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);FP={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"},UP={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"};qP={git:e=>{let t=je(e,new Set(["-C","-c"])),n=FP[t[0]];return n?n(t,e):null},gh:e=>{let t=je(e),n=UP[t[0]];return n?n(t):null},sed:HP,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])&&!q0(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=W0(e);return t?`Fetch ${Zs(t,56)}`:"Fetch a URL"},wget:e=>{let t=W0(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:WP,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}},GP=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 V0(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 z0(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(...VP(i,r.get(a)||[]));return s.sort((a,i)=>(a.ts||0)-(i.ts||0))}function VP(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();K0();mm=new Set(["Bash"])});import J0 from"node:fs";import wm from"node:fs/promises";import hm from"node:path";import zP from"node:os";async function Y0(e){try{return await wm.realpath(e)}catch{return e}}function YP(e=JP){let t=[],n=r=>{let o;try{o=J0.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:J0.statSync(a).mtimeMs})}catch{}}}};return n(e),t.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function XP(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 ZP(e){let t=await XP(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=YP();if(t){let o=n.find(s=>s.id===t);return o||null}let r=await Y0(e);for(let o of n){let s=await ZP(o.file);if(s&&(s===e||await Y0(s)===r))return o}return null}async function X0(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=eD(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 tD(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 nD(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 rD(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=nD(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function Z0(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"&&ji.has(u.name)){let f;try{f=JSON.parse(u.arguments||"{}")}catch{continue}let w=tD(f);w&&(d=[w])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=rD(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 Q0(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 JP,QP,eD,ji,gm,Uo,Ho=q(()=>{pn();ea();JP=process.env.BOSTRAT_CODEX_SESSIONS||hm.join(process.env.CODEX_HOME||hm.join(zP.homedir(),".codex"),"sessions");QP=new Set(["output_text","text"]),eD=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&typeof t.text=="string"&&QP.has(t.type)).map(t=>t.text).join(`
|
|
99
|
+
`).trim():"";ji=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 Li({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&ex.has(r))return ex.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 tx(e){if(!e||e.role!=="assistant")return null;if(e.apiError){let t=e.apiError;return{cls:t.cls||Li({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":Li({text:t}),code:null,status:null,requestId:null}}return null}function nx(e,{codexFailedText:t=null}={}){let n=String(e?.text||"").trim();return n?e?.model==="<synthetic>"?/^No response requested\.$/.test(n)?null:{cls:Li({text:n}),code:null,status:oD(n),requestId:null,text:n}:t&&n===t?{cls:"other",code:null,status:null,requestId:null,text:n}:null:null}function oD(e){let t=/^API Error:\s*(\d{3})\b/.exec(e);return t?Number(t[1]):null}var R3,ex,ku=q(()=>{Ho();R3=Object.freeze(["entitlement","auth","rate_limit","overloaded","server","other"]),ex=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 sD(e,t){return!e||!t?null:dn(e,t)}function aD(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 iD(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 cD(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 rx(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"&&lD.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(cD(u.content)){let y=rx(l,s);y&&o.push({role:"assistant",text:"",images:y,ts:l.timestamp?Date.parse(l.timestamp):null,model:null});continue}let p=aD(u.content),f=d==="assistant"?iD(u.content):null,w=d==="user"?rx(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:Li({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 ox(e,t,{turns:n=16}={}){let r=sD(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}:** ${uD(s.text||c,700)} ${i}`.trim()}).join(`
|
|
102
|
+
|
|
103
|
+
`):""}var lD,uD,xu=q(()=>{ku();pn();lD=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;uD=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e});import dD from"node:fs/promises";import{execFile as pD}from"node:child_process";import{promisify as fD}from"node:util";function cx(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 lx({sessionName:e,from:t,to:n}={}){let r=String(n||"").trim();!r||sx.has(r)||(sx.add(r),ax=ax.then(()=>hD({sessionName:e,from:t,to:r})).catch(()=>{}))}async function hD({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 dD.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:mD});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 ix({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 ix({sessionName:e,from:t,to:n,status:"failed",error:o})}}async function ix(e){if(xm)try{await xm(e)}catch{}}var km,mD,sx,ax,xm,vu=q(()=>{Rn();km=fD(pD),mD=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,sx=new Set,ax=Promise.resolve(),xm=null});function gD(e,{provider:t,box:n}){let r=String(e).replace(/\r/g,"").split(`
|
|
104
|
+
`);if(!ux.test(e))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(wD);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"&&ux.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(fx.test(n[r]))return n.slice(r).join(`
|
|
106
|
+
`);return t}function yD(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 bD(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 kD(e,t=""){let n=qr(e);if(Fi.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(dx);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(dx.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&&!fx.test(r[l])&&yD(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=bD(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function SD(e,t=""){let n=qr(e);if(Fi.test(n)||!xD.test(n))return null;let r=n.trimEnd().split(`
|
|
108
|
+
`).pop()||"";return vD.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 TD(e,t=""){let n=qr(e);if(Fi.test(n)||!px.test(n))return null;let r=n.split(`
|
|
109
|
+
`),o=r.findIndex(i=>px.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(AD);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 ED(e,t=""){let n=qr(e);return Fi.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 ID(e,t=""){let n=qr(e);return Fi.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 _D(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 RD(e,t=""){let n=String(e);if(!$D.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 OD(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 mx(e,t,n=""){if(e!=="cursor"&&e!=="codex")return null;if(e==="codex"){let r=SD(t,n)||TD(t,n)||ED(t,n)||ID(t,n)||kD(t,n);if(r)return r}if(e==="cursor"){let r=RD(t,n)||OD(t,n)||_D(t,n);if(r)return r}return gD(t,{provider:e,box:n})}function hx(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 wD,ux,Fi,fx,dx,xD,vD,px,AD,Sm,$D,Su=q(()=>{wD=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,ux=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;Fi=/(^|\n)\s*›/,fx=/^\s*Welcome to Codex\b|^\s*(?:│\s*)?>_ OpenAI Codex\b/;dx=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;xD=/Successfully logged in\./,vD=/^\s*Press Enter to continue\.\s*$/;px=/Enter this one-time code/,AD=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;Sm=/Run Everything|Add a follow-up|Plan, search, build anything/;$D=/click this link to log in|Signing in with the browser/});function PD(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(CD.test(r)||ND.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 FD(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(LD);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=UD();for(let[s,a]of Tu)n-a>=r&&Tu.delete(s);let o=`${e}::${t}`;return Tu.has(o)?!1:(Tu.set(o,n),!0)}function bx(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(yx.test(n[o])){r=o;break}return r<0?t:n.slice(r+1).join(`
|
|
115
|
+
`)}function WD(e){let t=0;for(let n of String(e).split(`
|
|
116
|
+
`))yx.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(bx(n,t)))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(t))return{state:"busy",reason:null};let r=WD(t);return e==="claude"&&r>0&&r<HD?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function Em(e,t){return Tm(e,t).state}function kx(e,t){if(e!=="claude")return!1;let n=String(t||"").split(`
|
|
117
|
+
`).slice(-GD).join(`
|
|
118
|
+
`);return qD.test(n)}function na(e,t){return Em(e,t)==="busy"}function Ui(e,t){return Em(e,t)!=="idle"}function Wo(e,t){let n=et[e];return n?.up?n.up.test(t):!0}function KD(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 Ji(r)}function
|
|
125
|
-
${o}`.trim()||t.slice(0,2e3)}function
|
|
126
|
-
`):""}async function
|
|
120
|
+
`);return e}function xx(e){let t=String(e||"").trim();return t?t.slice(0,32):null}function wx(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 gx(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(KD(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>=Ci()){let u=wx(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"&&jD.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"&&BD.test(l)&&!et.codex.up.test(l)&&i<3){if(!Am(e,"codex-trust")){await Mn(Au);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"&&DD.test(l)&&!et.cursor.up.test(l)&&i<3){if(!Am(e,"cursor-trust")){await Mn(Au);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=FD(l);if(u){if(!Am(e,`codex-update:${u.from}->${u.to}`)){await Mn(Au);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"}),lx({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=wx(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(bx(et[t],l))}).catch(()=>{}),await Mn(ta),"timeout"}await Mn(Au)}}function Hi({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 vx({agent:e,agentCmd:t}={}){let{realClaude:n,realCodex:r,realCursor:o}=Hi({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}=Hi({agent:n,agentCmd:r});return!t||!a&&!i?null:sr({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o}).then(()=>{},c=>{try{VD(c,{onBlocked:s,agent:n,sessionName:t})}catch{}})}function VD(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=MD(),bailOnDialog:a=!1,launchEcho:i=null,onReady:c}={}){let{label:l,realClaude:u,realCodex:d,realCursor:p}=Hi({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 gx(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 gx(n,f,s,{launchEcho:i}):(await Mn(ta),"settle")}var MD,Au,ta,CD,ND,et,DD,BD,jD,LD,UD,Tu,HD,yx,qD,GD,ra=q(()=>{ht();di();Ul();Hl();vu();Su();Rn();MD=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Au=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,ta=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,CD=/^\s{0,4}[^\s›•■◦╭╰│]\S*(?: \S+){0,3} · \S/,ND=/^\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:PD},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/}},DD=/Trust this workspace|\[a\]\s*Trust/,BD=/Do you trust the contents of this directory\?[\s\S]*›\s*1\. Yes, continue[\s\S]*Press enter to continue/,jD=/Resume from summary/i,LD=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;UD=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,Tu=new Map;HD=68,yx=/^\s*[─━]{10,}(?: [^─━\n]+ [─━]+)?\s*$/;qD=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,GD=15});function XD(e){return typeof e!="string"||!e?!1:e.includes(`
|
|
121
|
+
`)?!0:Buffer.byteLength(e,"utf8")>=YD}function Cn(e,t){return e==="codex"?{enter:!0,submitDelayMs:zD,...XD(t)?{paste:!0}:{}}:e==="claude"?{enter:!0,submitDelayMs:JD}:{enter:!0}}var zD,JD,YD,Wi=q(()=>{zD=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),JD=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),YD=(()=>{let e=Number(process.env.BOSTRAT_CODEX_PASTE_MIN_BYTES??512);return Number.isFinite(e)&&e>=0?e:512})()});function qi(e,t){if(e==="codex")return n1(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(Sx.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(Sx.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(ZD);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 n1(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]){(QD.test(t[o])||e1.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(t1.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(r1,(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 Ax(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 Tx(e,t,n,{busy:r=!1}={}){let o=qi(e,n);return o&&Eu(t,o)||o&&e==="codex"&&Rm(t,o)?"stuck":r&&e!=="codex"||o?"submitted":"unknown"}function Eu(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 Sx,ZD,$m,QD,e1,t1,r1,Gi=q(()=>{Sx=/^\s*─{8,}(?: [^─\n]+ ─+)?\s*$/,ZD=/^❯[ ]?(.*)$/,$m=/^\s*›[ ]?(.*)$/,QD=/\S+ · \S/,e1=/^\s*(?:\? for shortcuts|tab to queue message)\b/,t1=/^\s*(?:[•■◦✶✻⚠]|╭|╰|│)/;r1=/\[Pasted Content (\d+) chars\](?: #\d+)?/g});async function Vi({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(!o1.has(t))return l("skipped",!0);let u=i.waitForUp??vx({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||c1,w=i.now||Date.now,m=i.log||(L=>fe(L).catch(()=>{})),g=i.maxMs??i1(),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(Ki[0]);;){let L=await d(e).catch(()=>""),B=Wo(t,L),A=na(t,L),I=Tx(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(s1);continue}if(M=!0,I==="stuck"){if(k>=a1)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(Ki[0]);continue}if(S+=1,S>=Ki.length||w()>=y)return l("assumed",!0,{retries:k,sawUp:!0});$+=1,await f(Ki[Math.min($,Ki.length-1)])}}var o1,Ki,s1,a1,i1,c1,Om=q(()=>{ht();ra();Gi();Rn();o1=new Set(["claude","codex"]),Ki=[250,450,700],s1=500,a1=2,i1=()=>{let e=Number(process.env.BOSTRAT_SUBMIT_VERIFY_MAX_MS);return Number.isFinite(e)&&e>0?e:3e4},c1=e=>new Promise(t=>setTimeout(t,e))});function Ex(e){return String(e||"").replace(/[\u2018\u2019\u201A\u02BC\u0060\u00B4]/g,"'").replace(/[\u201C\u201D\u201E]/g,'"')}function ar(e){let t=Ex(e).toLowerCase();if(!t.trim())return null;let n=null;for(let r of l1){let o=0;for(let s of u1[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function Ix(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))d1.has(r)&&(t+=o||0),p1.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function zi(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 _x(e){let t=Ex(e).trim();return t?m1.test(t)?!0:t.split(/\s+/).length<=f1:!1}var l1,u1,d1,p1,f1,m1,Iu=q(()=>{l1=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],u1={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/],"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/],"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/],design:[/\bdesign/,/\bui\b/,/\bux\b/,/\bstyl(e|es|ing)\b/,/\bcss\b/,/\blayout\b/,/\bmock-?up/,/\brender(ing|s)?\b/,/\bpolish/,/\btheme\b/,/\bfonts?\b/,/\bresponsive\b/,/\banimation/,/\bvisual/,/\bpopover\b/],orchestration:[/\bhand[ -]?(off|over)/,/\bhand (it|the lead|this) (over|off)\b/,/\bdelegat/,/\bsubtask/,/\bmerge[ -]?back\b/,/\borchestrat/,/\b(switch|change) (the )?model\b/,/\bmodel switch/],"docs-writing":[/\bdocs?\b/,/\bdocument/,/\bmemo\b/,/\breadme\b/,/\bwrite-?up\b/,/\bchangelog\b/,/\bclaude\.md\b/,/\bblog/,/\bproofread/,/\bcopy(edit|writ)/,/\b(linked[ -]?in|social( media)?) post\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/]};d1=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),p1=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);f1=12,m1=/^\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 Mx(e){let t=zi(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||h1.has(r)||n.push(r);return n}function w1(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(Ox(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(Ox(n,"assistant",o))}}return t}function Ox(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 Ji(e){let t=w1(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=Mx(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(Mx(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+$x*(1-Rx+Rx*(g/(e.avgdl||1))),b=f*(m*($x+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 $x,Rx,h1,Mm=q(()=>{Iu();$x=1.5,Rx=.75,h1=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 $u from"node:fs/promises";import Cx from"node:path";async function g1(e){let t=Cx.join(e,".bostrat","events"),n;try{n=await $u.readdir(t)}catch{return Ji([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await $u.readFile(Cx.join(t,o),"utf8");for(let a of s.split(`
|
|
124
|
+
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return Ji(r)}function y1(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 b1(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?["",`## ${Nx}, 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 Px(e,t,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await $u.readFile(t,"utf8");if(a.includes(Nx))return{appended:!1,reason:"already-enriched"};let i=await g1(e);if(!i.N)return{appended:!1,reason:"empty-index"};let c=b1(i,y1(a),{k:r,maxChars:o,exclude:s});return c?(await $u.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 Nx,Dx=q(()=>{Mm();Nx="Relevant prior discussion (verbatim, retrieved"});function T1(e){return k1.test(String(e||"").trimStart())}function Lx(e){let t=String(e||"");if(!T1(t.trimStart()))return null;let n=t.replace(x1,"").trim(),r=E1(n),o=[];for(let u of S1){let d=u.from.map(p=>oa(r.get(p))).find(p=>p&&!Bx(p));d&&o.push({h:u.h,text:Yi(d,v1)})}let s=oa(r.get("Current Work"))||oa(r.get("Primary Request and Intent"))||_1(n),a=Ou(s),i=Yi(Ru(Pm(a[0]||"")),A1)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(Yi(Ru(Pm(l)),Nm));for(let u of["Pending Tasks","Optional Next Step"]){let d=Ou(oa(r.get(u)))[0]||"";if(!(!d||Bx(d))){c.push(Yi(`Left to do: ${Ru(Pm(d))}`,Nm));break}}if(!c.length){let u=Ou(oa(r.get("Problem Solving"))||"")[0];u&&c.push(Yi(Ru(u),Nm))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function E1(e){let t=new Map,n=[];Cm.lastIndex=0;for(let r=Cm.exec(e);r;r=Cm.exec(e))n.push({name:I1(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 I1(e){return jx.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 _1(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return oa(t.split(/\n{2,}/).slice(0,2).join(" "))}function Ou(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 Ru(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function Bx(e){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(e||"").trim())}function Yi(e,t){let n=String(e||"").trim();if(n.length<=t)return n;let r=Ou(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 k1,jx,Cm,x1,v1,S1,A1,Nm,Fx=q(()=>{k1=/^This session is being continued from a previous conversation that ran out of context\./,jx=["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}(${jx.join("|")})[:*\t ]*$`,"gim"),x1=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,v1=420,S1=[{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"]}],A1=116,Nm=168});function Wx(e){return Dm.has(String(e||"").replace(/^\//,"").toLowerCase())}function Xi(e){let t=String(e||"").trim();if(!t)return null;let n=Lx(t);if(n)return n;let r=t.match($1);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(R1);if(o)return{kind:"skill",name:o[1]};let s=t.match(O1);if(s)return{kind:"skill",name:s[1].toLowerCase()};let a=t.match(M1);if(a){let c=a[1].toLowerCase();return Dm.has(c)?{kind:"suppress"}:{kind:"command",command:`/${c}`}}let i=t.match(H1);if(i&&Dm.has(i[1].toLowerCase()))return{kind:"suppress"};if(C1.test(t))return{kind:"suppress"};if(P1.test(t))return{kind:"suppress"};if(N1.test(t))return{kind:"suppress"};if(D1.test(t)){let c=u=>K1(t.match(u)?.[1].trim()||"")||null,l=c(U1);return{kind:"task",summary:c(B1)||"",taskId:c(j1),toolUseId:c(L1),status:c(F1)?.toLowerCase()||null,resultHead:l?l.length>Ux?`${l.slice(0,Ux).trimEnd()}\u2026`:l:null}}return null}function qx(e){let t=Xi(e?.text);return t||(e?.injected?{kind:"skill",name:e.injected.skill||"skill"}:null)}function Gx(e){let t=String(e||""),n=t.match(W1);if(n)return{kind:"shell-input",command:n[1].trim()};let r=t.match(q1);return r?{kind:"shell-output",stdout:Hx(r[1]),stderr:Hx(r[2])}:null}function K1(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function Kx(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 Vx(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var $1,R1,O1,M1,C1,N1,P1,D1,B1,j1,L1,F1,U1,Ux,Dm,H1,W1,q1,G1,Hx,Mu=q(()=>{Fx();$1=/^Base directory for this skill:\s*(\S+)/,R1=/^Run the "([^"]+)" workflow\./,O1=/^#\s*\/([a-z0-9:_-]+)\s*[—–:-]/i,M1=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,C1=/^<local-command-(?:stdout|caveat)>/i,N1=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,P1=/^Continue from where you left off\.$/,D1=/^<task-notification>/i,B1=/<summary>([\s\S]*?)<\/summary>/i,j1=/<task-id>([\s\S]*?)<\/task-id>/i,L1=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,F1=/<status>([\s\S]*?)<\/status>/i,U1=/<result>([\s\S]*?)(?:<\/result>|$)/i,Ux=600,Dm=new Set(["model","clear","compact","login","exit","quit"]);H1=/^\/([a-z0-9:_-]+)$/i;W1=/^\s*<bash-input>([\s\S]*?)<\/bash-input>\s*$/i,q1=/^\s*<bash-stdout>([\s\S]*?)<\/bash-stdout>\s*(?:<bash-stderr>([\s\S]*?)<\/bash-stderr>)?\s*$/i,G1=/\x1b\[[0-9;?]*[ -/]*[@-~]/g,Hx=e=>String(e||"").replace(G1,"").replace(/^\n+/,"").replace(/\s+$/,"")});import V1 from"node:crypto";function X1(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,Jx)){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,z1)){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<J1)return null;n.push({question:o,header:sa(r?.header).slice(0,Y1),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function Bm(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;zx.lastIndex=0;let t=null;for(let o;o=zx.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=X1(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function jm(e,t,n){let r=V1.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=Jx}={}){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 Yx(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 Jx,z1,J1,Y1,zx,sa,Xx,Fm=q(()=>{Jx=4,z1=4,J1=2,Y1=24,zx=/^[ \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():"";Xx=`
|
|
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,82 +150,87 @@ 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 HB(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 zx(e,t,n){let r=Fm(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 Mn(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=(k,h)=>{d.set(k,h),t.push(h)},f=k=>{let h=d.get(k);h&&(h.retired=!0)},w=/^Agent "([\s\S]+)" finished$/,m=new Map;for(let k of e||[]){if(k.type!=="tool.called"||k.source!=="transcript")continue;let h=String(k.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(k.payload?.ts??k.ts)}}let g=3*6e4,y=20*6e4,x=(k,h)=>!!(k?.name&&m.get(String(k.name).toLowerCase()))?.some(D=>D>=h-g&&D<=h+y);for(let k of e||[])if(k.type==="turn.retracted"){r.add(`${k.boxId}|${k.payload?.saidTs}`);let h=Ue(k.payload?.text);h&&(o.has(k.boxId)||o.set(k.boxId,[]),o.get(k.boxId).push(h))}else if(k.type==="turn.user"&&k.source==="orchestrator"){let h=Ue(k.payload?.text);h&&(s.has(k.boxId)||s.set(k.boxId,new Set),s.get(k.boxId).add(h))}else if(k.type==="job.stalled"&&k.payload?.id)i.set(k.payload.id,{ts:he(k),quietMs:Number(k.payload.quietMs)||null});else if(k.type==="job.started"&&k.payload?.id)a.add(k.payload.id),(k.payload.kind||"agent")==="agent"&&k.payload.name&&l.add(k.payload.name);else if(k.type==="turn.user"&&k.payload?.note?.kind==="task"){let h=k.payload.note,v={status:h.status||null,summary:h.summary||null,resultHead:h.resultHead??null,ts:he(k)};h.toolUseId&&c.set(h.toolUseId,v);let D=String(h.summary||"").match(w);D&&u.set(D[1],v)}let b=new Map,I=new Map,$=null,P=new Map,F=new Map,B=new Map,T=new Map,_=new Map,U=new Map,J=new Map,R=new Map,A=new Map,M=new Map,N=new Map,K=new Map,Y=new Map,fe=new Map,Ie=new Map,Ce=[],X=k=>Ce.push(k),et=k=>{if(!Ce.length)return;let h=he(k);for(let v of Ce){if(v.status!=="open"||v.box&&v.box!==k.boxId||(v.ts??0)>h)continue;let D=t.indexOf(v);D>=0&&t.splice(D,1),v.ts=h,t.push(v)}},Ee=new Map,se=new Map,ee=new Map,Oe=new Map,z=new Map,we=new Map,Fe=new Map,O=new Map,ce=()=>{for(let k of O.values())k.questions.length&&t.push(k);O.clear()},ve=null,wt=new Map,Q=6e4,ge=new Map,pt=(k,h)=>{let v=o.get(k);if(!v?.length)return!1;let D=Ue(h),re=s.get(k);return v.some(ke=>{if(!D.startsWith(ke))return!1;let Pe=Ue(D.slice(ke.length));return!!Pe&&!!re?.has(Pe)})};for(let k of e||[]){let h=k.payload||{};switch(k.type){case"turn.user":{if(k.source==="hook"||(Ce.length=0,!String(h.text||"").trim()&&!h.images?.length&&!h.attachments?.length))break;{let v=Fx(h.text);if(v){if(v.kind==="shell-input"){let D={id:k.id,kind:"shell",command:v.command,stdout:null,stderr:null,done:!1,ts:he(k),box:k.boxId,agent:k.provider};t.push(D),wt.set(k.boxId,D)}else{let D=wt.get(k.boxId);D?(D.stdout=v.stdout||null,D.stderr=v.stderr||null,D.done=!0,wt.delete(k.boxId)):t.push({id:k.id,kind:"shell",command:null,stdout:v.stdout||null,stderr:v.stderr||null,done:!0,ts:he(k),box:k.boxId,agent:k.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"&&jx(v.command))break;if(v.kind==="resume-summary"){t.push({id:k.id,kind:"resume-summary",title:v.title||"Context resumed from summary",bullets:v.bullets||[],sections:v.sections||[],rawChars:v.rawChars??null,ts:he(k),box:k.boxId,agent:k.provider});break}v.kind!=="suppress"&&t.push({id:k.id,kind:"event",type:Hx(v),name:v.name??null,command:v.command??null,summary:v.summary??null,status:v.status??null,ts:he(k),box:k.boxId,agent:k.provider});break}}if(r.has(`${k.boxId}|${he(k)}`)||k.source==="transcript"&&pt(k.boxId,h.text))break;if(h.kind==="merge-notify"||h.kind==="ship-notify"){t.push({id:k.id,kind:"event",type:h.kind==="ship-notify"?"ship-notified":"merge-notified",text:h.text,ts:he(k),box:k.boxId,agent:k.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"&&(ve=null);break}for(let v of P.values())v.status="stale";P.clear();for(let v of B.values())v.status="stale";B.clear();for(let v of Y.values())v.status="stale";Y.clear(),ve&&ve.box===k.boxId&&(t.push(HB(ve)),ve=null),t.push({id:k.id,kind:"message",role:"user",text:h.text,ts:he(k),box:k.boxId,agent:k.provider,boxRole:h.boxRole,...k.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:k.id,kind:"event",type:"peer-message",text:h.text,from:v.trackName||v.boxId||v.name||null,fromTrackId:v.trackId||null,ts:he(k),box:k.boxId,agent:k.provider});break}case"turn.agent":{if(h.model==="<synthetic>"&&/^No response requested\.$/.test(String(h.text||"").trim()))break;let v=X0(h,{codexFailedText:Uo});if(v){t.push({id:k.id,kind:"event",type:"provider-error",cls:v.cls,status:v.status??void 0,text:v.text,provider:k.provider,legacy:!0,at:he(k),ts:he(k),box:k.boxId,agent:k.provider});break}ce();let D=(h.images||[]).filter(tt=>!x(tt,he(k)));if(!String(h.text||"").trim()&&!D.length)break;if(k.provider==="cursor"){let tt=`${k.boxId}|${h.text}`;if(n.has(tt))break;n.add(tt)}(!ve||ve.box===k.boxId)&&(ve=null);let re=k.provider==="codex"?(h.tools||[]).filter(tt=>!wm.has(tt)&&!ji.has(tt)):h.tools||[],ke=h.text,Pe=null,St=Dm(String(h.text||""));St&&(ke=St.text,Pe={questions:St.questions}),t.push({id:k.id,kind:"message",role:"assistant",text:ke,ts:he(k),box:k.boxId,agent:k.provider,boxRole:h.boxRole,...Pe?{ask:Pe}:{},...re.length?{tools:re}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},...D.length?{images:D}:{}}),et(k),Pe&&(ve={id:k.id,groupId:Bm(k.provider,h.sourceId||k.id,Pe.questions),provider:k.provider,box:k.boxId,questions:Pe.questions,ts:he(k)});break}case"job.started":{if(!h.id)break;let v=he(k),D=c.get(h.id);if(!D&&(h.kind||"agent")==="agent"&&h.name){let At=u.get(h.name);At&&At.ts>=v&&(D=At)}let re=D?null:i.get(h.id)||null,ke={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":re?"stalled":"running",startedTs:v,finishedTs:D?.ts??re?.ts??null,quietMs:re?.quietMs??null,summary:D?.summary??null,resultHead:D?.resultHead??null},Pe=ge.get(k.boxId),St=Pe?.jobs[Pe.jobs.length-1]?.startedTs;if(Pe&&St!=null&&v-St<=Q)Pe.jobs.push(ke);else{let At={id:k.id,kind:"job-dock",dockId:h.id,ts:v,box:k.boxId,agent:k.provider,jobs:[ke]};ge.set(k.boxId,At),t.push(At)}break}case"handoff.done":t.push(mt(k,"lead-handoff"));break;case"subtask.delegated":t.push({...mt(k,"subtask-delegated"),to:k.boxId,toAgent:k.provider});break;case"subtask.merged":t.push(mt(k,"subtask-merged"));break;case"pr.state.changed":{if(!h.change)break;if(h.change==="opened"){for(let D of P.values())D.status="stale";P.clear();for(let D of B.values())D.status="stale";B.clear()}let v=mt(k,`pr-${h.change}`);if(v.box=k.boxId??null,Vx.has(h.change)&&h.number!=null){let D=zx(b,I,h);if(b.size>=2){for(let ke of I.values())for(let Pe of ke)Pe.grouped=!0;v.groupPrs=[...b.values()];let re=[v];for(let ke of b.keys())I.set(ke,re)}else{let re=I.get(D)??[];re.push(v),I.set(D,re)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let D=Fm(h),re=$&&!$.complete||b.size>=2;if(b.has(D))b.delete(D),I.delete(D);else if(h.number!=null)for(let ke of[...b.keys()])ke.endsWith(`#${h.number}`)&&(b.delete(ke),I.delete(ke));if(re&&h.number!=null){(!$||$.complete)&&($={id:k.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:he(k),ts:he(k),box:k.boxId,agent:k.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:he(k)}),$.remaining=[...b.values()],$.complete=b.size===0;break}}t.push(v);break}case"pr.offered":{if(!h.offerId)break;for(let D of P.values())D.status="stale";P.clear();for(let D of B.values())D.status="stale";B.clear();let v={id:k.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:k.source==="cli"||h.via==="cli"?"cli":"transcript",ts:he(k),box:k.boxId,agent:k.provider};P.set(h.offerId,v),F.set(h.offerId,v),t.push(v),v.source==="cli"&&X(v);break}case"pr.offer.answered":{let v=F.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}),P.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:k.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:he(k),box:k.boxId,agent:k.provider};B.set(h.offerId,v),T.set(h.offerId,v),t.push(v);break}case"pr.exists.answered":{let v=T.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=mt(k,v);if(v==="model-change"&&(D.to=h.model),v==="node-update"&&D.latest){let re=t.findIndex(ke=>ke.kind==="event"&&ke.type==="node-update"&&ke.latest===D.latest);if(re>=0){let ke=t[re];t[re]={...D,ts:ke.ts,at:ke.at,firstAt:ke.firstAt??ke.ts,updatedAt:D.ts};break}}t.push(D);break}case"track.transferred":case"track.recovered":t.push(mt(k,"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:k.id,kind:"view-card",viewId:h.viewId,name:h.name,slug:h.slug,port:h.port,origin:h.origin,ts:he(k),box:k.boxId,agent:k.provider});break}case"view.spinup.failed":{t.push({id:k.id,kind:"view-spinup-failed",name:h.name||null,reason:h.reason||"no-view",ts:he(k),box:k.boxId,agent:k.provider});break}case"mock.state.changed":{if(h.change==="picked"){t.push({id:k.id,kind:"mock-picked",mockId:h.mockId,name:h.name,group:h.group??null,version:h.version??null,siblings:h.siblings??[],ts:he(k),box:k.boxId,agent:k.provider});break}if(h.change==="deleted"){f(`mock:${h.mockId}`);break}if(h.change!=="created")break;p(`mock:${h.mockId}`,{id:k.id,kind:"mock-card",mockId:h.mockId,name:h.name,group:h.group??null,ts:he(k),box:k.boxId,agent:k.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:k.id,kind:"flow-card",flowId:h.flowId,name:h.name,stepCount:h.stepCount??null,mockIds:h.mockIds??[],ts:he(k),box:k.boxId,agent:k.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:k.id,kind:"runbook-card",runbookId:h.runbookId,name:h.name,ts:he(k),box:k.boxId,agent:k.provider});break}case"ticket.linked":{t.push({id:k.id,kind:"ticket-card",provider:h.provider||"linear",key:h.key,url:h.url,title:h.title??null,linkSource:h.linkSource??null,ts:he(k),box:k.boxId,agent:k.provider});break}case"box.fullscreen.detected":{if(!h.offerId)break;for(let D of J.values())D.box===(h.box||k.boxId)&&(D.status="stale",J.delete(D.offerId));let v={id:k.id,kind:"fullscreen-offer",offerId:h.offerId,status:"open",ts:he(k),box:h.box||k.boxId,agent:h.agent||k.provider};J.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",J.delete(h.offerId));break}case"ticket.move.offered":{if(!h.offerId)break;for(let D of _.values())D.key===h.key&&(D.status="stale",_.delete(D.offerId));let v={id:k.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:he(k),box:k.boxId,agent:k.provider};_.set(h.offerId,v),U.set(h.offerId,v),t.push(v);break}case"ticket.move.answered":{let v=U.get(h.offerId);v&&(v.status=h.action==="moved"?"moved":"dismiss",_.delete(h.offerId));break}case"ticket.state.changed":{for(let v of _.values())v.key===h.key&&v.to===h.to&&(v.status="stale",_.delete(v.offerId));if(h.mode==="offer")break;t.push(mt(k,"ticket-state"));break}case"ticket.commented":{t.push({id:k.id,kind:"ticket-comment",key:h.key,url:h.url??null,excerpt:h.excerpt??null,ts:he(k),box:k.boxId,agent:k.provider});break}case"ticket.flow.changed":t.push(mt(k,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let D of A.values())D.status="stale";A.clear();let v={id:k.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:he(k),box:k.boxId,agent:k.provider};A.set(h.proposalId,v),M.set(h.proposalId,v),t.push(v),X(v);break}case"ticket.create.answered":{let v=M.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",v.createdCount=h.createdCount??null,v.failedCount=h.failedCount??null,A.delete(h.proposalId));break}case"loop.create.offered":{if(!h.proposalId)break;for(let D of N.values())D.status="stale";N.clear();let v={id:k.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:he(k),box:k.boxId,agent:k.provider};N.set(h.proposalId,v),K.set(h.proposalId,v),t.push(v),X(v);break}case"loop.create.answered":{let v=K.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",N.delete(h.proposalId));break}case"followup.armed":{if(!h.armId)break;let v=h.replaces?Ie.get(h.replaces):null;v&&v.status==="armed"&&(v.status="replaced");let D={id:k.id,kind:"followup-armed",armId:h.armId,trigger:h.trigger??"now",label:h.label??"",prompt:h.prompt??"",why:h.why??null,status:"armed",ts:he(k),box:k.boxId,agent:k.provider};Ie.set(h.armId,D),D.trigger!=="now"&&t.push(D);break}case"followup.armed.cancelled":{let v=Ie.get(h.armId);v&&v.status==="armed"&&(v.status="cancelled");break}case"followup.offered":{if(!h.offerId)break;for(let re of Y.values())re.trigger===h.trigger&&(re.status="stale",Y.delete(re.offerId));let v=Array.isArray(h.rows)?h.rows:[];for(let re of v){let ke=re.armId?Ie.get(re.armId):null;ke&&ke.status==="armed"&&(ke.status="offered")}let D={id:k.id,kind:"followup-offer",offerId:h.offerId,trigger:h.trigger??"now",repo:h.repo??null,context:h.context&&typeof h.context=="object"?h.context:{},rows:v.map(re=>({followupId:re.followupId??null,armId:re.armId??null,label:re.label??"",prompt:re.prompt??null,promptPreview:re.promptPreview??null,promptHash:re.promptHash??null,origin:re.origin??"operator",why:re.why??null})),status:"open",answer:null,ts:he(k),box:k.boxId,agent:k.provider};Y.set(h.offerId,D),fe.set(h.offerId,D),t.push(D),D.trigger==="now"&&X(D);break}case"followup.answered":{let v=fe.get(h.offerId);v&&(v.status=h.action==="sent"?"sent":h.action==="removed"?"removed":"dismiss",v.answer={followupId:h.followupId??null,armId:h.armId??null,label:h.label??null,promoted:h.promoted??null,revived:h.revived===!0},Y.delete(h.offerId));break}case"followup.create.offered":{if(!h.proposalId)break;for(let D of Ee.values())D.status="stale";Ee.clear();let v={id:k.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:he(k),box:k.boxId,agent:k.provider};Ee.set(h.proposalId,v),se.set(h.proposalId,v),t.push(v),X(v);break}case"followup.create.answered":{let v=se.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",Ee.delete(h.proposalId));break}case"followup.created":{let v=h.proposalId?se.get(h.proposalId):null;v&&(v.created={followupId:h.followupId??null});break}case"loop.created":{let v=h.proposalId?K.get(h.proposalId):null;v&&(v.created={loopId:h.loopId??null,nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null}),t.push(mt(k,"loop-created"));break}case"loop.update.offered":case"loop.delete.offered":{if(!h.proposalId||!h.loopId)break;for(let re of ee.values())re.loopId===h.loopId&&(re.status="stale",re.staleReason="superseded",ee.delete(re.proposalId));let v=k.type==="loop.update.offered",D={id:k.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:he(k),box:k.boxId,agent:k.provider};ee.set(h.proposalId,D),Oe.set(h.proposalId,D),t.push(D),X(D);break}case"loop.update.answered":{let v=Oe.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="applied"?"applied":"dismiss",ee.delete(h.proposalId));break}case"loop.delete.answered":{let v=Oe.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="delete"?"deleted":h.action==="pause"?"paused":"keep",ee.delete(h.proposalId));break}case"loop.updated":{let v=h.proposalId?Oe.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(mt(k,"loop-updated"));break}case"loop.deleted":t.push(mt(k,"loop-deleted"));break;case"loop.paused":t.push(mt(k,"loop-paused"));break;case"planning.change.offered":{if(!h.proposalId||!Array.isArray(h.ops)||!h.ops.length)break;for(let D of z.values())D.status="stale",D.staleReason="superseded",z.delete(D.proposalId);let v={id:k.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:he(k),box:k.boxId,agent:k.provider};z.set(h.proposalId,v),we.set(h.proposalId,v),t.push(v),X(v);break}case"planning.change.answered":{let v=we.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="applied"?"applied":"dismiss",z.delete(h.proposalId));break}case"planning.changed":{let v=h.proposalId?we.get(h.proposalId):null;v&&(v.applied={results:Array.isArray(h.results)?h.results:[]}),t.push(mt(k,"planning-changed"));break}case"rework.cause.offered":{if(!h.sha||Fe.has(h.sha))break;let v={id:k.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:he(k),box:k.boxId,agent:k.provider};Fe.set(h.sha,v),t.push(v);break}case"rework.cause.answered":{let v=h.sha?Fe.get(h.sha):null;v&&(v.status="answered",v.cause=h.cause??null);break}case"rework.cause.retracted":{let v=h.sha?Fe.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?M.get(h.proposalId):null;if(D){D.created=v;break}t.push({id:k.id,kind:"ticket-created",...v,ts:he(k),box:k.boxId,agent:k.provider});break}case"error":{if(!h.kind)break;t.push(mt(k,h.kind));break}case"provider.error":t.push({...mt(k,"provider-error"),provider:k.provider,box:k.boxId,agent:k.provider});break;case"usage.cap.hit":t.push(mt(k,"usage-cap-hit"));break;case"usage.cap.warning":t.push(mt(k,"usage-cap-warning"));break;case"provider.connected":{t.push({id:k.id,kind:"event",type:"provider-connected",provider:h.provider||k.provider,ts:he(k),at:he(k),box:k.boxId,agent:k.provider});break}case"prompt.answered":{if(ve=null,h.control||!h.question)break;let v=h.groupId||h.promptId;if(!v)break;let D=O.get(v);D||(D={id:k.id,kind:"prompt",groupId:v,provider:k.provider,box:k.boxId,agent:k.provider,questionTotal:h.questionTotal??1,questions:[],ts:he(k)},O.set(v,D));let re=h.questionIndex??0,ke={index:re,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},Pe=D.questions.findIndex(tt=>tt.index===re&&Ue(tt.question)===Ue(ke.question));Pe>=0?D.questions[Pe]=ke:D.questions.push(ke),D.questions.sort((tt,At)=>tt.index-At.index),D.questionTotal=Math.max(D.questionTotal,h.questionTotal??1),D.questions.length>=D.questionTotal&&(t.push(D),O.delete(v));break}case"prompt.dismissed":{ve=null;let v=h.groupId||h.promptId;if(!v||!h.question)break;let re=O.get(v)||{id:k.id,kind:"prompt",groupId:v,provider:k.provider,box:k.boxId,agent:k.provider,questionTotal:h.questionTotal??1,questions:[],ts:he(k)},ke=h.questionIndex??0;re.questions.some(Pe=>Pe.index===ke&&Ue(Pe.question)===Ue(h.question))||(re.questions.push({index:ke,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:[]}),re.questions.sort((Pe,St)=>Pe.index-St.index)),re.answeredVia="chat",t.push(re),O.delete(v);break}default:break}}let rt=d.size?t.filter(k=>!k.retired):t;return qB(rt.sort((k,h)=>(k.ts??0)-(h.ts??0)))}function qB(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<=WB){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 Um(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:KB(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??Mn(e)).filter(o=>o.kind==="message");return W0(n,r)}function KB(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==Xs(e.command)?t:Wr(e.command,e.description||"")}function mt(e,t){let{ts:n,change:r,kind:o,...s}=e.payload||{};return{id:e.id,kind:"event",type:t,...s,at:he(e),ts:he(e)}}function Jx(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)+GB,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&VB.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function Yx(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!JB.has(r.type)||!r.box)continue;let o=(r.ts??0)+zB,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=>YB.has(t?.kind)&&t.status==="open")}function Xx(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(Vx.has(o.change)&&o.number!=null)zx(t,n,o);else if(o.change==="merged"||o.change==="closed"||o.change==="lost"){let s=Fm(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 he,Vx,Fm,WB,GB,VB,zB,JB,YB,Nu=W(()=>{Zs();Ho();ku();Ki();Mu();Lm();he=e=>e.payload?.ts??e.ts;Vx=new Set(["opened","ci-fixed","reconciled","pushed"]),Fm=e=>`${e.repo??""}#${e.number}`;WB=2*6e4;GB=12e4,VB=new Set(["archived","unarchived","opened"]);zB=2500,JB=new Set(["pr-opened","pr-merged","pr-auto-armed"]);YB=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 t1(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 r1(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 o1(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<QB&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<e1&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function tv(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=t1(e),s=r1(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=Mn(e??[]).filter(p=>p?.kind==="message"&&typeof p.text=="string"&&p.text.trim()).slice(-XB);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(ev[r]??ev.failover),l.push(""),o.lastPush?.sha){let p=Qx(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=Qx(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=o1(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"}:** ${n1(p.text,ZB)}`),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 m1(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(sv.test(s)){if(r.length>=wv)continue;let a=f1(s.replace(sv,"").replace(/[.;,:]+$/,""),gv,d1);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>ov&&(o=`${o.slice(0,ov-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function yv(e,t,{timeoutMs:n=9e4}={}){let r=Gr.join(t,l1),o=Gr.join(r,u1);await Kr.mkdir(r,{recursive:!0});let s=await Pu(o),a=await be(e);if(!a)throw new Error(`session "${e}" not found`);return await Du(e,a,p1(o)),await Tv(o,s,n),m1(await Kr.readFile(o,"utf8"))}function bv(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===pv||n===hv;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(dv)||n===a1||r.startsWith(fv)||r.startsWith(mv)}function kv(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-h1;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 xv(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 vv(e,{windows:t=[]}={}){if(bv(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 Hm.get(e)||null}async function Sv(e){let t=Hm.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await be(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:Gr.join(n.cwd,cv,lv)};return Hm.set(e,r),y1(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function y1(e,t){e.step="summarize",await Av(e.name,t),e.step="clear";let n=await be(e.name);await Du(e.name,n,"/clear"),await Wm(w1),e.step="resume",await Du(e.name,n,c1),e.step="done",e.finishedAt=Date.now()}async function Av(e,t,{timeoutMs:n=g1}={}){let r=Gr.join(t,cv),o=Gr.join(r,lv);await Kr.mkdir(r,{recursive:!0});let s=await Pu(o),a=await be(e);await pe({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await Du(e,a,i1(o)),await Tv(o,s,n),await pe({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await Pu(o)});let i=await Rx(t,o,{exclude:u=>bv(u.role,u.text)});await pe({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(c){await C({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 Kr.readFile(o,"utf8");await C({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...uv(u),ts:Date.now()}})}catch{}}await qf(t);let l=await b1(r,o);return c&&await l0(c,l||o,{box:e,agent:a?.agent??null}),o}async function ju(e,{timeoutMs:t}={}){let n=await be(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return Av(e,n.cwd,t?{timeoutMs:t}:void 0)}async function Lu(e,t,{trackName:n="this Track"}={}){let r=await q({id:e,cwd:t}),o=tv(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=Gr.join(t,av);return await Kr.mkdir(Gr.dirname(s),{recursive:!0}),await Kr.writeFile(s,o,"utf8"),await C({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...uv(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),av}async function Pu(e){try{return(await Kr.stat(e)).mtimeMs}catch{return 0}}async function Tv(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 Wm(2e3)}}async function b1(e,t){try{if(!s1.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=Gr.join(e,`${n}.md`);return await Kr.copyFile(t,r),r}catch{return null}}function Wm(e){return new Promise(t=>setTimeout(t,e))}function iv(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=iv(t.agent);await sr({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await be(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await Ge(e,["Escape"],{kind:"drive-wipe"}),await Wm(150),await Vs(e,gu,{kind:"drive-wipe"}),await at(e,n,{kind:"orchestration-drive",...On(r.agent,n)}),await Vi({sessionName:e,agent:r.agent,agentCmd:iv(r.agent),text:n,flow:"orchestration-drive"})}var cv,lv,rv,dv,pv,fv,a1,i1,c1,mv,hv,l1,u1,ov,wv,gv,d1,p1,sv,f1,h1,w1,g1,Hm,av,qm=W(()=>{ft();ta();In();Wi();Rm();Ox();xi();me();cu();nv();cv=".bostrat/handoff",lv="latest.md",rv=32*1024;dv="Checkpoint: write a work-in-progress handoff",pv="HANDOFF WRITTEN",fv="Resume from .bostrat/handoff/latest.md",a1="/clear",i1=e=>`${dv} 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 ${pv} and take no further action.`,c1=`${fv}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,mv="Beat: write a",hv="BEAT WRITTEN",l1=".bostrat/arc",u1="beat.txt",ov=200,wv=4,gv=5,d1=36,p1=e=>`${mv} 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 ${wv} further lines, each starting with "- " and naming ONE piece in at most ${gv} 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 ${hv} and take no further action.`,sv=/^(?:[-–—*•·]|\d+[.)])\s+/,f1=(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};h1=6e5;w1=3500,g1=3e5,Hm=new Map;av=".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 Iv(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 Gm(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 x1(e){if(!e)return null;let t=String(e).trim();return Vr[t]?t:Ev[t]?Ev[t]:Object.keys(Vr).find(r=>t.startsWith(r))||null}function v1(e){if(!e||e===Vr)return k1;let t=_v.get(e);return t||(t=Iv(e),_v.set(e,t)),t}function $v(e,t,n={}){let{table:r,speed:o}=n,s=v1(r),a=r&&r!==Vr?s[e]?e:null:x1(e);if(!a)return null;let i=Gm(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 Cn(e,t={},n,r){let o=$v(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 S1(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 T1({now:e=Date.now(),table:t=Vr,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...A1,...n},a=[],i=Gm(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 oa({now:e=Date.now(),table:t=Vr,...n}={}){let r=Gm(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=[...S1(t),...T1({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var Vr,Ev,Fu,k1,_v,Rv,A1,Km,ir=W(()=>{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)."}]},Ev={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}$/;k1=Iv(Vr);_v=new WeakMap;Rv=(e,t,n)=>$v(e,t,n)!==null;A1=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),Km=864e5});function E1(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function Mv(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=E1(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=Cn(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&&!Rv(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=Ov();for(let p of n){let{usd:f,model:w,tokens:m,repriced:g,unpriced:y,asRecorded:x}=Xt(p);i+=x;let b=p.provider||"unknown",I=p.boxId||"unattributed";if(g&&c++,y&&(l++,u.add(w)),a+=f,Mv(d,m),r[w]||={usd:0,tokens:Ov(),events:0},r[w].usd+=f,r[w].events+=1,Mv(r[w].tokens,m),o[b]||={usd:0,events:0},o[b].usd+=f,o[b].events+=1,s[I]||={usd:0,events:0,provider:b,models:new Set,label:t[I]||I},s[I].usd+=f,s[I].events+=1,w!=="unknown"&&s[I].models.add(w),p.epoch!=null){let $=(s[I].epochs||={})[p.epoch]||={usd:0,events:0,provider:b,models:new Set};$.usd+=f,$.events+=1,$.provider=b,w!=="unknown"&&$.models.add(w)}}return{totalUsd:sa(a),asRecordedUsd:sa(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Uu(r,p=>({...p,usd:sa(p.usd)})),byProvider:Uu(o,p=>({...p,usd:sa(p.usd)})),byBox:Uu(s,p=>({usd:sa(p.usd),events:p.events,provider:p.provider,label:p.label,models:[...p.models],...p.epochs?{epochs:Uu(p.epochs,f=>({usd:sa(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var Ov,sa,Uu,Yr=W(()=>{ir();Ov=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});sa=e=>Math.round(e*1e4)/1e4,Uu=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function _1(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 Cv(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(Vm(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Vm(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(`${Vm(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)?_1(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Vm,Nv=W(()=>{Yr();Vm=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as I1}from"node:child_process";import{promisify as $1}from"node:util";import{randomUUID as R1}from"node:crypto";async function zm(e,t){let n=Date.now()+t;for(;;){if(await or(e))return!0;if(Date.now()>n)return!1;await Zi(O1)}}async function C1(e,t){let n=Pv[t]||Pv.default;n.escape&&(await Ge(e,["Escape"],{kind:"swap-exit"}),await Zi(150)),n.wipe&&await Vs(e,gu,{kind:"swap-exit"}),await yu(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=On(t).submitDelayMs||0;await Zi(Math.max(r,250)),await Ge(e,["Enter"],{kind:"swap-exit"})}async function Dv(e,t){try{let{stdout:n}=await Ko(ne,oe("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function Lv(e){await Ko(ne,oe("respawn-pane","-k","-t",e))}async function Jm(e,{exitTimeoutMs:t=Bv,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 be(e))?.agent||"agent";if(await C1(e,o),!await zm(e,t)&&(r=!0,await pe({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await Lv(e),!await zm(e,jv)))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);return{forced:r}}async function Ym(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 be(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)||Bv;await pe({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let{forced:i}=await Jm(e,{exitTimeoutMs:a,fromAgent:o}),c=s==="claude"&&jo(n),l=c?Ll():null,u=await Dv(e,"ANTHROPIC_API_KEY"),d=!1;try{l?(await Ko(ne,oe("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await Ko(ne,oe("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),d=!0)}catch(g){throw l?du(g,l):g}if(d&&!i&&(await Lv(e),!await zm(e,jv)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let p=!!t.resumeSessionId,f=t.resumeSessionId||R1(),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 Cs(r.cwd),s==="cursor"&&await Ns(r.cwd),await Lo(e),await Ko(ne,oe("set-option","-t",e,"@bostrat-sid",f)),await Ko(ne,oe("set-option","-t",e,"@bostrat-agent",s)),await Zi(M1),c&&Ri().length&&(await Dv(e,$i)||await Ko(ne,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 pe({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 Ko,Zi,Bv,jv,O1,M1,Pv,Fv=W(()=>{ft();di();Ul();Hl();Wi();In();Ko=$1(I1),Zi=e=>new Promise(t=>setTimeout(t,e)),Bv=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,jv=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,O1=250,M1=400,Pv={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 Xm(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Zm({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!Xm(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),Uv);try{let a=await Hv(`${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(!Xm(a))return{ok:!1,source:"unconfigured"};let c=Hu.get(e.id)||0;if(i()-c<N1)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 Zm({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(!Xm(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),Uv);try{let r=await Hv(`${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 Uv,Hv,N1,Hu,Wu=W(()=>{gt();Uv=1e4,Hv=(...e)=>fetch(...e);N1=10*6e4,Hu=new Map});import{execFile as P1}from"node:child_process";import qu from"node:fs/promises";import Qm from"node:path";import{promisify as D1}from"node:util";function W1(e){let n=String(e||"").trim().match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.+-]+)?/);return n?n[0]:null}async function q1(e,{env:t=process.env}={}){let n=String(e||"").trim();if(!n)return null;let r=n.includes("/")?[Qm.resolve(n)]:String(t.PATH||"").split(Qm.delimiter).filter(Boolean).map(o=>Qm.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 Go({agent:e=null,agentCmd:t=null,env:n=process.env}={}){let r=String(t||(e?n[F1[e]]||L1[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 q1(r.split(/\s+/)[0],{env:n});if(!i)return null;let c=`${i.path}@${i.mtimeMs}`;return eh.has(c)||eh.set(c,Promise.resolve().then(()=>H1(i.path,{agent:e})).then(l=>W1(l)).catch(()=>null)),eh.get(c)}var B1,j1,L1,F1,eh,U1,H1,Wv=W(()=>{ta();B1=D1(P1),j1=8e3,L1={claude:"claude",codex:"codex",cursor:"cursor-agent"},F1={claude:"BOSTRAT_AGENT_CMD",codex:"BOSTRAT_CODEX_CMD",cursor:"BOSTRAT_CURSOR_CMD"},eh=new Map,U1=async e=>(await B1(e,["--version"],{timeout:j1})).stdout,H1=U1});function J1(e){if(e.type==="queue-operation"&&e.operation==="enqueue")return qv.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"&&qv.test(t)?t:null}async function Kv(e,t={}){return(await Vv(e,t)).launches}async function Gv(e,t={}){let{launches:n,done:r}=await Vv(e,t);return n.filter(o=>!r.has(o.id))}async function Vv(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=
|
|
166
|
-
`),o=
|
|
167
|
-
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=
|
|
168
|
-
`)].reverse().find(a=>
|
|
169
|
-
`),o=
|
|
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
|
|
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
|
|
176
|
-
`).map(s=>s.trim()).filter(Boolean)}async function
|
|
177
|
-
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var
|
|
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 zS(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&&!GS.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=VS.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!Jj.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function oc(e,t,{timeoutMs:n=Vj}={}){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(zj)}}async function Zj(e,t){return await at(e,`/model ${t.model}`,On("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function Qj(e,t){await wu(e),await at(e,"/model",{enter:!1}),await nd(250),await Ge(e,["Enter"],{kind:"model-switch"});let n=await oc(e,HS);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await Ge(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=Xj(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=>WS(i)||!HS(i)&&o(i)&&{done:o(i)});if(!s)throw await Ge(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?GS.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 Ge(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=>!WS(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 eL(){let e=US.join(process.env.CURSOR_CONFIG_DIR||US.join(Gj.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await Kj.readFile(e,"utf8"))?.model||null}catch{return null}}async function tL(e,t){await wu(e),await yu(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await oc(e,qS),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,qS,{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 Ge(e,["Down"],{kind:"model-switch"}),await nd(250)}if(!n||!s||!o(s)){await Ge(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await Ge(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 Ge(e,["Enter"],{kind:"model-switch"}),await nd(800);let a=await eL();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 JS(e,t,n){return e==="codex"?Qj(t,n):e==="cursor"?tL(t,n):Zj(t,n)}var nd,Vj,zj,KS,Jj,Yj,GS,VS,YS=W(()=>{ft();Wi();nd=e=>new Promise(t=>setTimeout(t,e)),Vj=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,zj=300,KS=[{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"}],Jj=new Set([...KS.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"]),Yj=[{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"}],GS=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),VS=[{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=nL(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function nL(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:{...Pn(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",...Pn(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{change:"subtask-resumed",...Pn(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",...Pn(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...Pn(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...Pn(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:{...Pn(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Pn(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",...Pn(e)}};case"startup-gate":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:it(e.agent),payload:{kind:"startup-gate",...Pn(e)}};default:return null}}function QS(e,t){let n=sd(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;ad(()=>C(r,n))}async function eA(e){if(!(!e?.id||XS.has(e.id))){XS.add(e.id);try{let t=await q(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 C(e,o)}}catch{}}}function Pn(e){let{type:t,at:n,...r}=e;return r}function tA(e,t,n){!n?.promptId||od.get(e.id)===n.promptId||(od.set(e.id,n.promptId),ad(()=>C(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 nA(e,t,n,r={}){od.delete(e.id),ad(()=>C(e,{type:"prompt.dismissed",actor:"user",source:"orchestrator",boxId:t?.name,provider:it(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}function Nh(e,t,n,r={}){od.delete(e.id),ad(()=>C(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,ZS,XS,od,Ph=W(()=>{me();it=e=>e||null;rd=Promise.resolve(),ad=e=>(rd=rd.then(e).catch(()=>{}),rd),ZS=()=>rd;XS=new Set;od=new Map});function iL(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function cA(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of iL(n).reverse()){if(!rL.test(r))continue;let o=r.endsWith("?")&&r.length<=aL&&sL.test(r);if(oL.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function uA(e){let t=String(e||""),n=[];cL.test(t)&&n.push("commit"),lA.test(t)&&n.push("rebase"),lL.test(t)&&n.push("push");let r=dL.test(t);!r&&uL.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 dA(e){return lA.test(String(e||""))}function ma(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 pA(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 fA(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<=pL&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function mA(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+fL||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function hA(e){let t=(Array.isArray(e)?e:[]).filter(r=>rA[r]).map(r=>rA[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 oA,sA,aA,iA,rL,oL,sL,aL,ro,cL,lA,lL,uL,dL,pL,fL,rA,ts=W(()=>{oA=/(?<!\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,sA=/(?<!\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,aA=/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,iA=/(?<!\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,rL=new RegExp(`\\b(?:${oA}|${sA}|${aA}|${iA})\\b`,"i"),oL=/\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,sL=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,aL=90;ro=["commit","rebase","push","pr"],cL=new RegExp(`\\b(?:${oA})\\b`,"i"),lA=new RegExp(`\\b(?:${iA})\\b`,"i"),lL=new RegExp(`\\b(?:${sA})\\b`,"i"),uL=new RegExp(`\\b(?:${aA})\\b`,"i"),dL=/\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;pL=3e4;fL=2500;rA={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import mL from"node:crypto";import id from"node:fs/promises";import wA from"node:path";import hL from"node:os";function kA(e){bA=typeof e=="function"?e:async()=>new Set}function wL(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||(Dh??=id.readFile(jh,"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 lt("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){let o=a0(),s=await bA().catch(()=>new Set),a=0;for(let i of n){if(i.deletedAt||!i.data?.repo||i.data?.number==null)continue;if(!wL(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 Lh().catch(()=>{})}}return lr}).finally(()=>Dh=null),Dh)}async function Lh(){let e=JSON.stringify({links:lr},null,2),t=`${jh}.${process.pid}.tmp`;await id.mkdir(gA,{recursive:!0}),await id.writeFile(t,e),await id.rename(t,jh)}async function Ye(e){Bh=Bh.then(Lh,Lh),await Bh.catch(()=>{});let t=lr?.[e];t&&Tt("prlinks",{id:yA(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:gL.has(a),firstSeen:Date.now()},await Ye(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 Ye(ie(e,t)),n)}async function xA(e,t){if(!e||t==null)return!1;let n=(await He())[ie(e,t)];return n?.notifyOnMerge?(delete n.notifyOnMerge,await Ye(ie(e,t)),!0):!1}async function ut(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 Ye(ie(e,t)),r):null}async function vA(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 Ye(ie(e,t)),o):null}async function SA(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 Ye(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 Ye(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 Ye(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 Ye(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 Ye(ie(e,t)),n)}async function AA(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 Ye(ie(e,t)),r):null}async function TA(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 Ye(ie(e,t)),o):null}async function EA(e,t){if(!e||t==null)return null;let n=(await He())[ie(e,t)];return n?.brokerRefused?(delete n.brokerRefused,await Ye(ie(e,t)),n):null}async function _A(e,t){if(!e||t==null)return null;let n=(await He())[ie(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Ye(ie(e,t)),n)}async function IA(e,t,n){if(!e||t==null||!n)return null;let r=(await He())[ie(e,t)];return r?(r.lastHeadSha=n,await Ye(ie(e,t)),r):null}async function $A(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 Ye(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 Ye(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 Ye(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 Ye(ie(e,t)),r):null}async function RA(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 Ye(ie(e,t)),r}async function OA(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 Ye(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 Ye(ie(e,t)),n)}async function MA(){return Object.values(await He()).filter(e=>!yL.has(e.state))}async function mn(){return Object.values(await He()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function CA(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await lt("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 Tt("prlinks",{id:yA(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var gA,jh,ie,yA,lr,Dh,Bh,bA,gL,yL,Dn=W(()=>{Fr();gA=process.env.BOSTRAT_STATE_DIR||wA.join(process.env.HOME||hL.tmpdir(),".bostrat"),jh=wA.join(gA,"pr-links.json"),ie=(e,t)=>`${e}#${Number(t)}`,yA=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=mL.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},lr=null,Dh=null,Bh=Promise.resolve(),bA=async()=>new Set;gL=new Set(["api","observed","reconciled"]);yL=new Set(["merged","closed","lost"])});import bL from"node:fs";import Fh from"node:fs/promises";import BA from"node:path";import kL from"node:os";function FA(e,t=!1){if(!t&&e-PA<xL)return;PA=e;let n=JSON.stringify({windowStart:ha,byCaller:Object.fromEntries(dr),lastHour:so});DA=DA.then(()=>Fh.mkdir(jA,{recursive:!0})).then(()=>Fh.writeFile(`${pd}.tmp`,n)).then(()=>Fh.rename(`${pd}.tmp`,pd)).catch(()=>{})}function UA(e){e-ha<LA||(so={start:ha,total:fd(dr),byCaller:Object.fromEntries(dr)},so.total&&console.log(`[github] ${so.total} calls last hour`,so.byCaller),ha=e,dr=new Map,FA(e,!0))}function pr(e="other",t=Date.now()){UA(t),dr.set(e,(dr.get(e)||0)+1),FA(t)}function HA(e=Date.now()){return UA(e),{windowStart:ha,total:fd(dr),byCaller:Object.fromEntries(dr),lastHour:so,backoffUntil:os>e?os:null}}function ao(e){return vL.test(String(e?.message||e||""))}function io(e,t=Date.now()){return os<=t&&console.warn(`[github] rate limited \u2014 backing off ${NA/6e4} min:`,String(e?.message||e||"").slice(0,200)),os=Math.max(os,t+NA),os}function md(e=Date.now()){return Math.max(0,os-e)}var jA,pd,LA,NA,xL,ha,dr,so,os,PA,fd,DA,vL,ic=W(()=>{jA=process.env.BOSTRAT_STATE_DIR||BA.join(process.env.HOME||kL.tmpdir(),".bostrat"),pd=BA.join(jA,"github-metrics.json"),LA=60*6e4,NA=5*6e4,xL=6e4,ha=Date.now(),dr=new Map,so=null,os=0,PA=0,fd=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(bL.readFileSync(pd,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<LA?(ha=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{}DA=Promise.resolve();vL=/rate limit|too quickly|abuse detection|HTTP 429/i});import WA from"node:fs/promises";import qA from"node:path";import SL 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>TL&&hd.shift();let u=Uh.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)),Uh.set(e,u),EL(l),l}async function EL(e){try{await WA.mkdir(KA,{recursive:!0}),await WA.appendFile(AL,JSON.stringify(e)+`
|
|
182
|
-
`)}catch{}}function GA(){let e={};for(let[t,n]of Uh)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 KA,AL,TL,hd,Uh,gd=W(()=>{KA=process.env.BOSTRAT_STATE_DIR||qA.join(process.env.HOME||SL.tmpdir(),".bostrat"),AL=qA.join(KA,"github-latency.ndjson"),TL=100,hd=[],Uh=new Map});import{execFile as _L}from"node:child_process";import{promisify as IL}from"node:util";async function $L(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([ga("git",["-C",e,"rev-parse","HEAD"]),ga("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await C({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 En({cwd:e});pr(r);try{let{stdout:a}=await ga("gh",t,{cwd:e,...RL,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw ao(a)&&io(a),a}}async function qh(){if(yd&&Date.now()-yd.at<6e4)return yd.data;let e;try{pr("auth");let t=await ga("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 OL(e){try{let{stdout:t}=await ga("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function zA(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 Hh(e){let t=(e||[]).map(zA),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 Gh(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function YA(e,t){if(!t)return null;let n=await Re(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 XA(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 ZA(e,t){let n=await YA(e,t);return n===null?{...Hh([]),unavailable:!0}:Hh(XA(n))}function bd(e,t=Hh(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 CL(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){wa.delete(s);continue}r.add(s),wa.has(s)||wa.set(s,n),o.mergeabilityPending=n-wa.get(s)<ML}for(let o of wa.keys())o.startsWith(`${e}#`)&&!r.has(o)&&wa.delete(o);return t}async function ya(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(!Gh(o))throw o;let s=await Qt(n.path,["pr","view",String(r),"--json",JA]),a=JSON.parse(s);return bd(a,await ZA(n.path,a.headRefOid))}}async function Bn(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(VA)}catch(n){if(!Gh(n))throw n;let r=await Qt(e,["pr","list","--json",JA,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>bd(o,await ZA(e,o.headRefOid))))).sort(VA)}}function en(){cc.clear()}async function DL(e,t){if(!e)return t;for(let n of t){let r=await ut(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 QA(){let e=await qh();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await nr())await OL(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?PL:NL;if(i&&(r||n-i.at<c))return i.entry;try{let l=await Re(a.path).catch(()=>null),u=CL(a.path,await Bn(a.path,{caller:"tab"}),n);if(await DL(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:jn(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 Ps(e)}async function ba(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 En({cwd:r.path})).GH_TOKEN){let a=await Nf().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Qt(r.path,s)}catch(a){throw new Error(jn(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(jn(s))}finally{en()}let o=await Re(n.path).catch(()=>null);return o&&await AA(o,r).catch(()=>{}),{ok:!0,number:r}}async function Vh(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(jn(o))}finally{en()}return{ok:!0,number:r}}function zh(e){return e.green&&!e.isDraft}function FL(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 eT(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:zh(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function tT(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 nT(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 rT(){let e={};for(let[t,n]of Wh)e[t]={...n};return e}async function oT(e,{method:t="merge",onMerged:n=null}={}){let r=await fr(e);if(!lc.has(t))throw new Error(`invalid merge method: ${t}`);if(Wh.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Bn(r.path)).filter(zh).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 Wh.set(r.path,s),HL(s,o,{listPrs:()=>Bn(r.path),merge:a=>ba(r.path,a,{method:t}),sleep:LL,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function UL(e,t){let n=Date.now()+(t.settleMaxMs??jL),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(BL)}}async function HL(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 UL(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: ${jn(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(!zh(a)){e.skipped.push({number:a.number,title:a.title,reason:FL(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:jn(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),en()}}async function Jh(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await ga("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${jn(l)}`)}await $L(e);let o=n||"";if((await En({cwd:e})).GH_TOKEN){let l=await Nf().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(
|
|
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<=rv?d:`${d.slice(0,rv)}
|
|
155
|
+
|
|
156
|
+
[\u2026 brief truncated]`}var iB,cB,rv,lB,uB,pB,ov,sv,iv=q(()=>{Nu();iB=12,cB=2e3,rv=48e3,lB=30,uB=10;pB=(e,t)=>{let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1)}\u2026`},ov=(e,t)=>e&&t?Math.max(1,Math.round((e-t)/6e4)):null;sv={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 hB from"node:fs";import Gr from"node:fs/promises";import Kr from"node:path";function hv(e){let t=Do(String(e??"")),n=Buffer.byteLength(t,"utf8");return n<=cv?{content:t,bytes:n,truncated:!1}:{content:`${Buffer.from(t,"utf8").subarray(0,cv).toString("utf8").replace(/�+$/,"")}
|
|
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=sj(d);if(p){let m=p.match(oj)?.[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=aj(m.content);if(tj.test(g)&&s.add(m.tool_use_id),g.match(nj)){let b=i.get(m.tool_use_id);b&&(i.delete(m.tool_use_id),b.proven=!0,b.outputFile=g.match(rj)?.[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 tj,nj,rj,Jv,oj,aj,Qv=q(()=>{pn();ea();tj=/^Async (?:agent|task) launched/i,nj=/^Command did not complete within its [^\n]*?moved to the background \(ID: ([^)]+)\)/i,rj=/Output is being written to:\s*(\S+)/i,Jv=/^\s*<task-notification>/i,oj=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i;aj=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 ij(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 eS(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=ij(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 Gu,fn,ca,nh,tS=q(()=>{Gu=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 tc from"node:fs";import cj from"node:fs/promises";import Vo from"node:path";import lj from"node:os";import uj from"node:crypto";import dj from"node:zlib";function oS(e){let t=uj.createHash("md5").update(String(e)).digest("hex");return Vo.join(pj,t)}function sS(e){try{let t=JSON.parse(tc.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 fj(e){let t=oS(e),n;try{n=tc.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(!tc.existsSync(a))continue;let i=sS(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 Ku(e,t){if(!e)return null;if(t){let n=Vo.join(oS(e),t),r=Vo.join(n,"store.db");if(tc.existsSync(r))return{id:t,dir:n,store:r,...sS(n)||{createdAtMs:null,updatedAtMs:null}}}return fj(e)}function nS(e,t){let n=e.prepare("SELECT data FROM blobs WHERE id = ?").get(t);return n?Buffer.from(n.data):null}function mj(e){if(!e)return null;let t=e;if(t.length>1&&t[0]===31&&t[1]===139)try{t=dj.gunzipSync(t)}catch{}try{return JSON.parse(t.toString("utf8"))}catch{return null}}function hj(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 wj(e,t){if(!Array.isArray(e))return[];let n=[];for(let r of e){if(!r||r.type!=="tool-call"||!Gu.has(r.toolName))continue;let o=eS(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(!rS)return la;let{DatabaseSync:n}=rS,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?nS(r,s.latestRootBlobId):null;if(!i)return{...la,model:a};let c=[],l=[],u=[],d=null,p=[];for(let f of hj(i)){let w=mj(nS(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(...wj(w.content,t).map((b,k)=>({...b,id:b.id||`${f}:${k}`}))),u.push(...yj(w.content));let g=gj(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 aS(e,t){let n=e.updatedAtMs;if(!n)try{n=Math.round(tc.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 iS(e,t,{limit:n=80}={}){let r=Ku(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}=aS(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 cS(e,t){let n=Ku(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 lS(e,t,{limit:n=80}={}){let r=Ku(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}=aS(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 uS(e,t){let n=Ku(e,t);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await cj.stat(n.store)).mtimeMs)}catch{return null}}var pj,gj,yj,rS,la,oh=q(()=>{ea();tS();pj=process.env.BOSTRAT_CURSOR_CHATS||Vo.join(lj.homedir(),".cursor","chats");gj=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():"",yj=e=>Array.isArray(e)?e.filter(t=>t&&t.type==="tool-call"&&t.toolName).map(t=>t.toolName):[],rS=process.getBuiltinModule?.("node:sqlite"),la={turns:[],trailingCommands:[],model:null,requestIds:[]}});import bj from"node:fs/promises";function ch(e){return dS.set(e.agent,e),e}function Zr(e){return dS.get(e)||kj}async function lh(e,t={}){return(await Zr(e.agent).readMessages(e,t)).map(Vu(e))}async function pS(e,t={}){return(await Zr(e.agent).readCommands(e,t)).map(Vu(e))}async function fS(e,t={}){return(await Zr(e.agent).readJobs(e,t)).map(Vu(e))}async function mS(e,t={}){return(await Zr(e.agent).readOpenJobs(e,t)).map(Vu(e))}var ua,sh,ah,ih,kj,dS,Vu,uh=q(()=>{pn();xu();ea();Qv();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?V0(r,n):[]}async readJobs(t,n){let r=this.transcript(t);return r?Yv(r,n):[]}async readOpenJobs(t,n){let r=this.transcript(t);return r?Xv(r,n):[]}async updatedAt(t){let n=this.transcript(t);if(!n)return null;try{return Math.round((await bj.stat(n)).mtimeMs)}catch{return null}}},ah=class extends ua{async readMessages(t,n){return iS(t.cwd,t.sessionId,n)}async readCommands(t,n){return lS(t.cwd,t.sessionId,n)}async updatedAt(t){return uS(t.cwd,t.sessionId)}},ih=class extends ua{sessionId(t){return t?.pinned?t.sessionId:null}async readMessages(t,n){return X0(t.cwd,this.sessionId(t),n)}async readCommands(t,n){return Z0(t.cwd,this.sessionId(t),n)}async updatedAt(t){return Q0(t.cwd,this.sessionId(t))}},kj=new ua("unknown"),dS=new Map;ch(new sh("claude",mm));ch(new ah("cursor",Gu));ch(new ih("codex",ji));Vu=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 xj(e){if(!e)return"";let t=e.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!t||/hidden|\d+\s+lines?/i.test(t)?"":t}function vj(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 Sj(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=Aj(n,t);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function Aj(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 hS(e){return e.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function Tj(e,t=""){let n=String(e);if(!gS.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 Ij(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||!yS.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&&!(Ej.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 _j(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 Rj(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($j.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 Oj(e,t=""){let n=String(e);if(!bS.test(n)||!kS.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 Mj(e,t=""){let n=String(e);if(!xS.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 Nj(e,t){let n=e[t].trim();for(let r=t+1;r<e.length;r++){let o=e[r].trim();if(!o||!Cj.test(o)||wh.test(e[r]))break;n+=o}return n}function Pj(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=Nj(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 Bj(e,t=""){let n=String(e);if(!ph.test(n)||!Dj.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 jj(e,t=""){let n=String(e);if(!vS.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 Lj(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(SS);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 Fj(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 Uj(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(zu.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 Hj(e,t=""){let n=String(e);if(!wS.test(n)||!fh.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
171
|
+
`),o=Fj(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||vj(r[j])||zu.test(K))){I.desc=xj(K);break}}let d=Uj(l,r),p=l.filter(I=>I!==d&&!zu.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&&!zu.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?hS(r[s]):"")||"The agent is asking a question",k=s>=0?Sj(r[s],b):null,S=k?.currentTabLabel||(s>=0?hS(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"?wS.test(n)&&fh.test(n)||gS.test(n)||dh.test(n)||yS.test(n.trimEnd().split(`
|
|
172
|
+
`).pop()||"")?!0:Jo.test(n)?!1:bS.test(n)&&kS.test(n)||xS.test(n)||wh.test(n)||ph.test(n)||vS.test(n)||SS.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"?_j(t,e.name)||Hj(t,e.name)||Tj(t,e.name)||Rj(t,e.name)||Bj(t,e.name)||Pj(t,e.name)||Mj(t,e.name)||Oj(t,e.name)||jj(t,e.name)||Lj(t,e.name)||Ij(t,e.name):e.agent==="cursor"||e.agent==="codex"?mx(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 AS(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 TS(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 wS,fh,zo,zu,Qr,mh,gS,yS,Ej,dh,$j,Jo,bS,kS,xS,wh,Cj,ph,Dj,vS,SS,bh=q(()=>{ht();Su();wS=/to navigate\b.*\bEsc to cancel|Enter to select\b/,fh=/Chat about this/i,zo=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,zu=/^(type something|chat about this|submit)\b/i,Qr=/^[─—-]{6,}\s*$/,mh=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;gS=/^\s*Switch model\?\s*$/m;yS=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,Ej=/^\s*[─—▔-]{6,}\s*$/;dh=/Resume from summary/i,$j=/^\s*This session is\b/;Jo=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;bS=/Choose the text style that looks best with your terminal/,kS=/Let's get started\./;xS=/Select login method:/;wh=/Paste code here if prompted/,Cj=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;ph=/OAuth error:/,Dj=/Press Enter to retry\./;vS=/Quick safety check: Is this a project you created or one you trust/;SS=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function Ju(e,t,n){if(!e||!t)return null;for(let r of Wj)if(r.provider===e.agent)try{if(r.match(t,n))return r}catch{}return null}function Gj(e){let t=kh.get(e);return t||(t={sentAt:[],last:0,emitted:new Set},kh.set(e,t)),t}function _S(e,t,n=Date.now()){let r=Gj(e);if(r.sentAt=r.sentAt.filter(s=>n-s<IS),r.sentAt.length>=ES)return{action:"give-up"};if(n-r.last<qj)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 $S(e,t=Date.now()){let n=kh.get(e);return n?n.sentAt.filter(r=>t-r<IS).length>=ES:!1}var Wj,qj,ES,IS,kh,xh=q(()=>{Wj=[{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"]}];qj=5e3,ES=5,IS=3600*1e3,kh=new Map});function da(e){return typeof e=="string"?e.trim():""}var RS=q(()=>{});import{execFile as Kj}from"node:child_process";import{promisify as Vj}from"node:util";async function Ye(e,t,{input:n}={}){let r=Sh("git",["-C",e,...t],{maxBuffer:OS});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 MS(e){let{stdout:t}=await Ye(e,["rev-parse","--show-toplevel"]);return t.trim()}async function zj(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:OS,env:o,...i});try{let a=await zj(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 nc(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 Jj(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 Jj(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 Yj(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 Yj(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,OS,Yu=q(()=>{Sh=Vj(Kj),OS=64*1024*1024});function Xu(e,{at:t=Date.now(),reason:n="node-transfer"}={}){let r=!1;for(let o of e?.boxes||[]){if(!o||typeof o!="object")continue;!Xj.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,Xj,Zt=q(()=>{Le=e=>!!(e&&(e.archivedAt||e.transferredTo)),Xj=new Set(["merged","conflict","canceled","failed","handed-off"])});import Oh from"node:fs/promises";import NS from"node:path";import Zj from"node:os";function jS(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function LS(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function FS(e){let t=new Date(jS(e));return t.setDate(t.getDate()+7),t.getTime()}function US(e){let t=new Date(LS(e));return t.setMonth(t.getMonth()+1),t.getTime()}function Qu(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 HS(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;Qu(t);let n=es.get(e);for(let r of Object.keys(ma)){let o=HS(e,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:e,window:r,limitUsd:o,spentUsd:Zu(s),resetsAt:ma[r].nextStart(t)}}return null}function WS(e,t){let n=`hit:${e}:${t.provider}:${t.window}:${cr[t.window]}`;return ha.has(n)?!1:(ha.add(n),!0)}function CS(e,t,n,r=Date.now()){if(!e||!t)return;Qu(r);let o=Qj(e);for(let[s,a]of Object.entries(ma))n>=cr[s]&&n<a.nextStart(r)&&(o[s]+=t)}async function eL(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=HS(e,o);if(a==null)continue;let i=r[o]||0;if(i<a*BS||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:Zu(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function qS(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 ed(e,{source:t="local",updatedAt:n=Date.now()}={}){mn={caps:qS(e),updatedAt:n,source:t};try{await Oh.mkdir(PS,{recursive:!0}),await Oh.writeFile(DS(),JSON.stringify(mn,null,2))}catch{}return rc()}function rc(e=Date.now()){Qu(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:Zu(o?.weekly||0),monthlyUsd:Zu(o?.monthly||0)}}return{caps:mn?.caps||{},configured:!!mn,seeded:Ch,updatedAt:mn?.updatedAt??null,spent:n,windows:{weekly:{start:cr.weekly,resetsAt:FS(e)},monthly:{start:cr.monthly,resetsAt:US(e)}},warnPct:BS}}async function GS({listTracks:e,getTrack:t}={}){Mh=t||null,Qu();try{let o=JSON.parse(await Oh.readFile(DS(),"utf8"));mn={caps:qS(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);CS(o.provider||"unknown",s,o.ts??Date.now()),eL(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||CS(a.provider||"unknown",Xt(a).usd,i,n)}}return Ch=!0,rc()}var PS,DS,BS,ma,mn,Ch,cr,es,ha,Mh,Qj,Zu,td=q(()=>{ye();Yr();PS=process.env.BOSTRAT_STATE_DIR||NS.join(process.env.HOME||Zj.tmpdir(),".bostrat"),DS=()=>NS.join(PS,"usage-caps.json"),BS=.8;ma={weekly:{startOf:jS,nextStart:FS,capKey:"weeklyUsd"},monthly:{startOf:LS,nextStart:US,capKey:"monthlyUsd"}},mn=null,Ch=!1,cr={weekly:0,monthly:0},es=new Map,ha=new Set,Mh=null,Qj=e=>{let t=es.get(e);return t||(t={weekly:0,monthly:0},es.set(e,t)),t};Zu=e=>Math.round(e*1e4)/1e4});import tL from"node:fs/promises";import nL from"node:os";import KS from"node:path";function Nh(e){return e==="codex"?aL:e==="cursor"?ZS:YS}function VS(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 zS(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 iL(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 JS(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 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 uT(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 tT(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(Fn(i));o=nT(l).map(QA).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=QL(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await Qt(n.path,["run","view",c,"--log-failed"]).then(u=>eF(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function QL(e){return String(e||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function eF(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
|
-
`+r.slice(-n)),r}function
|
|
189
|
-
`).find(n=>n.trim())||"gh failed"}var
|
|
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
|
|
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,FL,yd,Kh,eT,HL,ya,ZA,qL,GL,cc,lc,VL,zL,JL,qh,as=q(()=>{Pr();yt();jn();ic();gd();ye();ba=jL(BL),FL={timeout:3e4,maxBuffer:8*1024*1024};yd=null;Kh="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt,state,mergedAt,closedAt",eT=Kh.replace("statusCheckRollup,","");HL=9e4,ya=new Map;ZA=(e,t)=>e.number-t.number;qL=3e4,GL=1500,cc=new Map;lc=new Set(["squash","merge","rebase"]);VL=2500,zL=9e4,JL=e=>new Promise(t=>setTimeout(t,e));qh=new Map});import{execFile as tF}from"node:child_process";import{promisify as nF}from"node:util";async function oF(e,t){try{let{stdout:n}=await rF("git",["-C",e,"rev-list","--count",t]);return Number(n.trim())||0}catch{return null}}async function sF(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 oF(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:kd.filter(w=>p.includes(w)),defaults:kd.filter(w=>f.includes(w))}}async function xd(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 sF(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=kd.filter(p=>c.some(f=>f.available.includes(p))),u=kd.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 rF,kd,Xh=q(()=>{Yu();as();rF=nF(tF),kd=["commit","rebase","push","pr"]});function vd(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 pc)a-c.at>=dT()&&pc.delete(i);pc.set(e,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!e)return!1;let s=pc.get(e);return s?Date.now()-s.at>=dT()?(pc.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(!vd(e,t,r))return kx(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!Ju({name:e,agent:t},s,o)||$S(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 dT,pc,fc=q(()=>{ht();ra();bh();xh();dT=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,pc=new Map});import{execFile as aF}from"node:child_process";import{promisify as iF}from"node:util";async function co(e){if(!e)return null;let t=pT.get(e);if(t&&Date.now()-t.at<lF)return t.branch;let n=null;try{let{stdout:r}=await cF("git",["-C",e,"symbolic-ref","--short","-q","HEAD"],{timeout:5e3});n=r.trim()||null}catch{n=null}return pT.set(e,{at:Date.now(),branch:n}),n}function fT(e){return String(e||"").match(uF)?.[1]??null}function mT(e,t){let n=fT(e);return!!n&&!!t&&n===t}function hT(e,t){let n=fT(e);return!!n&&n!==t}function dF(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 dF(e))n(await co(r));return t}async function Sd(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=>mT(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 pF(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 Ad(e,{branch:t=null,repo:n=null,slug:r=null}={}){return mT(t,e?.id)?!0:pF(e,n,r)}var cF,lF,pT,uF,cs=q(()=>{Zt();cF=iF(aF),lF=5e3,pT=new Map;uF=/^track\/.+-([0-9a-f]{8})$/});import{execFile as fF}from"node:child_process";import{promisify as mF}from"node:util";function Sa({trackId:e,boxId:t,epoch:n}){return`${e}/${t}/${Number.isFinite(Number(n))?Number(n):0}`}function Ed(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?kF[r]:null;return a&&s.push(a),s.push(`${kT}: ${Sa({trackId:e,boxId:t,epoch:n})}`),o&&s.push(`${xT}: ${String(o).trim()}`),s}function wT(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===kT.toLowerCase()&&!t.epoch)t.epoch=Ed(a);else if(s===xT.toLowerCase()&&!t.model)t.model=a;else if(s===vT.toLowerCase()&&!t.cause){let i=a.toLowerCase();mc.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 xF(e,t=lo){return e<=t?"rework":"drift"}function vF(e,{window:t=lo,end:n=rw}={}){return!Number.isFinite(e)||e<=t?1:e>=n?0:1-(e-t)/(n-t)}function $d({subject:e="",trailerCause:t=null,wallReason:n=null,fixerCategory:r=null,ciFix:o=!1}={}){let s=String(e||"");return SF.test(s)?{cause:"defect",level:"A",basis:"revert"}:o?{cause:"defect",level:"A",basis:"ci-fix"}:t?{cause:t,level:"E",basis:`${vT} trailer`}:TF.test(s)&&!gT.test(s)?{cause:"style",level:"C",basis:"subject"}:gT.test(s)?{cause:"defect",level:"C",basis:"subject"}:AF.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=EF.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&&IF.has(c)?{intent:"fix",basis:`subject prefix: ${c}:`}:c&&_F.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"}:yT.test(String(n||""))||yT.test(a)?{intent:"planned",basis:"blueprint item"}:o&&$F.has(o)?{intent:"planned",basis:`prompt: ${o}`}:{intent:"unknown",basis:null}}function CF(e){let t=String(e||"");return RF.some(n=>n.test(t))?"excluded":OF.some(n=>n.test(t))?"docs":MF.some(n=>n.test(t))?"config":"code"}function NF(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 PF(e,t){let n=nw(e),r=nw(t);if(n===r)return 1;if(!n.length||!r.length)return 0;n.length>Td&&(n=n.slice(0,Td)),r.length>Td&&(r=r.slice(0,Td));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 DF(e,t=ST){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&&PF(r[s],o[s])>=t&&n.add(a)}return n}function BF(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 jF(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 bT(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 bT(e,t,{fixKey:n=null,window:r=lo,resolveOrigin:o=null,context:s={}}={}){let a=await mT(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=dT(a.message),l=n?Ed(n):c.epoch,u=l?xa(l):null,{stdout:d}=await ew(e,["diff","-U0","--no-color","-M","-C",i,t]),p=AF(d),f=0,w=0,m=0,g=0,y=0,x=0,b=0,I=new Map;for(let _ of p){if(_.binary)continue;let U=_.oldPath||_.newPath||"",J=_.deleted.reduce((K,[,Y])=>K+Y,0),R=SF(U);if(R==="excluded"){m+=J;continue}if(R==="docs"){g+=J,y+=_.added;continue}if(R==="config"){x+=J;continue}f+=_.added;let A=new Set;for(let K of _.hunks||[])for(let Y of EF(K))A.add(Y);b+=A.size;let M=[];for(let[K,Y]of _.deleted)for(let fe=K;fe<K+Y;fe++)A.has(fe)||M.push(fe);if(w+=M.length,!_.oldPath||!M.length)continue;let N;try{let K=["blame","-w","-M","-C","--line-porcelain"];for(let[Y,fe]of _F(M))K.push("-L",`${Y},${Y+fe-1}`);K.push(i,"--",_.oldPath),N=IF((await ew(e,K)).stdout)}catch{continue}for(let{sha:K,time:Y}of N.values()){let fe=I.get(K)??{lines:0,time:Y};fe.lines+=1,I.set(K,fe)}}let $=[],P=0,F=new Map;for(let[_,{lines:U,time:J}]of I){let R=F.get(_);if(!R){try{R=await mT(e,_)}catch{R=null}F.set(_,R)}let A=R?dT(R.message):{epoch:null},M=A.epoch,N=A.provider,K=A.model;if(!M&&o){let X=await o(_,R);X?.trackId&&X?.boxId!=null&&(M={trackId:X.trackId,boxId:X.boxId,epoch:X.epoch??0},N=N??X.provider??null,K=K??X.model??null)}if(!M){P+=U;continue}let Y=Number.isFinite(J)?J:R?.ts??a.ts,fe=Math.max(0,(a.ts-Y)/iF),Ie=xa(M),Ce=Ie===u?"self":dF(fe,r);$.push({sha:_,key:Ie,trackId:M.trackId,boxId:M.boxId,epoch:M.epoch,provider:N??null,model:K??null,lines:U,ageDays:Math.round(fe*100)/100,class:Ce,decay:Ce==="rework"?1:Ce==="drift"?Math.round(pF(fe,{window:r})*1e3)/1e3:0})}$.sort((_,U)=>U.lines-_.lines);let B=$d({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0}),T=ow({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:b,docsDeleted:g,docsAdded:y,configDeleted:x,codeDelta:f+w+b>0,origins:$,unattributedLines:P,ghost:w===0&&f>0,heuristic:B,intent:T}}function kT(e,{minLines:t=rw}={}){if(!e)return!1;let n=e.intent?.intent??"unknown";return va.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 Qh(e){let t=e.genUsd;return{...e,genUsd:hn(t),reworkChargedUsd:hn(e.reworkChargedUsd),reworkPendingUsd:hn(e.reworkPendingUsd),trueUsd:hn(t+e.reworkChargedUsd),multiplier:t>0?hn((t+e.reworkChargedUsd)/t):null}}function xT(e,{window:t=lo,boxMeta:n={}}={}){let r=e||[],o=r.filter(T=>T.type==="cost.recorded"),s=r.filter(T=>T.type==="code.committed"),a=r.filter(T=>T.type==="code.rework"),i=T=>Array.isArray(T?.shas)&&T.shas.length?T.shas:T?.sha?[T.sha]:[],c=new Map,l=new Set;for(let T of r)if(T.type==="rework.cause.offered")for(let _ of i(T.payload))l.add(_);else if(T.type==="rework.cause.answered"&&T.payload?.cause)for(let _ of i(T.payload))c.set(_,T.payload.cause);else if(T.type==="rework.cause.retracted")for(let _ of i(T.payload))c.delete(_);let u={},d=(T,_,U)=>xa({trackId:T,boxId:_,epoch:U??0}),p=(T,_={})=>{if(!u[T]){let J=Ed(T)||{};u[T]={key:T,...J,provider:null,model:null,...Zh()}}let U=u[T];return _.provider&&!U.provider&&(U.provider=_.provider),_.model&&!U.model&&(U.model=_.model),U};for(let T of o){if(!T.boxId)continue;let _=d(T.trackId,T.boxId,T.epoch),{usd:U,model:J}=Xt(T),R=p(_,{provider:T.provider,model:J!=="unknown"?J:null});R.genUsd+=U}let f=new Map;for(let T of s){let _=`${T.trackId}/${T.boxId}`;(f.get(_)??f.set(_,[]).get(_)).push({...T,ts:T.payload?.ts??T.ts})}for(let T of f.values())T.sort((_,U)=>_.ts-U.ts);let w=(T,_,U,J)=>{let R=f.get(`${T}/${_}`)||[],A=-1/0;for(let K of R)K.ts<J&&(A=K.ts);let M=0,N=0;for(let K of o)K.trackId!==T||K.boxId!==_||(K.epoch??0)===(U??0)&&(K.ts<=A||K.ts>J||(M+=Xt(K).usd,N++));return{usd:M,turns:N,from:Number.isFinite(A)?A: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},x=0,b=[],I=[],$={},P={},F=new Set;for(let T of a){let _=T.payload||{};if(!_.sha||F.has(_.sha))continue;if(F.add(_.sha),_.codeDelta===!1){x++;continue}let U=Ed(_.fixKey)||{trackId:T.trackId,boxId:T.boxId,epoch:T.epoch??0},{usd:J,turns:R}=w(U.trackId,U.boxId,U.epoch,_.ts??T.ts),A=lF.includes(_.intent?.intent)?_.intent.intent:"unknown";y[A]+=1;let M=null;c.has(_.sha)?M={cause:c.get(_.sha),level:"B",basis:"operator"}:(_.heuristic?.level&&_.heuristic.level!=="C"||_.heuristic)&&(M=_.heuristic);let N=M?.level??"none";g[N]=(g[N]||0)+1;let K=cF.has(N),Y=Number(_.deleted)||0,fe=Array.isArray(_.origins)?_.origins:[],Ie=fe.filter(X=>X.class==="rework").reduce((X,et)=>X+et.lines,0),Ce={sha:_.sha,subject:_.subject??"",ts:_.ts??T.ts,trackId:T.trackId,fixKey:_.fixKey??null,fixProvider:_.fixProvider??T.provider??null,fixUsd:hn(J),fixTurns:R,linesReworked:Ie,unattributedLines:Number(_.unattributedLines)||0,ghost:_.ghost===!0,intent:{intent:A,basis:_.intent?.basis??null},verdict:M?{cause:M.cause,level:N,basis:M.basis??null}:null,charged:hn(0),origins:[]};if(A==="planned"||M?.cause===_d){m.planned+=J;for(let X of fe)X.class==="rework"&&Ce.origins.push({key:X.key,provider:X.provider??null,model:X.model??null,lines:X.lines,ageDays:X.ageDays,class:X.class,usd:0})}else if(_.ghost||Y===0)m.ghost+=J;else{let X=0;for(let Ee of fe){let se=Ee.lines/Y,ee=J*se;if(X+=ee,Ee.class==="self"){m.self+=ee;continue}let Oe=Ee.class==="rework"?1:Number(Ee.decay)||0,z=ee*Oe;if(m.decayed+=ee-z,z<=0)continue;let we=p(Ee.key,{provider:Ee.provider,model:Ee.model});we.fixes+=1,we.linesReworked+=Ee.class==="rework"?Ee.lines:0;let Fe=M?.cause&&mc.includes(M.cause)?M.cause:"unclassified";we.causes[Fe]+=1,K?(we.reworkChargedUsd+=z,m.charged+=z,Ce.charged=hn(Ce.charged+z)):(we.reworkPendingUsd+=z,m.pending+=z),Ce.origins.push({key:Ee.key,provider:we.provider,model:we.model,lines:Ee.lines,ageDays:Ee.ageDays,class:Ee.class,usd:hn(z)})}let et=Math.max(0,J-X);m.unattributed+=et}b.push(Ce),!c.has(_.sha)&&l.has(_.sha)&&I.push(_.sha)}for(let T of Object.values(u)){let _=T.provider||"unknown",U=T.model||"unknown",J=R=>{R.genUsd+=T.genUsd,R.reworkChargedUsd+=T.reworkChargedUsd,R.reworkPendingUsd+=T.reworkPendingUsd,R.linesReworked+=T.linesReworked,R.fixes+=T.fixes;for(let A of Object.keys(R.causes))R.causes[A]+=T.causes[A]};J($[_]||=Zh()),J(P[U]||=Zh())}b.sort((T,_)=>_.ts-T.ts);let B=Object.values(u).map(T=>{let _=n[`${T.trackId}/${T.boxId}`]||{};return Qh({...T,trackName:_.trackName??null,label:_.label??null})}).sort((T,_)=>_.reworkChargedUsd+_.reworkPendingUsd-(T.reworkChargedUsd+T.reworkPendingUsd)||_.genUsd-T.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${yT} 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:x,byEpoch:Object.fromEntries(B.map(T=>[T.key,T])),epochs:B,byProvider:Object.fromEntries(Object.entries($).map(([T,_])=>[T,Qh(_)])),byModel:Object.fromEntries(Object.entries(P).map(([T,_])=>[T,Qh(_)])),buckets:Object.fromEntries(Object.entries(m).map(([T,_])=>[T,hn(_)])),fixUsdTotal:hn(Object.values(m).reduce((T,_)=>T+_,0)),fixes:b,verdicts:g,pendingTaps:I,commits:s.length}}var sF,aF,ew,hT,wT,gT,iF,lo,nw,rw,mc,_d,cF,lF,va,yT,uF,fF,pT,mF,hF,wF,gF,yF,fT,bF,kF,xF,vF,tw,Td,hn,Zh,Rd=W(()=>{Yr();sF=oF(rF),aF=64*1024*1024,ew=(e,t)=>sF("git",["-C",e,...t],{maxBuffer:aF,timeout:6e4}),hT="Bostrat-Epoch",wT="Bostrat-Model",gT="Bostrat-Fix-Cause",iF=24*36e5,lo=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,nw=90,rw=3,mc=Object.freeze(["defect","design","requirements","style"]),_d="not-a-fix",cF=new Set(["A","B","E"]),lF=Object.freeze(["fix","likely-fix","planned","unknown"]),va=new Set(["fix","likely-fix"]),yT=.8;uF={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};fF=/^revert\b|^revert "/i,pT=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,mF=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,hF=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;wF=/^([a-z]+)(\([^)]*\))?!?:\s/i,gF=new Set(["fix","hotfix","bugfix","revert"]),yF=new Set(["feat","feature","docs","doc","test","tests","build","ci","chore"]),fT=/\bBP-[A-Z]*\d+\b/i,bF=new Set(["feature-build","docs-writing","research","design","ops-infra","orchestration"]);kF=[/(^|\/)\.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)\//],xF=[/\.(md|mdx|markdown|rst|txt|adoc|asciidoc)$/i,/(^|\/)(docs?|notes|business|design)\//i,/(^|\/)(README|CHANGELOG|CHANGES|LICENSE|CONTRIBUTING|AUTHORS|NOTICE)(\.[a-z]+)?$/i],vF=[/\.(json|jsonc|json5|ya?ml|toml|ini|cfg|conf|properties|env|example)$/i,/(^|\/)\.[^/]+$/,/(^|\/)\.github\//,/(^|\/)(Dockerfile|Makefile|Procfile|fly\.toml)$/];tw=e=>String(e??"").replace(/\s+/g," ").trim(),Td=400;hn=e=>Math.round(e*1e4)/1e4,Zh=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import{execFile as $F}from"node:child_process";import{promisify as RF}from"node:util";function vT(e,t=Date.now()){return Lf(e,t)??0}function MF(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 ST(e,t,{now:n=Date.now()}={}){let r=vT(t,n),o=await q(e);return Id({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:MF(o,t.name,r)})}function CF(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 NF(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=CF(n,t.name),c=i?ar(i.text)?.category??null:null;return{wallReason:a,fixerCategory:c,promptKind:i?.kind??null}}async function PF(e,t){try{let{stdout:n}=await sw(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 DF(e){try{let{stdout:t}=await sw(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function AT(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 q(e);if(a.some(w=>w.type==="code.committed"&&w.payload?.sha===n))return{recorded:!1,reason:"duplicate"};let i=s;try{let{stdout:w}=await sw(r,["show","-s","--format=%ct",n]),m=Number(w.trim())*1e3;Number.isFinite(m)&&m>0&&(i=m)}catch{return{recorded:!1,reason:"unknown-sha"}}let c=vT(t,i),l=await PF(r,n),u=await DF(r),d=await C(e,{type:"code.committed",actor:"agent",source:"hook",boxId:t.name,provider:t.agent??null,payload:{sha:n,ref:u,...l,ts:i}}),p=null,f=!1;try{let w=await bT(r,n,{fixKey:xa({trackId:e.id,boxId:t.name,epoch:c}),window:lo,context:{...NF(e,t,a,c),ref:u,provider:t.agent??null}});if(w&&(p=await C(e,{type:"code.rework",actor:"system",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:d.id,payload:w}),!(e?.workspace?.kind==="worktree"&&!!e?.workspace?.branch)&&kT(w))){let g=hc(w),y=w.origins.filter(x=>x.class==="rework").slice(0,3);await C(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:p.id,payload:{sha:n,subject:w.subject,linesReworked:g,origins:y.map(x=>({key:x.key,provider:x.provider,model:x.model,lines:x.lines,ageDays:x.ageDays})),heuristic:w.heuristic,intent:w.intent,ts:i}}),f=!0}}catch(w){o.warn?.(`[rework] ${e.id}: analysis of ${String(n).slice(0,7)} failed: ${w?.message||w}`)}return{recorded:!0,event:d,rework:p,offered:f}}function TT(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 ET(e,t,n){if(!mc.includes(n)&&n!==_d)throw new Error(`bad cause "${n}" (${mc.join("|")}|${_d})`);let r=await q(e),o=r.find(s=>s.type==="rework.cause.offered"&&s.payload?.sha===t);if(!o)throw new Error("unknown rework offer");if(TT(r,t))throw new Error("already answered");return C(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 _T(e,t){let n=await q(e);if(!TT(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 C(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 Od(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 q(e);if(c.some(x=>x.type==="rework.cause.offered"&&x.payload?.pr?.number===n&&(t==null||x.payload.pr.repo===t)))return{offered:!1,reason:"duplicate"};let l=new Set;for(let x of c)if(x.type==="rework.cause.offered")for(let b of Array.isArray(x.payload?.shas)?x.payload.shas:[x.payload?.sha])b&&l.add(b);let u=new Map;for(let x of c)x.type==="code.committed"&&x.payload?.sha&&u.set(x.payload.sha,x.payload.ref??null);let d=ow({heuristic:$d({subject:r}),subject:r,ref:o}),p=[];for(let x of c){if(x.type!=="code.rework")continue;let b=x.payload||{};if(!b.sha||l.has(b.sha)||b.codeDelta===!1||o&&u.has(b.sha)&&u.get(b.sha)!==o)continue;let I=b.intent?.intent??"unknown";(va.has(I)||I==="unknown"&&va.has(d.intent))&&hc(b)!==0&&p.push(b)}let f=p.reduce((x,b)=>x+hc(b),0);if(f<rw)return{offered:!1,reason:"below-threshold"};let w=new Map;for(let x of p)for(let b of x.origins??[]){if(b.class!=="rework")continue;let I=w.get(b.key)??{key:b.key,provider:b.provider??null,model:b.model??null,lines:0,ageDays:b.ageDays};I.lines+=b.lines,I.ageDays=Math.min(I.ageDays,b.ageDays),w.set(b.key,I)}let m=p.map(x=>x.sha),g=p.map(x=>x.heuristic).find(x=>x&&x.level!=="C")??$d({subject:r})??p.map(x=>x.heuristic).find(Boolean)??null,y=va.has(d.intent)?d:p.find(x=>va.has(x.intent?.intent))?.intent??d;return await C(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((x,b)=>b.lines-x.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 OF,sw,Md=W(()=>{me();_u();Rd();OF=RF($F),sw=(e,t)=>OF("git",["-C",e,...t],{maxBuffer:16*1024*1024,timeout:3e4})});import{execFile as BF}from"node:child_process";import{promisify as jF}from"node:util";async function aw(e,t,{midTurn:n=is,pollMs:r=LF,capMs:o=FF}={}){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 CT(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){IT.lastIndex=n;let o=IT.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(`
|
|
195
|
-
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+VF(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 iw(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function PT(e){let t=CT(e),n=t.match(qF);if(!n)return null;let r=iw(t,n.index);return GF.test(r)?null:{repo:r.match(NT)?.[1]??null,head:r.match(zF)?.[1]??null}}function DT(e){let t=CT(e),n=t.match(KF);if(!n)return null;let r=iw(t,n.index),o=iw(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("-")){YF.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(NT)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function ZF(e,t,{exclude:n=()=>!1}={}){let r=async i=>{pr("observer");let{stdout:c}=await $T("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",XF],{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-UF&&!n(c)),s=await En({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 QF(e,t){if(t)return[t];let n=[];for(let r of BT(e)){let o=await Re(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await nr().catch(()=>[])){let o=await Re(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function BT(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 eU(e,t,n){let r=await ka(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)||JF(c.headRefName,e.id),i=[];for(let c of await QF(e,t?.repo??null)){let l;try{l=await ZF(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await ut(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 jT(e){if(!e)return 0;let t=0;for(let n of mr)n.startsWith(`${e}|`)&&t++;return t}function LT(){for(let e of[...cw])e()}function FT(e,t,{delays:n=RT,turnEnd:r}={}){let o=PT(t.command);if(!o)return null;let s=!!DT(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),cw.delete(g),m()},y=setTimeout(g,l);cw.add(g)});let u=await eU(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 aw(t.box,t.agent,r),await C({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 UT(e,t,{delays:n=RT,turnEnd:r,openedWait:o}={}){let s=DT(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=BT(e);if(w.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let m of w){let g=await Re(m).catch(()=>null);g&&!d.includes(g)&&d.push(g)}}if(l||=(await ka(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 ut(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 q(e).catch(()=>[])).some(b=>b.type==="pr.state.changed"&&(b.payload?.change==="auto-armed"||b.payload?.change==="merged")&&b.payload?.number===w.number)||(await aw(t.box,t.agent,r),await C({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(P=>setTimeout(P,w));let m=[];for(let P of d)try{pr("observer");let{stdout:F}=await $T("gh",["pr","view",String(l),"--repo",P,"--json",tU],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await En({slug:P})}}),B=JSON.parse(F||"null");B?.number&&m.push({slug:P,pr:B})}catch(F){ao(F)&&io(F)}let g=m.find(P=>P.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 x=await ut(c,y.number);if(x&&x.trackId!==e.id)return null;x||await Ut({repo:c,number:y.number,trackId:e.id,url:y.url,headRef:y.headRefName,title:y.title,source:"merge-observed"}),await aw(t.box,t.agent,r);let b=await q(e).catch(()=>[]);if(b.some(P=>P.type==="pr.state.changed"&&P.payload?.change==="merged"&&P.payload?.number===y.number))return null;let I=P=>P.some(F=>F.type==="pr.state.changed"&&F.payload?.change==="opened"&&F.payload?.number===y.number),$=0;if(!I(b)){if(PT(t.command)&&await ur(c,y.number)){let P=Date.now();$=P+1,await C({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:P,change:"opened",number:y.number,url:y.url,title:y.title,branch:y.headRefName,repo:c,selfMerge:!0}})}else if((await ut(c,y.number))?.announced){let P=o?.capMs??HF,F=o?.pollMs??WF,B=Date.now()+P;for(;Date.now()<B&&(await new Promise(T=>setTimeout(T,F)),!I(await q(e).catch(()=>[]))););}}return await C({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 $T,RT,LF,FF,UF,HF,WF,OT,MT,qF,KF,GF,IT,VF,NT,zF,JF,YF,XF,mr,cw,tU,Cd=W(()=>{me();Pr();gt();ic();Dn();gr();fc();cs();Md();$T=jF(BF),RT=[4e3,15e3,45e3],LF=2500,FF=5*6e4;UF=15e3,HF=9e4,WF=2e3,OT=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,MT=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,qF=new RegExp(`${OT}${MT}create\\b`),KF=new RegExp(`${OT}${MT}merge\\b`),GF=/\s--web\b/,IT=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,VF=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");NT=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,zF=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,JF=uT;YF=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);XF="number,url,title,headRefName,createdAt";mr=new Set;cw=new Set;tU="number,url,title,headRefName,state"});import nU from"node:fs/promises";import uw from"node:path";import rU from"node:os";function lw(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function Nd(e){let t=oU.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=lw(n,"from"),s=o?o.replace(/^uds:/,""):null,a=HT(s);return{socket:s,pid:a,name:lw(n,"from-name"),mode:lw(n,"from-mode"),text:r.trim()}}function HT(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function sU(){return process.env.CLAUDE_CONFIG_DIR?uw.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):uw.join(rU.homedir(),".claude","sessions")}async function aU(e){let t=HT(e);if(!t)return null;try{let n=await nU.readFile(uw.join(sU(),`${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 WT(e,t){let n=await aU(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 oU,dw=W(()=>{oU=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function qT(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function KT(e,t,n=pw){let r=new AbortController,o=setTimeout(()=>r.abort(),n);try{return await cU(`${Ne(t)}${e}`,{headers:lU(t),signal:r.signal})}finally{clearTimeout(o)}}async function wc({env:e=process.env,force:t=!1,timeoutMs:n=pw}={}){if(!qT(e))return{followups:[],source:"unconfigured"};if(!t&&ls&&Date.now()-ls.at<iU)return{followups:ls.followups,source:"cache"};try{let r=await KT("/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 GT(e,{env:t=process.env,timeoutMs:n=pw}={}){if(!e)return{followup:null,source:"unconfigured"};if(!qT(t))return{followup:null,source:"unconfigured"};try{let r=await KT(`/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 iU,pw,ls,cU,lU,VT=W(()=>{gt();iU=6e4,pw=5e3,ls=null,cU=(...e)=>fetch(...e);lU=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});async function uo(e,t){if(fw.has(e)){let n=new Error("offer already answered (answer in flight)");throw n.code="answer-in-flight",n}fw.add(e);try{return await t()}finally{fw.delete(e)}}var fw,gc=W(()=>{fw=new Set});import{randomUUID as Aa,createHash as uU}from"node:crypto";function XT(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function hU(e){for(let[t,n]of mU)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 ZT(e,{standing:t=!1}={}){let n=e&&typeof e=="object"?e:{},r=Sa(n.label).replace(/\s+/g," ");if(!r)throw new Error('label required (\u226460 chars, e.g. "Deploy to Pi")');if(r.length>zT)throw new Error(`label must be \u2264${zT} characters`);let o=Sa(n.trigger)||(n.ship?"now":""),s=t?pU:YT.filter(u=>u!=="track.opened");if(!s.includes(o))throw new Error(`trigger must be one of ${s.join(" | ")}${t?" (a standing follow-up can't be `now` \u2014 it would fire every turn)":""}, got "${n.trigger??""}"`);let a=n.ship&&o==="now"?gU(n.ship):null,i=Sa(n.prompt);if(!a&&!i)throw new Error("prompt required \u2014 the full instruction a fresh session could execute");if(Buffer.byteLength(i,"utf8")>JT)throw new Error(`prompt must be \u2264${JT} bytes`);if(t){let u=hU(i);if(u)throw new Error(u)}let c=Sa(n.why).slice(0,dU)||null,l=t?wU(n.repo):null;return{label:r,prompt:i,trigger:o,why:c,...l?{repo:l}:{},...a?{ship:a}:{}}}function wU(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 gU(e){let t=e&&typeof e=="object"?e:{},n=Array.isArray(t.actions)?t.actions.map(i=>String(i).trim().toLowerCase()):mw.slice();for(let i of n)if(!mw.includes(i))throw new Error(`ship.actions may only contain ${mw.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=Sa(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=>Sa(i)).filter(Boolean),a.length||(a=null)}return{actions:[...new Set(n)],rebase:r,toPr:o,base:s,repos:a}}function Pd(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)}async function QT(e,t,{pull:n=wc,ship:r={}}={}){if(gw(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=ZT(t);if(o.ship)return{kind:"ship",...await xU(e,o,r)};let s={id:e.id,cwd:e.cwd},a=(e.boxes||[]).find(g=>g.name===e.leadBox)||null,i={boxId:e.leadBox??null,provider:a?.agent??null};if(o.trigger==="now"){let g=Aa(),y=Aa();return await C(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:{ts:Date.now(),armId:g,trigger:"now",label:o.label,prompt:o.prompt,why:o.why}}),await C(s,{type:"followup.offered",actor:"system",source:"orchestrator",...i,payload:{ts:Date.now(),offerId:y,trigger:"now",repo:e.workspace?.repo||null,context:{},rows:[{armId:g,label:o.label,prompt:o.prompt,promptHash:hw(o.prompt),origin:"agent",why:o.why}]}}),{kind:"now",armId:g,offerId:y,...o}}let c=e.workspace?.repo||null,{followups:l}=await n().catch(()=>({followups:[]})),u=XT(o.label),d=l.find(g=>g.status==="active"&&g.mode!=="off"&&g.trigger===o.trigger&&(g.repo==null||g.repo===c)&&g.dedupeKey===u);if(d)return{kind:"covered",covered:{id:d.id,label:d.label},...o};let p=await q(s),f=Pd(p,{trigger:o.trigger}),w=f.length?f[f.length-1].armId:null,m=Aa();return await C(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:{ts:Date.now(),armId:m,trigger:o.trigger,label:o.label,prompt:o.prompt,why:o.why,...w?{replaces:w}:{}}}),{kind:"armed",armId:m,replaces:w,...o}}async function eE(e,t){if(!t)throw new Error("armId required");let n={id:e.id,cwd:e.cwd},r=await q(n);if(!Pd(r).some(o=>o.armId===t))throw new Error("unknown or already-settled arm");return await C(n,{type:"followup.armed.cancelled",actor:"user",source:"orchestrator",payload:{ts:Date.now(),armId:t}}),{armId:t}}async function tE(e,t,{pull:n=wc}={}){if(gw(e))throw new Error("propose from a lead Track, not a loop's run Track");let r=ZT(t,{standing:!0}),o=r.repo??e.workspace?.repo??null,s={id:e.id,cwd:e.cwd};if((await q(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=XT(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}" (${ww[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=Aa(),m=(e.boxes||[]).find(g=>g.name===e.leadBox)||null;return await C(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 nE(e,t,n,{followupId:r=null}={}){if(!t)throw new Error("proposalId required");let o={id:e.id,cwd:e.cwd},s=await q(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 C(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 C(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 C(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||gw(e))return null;let o={id:e.id,cwd:e.cwd},s=await q(o).catch(()=>[]),a=n.repo??e.workspace?.repo??null;if(t==="pr.merged"){let p=m=>{let g=(m?.prs||[]).filter(y=>Number.isFinite(Number(y?.number))).map(y=>`${y?.repo??m?.repo??""}#${Number(y.number)}`);return Number.isFinite(Number(m?.prNumber))&&g.push(`${m?.repo??""}#${Number(m.prNumber)}`),g},f=new Set(p(n));if(s.some(m=>m.type==="followup.offered"&&m.payload?.trigger==="pr.merged"&&p(m.payload?.context).some(g=>f.has(g))))return null}let i=Pd(s,{trigger:t}).map(p=>({armId:p.armId,label:p.label,prompt:p.prompt,promptHash:hw(p.prompt),origin:"agent",why:p.why??null})),c=[];if(t!=="now"){let{followups:p}=await r().catch(()=>({followups:[]}));c=p.filter(f=>f.status==="active"&&f.mode!=="off"&&f.trigger===t&&(f.repo==null||f.repo===a)).sort((f,w)=>{let m=g=>g.stats?.offered?(g.stats.accepted||0)/g.stats.offered:0;return m(w)-m(f)||(f.origin==="operator"?-1:0)-(w.origin==="operator"?-1:0)||(w.createdAt||0)-(f.createdAt||0)}).map(f=>({followupId:f.id,label:f.label,promptPreview:yU(f.prompt),promptHash:hw(f.prompt),origin:f.origin,repo:f.repo??null}))}let l=[...i,...c].slice(0,fU);if(!l.length)return null;let u=Aa(),d={ts:Date.now(),offerId:u,trigger:t,repo:a,context:{...n,repo:a},rows:l};return await C(o,{type:"followup.offered",actor:"system",source:"orchestrator",payload:d}),d}function bU(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 rE(e,{offerId:t,followupId:n=null,armId:r=null,promoted:o=null}={},s={}){if(!t)throw new Error("offerId required");return uo(`fu:${e.id}:${t}`,()=>kU(e,{offerId:t,followupId:n,armId:r,promoted:o},s))}async function kU(e,{offerId:t,followupId:n,armId:r,promoted:o},s){let{sayToLead:a,unarchiveTrack:i,getFollowup:c=GT}=s,l={id:e.id,cwd:e.cwd},u=await q(l),d=u.find(b=>b.type==="followup.offered"&&b.payload?.offerId===t);if(!d)throw new Error("unknown offer");if(u.some(b=>b.type==="followup.answered"&&b.payload?.offerId===t))throw new Error("offer already answered");let p=d.payload.rows||[],f=b=>C(l,{type:"followup.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,...b}});if(!n&&!r)return await f({action:"dismiss",followupIds:p.map(b=>b.followupId).filter(Boolean),armIds:p.map(b=>b.armId).filter(Boolean)}),{offerId:t,action:"dismiss"};let w=null,m=null;if(n){if(!p.some($=>$.followupId===n))throw new Error("that follow-up is not on this card");let{followup:b,source:I}=await c(n);if(I==="unreachable")throw new Error("plane unreachable \u2014 the follow-up's current prompt can't be read; try again shortly");if(!b||b.status!=="active"){await f({action:"removed",followupId:n});let $=new Error("removed in Settings since it was offered");throw $.code="removed",$}w=b.prompt,m=b.label}else{let b=p.find($=>$.armId===r);if(!b)throw new Error("that arm is not on this card");if(w=u.find($=>$.type==="followup.armed"&&$.payload?.armId===r)?.payload?.prompt??b.prompt,m=b.label,!w)throw new Error("the arm's prompt is missing from the log")}let g=!1;if(e.archivedAt){if(!i)throw new Error("this Track is archived");await i(e.id,{freshSession:!0}),g=!0}if(!a)throw new Error("sayToLead unavailable");let y=`${bU(d.payload)}
|
|
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-Z]*\d+\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(w=>w.type==="code.committed"&&w.payload?.sha===n))return{recorded:!1,reason:"duplicate"};let i=s;try{let{stdout:w}=await aw(r,["show","-s","--format=%ct",n]),m=Number(w.trim())*1e3;Number.isFinite(m)&&m>0&&(i=m)}catch{return{recorded:!1,reason:"unknown-sha"}}let c=IT(t,i),l=await GF(r,n),u=await KF(r),d=await N(e,{type:"code.committed",actor:"agent",source:"hook",boxId:t.name,provider:t.agent??null,payload:{sha:n,ref:u,...l,ts:i}}),p=null,f=!1;try{let w=await AT(r,n,{fixKey:Sa({trackId:e.id,boxId:t.name,epoch:c}),window:lo,context:{...qF(e,t,a,c),ref:u,provider:t.agent??null}});if(w&&(p=await N(e,{type:"code.rework",actor:"system",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:d.id,payload:w}),!(e?.workspace?.kind==="worktree"&&!!e?.workspace?.branch)&&TT(w))){let g=hc(w),y=w.origins.filter(b=>b.class==="rework").slice(0,3);await N(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:p.id,payload:{sha:n,subject:w.subject,linesReworked:g,origins:y.map(b=>({key:b.key,provider:b.provider,model:b.model,lines:b.lines,ageDays:b.ageDays})),heuristic:w.heuristic,intent:w.intent,ts:i}}),f=!0}}catch(w){o.warn?.(`[rework] ${e.id}: analysis of ${String(n).slice(0,7)} failed: ${w?.message||w}`)}return{recorded:!0,event:d,rework:p,offered:f}}function RT(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 OT(e,t,n){if(!mc.includes(n)&&n!==Id)throw new Error(`bad cause "${n}" (${mc.join("|")}|${Id})`);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(RT(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 MT(e,t){let n=await G(e);if(!RT(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 Od(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:$d({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))&&hc(k)!==0&&p.push(k)}let f=p.reduce((b,k)=>b+hc(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")??$d({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 UF,aw,Md=q(()=>{ye();Iu();Rd();UF=FF(LF),aw=(e,t)=>UF("git",["-C",e,...t],{maxBuffer:16*1024*1024,timeout:3e4})});import{execFile as VF}from"node:child_process";import{promisify as zF}from"node:util";async function iw(e,t,{midTurn:n=is,pollMs:r=JF,capMs:o=YF}={}){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 jT(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){CT.lastIndex=n;let o=CT.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(`
|
|
195
|
+
`,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
|
+
${W.prompt}`).join(`
|
|
196
197
|
|
|
197
|
-
|
|
198
|
+
`),C=await i(e.id,`${R}
|
|
199
|
+
|
|
200
|
+
${E}`,{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=`${rE(p.payload)}
|
|
201
|
+
|
|
202
|
+
${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 MU(e,t,{changedRepos:n=null,probe:r=xd,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 hE(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 wE(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 gE(e,{pull:t=wc}={}){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?Dd(s):[]}}var hw,QT,oE,sE,xU,gw,vU,eE,tE,SU,ww,aE,ds,AU,yw,_U,RU,Bd=q(()=>{ye();ts();Xh();cs();ZT();gc();hw=60,QT=8*1024,oE=200,sE=["now","pr.merged","ship.done","pr.opened","checks.green","track.opened"],xU=sE.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"},vU=3,eE=6,tE=5,SU=["offerable","never"],ww=["commit","push","pr"];aE=e=>kU("sha256").update(String(e)).digest("hex").slice(0,16),ds=e=>typeof e=="string"?e.trim():"",AU=[[/(^|[^\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");_U=e=>String(e||"").split(`
|
|
198
203
|
`).slice(0,3).join(`
|
|
199
|
-
`).slice(0,400)});import{randomUUID as
|
|
200
|
-
`)){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 AU(e,t){let n=`${e.id}:${t.name}`;if(!kw.has(n)){let r=await q(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);kw.set(n,o)}return kw.get(n)}async function fE(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 AU(e,t),o=SU(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 C(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:Cn(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 C(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 kw,mE=W(()=>{me();Ho();dn();ir();kw=new Map});import jd from"node:fs";import vw from"node:path";import TU from"node:os";async function IU({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(_U,{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:hE})});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<hE)break}return r}function $U(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 gE(){try{return JSON.parse(jd.readFileSync(Ld(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function OU(e){try{jd.mkdirSync(vw.dirname(Ld()),{recursive:!0});let t=Ld()+".tmp";jd.writeFileSync(t,JSON.stringify(e)),jd.renameSync(t,Ld())}catch{}}async function CU({fetchImpl:e,now:t=Date.now()}={}){if(po.rows&&t-po.at<RU)return po.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=gE(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await IU({sinceMs:o,cookie:n,fetchImpl:e});po={at:t,rows:s.map($U).filter(Boolean),error:null}}catch(s){po={at:t,rows:null,error:String(s?.message||s)}}return po.rows}async function NU(e,t){let n=`${e.id}:${t.name}`;if(!xw.has(n)){let r=await q(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);xw.set(n,o)}return xw.get(n)}async function yE(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!wE())return;let r=await CU({fetchImpl:n});if(!r||!r.length)return;let o=await rS(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await NU(e,t),a=gE(),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=Cn(u,d,l.ts??void 0)),!p&&l.chargedCents!=null&&(p=l.chargedCents/100),await C(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}})}OU({sinceMs:c,seen:[...i].slice(-MU)})}catch{}}var EU,wE,_U,hE,RU,po,Ld,MU,Sw,xw,Aw=W(()=>{me();rh();ir();EU=process.env.BOSTRAT_STATE_DIR||vw.join(process.env.HOME||TU.tmpdir(),".bostrat"),wE=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,_U="https://cursor.com/api/dashboard/get-filtered-usage-events",hE=100;RU=5*6e4,po={at:0,rows:null,error:null},Ld=()=>vw.join(EU,"cursor-usage.json");MU=5e3;Sw=()=>({enabled:wE(),lastPollAt:po.at||null,error:po.error}),xw=new Map});import{randomUUID as PU,createHmac as DU}from"node:crypto";function kE(e){Ew=typeof e=="string"&&e?e:null}function BU(e){if(!Ew)return null;let t=Ue(e);return t?DU("sha256",Buffer.from(Ew,"base64url")).update(t,"utf8").digest("hex").slice(0,16):null}function SE(){let e={};for(let t of vE)e[String(t)]=0;return e.inf=0,{buckets:e,sumMs:0,count:0}}function bE(e,t){let n=Math.max(0,Number(t)||0);for(let r of vE)n<=r&&(e.buckets[String(r)]+=1);e.buckets.inf+=1,e.sumMs+=n,e.count+=1}function AE(){let e={};for(let t of xE)e[t]=0;return e}function LU(){Ln={total:new Map,holds:AE(),queueWaitMs:SE(),readyWaitMs:new Map,verify:new Map,retries:new Map}}function TE({queueDepth:e=0,queueOldestAgeMs:t=0}={}){let n=Ln;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:PU(),producer:t,kind:n||null,provider:r||null,textBytes:Buffer.byteLength(String(e||""),"utf8"),textHmac16:BU(e),attachments:Number(o)||0,paste:null,t0:a,queuedAt:null,dequeuedAt:null,holds:AE(),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 C(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 EE(e,t){xE.includes(t)&&(e?.holds&&(e.holds[t]+=1),Ln.holds[t]+=1)}function Ac(e,{ms:t,outcome:n}){e&&(e.readyWaitMs=Math.max(0,Number(t)||0),e.readyOutcome=n||null)}function FU(e){let t=e?.boxes?.find?.(n=>n.name===e.leadBox);return typeof t?.cliVersion=="string"&&t.cliVersion?t.cliVersion:null}async function wn(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(Tw(Ln.total,`${s}|${t.producer}|${n}`),a&&bE(Ln.queueWaitMs,i),t.readyWaitMs!=null){let u=`${s}|${t.readyOutcome||"unknown"}`;Ln.readyWaitMs.has(u)||Ln.readyWaitMs.set(u,SE()),bE(Ln.readyWaitMs.get(u),t.readyWaitMs)}let c=FU(e);t.verify?.verdict&&(Tw(Ln.verify,`${s}|${t.verify.verdict}|${c||""}`),t.verify.retries&&Tw(Ln.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 C(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 Ew,xE,vE,jU,Ln,Tw,_w=W(()=>{me();Ki();Ew=null;xE=["busy","prompt","handoff","noSession","cap","grace"],vE=[500,2e3,1e4,6e4,6e5],jU=new Set(["composer","voice","loop"]);Ln=null;LU();Tw=(e,t,n=1)=>e.set(t,(e.get(t)||0)+n)});import _c from"node:fs/promises";import Pt from"node:path";import UU from"node:os";import HU from"node:net";import{randomUUID as WU}from"node:crypto";import{execFile as qU}from"node:child_process";import{promisify as KU}from"node:util";function ZU(e){for(let t of Nw(e))if(!ME.test(t))return t;return null}function CE(e){let t=Nw(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function QU(e,t){let n=CE(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 t4(e){let t=!1,n=!1;for(let r of Nw(e)){if(!t){if(ME.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&Iw(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,Iw(r))return`serves the filesystem root ("${r}")`;continue}if(Iw(r))return`serves the filesystem root ("${r}")`;if(e4(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function r4(e,t){if(Pt.resolve(e)===Pt.resolve(t))return!0;let[n,r]=await Promise.all([Fd(e),Fd(t)]);return n===r}async function o4(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 NE(Pt.join(e,o.name,Ow))?`./${o.name}`:null))).filter(Boolean)}async function s4(e,t){let[n,r]=await Promise.all([Fd(e),Fd(t)]),o=Pt.relative(n,r);if(o&&(o.startsWith("..")||Pt.isAbsolute(o)))throw Tc(`serve directory escapes the Track workspace: ${t}`)}async function a4(e,t){let n=t4(t);if(n)throw Tc(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await n4(e))throw Tc(`serve directory does not exist: ${e}`);let r=ZU(t);if(!r)return;if(!r.includes("/")&&!await v0(r,{loginShell:!0,failOpen:!0}))throw Tc(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(JU.test(t)||YU.has(r)||!zU.has(r)||!CE(t).includes("run")||XU.test(t))return;let o=QU(e,t);if(await NE(Pt.join(o,Ow)))return;let s=await o4(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Tc(`no ${Ow} in ${o}${a}`)}async function kr(){return Fn||($w||=(async()=>{try{Fn=JSON.parse(await _c.readFile(OE,"utf8"))}catch{Fn={views:[],adoptWatermarks:{}}}finally{$w=null}return Fn.views||=[],Fn.adoptWatermarks||={},Fn})(),$w)}async function Hn(){await Mt(OE,Fn)}function br(e,t,n={}){return C({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 Dw(e){return Ec.get(e)||null}function DE(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 Mw(e,t){let n=Ec.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function Ud(e,{error:t=null}={}){let n=Ec.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function BE(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 Cw(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function i4(e,t){let n=Cw(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!$E.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!$E.has(s))return s}}function Ic(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=HU.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 Bw(e){if(process.platform!=="darwin"){await Un("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await Un("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 c4(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=_E;o<=IE;o++)if(!(n.has(o)||r.has(o))&&!await Ic(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${_E}-${IE}`)}async function l4(e){try{let{stdout:t}=await Un(ne,oe("list-sessions","-F","#{session_name}"));return t.split(`
|
|
201
|
-
`).some(n=>n.trim()===Ea(e))}catch{return!1}}async function $c(e){try{await
|
|
204
|
+
`).slice(0,400);RU=(e,t)=>t.filter(n=>e.get(n.armId?`arm:${n.armId}`:`fu:${n.followupId}`)==null)});import{randomUUID as CU}from"node:crypto";function bc(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function yE(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function bE(e){return`job|${e.box}|id:${e.id}`}async function kw(e){return bw.has(e.id)?bw.get(e.id):(jd.has(e.id)||jd.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(bc({role:o,sourceId:r.payload.sourceId})),n.keys.add(bc({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(bc({role:"user",sourceId:r.payload.sourceId})),n.keys.add(bc({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(yE({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(bE({id:r.payload.id,box:r.boxId}));return bw.set(e.id,n),n}).finally(()=>jd.delete(e.id))),jd.get(e.id))}async function kE(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=bc(o);if(r.has(s))continue;r.add(s);let a=o.role==="user"?Nd(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 zT(a,kc),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=tx(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}}),Qi(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=fA(o.text);if(c&&hE(await G(e).catch(()=>[])))continue;if(c){let l=wA(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 xd(e,{branch:d,rebase:l}).catch(()=>null),f=p?null:hA(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:CU(),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 xE(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=yE(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}}),GT(e,o),KT(e,o)}}catch{}}async function vE(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=bE(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,jd,SE=q(()=>{ye();ts();Xh();Cd();cs();pw();gr();ku();Wu();Bd();bw=new Map,jd=new Map});function NU(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
205
|
+
`)){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(`
|
|
206
|
+
`).some(n=>n.trim()===Ea(e))}catch{return!1}}async function $c(e){try{await Wn(te,oe("kill-session","-t",Ea(e)))}catch{}}async function Lw(e){if(!await Mi())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,oe("new-session","-d","-s",t,"-c",n)),await Wn(te,oe("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await Wn(te,oe("send-keys","-t",t,"Enter")),await Wn(te,oe("send-keys","-t",t,"-l",e.cmd)),await Wn(te,oe("send-keys","-t",t,"Enter"))}async function UE(e,t=12){try{let{stdout:n}=await Wn(te,oe("capture-pane","-t",Ea(e),"-p"));return String(n||"").trimEnd().split(`
|
|
202
207
|
`).filter(Boolean).slice(-t).join(`
|
|
203
|
-
`)}catch{return""}}async function d4(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await l4(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await RE(e.slug)||"server exited before listening"),yr.delete(e.id),await br(e,e.state,e.error?{error:e.error}:{}),!0}if(await Ic(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>u4?(e.state="error",e.error=await RE(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 Hd(){if(!Rw){Rw=!0;try{let e=await kr(),t=!1;for(let n of e.views)await d4(n)&&(t=!0);t&&await Hn()}finally{Rw=!1}}}async function Lw({trackId:e}={}){await Hd();let t=await kr();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function LE(e){let n=(await kr()).views.find(r=>r.id===e);return n?{...n}:null}function Fw(e){return Fn&&Fn.views.find(t=>t.slug===e)||null}async function Uw({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 s4(r,u),await a4(u,n)},p=await kr();if(i!=="detected"){let m=Cw(t),g=String(n).trim(),y=p.views.find(x=>x.trackId===e&&(x.slug===m||Cw(x.name)===m||x.cmd===g&&Pt.resolve(x.serveCwd??x.cwd)===u));if(y)return p4(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:WU().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:i4(p,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await c4(p,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await jw(w),p.views.push(w),await Hn(),await br(w,"created",{origin:i}),Mw(e,w.id),i==="detected"&&await Ic(w.port)&&(w.state="live",w.lastHealthyAt=Date.now(),await Hn(),await br(w,"live")),{...w}}async function p4(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 r4(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Ic(t.port))return await Hn(),Mw(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 Bw(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 jw(t),await Hn(),await br(t,"created",{restarted:!0,reused:!0}),Mw(t.trackId,t.id),{...t,reused:!0}}async function FE(e){let n=(await kr()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await Bw(n.port):await $c(n.slug),n.state!=="stopped"&&(n.state="stopped",yr.delete(n.id),await Hn(),await br(n,"stopped")),{...n}}async function UE(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 jw(n),await Hn(),await br(n,"created",{restarted:!0}),{...n}}async function HE(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 Bw(r.port):await $c(r.slug),t.views.splice(n,1),yr.delete(r.id),await Hn(),await br(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function Hw(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 Hn()),n.length}async function WE(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 Hn()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(f4)){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 Ic(c))try{i.push(await Uw({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 Un,GU,OE,_E,IE,fo,VU,Ea,$E,Tc,zU,Ow,JU,YU,XU,Nw,ME,Iw,e4,n4,NE,Fd,Fn,$w,Ec,Pw,PE,yr,u4,Rw,f4,Wd=W(()=>{ft();me();jr();Un=KU(qU),GU=process.env.BOSTRAT_STATE_DIR||Pt.join(process.env.HOME||UU.tmpdir(),".bostrat"),OE=Pt.join(GU,"views.json"),[_E,IE]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),fo=Number(process.env.BOSTRAT_VIEW_MAX)||3,VU="bostrat-view-",Ea=e=>`${VU}${e}`,$E=new Set(["bostrat","www","api","views","app"]),Tc=e=>Object.assign(new Error(e),{status:400}),zU=new Set(["npm","pnpm","yarn","bun"]),Ow="package.json",JU=/[|;&`]|\$\(/,YU=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),XU=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Nw=e=>String(e).trim().split(/\s+/).filter(Boolean),ME=/^[A-Za-z_][A-Za-z0-9_]*=/;Iw=e=>e==="/",e4=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");n4=e=>_c.stat(e).then(t=>t.isDirectory(),()=>!1),NE=e=>_c.stat(e).then(t=>t.isFile(),()=>!1),Fd=e=>_c.realpath(e).catch(()=>Pt.resolve(e));Fn=null,$w=null;Ec=new Map,Pw=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,PE=12e3;yr=new Map,u4=12e4;Rw=!1;f4=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function _a(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>qw?null:n}function h4(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function qd(e,{bypassThrottle:t=!1}={}){let n=e?.workspace?.branch;if(!n)return;let r;try{r=await q(e)}catch{return}let o=_a(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-(Ww.get(e.id)||0)<m4)return;Ww.set(e.id,a);let i;try{i=await Bn(e.cwd,{caller:"reconcile"})}catch{return}let c=await ka(e).catch(()=>[n]),l=(i||[]).find(d=>c.includes(d.headRefName));if(!l)return;let u=await Re(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(!h4(d,o))return;let f=await q(e).catch(()=>null);if(f&&_a(f)==null)return;await C(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 C(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(()=>{}),Ww.delete(e.id)}var m4,Ww,qw,Rc=W(()=>{me();as();Pr();Dn();fc();cs();m4=6e3,Ww=new Map,qw=30*6e4});import Kw from"node:os";import{execFile as w4}from"node:child_process";import{promisify as g4}from"node:util";async function Kd(){if(process.platform!=="darwin")return Kw.freemem();try{let{stdout:e}=await y4("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 y4,Gw=W(()=>{y4=g4(w4)});import Qe from"node:fs/promises";import nt from"node:path";import VE from"node:os";import{randomUUID as b4}from"node:crypto";import{execFile as k4}from"node:child_process";import{promisify as x4}from"node:util";import{chromium as v4}from"playwright-core";async function Kn(){return ds||(Vw||=(async()=>{try{ds=JSON.parse(await Qe.readFile(JE,"utf8"))}catch{ds={mocks:[]}}finally{Vw=null}return ds.mocks||=[],ds})(),Vw)}async function tn(){await Mt(JE,ds)}function QE(e,t){qn(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&qn(e,t.v,"png")}function _t(e,t,n={}){return C({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:nt.join(n,`${e.id}.v${t}.html`),png:nt.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?nt.join(e.cwd,r):null}function gn(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 Yw(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function Gd(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals($4)}function R4(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 O4(e){let t=Buffer.alloc(24),n=await Qe.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function M4({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 C4(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 N4(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 P4({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await v4.launch({executablePath:YE,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:Ia})).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 C4(a,i),u=l&&M4({...l,pad:T4*Ia,align:Ia});u&&(i=await N4(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<XE)throw new Ht(`capture produced a ${i.length}-byte image`);let c=Yw(i);if(!c)throw new Ht("output is not a PNG");return await Qe.writeFile(t,i),{width:Math.round(c.width/Ia),height:Math.round(c.height/Ia),dpr:Ia,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 GE({htmlPath:e,outPath:t,width:n}){let r=await Qe.mkdtemp(nt.join(VE.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Jw(qE,["--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 (${qE})`,""):new Ht(c?.killed?`render timed out after ${$a}ms`:"firefox exited non-zero",s)}let a=await Qe.stat(t).catch(()=>null);if(!a||a.size<XE)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=Yw(await O4(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 Qe.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function D4(e){if(S4==="firefox")return GE(e);try{return await P4(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await GE(e)}catch(n){throw/not found/.test(n?.message||"")?new Ht(`no browser installed \u2014 chromium missing at ${YE} 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 e_(e){return zw=zw.then(e,e),zw}async function t_(e,t){let n=await Kn(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await tn(),await _t(r,"rendering",{version:t});try{let o=await Kd();if(o<E4)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 B4({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await Qe.rm(s.png,{force:!0}).catch(()=>{});return}Xw(r,{v:t,source:"html",htmlRel:nt.relative(r.cwd,s.html),pngRel:nt.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await tn(),await _t(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),Wn(r),qn(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 _t(r,"error",{version:t,error:r.error}),Wn(r)}}}function Xw(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?A4: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 Kn(),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 Kn()).mocks.find(r=>r.id===e);return n?{...n}:null}function n_({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&&!Gd(t))throw new Error("image must be a PNG");if(n){let o=R4(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>KE)throw new Error(`video must be ${KE/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function r_({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=n_({html:n,image:r,video:o}),w=await Kn(),m={id:b4().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 Qe.mkdir(Mc(s),{recursive:!0}),w.mocks.push(m),p?(await Qe.writeFile(nn(m,1).html,n),await tn(),await _t(m,"created"),Wn(m),qn(m,1,"html"),e_(()=>t_(m.id,1))):o?(await s_(m,1,o,f),await tn(),await _t(m,"created"),await _t(m,"ready",{version:1,width:m.width,height:m.height,dpr:m.dpr,durationS:m.versions[0]?.durationS??null}),Wn(m),QE(m,m.versions[0])):(await a_(m,1,r),await tn(),await _t(m,"created"),await _t(m,"ready",{version:1,width:m.width,height:m.height,dpr:m.dpr}),Wn(m),qn(m,1,"png")),{...m}}async function o_(e,{html:t,image:n,video:r,width:o}={}){let a=(await Kn()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=n_({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 Qe.mkdir(Mc(a.cwd),{recursive:!0}),i?(a.state="queued",await Qe.writeFile(nn(a,l).html,t),await tn(),await _t(a,"updated",{version:l}),Wn(a),qn(a,l,"html"),e_(()=>t_(a.id,l))):r?(await s_(a,l,r,c),await tn(),await _t(a,"updated",{version:l}),await _t(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:gn(a)?.durationS??null}),Wn(a),QE(a,a.versions.find(u=>u.v===l))):(await a_(a,l,n),await tn(),await _t(a,"updated",{version:l}),await _t(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Wn(a),qn(a,l,"png")),{...a}}async function L4(e){try{let{stdout:t}=await Jw(_4,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:ZE,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 F4(e,t){try{await Jw(I4,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:ZE,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await Qe.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function s_(e,t,n,r){let o=Mc(e.cwd),s=nt.join(o,`${e.id}.v${t}.${r.ext}`),a=nt.join(o,`${e.id}.v${t}.poster.png`);await Qe.writeFile(s,n);let i=await L4(s),c=await F4(s,a);Xw(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:nt.relative(e.cwd,s),posterRel:c?nt.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 a_(e,t,n){let r=Yw(n);if(!r)throw new Error("image must be a PNG");let o=nn(e,t);await Qe.writeFile(o.png,n),Xw(e,{v:t,source:"image",htmlRel:null,pngRel:nt.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function i_(e){let t=await Kn(),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 _t(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function c_(e){let t=await Kn(),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 Qe.rm(s.html,{force:!0}).catch(()=>{}),await Qe.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await Qe.rm(nt.join(r.cwd,s),{force:!0}).catch(()=>{});return await tn(),await _t(r,"deleted"),an("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function l_(e){let n=(await Kn()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=nt.join(zE,"mock-archive",e.id),o=Mc(e.cwd),s=nt.join(r,".bostrat","mocks");await Qe.mkdir(s,{recursive:!0});let a=await Qe.readdir(o).catch(()=>[]);for(let i of a)await Qe.copyFile(nt.join(o,i),nt.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await Qe.stat(nt.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=e.name||e.id,i.updatedAt=Date.now(),Wn(i)}return await tn(),n.length}async function u_(e){let t=await Kn(),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)an("mocks",r.id).catch(()=>{})}return n.length}async function d_(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await lt("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Kn();for(let r of n.mocks){await Wn(r);for(let o of r.versions||[])o.htmlRel&&await qn(r,o.v,"html"),(o.pngRel||o.posterRel)&&await qn(r,o.v,"png"),o.videoRel&&await qn(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var Jw,zE,JE,S4,YE,qE,A4,Ia,T4,XE,$a,E4,KE,_4,I4,ZE,Ht,ds,Vw,Wn,qn,Mc,$4,B4,zw,Ra=W(()=>{me();jr();Gw();Fr();Jw=x4(k4),zE=process.env.BOSTRAT_STATE_DIR||nt.join(process.env.HOME||VE.tmpdir(),".bostrat"),JE=nt.join(zE,"mocks.json"),S4=process.env.BOSTRAT_MOCK_ENGINE||"chromium",YE=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",qE=process.env.BOSTRAT_FIREFOX_BIN||"firefox",A4=1300,Ia=2,T4=32,XE=1500,$a=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,E4=700*1024*1024,KE=100*1024*1024,_4=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",I4=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",ZE=2e4,Ht=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
208
|
+
`)}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}
|
|
204
209
|
${n.trim().split(`
|
|
205
210
|
`).slice(-6).join(`
|
|
206
|
-
`)}`:t),this.name="MockRenderError"}},ds=null,Vw=null;Wn=e=>Tt("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),qn=(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=>nt.join(e,".bostrat","mocks");$4=Buffer.from([137,80,78,71,13,10,26,10]);B4=D4,zw=Promise.resolve()});import U4 from"node:fs/promises";import w_ from"node:path";import H4 from"node:os";import{randomUUID as g_}from"node:crypto";async function Sr(){return vr||(Zw||=(async()=>{let e=!0;try{vr=JSON.parse(await U4.readFile(y_,"utf8"))}catch{vr={flows:[]},e=!1}finally{Zw=null}if(vr.flows||=[],!e&&qe()){let{records:t,source:n}=await lt("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})(),Zw)}async function Oa(){await Mt(y_,vr)}function Qw(e,t,n={}){return C({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 b_({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>p_)throw new Error(`steps must be an array of 1-${p_}`);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>m_)throw new Error(`${l}: caption over ${m_} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>h_)throw new Error(`${l}: title over ${h_} chars`);let p=String(c.mock??"").trim().replace(/^mock:/,"");if(!p)throw new Error(`${l}: mock (name or id) required`);let f=r.filter(x=>x.id===p);if(f.length||(f=r.filter(x=>x.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(x=>`mock:${x.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(x=>x.v===m)))throw new Error(`${l}: "${w.name}" has no captured v${c.v} to pin`);let g=o.findIndex(x=>x.mockId===w.id),y=g>=0?o.splice(g,1)[0].id:g_().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 q4(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>f_)throw new Error(`name over ${f_} chars`);return e.trim()}async function k_({trackId:e}={}){let t=await Sr();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function K4(e){let n=(await Sr()).flows.find(r=>r.id===e);return n?{...n}:null}async function x_(e){let t=await K4(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=gn(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 v_({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=q4(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 b_({trackId:e,steps:n}),d=Date.now(),p={id:g_().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 Qw(p,"created"),Vd(p),{...p}}async function S_(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 b_({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 Qw(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Vd(o),{...o}}async function A_(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 Qw(r,"deleted"),an("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function T_(e){return(await Sr()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function E_(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(),Vd(r);return n.length&&await Oa(),n.length}async function __(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)an("flows",r.id).catch(()=>{})}return n.length}async function I_(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await lt("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Sr();for(let r of n.flows)await Vd(r);return{pushed:n.flows.length,source:"plane"}}var W4,y_,p_,f_,m_,h_,vr,Zw,Vd,eg=W(()=>{me();jr();Ra();Fr();W4=process.env.BOSTRAT_STATE_DIR||w_.join(process.env.HOME||H4.tmpdir(),".bostrat"),y_=w_.join(W4,"flows.json"),p_=20,f_=80,m_=500,h_=80,vr=null,Zw=null;Vd=e=>Tt("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import Er from"node:fs/promises";import _r from"node:path";import G4 from"node:os";import{randomUUID as V4}from"node:crypto";function Nc(e){return z4.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function Gn(){return Ar||(tg||=(async()=>{let e=!0;try{Ar=JSON.parse(await Er.readFile(R_,"utf8"))}catch{Ar={runbooks:[]},e=!1}finally{tg=null}return Ar.runbooks||=[],!e&&qe()&&await J4().catch(()=>{}),Ar})(),tg)}async function fs(){await Mt(R_,Ar)}function Jd(e){return Pc(Tt("runbooks",{...e},{trackId:e.trackId}))}async function J4(){let{records:e,source:t}=await lt("runbooks");if(!(t!=="plane"||!e.length)){await Er.mkdir(zd,{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 fs()}}function Ma(e,t,n={}){return C({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 _r.join(zd,`${e.id}.v${t}.md`)}function O_(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 M_(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>$_)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${$_/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 Y4(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function X4({trackId:e,status:t}={}){let n=await Gn(),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 C_({trackId:e,status:t}={}){let n=await Gn(),r=await X4({trackId:e,status:t});if(!qe())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await lt("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 ng(e){let t=await Z4(e);if(t)return t;if(!qe())return null;let{records:n,source:r}=await lt("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 N_(e,t){let n=await Ti("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function Z4(e){let n=(await Gn()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function P_({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=M_(r),c=await Gn(),l={id:V4().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(zd,{recursive:!0}),await Er.writeFile(Tr(l,1),r),l.versions.push({v:1,mdRel:_r.relative(ps,Tr(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await fs(),await Ma(l,"created",{version:1}),Jd(l),Pc(Lr("runbooks",l.id,1,"md",Tr(l,1))),{...l}}async function D_(e,{md:t,note:n}={}){let o=(await Gn()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");Y4(o,"update");let s=M_(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Er.mkdir(zd,{recursive:!0}),await Er.writeFile(Tr(o,a),t),o.versions.push({v:a,mdRel:_r.relative(ps,Tr(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await fs(),await Ma(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Jd(o),Pc(Lr("runbooks",o.id,a,"md",Tr(o,a))),{...o}}async function B_(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await Gn()).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 fs(),await Ma(r,t==="done"?"done":"reopened"),Jd(r),{...r})}async function j_(e){let t=await Gn(),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(_r.join(ps,o.mdRel),{force:!0}).catch(()=>{});return await fs(),r.trackDeletedAt||await Ma(r,"deleted"),Pc(an("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function L_(e){let n=(await Gn()).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,Jd(r);return n.length&&await fs(),n.length}async function F_(e){let t=await Gn(),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(_r.join(ps,o.mdRel),{force:!0}).catch(()=>{});Pc(an("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await fs()),n.length}async function U_(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await lt("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Gn();for(let r of n.runbooks){await Tt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await Lr("runbooks",r.id,o.v,"md",_r.join(ps,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var ps,R_,zd,$_,z4,Ar,tg,Pc,Yd=W(()=>{jr();me();Fr();ps=process.env.BOSTRAT_STATE_DIR||_r.join(process.env.HOME||G4.tmpdir(),".bostrat"),R_=_r.join(ps,"runbooks.json"),zd=_r.join(ps,"runbooks"),$_=256*1024,z4=[{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,tg=null;Pc=e=>Promise.resolve(e).catch(()=>{})});import rg from"node:fs/promises";import Dc from"node:path";import{randomUUID as Q4}from"node:crypto";function sg(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return e2.has(t)?"image":t2.has(t)?"doc":null}function n2(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 q_(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>H_)throw new Error(`file too large \u2014 max ${Math.floor(H_/(1024*1024))} MB`);let r=n2(t),o=Dc.extname(r).slice(1),s=sg(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=Q4().slice(0,8),i=og(e);await rg.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await rg.writeFile(Dc.join(i,c),n,{mode:384}),await rg.chmod(Dc.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${W_}/${c}`,kind:s,size:n.length}}function K_(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function ag(e){return e.role==="user"&&r2.test((e.text||"").trim())}var W_,og,H_,e2,t2,r2,ig=W(()=>{W_=".bostrat/uploads",og=e=>Dc.join(e,W_),H_=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,e2=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),t2=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"]);r2=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function ms(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}function cg(){let e=Number(process.env.BOSTRAT_LOOP_QUEUE_HOLD_MS);return Number.isFinite(e)&&e>0?e:o2}function a2(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 G_(e,t,{capMs:n=cg(),now:r=Date.now()}={}){if(!e)return!1;let o=a2(e,t,r);return e.delivery?.producer!=="loop"||!s2.has(t)?!1:o>=n}var o2,s2,V_=W(()=>{o2=5*6e4,s2=new Set(["busy","noSession"])});import{execFile as i2}from"node:child_process";import{promisify as c2}from"node:util";function lg(e){return`refs/bostrat/wip/${e}`}function d2(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function Y_(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function p2(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:u2}function X_(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 f2(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 z_.has(s)||(z_.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 da(s,"bostrat: wip snapshot"),{stdout:c}=await mo(s,["rev-parse",`${i}^{tree}`]),l=c.trim();if(!n&&Xd.get(s)===l)return{pushed:!1,reason:"unchanged"};let u=lg(o);return await mo(s,["push","--force","origin",`${i}:${u}`],J_),Xd.set(s,l),await C(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:u,sha:i,wip:!0,...Z_(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(`
|
|
207
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function
|
|
208
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function
|
|
211
|
+
`)}`: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(`
|
|
212
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function dg(e,{log:t=console,force:n=!1}={}){let r=[],o=[];for(let s of c_(e)){let a=await A2(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 T2(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(Zd.get(s)===l)return{pushed:!1,reason:"unchanged"};let u=v2(o,r);return await mo(s,["push","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,rescue:!0,...l_(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(`
|
|
213
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function pg(e,{log:t=console,ts:n=Date.now()}={}){let r=[],o=[];for(let s of c_(e)){let a=await T2(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 u_({listTracks:e,sweepMs:t=S2(),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 k2,x2,a_,mo,Zd,s_,l_,fg=q(()=>{Yu();ye();Zt();k2=b2(y2),x2=12e4,a_=6e4;mo=(e,t,n=15e3)=>k2("git",["-C",e,...t],{timeout:n,maxBuffer:4*1024*1024}),Zd=new Map,s_=new Set,l_=e=>e?.name||e?.repo?{name:e.name??null,repo:e.repo??null}:{}});import E2 from"node:fs";import mg from"node:fs/promises";import I2 from"node:path";function O2(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function d_(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(!O2(t))return{restored:!1,reason:"plane not configured"};let s=Hs(e),a=I2.join(s,`${o}.ndjson`);try{if(E2.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(),R2),w;try{w=await n(`${i}/v1/spine/${encodeURIComponent(o)}?after=${u}&limit=${_2}`,{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>$2)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(`
|
|
209
214
|
`)+`
|
|
210
|
-
`),await fg.rename(d,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var g2,y2,b2,tI=W(()=>{gt();me();g2=1e3,y2=5e5,b2=3e4});function nI(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 rI(e){let t=(e||[]).filter(n=>!x2.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 oI({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?yn("archived","lifecycle",e):n||r>0||s?yn("needs-you",n?"pane":s?"loop":"obligations",o||w,{count:r||(n||s?1:null)}):t?yn("archiving","lifecycle",e||w):a?.count?yn("needs-you","pr",a.since,{count:a.count,summary:a.summary??null}):i?.status==="working"?yn("working","operation",i.since,{summary:i.summary??null,count:i.count??null}):(l||n)&&c?.phase?yn("working","agent-phase",c.at,{summary:c.summary??null}):l?yn("working","pane",u||w):i?.status==="waiting"?yn("waiting","operation",i.since,{summary:i.summary??null,count:i.count??null}):d?.count?yn("waiting",d.source||"pending",d.since,{summary:d.summary??null,count:d.count}):p&&f?yn("settled","obligations",p,{summary:"no open actions"}):yn("idle","fallback",p||w)}var yn,x2,sI=W(()=>{yn=(e,t,n,r={})=>({kind:e,source:t,since:n||0,...r});x2=new Set(["merged","closed","lost"])});function v2(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 S2(e,t,n){let r=v2(t,n);return r?C(e,{type:"track.obligations.changed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),open:r.open,since:r.since}}).catch(()=>null):null}async function mg(e){if(Zd.has(e.id))return Zd.get(e.id);let t=await q(e).catch(()=>[]),n=[];try{n=Mn(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 Zd.set(e.id,p),!Le(e)&&e.kind!=="setup"&&S2(e,t,p),p}function aI({trackOf:e,debounceMs:t=A2,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"?mg(i):null).catch(i=>n.warn?.(`[obligations] fold ${s} failed: ${i?.message||i}`))},t);a.unref?.(),Bc.set(s,a)};return Je.on("event",r),()=>{Je.off("event",r);for(let o of Bc.values())clearTimeout(o);Bc.clear()}}var Zd,A2,Bc,hg=W(()=>{me();Nu();Zt();Zd=new Map;Je.on("event",e=>{e?.trackId&&Zd.delete(e.trackId)});A2=750,Bc=new Map});import{randomUUID as T2}from"node:crypto";async function ep(e,t,n={}){let r={operationId:T2(),kind:t,startedAt:Date.now(),...n};return Qd.set(e.id,r),await C(e,{type:"track.operation.changed",actor:"orchestrator",source:"orchestrator",payload:{ts:r.startedAt,state:"started",...r}}).catch(()=>{}),r}async function wg(e,t,n="completed",r=null){Qd.get(e.id)?.operationId===t?.operationId&&Qd.delete(e.id),await C(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 Qd,iI,gg=W(()=>{me();Qd=new Map;iI=e=>Qd.get(e)||null});var FI={};Tf(FI,{HANDOFF_LATEST_REL:()=>go,KNOWN_AGENTS:()=>Jc,TRACK_NAME_MAX:()=>$g,addTrackRepo:()=>wp,adoptTrack:()=>dy,agentAvailability:()=>dp,announceNodeUpdate:()=>zn,answerFullscreenOffer:()=>oy,answerPrompt:()=>fp,archiveTrack:()=>ys,archiveTrackForMergedPr:()=>hr,augmentFirstPromptAfterHandoff:()=>gI,buildAgentCmd:()=>Rg,canResumeSubtask:()=>BI,cancelQueued:()=>Xc,cancelQueuedToDraft:()=>jg,cancelStopJob:()=>Ua,cancelSubtask:()=>_p,changedRepos:()=>us,commitTransferOut:()=>Ap,createSetupTrack:()=>Yg,createTrack:()=>yp,deleteTrack:()=>Zg,demoteTransferredTracks:()=>uy,ensureLeadEpochs:()=>wo,forceMergeSubtask:()=>my,getHandoffJob:()=>Wa,getReshapeJob:()=>Zc,getSubtaskJob:()=>jH,getTrack:()=>ue,getTransferOutJob:()=>cy,healDeadLead:()=>Vc,interruptLead:()=>hp,isArchiving:()=>AH,isHandoffNoteEcho:()=>Ag,isInterruptEcho:()=>Tg,isModelSwitchEcho:()=>Eg,leadCliDead:()=>ey,listSubtaskJobs:()=>ks,listTracks:()=>ye,matchTranscriptsToBoxes:()=>xI,mergeNotifyMessage:()=>CI,navigatePrompt:()=>Dg,notifyTrackForMergedPr:()=>wr,notifyTrackForResolvedPr:()=>bp,offerFollowupsForMergedPr:()=>bs,pendingOffers:()=>Ng,queueGauges:()=>Bg,queueShipCompleteNotify:()=>Fg,queuedMessages:()=>hs,rawTrack:()=>Cg,reapArchivedOrphans:()=>ry,reconcileDemotedTracks:()=>ny,reconcileHandoffOutcome:()=>NI,reconcileInterruptedHandoffs:()=>ay,reconcileInterruptedReshapes:()=>zg,reconcileInterruptedSubtasks:()=>py,reconcileNodeUpdateNotices:()=>Mg,reconcileSubtaskOutcome:()=>DI,refreshStandingInstructions:()=>Jg,refuseIfTransferred:()=>Sp,rehydrateLead:()=>Qc,removeTrackRepo:()=>gp,renameTrack:()=>ty,reposOf:()=>jt,reshapeTrack:()=>Vg,resolveTrackBox:()=>kc,resolveTrackBoxByHookToken:()=>Yc,restartLead:()=>xp,resumeAgentCmd:()=>up,sayToLead:()=>qt,sendOptionsForAgent:()=>On,setTrackDraft:()=>Wg,setTrackModel:()=>qg,startLeadHandoff:()=>vp,startSpinUpView:()=>Ug,startSubtask:()=>fy,startTransferOut:()=>ly,stopJobActive:()=>AI,subtaskMessages:()=>Pg,trackMessages:()=>pp,unarchiveTrack:()=>kp});import V from"node:fs/promises";import G from"node:path";import{randomUUID as ip}from"node:crypto";import{execFile as E2}from"node:child_process";import{promisify as _2}from"node:util";function Rg(e,{skipPermissions:t=!0,model:n=null}={}){let r=lp[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=Ch(e).find(i=>i.alias===n);if(!a)throw new Error(`unknown ${e} launch model "${n}"`);s+=` --model ${a.alias}`}return s}function up(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=lp[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&&!un(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 gs(e){let t=lp[e];if(t)return process.env[t.envVar]||t.base}async function dp({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(gs(r),{retries:[]});return o===Gs?[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=Rg(e,{skipPermissions:t,model:n});if(await mu(r)===Ks){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.":nm[o]?`Install it on this node: ${nm[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 gI(e,t=go){return`${e.trim()}
|
|
215
|
+
`),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()}
|
|
211
216
|
|
|
212
|
-
(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
|
|
213
|
-
`;return bg=bg.then(()=>V.mkdir(Vn,{recursive:!0})).then(()=>V.appendFile(I2,n)).catch(()=>{}),bg}async function yI(e,{box:t,agent:n,message:r}){let o=de(await Z(),e);!o||o.archivedAt||(Ae(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 zn({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 V.readFile(kg,"utf8"))}catch{}if(d?.latest===t&&d?.current===e)return{announced:0,deduped:!0}}let l=await Z(),u=0;for(let d of l.tracks)Le(d)||!d.leadBox||(Ae(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 V.mkdir(G.dirname(kg),{recursive:!0}),await V.writeFile(kg,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function Mg({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await Z(),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)||N2(e,c)||(Ae(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 N2(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 bI(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=>P2.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 kI(e,t,n,{kind:r}={}){let o=await be(t);if(!o)throw new Error(`box "${t}" is not running`);let s=gs(o.agent),a=vc({text:n,producer:xc({kind:r,orchestration:!0}),kind:r||"orchestration",provider:o.agent}),i=de(await Z(),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 wn(i,a,Ta(g)),g}Ac(a,{ms:Date.now()-c,outcome:l||"up"});let u=On(o.agent,n);a.paste=!!u.paste;try{await at(t,n,{...u,kind:r||"orchestration"})}catch(g){throw await wn(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 wn(i,a,p?d.verdict==="unverified"?"unverified":"delivered":"unsubmitted"),await pe({flow:"orchestration",phase:"sent",submitted:p,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let f=await Z(),w=de(f,e);if(!w)return;let m=w.boxes.find(g=>g.name===t);Ae(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 D2(e){let t=G.join(Fs(e),`${e.id}.ndjson`),n;try{n=await V.stat(t)}catch{return null}let r=cI.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await q(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return cI.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function B2(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Xh(n.name,n.agent)}async function Wt(e,t=null,n=null){let r=t??new Map((await cn()).map(b=>[b.name,b])),o=!!e.transferredTo,s=e.boxes.map(b=>{let I=o?null:r.get(b.name),$=Ir.has(b.state)?b.state:I?b.role==="lead"?"live":b.state:"stopped";return{...b,state:$,cwd:I?.cwd||b.cwd,agent:I?.agent||b.agent,sessionId:I?.sessionId||b.sessionId}}),[a,{working:i,prompted:c},l,u]=await Promise.all([D2(e),B2(e,s),mg(e),n??mn().catch(()=>[])]),d=Gc.has(e.id),p=(u||[]).filter(b=>b.trackId===e.id&&!["merged","closed","lost"].includes(b.state)),f=iI(e.id),w=f&&{status:"waiting",since:f.startedAt,summary:f.summary??null,count:f.count??null}||nI(p),m=s.find(b=>b.name===e.leadBox)?.state==="live",g=m?hs(e.id).filter(b=>b.kind!=="ship-notify"&&b.kind!=="prompt-answer"):[],y=m&&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`}:g.length?{source:"queue",count:g.length,since:Math.min(...g.map(b=>b.ts||Date.now())),summary:"a queued message is about to send"}:null,x=oI({archivedAt:e.archivedAt,archiving:d,prompted:c,openActions:l.openActions,openActionSince:l.openActionSince,prDecisions:rI(p),operation:w,phase:l.phase,working:i,workingSince:l.currentTurnAt,pending:y,completedTurnAt:l.completedTurnAt||a,live:m});return{...e,boxes:s,lastResponseAt:a,working:i,prompted:c,archiving:d,status:x}}function xI(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-j2,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 vI(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=D0(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=xI(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 T0(p.box,p.sessionId),n=!0}}return n}async function ye(){let e=await Z();await vI(e)&&await ae();let t=new Map((await cn()).map(r=>[r.name,r])),n=await mn().catch(()=>[]);return Promise.all(e.tracks.map(r=>Wt(r,t,n)))}async function kc(e,t){let n=await Z();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 Z();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&ck(o.hookTokenHash,t))return{track:r,box:o};return null}async function Cg(e){return de(await Z(),e)??null}async function ue(e){let t=await Z(),n=de(t,e);return n?(await vI(t)&&await ae(),Wt(n)):null}async function Ng(e){let t=await q(e).catch(()=>[]),n;try{n=Mn(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=Xx(t)}catch{o=[]}return r.concat(o.map(s=>({kind:"pr-open",id:`${s.repo??""}#${s.number}`,ts:null})))}function Tg(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(L2[e.agent]||[]).some(n=>n.test(t))}function Eg(e){let t=(e.text||"").trim();return t?F2.some(n=>n.test(t)):!1}async function pp(e,t={}){let n=await ue(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=Dw(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...BE(n),...l],p={windows:[...kv(n),...xv(n,Bu),...a,...u]},w=(await Promise.all(n.boxes.map(O=>ch(O,t)))).flat(),m=w.map(O=>{let ce={kind:"message",...O};if(ce.role==="assistant"&&(ce.text||"").trim()&&ce.tools?.length){let ve=Zr(ce.agent).cliTools,wt=ce.tools.filter(Q=>!ve.has(Q));return{...ce,tools:wt.length?wt:void 0}}return ce}).map(O=>{if(O.role!=="user")return O;let ce=Lx(O);return ce?ce.kind==="suppress"?null:{...O,note:ce,text:Ux(ce)}:O}).filter(Boolean).filter(O=>!(O.role==="user"&&r.has((O.text||"").trim()))).filter(O=>!Tg(O)).filter(O=>!Eg(O)).filter(O=>!Ag(O)).filter(O=>!ag(O)).filter(O=>O.role!=="assistant"||!!(O.text||"").trim()||!!O.images?.length).filter(O=>!vv(O,p)),g=m.filter(O=>O.failed&&O.text===Uo);if(g.length){let O=new Map;for(let ce of g){if(!O.has(ce.box)){let Q=[...(await De(ce.box,{lines:Xe}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(ge=>ge[1]);O.set(ce.box,Q.length?Q[Q.length-1]:null)}let ve=O.get(ce.box);ve&&(ce.text=`\u25A0 ${ve}`)}}let y=await Promise.all(n.boxes.map(O=>cS(O,t)));await dE(n,y.flat());let{agentTurnBoxes:x}=await lE(n,m);for(let O of n.boxes)x.has(O.name)&&O.cwd&&tu(O.cwd);await eA(n),await WE(n,m).catch(()=>{});for(let O of n.boxes)O.agent==="codex"&&O.state==="live"&&await fE(n,O),O.agent==="cursor"&&O.state==="live"&&await yE(n,O);let b=await Promise.all(n.boxes.map(O=>iS(O,t)));await uE(n,b.flat().filter(O=>!xg(O.box,O.ts,u)));let I=await q(n),$=c&&i.viewId?i.viewId:null,P=fA(I),F=Mn(I).filter(O=>!(O.kind==="message"&&(Tg(O)||Eg(O)||Ag(O)||ag(O)))&&!(O.kind==="view-card"&&$&&O.viewId===$)),B=mA(F,P),T=F.filter(O=>!(O.kind==="message"&&O.role==="assistant"&&!B.has(O)&&xg(O.box,O.ts,P))),_=Um(I,T).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=>!xg(O.anchorBox,O.anchorTs??O.ts,u)),U=Jx(Yx([...T,..._].filter(O=>O.ts!=null).sort((O,ce)=>O.ts-ce.ts))),J=H2(n),R=J.size?U.filter(O=>O.kind==="event"||!J.has(O.box??O.anchorBox)):U,A=n.boxes.find(O=>O.name===n.leadBox)||null,M=A?await Pi(A.cwd,A.sessionId,{fallbackNewest:!1}):null;if(!M&&A){let O=[...I].reverse().find(ce=>ce.type==="context.usage"&&ce.boxId===A.name);O?.payload?.window&&(M={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 N=[...w].reverse().find(O=>O.role==="assistant"&&O.model),K=N?.model||null,Y=[...n.events||[]].reverse().find(O=>O.type==="model-change"),fe=M?.model||K;Y&&(!N?.ts||Y.at>=N.ts)&&(fe=Y.to);let Ie=A?await De(A.name,{lines:Xe}).catch(()=>""):"",Ce=A&&to(A.agent,Ie)?await ln(A.name).catch(()=>Ie):Ie,X=A?Nn(A,Ce):null;if(X&&A){let O=Ju(A,X,Ce);if(O){let ce=SS(A.name,X.promptId);ce.action!=="give-up"&&(ce.action==="send"&&(Ge(A.name,O.keys,{kind:"auto-reject"}).catch(()=>{}),ce.first&&C(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:A.name,provider:A.agent,payload:{ts:Date.now(),promptId:X.promptId,rule:O.id}}).catch(()=>{})),X=null)}}!X&&A&&!ea(A.agent,Ie)&&!hs(n.id).length&&(X=jm(R,{leadBoxId:A.name,provider:A.agent,isAnswered:O=>I.some(ce=>ce.type==="prompt.answered"&&ce.payload?.promptId===O)})),X&&tA(n,A,X);let et=R.filter(O=>!(O.kind==="message"&&O.role==="assistant"&&O.ask&&!String(O.text||"").trim()&&!O.images?.length)),se=!!A&&vd(A.name,A.agent,Ie)&&!X;A&&!A.readyAt&&(se||X||Ie&&Wo(A.agent,Ie))&&(A.readyAt=Date.now(),await ae());let ee=!!A&&!A.readyAt&&Date.now()-(n.createdAt||0)<U2;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&A&&Wo(A.agent,Ie)&&!X){let O=await Z(),ce=de(O,n.id);ce?.setup&&!ce.setup.connectedAt&&(ce.setup.connectedAt=Date.now(),n.setup.connectedAt=ce.setup.connectedAt,await C(ce,{type:"provider.connected",actor:"system",source:"pane",boxId:A.name,provider:A.agent,payload:{provider:A.agent,ts:ce.setup.connectedAt}}).catch(()=>{}),await ae())}let Oe=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,z=Zc(n.id),we=z&&!z.finishedAt?{running:!0,step:z.step||null,added:z.added||[],removed:z.removed||[]}:null,Fe=null;if(c){let O=Date.now()-i.startedAt;if(i.viewId&&!se){let ce=Ud(n.id);ce&&(Ae(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:ce.finishedAt}),await ae())}else if(X||O>PE&&!se&&!i.viewId||O>Pw){let ce=X?"needs-input":O>Pw?"timeout":"no-view";Ud(n.id,{error:ce})&&await C(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:A?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:ce}}).catch(()=>{})}else Fe={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:et,context:M?{pct:M.pct,occupancy:M.occupancy,window:M.window,windowKnown:M.windowKnown,model:M.model}:null,models:{current:fe,options:Ch(A?.agent)},pendingPrompt:X,working:se,stopping:AI(n.id),leadStarting:ee,handoff:Oe,reshape:we,view:Fe,queued:hs(n.id).filter(O=>O.kind!=="ship-notify"&&O.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function H2(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 Pg(e,t){let n=await ue(e);if(!n)return null;let r=ks(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 q(n),a=Mn(s).filter(d=>d.box===o&&!(d.kind==="message"&&d.role==="assistant"&&d.ask&&!String(d.text||"").trim()&&!d.images?.length)),i=Um(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:Xe}).catch(()=>"");u=vd(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}function W2(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>lI)throw new Error(`custom answer is too long (max ${lI} characters)`);return r})}function q2(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 fp(e,t={}){let n=jc.get(e);n&&await n.catch(()=>{});let r=K2(e,t);jc.set(e,r);try{return await r}finally{jc.get(e)===r&&jc.delete(e)}}async function K2(e,{promptId:t,answers:n,text:r,custom:o}={}){let s=de(await Z(),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 be(s.leadBox))throw new Error(`lead box "${s.leadBox}" is not running`);let c=await wh(a);if(c||(c=await G2(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=W2(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,x)=>l[x]&&!y.multiSelect?[]:Array.isArray(n?.[x])?n[x]:[]));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 Ge(s.leadBox,["Escape"],{kind:"answer-prompt"}),d={mode:"keys",value:"Escape",count:1};else if(c.answerMode==="say"){let y=Kx(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=bS(c,n||[],l[0]);await rm(s.leadBox,[...y.pre,{text:y.text}],{kind:"answer-prompt"});let x=!1;for(let I of[150,300,600]){await $r(I);let $=await ln(s.leadBox).catch(()=>""),P=Nn(a,$);if(P?.customText&&q2(P.customText,y.text)){x=!0;break}}if(!x)throw new Error("custom answer didn't take \u2014 the widget moved; reload and answer again");await rm(s.leadBox,y.post,{kind:"answer-prompt"});let b=I=>I.map($=>$.key).join(" ");d={mode:"keys+text",value:`${b(y.pre)} \u2039text\u203A ${b(y.post)}`.trim(),count:y.pre.length+1+y.post.length}}else if(c.provider==="claude"||c.answerMode==="keys"){let y=gh(c,n||[]);await Ge(s.leadBox,y,{kind:"answer-prompt"}),d={mode:"keys",value:y.join(" "),count:y.length}}else{let y=ux(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 x=0;x<y.length;x++){let b=y[x],I=new Set(Array.isArray(n?.[x])?n[x]:[]),$=[...I].map(F=>b.options?.[F]).filter(Boolean).map(F=>({label:F.label,description:F.description||""}));l[x]&&$.push({label:l[x],description:"",custom:!0});let P=(b.options||[]).map((F,B)=>I.has(B)?null:F?.label).filter(Boolean);Nh(s,a,c,{...d,groupId:c.groupId||c.promptId,questionIndex:x,questionTotal:c.questionTotal??y.length,header:b.header||"",question:b.question||"",multiSelect:!!b.multiSelect,chosen:$,rejected:P})}return await pe({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,x)=>f.has(x)?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 pe({flow:"prompt",phase:"answered",sessionName:s.leadBox,agent:a.agent,provider:c.provider,promptId:c.promptId,...d}).catch(()=>{}),Nh(s,a,c,d),{ok:!0,promptId:c.promptId,sent:d.count}}async function G2(e,t){if(!t)return null;let n=await q(e).catch(()=>[]);return jm(Mn(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function Dg(e,{promptId:t,questionIndex:n}={}){let r=de(await Z(),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 be(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await wh(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=kS(a,n);return c.length?(await Ge(r.leadBox,c,{kind:"navigate-prompt"}),await pe({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 mp(e){let t=Ba.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Ba.set(e,t)),t}function hs(e){return(Ba.get(e)?.items??[]).map(({delivery:t,...n})=>({...n}))}function Bg(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 z2(e,t,n){let r=t.filter(o=>o.delivery&&!o.delivery.settled);r.length&&(async()=>{let o=de(await Z(),e)||null;for(let s of r)await wn(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),z2(e,r,t),r}async function jg(e){let t=Xc(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=de(await Z(),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 Lg(e){let t=mp(e);t.timer||(t.timer=setInterval(()=>J2(e).catch(()=>{}),V2))}async function J2(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 Z(),e);if(!n||n.archivedAt){Xc(e,"archived");return}let r=async(l,u="")=>{let d=[];for(let g of t.items)EE(g.delivery,l),G_(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(cg()/6e4)),w=String(u||"").split(`
|
|
217
|
+
(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 Mg(e){return e.role==="user"&&eH.test((e.text||"").trim())}async function Q(){return Dt||(Eg||=(async()=>{try{Dt=JSON.parse(await z.readFile(N_,"utf8"))}catch{Dt={tracks:[]}}finally{Eg=null}return Dt})(),Eg)}async function ae(){await Mt(N_,Dt)}function rH(e,t){let n=JSON.stringify({trackId:e,...t})+`
|
|
218
|
+
`;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(`
|
|
214
219
|
`).map(g=>g.trimEnd()).filter(g=>g.trim()).slice(-3).join(`
|
|
215
|
-
`),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
|
|
220
|
+
`),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(),Qi(n,{provider:o?.agent??null,cls:"delivery-held",note:m}).catch(()=>{})},o=n.boxes.find(l=>l.name===n.leadBox),s=Wa(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 cy(a)&&(a=await Vc(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(Ui(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 Pg(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 Sc(n,c.delivery,{position:1,reason:"requeue"})),await Vc(n,o,"queue-gate")}await ae()}finally{t.flushing=!1}}}async function U_(e,t,n,r){let o=no(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(ja(e,[n]),await ae()),WS(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 kH(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 Pg(e,t,n,r,o=null,s=null,a=null){a||(a=vc({text:r,producer:xc({kind:o}),kind:o,provider:t?.agent??null,attachments:Array.isArray(s)?s.length:0})),a.provider??=t?.agent??null,await U_(e,t,r,o);let i=Wc.get(e.id)||0,c=r,l=!1,u=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(c=P_(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=n_(d.map(g=>g.rel));c=c.trim()?`${c.trim()}
|
|
216
221
|
|
|
217
|
-
${m}`:m}let p=
|
|
222
|
+
${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 Ac(a,{ms:Date.now()-m,outcome:b?.code==="cli-not-running"?"cli-not-running":"blocked"}),b}if(Ac(a,{ms:Date.now()-m,outcome:g||"up"}),(Wc.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 kH(e,t)){let b=hp(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),Vg(e.id),await Sc(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 DH(e,t,c,f);a.verify=f,w?await gn(e,a,f.verdict==="unverified"?"unverified":"delivered"):(await W_(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 zg(e,{actions:t}={}){let n=hp(e),r=kA(t),o=vc({text:r,producer:xc({kind:"ship-notify"}),kind:"ship-notify"});n.items.push({id:o.id,text:r,ts:Date.now(),kind:"ship-notify",delivery:o}),Vg(e),(async()=>{let s=pe(await Q(),e);s&&(o.provider=s.boxes.find(a=>a.name===s.leadBox)?.agent??null,await Sc(s,o,{position:n.items.length,reason:"queue"}))})().catch(()=>{})}async function xH(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&&iA(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=pe(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`);Ap(i);let c=i.boxes.find(d=>d.name===i.leadBox),l=da(t),u=vc({text:l,producer:xc({kind:n,producer:o}),kind:n,provider:c?.agent??null,attachments:a.length});try{return await vH(e,i,c,l,{kind:n,atts:a,d:u,dismissPrompt:s})}catch(d){throw await gn(i,u,Ta(d)),d}}async function vH(e,t,n,r,{kind:o,atts:s,d:a,dismissPrompt:i=!1}){await U_(t,n,r,o),Ua(e,"say");let c=await xe(t.leadBox);if(c&&await cy(c)?await Vc(t,n,"say")?(await ae(),c=await xe(t.leadBox)):c=null:!c&&await Qc(t)&&(await ae(),c=await xe(t.leadBox)),!c)throw new Error(`lead box "${t.leadBox}" is not running`);let l=jc.get(e);l&&await l.catch(()=>{});let u=hp(e),d=await De(t.leadBox,{lines:Qe}).catch(()=>""),p=u.items.length>0?"queue":Ui(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 xH(t,n,y,a);b.gone&&p==="prompt"&&(d=b.pane,f=Ui(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),Vg(e),o||Da(t,null),await ae(),await Sc(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 Pg(t,n,c,r,o,s,a)}catch(g){if(g?.code!=="cli-not-running"||!await Vc(t,n,"say-gate")||(await ae(),c=await xe(t.leadBox),!c))throw g;m=await Pg(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 Jg(e,{hint:t}={}){let n=pe(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";VE(n.id,{name:s,box:n.leadBox});let a=String(t||"").trim()?`${__}
|
|
218
223
|
|
|
219
|
-
What to preview: ${String(t).trim()}`:
|
|
224
|
+
What to preview: ${String(t).trim()}`:__;try{await qt(e,a,{kind:"spin-up-view"})}catch(i){throw Hd(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function wp(e){let t=pe(await Q(),e);if(!t)throw new Error(`track "${e}" not found`);if(t.archivedAt)throw new Error(`track "${e}" is archived`);Wc.set(e,(Wc.get(e)||0)+1);let n=Xc(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=_H(e,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,$r(SH)]);let a=pe(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 H_(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 Yg(e){Ua(e,"track-ended"),Wc.delete(e),Xc(e,"track-ended")}function _H(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=pe(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<AH&&!r.canceled;g++){await Ke(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let y of TH){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<IH&&!r.canceled&&(await $r(EH),!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=pe(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 MH(u,d).catch(()=>null),p||(f=await PH(u,d).catch(()=>null)));let w=u&&i&&!r.canceled?await RH(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 $H(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 $H(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 RH(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 MH(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 OH){await $r(r);let o=await De(e.leadBox,{lines:Qe}).catch(()=>""),s=qi(t.agent,o);if(s&&Eu(n.text,s))return await W_(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 PH(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>CH||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!Ue(r.text))return null;for(let s of NH){await $r(s);let a;try{a=await lh(t,{})}catch{return null}if(Ax(a,r))return null;let i=await De(e.leadBox,{lines:Qe}).catch(()=>"");if(qi(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 DH(e,t,n,r=null){return Vi({sessionName:e.leadBox,agent:t?.agent,agentCmd:bs(t?.agent),text:n,trackId:e.id,flow:"say",stats:r})}async function W_(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=qi(t,s);if(!a||r&&!Eu(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(`
|
|
220
225
|
|
|
221
|
-
`),{source:"restore"})?.text??null:null}function
|
|
226
|
+
`),{source:"restore"})?.text??null:null}function jH(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>BH)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 Xg(e,t,{rev:n=null}={}){if(typeof t!="string")throw new Error("text (string) required");let r=pe(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&&jH(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}
|
|
222
227
|
|
|
223
|
-
${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 Z(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await be(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=zS(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:gs(o.agent)});let u=await JS(a,r.leadBox,i);return Ae(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 EI(e){for(let t of e){if(!t||!await ua(t))continue;let n=await _S(t).catch(()=>null);if(!n)continue;if((await V.stat(G.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:G.basename(t.base),dir:t.dir??null,base:t.base,baseRef:t.baseRef??null,repo:t.repo??null}]:[]:[]}async function us(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 Kg(e){let t=e;typeof t=="string"&&!G.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await Ps(t)??await Vl(t));let n=await EI([t]);if(!n){let r=typeof e=="string"&&G.isAbsolute(e)&&!await V.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/${Ep(t)}-${e}`,s=[];try{await V.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await Kg(i);if(s.some(f=>f.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await Re(c).catch(()=>null),u=G.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let f=2;a.has(u);f++)u=`${G.basename(c)}-${f}`;a.add(u);let d=G.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 Et(i.base,i.dir).catch(()=>{});throw await V.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function wp(e,t){let n=await Z(),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 Kg(typeof t=="string"?t.trim():t);if((o.repos??[]).some(p=>p?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await Re(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(p=>p?.name).filter(Boolean)]),c=G.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let p=2;i.has(c);p++)c=`${G.basename(s)}-${p}`;let l=G.join(o.dir,c),u=await Qo(s);await Ah(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),Ae(r,"retagged",{action:"add",repo:a??s,name:c,workspace:Gg(o)}),await ae(),d}async function gp(e,t){let n=await Z(),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 Et(s.base,s.dir).catch(()=>{}),await V.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),Ae(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:Gg(o)}),await ae(),{removed:s.name}}async function rp(e,t){let n=await Z(),r=de(n,e);r&&(r.reshape={...r.reshape||{},...t},await ae())}async function wH(e){let t=await V.readdir(e).catch(()=>[]);for(let n of t)if(!(n===".git"||n===".bostrat")){if(n==="AGENTS.md"){let r=await V.readFile(G.join(e,n),"utf8").catch(()=>"");if(Fc(r))continue}return!0}try{let{stdout:n}=await cp("git",["-C",e,"rev-list","--count","HEAD"]);if(Number(n.trim())>1)return!0}catch{}return!1}function dI(e,t,n){let r=G.basename(e);n.has(r)&&t&&(r=t.replace("/","-"));for(let o=2;n.has(r);o++)r=`${G.basename(e)}-${o}`;return n.add(r),r}async function _I(e,{added:t=[]}={}){let{shape:n,dir:r,staging:o,sub:s,base:a,scratch:i}=e;for(let l of t)await Et(l.base,l.dir).catch(()=>{});let c=async(l,u)=>{await bn(l)&&!await bn(u)&&await V.rename(l,u)};if(n==="worktree"&&s){let l=G.join(r,s);await bn(l)&&(await c(G.join(r,".bostrat"),G.join(l,".bostrat")),await op(a,l,o))}if(n==="repo"&&i){let l=G.join(r,i);await bn(l)&&(await bn(o)&&await V.rm(o,{recursive:!0,force:!0}),await V.rename(l,o))}return await bn(o)?(await bn(r)&&(await c(G.join(r,".bostrat"),G.join(o,".bostrat")),await V.rm(r,{recursive:!0,force:!0})),n==="worktree"?await op(a,o,r):await V.rename(o,r),!0):!1}async function Vg(e,t,{skipPermissions:n=!0}={}){let r=await Z(),o=de(r,e);if(!o)throw bt(`track "${e}" not found`,404);if(o.archivedAt)throw bt("track is archived \u2014 unarchive it first");try{Sp(o)}catch(g){throw Object.assign(g,{status:409})}if(o.kind==="setup")throw bt("setup boxes have no repos to reshape");if(String(o.name||"").startsWith("\u21BB"))throw bt("this Track's repos are the loop's \u2014 propose-update the loop instead");let s=Ig.get(e);if(s&&!s.finishedAt)throw bt("a reshape is already running for this Track");let a=ws.get(e);if(a&&!a.finishedAt)throw bt("a lead handoff is running \u2014 retry when it settles");let i=ap.get(e);if(i&&!i.finishedAt)throw bt("a node transfer is running \u2014 retry when it settles");if(tp.has(e))throw bt("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 bt("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 bt("repos (non-empty list of paths or owner/name slugs) required",400);let l=[];for(let g of c){let y;try{y=await Kg(g)}catch(x){throw bt(x?.message||String(x),400)}if(l.some(x=>x.base===y))throw bt(`repo tagged twice: ${g}`,400);l.push({sel:g,base:y,slug:await Re(y).catch(()=>null)})}let u=o.workspace;if(u?.kind==="multi"){let g=(u.repos??[]).filter(Boolean),y=g.filter(I=>!l.some($=>$.base===I.base)),x=l.filter(I=>!g.some($=>$.base===I.base));if(!y.length&&!x.length)return{trackId:e,step:"done",inPlace:!0,added:[],removed:[],startedAt:Date.now(),finishedAt:Date.now(),error:null};for(let I of y){let $=await Yo(I.dir,{ignorePlumbing:!0}).catch(()=>!1),P=await Xo(I.dir,I.baseRef).catch(()=>0);if($||P>0)throw bt(`"${I.name}" has work \u2014 ship or discard it before dropping it`)}let b={trackId:e,step:"retag",inPlace:!0,added:[],removed:[],startedAt:Date.now(),finishedAt:null,error:null};for(let I of x){let $=await wp(e,I.base);b.added.push($.repo??$.base)}for(let I of y)await gp(e,I.name),b.removed.push(I.repo??I.base);return b.step="done",b.finishedAt=Date.now(),b}if(u?.kind!=="worktree"&&u?.kind!=="repo")throw bt(`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 bt(`"${G.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 bt("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 Ig.set(e,m),await rp(e,{step:m.step,startedAt:m.startedAt,finishedAt:null,error:null,added:m.added,removed:m.removed,layout:null}),gH(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 rp(e,{step:m.step,error:m.error,finishedAt:m.finishedAt}).catch(()=>{})}),m}async function gH(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(A,M={})=>{e.step=A,console.log(`[reshape ${c}] step \u2192 ${A}`),await rp(c,{step:A,...M})},p=await be(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:Og})}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 Hw(c).catch(()=>0),await nu(u).catch(()=>{}),p&&(await Jm(o.name,{fromAgent:s}).catch(()=>{}),await Ct(o.name).catch(()=>{}));let g=`${u}.reshape-${Date.now()}`,y=new Set([".bostrat"]),x={shape:l.kind,dir:u,staging:g,base:l.kind==="worktree"?l.base:null,sub:l.kind==="worktree"&&r?dI(l.base,l.repo??null,y):null,scratch:null},b=l.branch||`track/${Ep(t.name||c)}-${c}`,I=[],$=[],P=null;try{if(await d("move",{layout:x}),l.kind==="worktree")if(await op(l.base,u,g),await V.mkdir(u,{recursive:!0}),r){let M=G.join(u,x.sub);await op(l.base,g,M),await bn(G.join(M,".bostrat"))&&await V.rename(G.join(M,".bostrat"),G.join(u,".bostrat"));let N=G.join(M,"AGENTS.md");Fc(await V.readFile(N,"utf8").catch(()=>null))&&(await V.rm(N,{force:!0}).catch(()=>{}),await RI(M,"/AGENTS.md").catch(()=>{})),I.push({name:x.sub,dir:M,base:l.base,baseRef:l.baseRef??null,repo:l.repo??null})}else await bn(G.join(g,".bostrat"))&&await V.rename(G.join(g,".bostrat"),G.join(u,".bostrat"));else await V.rename(u,g),await V.mkdir(u,{recursive:!0}),await bn(G.join(g,".bostrat"))&&await V.rename(G.join(g,".bostrat"),G.join(u,".bostrat"));await d("materialize");for(let M of n){if(l.kind==="worktree"&&M.base===l.base)continue;let N=dI(M.base,M.slug,y),K=G.join(u,N),Y=await Qo(M.base);await Ah(M.base,b)?await nc(M.base,K,b):await Zo(M.base,K,b,Y);let fe={name:N,dir:K,base:M.base,baseRef:Y,repo:M.slug};I.push(fe),$.push(fe)}if(l.kind==="repo"&&await wH(g)){let M="scratch";for(let N=2;y.has(M);N++)M=`scratch-${N}`;y.add(M),x.scratch=M,await rp(c,{layout:x}),await V.rename(g,G.join(u,M)),e.scratch=!0}await Ha(u,I,b,{scratch:x.scratch}),await d("relaunch"),P=await $n(o.name,u,{agentCmd:a,agent:s}),await sr({cwd:u,sessionName:o.name,agent:s,agentCmd:gs(s)}).catch(M=>{if(M?.code!=="cli-not-running")throw M});let A=await De(o.name,{lines:Xe}).catch(()=>"");if(!Wo(s,A))throw new Error(`relaunched ${s} CLI never drew its composer in the hub`)}catch(A){await d("rollback",{error:A?.message||String(A)}),await Ct(o.name).catch(()=>{}),await _I(x,{added:$}).catch(Y=>{console.error(`[reshape ${c}] rollback move failed: ${Y?.message||Y}`)});let M=await Z(),N=de(M,c),K=N?.boxes.find(Y=>Y.name===o.name)||null;try{let Y=up(s,f,{skipPermissions:i,cwd:u,peerName:o.name}),fe=await $n(o.name,u,{agentCmd:Y,agent:s,resumeSessionId:f,pinned:w});K&&(K.hookTokenHash=Oo(fe.hookToken),K.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 N&&(Ae(N,"retag-failed",{action:"reshape",box:o.name,agent:s,reason:A?.message||String(A),rolledBack:!0}),N.reshape={...N.reshape||{},step:"error",error:A?.message||String(A),finishedAt:Date.now(),recovered:"rolled-back"},await ae()),e.step="error",e.recovered="rolled-back",e.error=A?.message||String(A),e.finishedAt=Date.now(),A}await d("commit");let F=await Z(),B=de(F,c);if(!B)throw new Error(`track "${c}" vanished mid-reshape`);let T=B.boxes.find(A=>A.name===o.name);if(!T)throw new Error(`lead box row "${o.name}" missing during reshape commit`);let _=Date.now(),U=wo(T),J=U[U.length-1];J.endedAt==null&&(J.endedAt=_),J.endReason==null&&(J.endReason="reshape");let R=await Go({agent:s,agentCmd:a});U.push({agent:s,sessionId:P.sessionId,pinned:P.pinned===!0,cliVersion:R,startedAt:_,endedAt:null}),T.cwd=u,T.agent=s,T.cliVersion=R,T.sessionId=P.sessionId,T.pinned=P.pinned===!0,T.hookTokenHash=Oo(P.hookToken),T.state="live",T.role="lead",delete T.readyAt,B.cwd=u,B.workspace={dir:u,kind:"multi",branch:b,repos:I},B.pendingHandoffHydrate={artifact:m||go,at:_},Ae(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:P.sessionId,epoch:U.length-1,workspace:Gg(B.workspace)}),B.reshape={...B.reshape||{},step:"done",finishedAt:_,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()=>{T.readyAt||(T.readyAt=Date.now(),await ae())}}),await bn(g)&&(l.kind==="worktree"&&await Et(l.base,g).catch(()=>{}),await V.rm(g,{recursive:!0,force:!0}).catch(()=>{})),e.step="done",e.finishedAt=_}async function zg(){let e=await Z(),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 V.readdir(c).catch(()=>[])){if(d.has(f))continue;let w=G.join(c,f),m=await V.readFile(G.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 _I(r.layout,{added:p})}catch(c){a=c?.message||String(c),console.error(`[reshape ${n.id}] boot rollback failed: ${a}`)}o&&!Ir.has(o.state)&&(o.state="stopped");let i=`interrupted by a node restart during "${r.step}"${s?" \u2014 moved back":""}${a?` (${a})`:""}`;Ae(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 yH(e,t,{repo:n,repos:r,cwd:o}={}){await V.mkdir(Uc,{recursive:!0});let s=G.join(Uc,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"&&!G.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await Ps(n)??await Vl(n));let i=await EI([n,o,$2]);if(i){let c=`track/${Ep(t)}-${e}`;try{let l=await Qo(i);await Zo(i,s,c,l),await Kc(s);let u=await Re(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await V.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await V.mkdir(s,{recursive:!0}),await Th(s),await Kc(s),{dir:s,base:null,branch:null,kind:"repo"}}async function Kc(e){try{await V.writeFile(G.join(e,"AGENTS.md"),Lc,{flag:"wx"})}catch{return}await vH(e,["/AGENTS.md"])}function xH(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(`
|
|
228
|
+
${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(`
|
|
224
229
|
`),o=n?`
|
|
225
230
|
\`${n}/\` is this Track's pre-repo workspace, carried over when the
|
|
226
231
|
Track was reshaped onto these repos. It is not part of any tagged repo and
|
|
227
232
|
cannot be shipped \u2014 move anything worth keeping into the repo it belongs to.
|
|
228
|
-
`:"";return`${
|
|
233
|
+
`:"";return`${qH}
|
|
229
234
|
|
|
230
235
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
231
236
|
git worktree per tagged repo, ALL on the same branch \`${t}\`:
|
|
@@ -237,20 +242,20 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
237
242
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
238
243
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
239
244
|
or preview an app, run it from that app's own repo directory.
|
|
240
|
-
${o}`}async function Ha(e,t,n,r={}){let o=
|
|
241
|
-
`).includes(s));o.length&&await
|
|
245
|
+
${o}`}async function Ha(e,t,n,r={}){let o=GH(t,n,r);for(let s of["CLAUDE.md","AGENTS.md"])await z.writeFile(V.join(e,s),o).catch(()=>{})}async function V_(e){let{stdout:t}=await lp("git",["-C",e,"rev-parse","--git-path","info/exclude"]);return V.resolve(e,t.trim())}async function KH(e,t){try{let n=await V_(e);await z.mkdir(V.dirname(n),{recursive:!0});let r=await z.readFile(n,"utf8").catch(()=>""),o=t.filter(s=>!r.split(`
|
|
246
|
+
`).includes(s));o.length&&await z.appendFile(n,(r.endsWith(`
|
|
242
247
|
`)||!r?"":`
|
|
243
248
|
`)+o.join(`
|
|
244
249
|
`)+`
|
|
245
|
-
`)}catch{}}async function
|
|
250
|
+
`)}catch{}}async function z_(e,t){try{let n=await V_(e),r=await z.readFile(n,"utf8"),o=r.split(`
|
|
246
251
|
`).filter(s=>s!==t);o.length!==r.split(`
|
|
247
|
-
`).length&&await
|
|
248
|
-
`))}catch{}}async function
|
|
252
|
+
`).length&&await z.writeFile(n,o.join(`
|
|
253
|
+
`))}catch{}}async function ry(){let e={refreshed:0,removed:0,codexGlobal:null};try{await z.mkdir(Jn,{recursive:!0}),await z.writeFile(V.join(Jn,"CLAUDE.md"),WH).catch(()=>{}),await z.writeFile(V.join(Jn,"AGENTS.md"),Lc).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"):Fc(o)&&o!==Rg&&(await z.writeFile(r,Rg).catch(()=>{}),e.codexGlobal="refreshed")}for(let r of[Uc,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&&Fc(c)&&c!==Lc&&(await z.writeFile(i,Lc).catch(()=>{}),e.refreshed++);let l=V.join(a,"CLAUDE.md"),u=await z.readFile(l,"utf8").catch(()=>null);u!==null&&Fc(u)&&(await z.rm(l,{force:!0}).catch(()=>{}),await z_(a,"/CLAUDE.md"),e.removed++)}}}catch{}return e}async function J_(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 bp({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=cp().slice(0,8),d=await HH(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:[fy(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})=>D_(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 oy({provider:e="claude",fresh:t=!1}={}){if(!VH.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=cp().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:[fy(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 sy(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 Sy(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 ay(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 kI(s),Yg(e),await sy(e,s,"track deleted"),n?await XI(e).catch(()=>{}):await YI(e).catch(()=>{}),await rA(),await J_(s),t&&await xI(e).catch(()=>{}),t?await jI(e).catch(()=>{}):await BI(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=pe(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(),Yg(e),Te(n,"archived",{boxes:n.boxes.length}),await ae(),n.kind==="setup"?(await Y_(e),Wt(n)):(Kc.add(e),YH(e).catch(()=>{}).finally(()=>Kc.delete(e)),{...await Wt(n),archiving:!0})}async function YH(e){try{await XH(e)}catch{}await Y_(e).catch(()=>{})}async function Y_(e){let t=await Q(),n=pe(t,e);if(!(!n||!n.archivedAt)){await sy(e,n,"track archived");for(let r of n.boxes)_r.has(r.state)||(r.state="stopped");await ae()}}async function XH(e){let t=await Q(),n=pe(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 Sv(n.leadBox,i.cwd,{timeoutMs:JH})),s="lead-cli"}catch{}if(r||(r=jv(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 iy(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 iy(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 kp(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 iy(n);if(o.length){let c=[...n.followupPending||[],r];for(let l of o)await DA(l.repo,l.number,c).catch(()=>{});return null}let s=[...n.followupPending||[],r],a=pe(await Q(),n.trackId);if(!a)return null;let i=s[0];return yc(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 kp(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 iy(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await PA(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];if(!await EA(r.repo,r.number).catch(()=>!0))return!1;try{return await qt(r.trackId,X_(a),{kind:"merge-notify"}),!0}catch{return await rs(r.repo,r.number,!0).catch(()=>{}),!1}}function X_(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 ${R_[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 ${R_[i.outcome]??i.outcome}`).join(`
|
|
249
254
|
`),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}
|
|
250
255
|
${s}
|
|
251
|
-
${a}`}async function ey(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||Ir.has(t.state)?!1:(await pe({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=Sg.get(r.name);if(o)return o;let s=IH(e,r,{freshSession:t,reason:n}).finally(()=>Sg.delete(r.name));return Sg.set(r.name,s),s}async function IH(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||Ir.has(t.state)||await be(t.name))return!1;let o=n===!0,s=o?ip():null,a=o?Oi(Rg(t.agent),s,t.name):up(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await mu(a);if(i===Ks)return Ae(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===Gs&&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=Go({agent:t.agent,agentCmd:a}),d=await $n(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})=>yI(e.id,{box:t.name,agent:t.agent,message:p}).catch(()=>{})}),t.state="live",o&&Ae(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}),Ae(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),Ae(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function kp(e,{freshSession:t=!1}={}){let n=await Z(),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,Gc.delete(e),Ae(r,"unarchived",{}),await Qc(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await ae(),Wt(r)}async function ty(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>$g)throw new Error(`name too long (max ${$g} chars)`);let r=await Z(),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,Ae(o,"renamed",{from:s,to:n}),await ae(),Wt(o)}async function ny(){let e=await Z(),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 ry(){let e=await Z(),t=new Set((await cn()).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})`):Ae(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!Ir.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await ae(),r}async function xp(e,{freshSession:t=!1}={}){let n=await Z(),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`);Sp(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(Ir.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await be(r.leadBox)){let i=await De(r.leadBox,{lines:Xe}).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 oy(e,t,n){let r=await ue(e);if(!r)throw new Error(`track "${e}" not found`);if(!t)throw new Error("unknown offer");let o=await q(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 xp(e),await C(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 fI(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return Id({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 sp(e,t){let n=await Z(),r=de(n,e);r&&(r.handoff={...r.handoff||{},...t},await ae())}function NI(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 ay(){let e=await Z(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=NI(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await be(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 Go({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},Ae(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 pe({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 pe({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 vp(e,t,n=!0,r=null){let o=ws.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=de(await Z(),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 be(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:bI(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return ws.set(e,l),await sp(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),$H(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 sp(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),pe({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),pe({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function $H(e,t,n,r,o){let s=async(F,B={})=>{e.step=F,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${F}`),await sp(e.trackId,{step:F,toBox:e.toBox}),await pe({flow:"lead-handoff",phase:F,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 Z(),i=de(a,e.trackId)?.boxes.find(F=>F.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:Og})}catch(F){e.checkpoint="reconstructed",await pe({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:F?.message||String(F)}).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=Go({agent:o,agentCmd:r}),f=await Ym(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let w=wx(f.launchCmd);await sr({cwd:n.cwd,sessionId:f.sessionId,sessionName:e.fromBox,agent:o,agentCmd:gs(o),launchEcho:w}).catch(F=>{if(F?.code!=="cli-not-running")throw F});let m=_m(await De(e.fromBox,{lines:Xe}).catch(()=>""),w);if(!Wo(o,m)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let F=up(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await Ym(e.fromBox,{agentCmd:F,agent:c,resumeSessionId:l,pinned:u});let B=await Z(),T=de(B,e.trackId);if(T){let _=T.boxes.find(U=>U.name===e.fromBox);_&&(_.state="live"),Ae(T,"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 Z(),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 x=y.boxes.find(F=>F.name===e.fromBox);if(!x)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let b=Date.now(),I=wo(x),$=I[I.length-1];$.endedAt=b,e.reason&&($.endReason=e.reason);let P=await p;I.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,cliVersion:P,startedAt:b,endedAt:null}),x.agent=o,x.sessionId=f.sessionId,x.pinned=f.pinned,x.cliVersion=P,x.state="live",x.role="lead",y.pendingHandoffHydrate={artifact:e.statusArtifact||go,at:b},Ae(y,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:I.length-1,singleBox:!0,forced:f.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,cliVersion:P,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await ae(),await s("done",{sessionName:e.fromBox}),await sp(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function ly(e){let t=ap.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await Z(),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=ws.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 ap.set(e,s),RH(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 RH(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await be(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:Og})}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 kn(u.dir,["push","origin",o],iy);let{stdout:p}=await kn(u.dir,["rev-parse","HEAD"]);d.baseSha=p.trim(),d.pushed=!0}catch(p){d.error=String(p?.message||p).split(`
|
|
252
|
-
`)[0]}s.push(d)}e.step="wip";let a=await
|
|
253
|
-
`)[0],advanced:null}}}function NH(e){if(typeof e!="string"||!CH.test(e)||e.includes("..")||e.includes("@{")||e.endsWith(".lock"))throw new Error("branch fails the safe-ref check")}async function dy(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&&NH(e.branch),tp.has(t))throw new Error("an adopt is already running for this Track");tp.set(t,{startedAt:Date.now()});try{return await PH({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{tp.delete(t)}}async function PH({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),x=await Z(),b=de(x,e);if(b&&!b.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);b&&(await dg(b).catch(()=>{}),await OI(b),x.tracks.splice(x.tracks.indexOf(b),1),await ae()),await V.mkdir(Uc,{recursive:!0});let I=G.join(Uc,e);await V.rm(I,{recursive:!0,force:!0}).catch(()=>{});let $=null,P=!1,F=[];try{let B=[];if(i)for(let se of Array.isArray(c)?c:[]){let ee=typeof se=="string"?se:se?.repo;if(!ee||!/^[^/\s]+\/[^/\s]+$/.test(ee))continue;let Oe=await Ps(ee)??await Vl(ee);await kn(Oe,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],iy),B.push({slug:ee,base:Oe,name:typeof se=="object"?se?.name??null:null})}if(i&&B.length===1){let{base:se,slug:ee}=B[0];await mI(se,I,i),F.push({base:se,dir:I}),await Kc(I);let Oe=await Qo(se);$={dir:I,base:se,branch:i,kind:"worktree",baseRef:Oe,repo:ee}}else if(i&&B.length>=2){await V.mkdir(I,{recursive:!0});let se=new Set([".bostrat"]),ee=[];for(let Oe of B){let z=Oe.name||G.basename(Oe.base);se.has(z)&&(z=Oe.slug.replace("/","-"));for(let O=2;se.has(z);O++)z=`${G.basename(Oe.base)}-${O}`;se.add(z);let we=G.join(I,z);await mI(Oe.base,we,i),F.push({base:Oe.base,dir:we});let Fe=await Qo(Oe.base);ee.push({name:z,dir:we,base:Oe.base,baseRef:Fe,repo:Oe.slug})}await Ha(I,ee,i),$={dir:I,kind:"multi",branch:i,repos:ee}}else P=!0,await V.mkdir(I,{recursive:!0}),await Th(I),await Kc(I),$={dir:I,base:null,branch:null,kind:"repo"};let T=[];for(let se of OH($,{wip:l,wips:u})){let ee=await MH(se.dir,e);T.push({name:se.name,repo:se.repo,applied:ee.applied,skipped:ee.skipped,...ee.advanced?{advanced:ee.advanced}:{}})}let _=T.some(se=>se.applied),U=T.length===1?T[0].skipped:T.filter(se=>se.skipped).map(se=>`${se.name??se.repo}: ${se.skipped}`).join("; ")||null,J=T.length===1?T[0].advanced??null:null;await eI({id:e,cwd:I}).catch(()=>({restored:!1}));let R=G.join(I,go),A=null;(await iu(I)).restored?A="checkpoint":typeof d=="string"&&d.trim()&&(await V.mkdir(G.dirname(R),{recursive:!0}),await V.writeFile(R,d),A="brief");let N=new Set(x.tracks.flatMap(se=>(se.boxes||[]).map(ee=>ee.name))),K=null;w&&!N.has(w)&&!await be(w).catch(()=>null)?K=w:(K=`${t.trim()} lead`,N.has(hu(K))&&(K=`${t.trim().slice(0,50).trim()} ${e} lead`));let Y=await $n(K,I,{agentCmd:y,agent:n});qo({cwd:Y.cwd,sessionName:Y.name,agent:n,agentCmd:y});let fe=Date.now(),Ie=sy(Y,"lead","live"),Ce=(Array.isArray(p)?p:[]).filter(se=>se&&typeof se=="object").map(se=>({...se})),X=Ce[Ce.length-1];X&&X.endedAt==null&&(X.endedAt=fe,X.endReason==null&&(X.endReason=o==="recover"?"node-offline":"node-transfer"));let et=await Go({agent:n,agentCmd:y});Ce.push({agent:n,sessionId:Y.sessionId??null,pinned:Y.pinned===!0,cliVersion:et,startedAt:fe,endedAt:null}),Ie.epochs=Ce,Ie.cliVersion=et;let Ee={id:e,name:t.trim(),cwd:Y.cwd,workspace:$,leadBox:Y.name,boxes:[Ie],events:[],createdAt:Number.isFinite(f)?f:fe,...A?{pendingHandoffHydrate:{artifact:go,at:fe}}:{}};return Ae(Ee,"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:_,...J?{wipAdvanced:J}:{},...$.kind==="multi"&&T.length?{wipRepos:T}:{},epoch:Ce.length-1,name:Ee.name,cwd:Ee.cwd,workspace:{base:$.base??null,branch:$.branch??null,kind:$.kind,...$.baseRef?{baseRef:$.baseRef}:{},...$.repo?{repo:$.repo}:{},...$.repos?{repos:$.repos.map(({name:se,base:ee,baseRef:Oe,repo:z})=>({name:se,base:ee,baseRef:Oe,repo:z}))}:{}}}),x.tracks.push(Ee),await ae(),{...await Wt(Ee),adopt:{wipApplied:_,wipSkipped:U,...J?{wipAdvanced:J}:{},...$.kind==="multi"?{wipRepos:T}:{},codeless:P,artifactSource:A}}}catch(B){for(let T of F)await Et(T.base,T.dir).catch(()=>{});throw await V.rm(I,{recursive:!0,force:!0}).catch(()=>{}),B}}async function PI(e,t,n){let r=await Z(),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 DI(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 BI(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function LH(e,t,n){if(!BI(n)||Bt.has(t)||!await be(n.box).catch(()=>null))return!1;try{await V.access(n.dir)}catch{return!1}let o=await be(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),LI(s,o).catch(a=>jI(s,a)),!0}async function py(){let e=await Z(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Bt.has(r))continue;if(await LH(n,r,o)){t=!0,Ae(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await pe({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=DI(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!Ir.has(i.state)&&(i.state="canceled"),Ae(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 pe({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await hI(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||Ir.has(r.state)||Bt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?G.join(La,`${n.id}-${r.subtaskId}`):null;await pe({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await hI(n,r.name,o)}}return t=await FH(e)||t,t&&await ae(),t}async function hI(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=G.join(n,o.name);await Et(o.base,s).then(()=>!0,()=>!1)||await Et(o.dir,s).catch(()=>{})}await V.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await be(e.leadBox).catch(()=>null);r?.cwd&&await Et(r.cwd,n).catch(()=>{})}async function FH(e){let t;try{t=await V.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(G.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=G.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 Et(i.base,G.join(s,i.name)).catch(()=>{});await V.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await be(a.leadBox).catch(()=>null):null;i?.cwd?await Et(i.cwd,s).catch(()=>{}):await V.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await pe({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function fy(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!la(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 Z(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await be(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 ua(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 ua(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=ip().slice(0,8),p={id:d,trackId:e,trackName:i.name,agent:t,task:la(n),skipPermissions:r,reason:bI(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${Ep(i.name)}-${d}`,base:null,repos:u,dir:G.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),UH(p,c,s).catch(f=>jI(p,f)),p}async function jI(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 hy(e).catch(()=>{}),await el(e).catch(()=>{}),await KH(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 my(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 UH(e,t,n){if(await Na(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await da(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await el(e)}else e.base=await da(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await Na(e,"spawn"),await V.mkdir(La,{recursive:!0}),e.repos){await V.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await Zo(a.leadDir,G.join(e.dir,a.name),e.branch,a.base);let s=jt((de(await Z(),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 HH(e,t);let r=e.trackName.slice(0,38).trim(),o=await $n(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await WH(e,o),await kI(e.trackId,o.name,O2(e.task,!!e.repos),{kind:"subtask"}),await LI(e,t)}async function LI(e,t){await Na(e,"working");let n=G.join(e.dir,np);if(await VH(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=G.join(e.dir,l.name);await Sh(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:fI({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 Ih(l.leadDir,l.base,e.branch,Ca);let d=await $h(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=>G.join(l.name,p))),i.push(...l.conflicts.map(p=>p.startsWith("patch did not apply cleanly")?`${l.name}: ${p}`:G.join(l.name,p))),d.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await Sh(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:fI({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 Ih(t.cwd,e.base,e.branch,Ca);let s=await $h(t.cwd,e.base,e.branch,Ca);e.applied=s.applied,e.conflicts=s.conflicts}await el(e);let r=await GH(e,t,n);await Na(e,"resume"),await kI(e.trackId,e.leadBox,M2(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await Na(e,"reap"),await hy(e),e.step="done";let o=Date.now();await PI(e.trackId,e.id,{...Tp(e),finishedAt:o}),await qH(e,o),e.finishedAt=o}async function HH(e,t){let n=G.join(e.dir,G.dirname(Hc));await V.mkdir(n,{recursive:!0});let r="";try{r=await Q0(t.cwd,t.sessionId)}catch{}if(!r)try{r=await V.readFile(G.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}
|
|
256
|
+
${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(`
|
|
257
|
+
`)[0]}s.push(d)}e.step="wip";let a=await dg(t,{force:!0}),i=await Q(),c=pe(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 Ap(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 Tp(e,{toNode:t,mode:n="transfer"}={}){if(!t||typeof t!="string")throw new Error("toNode required");let r=await Q(),o=pe(r,e);if(!o)throw new Error(`track "${e}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};Yg(e),await sy(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();Xu(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 yy(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await Tp(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 M_(e,t,n){try{await nc(e,t,n)}catch{await vn(e,["worktree","prune"]).catch(()=>{}),await nc(e,t,n)}return t}function tW(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 nW(e,t){try{await vn(e,["fetch","origin",ug(t)],hy);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(`
|
|
258
|
+
`)[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}
|
|
254
259
|
|
|
255
260
|
**Delegated task:** ${e.task}
|
|
256
261
|
|
|
@@ -261,15 +266,15 @@ ${a}`}async function ey(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<
|
|
|
261
266
|
## Conversation so far (lead \u2192 you)
|
|
262
267
|
|
|
263
268
|
${r}
|
|
264
|
-
`;await
|
|
269
|
+
`;await z.writeFile(V.join(e.dir,Hc),o)}async function fW(e,t){let n=await Q(),r=pe(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]||{},...Ep(e)},await ae())}async function mW(e,t=e.finishedAt){let n=await Q(),r=pe(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 hW(e){let t=await Q(),n=pe(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]||{},...Ep(e)},await ae()}async function wW(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 Sy(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 gW(e,t){let n=(t.startedAt||Date.now())+aW;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(iW)}}function $r(e){return new Promise(t=>setTimeout(t,e))}var lp,Jn,N_,Z2,Uc,Q2,up,Jc,ho,go,Lg,eH,rp,Hc,tH,nH,Dt,Eg,pe,Ig,Te,_g,$g,sH,_r,E_,cH,lH,uH,dH,I_,jc,gH,Ba,__,SH,AH,TH,EH,IH,Pa,Wc,OH,CH,NH,BH,ey,Dg,Zc,kt,xn,sp,qc,K_,WH,Lc,Fc,qH,Rg,VH,Kc,zH,JH,R_,Og,Bg,fy,ys,Wa,hy,vn,ip,wy,rW,np,aW,iW,La,Ca,Bt,cW,vs,Ep,el,Ip,zc,gr=q(()=>{xi();ht();Gm();Lv();ra();jr();vu();Gv();yt();Rn();Wu();zv();Ns();xu();uh();Ho();bh();xh();RS();Gi();Om();Su();Fm();pn();Yu();Pr();Zt();Wi();td();tA();Dh();SE();Nu();Mu();TE();Tw();ye();_w();Rd();qd();fc();ts();Rc();Ra();tg();Xd();cg();jn();Bd();o_();fg();p_();cu();w_();wg();Sg();Tg();lp=X2(Y2),Jn=process.env.BOSTRAT_STATE_DIR||V.join(process.env.HOME||"/tmp",".bostrat"),N_=V.join(Jn,"tracks.json"),Z2=V.join(Jn,"events.jsonl"),Uc=V.join(Jn,"workspaces"),Q2=process.env.BOSTRAT_DEFAULT_REPO||null,up={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"}},Jc=Object.keys(up);ho={at:0,value:null};go=".bostrat/handoff/latest.md",Lg=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;eH=/\(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\.\)$/;rp=".bostrat/handoff/subtask-result.md",Hc=".bostrat/handoff/lead-context.md",tH=(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 ${Hc} \u2014 read that first for context. Your scoped task:
|
|
265
270
|
|
|
266
271
|
${e}
|
|
267
272
|
|
|
268
|
-
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 ${
|
|
273
|
+
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 ${rp} (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 ${Hc} \u2014 read that first for context. Your scoped task:
|
|
269
274
|
|
|
270
275
|
${e}
|
|
271
276
|
|
|
272
|
-
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 ${
|
|
277
|
+
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 ${rp} 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.`,nH=(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;pe=(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),rH(e.id,r),oA(e,r),r};cx(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),sH=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);_r=new Set(["merged","conflict","canceled","failed","handed-off"]),E_=new Map;cH=5e3;lH={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};uH=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];dH=10*6e4;I_=400;jc=new Map;gH=1500,Ba=new Map;__="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.";SH=4e3,AH=3,TH=[300,350,400],EH=1e3,IH=12,Pa=new Map,Wc=new Map;OH=[250,400,500,600];CH=3e4,NH=[350,650,900];BH=12e4;ey=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}))});Dg=new Map,Zc=e=>Dg.get(e)||(Dt?pe(Dt,e)?.reshape:null)||null,kt=(e,t=409)=>Object.assign(new Error(e),{status:t});xn=e=>z.access(e).then(()=>!0,()=>!1),sp=(e,t,n)=>lp("git",["-C",e,"worktree","move","--end-of-options",t,n],{timeout:6e4});qc="# bostrat workspace",K_=`${qc}
|
|
273
278
|
|
|
274
279
|
TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
|
|
275
280
|
before substantive work \u2014 it is the shared cross-provider memory for this repo
|
|
@@ -632,13 +637,28 @@ arm it BEFORE your closing line:
|
|
|
632
637
|
|
|
633
638
|
bostrat-followup offer <plan.json>
|
|
634
639
|
|
|
635
|
-
Plan: { "
|
|
636
|
-
"
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
640
|
+
Plan: { "trigger": "pr.merged"|"ship.done"|"pr.opened"|"checks.green"|"now",
|
|
641
|
+
"why": "<one line, optional>", "items": [ { "label": "Migrate almanac 0016",
|
|
642
|
+
"prompt": "<the full instruction a fresh session could execute \u2014 the Track may
|
|
643
|
+
be revived to run it>", "standing": "never" }, { "label": "Deploy almanac to the
|
|
644
|
+
Pi", "prompt": "<\u2026>", "why": "<one line>" } ] } (1\u20135 items, in execution order;
|
|
645
|
+
the one-item { "label", "trigger", "prompt", "why" } shape still works). ITEMS
|
|
646
|
+
ARE THE UNIT: one item per step the operator might run, save or skip SEPARATELY
|
|
647
|
+
\u2014 a deploy and a migration are two items, a build and its smoke test are one.
|
|
648
|
+
Bostrat offers them as one card at that moment, each item its own row with its
|
|
649
|
+
own Run; "Run all in order" sends them as one numbered instruction. Every prompt
|
|
650
|
+
must stand alone and be order-safe (its directory, command, verification, and
|
|
651
|
+
its precondition when order matters \u2014 it may be tapped alone, first, last, or a
|
|
652
|
+
day later). Mark one-off steps "standing": "never" (numbered migrations,
|
|
653
|
+
backfills, one-time installs) \u2014 the card offers no "Always offer" for them.
|
|
654
|
+
Write recurring steps generically (no PR numbers, URLs, SHAs, tags \u2014 the card
|
|
655
|
+
supplies the moment) so the operator's "Always offer" can save them; put
|
|
656
|
+
Track-specific verification in its own one-off item. now renders the card at
|
|
657
|
+
the end of this turn (a structured "shall I do X next?" instead of a question \u2014
|
|
658
|
+
then stop). A second offer on the same trigger ADDS items; the same label
|
|
659
|
+
replaces the earlier item. Arm freely; it is cheap, Track-local, and the
|
|
660
|
+
operator answers later. Don't arm the generic "pick up whatever this unblocks"
|
|
661
|
+
\u2014 that nudge already exists.
|
|
642
662
|
bostrat-followup propose <plan.json> saves a STANDING follow-up (offered on
|
|
643
663
|
every matching moment, listed in Settings) \u2014 propose one ONLY when the operator
|
|
644
664
|
asks ("always deploy after merging", "remember this") or you have seen them ask
|
|
@@ -647,16 +667,16 @@ for the same follow-up at the same moment twice; run bostrat-followup ls first
|
|
|
647
667
|
specifics (no PR numbers, URLs, SHAs, tags \u2014 the card supplies them), and HOLD
|
|
648
668
|
HERE after proposing until the operator answers the card. Bostrat never runs a
|
|
649
669
|
follow-up on its own; the operator's tap is the gate.
|
|
650
|
-
`,
|
|
651
|
-
`)),
|
|
670
|
+
`,WH=K_,Lc=K_+Xx;Fc=e=>typeof e=="string"&&(e===qc||e.startsWith(`${qc}
|
|
671
|
+
`)),qH="# bostrat multi-repo workspace";Rg=Lc.replace(`${qc}
|
|
652
672
|
`,`${qc}
|
|
653
673
|
|
|
654
674
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
655
675
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
656
676
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
657
677
|
When working anywhere else, ignore everything below.
|
|
658
|
-
`);SH=new Set(["claude","codex","cursor"]);Gc=new Set,AH=e=>Gc.has(e);TH=9e4;pI={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};Sg=new Map;$g=120;sy=(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()});ws=new Map,Wa=e=>ws.get(e)||(Dt?de(Dt,e)?.handoff:null)||null;iy=6e4,kn=(e,t,n=15e3)=>cp("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),ap=new Map,cy=e=>ap.get(e)||null;CH=/^[A-Za-z0-9][A-Za-z0-9._/-]{0,200}$/;tp=new Map;DH=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,BH=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,La=G.join(Vn,"worktrees"),Ca=[Hc,np],Bt=new Map,jH=e=>Bt.get(e)||(Dt?Dt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,ks=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)},Tp=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=>PI(e.trackId,e.id,Tp(e));Ep=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",zc=class extends Error{}});ft();import $e from"node:path";import vt from"node:fs";import VG from"node:readline";import{fileURLToPath as zG}from"node:url";import Jt from"express";import JG from"node:http";import YG from"node:https";import Ol from"node:os";import{randomUUID as XG}from"node:crypto";import{WebSocketServer as ZG}from"ws";ft();import kP from"node-pty";async function O0(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 be(t)){bu(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=R0(n.cols,120,M0),o=R0(n.rows,32,C0),s;try{s=kP.spawn(ne,["-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&&om(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(om(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"&&AP(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{}SP(t)})}var xP=6,vP=250;async function SP(e,t=xP,n=vP){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 M0=20,C0=6;function AP(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=M0&&Number(t)>=C0}function R0(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function om(e,t){try{e.send(JSON.stringify(t))}catch{}}function bu(e,t){om(e,{type:"error",message:t})}function N0(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}}dn();xu();qm();gr();import{createRequire as QG}from"node:module";var zH=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function wy({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(zH)&&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}}me();lh();ft();import ZH from"node:fs/promises";import gy from"node:fs/promises";var JH=100;async function YH(e){let t;try{t=await gy.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/JH*1e3,ioBytes:await XH(e)}}async function XH(e){let t;try{t=await gy.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 UI(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 gy.readdir("/proc")}catch{return null}let r=new Map;await Promise.all(n.map(async l=>{if(!/^\d+$/.test(l))return;let u=await YH(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 QH=Number(process.env.BOSTRAT_JOB_CHECK_MS)||15*6e4,eW=Number(process.env.BOSTRAT_JOB_STALL_CHECKS)||3,tW=Number(process.env.BOSTRAT_JOB_CPU_RATE_MIN)||25,nW=Number(process.env.BOSTRAT_JOB_IO_RATE_MIN)||64*1024,rW=5*6e4,WI={readOpenJobs:lS,statFile:async e=>{try{let t=await ZH.stat(e);return{size:t.size,mtimeMs:Math.round(t.mtimeMs)}}catch{return null}},updatedAt:e=>Zr(e.agent).updatedAt(e),panePids:E0,treeStats:UI,appendEvent:C},xn=new Map,HI=new Map,yy=new Map,by=(e,t,n)=>`${e}|${t}|${n}`;function qI(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 KI(e,t,n){let r=null;try{r=await n.updatedAt(e)}catch{r=null}let o=HI.get(e.name);if(o&&o.mtime===r&&t-o.at<rW)return o.jobs;let s=[];try{s=await n.readOpenJobs(e)}catch{s=[]}return HI.set(e.name,{at:t,mtime:r,jobs:s}),s}async function oW(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 sW(e,t){return e?.outputFile?await t.statFile(e.outputFile):null}function aW(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>=tW||t.ioBytes!=null&&e.ioBytes!=null&&(t.ioBytes-e.ioBytes)/n>=nW}function GI(e,t,n,r){let o=[];for(let s of n){let a=by(e.id,t.name,s.id),i=xn.get(a);i?i.job=s:xn.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 VI(e,{now:t=Date.now(),deps:n=WI}={}){let r=new Map,o=new Set;for(let{track:s,box:a}of qI(e)){let i=await KI(a,t,n);for(let l of i)o.add(by(s.id,a.name,l.id));let c=GI(s,a,i,t).filter(l=>!xn.get(l).stalled).map(l=>xn.get(l).job);c.length&&r.set(s.id,[...r.get(s.id)??[],...c])}for(let s of[...xn.keys()])o.has(s)||xn.delete(s);return r}async function iW(e,{now:t=Date.now(),deps:n=WI}={}){let r={checked:0,progressing:0,quiet:0,stalled:[]},o=qI(e),s=new Set(o.map(({box:a})=>a.name));for(let[a,i]of[...xn])s.has(i.boxName)||xn.delete(a);for(let{track:a,box:i}of o){let c=await KI(i,t,n),l=new Set(c.map(w=>by(a.id,i.name,w.id)));for(let[w,m]of[...xn])m.boxName===i.name&&!l.has(w)&&xn.delete(w);if(!c.length){yy.delete(i.name);continue}let u=GI(a,i,c,t),d=await oW(i,t,n),p=yy.get(i.name);yy.set(i.name,d);let f=aW(p,d);for(let w of u){let m=xn.get(w);r.checked++;let g=await sW(m.job,n),y=!!g&&!!m.file&&(g.size>m.file.size||g.mtimeMs>m.file.mtimeMs),x=!!g&&!m.file;if(m.file=g??m.file,f||y||x){m.quiet=0,m.lastProgressAt=t,m.stalled=!1,r.progressing++;continue}if(m.stalled||(m.quiet++,r.quiet++,m.quiet<eW))continue;m.stalled=!0,m.stalledAt=t,r.stalled.push(w);let I=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:I,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(I/6e4)}m over ${m.quiet} check(s) \u2014 released; the node may sleep`)}}return r}function zI({listTracks:e,intervalMs:t=QH}={}){let n=!1,o=setInterval(async()=>{if(!n){n=!0;try{await iW(await e())}catch(s){console.error(`[job-watch] sweep: ${s?.message||s}`)}finally{n=!1}}},t);return o.unref?.(),o}me();Rc();Dn();Cd();Zt();var Ip=60*6e4,cW=1440*6e4,lW=new Set(["merged","closed","lost"]),tl=(e,t,n)=>Number.isFinite(Number(e))&&n-Number(e)<=t;function uW({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)<=qw&&s.push("ship"),r>0&&s.push("create-net");for(let a of n||[]){if(!a||lW.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,Ip,o)&&s.push(`ci-fix#${l}`),a.reconcile&&tl(a.reconcile.delegatedAt,Ip,o)&&s.push(`reconcile#${l}`),a.ciFixAwait&&tl(a.ciFixAwait.since,Ip,o)&&s.push(`ci-fix-await#${l}`),a.reconcileAwait&&tl(a.reconcileAwait.since,Ip,o)&&s.push(`reconcile-await#${l}`);let u=a.autoMerge?.armedAt??a.armedAt??null;a.trackId===e&&(a.autoMerge||a.armedAt)&&tl(u,cW,o)&&s.push(`auto-merge#${l}`)}return s}var $p=new Map;Je.on("event",e=>{e?.trackId&&$p.delete(e.trackId)});async function dW(e){if($p.has(e.id))return $p.get(e.id);let t=await q(e).catch(()=>[]),n=_a(t,-1/0);return $p.set(e.id,n),n}async function JI(e,{now:t=Date.now()}={}){let n=new Map,r=await mn().catch(()=>[]);for(let o of e||[]){if(!o||Le(o))continue;let s=uW({trackId:o.id,shipPending:await dW(o),links:r,observers:jT(o.id),now:t});s.length&&n.set(o.id,s)}return n}hg();import i$ from"node:fs";import bo from"node:fs/promises";import $W from"node:os";import ht from"node:path";import{spawn as RW}from"node:child_process";import{createRequire as OW}from"node:module";me();td();import qa from"node:fs";import rl from"node:fs/promises";import mW from"node:http";import Kt from"node:path";import Op from"node:os";import{createRequire as hW}from"node:module";import xy from"ws";import pW from"node:path";import fW from"node:os";var nl=process.env.BOSTRAT_STATE_DIR||pW.join(process.env.HOME||fW.tmpdir(),".bostrat");function ky(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 YI(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=ky(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function XI(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}Mf();var vn=(()=>{try{return hW(import.meta.url)("../package.json").version||null}catch{return null}})(),Sy=process.env.BOSTRAT_STATE_DIR||Kt.join(process.env.HOME||Op.tmpdir(),".bostrat"),o$=Kt.join(Sy,"plane-link","removed.json");function ZI(e){return typeof e!="string"||e.length<8?"\u2022\u2022\u2022\u2022":`${e.slice(0,6)}\u2026${e.slice(-4)}`}function wW(e=o$){try{let t=JSON.parse(qa.readFileSync(e,"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function xs({env:e=process.env,pkgDir:t=Kt.resolve(Kt.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(Kt.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 Rp(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 gW({home:e=process.env.HOME||Op.homedir(),env:t=process.env}={}){let n=r=>{try{return qa.statSync(r),!0}catch{return!1}};return{claude:n(Kt.join(e,".claude",".credentials.json")),codex:n(Kt.join(t.CODEX_HOME||Kt.join(e,".codex"),"auth.json"))}}var dt=1,QI=1e3,e$=3e4,yW=6e4,bW=1500,kW=500,yo=100,xW=2e3,vW=25e3,SW=6e4,s$=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},AW=s$(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),TW=3e4,t$=10*6e4,EW=400,n$=250,_W=s$(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),vy=4,IW=6e4,r$=6e4;function a$({url:e,token:t,nodeId:n=Op.hostname(),name:r=Op.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,deliverySample:c=null,onHashKey:l=null,onViewBase:u=null,metricsMs:d=IW,onUpdateAvailable:p=null,onUpdatePolicy:f=null,onHelloRefused:w=null,updatePolicy:m=null,installKind:g=xs(),selfUrl:y=`http://127.0.0.1:${process.env.PORT||3362}`,apiToken:x=null,removedMarkerFile:b=o$,onRemoved:I=null,onDegraded:$=null,onRecovered:P=null,onConnected:F=null,ingestMode:B=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:T=AW,unackedRetryMs:_=SW,pingMs:U=vW,maxInflightEvents:J=EW,httpTimeoutMs:R=_W,stallBackoffMinMs:A=TW,fetchImpl:M=globalThis.fetch,log:N=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=XI(e);let K=y.replace(/^http/,"ws"),Y=new Set,fe=new Map,Ie=new Map,Ce=Kt.join(Sy,"plane-link","cursor.json"),X=Kt.join(Sy,"plane-link","dead-letter.ndjson"),et=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),Ee={},se={},ee=new Map,Oe=null,z=null,we=0,Fe=0,O=0,ce=Promise.resolve(),ve="ws",wt=0,Q=!1,ge=0,pt=!1,rt=yo,k=new Set,h=[],v=null,D=null,re=!1,ke=!1,Pe=QI,St=null,tt=null,At=null,Zn=null,Qn=null,bf=!1,jl=0,ai=!1,Mr=wW(b);if(Mr)if(Mr.tokenPreview===ZI(t))ai=!0,Of({reason:Mr.reason??"removed",at:Mr.at??null,nodeId:Mr.nodeId??n}),N.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 ${b} to force a dial.`);else try{qa.unlinkSync(b),N.log("[plane-link] stale removed-marker discarded \u2014 the node token changed")}catch{}function kf({reason:j="removed",at:H=Date.now(),via:S="ctl"}={}){if(!ai){ai=!0,Of({reason:j,at:H,nodeId:n,via:S});try{qa.mkdirSync(Kt.dirname(b),{recursive:!0}),qa.writeFileSync(b,JSON.stringify({at:H,reason:j,via:S,nodeId:n,tokenPreview:ZI(t)},null,2))}catch(L){N.error(`[plane-link] could not write ${b}: ${L?.message||L}`)}N.error(`[plane-link] the plane REMOVED this node (${j}, via ${S}) \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{I?.({reason:j,at:H,via:S})}catch{}sk({bye:!1})}}async function YM(){try{Ee=JSON.parse(await rl.readFile(Ce,"utf8"))}catch{Ee={}}}function XM(){bf=!0,!At&&(At=setTimeout(async()=>{if(At=null,!!bf){bf=!1;try{await rl.mkdir(Kt.dirname(Ce),{recursive:!0}),await rl.writeFile(Ce,JSON.stringify(Ee))}catch(j){N.error("[plane-link] cursor flush failed:",j?.message||j)}}},xW),At.unref?.())}function ct(j){return!D||D.readyState!==xy.OPEN?!1:(D.send(JSON.stringify(j)),!0)}function ii(j){let H=Date.now();for(let{trackId:S,event:L}of j){let te=ee.get(L.id);ee.set(L.id,{trackId:S,seq:L.seq,event:L,sentAt:H,firstSentAt:te?.firstSentAt??H,retryAt:H+_,tries:(te?.tries??0)+1,nacks:te?.nacks??0})}return ve==="http"?(tC(j),!0):ct({v:dt,ch:"events",type:"append",events:j})}function ZM(j){let H=null;for(let S of ee.values())S.trackId===j&&(H==null||S.seq<H)&&(H=S.seq);return H}function xf(j){let H=se[j];if(H==null)return;let S=ZM(j),L=S==null?H:Math.min(H,S-1);L>(Ee[j]??0)&&(Ee[j]=L,XM())}function ci(){if(Oe=Date.now(),z!=null){let j=Date.now()-z;N.log(`[plane-link] spine sync recovered after ${Math.round(j/1e3)}s degraded (${we} stall redial(s))`);let H=z;z=null,we=0,Fe=0,Q=!1,ge=0;try{P?.({since:H,downMs:j})}catch(S){N.error(`[plane-link] onRecovered failed: ${S?.message||S}`)}}}function vf(j){if(!Array.isArray(j)||!j.length)return;let H=new Set;for(let S of j){let L=ee.get(S);L&&(ee.delete(S),(se[L.trackId]??0)<L.seq&&(se[L.trackId]=L.seq),H.add(L.trackId))}for(let S of H)xf(S);ci()}function Xb(j,H){O++;let S={at:Date.now(),trackId:j.trackId,seq:j.seq,id:j.event?.id??null,type:j.event?.type??null,reason:H,event:j.event??null};N.error(`[plane-link] DEAD-LETTER ${j.trackId} seq ${j.seq} (${S.type??"?"}): plane rejected it permanently (${H}) \u2014 kept in ${X}`),ce=ce.then(async()=>{await rl.mkdir(Kt.dirname(X),{recursive:!0}),await rl.appendFile(X,JSON.stringify(S)+`
|
|
659
|
-
`)}).catch(L=>N.error(`[plane-link] dead-letter write failed: ${L?.message||L}`))}function Zb(j,H,S,L=null){if(!Array.isArray(j)||!j.length)return;let te=new Set,xe=Date.now();for(let Te of j){let Ke=ee.get(Te);Ke&&(S?(ee.delete(Te),Xb(Ke,H),(se[Ke.trackId]??0)<Ke.seq&&(se[Ke.trackId]=Ke.seq),te.add(Ke.trackId)):(Ke.nacks++,Ke.retryAt=xe+Math.max(1e3,Number(L)||_)))}for(let Te of te)xf(Te);ci()}function Qb(j=Date.now()){let H=1/0;for(let S of ee.values())S.firstSentAt<H&&(H=S.firstSentAt);return H===1/0?0:j-H}function ek(){if(!re||!ee.size)return;let j=Date.now(),H=Qb(j);if(H<T||Oe!=null&&j-Oe<T||ve==="http"&&Q||Fe>j)return;we++;let S=Math.min(A*2**(we-1),t$);if(z==null){z=j-H,N.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(H/1e3)}s with ${ee.size} event(s) in flight (${ve} door) \u2014 the plane is not answering appends`);try{$?.({since:z,unacked:ee.size,oldestAgeMs:H,mode:ve})}catch(L){N.error(`[plane-link] onDegraded failed: ${L?.message||L}`)}}if(ve==="http"){Q=!0,ge=j+S,N.error(`[plane-link] ingest circuit OPEN (stall #${we}); probing /v1/ingest/health in ${Math.round(S/1e3)}s`);return}Fe=j+S,N.error(`[plane-link] closing the link and redialing in ${Math.round(S/1e3)}s (stall redial #${we})`);try{D?.terminate()}catch{}}function Sf(){if(!re)return;let j=Date.now();if(ve==="http"&&Q)return;let H=[...ee.values()].filter(L=>L.event&&L.retryAt<=j&&!(ve==="http"&&k.has(L.trackId)));if(!H.length)return;H.sort((L,te)=>L.trackId===te.trackId?L.seq-te.seq:L.trackId<te.trackId?-1:1),N.log(`[plane-link] re-sending ${H.length} unacked event(s) the plane never confirmed`);let S=ve==="http"?rt:yo;if(ve==="http"){let L=new Map;for(let te of H)(L.get(te.trackId)??L.set(te.trackId,[]).get(te.trackId)).push(te);for(let[,te]of L){if(wt>=vy)return;ii(te.slice(0,S).map(xe=>({trackId:xe.trackId,event:xe.event})))}return}for(let L=0;L<H.length;L+=S){let te=H.slice(L,L+S).map(xe=>({trackId:xe.trackId,event:xe.event}));if(!ii(te))return}}async function QM(j=null){for(;;){if(!re)return!1;let H=ee.size<J,S=ve!=="http"||wt<vy&&!Q&&(!j||!k.has(j));if(H&&S)return!0;await new Promise(L=>setTimeout(L,100))}}function Af(){if(v=null,!re||!h.length)return;let j=h;if(h=[],ve!=="http"){for(let S=0;S<j.length;S+=yo)ii(j.slice(S,S+yo));return}let H=new Map;for(let S of j)(H.get(S.trackId)??H.set(S.trackId,[]).get(S.trackId)).push(S);for(let[S,L]of H){if(k.has(S)||wt>=vy||Q){h.push(...L);continue}for(let te=0;te<L.length;te+=rt)ii(L.slice(te,te+rt))}h.length&&!v&&(v=setTimeout(Af,n$),v.unref?.())}let eC=j=>{let H=new Map;for(let{trackId:S,event:L}of j)(H.get(S)??H.set(S,[]).get(S)).push(L);return[...H].map(([S,L])=>({trackId:S,events:L}))},Ro=(j,H,S,{progress:L})=>{let te=Date.now();for(let{event:xe}of j){let Te=ee.get(xe.id);Te&&(Te.nacks++,Te.retryAt=te+Math.max(1e3,S||_))}L&&ci()};async function tC(j){let H=new Set(j.map(S=>S.trackId));for(let S of H)k.add(S);wt++;try{let S=await M(`${et}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:eC(j)}),signal:AbortSignal.timeout(R)}),L=(Number(S.headers.get("retry-after"))||0)*1e3;if(S.status===200){let te=await S.json();rt=yo;for(let xe of te.results??[]){vf(xe.acked??[]);let Te=(Ke,Rt)=>{let Yt=new Map;for(let ot of Ke??[])(Yt.get(ot.reason)??Yt.set(ot.reason,[]).get(ot.reason)).push(ot.id);for(let[ot,ak]of Yt)Zb(ak,ot,Rt,te.retryAfterMs??null)};Te(xe.rejected,!0),Te(xe.deferred,!1)}vf(te.discarded??[]),ci();return}if(S.status===429){Ro(j,"overloaded",L||5e3,{progress:!0});return}if(S.status===413&&j.length>1){rt=Math.max(1,Math.floor(j.length/2)),N.error(`[plane-link] ingest request too large (${j.length} events) \u2014 halving batches to ${rt}`),Ro(j,"too_large",1e3,{progress:!0});return}if(S.status===413){let te=ee.get(j[0].event.id);te&&(ee.delete(te.event.id),Xb(te,"too_large"),(se[te.trackId]??0)<te.seq&&(se[te.trackId]=te.seq),xf(te.trackId)),ci();return}if(S.status===410){kf({reason:"removed",via:"410"}),Ro(j,"http_410",6e4,{progress:!1});return}if(S.status===401||S.status===403){let{tripped:te,holdMs:xe}=Ds();if(te&&N.error(`[plane-link] holding every plane call for ${Math.round(xe/6e4)} min \u2014 the ingest door refused this node's token three times in a row`),Date.now()-jl>=r$){jl=Date.now();let Te="";try{Te=(await S.json())?.error??""}catch{}N.error(`[plane-link] plane refused ingest (HTTP ${S.status}${Te?`: ${Te}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}Ro(j,`http_${S.status}`,Math.max(6e4,hi()),{progress:!1});return}if(S.status===400){N.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${j.length} event(s) for retry; this is a bug, not data`),Ro(j,"http_400",6e4,{progress:!1});return}Ro(j,`http_${S.status}`,L||5e3,{progress:!1})}catch(S){let L=S?.name==="TimeoutError"||S?.name==="AbortError";L&&j.length>1&&(rt=Math.max(1,Math.floor(j.length/2)),N.error(`[plane-link] ingest request of ${j.length} event(s) outlived its ${R} ms deadline \u2014 halving batches to ${rt}`)),Ro(j,L?"timeout":"network",5e3,{progress:!1})}finally{wt--;for(let S of H)k.delete(S)}}async function nC(){if(ve!=="http"||!Q||!re||pt)return;let j=Date.now();if(!(ge>j||Bs(j))){pt=!0;try{let H=await M(`${et}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(R)});if(!H.ok)throw new Error(`HTTP ${H.status}`);N.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),Q=!1,ge=0,Fe=Date.now()+R+_+1e3;for(let S of ee.values())S.retryAt=Math.min(S.retryAt,Date.now());Sf()}catch(H){we++;let S=Math.min(A*2**(we-1),t$);ge=Date.now()+S,N.error(`[plane-link] ingest door still not answering (${H?.message||H}); next probe in ${Math.round(S/1e3)}s`)}finally{pt=!1}}}function tk(){let j=Date.now();return{mode:ve,unacked:ee.size,oldestUnackedAgeMs:Qb(j),lastProgressAt:Oe,degradedSince:z,stallRedials:we,deadLettered:O,http:ve==="http"?{inFlight:wt,circuitOpen:Q,nextProbeInMs:Q?Math.max(0,ge-j):0,batchCap:rt}:null}}async function nk(){if(!i)return;let j;try{j=await i()}catch(S){N.error("[plane-link] metrics sample failed:",S?.message||S);return}let H=null;try{H=c?.()??null}catch{}j&&ct({v:dt,ch:"ctl",type:"metrics",metrics:{...j,planeLink:tk(),...H?{delivery:H}:{}}})}function rC(){i&&(Qn&&clearInterval(Qn),nk(),Qn=setInterval(nk,d),Qn.unref?.())}async function oC(){let j;try{j=await o()}catch(H){N.error("[plane-link] backfill listTracks failed:",H?.message||H);return}ct({v:dt,ch:"ctl",type:"tracks",ids:j.filter(H=>!H.transferredTo).map(H=>H.id).filter(H=>typeof H=="string")});for(let H of j){if(!re)return;let S;try{S=await q(H)}catch{continue}let L=Ee[H.id]??0,te=S.filter(Te=>Te.seq>L&&typeof Te.id=="string"),xe=ve==="http"?rt:yo;for(let Te=0;Te<te.length;Te+=xe){if(!await QM(H.id))return;let Ke=te.slice(Te,Te+Math.min(xe,ve==="http"?rt:yo)).map(Rt=>({trackId:H.id,event:Rt}));if(!ii(Ke))return}}}function rk(j){!re||!j?.trackId||typeof j.id!="string"||(h.push({trackId:j.trackId,event:j}),v||(v=setTimeout(Af,n$),v.unref?.()))}let li=new Map;async function sC(j){let{reqId:H,method:S,path:L,headers:te={},body:xe}=j,Te=Object.fromEntries(Object.entries(te).filter(([,Rt])=>Rt!=null)),Ke=new AbortController;li.set(H,Ke);try{if(j.host){await aC(j,Te,Ke);return}x&&(Te["x-bostrat-token"]=x);let Rt=await fetch(y+L,{method:S,headers:Te,body:xe?Buffer.from(xe,"base64"):void 0,signal:Ke.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"),ct({v:dt,ch:"control",type:"http_head",reqId:H,status:Rt.status,headers:Yt}),Rt.body){for await(let ot of Rt.body)if(!ct({v:dt,ch:"control",type:"http_data",reqId:H,data:Buffer.from(ot).toString("base64")}))break}ct({v:dt,ch:"control",type:"http_end",reqId:H,error:null})}finally{li.delete(H)}}function aC(j,H,S){let{reqId:L,method:te,path:xe,body:Te}=j,Ke=new URL(y);return new Promise(Rt=>{let Yt=mW.request({host:Ke.hostname,port:Ke.port,method:te,path:xe,headers:{...H,host:j.host},signal:S.signal},ot=>{let{connection:ak,"keep-alive":gV,"transfer-encoding":yV,upgrade:bV,...iC}=ot.headers;ct({v:dt,ch:"control",type:"http_head",reqId:L,status:ot.statusCode||502,headers:iC}),ot.on("data",cC=>{ct({v:dt,ch:"control",type:"http_data",reqId:L,data:Buffer.from(cC).toString("base64")})||ot.destroy()}),ot.on("end",()=>{ct({v:dt,ch:"control",type:"http_end",reqId:L,error:null}),Rt()}),ot.on("error",()=>{ct({v:dt,ch:"control",type:"http_end",reqId:L,error:"view upstream error"}),Rt()})});Yt.on("error",ot=>{ct({v:dt,ch:"control",type:"http_end",reqId:L,error:ot?.message||"view request failed"}),Rt()}),Te&&Yt.write(Buffer.from(Te,"base64")),Yt.end()})}function ok(){if(ke||ai)return;D=new xy(e,{headers:{authorization:`Bearer ${t}`}}),D.on("open",async()=>{let H=null;try{H=await s?.()||null}catch{H=null}ct({v:dt,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:vn,installKind:g,...typeof m=="function"?{updatePolicy:m()}:m?{updatePolicy:m}:{},providers:gW(),...H?{agents:H}:{}})}),D.on("message",H=>{let S;try{S=JSON.parse(H)}catch{return}if(S.ch==="ctl"&&S.type==="hello_ok"){re=!0,Pe=QI,St=null,mk(),N.log(`[plane-link] connected to plane as ${n}`);try{F?.()}catch{}if(typeof S.hashKey=="string"&&S.hashKey)try{l?.(S.hashKey)}catch{}try{u?.(typeof S.viewBase=="string"&&S.viewBase?S.viewBase:null)}catch{}S.minNodeVersion&&vn&&ol(vn,S.minNodeVersion)&&N.error(`[plane-link] this node is bostrat-node ${vn}; the plane asks for \u2265${S.minNodeVersion} \u2014 update with: ${Rp(g)}`);try{f?.(typeof S.updatePolicy=="string"?S.updatePolicy:null)}catch{}let L=typeof S.latestNodeVersion=="string"?S.latestNodeVersion:null;if(L&&vn&&ol(vn,L)&&!Y.has(L)){Y.add(L),N.log(`[plane-link] bostrat-node ${L} is available (this node runs ${vn}) \u2014 ${Rp(g)}`);try{p?.({current:vn,latest:L,min:S.minNodeVersion??null,installKind:g,required:!!(S.minNodeVersion&&ol(vn,S.minNodeVersion))})}catch(xe){N.error(`[plane-link] update notice failed: ${xe?.message||xe}`)}}let te=S.ingest==="http";ve=B==="ws"?"ws":B==="http"||te?"http":"ws",B==="http"&&!te&&N.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),ve==="http"&&N.log("[plane-link] spine rides the HTTPS ingest door"),Q=!1,ge=0,rt=yo,Zn&&clearInterval(Zn),Zn=setInterval(()=>{ct({v:dt,ch:"ctl",type:"ping"}),ek(),nC().catch(()=>{}),Sf(),h.length&&!v&&Af()},U),Zn.unref?.(),rC(),oC().catch(xe=>N.error("[plane-link] backfill failed:",xe?.message||xe));return}if(S.ch==="ctl"&&S.type==="hello_refused"){let L=typeof S.minNodeVersion=="string"?S.minNodeVersion:null,te=typeof S.latestNodeVersion=="string"?S.latestNodeVersion:null;N.error(`[plane-link] plane REFUSED this node: bostrat-node ${vn??"?"} is below its minimum ${L??"?"} \u2014 ${S.fix||Rp(g)}`),Pe=e$,St=0;try{w?.({current:vn,min:L,latest:te,fix:S.fix||Rp(g),installKind:g})}catch(xe){N.error(`[plane-link] refusal handler failed: ${xe?.message||xe}`)}return}if(S.ch==="ctl"&&S.type==="removed"){ct({v:dt,ch:"ctl",type:"removed_ack",nodeId:n}),kf({reason:typeof S.reason=="string"?S.reason:"removed",at:Number(S.at)||Date.now(),via:"ctl"});return}if(S.ch==="ctl"&&S.type==="update_policy"){try{f?.(typeof S.mode=="string"?S.mode:null)}catch{}return}if(S.ch==="ctl"&&S.type==="usage_caps"){ed(S.caps??{},{source:"plane",updatedAt:S.updatedAt??Date.now()}).then(L=>N.log(`[plane-link] spend caps ${Object.keys(L.caps).length?`updated (${Object.keys(L.caps).join(", ")})`:"cleared"} from plane`)).catch(L=>N.error("[plane-link] spend-caps push rejected:",L?.message||L));return}if(S.ch==="ctl"&&S.type==="transferred"&&Array.isArray(S.tracks)){Promise.resolve().then(()=>a?.(S.tracks)).catch(L=>N.error("[plane-link] transfer notice failed:",L?.message||L));return}if(S.ch==="events"&&S.type==="ack"&&Array.isArray(S.ids)){vf(S.ids);return}if(S.ch==="events"&&S.type==="nack"&&Array.isArray(S.ids)){Zb(S.ids,S.reason,S.permanent===!0,S.retryAfterMs??null);return}if(S.ch==="control"&&S.type==="http_open"){sC(S).catch(L=>ct({v:dt,ch:"control",type:"http_end",reqId:S.reqId,error:L?.message||"tunnel error"}));return}if(S.ch==="control"&&S.type==="http_cancel"){li.get(S.reqId)?.abort();return}if(S.ch==="attach"&&S.type==="open"){let L=new xy(K+S.path,Array.isArray(S.protocols)?S.protocols:[],S.host?{headers:{host:S.host}}:void 0);fe.set(S.streamId,L),Ie.set(S.streamId,[]),L.on("open",()=>{let xe=Ie.get(S.streamId)||[];Ie.delete(S.streamId);for(let{buf:Te,bin:Ke}of xe)L.send(Te,{binary:Ke})}),L.on("message",(xe,Te)=>{let Ke=Te?xe:Buffer.from(String(xe));ct({v:dt,ch:"attach",type:"data",streamId:S.streamId,data:Ke.toString("base64"),...Te?{bin:!0}:{}})});let te=()=>{Ie.delete(S.streamId),fe.delete(S.streamId)&&ct({v:dt,ch:"attach",type:"close",streamId:S.streamId})};L.on("close",te),L.on("error",te);return}if(S.ch==="attach"&&S.type==="data"){let L=fe.get(S.streamId);if(!L)return;let te=Buffer.from(S.data,"base64"),xe=S.bin===!0,Te=Ie.get(S.streamId);Te?Te.push({buf:te,bin:xe}):L.send(te,{binary:xe});return}if(S.ch==="attach"&&S.type==="close"){let L=fe.get(S.streamId);fe.delete(S.streamId),Ie.delete(S.streamId),L?.close();return}});let j=()=>{re&&N.log("[plane-link] disconnected from plane"),re=!1,Zn&&(clearInterval(Zn),Zn=null),Qn&&(clearInterval(Qn),Qn=null),ee.clear(),se={},h=[],v&&(clearTimeout(v),v=null),k.clear();for(let L of fe.values())L.close();fe.clear(),Ie.clear();for(let L of li.values())L.abort();if(li.clear(),ke||tt)return;St==null&&(St=Date.now());let H=Date.now()-St<yW,S=Math.max(H?bW+Math.random()*kW:Pe,Fe-Date.now(),hi());tt=setTimeout(()=>{tt=null,ok()},S),tt.unref?.(),H||(Pe=Math.min(Pe*2,e$))};D.on("close",j),D.on("error",H=>{let S=/Unexpected server response: (\d+)/.exec(H?.message||"")?.[1];if(S==="410"){kf({reason:"removed",via:"410"});return}if(S==="401"||S==="403"){let{tripped:L,holdMs:te}=Ds();L?N.error(`[plane-link] holding every plane call for ${Math.round(te/6e4)} min \u2014 three credential refusals in a row (HTTP ${S}) 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(te/6e4)} min.`):Date.now()-jl>=r$&&(jl=Date.now(),N.error(`[plane-link] plane refused this node's credentials (HTTP ${S}) \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)`))}j()})}Je.on("event",rk),YM().then(ok);function sk({bye:j=!0}={}){ke=!0,Je.off("event",rk),tt&&clearTimeout(tt),At&&clearTimeout(At),Zn&&clearInterval(Zn),Qn&&clearInterval(Qn),v&&clearTimeout(v),j&&ct({v:dt,ch:"ctl",type:"bye",reason:"shutdown"}),D?.close()}return{stop:()=>sk(),status:()=>({connected:re,nodeId:n,cursor:{...Ee},inflight:ee.size,...tk(),auth:hk(),dormant:ai,removed:Kl()}),_tick:()=>{ek(),Sf()}}}vu();var Mp=(()=>{try{return OW(import.meta.url)("../package.json").version||null}catch{return null}})(),MW=process.env.BOSTRAT_STATE_DIR||ht.join(process.env.HOME||$W.tmpdir(),".bostrat"),c$=ht.join(MW,"self-update"),Cp=ht.join(c$,"status.json"),CW=1440*6e4,NW=6e4,sl=new Set(["npm-global","git-checkout"]);function l$({kind:e=xs(),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"||PW(t)?"auto":"prompt":"off"}function PW(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var Gt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function DW({kind:e,to:t,from:n=Mp,statusFile:r=Cp,logFile:o=ht.join(c$,"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||ht.resolve(ht.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 ${Gt(g)} ${y}`,p=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${Gt(i)}`,f=`
|
|
678
|
+
`);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)+`
|
|
679
|
+
`)}).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=`
|
|
660
680
|
health() {
|
|
661
681
|
for i in $(seq 1 45); do
|
|
662
682
|
sleep 1
|
|
@@ -667,22 +687,22 @@ health() {
|
|
|
667
687
|
# bostrat-node self-update one-shot \u2014 written by agent/src/self-update.js.
|
|
668
688
|
# ${n??"?"} \u2192 ${t} (${e}). Status: ${r}
|
|
669
689
|
set -u
|
|
670
|
-
FROM=${
|
|
671
|
-
TO=${
|
|
672
|
-
STATUS_FILE=${
|
|
690
|
+
FROM=${Kt(n??"")}
|
|
691
|
+
TO=${Kt(t)}
|
|
692
|
+
STATUS_FILE=${Kt(r)}
|
|
673
693
|
STARTED_AT=${Number(u)}
|
|
674
694
|
write_status() { # <status> [error]
|
|
675
695
|
local err="\${2:-}"
|
|
676
696
|
err="\${err//\\\\/\\\\\\\\}"; err="\${err//\\"/\\\\\\"}"
|
|
677
697
|
printf '{"status":"%s","from":"%s","to":"%s","kind":"%s","startedAt":%s,"updatedAt":%s%s}\\n' \\
|
|
678
|
-
"$1" "$FROM" "$TO" ${
|
|
698
|
+
"$1" "$FROM" "$TO" ${Kt(e)} "$STARTED_AT" "$(date +%s000)" \\
|
|
679
699
|
"\${err:+,\\"error\\":\\"$err\\"}" > "$STATUS_FILE.tmp" && mv "$STATUS_FILE.tmp" "$STATUS_FILE"
|
|
680
700
|
}
|
|
681
701
|
${f}
|
|
682
|
-
`;if(e==="npm-global"){let g=a?
|
|
683
|
-
`:"",
|
|
702
|
+
`;if(e==="npm-global"){let g=a?gt.join(a,"bin","npm"):"npm",y=a?`export PATH=${Kt(gt.join(a,"bin"))}:"$PATH"
|
|
703
|
+
`:"",b=a?` --prefix ${Kt(a)}`:"",k=S=>`${Kt(g)} install -g "bostrat-node@${S}" --no-audit --no-fund${b}`;return w+y+`
|
|
684
704
|
${d("updating",'"" ')}
|
|
685
|
-
if ! ${
|
|
705
|
+
if ! ${k("$TO")}; then
|
|
686
706
|
${d("failed",'"npm install failed"')}
|
|
687
707
|
exit 1
|
|
688
708
|
fi
|
|
@@ -697,14 +717,14 @@ if health; then
|
|
|
697
717
|
fi
|
|
698
718
|
# \u2500\u2500 rollback: the same primitive with the previous version \u2500\u2500
|
|
699
719
|
${d("rolling-back",'"agent did not answer on :'+s+' after the update"')}
|
|
700
|
-
if [ -n "$FROM" ] && ${
|
|
720
|
+
if [ -n "$FROM" ] && ${k("$FROM")} && ${p} && health; then
|
|
701
721
|
${d("rolled-back",'"agent did not answer on :'+s+' after the update; rolled back"')}
|
|
702
722
|
exit 1
|
|
703
723
|
fi
|
|
704
724
|
${d("failed",'"agent did not answer after the update AND the rollback"')}
|
|
705
725
|
exit 1
|
|
706
|
-
`}let m=g=>`( cd ${
|
|
707
|
-
LIVE=${
|
|
726
|
+
`}let m=g=>`( cd ${Kt(l)} && git fetch -q origin main --tags && git show origin/main:scripts/deploy-pi.sh > "$TMP_SCRIPT" && bash "$TMP_SCRIPT" "v${g}" )`;return w+`
|
|
727
|
+
LIVE=${Kt(l)}
|
|
708
728
|
TMP_SCRIPT="$(mktemp)"
|
|
709
729
|
trap 'rm -f "$TMP_SCRIPT"' EXIT
|
|
710
730
|
# "restarting" is written BEFORE deploy-pi.sh runs (it does fetch \u2192 build \u2192
|
|
@@ -721,66 +741,66 @@ if [ -n "$FROM" ] && ${m("$FROM")}; then
|
|
|
721
741
|
fi
|
|
722
742
|
${d("failed",'"deploy-pi.sh v$TO failed AND the rollback failed"')}
|
|
723
743
|
exit 1
|
|
724
|
-
`}async function al(e=Cp){try{return JSON.parse(await bo.readFile(e,"utf8"))}catch{return null}}var BW=new Set(["updating","restarting","rolling-back"]),Np=e=>!!(e&&BW.has(e.status));async function Pp({to:e,from:t=Mp,kind:n=xs(),reason:r="manual",statusFile:o=Cp,spawnImpl:s=RW,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(Np(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(ht.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?xm(await jW())||ht.resolve(ht.dirname(process.execPath),".."):null,d=DW({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),p=ht.join(ht.dirname(o),`update-${e}.sh`),f=ht.join(ht.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=i$.existsSync("/run/systemd/system")&&await LW("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",p]]:["setsid",["-f","bash","-c",`exec bash ${Gt(p)} >> ${Gt(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 LW(e){for(let t of String(process.env.PATH||"").split(ht.delimiter))try{return await bo.access(ht.join(t,e),i$.constants.X_OK),!0}catch{}return!1}var FW=45*6e4;async function u$({statusFile:e=Cp,current:t=Mp,now:n=Date.now,staleMs:r=FW}={}){let o=await al(e);if(!o)return null;let s=a=>bo.writeFile(e,JSON.stringify(a)).catch(()=>{});if(Np(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 d$({isBusy:e,start:t=Pp,onStuck:n=null,onStarted:r=null,tickMs:o=NW,stuckMs:s=CW,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=Mp,kind:m=xs()}){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}}}me();gt();var p$=(...e)=>fetch(...e);function f$(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Ay(e=process.env){if(!f$(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 p$(`${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 Ty(e=process.env){if(!f$(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 p$(`${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 Ay(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 Dp(e,t,{source:n="chat"}={}){return C(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}me();gt();var UW=(...e)=>fetch(...e);async function Bp(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 UW(`${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 HW(e,t=process.env){return Bp("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function m$({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await HW(t,o):await Ka(e,o),a=await Bp("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function Ey(e,t=process.env){let n=await Ka(e,t),r=await Bp("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function _y(e,t=process.env){return(await Bp("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function h$(e,t,n,r=process.env){let{blueprint:o,items:s}=await Ey(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 C(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}me();gt();var WW=(...e)=>fetch(...e);async function qW(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 WW(`${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 w$=72,Iy=160;async function g$(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>w$)throw new Error(`the title is the choice as a LABEL \u2014 ${w$} 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 ${Iy} chars) a reader can act on without opening the record`);if(c.length>Iy)throw new Error(`the summary is one sentence \u2014 ${Iy} 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 C(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 y$(e,t=process.env){return qW("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}ig();me();import fl from"node:fs/promises";import Vp from"node:path";import Fq from"node:os";jr();import KW from"node:fs/promises";import b$ from"node:path";import GW from"node:os";var k$=()=>b$.join(process.env.BOSTRAT_STATE_DIR||b$.join(process.env.HOME||GW.tmpdir(),".bostrat"),"integrations.json");async function Rr(){try{return JSON.parse(await KW.readFile(k$(),"utf8"))||{}}catch{return{}}}var jp=["linked","prOpened","prMerged","create"],$y=["prompt","auto","off"],VW={create:["prompt","off"]},x$=e=>VW[e]??$y;function Lp(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of jp){let s=x$(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 vs(){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:Lp(e),defaultTeam:n}:null}async function Fp(){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:Lp(e)}}async function Ry(){return Lp(await Rr())}async function v$(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${jp.join(", ")}`);for(let[r,o]of t){if(!jp.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${jp.join(", ")}`);let s=x$(r);if(!s.includes(o))throw new Error(s.length<$y.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${$y.join(", ")}`)}let n=await Rr();return n.ticketFlow={...Lp(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Mt(k$(),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 zW(){let e=(await Rr()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var JW={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Up(){let t=(await Rr()).voiceChat||{},n=t.provider||"openai",r=JW[n];if(!r)return null;let o=n==="openai"?await zW():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function S$(){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 Ss(){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}}me();Fr();Yr();Ra();import ll from"node:fs/promises";import U$ from"node:path";import Sq from"node:os";import{randomUUID as H$}from"node:crypto";var My={};Tf(My,{createComment:()=>oq,findIssueUrl:()=>QW,issueStates:()=>iq,mdToAdf:()=>E$,moveIssue:()=>cq,parseIssueRef:()=>ZW,resolveIssue:()=>eq,uploadImage:()=>sq,upsertCard:()=>tq,whoami:()=>Oy});var Ga=e=>String(e.baseUrl||"").replace(/\/$/,""),T$=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function As(e,t,n,r){let o=await fetch(`${Ga(e)}${n}`,{method:t,headers:{authorization:T$(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 YW=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,XW=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function ZW(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(YW);return r?r[1].toUpperCase():null}let n=t.match(XW);return n?n[1]:null}function QW(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 eq(e,t){let n;try{n=await As(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:`${Ga(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function tq(e,t,{url:n,title:r,subtitle:o}){await As(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 nq=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function rq(e){let t=[],n=0;for(let r of e.matchAll(nq)){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 E$(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
725
|
-
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...
|
|
744
|
+
`}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(`
|
|
745
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...mq(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 hq(e,t,n){let r=await Es(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/comment`,{body:D$(n)});return r?.id?`${Ka(e)}/browse/${t.key}?focusedCommentId=${r.id}`:null}async function Dy(e){let t=await Es(e,"GET","/rest/api/3/myself"),n=null;try{n=new URL(Ka(e)).host}catch{n=e.baseUrl??null}return{user:t?.displayName??t?.emailAddress??null,org:n}}async function wq(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(`${Ka(e)}/rest/api/3/issue/${encodeURIComponent(t)}/attachments`,{method:"POST",headers:{authorization:P$(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??`${Ka(e)}/rest/api/3/attachment/content/${c.id}`}var gq={new:"unstarted",indeterminate:"started",done:"completed"},N$=e=>gq[e?.key]??"started";async function yq(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:N$(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:N$(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function bq(e,t,n){await Es(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/transitions`,{transition:{id:String(n)}})}var Uy={};Ef(Uy,{createComment:()=>Iq,createIssue:()=>Fy,createProject:()=>Ly,findIssueUrl:()=>Aq,issueStates:()=>$q,moveIssue:()=>Rq,parseIssueRef:()=>Sq,resolveIssue:()=>Tq,teams:()=>Va,uploadImage:()=>_q,upsertCard:()=>Eq,whoami:()=>jy});var kq=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function Xn(e,t,n={}){let r=await fetch(kq(),{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 xq=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,vq=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function Sq(e){let t=String(e||"").trim(),n=t.match(xq);if(n)return n[1].toUpperCase();let r=t.match(vq);return r?r[1]:null}function Aq(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 Tq(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 Eq(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 Iq(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 jy(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 _q(e,{filename:t,contentType:n,buffer:r}){let o=await Xn(e,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
726
746
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
727
747
|
success
|
|
728
748
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
729
749
|
}
|
|
730
|
-
}`,{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
|
|
731
|
-
`)):Hp.rmSync(r,{force:!0})}catch{}}function jy(){return R$().viewBase}function Ly(e=process.env.BOSTRAT_PUBLIC_HOST){return O$(e,"https")}function C$(){let e=jy();if(e)return{proto:"https",host:e,source:"learned"};let t=Ly();return t?{...t,source:"env"}:null}function Wp(e,t){return e?`${e.proto}://${t}.${e.host}/`:null}function vq(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 N$(){let e=String(process.env.BOSTRAT_PUBLIC_URL||"").trim().replace(/\/+$/,"");if(e)return{url:e,source:"env"};let t=vq();return t?{url:t,source:"plane"}:null}var P$="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 W$={linear:{api:Dy,config:vs},jira:{api:My,config:Fp}};async function Gy(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(W$)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Ts(e){let t=W$[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function Vy(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 q$=()=>process.env.BOSTRAT_STATE_DIR||U$.join(process.env.HOME||Sq.tmpdir(),".bostrat"),Wy=()=>U$.join(q$(),"tickets.json"),dl=e=>{let t=N$();if(!t)throw new Error(P$);return`${t.url}/track/${e}`},Aq=e=>`http://bostrat.globopi/track/${e}`,D$=e=>{let t=0;for(let[n,r]of Object.entries(e))r&&typeof r=="object"&&!r.cardUrl&&(r.cardUrl=Aq(n),t++);return t},Yn=null,Fy=null,Uy=Promise.resolve();async function pl(){return Yn||(Fy??=ll.readFile(Wy(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{Yn=e;let n=D$(Yn)>0;if(!t&&qe()){let{records:r,source:o}=await lt("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;Yn[a]=c}D$(Yn),n=n||Object.keys(Yn).length>0}}return n&&await qy().catch(()=>{}),Yn}).finally(()=>Fy=null),Fy)}async function qy(){let e=JSON.stringify({tickets:Yn},null,2),t=`${Wy()}.${process.pid}.tmp`;await ll.mkdir(q$(),{recursive:!0}),await ll.writeFile(t,e),await ll.rename(t,Wy())}async function ko(e=null){if(Uy=Uy.then(qy,qy),await Uy.catch(()=>{}),!e)return;let t=Yn?.[e];t?Tt("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):an("tickets",e).catch(()=>{})}async function Xn(e){return(await pl())[e]??null}async function K$(){return{...await pl()}}async function G$(){return(await Gy()).length>0}async function Ja(e,t,{source:n="api"}={}){let r=await Gy();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 C({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 J$(e.id,c,"linked"),c}async function zy(e){let t=await pl();return t[e]?(delete t[e],await ko(e),!0):!1}var Ky=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function V$(e,t){if(!e.track?.cwd)return null;let n=Jr(await q({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function Tq(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Ky(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${Ky(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function Eq(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(Ky(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function ul(e,t){let n=await Ts(t);if(!n)return;let r=await V$(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:t.cardUrl??dl(e),title:`Bostrat Track: ${t.track.name}`,subtitle:Eq(t,r)})}function _q(e){return Wp(C$(),e)}var qp=e=>_q(e)??"(no public URL on this node \u2014 link it to a plane)",Iq={linked:"linked",opened:"prOpened",merged:"prMerged"};function $q(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 z$(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await ko(e),await C({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 J$(e,t,n){let r=await Ts(t);if(!r)return;let o=r.cfg.ticketFlow?.[Iq[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=Vy(s,a),c=$q(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 z$(e,t,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await C({id:e,cwd:t.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:H$(),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 Y$(e){let t=await Xn(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Ts(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=Vy(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 Jy(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await Xn(e);if(!r)throw new Error("no ticket linked to this Track");let o=await Ts(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Vy(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 z$(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function X$(e,t){let n=await Xn(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Ts(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 C({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 Z$(e,t,n){if(!t)throw new Error("offerId required");return uo(`tkt:${e.id}:${t}`,()=>Rq(e,t,n))}async function Rq(e,t,n){let r=await q({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 Jy(e.id,o.payload.to?.id,{mode:"offer"})),await C({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 B$=50;async function Q$(){let e=await vs();if(!e)throw new Error("no ticketing integration configured");return Va(e)}function Oq(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>B$)throw new Error(`too many issues (${n.length}); cap is ${B$} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function Mq(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 Xn(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 eR(e,t){let n=await vs();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=Oq(t),o=await Va(n),s=await Mq(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=H$();return await C({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 tR(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await q({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 C({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 vs();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 Ny(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 Py(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 C({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 C({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 Xn(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 Ts(e);n&&await n.api.createComment(n.cfg,e,t)}async function Cq(e,t,n){let r=await Ts(e);if(!r)return null;let o=await xr(t);if(!o)return null;let s=gn(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 j$=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),Hy=new Map;function Nq(e,t){if(j$()<=0)return ul(e,t);if(Hy.has(e))return;let n=setTimeout(()=>{Hy.delete(e),Xn(e).then(r=>r?ul(e,r):null).catch(()=>{})},j$());n.unref?.(),Hy.set(e,n)}var L$=new Map;async function F$(e,t,n){let r=L$.get(e)??0;Date.now()-r<600*1e3||(L$.set(e,Date.now()),await C({id:e,cwd:t},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function Pq(e,{resolveTrack:t=null}={}){let n=await Xn(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await Gy())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 F$(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 V$(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?`
|
|
750
|
+
}`,{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 $q(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 Rq(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 Va(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 B$=255;async function Ly(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>B$,a=s?`${o.slice(0,B$-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 Fy(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}}gc();import Wp from"node:fs";import Hy from"node:path";import Oq from"node:os";var Mq=()=>process.env.BOSTRAT_STATE_DIR||Hy.join(process.env.HOME||Oq.tmpdir(),".bostrat"),j$=()=>Hy.join(Mq(),"plane-link","public-host.json"),L$="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",za=null;function F$(){if(za)return za;try{let e=JSON.parse(Wp.readFileSync(j$(),"utf8"));za={viewBase:typeof e?.viewBase=="string"&&e.viewBase?e.viewBase:null}}catch{za={viewBase:null}}return za}function U$(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 H$(e){let t=typeof e=="string"&&e.trim()?U$(e,"https").host:null;if(F$().viewBase===t)return;za={viewBase:t};let r=j$();try{t?(Wp.mkdirSync(Hy.dirname(r),{recursive:!0}),Wp.writeFileSync(r,JSON.stringify({viewBase:t,learnedAt:new Date().toISOString()})+`
|
|
751
|
+
`)):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?`
|
|
732
752
|
|
|
733
|
-
Agent cost for this Track: ${
|
|
753
|
+
Agent cost for this Track: ${Dq(o)}.`:"")),await sR(e.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await qq(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}\`)`:""}:
|
|
734
754
|
|
|
735
|
-
`)}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}**: ${
|
|
736
|
-
_(runs on the Track's branch; a Bostrat-fronted preview, not a public deployment)_`);break}case"cost.recorded":await
|
|
737
|
-
${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
|
|
755
|
+
`)}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}**: ${Gp(r.slug)}
|
|
756
|
+
_(runs on the Track's branch; a Bostrat-fronted preview, not a public deployment)_`);break}case"cost.recorded":await Gq(e.trackId,n);break;default:break}}catch(o){let s=t?await t(e.trackId).catch(()=>null):null;await Y$(e.trackId,s?.cwd??null,o)}}function pR({resolveTrack:e}){Xe.on("event",t=>{Kq(t,{resolveTrack:e}).catch(()=>{})})}async function fR(){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 Vq from"ws";var mR=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function Kp(e,t,n={}){let r=await fetch(`${mR()}/${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 tb=(e,t)=>Kp("chat.postMessage",e,t),_s=(e,t)=>Kp("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(`${mR()}/${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 hR(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 Kp("files.completeUploadExternal",e,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var Jq=2200;function wR({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 Kp("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new Vq(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()},Jq);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 bR=()=>process.env.BOSTRAT_STATE_DIR||zp.join(process.env.HOME||Xq.tmpdir(),".bostrat"),ab=()=>zp.join(bR(),"slack.json"),Vp=null,nb=null,rb=Promise.resolve();async function cb(){return Vp||(nb??=fl.readFile(ab(),"utf8").then(e=>JSON.parse(e)?.threads??{}).catch(()=>({})).then(e=>Vp=e).finally(()=>nb=null),nb)}async function gR(){let e=JSON.stringify({threads:Vp},null,2),t=`${ab()}.${process.pid}.tmp`;await fl.mkdir(bR(),{recursive:!0}),await fl.writeFile(t,e),await fl.rename(t,ab())}async function An(){rb=rb.then(gR,gR),await rb.catch(()=>{})}async function lb(e){return(await cb())[e]??null}async function Zq(e,t){let n=await cb();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 Qq(e,t=500){let n=String(e??"").trim().split(/\n\s*\n/)[0]??"";return ml(n.replace(/\n+/g," "),t)}function kR(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}
|
|
757
|
+
${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 ib(e,t){await _s(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:kR(t)})}async function vo(e,t,n,r=void 0){return tb(e.botToken,{channel:t.channel,thread_ts:t.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var yR=new Set;function xo(e){yR.add(e),e.catch(()=>{}).finally(()=>yR.delete(e))}var We=e=>({response_type:"ephemeral",text:e}),e5="*/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._",t5=["claude","cursor","codex"];function ob(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 sb=(e,t)=>We(`No live Track matches \u201C${Ie(t)}\u201D. Live: ${e.map(n=>`\`${Ie(n.name)}\``).join(" \xB7 ")||"none"}`);async function xR(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?zp.basename(c.base):"fresh workspace"),lead:"claude"},u=await tb(t.botToken,{channel:t.channel,text:`Track started: ${l.name}`,blocks:kR(l),unfurl_links:!1});l.ts=u.ts,(await cb())[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 n5(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.archiveTrack(n.id);let s=await lb(n.id);s&&(s.archivedAt=Date.now(),await An(),await ib(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 r5(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 o5(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 s5(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 a5(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?zp.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(`
|
|
738
758
|
`)+(o?`
|
|
739
|
-
_${o} archived_`:"")}async function
|
|
740
|
-
\u2713 *${
|
|
741
|
-
\u2713 _answered_`}}]})}async function
|
|
742
|
-
_runs on the Track's branch; a Bostrat-fronted preview, not a public deployment_`}}]);return}case"mock.state.changed":return
|
|
743
|
-
`)){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
|
|
744
|
-
`)}me();Ph();import $5 from"node:fs/promises";import R5 from"node:path";_u();Mu();Yr();var Jp=e=>e.payload?.ts??e.ts,S5=12e4,rn=e=>Math.round(e*1e4)/1e4,A5=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{},byTrack:new Map});function T5(e,{since:t}){let{track:n,events:r,brief:o}=e,s=b=>Jp(b)>=t,a=ar([zi(n?.name),zi(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let b of r)if(b.type==="subtask.delegated"&&b.boxId&&b.payload?.task){let I=ar(b.payload.task);I&&i.set(b.boxId,I)}let c=new Map,l=b=>{let I=b||"unattributed";return c.has(I)||c.set(I,{segments:[u(null)],lastTs:null,provider:null}),c.get(I)},u=(b,{shape:I="initial",continuation:$=!1,text:P=null,ts:F=null}={})=>({textCat:b,shape:I,continuation:$,text:P,ts:F,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((b,I)=>Jp(b)-Jp(I)),p=0,f=0,w=0,m={turns:0};for(let b of d){let I=l(b.boxId),$=I.segments[I.segments.length-1],P=Jp(b);switch(I.lastTs!=null&&P>I.lastTs&&s(b)&&($.activeMs+=Math.min(P-I.lastTs,S5)),I.lastTs=P,b.provider&&(I.provider=b.provider),b.type){case"turn.user":{let F=b.payload?.text;if(b.source==="hook"||!String(F||"").trim()||b.payload?.note||Xi(F))break;let B=ar(F),T=!!b.payload?.kind,_=T||Sx(F),U=u(B,{shape:T?"injected":_?"follow-up":"novel-ask",continuation:!B&&_,text:yR(F),ts:P});I.segments.push(U),s(b)&&(U.turns+=1);break}case"peer.message":{let F=b.payload?.text;if(!String(F||"").trim())break;let B=ar(F),T=u(B,{shape:"peer",continuation:!B,text:yR(F),ts:P});I.segments.push(T),s(b)&&(T.turns+=1);break}case"tool.called":{if(b.source==="transcript")break;let F=b.payload?.tool;F&&($.tools[F]=($.tools[F]||0)+1),s(b)&&($.toolCalls+=1);break}case"cost.recorded":{if(!s(b))break;let{usd:F,tokens:B,model:T,unpriced:_}=Xt(b);_&&(m.turns+=1),$.usd+=F,B&&($.tokens.input+=B.input,$.tokens.output+=B.output,$.tokens.cacheRead+=B.cacheRead,$.tokens.cacheWrite+=B.cacheWrite),$.models[T]=($.models[T]||0)+F,b.provider&&($.provider=b.provider);break}case"handoff.done":s(b)&&(p+=1);break;case"subtask.merged":s(b)&&(f+=1);break;case"error":s(b)&&b.payload?.kind==="subtask-failed"&&(w+=1);break;default:break}}let g=new Map,y={usd:0,turns:0,byShape:{},samples:[]},x=!1;for(let[b,I]of c){let $=null;for(let P of I.segments){let F=null,B=null;if(P.textCat)F=P.textCat.category,B="text";else if(i.get(b))F=i.get(b).category,B="boxTask";else if(P.continuation&&$)F=$,B="inherited";else{let R=vx(P.tools);R?(F=R.category,B="toolMix"):a&&(F=a.category,B="hint")}if(F&&($=F),!(P.usd>0||P.activeMs>0||P.turns>0||P.toolCalls>0))continue;let _=F??"uncategorized";g.has(_)||g.set(_,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{}});let U=g.get(_);U.usd+=P.usd,U.activeMs+=P.activeMs,U.turns+=P.turns,U.toolCalls+=P.toolCalls,B&&(U.provenance[B]=(U.provenance[B]||0)+P.usd);for(let[R,A]of Object.entries(P.models))U.models[R]=(U.models[R]||0)+A;let J=P.provider||I.provider;J&&(U.providers[J]=(U.providers[J]||0)+P.usd),J==="cursor"&&P.usd===0&&(x=!0),F||(y.usd+=P.usd,y.turns+=P.turns,y.byShape[P.shape]=(y.byShape[P.shape]||0)+P.usd,P.usd>0&&P.text&&y.samples.push({trackId:n?.id,track:n?.name||n?.id,shape:P.shape,usd:rn(P.usd),ts:P.ts,text:P.text}))}}return{perCategory:g,uncategorized:y,handoffs:p,merged:f,failed:w,sawCursorActivity:x,unpricedTurns:m.turns}}function ab(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:x,events:b}=y;if(!b?.length){a+=1;continue}let{perCategory:I,uncategorized:$,handoffs:P,merged:F,failed:B,sawCursorActivity:T,unpricedTurns:_}=T5(y,{since:r});T&&(i=!0),c+=_,l.usd+=$.usd,l.turns+=$.turns;for(let[J,R]of Object.entries($.byShape))l.byShape[J]=(l.byShape[J]||0)+R;l.samples.push(...$.samples);let U={trackId:x?.id,name:x?.name||x?.id,archived:!!x?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:P,subtasksMerged:F,subtasksFailed:B};for(let[J,R]of I){U.usd+=R.usd,U.activeMs+=R.activeMs,U.turns+=R.turns,U.categories[J]={usd:rn(R.usd),activeMs:R.activeMs};for(let[N,K]of Object.entries(R.models))U.models[N]=rn((U.models[N]||0)+K);for(let[N,K]of Object.entries(R.providers))U.providers[N]=rn((U.providers[N]||0)+K);o.has(J)||o.set(J,A5());let A=o.get(J);A.usd+=R.usd,A.activeMs+=R.activeMs,A.turns+=R.turns,A.toolCalls+=R.toolCalls;for(let[N,K]of Object.entries(R.models))A.models[N]=(A.models[N]||0)+K;for(let[N,K]of Object.entries(R.providers))A.providers[N]=(A.providers[N]||0)+K;for(let[N,K]of Object.entries(R.provenance))A.provenance[N]=(A.provenance[N]||0)+K;let M=A.byTrack.get(x?.id)||{usd:0,activeMs:0};M.usd+=R.usd,M.activeMs+=R.activeMs,A.byTrack.set(x?.id,M)}(U.usd>0||U.activeMs>0||U.turns>0)&&(U.usd=rn(U.usd),U.dominantCategory=I5(U.categories),s.push(U))}let u=[...o.values()].reduce((y,x)=>y+x.usd,0),d=[...o.values()].reduce((y,x)=>y+x.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,x])=>({category:y,sharePct:f>0?Math.round((p==="usd"?x.usd:x.activeMs)/f*100):0,shareBasis:p,usd:rn(x.usd),activeMs:x.activeMs,turns:x.turns,toolCalls:x.toolCalls,models:wl(x.models),providers:wl(x.providers),provenance:wl(x.provenance),topWorkstreams:[...x.byTrack.entries()].sort((b,I)=>I[1].usd-b[1].usd||I[1].activeMs-b[1].activeMs).slice(0,2).map(([b,I])=>({trackId:b,name:w.get(b),usd:rn(I.usd),activeMs:I.activeMs}))})).sort((y,x)=>x.usd-y.usd||x.activeMs-y.activeMs);s.sort((y,x)=>x.usd-y.usd||x.activeMs-y.activeMs);let g={};for(let y of o.values())for(let[x,b]of Object.entries(y.provenance))g[x]=(g[x]||0)+b;return{since:r,capturedAt:n,totalUsd:rn(u),activeMs:d,turns:[...o.values()].reduce((y,x)=>y+x.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,x)=>x.usd-y.usd).slice(0,E5)}}}}var E5=25,gR=160,yR=e=>{let t=String(e||"").replace(/\s+/g," ").trim();return t.length>gR?`${t.slice(0,gR).trimEnd()}\u2026`:t},_5=10;function bR(e,{thresholdPct:t=_5}={}){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 I5=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 Is={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function O5(e){let t=String(e||"7d");return t in Is?{key:t,ms:Is[t]}:{key:"7d",ms:Is["7d"]}}function M5(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 C5(e){if(!e?.cwd)return null;try{let n=(await $5.readFile(R5.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 ib(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}:O5(t),d=[];for(let w of e||[]){let m=await q(w),g=M5(w,m);i&&c<o&&(g=g.filter(y=>(y.ts??0)<=c)),d.push({track:w,events:g,brief:await C5(w)})}let p=ab(d,{windowMs:u,now:c}),f=l==="7d"&&c===o?p:ab(d,{windowMs:Is["7d"],now:o});return{window:l,...p,categorization:bR(f)}}var gl=e=>`$${Number(e||0).toFixed(2)}`,kR=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 xR(e){let t=[];t.push(`USAGE ROUNDUP \xB7 ${e.window} \xB7 ${gl(e.totalUsd)} \xB7 ${kR(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(kR(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(`
|
|
745
|
-
`)}ir();var Ao=36e5,
|
|
746
|
-
`).slice(-3).join(" | ").slice(0,300);d!==
|
|
747
|
-
`).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
|
|
748
|
-
`).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
|
|
749
|
-
`),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=
|
|
750
|
-
`,{mode:384}),
|
|
759
|
+
_${o} archived_`:"")}async function i5(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(xR(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:s5(u,i)}}case"archive":{if(!a.length)return We("Usage: `/bostrat archive <track>`");let{match:i,live:c}=ob(await r.listTracks(),a.join(" "));return i?(xo(n5(e,n,{track:i,responseUrl:t.response_url})),We(`Archiving *${Ie(i.name)}*\u2026`)):sb(c,a.join(" "))}case"handoff":{let i=r.knownAgents??t5,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}=ob(await r.listTracks(),l);return u?(xo(r5(e,n,{track:u,agent:c,responseUrl:t.response_url})),We(`Handing *${Ie(u.name)}* to ${Ie(c)}\u2026`)):sb(d,l)}case"stop":{if(!a.length)return We("Usage: `/bostrat stop <track>`");let{match:i,live:c}=ob(await r.listTracks(),a.join(" "));return i?(xo(o5(e,n,{track:i,responseUrl:t.response_url})),We(`Stopping *${Ie(i.name)}*\u2026`)):sb(c,a.join(" "))}case"list":return We(await a5(r));default:return We(e5)}}async function c5(e,t,n,r){let{trackId:o,promptId:s,label:a}=r,i=await lb(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")}~
|
|
760
|
+
\u2713 *${Ie(a)}*`}}]}).catch(()=>{})}}catch(c){await It(n.response_url,We(`Couldn't answer: ${c?.message||c}`))}}async function l5(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 u5(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 d5(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(xR(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(c5(e,r,t,o)):n.action_id==="bostrat-ship"?xo(l5(e,r,t,o)):n.action_id==="bostrat-pr-exists"&&xo(u5(e,r,t,o))}async function p5(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 Zq(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 f5(e){return async t=>{if(t.type==="slash_commands")return i5(e,t.payload);if(t.type==="interactive"){await d5(e,t.payload);return}if(t.type==="events_api"){await p5(e,t.payload?.event);return}}}var m5=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 h5(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 w5(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",h5(n.trackId,r));t.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await An()}async function g5(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")}~
|
|
761
|
+
\u2713 _answered_`}}]})}async function y5(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 b5(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 k5(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 x5(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 v5(e,t,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??S5)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await hR(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 S5(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 A5(e,t){if(!m5.has(t.type))return;let n=await lb(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 ib(r,n);return;case"turn.agent":{if(!o.text)return;let s=Qq(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 w5(r,n,t);case"prompt.answered":return g5(r,n,t);case"pr.offered":return y5(r,n,t);case"pr.offer.answered":return b5(r,n,t);case"pr.exists.offered":return k5(r,n,t);case"pr.exists.answered":return x5(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 ib(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await vo(r,n,`Live preview ${o.name}: ${Gp(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${Ie(o.name)}*: ${Gp(o.slug)}
|
|
762
|
+
_runs on the Track's branch; a Bostrat-fronted preview, not a public deployment_`}}]);return}case"mock.state.changed":return v5(r,n,t,e.deps)}}function vR(e){let t={deps:e};Xe.on("event",s=>{A5(t,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await Ts()||(r&&clearInterval(r),r=null,n=wR({getToken:async()=>(await Ts())?.appToken,onEnvelope:f5(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 T5 from"node:fs/promises";function E5(e){let t=new Map;for(let n of String(e||"").split(`
|
|
763
|
+
`)){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 I5(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 _5=new Set(["Write","Edit","NotebookEdit"]),$5=220,R5=160;function O5(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(_5.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,$5)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=Do(o.command.trim().replace(/\s+/g," ")).slice(0,R5));return r}function M5(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 SR(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=Nd(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:O5(e,o)});break;case"Stop":await C5(r,o,e,i);break;default:break}return a}async function C5(e,t,n,r){let o=[],s=M5(n,t);if(s)try{o=E5(await T5.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=I5(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();Qf();Md();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 AR=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),TR=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},Jp=e=>Math.round(e*1e4)/1e4;async function ub(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=AR(),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);TR(u,m.totalTokens);for(let[g,y]of Object.entries(m.byModel))n[g]||={usd:0,events:0,tokens:AR()},n[g].usd=Jp(n[g].usd+y.usd),n[g].events+=y.events,TR(n[g].tokens,y.tokens);for(let[g,y]of Object.entries(m.byProvider))r[g]||={usd:0,events:0},r[g].usd=Jp(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:Jp(o),asRecordedUsd:Jp(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)}`,Ya=(e,t)=>String(e).padEnd(t);function ER(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(` ${Ya(a,26)} ${Ya(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(` ${Ya(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(` ${Ya(c.label,40)} ${Ya(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))}${Ya(So(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
764
|
+
`)}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(`
|
|
765
|
+
`)}ir();var Ao=36e5,PR=24*Ao,G5=400,K5=12,Os=e=>Math.round(e*1e4)/1e4;function fb({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 V5=(e,t)=>e!=null&&t-e<=48*Ao?Ao:PR;function MR(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 CR=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),NR=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},J5=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Os(n)]));async function DR(e,t={}){let n=t.now??Date.now(),r=fb({...t,now:n}),o=r.since==null?PR:V5(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=CR(),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(E=>E.type==="cost.recorded");if(!j.length){A.archivedAt||$.push(A.name||A.id);continue}let K=[];for(let E of j){let C=E.ts??0;s(C)?K.push(E):i(C)&&(g+=Xt(E).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 E of R.unpricedModels||[])y.add(E);NR(u,R.totalTokens);for(let[E,C]of Object.entries(R.byModel))c[E]||={usd:0,events:0,tokens:CR()},c[E].usd=Os(c[E].usd+C.usd),c[E].events+=C.events,NR(c[E].tokens,C.tokens);for(let[E,C]of Object.entries(R.byProvider))l[E]||={usd:0,events:0},l[E].usd=Os(l[E].usd+C.usd),l[E].events+=C.events;for(let E of K){let{usd:C}=Xt(E);if(!C)continue;let P=MR(E.ts??0,o),W=b.get(P)??{t:P,total:0,byProvider:{}};W.total+=C;let Y=E.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[E,C]of Object.entries(R.byBox))S.push({trackId:A.id,trackName:A.name||A.id,boxId:E,...C})}let L=r.since!=null?MR(r.since,o):M,B=[];for(let A=L,I=0;A!=null&&A<r.until&&I<G5;A=z5(A,o),I++){let j=b.get(A);B.push(j?{t:A,total:Os(j.total),byProvider:J5(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,K5)}}Rd();var Y5=new Set(["cost.recorded","code.committed","code.rework","rework.cause.offered","rework.cause.answered"]),X5=20,Z5=40;async function BR(e,t={}){let n=t.now??Date.now(),r=fb({...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(!Y5.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=ET(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,X5),fixes:c.fixes.slice(0,Z5)}}import Q5 from"node:fs";import jR from"node:fs/promises";import yl from"node:os";import e6 from"node:path";Vw();ht();var t6=1024*1024,Xp=e=>Math.round(e/t6);async function n6(e){try{let t=await jR.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:Xp(r),diskTotalMb:Xp(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function r6(){try{return(await ln()).filter(t=>t.isBostrat).length}catch{return null}}async function o6(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await jR.readdir(e6.join(nl,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function LR({stateDir:e=nl,now:t=Date.now}={}){let n=yl.totalmem(),[r,o,s,a]=await Promise.all([Kd().catch(()=>null),n6(Q5.existsSync(e)?e:yl.tmpdir()),r6(),o6()]),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?Xp(n-r):null,memTotalMb:Xp(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(yl.uptime())}}_w();ye();Mm();xi();ir();ye();ir();var s6=864e5;function a6(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 FR(e,{days:t=14,tolerance:n=.005,now:r=Date.now()}={}){let o=r-t*s6,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=a6(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();td();yt();Zt();import Xa from"node:fs/promises";import i6 from"node:fs";import xt from"node:path";import c6 from"node:os";import{execFile as l6}from"node:child_process";import{promisify as u6}from"node:util";var d6=u6(l6),UR=process.env.BOSTRAT_STATE_DIR||xt.join(process.env.HOME||c6.tmpdir(),".bostrat"),Zp=!1,mb=null;async function hb(e,t){let n=await Xa.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await Xa.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await Xa.mkdir(xt.dirname(t),{recursive:!0}),await Xa.copyFile(e,t),!0)}var p6=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 Qp(e,t,{skip:n=null}={}){let r=0,o=await Xa.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(p6.has(s.name)||n?.has(s.name))continue;let a=xt.join(e,s.name);s.isDirectory()?r+=await Qp(a,xt.join(t,s.name)):s.isFile()&&(r+=await hb(a,xt.join(t,s.name))?1:0)}return r}async function HR({stateDir:e=UR,dest:t=process.env.BOSTRAT_SYNC_DIR||`${UR}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await Xa.mkdir(t,{recursive:!0});let o=0;o+=await hb(xt.join(e,"tracks.json"),xt.join(t,"tracks.json"))?1:0,o+=await hb(xt.join(e,"events.jsonl"),xt.join(t,"events.jsonl"))?1:0,o+=await Qp(xt.join(e,"events"),xt.join(t,"events")),o+=await Qp(xt.join(e,"memory"),xt.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await Qp(xt.join(u.cwd,".bostrat"),xt.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>d6("git",["-C",t,...u]);i6.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 Df(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||Zp))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,Zp&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),Zp=!1,mb=null}catch(u){Zp=!0;let d=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
766
|
+
`).slice(-3).join(" | ").slice(0,300);d!==mb&&(mb=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();di();import{execFile as f6}from"node:child_process";import{promisify as m6}from"node:util";import{randomUUID as h6}from"node:crypto";import w6 from"node:fs";import g6 from"node:os";import y6 from"node:path";var Eo=m6(f6),ef=e=>new Promise(t=>setTimeout(t,e)),Io=nm,wb=process.env.BOSTRAT_USAGE_PROBE_CWD||y6.join(g6.homedir(),".bostrat","usage-probe"),b6=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,k6=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,x6=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,WR=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,To=null,bl=null,kl=null;async function v6(){try{return await Eo(te,oe("has-session","-t",Io)),!0}catch{return!1}}function S6(){To&&clearTimeout(To),To=setTimeout(()=>A6().catch(()=>{}),b6),To.unref&&To.unref()}async function A6(){To&&(clearTimeout(To),To=null);try{await Eo(te,oe("kill-session","-t",Io))}catch{}}async function T6(){let e=Date.now()+x6;for(;;){let t=await gb().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(t)){await ef(500);return}if(Date.now()>e)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await ef(500)}}async function E6(){if(await v6())return;if(!await Hr())throw new Error("host tmux server not running \u2014 start globo-tmux.service");w6.mkdirSync(wb,{recursive:!0}),await Cr(wb);let e=h6();await Eo(te,oe("new-session","-d","-s",Io,"-c",wb,"-x","200","-y","60",...Ri()));let t=`${tm} --session-id ${e}`;await Eo(te,oe("send-keys","-t",Io,t,"Enter")),await T6()}async function gb(){let{stdout:e}=await Eo(te,oe("capture-pane","-t",Io,"-p"));return e}async function I6(){await E6(),await Eo(te,oe("send-keys","-t",Io,"-l","/usage")),await Eo(te,oe("send-keys","-t",Io,"Enter"));let e=Date.now()+k6,t="";for(;await ef(400),t=await gb(),!(/Current\s+(session|week)/i.test(t)||/\d+%\s*used/i.test(t)||Date.now()>e););return await ef(900),t=await gb(),await Eo(te,oe("send-keys","-t",Io,"Escape")).catch(()=>{}),S6(),t}function _6(e){let t=String(e||"").split(`
|
|
767
|
+
`).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 $6(e){let t=String(e||"").split(`
|
|
768
|
+
`).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 qR({fresh:e=!1}={}){return!e&&bl&&Date.now()-bl.at<WR?bl.data:kl||(kl=(async()=>{try{let t=await I6(),n=_6(t),r={...n,contributors:$6(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()-(WR-3e3),data:n},n}finally{kl=null}})(),kl)}var R6=262144;function O6(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 M6=new Set([300]),C6=new Set([10080]);function N6(e){let t=Number(e);return!Number.isFinite(t)||t<=0?null:M6.has(t)?"session":C6.has(t)?"week":"other"}function P6(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 D6(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?O6((o-n)/1e3):null,resetsAt:o}}var xl=e=>({session:null,week:null,other:null,planType:null,credits:null,limitReached:null,sawTokenCount:e});function B6(e,t=Date.now()){let n=String(e||"").split(`
|
|
769
|
+
`),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=D6(f,l,t);if(!m)return;let g=N6(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:P6(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 yb(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,R6),o=B6(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 GR(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 tf({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 yb(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await yb(null))}return n.has("cursor")&&r.push(await tf({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function tf(e,{fresh:t=!1}={}){let n=e?.agent||"claude";return n==="codex"?yb(e):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await qR({fresh:t})}}Pr();yt();yt();function j6(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}var L6=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),nf=(e,t)=>Object.assign(new Error(e),{status:t});async function KR(e,t){if(!j6(t))throw nf("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${Ne(t)}${e}`,{headers:L6(t)})}catch(o){throw nf(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw nf(r?.error||`plane replied ${n.status}`,n.status);return r}async function VR(e,{max:t=10,env:n=process.env}={}){let r=new URLSearchParams({q:e||""});return t&&r.set("max",String(t)),(await KR(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function zR(e,{env:t=process.env}={}){if(!e)throw nf("message id required",400);return(await KR(`/v1/gmail/messages/${encodeURIComponent(e)}`,t)).message}ic();gd();function bb(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 F6(e){let[t,n]=e.split("/"),r=bb(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 JR(e){return(e||"").split(",").map(t=>t.trim()).filter(Boolean).map(F6).filter(Boolean)}function U6(e){let t=e.headers["fly-client-ip"];return typeof t=="string"&&t.trim()?t.trim():(e.socket&&e.socket.remoteAddress||"").replace(/^::ffff:/,"")}function YR(e){let t=JR(e),n=String(e||"").trim().toLowerCase()==="open";return{open:n,clientIp:U6,allowed(r){if(n)return!0;let o=bb((r||"").replace(/^::ffff:/,""));return o==null?!1:t.some(s=>s(o))}}}function XR(e,t){let n=JR(e);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=bb(o);if(s==null||!n.some(i=>i(s)))return!1;let a=t(r);return!!(a&&a.view)}}}import ZR from"node:crypto";import rf from"node:fs";import H6 from"node:path";function QR(e=process.env,{stateDir:t=nl,log:n=console}={}){let r=String(e.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=H6.join(t,"api-token");try{let a=rf.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=ZR.randomBytes(32).toString("base64url");try{rf.mkdirSync(t,{recursive:!0}),rf.writeFileSync(o,s+`
|
|
770
|
+
`,{mode:384}),rf.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 eO(e,t){if(!e||!t)return!1;let n=Buffer.from(String(e)),r=Buffer.from(String(t));return n.length!==r.length?!1:ZR.timingSafeEqual(n,r)}function tO(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 W6=[/^\/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/],q6=e=>W6.some(t=>t.test(e));function nO({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=tO(o);return eO(c,e)?a():q6(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 rO(e,t,{enforce:n=!0}={}){return!n||eO(tO(e),t)?!0:vl(e)}Rn();qd();qd();import G6 from"node:http";import K6 from"node:net";function of(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 sf(e){let t=of(e.headers.host);return t?{slug:t,view:Uw(t)}:null}var V6=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),z6=e=>{let t=V6(e.name);return`<!doctype html><meta charset="utf-8">
|
|
751
771
|
<meta http-equiv="refresh" content="3">
|
|
752
772
|
<title>${t} \u2014 starting</title>
|
|
753
773
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
754
774
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
755
|
-
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function
|
|
775
|
+
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function oO(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 sO(e,t,n){let r=sf(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=G6.request({host:"127.0.0.1",port:o.port,method:e.method,path:oO(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 aO(e,t,n){let r=sf(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
|
|
756
776
|
\r
|
|
757
777
|
bostrat: no live view "${s}"\r
|
|
758
|
-
`),t.destroy(),!0;let a=
|
|
778
|
+
`),t.destroy(),!0;let a=K6.connect(o.port,"127.0.0.1",()=>{let c=[`${e.method} ${oO(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
|
|
759
779
|
`)+`\r
|
|
760
780
|
\r
|
|
761
|
-
`),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 hb(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=jy();if(r)return{proto:"https",host:r,source:"learned"};let o=Ly();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:rf(s)&&Fw(a[0])?a.slice(1).join("."):s,source:"request"}}function XR(e,t){return Wp(hb(t),e.slug)}Ra();eg();Yd();Wu();me();import ZR from"node:os";import{randomUUID as gb}from"node:crypto";var j6=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||ZR.hostname(),nodeName:e.BOSTRAT_NODE_NAME||ZR.hostname()});var QR=32768,eO=120,tO=1e3,nO=new Set(["claude","codex","cursor"]),L6=new Set(["off","transition","nag"]),F6=[[0,59],[0,23],[1,31],[1,12],[0,6]];function U6(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=F6[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 An=e=>String(e??"").trim(),Rs={name(e){let t=An(e);if(!t)throw new Error("a loop needs a name");if(t.length>eO)throw new Error(`name is too long (${t.length}); cap is ${eO}`);return t},schedule(e){let t=An(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!U6(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=An(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>QR)throw new Error(`prompt is too long (${t.length} chars); cap is ${QR}`);return t},expectation(e){let t=An(e)||null;if(t&&t.length>tO)throw new Error(`expectation is too long (${t.length}); cap is ${tO}`);return t},notify(e){let t=An(e)||"off";if(!L6.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=An(typeof t=="string"?t:t?.provider)||"claude";if(!nO.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...nO].join(", ")}`);let r=typeof t=="object"&&t&&An(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 H6(e,t){let n=e&&typeof e=="object"?e:{},r=Rs.name(n.name),o=Rs.schedule(n.schedule),s=Rs.prompt(n.prompt),a=Rs.expectation(n.expectation),i=Rs.notify(n.notify),c=Rs.assignment(n.assignment),l=An(t?.workspace?.repo)||An(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 oO(e,t){let n=H6(t,e),r=gb();return await C({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 sO(e,t,n,{loopId:r=null,nextFireAt:o=null}={}){if(!t)throw new Error("proposalId required");let s=await q({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 C({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 C({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 C({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 wb=["name","schedule","prompt","expectation","notify","assignment","enabled"],W6=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function aO(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 iO(e,t){let n=An(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 q6(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=>!wb.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: ${wb.join(", ")}`)}let o={};for(let a of wb){if(!(a in n))continue;let i=Rs[a](n[a]);(a==="assignment"?W6(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 cO(e,t,n){let r=aO(t),{patch:o,changed:s}=q6(n,r),a=gb(),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 C({id:e.id,cwd:e.cwd},{type:"loop.update.offered",actor:"system",source:"orchestrator",payload:i}),i}var rO=600;async function lO(e,t,n){let r=An(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>rO)throw new Error(`reason is too long (${r.length}); cap is ${rO}`);let o=aO(t),s=gb(),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 C({id:e.id,cwd:e.cwd},{type:"loop.delete.offered",actor:"system",source:"orchestrator",payload:a}),a}var uO=async(e,t,n)=>{if(!n)throw new Error("proposalId required");let r=await q({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 dO(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 uO(e,"loop.update.offered",t)).payload,a={id:e.id,cwd:e.cwd};return n==="applied"&&await C(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 C(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 pO(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 uO(e,"loop.delete.offered",t)).payload,s={id:e.id,cwd:e.cwd};return(n==="delete"||n==="pause")&&await C(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 C(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}}me();import{randomUUID as K6}from"node:crypto";var kb=20,G6=300,yb=120,fO=120,mO=2e3,sf=new Set(["build","ops","debt","spike"]),hO=new Set(["active","done","abandoned"]),xb=new Set(["circuit.create","circuit.update","circuit.delete"]),V6=new Set(["person.create","slot.create","slot.update","slot.delete","reservation.create","reservation.update","reservation.delete"]),Sl=new Set([...xb,...V6]),z6=["name","description","status","type"],J6=["person","startDate","endDate","estUsd"],Y6=["person","label","startDate","endDate"],X6=/^\d{4}-\d{2}-\d{2}$/,Vt=e=>String(e??"").trim(),vb=e=>typeof e=="string"&&e.startsWith("@"),yO=864e5;function bO(e){let t=Date.parse(`${e}T00:00:00Z`),n=new Date(t).getUTCDay();return new Date(t-(n+6)%7*yO).toISOString().slice(0,10)}function Z6(e){return new Date(Date.parse(`${bO(e)}T00:00:00Z`)+6*yO).toISOString().slice(0,10)}var wO=(e,t)=>{let n=Vt(e);if(!X6.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 Q6(e,t){let n=wO(e,"startDate"),r=wO(t,"endDate");if(r<n)throw new Error(`endDate ${r} is before startDate ${n}`);let o=bO(n),s=Z6(r);return{startDate:o,endDate:s,snapped:o!==n||s!==r}}var gO=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},Io=(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 bb(e,t){let n=Vt(t);if(!n)throw new Error("a circuit name or id is required");if(vb(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 eK(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"||vb(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 tK(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>kb)throw new Error(`too many ops (${o.length}); cap is ${kb} \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=b=>{b.id&&(l.circuits[b.id]={name:b.name,status:b.status,type:b.type??"build",updatedAt:b.updatedAt??null},u[b.id]=b.name)},m=b=>{b.id&&(u[b.id]=b.label)},g=(b,I)=>{let $=Vt(b);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[`@${$}`]=I,$},y=o.map((b,I)=>{let $=b&&typeof b=="object"?b:{},P=Vt($.op),F=`op ${I+1}`;if(!Sl.has(P))throw new Error(`${F}: unknown op "${P}" \u2014 one of ${[...Sl].join(", ")}`);if(!n.has(P))throw new Error(`${F}: "${P}" isn't a circuit op \u2014 roadmap changes go through \`bostrat-roadmap propose\` (one card, either CLI)`);let B=R=>{throw new Error(`${F} (${P}): ${R}`)},T=R=>{let A=$.set&&typeof $.set=="object"&&!Array.isArray($.set)?$.set:null;A||B("needs a `set` object naming the fields to change");let M=Object.keys(A).filter(N=>!R.includes(N));return M.length&&B(`unknown field(s) ${M.join(", ")} \u2014 settable: ${R.join(", ")}`),Object.keys(A).length||B("`set` names no fields"),A},_=R=>{let{startDate:A,endDate:M,snapped:N}=Q6(R.startDate,R.endDate);return N&&(p=!0),{startDate:A,endDate:M,snapped:N}},U=R=>{let A=eK(a,R);return Vt(R)==="me"&&(f=!0),m(A),{key:A.id??Vt(R),label:A.label}},J=(R,A,M,N,K)=>{let Y=Vt(R);Y||B(`needs a ${N} id`);let fe=A.find(Ie=>Ie[M]===Y);return fe||B(`no ${N} "${Y}" \u2014 see \`bostrat-roadmap ls\``),K[Y]={...fe},fe};switch(P){case"circuit.create":{let R=Io($.name,fO,"name");R||B("a circuit needs a name");let A=Vt($.type)||"build";sf.has(A)||B(`bad type "${A}" (${[...sf].join(" | ")})`);let M=s.find(K=>String(K.name).toLowerCase()===R.toLowerCase());M&&B(`a circuit named "${M.name}" already exists (${M.id}) \u2014 update it instead of making a twin`);let N=g($.ref,R);return{op:P,...N?{ref:N}:{},name:R,description:Io($.description,mO,"description"),type:A}}case"circuit.update":{let R=bb(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 A=T(z6),M={};if(A.name!==void 0){let N=Io(A.name,fO,"name");N||B("name can't be blank"),N!==R.name&&(M.name=N)}return A.description!==void 0&&(M.description=Io(A.description,mO,"description")),A.status!==void 0&&(hO.has(A.status)||B(`bad status "${A.status}" (${[...hO].join(" | ")})`),A.status!==R.status&&(M.status=A.status)),A.type!==void 0&&(sf.has(A.type)||B(`bad type "${A.type}" (${[...sf].join(" | ")})`),A.type!==(R.type??"build")&&(M.type=A.type)),Object.keys(M).length||B("nothing would change \u2014 the set equals the circuit's current values"),{op:P,circuit:R.id,name:R.name,set:M}}case"circuit.delete":{let R=bb(s,$.circuit);R.id||B("can't delete a circuit created in the same plan"),w(R);let A=o.some((M,N)=>N<I&&Vt(M?.op)==="circuit.update"&&M?.set?.status&&M.set.status!=="active"&&Vt(M?.circuit)===Vt($.circuit));return R.status==="active"&&!A&&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:P,circuit:R.id,name:R.name,status:R.status}}case"person.create":{let R=Io($.label,yb,"label");R||B("a person row needs a label");let A=a.find(N=>String(N.label).toLowerCase()===R.toLowerCase());A&&B(`a roadmap row labelled "${A.label}" already exists (${A.personId})`);let M=g($.ref,R);return{op:P,...M?{ref:M}:{},label:R}}case"slot.create":{let R=bb(s,$.circuit);w(R);let A=U($.person),M=_($);return{op:P,circuit:R.id??R.ref,circuitName:R.name,person:A.key,personLabel:A.label,...M,estUsd:gO($.estUsd)}}case"slot.update":{let R=J($.slot,i,"slotId","slot",l.slots),A=T(J6),M={};if(A.person!==void 0){let K=U(A.person);M.person=K.key,M.personLabel=K.label}if(A.startDate!==void 0||A.endDate!==void 0){let K=_({startDate:A.startDate??R.startDate,endDate:A.endDate??R.endDate});K.startDate!==R.startDate&&(M.startDate=K.startDate),K.endDate!==R.endDate&&(M.endDate=K.endDate)}A.estUsd!==void 0&&(M.estUsd=gO(A.estUsd)),Object.keys(M).length||B("nothing would change");let N=s.find(K=>K.id===R.circuitId);return{op:P,slot:R.slotId,circuitName:N?.name??null,set:M}}case"slot.delete":{let R=J($.slot,i,"slotId","slot",l.slots),A=s.find(M=>M.id===R.circuitId);return{op:P,slot:R.slotId,circuitName:A?.name??null,startDate:R.startDate,endDate:R.endDate}}case"reservation.create":{let R=U($.person),A=Io($.label,yb,"label");return A||B("a reservation needs a label (Leave, Support rotation, \u2026)"),{op:P,person:R.key,personLabel:R.label,label:A,..._($)}}case"reservation.update":{let R=J($.reservation,c,"reservationId","reservation",l.reservations),A=T(Y6),M={};if(A.person!==void 0){let N=U(A.person);M.person=N.key,M.personLabel=N.label}if(A.label!==void 0){let N=Io(A.label,yb,"label");N||B("label can't be blank"),N!==R.label&&(M.label=N)}if(A.startDate!==void 0||A.endDate!==void 0){let N=_({startDate:A.startDate??R.startDate,endDate:A.endDate??R.endDate});N.startDate!==R.startDate&&(M.startDate=N.startDate),N.endDate!==R.endDate&&(M.endDate=N.endDate)}return Object.keys(M).length||B("nothing would change"),{op:P,reservation:R.reservationId,label:R.label,set:M}}case"reservation.delete":{let R=J($.reservation,c,"reservationId","reservation",l.reservations);return{op:P,reservation:R.reservationId,label:R.label,startDate:R.startDate,endDate:R.endDate}}default:return B("unhandled op")}});y.forEach((b,I)=>{for(let $ of[b.circuit,b.person,b.set?.person])if(vb($)&&!d.has(String($).slice(1)))throw new Error(`op ${I+1} (${b.op}): unknown ref "${$}" \u2014 no earlier op in this plan creates it`)});let x=a.some(b=>b.kind==="user"&&b.userId);return{why:Io(r.why,G6,"why"),ops:y,base:l,resolved:u,snapped:p,createsMeRow:f&&!x}}async function kO(e,t,n,r={}){let o=tK(t,n,r),s=K6(),a={ts:Date.now(),proposalId:s,...o};return await C({id:e.id,cwd:e.cwd},{type:"planning.change.offered",actor:"system",source:"orchestrator",payload:a}),a}var nK=async(e,t)=>{if(!t)throw new Error("proposalId required");let n=await q({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 xO(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 nK(e,t),s={id:e.id,cwd:e.cwd},a=o.payload;return n==="applied"&&await C(s,{type:"planning.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,why:a.why??"",results:Array.isArray(r)?r.slice(0,kb+1):[]}}),await C(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}}Dd();me();Fr();me();Dn();Yd();var vO=10,SO=200,rK=120,oK=24,TO=/^\d{4}-\d{2}-\d{2}$/;function Sb(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var sK=(e=Date.now())=>new Date(e).toISOString().slice(0,10),xt=(e,t="i")=>new RegExp(e,t),aK=[{re:xt("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:xt("\\bSPC\\b",""),reason:"business (SPC)"},{re:xt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:xt("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:xt("\\bReplicas\\b",""),reason:"competitor research"},{re:xt("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:xt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:xt("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],iK=[xt("\\bSPC\\b",""),xt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),xt("competitor|cloudcli|\\bkiro\\b"),xt("\\bReplicas\\b",""),xt("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),xt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),xt("(^|[^\\w&])#\\d+\\b"),xt("\\btrack/[\\w.-]+")];function cK({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of aK)if(o.test(n)||o.test(r))return s;return null}function AO(e){let t=String(e||"");for(let n of iK){let r=n.exec(t);if(r)return r[0].trim()}return null}function EO(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 lK=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function uK(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(`
|
|
762
|
-
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,rK)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${oK}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=lK.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=AO(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>SO)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${SO}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=AO(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>vO)throw new Error(`${r.length} bullets \u2014 over the ${vO}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function dK(e,{repos:t=Sb()}={}){let n=new Set(t);return(await mn()).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:cK({title:r.title,headRef:r.headRef})}))}async function _O(e,t){let n=await dK(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function IO(e,{md:t,day:n=null}){let r=uK(t),o=n==null||n===""?sK():String(n);if(!TO.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await C({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 $O(e,{day:t}){let n=String(t||"");if(!TO.test(n)||Number.isNaN(Date.parse(n)))throw new Error(`invalid day "${t}" \u2014 expected YYYY-MM-DD`);let r=await C({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();Dn();cs();me();as();gt();gr();Dn();ic();Md();gd();fc();Zt();import{execFile as SK}from"node:child_process";import{promisify as AK}from"node:util";var pK=new Set(["pull_request","check_suite","workflow_run","push"]),fK=/^[^/\s]+\/[^/\s]+$/,mK=500,Ab=new Set,Tb=[],af=new Map,on={hourStart:0,byEvent:{},duplicates:0,rejected:0};function Eb(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 hK(e){if(!e||typeof e!="object"||e.forge!=="github"||typeof e.event!="string"||!pK.has(e.event)||typeof e.repo!="string"||!fK.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 RO(e,{now:t=Date.now(),onAccept:n=null,log:r=console}={}){let o=hK(e);if(!o)return Eb(t,"rejected"),{ok:!1,error:"malformed github event envelope"};if(o.deliveryId){if(Ab.has(o.deliveryId))return Eb(t,"duplicates"),{ok:!0,duplicate:!0,envelope:o};for(Ab.add(o.deliveryId),Tb.push(o.deliveryId);Tb.length>mK;)Ab.delete(Tb.shift())}let s=o.repo.toLowerCase(),a=af.get(s)||{lastDeliveryAt:0,count:0};a.lastDeliveryAt=t,a.count+=1,af.set(s,a),Eb(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 wK(e,t=Date.now()){let n=af.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 OO=1440*6e4;function _b(e,t=Date.now()){let n=wK(e,t);return n.covered&&n.ageMs<OO}function MO(e=Date.now()){let t={};for(let[n,r]of af)t[n]={lastDeliveryAt:r.lastDeliveryAt,count:r.count,covered:e-r.lastDeliveryAt<OO};return{hourStart:on.hourStart,byEvent:{...on.byEvent},duplicates:on.duplicates,rejected:on.rejected,repos:t}}me();Dn();Pr();gr();Zt();function CO(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function gK(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var yK=new Set(["merged","closed","lost"]);function bK(e,t){if(!e?.blocked)return null;let n=CO(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 kK(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function xK(e,t,n,r,o){return(await q(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 vK(e,t,n){await C({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 cf(e,t={}){let{now:n=Date.now(),slugOf:r=Re,lookup:o=ut,note:s=$A,tracks:a=async()=>(await ye()).filter(d=>!Le(d)),asked:i=xK,emitChange:c=vK}=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(!gK(f))continue;let w=await o(p,f.number).catch(()=>null);if(!w?.trackId||yK.has(w.state))continue;let{prev:m}=await s(p,f.number,{blocked:CO(f),sha:f.headSha??null,at:n});if(!m)continue;let g=bK(m,f);if(!g||kK(w,g.change))continue;u??=new Map((await a()).map(I=>[I.id,I]));let y=u.get(w.trackId);if(!y)continue;let x=f.headSha??null;if(await i(y,g.change,f.number,x,m.at||0))continue;let b={number:Number(f.number),url:f.url||w.url,title:f.title||w.title,branch:f.headRefName||w.headRef,repo:p,sha:x,delegated:!1,reason:g.reason};g.change==="ci-fixed"?b.passed=f.checks.passed:b.base=f.baseRefName||null,await c(y,g.change,b),l.push({change:g.change,number:f.number,reason:g.reason})}catch{}}return l}var Rb=AK(SK),HO=3e4,TK=30*6e4,EK=HO,_K=10*6e4,IK=10080*6e4,$K=60*6e4,NO=8,WO=1440*6e4,RK=3,qO="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",$b=60*6e4,OK=15*6e4,MK=9e4,CK=1440*6e4,Al=new Map,Tl=new Map,NK=360*6e4,PK=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}),DK=/could not resolve|not found|no pull requests?/i,BK=e=>DK.test(String(e?.message||e||""));function jK(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<$b)}var PO={fast:EK,slow:_K,decay:$K};function DO(e,t,n=_b){return KO(e,t,{grace:0})||!n(e.repo,t)&&(e.armedAt&&t-e.armedAt<TK||jK(e,t))?"fast":t-(e.firstSeen||0)>IK?"decay":"slow"}function _l(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 LK(e,t){pr("watch");try{let{stdout:n}=await Rb("gh",["pr","view",String(t),"--repo",e,"--json",qO],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await En({slug:e})}});return JSON.parse(n||"null")}catch(n){throw ao(n)&&io(n),n}}async function FK(e){pr("watch");try{let{stdout:t}=await Rb("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",qO],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await En({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 UK(){let e=new Map;for(let t of await ye().catch(()=>[]))e.set(t.id,t);return e}async function HK(e,t){await C({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 WK(e,t){let n=await ss(e);return n?ya(n,t):null}async function qK(e,t,n){let r=await ss(e);if(!r)throw new Error(`no local checkout for ${e}`);return ba(r,t,{method:n})}async function KK(e,t){let n=await ss(e);if(!n)throw new Error(`no local checkout for ${e}`);return uc(n,t)}async function GK(e,t){await C({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 VK=10*6e4;async function zK(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>WO){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<VK)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 GO(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function Qa(e,t,n){await C({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 JK(e,t){try{return await Rb("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 q(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 KO(e,t,{grace:n=MK}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=CK}async function YK(e,t,n,r){let o=r.now??Date.now();if(!KO(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 q(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 XK(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<OK;await IA(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await _A(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)>$b)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=_l(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)>$b)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"){_l(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 ZK(e,t,n,r){let o=_l(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 BO(e,t,n){_l(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 GO(e,t,n,r,o={}){let s=_l(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await q(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 jO(e={}){let{now:t=Date.now(),auth:n=qh,view:r=LK,list:o=FK,links:s=MA,trackMap:a=UK,emitMerged:i=HK,mark:c=oo,archiveIfArmed:l=hr,notifyIfArmed:u=wr,notifyIfResolved:d=bp,followupsIfMerged:p=bs,batchThreshold:f=RK,detail:w=WK,merge:m=qK,updateBranch:g=KK,lookup:y=ut,disarm:x=ac,emitDisarmed:b=GK,localHasCommit:I=JK,claim:$=ur,midTurn:P=is,only:F=null,wantDetail:B=null,covered:T=_b,verdicts:_=null,tokenFor:U=z=>js(z),noteRefusal:J=TA,clearRefusal:R=EA}=e;if(!(await n().catch(()=>null))?.authenticated||md()>0)return 0;let M=await s().catch(()=>[]),N=nT(M),K=z=>{try{return!!B?.(z)}catch{return!1}},Y=F?M.filter(z=>{try{return F(z)}catch{return!1}}):M.filter(z=>t-(Al.get(ie(z.repo,z.number))||0)>=PO[DO(z,t,T)]);if(!Y.length)return 0;let fe=Date.now(),Ie=new Map,Ce=new Map;for(let z of[...Y].sort(dc)){let we=F?"event":DO(z,t,T);Ie.set(ie(z.repo,z.number),{lane:we,intervalMs:PO[we]??null}),Al.set(ie(z.repo,z.number),t),Ce.has(z.repo)||Ce.set(z.repo,[]),Ce.get(z.repo).push(z)}let X=await a().catch(()=>new Map),et=new Map,Ee=(z,we)=>{let Fe=ie(z,we);if(!et.has(Fe)){let O=Promise.resolve().then(()=>w(z,we));O.catch(()=>{}),et.set(Fe,O)}return et.get(Fe)},se=_??((z,we)=>cf([{path:z,prs:[we]}],{now:t,slugOf:async()=>z,tracks:async()=>[...X.values()].filter(Fe=>!Le(Fe))}).catch(()=>{})),ee={now:t,lanes:Ie,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,followupsIfMerged:p,detail:Ee,merge:m,updateBranch:g,lookup:y,disarm:x,emitDisarmed:b,localHasCommit:I,claim:$,midTurn:P};for(let[z,we]of Ce){let Fe=PK(we);if(Fe.until>t)continue;let O=!1;try{if(await U(z),Fe.misses)for(let Q of we)await R(Q.repo,Q.number).catch(()=>{})}catch(Q){O=xk(Q)}if(O){let Q=Fe.misses+1;if(Q>=NO){console.log(`[pr-watch] ${z}: not granted ${Q}\xD7 \u2014 retiring ${we.length} link(s) as lost`);for(let ge of we){await BO(ge,X,ee);let pt=ie(ge.repo,ge.number);Tl.delete(pt),Al.delete(pt)}}else for(let ge of we)await J(ge.repo,ge.number,{misses:Q,until:t+NK}).catch(()=>{});continue}let ce,ve=new Set,wt=Q=>Q.ciFixAwait||Q.reconcileAwait||K(Q)?!0:!Q.autoMerge||Q.trackId&&N.get(Q.trackId)!==ie(Q.repo,Q.number)?!1:t-(Q.autoMerge.armedAt||0)<=WO;if(we.length>f)ce=await o(z).catch(()=>null);else{ce=new Map;for(let Q of we){if(wt(Q)){let ge=await Ee(z,Q.number).catch(()=>null);if(ge?.state){ce.set(Number(Q.number),{number:Number(Q.number),url:ge.url,title:ge.title,headRefName:ge.headRefName,headRefOid:ge.headSha??null,state:ge.state,mergedAt:ge.mergedAt??null,closedAt:ge.closedAt??null});continue}}try{let ge=await r(z,Q.number);ge?.number!=null&&ce.set(Number(ge.number),ge)}catch(ge){BK(ge)&&ve.add(ie(Q.repo,Q.number))}}}if(ce)for(let Q of we){let ge=ie(Q.repo,Q.number),pt=ce.get(Number(Q.number));if(!pt){if(ve.has(ge)){let rt=(Tl.get(ge)||0)+1;Tl.set(ge,rt),rt>=NO&&(await BO(Q,X,ee),Tl.delete(ge),Al.delete(ge))}continue}if(Tl.delete(ge),pt.state==="MERGED"||pt.state==="CLOSED")pt.state==="MERGED"?await GO(Q,pt,X,ee):await ZK(Q,pt,X,ee),Al.delete(ge);else{await YK(Q,pt,X,ee).catch(()=>{}),await XK(Q,pt,X,ee).catch(()=>{});let rt=!Q.trackId||N.get(Q.trackId)===ge;Q.autoMerge&&rt&&await zK(Q,X,ee).catch(()=>{});try{if(K(Q)&&!et.has(ge)&&Ee(Q.repo,Q.number),et.has(ge)){let k=await et.get(ge).catch(()=>null);k?.checks&&await se(Q.repo,k)}}catch{}}}}let Oe={};for(let{lane:z}of Ie.values())Oe[z]=(Oe[z]||0)+1;return console.log(`[pr-watch] probed ${Y.length} link(s) (${Object.entries(Oe).map(([z,we])=>`${z} ${we}`).join(", ")}) in ${Date.now()-fe}ms`),Y.length}function LO(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 Ib=!1,lf=!1,uf=[],FO=Promise.resolve();async function QK(){for(;lf||uf.length;){if(lf){lf=!1;try{await jO()}catch{}continue}let e=uf;uf=[];let t=e.map(r=>r.spec),n=Object.assign({},...e.map(r=>r.deps||{}));try{await jO({...n,only:r=>t.some(o=>LO(o,r)),wantDetail:r=>t.some(o=>o.detail&&LO(o,r))})}catch{}}}function df(e,t){return e===null?lf=!0:uf.push({spec:e,deps:t}),Ib||(Ib=!0,FO=QK().finally(()=>{Ib=!1})),FO}function VO(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 df({repo:n,numbers:r,branch:e.branch||null,event:e.event||null,detail:e.detail===!0},t)}var eG=6e4,UO=0;function zO(e=void 0){let t=e?.now??Date.now();return t-UO<eG?Promise.resolve():(UO=t,df({all:!0},e))}function JO(){df(null),setInterval(()=>df(null),HO)}as();Cd();Rc();gr();Zt();var tG=new Set(["opened","reopened","closed","synchronize","ready_for_review","converted_to_draft","edited"]);async function nG({tracks:e=ye,reconcile:t=qd}={}){for(let n of(await e().catch(()=>[])).filter(r=>!Le(r)))await t(n,{bypassThrottle:!0}).catch(()=>{})}function YO(e,t={}){let{invalidate:n=en,probe:r=VO,poke:o=LT,reconcileOpened:s=nG}=t;try{n()}catch{}let a={probe:null,poked:!1,reconciled:!1};if(!e?.repo)return a;if(e.event==="pull_request"&&tG.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}ft();me();gr();Zt();import{randomUUID as rG}from"node:crypto";var oG=2e4,XO=3,ti=new Map,Ob=new Set;async function ZO(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 sG(e={}){let{sessions:t=null,tracks:n=null,append:r=C,read:o=q,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await cn(),i=n??await ye()}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===XO&&(Ob.add(d.name),await ZO(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:rG(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let f=(ti.get(d.name)||0)>=XO;if(ti.delete(d.name),f||!Ob.has(d.name)){Ob.add(d.name);let w=await ZO(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 Il=null,Mb=!1;function QO(){return Il?()=>{}:(Il=setInterval(()=>{Mb||(Mb=!0,sG().catch(()=>{}).finally(()=>{Mb=!1}))},oG),Il.unref?.(),()=>{clearInterval(Il),Il=null})}ts();Rc();me();as();cs();ts();gc();import{randomUUID as aG}from"node:crypto";async function iG(){let e=await Promise.resolve().then(()=>(gr(),FI));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var Cb={listRepoPrs:Bn,readEvents:q,appendEvent:C,sayDeps:iG,currentBranch:co};async function cG(e,t=Cb,{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 Nb(e,t,{actions:n,notify:r=!1}={},o=Cb){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 cG(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=aG();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 Pb(e,{offerId:t,choice:n}={},r=Cb){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}`,()=>lG(e,{offerId:t,choice:n},r))}async function lG(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,ma(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,pA(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 aM from"node:os";import eM from"node:fs/promises";import pf from"node:path";import{execFile as uG}from"node:child_process";import{promisify as dG}from"node:util";import{fileURLToPath as pG}from"node:url";var fG=dG(uG),nM=pf.dirname(pG(import.meta.url)),mG=pf.resolve(nM,"..","..","client","dist"),hG="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",wG=new Map([...hG].map((e,t)=>[e,t]));function gG(e){let t=[],n=0,r=0;for(let o of e){let s=wG.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 yG(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=gG(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 bG(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 tM=new Map;async function kG(e){let t;try{t=await eM.stat(e)}catch{return null}let n=tM.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await eM.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:yG(r.mappings||"")};return tM.set(e,o),o}catch{return null}}var Db=null;function xG(){return Db||(Db=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):fG("git",["rev-parse","--short","HEAD"],{cwd:pf.resolve(nM,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),Db}var vG=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function SG(e,{distDir:t=mG}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(vG)){let[a,i,c,l]=s,u=await kG(pf.join(t,"assets",`${i}.map`)),d=u?bG(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 rM(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await xG();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await SG(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var AG=()=>process.env.BOSTRAT_STATE_DIR||ni.join(process.env.HOME||aM.tmpdir(),".bostrat"),$o=()=>ni.join(AG(),"telemetry"),iM=()=>ni.join($o(),".relay-offsets.json"),TG=50,EG=14,_G=3e4,oM=500,zt=(e,t)=>typeof e=="string"?e.slice(0,t):"";function IG(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 cM=e=>new Date(e).toISOString().slice(0,10),sM=0;async function lM(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,TG):[]).map(s=>IG(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await rM(s);await Or.mkdir($o(),{recursive:!0});let o=ni.join($o(),`${cM(t)}.ndjson`);return await Or.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
763
|
-
`).join(""),"utf8"),t-
|
|
764
|
-
`).filter(Boolean),d=[];for(let p of u)try{d.push(JSON.parse(p))}catch{}for(let p=0;p<d.length;p+=oM){let f=d.slice(p,p+oM).map(m=>RG(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 pM(r)}return{sent:o,disabled:!1}}var Bb=null,jb=!1;function fM(){Bb||(Bb=setInterval(async()=>{if(!jb){jb=!0;try{await OG()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{jb=!1}}},_G),Bb.unref?.())}import hf from"node:fs/promises";import yM from"node:path";import MG from"node:os";var bM=()=>process.env.BOSTRAT_STATE_DIR||yM.join(process.env.HOME||MG.tmpdir(),".bostrat"),kM=()=>yM.join(bM(),"dictation-glossary.json"),CG=100,NG=500,PG=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],wf=null,Lb=null,Fb=Promise.resolve();function mM(){let e={},t=Date.now();for(let n of PG)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function xM(){return wf||(Lb??=hf.readFile(kM(),"utf8").then(e=>JSON.parse(e)?.terms??mM()).catch(()=>mM()).then(e=>wf=e).finally(()=>Lb=null),Lb)}async function hM(){let e=JSON.stringify({terms:wf},null,2),t=kM(),n=`${t}.${process.pid}.tmp`;await hf.mkdir(bM(),{recursive:!0}),await hf.writeFile(n,e),await hf.rename(n,t)}function DG(){return Fb=Fb.then(hM,hM),Fb.catch(()=>{})}async function Ub(){return Object.values(await xM()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var BG=e=>Math.max(1,Math.ceil(e.length/4));async function jG(){let e=[],t=0;for(let{term:n}of await Ub()){let r=BG(n);if(!(t+r>NG)&&(e.push(n),t+=r,e.length>=CG))break}return e}async function LG(e,t){let n=await xM(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var wM=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),mf=e=>e.toLowerCase();function gM(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function vM(e,t){let n=wM(e),r=wM(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]=mf(n[u])===mf(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;)mf(n[c])===mf(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(gM(r[l])&&i.add(r[l]),l++);for(;l<s;l++)gM(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await LG(u,"learned");return await DG(),[...i]}var $l=null;function FG(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 SM(){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 AM(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 jG())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=FG(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??""}}me();var Hb={"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 UG(e){if(!e)return null;let t=String(e).trim();if(Hb[t])return t;let n=null;for(let r of Object.keys(Hb))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function HG(e,t={}){let n=Hb[UG(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 WG(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 qG(){if(!Rl)return null;let e=await Up();if(!e||e.apiKey.slice(-6)!==Rl.keyTail)return null;let{keyTail:t,...n}=Rl;return n}async function KG(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=WG(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 GG={openai:KG};async function TM(){let e=await Up();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await qG()}:{configured:!1}}async function EM(){let e=await Up();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 GG[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function _M(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=HG(t,s),i=await C(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();kA(async()=>new Set((await ye()).map(e=>e.id)));var PM=$e.dirname(zG(import.meta.url)),IM=Number(process.env.PORT)||3362,gf=ky(process.env),DM=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,ri=FR(process.env.BOSTRAT_ALLOW_IPS),Vb=UR(process.env.BOSTRAT_VIEW_FRONT_CIDRS,of),zb=WR(),BM=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",E=Jt();E.disable("x-powered-by");E.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),rf(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});E.use((e,t,n)=>{if(vl(e)||ri.allowed(ri.clientIp(e))||Vb.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});E.use(JR);E.use(GR({token:zb,enforce:BM}));E.use("/api/mocks",Jt.json({limit:"1mb"}));E.use("/api/mocks",Jt.raw({type:"image/png",limit:"24mb"}));E.use("/api/mocks",Jt.raw({type:["video/mp4","video/webm"],limit:"105mb"}));E.use("/api/runbooks",Jt.json({limit:"1mb"}));E.use("/api/tracks/adopt",Jt.json({limit:"1mb"}));E.use("/api/telemetry",Jt.json({limit:"256kb"}));E.use("/api/hook",Jt.json({limit:"2mb"}));E.use("/api/dictation",Jt.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));E.use("/api/tracks/:id/upload",Jt.raw({type:()=>!0,limit:"25mb"}));E.use(Jt.json({limit:"64kb"}));E.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:Ft,hostTmuxAlive:await Hr(),autoTriggerPct:DM,plane:oi?oi.status():null})});E.get("/api/sessions",async(e,t)=>{t.json({sessions:await cn()})});E.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 $n(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})}});E.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 be(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}`})}});E.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 be(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})});E.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 Sv(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)})}});E.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 $M=10;async function jM(){let e=await ye(),[t,n]=await Promise.all([VI(e),JI(e)]);return{tracks:e,views:await Lw(),queuedOf:r=>hs(r),jobsOf:r=>t.get(r)??[],prPendingOf:r=>n.get(r)??[]}}E.get("/api/idle-guard",async(e,t)=>{try{t.json(wy(await jM()))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var sn=xs(),eV=l$({kind:sn}),Ml=null,$t=()=>Ml&&["auto","prompt","off"].includes(Ml)?Ml:eV,Gb=null,tV=sl.has(sn),LM=async({to:e,from:t})=>!tV||Np(await al())?!1:(Nl.arm({to:e,from:t,kind:sn}),!0);async function nV(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}Gb&&await LM(Gb)}}var FM=(()=>{try{return QG(import.meta.url)("../package.json").version||null}catch{return null}})(),Nl=d$({isBusy:async()=>wy(await jM()).busy,onStuck:({to:e,from:t})=>zn({current:t,latest:e,installKind:sn,policy:$t(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?zn({current:t,latest:e,installKind:sn,policy:$t(),status:"updating",reason:"auto"}):zn({current:t,latest:e,installKind:sn,policy:$t(),status:"failed",error:n?.reason})).catch(()=>{})});E.get("/api/node/self-update",async(e,t)=>{t.json({installKind:sn,policy:$t(),policySource:Ml?"plane":"node",current:FM,status:await al(),armed:Nl.armed})});E.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 Pp({to:n,kind:sn,reason:"manual"});if(r.ok)return Nl.cancel(),zn({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 UM(){try{let e=await u$();e&&await zn({current:e.from,latest:e.to,installKind:e.kind||sn,policy:$t(),status:e.status,error:e.error,reason:e.reason}),await Mg({current:FM,installKind:sn,policy:$t()})}catch{}}UM();setInterval(UM,3e4).unref();E.get("/api/tracks",async(e,t)=>{let n=(await ye()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>$M){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,$M).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 dp(),tickets:await K$()})});E.get("/api/cost",async(e,t)=>{try{let n=await ye();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await sb(n);if(r.cursorUsage=Sw(),e.query.format==="text")return t.type("text/plain").send(wR(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/usage/roundup",async(e,t)=>{try{let n=await ib(await ye(),{window:e.query.window,from:e.query.from,to:e.query.to});if(e.query.format==="text")return t.type("text/plain").send(xR(n));t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/usage/spend",async(e,t)=>{try{let n=await ER(await ye(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=Sw(),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)})}});E.get("/api/usage/caps",(e,t)=>{t.json(rc())});E.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)})}});E.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await PR(await ye(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await lk(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 fR(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)})}});E.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 ST(n.track,n.box)).join(`
|
|
781
|
+
`),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(`
|
|
782
|
+
`)){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)+`
|
|
783
|
+
`).join(""),"utf8"),t-hM>6*3600*1e3&&(hM=t,FK(t).catch(()=>{})),r.length}async function FK(e=Date.now()){let t=yM(e-BK*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(ni.join($o(),s),{force:!0});let o=await xM();for(let s of r)delete o[s];await vM(o)}var mf=new Map;function kM(e,t=Date.now()){let n=e||"unknown",r=mf.get(n);return r||(mf.size>512&&mf.clear(),r={tokens:40,at:t},mf.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 xM(){try{return JSON.parse(await Or.readFile(gM(),"utf8"))||{}}catch{return{}}}async function vM(e){await Or.mkdir($o(),{recursive:!0}),await Or.writeFile(gM(),JSON.stringify(e),"utf8")}function UK(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||wM.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 HK({fetchImpl:e=fetch}={}){let t=await C$();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 xM(),o=0;for(let s of n){let a=ni.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(`
|
|
784
|
+
`).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(`
|
|
765
785
|
`)+`
|
|
766
|
-
`)}catch{t.status(204).end()}});E.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;AT(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)})}});E.post("/api/tracks/:id/rework-cause",async(e,t)=>{try{let n=await ue(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 ET(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})}});E.post("/api/tracks/:id/rework-cause/retract",async(e,t)=>{try{let n=await ue(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,event:await _T(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})}});E.get("/api/usage/rework",async(e,t)=>{try{t.json(await _R(await ye(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.post("/api/telemetry",async(e,t)=>{try{if(!uM(ri.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await lM(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});E.get("/api/repos",async(e,t)=>{try{let n=await nr(),r=new Set((await Promise.all(n.map(a=>Re(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)})}});E.get("/api/github/token",async(e,t)=>{try{t.json(await js(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});E.get("/api/github/status",(e,t)=>{t.json({configured:Gl(),calls:HA(),latency:GA(),webhook:MO()})});E.post("/api/github/event",Jt.json({limit:"64kb"}),(e,t)=>{let n=RO(e.body,{onAccept:r=>YO(r)});if(!n.ok)return t.status(400).json({error:n.error});t.json({ok:!0,duplicate:n.duplicate})});E.post("/api/github/refresh",(e,t)=>{vk(),t.json({ok:!0})});E.get("/api/gmail/search",async(e,t)=>{try{let n=await BR(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)})}});E.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await jR(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 Re(e):null;if(r&&t!=null){let s=await ut(r,t),a=s&&await ue(s.trackId);if(a)return a}if(!n)return null;let o=await ye();return Sd(o,n,s=>Ad(s,{branch:n,repo:e,slug:r}))}async function HM({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 Re(e).catch(()=>null):null,s=await ye();return Sd(s,n,a=>!Le(a)&&Ad(a,{branch:n,repo:e,slug:o}))}async function Tn(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 Re(s).catch(()=>null):null);await C({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 rV(e){let t=(await ye()).filter(n=>!Le(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await Re(n.path);if(r)for(let o of n.prs){if(await ut(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"})}}}E.get("/api/git/prs",async(e,t)=>{try{let n=await QA();await rV(n.repos).catch(()=>{}),await cf(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.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 x=o||"merge";if(!lc.has(x))throw new Error(`invalid merge method: ${x}`);let b=Number(r);if(!Number.isInteger(b)||b<=0)throw new Error("invalid PR number");let I=await Re((await fr(n)).path);if(!I)throw new Error("repo has no GitHub remote");let $=await Cl({repo:n,number:b,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 ep($,"merge",{count:1,summary:`arming auto-merge on PR #${b}`}),a&&await uc(n,b),await Ut({repo:I,number:b,trackId:$.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await sc(I,b,{merge:{method:x}}))throw new Error("couldn't record the auto-merge arm");return await ns(I,b,i===!0),await rs(I,b,c===!0&&i!==!0),en(),await Tn("auto-armed",{number:b,url:d,title:u,branch:l,repo:n,track:$,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:b,auto:!0})}p=await Cl({repo:n,number:r,branch:l}).catch(()=>null),p&&(f=await ep(p,"merge",{count:1,summary:`merging PR #${r}`}));let m=await ba(n,r,{method:o||"squash"});await Tn("merged",{number:m.number,url:d,title:u,branch:l,repo:n});let g=await Re(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 bs(g,m.number).catch(()=>{}),y=await hr(g,m.number).catch(()=>!1),await oo(g,m.number,"merged").catch(()=>{})),i===!0&&!y){let x=await Cl({repo:n,number:m.number,branch:l}).catch(()=>null);x&&!x.archivedAt&&await ys(x.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 wg(p,f,w?"failed":"completed",w).catch(()=>{})}});E.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 Re((await fr(n)).path).catch(()=>null),l=c?await ac(c,i).catch(()=>null):null;try{await Vh(n,i)}catch(u){if(!l)throw u}en(),await Tn("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})}});E.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await Re(n).catch(()=>null),s=await oT(n,{method:r||"merge",onMerged:a=>{Tn("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&wr(o,a.number).catch(()=>{}).then(()=>bs(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})}});E.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:rT()})});E.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 ue(n);if(!d)return t.status(404).json({error:`Track ${n} isn't on this node`});i=d;let p=(await mn()).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 ep(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 x=await ya(y,m.number);w.push({r:g,l:m,repoPath:y,pr:x,plan:eT(x,{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:x,plan:b}of tT(w))try{if(b.action==="skip")m.action="skipped",m.reason=b.reason;else if(b.action==="merge")await ba(y,g.number,{method:u}),await Tn("merged",{number:g.number,url:x.url,title:x.title,branch:x.headRefName,repo:y,track:d}),await wr(g.repo,g.number).catch(()=>{}),await bs(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(b.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 Tn("auto-armed",{number:g.number,url:x.url,title:x.title,branch:x.headRefName,repo:y,track:d,...m.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(I){m.ok=!1,m.action="failed",m.reason=I?.message||String(I)}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 wg(i,c,l?"failed":"completed",l).catch(()=>{})}});E.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 ue(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await mn()).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 Vh(c,a.number).catch(()=>{}),i&&(await Tn("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})}});E.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 ue(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 us(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 Jh(m.dir,{title:r,body:o,base:s}),y=m.repo||await Re(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 Tn("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 Jh(i.cwd,{title:r,body:o,base:s}),l=await Re(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 Tn("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})}});E.get("/api/tracks/:id/repos",async(e,t)=>{try{let n=await ue(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await us(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)})}});E.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 wp(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})}});E.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await gp(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})}});E.post("/api/tracks/:id/reshape",async(e,t)=>{let{repos:n,skipPermissions:r}=e.body||{};try{let o=await Vg(e.params.id,n,{skipPermissions:r!==!1});t.status(o.finishedAt?200:202).json({ok:!0,...Jb(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})}});E.get("/api/tracks/:id/reshape",(e,t)=>{t.json(Jb(Zc(e.params.id))??{running:!1})});function Jb(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}E.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await ya(n,r),s=await Re(n).catch(()=>null),a=await HM({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 sT(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 vt.promises.mkdir(d,{recursive:!0}),await vt.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
|
|
786
|
+
`)}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
|
|
767
787
|
${y.log??"(no fetchable log \u2014 see link)"}
|
|
768
788
|
`).join(`
|
|
769
|
-
`));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 vA(g,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Tn("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})}});E.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await ya(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await Re(n).catch(()=>null),a=await HM({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 SA(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Tn("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})}});E.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 ue(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await q(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 Nb(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,ma(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 C({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&&Fg(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})}});E.post("/api/tracks/:id/pr-exists",async(e,t)=>{let{offerId:n,choice:r}=e.body||{};try{let o=await ue(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await Pb(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:XR(t,e.headers)});E.get("/api/views",async(e,t)=>{try{let n=await Lw({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)})}});E.get("/api/views/:id",async(e,t)=>{let n=await LE(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:Pl(e)(n),session:Ea(n.slug)})});E.post("/api/views",async(e,t)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=e.body||{};try{let i=n?await ue(n):null;if(!i&&s&&(i=(await ye()).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(!hb(e.headers))return t.status(412).json({error:$$});let c=await Uw({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})}});E.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:Pl(e)(await FE(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});E.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:Pl(e)(await UE(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})}});E.delete("/api/views/:id",async(e,t)=>{try{t.json(await HE(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var oV=[["linear",vs,Cy],["jira",Fp,Oy]],Wb=new Map;E.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await Ry(),r={};for(let[o,s,a]of oV){let i=await s();if(!i){Wb.delete(o),r[o]={configured:!1};continue}let c=Wb.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},Wb.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)})}});E.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await Ry()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await v$(o),i=n?await ue(n):null;!i&&r&&(i=(await ye()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+$e.sep)))||null),i&&await C({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 Os=async(e,t)=>{let n=e?await ue(e):null;return!n&&t&&(n=(await ye()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+$e.sep)))||null),n};E.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await Ay()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});E.get("/api/roadmap",async(e,t)=>{try{t.json(await Ty())}catch(n){t.status(502).json({error:n?.message||String(n)})}});E.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await Os(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 Dp(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})}});E.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await Os(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await Dp(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var WM=async(e,t,n)=>{if(e)return{ref:e,track:await Os(t,n)};let r=await Os(t,n),o=r?await _y(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}};E.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 WM(o,n,r),l=await m$({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})}});E.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await WM(e.query.circuit,e.query.track,e.query.cwd);t.json(await Ey(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})}});E.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 Os(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=await g$(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})}});E.get("/api/decisions",async(e,t)=>{try{let n=await Os(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await y$(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})}});E.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 Os(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await _y(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 h$(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})}});E.post("/api/tickets/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=n?await ue(n):null;if(!s&&r&&(s=(await ye()).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})}});E.get("/api/tickets/status",async(e,t)=>{let{cwd:n,track:r}=e.query;try{let o=r?await ue(r):null;if(!o&&n&&(o=(await ye()).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 G$(),ticket:await Xn(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});E.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await Xn(e.params.id)})});E.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await ue(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})}});E.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await zy(e.params.id)})});var yf=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;E.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await Y$(e.params.id))}catch(n){let r=n?.message||String(n);t.status(yf(r)).json({error:r})}});E.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await Jy(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(yf(r)).json({error:r})}});E.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await X$(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(yf(r)).json({error:r})}});E.post("/api/tracks/:id/ticket-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{let o=await ue(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await Z$(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(yf(s)).json({error:s})}});E.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await oy(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})}});E.post("/api/tickets/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await ue(n):null;if(!s&&r&&(s=(await ye()).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 eR(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})}});E.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await Q$()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});E.post("/api/tracks/:id/ticket-create-offer",async(e,t)=>{let{proposalId:n,accept:r,teamId:o}=e.body||{};try{let s=await ue(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await tR(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})}});E.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)})}});E.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"};E.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=gn(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"})})});E.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=gn(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"})})});E.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=gn(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"})})});E.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&Gd(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 ue(a):null;if(!d&&c&&(d=(await ye()).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 r_({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})}});E.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&Gd(n)?n:null;try{let o=await o_(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})}});E.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await i_(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})}});E.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await T_(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await c_(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})}});E.get("/api/flows",async(e,t)=>{try{t.json({flows:await k_({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/flows/:id",async(e,t)=>{let n=await x_(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});E.post("/api/flows",async(e,t)=>{let{trackId:n,name:r,steps:o,cwd:s}=e.body||{};try{let a=n?await ue(n):null;if(!a&&s&&(a=(await ye()).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 v_({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})}});E.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await S_(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})}});E.delete("/api/flows/:id",async(e,t)=>{try{t.json(await A_(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});E.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await ye(),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 C_({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)})}});E.get("/api/runbooks/:id",async(e,t)=>{let n=await ng(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});E.get("/api/runbooks/:id/md",async(e,t)=>{let n=await ng(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=O_(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 N_(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"})})});E.get("/api/loops",async(e,t)=>{t.json(await ec())});E.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 ye()).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 Zm({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})});E.post("/api/loops/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await ue(n):null;if(!s&&r&&(s=(await ye()).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 oO(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var si=async(e,t)=>{let n=e?await ue(e):null;return!n&&t&&(n=(await ye()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+$e.sep)))||null),n};E.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 QT(s,o,{ship:{changedRepos:us}})})}catch(s){t.status(400).json({error:s?.message||String(s)})}});E.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 tE(s,o)})}catch(s){let a=s?.message||String(s);t.status(/unreachable/i.test(a)?503:400).json({error:a})}});E.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:ww,...await aE(r)})});var RM=new Set(["reconcile","fixing-ci","rebase","merge"]);E.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})});E.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&&!RM.has(l))throw new Error(`phase must be one of ${[...RM].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=XG();await C(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)})}});E.post("/api/tracks/:id/followup-offer",async(e,t)=>{let{offerId:n,followupId:r,armId:o,promoted:s}=e.body||{};try{let a=await ue(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await rE(a,{offerId:n,followupId:r||null,armId:o||null,promoted:s||null},{sayToLead:qt,unarchiveTrack:kp})})}catch(a){let i=a?.message||String(a),c=a?.code==="removed"?410:/unknown offer/i.test(i)?404:/already answered/i.test(i)?409:/required|not on this card/i.test(i)?400:/unreachable/i.test(i)?503:502;t.status(c).json({error:i})}});E.post("/api/tracks/:id/followup-arm-cancel",async(e,t)=>{try{let n=await ue(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await eE(n,e.body?.armId)})}catch(n){let r=n?.message||String(n);t.status(/unknown/i.test(r)?404:400).json({error:r})}});E.post("/api/tracks/:id/followup-create-offer",async(e,t)=>{let{proposalId:n,accept:r,followupId:o}=e.body||{};try{let s=await ue(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await nE(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})}});Je.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"||ue(e.trackId).then(async s=>{if(s&&(n&&await yc(s,n,o),t==="opened"||t==="pushed")){let a=sE(await q(s).catch(()=>[]));a&&await yc(s,"ship.done",{...o,shipOfferId:a})}}).catch(()=>{})});var qM=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await ue(n):null;if(!s&&r&&(s=(await ye()).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=iO(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}};E.post("/api/loops/propose-update",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await qM(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await cO(s.track,s.loop,e.body?.patch)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});E.post("/api/loops/propose-delete",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await qM(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await lO(s.track,s.loop,e.body?.reason)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});E.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 Ty()}catch(c){return t.status(503).json({error:c?.message||String(c)})}try{let c=s==="circuit"?xb:Sl;t.json({ok:!0,trackId:a.id,proposal:await kO(a,o,i,{allow:c})})}catch(c){t.status(400).json({error:c?.message||String(c)})}});var Yb=e=>/unknown proposal/i.test(e)?404:/already answered/i.test(e)?409:400;E.post("/api/tracks/:id/planning-offer",async(e,t)=>{let{proposalId:n,action:r,results:o}=e.body||{};try{let s=await ue(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await xO(s,n,r,{results:o||[]})})}catch(s){let a=s?.message||String(s);t.status(Yb(a)).json({error:a})}});E.post("/api/tracks/:id/loop-update-offer",async(e,t)=>{let{proposalId:n,action:r,nextFireAt:o}=e.body||{};try{let s=await ue(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await dO(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(Yb(a)).json({error:a})}});E.post("/api/tracks/:id/loop-delete-offer",async(e,t)=>{let{proposalId:n,action:r}=e.body||{};try{let o=await ue(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await pO(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(Yb(s)).json({error:s})}});E.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 ue(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await sO(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})}});E.post("/api/runbooks",async(e,t)=>{let{trackId:n,name:r,md:o,cwd:s}=e.body||{};try{let a=n?await ue(n):null;if(!a&&s&&(a=(await ye()).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 P_({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})}});E.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await D_(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})}});E.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await B_(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})}});E.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await j_(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});E.get("/api/changelog/draft",async(e,t)=>{try{let n=EO(e.query.since||null);t.json({since:n,repos:Sb(),...await _O(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var KM=async({trackId:e,cwd:t})=>{let n=e?await ue(e):null;return!n&&t&&(n=(await ye()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+$e.sep)))||null),n};E.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=await KM({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 IO(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})}});E.post("/api/changelog/retract",async(e,t)=>{let{day:n,trackId:r,cwd:o}=e.body||{};try{let s=await KM({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 $O(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var sV=120,qb=400,oi=null,OM=null;E.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>qb)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${qb} cap; a notification is a nudge, put the detail in chat`});let c=Nc(`${n||""}
|
|
770
|
-
${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
|
|
789
|
+
`));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||""}
|
|
790
|
+
${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 de(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,wV):l.name,body:d(i,Jb),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:oi?oi.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 NM(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 Kb(),problem:await CM()})});_.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 MM(n,r)})});_.get("/api/tracks/:id/voice-chat",async(e,t)=>{if(!await de(e.params.id))return t.status(404).json({error:"track not found"});t.json(await PM())});_.post("/api/tracks/:id/voice-chat/session",async(e,t)=>{try{let n=await de(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 DM())}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 de(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 BM(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 bp({name:n,cwd:r,repo:o,repos:s,agent:a,model:i,skipPermissions:c!==!1}),p=null;if(l)try{await Ja(d,l,{source:"create"})}catch(w){p=w?.message||String(w)}let f=null;if(u)try{await Bp(d,await Ga(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 de(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({track:n,handoff:nC(Wa(e.params.id)),reshape:ek(Zc(e.params.id)),subtasks:vs(e.params.id).map(Bl),pendingOffers:await Hg(n)})});_.delete("/api/tracks/:id",async(e,t)=>{try{let n=await ay(e.params.id,{cascadeMocks:e.query.mocks!=="0",cascadeRunbooks:e.query.runbooks==="1"});await Qy(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 ly(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 xp(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 fp(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 de(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}
|
|
771
791
|
data: ${JSON.stringify(i)}
|
|
772
792
|
|
|
773
|
-
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await
|
|
793
|
+
`),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=Bk(n.id,r),a=setInterval(()=>t.write(`: hb
|
|
774
794
|
|
|
775
|
-
`),25e3);e.on("close",()=>{clearInterval(a),s()})});
|
|
776
|
-
`);
|
|
795
|
+
`),25e3);e.on("close",()=>{clearInterval(a),s()})});_.get("/api/tracks/:id/search",async(e,t)=>{let n=await de(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=Ji(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 e0(),harvest:Yk()})});_.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 t0(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 gV=["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(`
|
|
796
|
+
`);_.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await qt(e.params.id,gV)})}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 vp(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 de(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 t_(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 de(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 yV=/^[0-9a-fA-F-]{8,64}$/;_.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(e,t)=>{try{let n=await de(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(!yV.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=oV.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 wp(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 Kg(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 mp(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 oy({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 qg(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 Xg(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 de(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 tf(r,{fresh:o}))});_.post("/api/tracks/:id/model",async(e,t)=>{let{model:n}=e.body||{};try{t.json(await Zg(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 xy(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 Wg(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(vy(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 Sp(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 Jg(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(nC(Wa(e.params.id))??{running:!1})});_.post("/api/tracks/:id/transfer-out",async(e,t)=>{try{let n=await gy(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=wy(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 Tp(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 by({...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 nC(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:y0,trace:await g0({limit:n})})});var Yb=$e.resolve(GM,"..","..","client","dist");St.existsSync(Yb)&&(_.use(Jt.static(Yb)),_.get(/^(?!\/api\/).*/,(e,t)=>t.sendFile($e.join(Yb,"index.html"),Dl)));var rC=aV.createServer(_),bV=new lV({noServer:!0}),oC=(e,t,n)=>{if(!(vl(e)||ri.allowed(ri.clientIp(e))||Zb.allows(e))){t.write(`HTTP/1.1 403 Forbidden\r
|
|
777
797
|
\r
|
|
778
|
-
`),t.destroy();return}if(
|
|
798
|
+
`),t.destroy();return}if(aO(e,t,n))return;if(!rO(e,Qb,{enforce:VM})){t.write(`HTTP/1.1 401 Unauthorized\r
|
|
779
799
|
\r
|
|
780
|
-
`),t.destroy();return}let r=
|
|
800
|
+
`),t.destroy();return}let r=L0(e.url);if(!r.ok){t.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
781
801
|
\r
|
|
782
802
|
`),t.destroy();return}let o=r.sessionName;if(!Be(o)){t.write(`HTTP/1.1 400 Bad Request\r
|
|
783
803
|
\r
|
|
784
|
-
`),t.destroy();return}let{cols:s,rows:a}=r;
|
|
804
|
+
`),t.destroy();return}let{cols:s,rows:a}=r;bV.handleUpgrade(e,t,n,i=>D0(i,o,{cols:s,rows:a}))};rC.on("upgrade",oC);var sC=process.env.BOSTRAT_CERT_DIR||$e.join(process.env.HOME||Ol.tmpdir(),".bostrat","certs"),HM=Number(process.env.BOSTRAT_TLS_PORT)||3443,WM=$e.join(sC,"bostrat.globopi.key"),qM=$e.join(sC,"bostrat.globopi.crt");if(St.existsSync(WM)&&St.existsSync(qM)){let e=iV.createServer({key:St.readFileSync(WM),cert:St.readFileSync(qM)},_);e.on("upgrade",oC),e.on("error",t=>{console.error(`[bostrat-agent] https listener failed (${t?.code||t?.message}) \u2014 continuing HTTP-only`)}),e.listen(HM,yf,()=>{console.log(`[bostrat-agent] https listening on ${yf||"0.0.0.0"}:${HM}`)})}rC.listen(jM,yf,()=>{console.log(`[bostrat-agent] listening on ${yf||"0.0.0.0"}:${jM}`);let e=a$(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: ${KM}%`),console.log(`[bostrat-agent] ip allowlist: ${ri.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(Zb.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),my().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),ny().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)),ky().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),uy().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(()=>dy()).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)),ry().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)),IV(),A_({listTracks:ke}),g_({trackOf:Ug}),vV(),SV(),EV(),GS({listTracks:ke,getTrack:de}).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)),i_()&&u_({listTracks:ke}),k0().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)),sM(),lM(),pR({resolveTrack:de}),vR({listTracks:ke,createTrack:bp,archiveTrack:ks,listRepos:nr,resolveTrack:de,sayToLead:qt,answerPrompt:mp,startLeadHandoff:Sp,interruptLead:wp,listSubtaskJobs:vs,cancelSubtask:_p,readEvents:G,appendEvent:N,divertShip:Lb,answerPrExists:Fb,knownAgents:Jc}),kV(),Mi().then(t=>{t||console.warn(`[boot] no host tmux server on ${Ft} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),SM(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(oi=w$({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,apiToken:Qb,nodeId:process.env.BOSTRAT_NODE_ID||Ol.hostname(),name:process.env.BOSTRAT_NODE_NAME||Ol.hostname(),listTracks:ke,detectAgents:pp,sampleMetrics:LR,deliverySample:()=>DE(Gg()),onHashKey:OE,onViewBase:H$,onTracksTransferred:yy,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(()=>{}),UM=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(()=>{}),UM=null},onConnected:()=>{oM()},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{Xb={latest:t.latest,current:t.current},await Yn({...t,policy:_t()}),_t()==="auto"&&await JM({to:t.latest,from:t.current})}catch(n){console.error(`[plane-link] update notice failed: ${n?.message||n}`)}},onUpdatePolicy:t=>fV(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 Dp({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(()=>{}),ZI().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),fR().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),vI().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),LI().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),jA().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{oi?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function kV(){xV(),Wd().catch(()=>{}),setInterval(()=>Wd().catch(()=>{}),1e4)}function xV(){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(GM,"..","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 vV(){let e=!1,t=async()=>{if(!e){e=!0;try{let n=await ke(),r=await HR({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 SV(){let e=null,t=async()=>{try{let n=aa(),r=0,o=[],s=null,a=null;try{let u=await ke(),d=await ub(u);r=d.fleet.unpricedTurns||0,o=d.fleet.unpricedModels||[],s=await FR(u,{days:7}),a=(await pb(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(`
|
|
785
805
|
`);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(`
|
|
786
|
-
`).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
|
|
806
|
+
`).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 AV=Number(process.env.BOSTRAT_MEMORY_RECONCILE_MS)||5*6e4,TV=Number(process.env.BOSTRAT_MEMORY_RECONCILE_FIRST_MS)||5*6e4;function EV(){let e=async()=>{try{let n=await Qk();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 Zk([...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,AV).unref()},TV).unref()}function IV(){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 fp(n.id).catch(()=>{}),await Gd(n).catch(()=>{}))}catch{}finally{e=!1}}},4e3)}
|