bostrat-node 1.9.9 → 1.9.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +198 -196
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
var
|
|
2
|
-
`).includes(a));if(!s.length)return;await
|
|
1
|
+
var VM=Object.defineProperty;var W=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var xf=(e,t)=>{for(var n in t)VM(e,n,{get:t[n],enumerable:!0})};import{createHash as zM,randomBytes as JM,timingSafeEqual as YM}from"node:crypto";function Qb(){return JM(32).toString("hex")}function Ro(e){let t=typeof e=="string"?e.trim():"";return t?zM("sha256").update(t).digest("hex"):null}function ek(e,t){return typeof e!="string"||typeof t!="string"||e.length!==t.length||!e?!1:YM(Buffer.from(e),Buffer.from(t))}function ci(e,t={},{now:n=Date.now(),log:r=console.warn}={}){if(vf.set(e,(vf.get(e)||0)+1),Sf.has(e)&&n-Sf.get(e)<XM)return!1;Sf.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=${vf.get(e)}${o?` ${o}`:""}`),!0}async function tk(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 Zn,Qn,vf,Sf,XM,Os=W(()=>{Zn="BOSTRAT_HOOK_TOKEN",Qn="x-bostrat-hook";vf=new Map,Sf=new Map,XM=6e4});import Lt from"node:fs/promises";import nk from"node:os";import Rt from"node:path";import{execFile as ZM}from"node:child_process";import{promisify as QM}from"node:util";function Dl(){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?Rt.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Rt.join(nk.homedir(),".claude.json")}function tC(){return process.env.CLAUDE_CONFIG_DIR?Rt.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Rt.join(nk.homedir(),".claude","settings.json")}async function Or(e){if(!e||typeof e!="string")return;let t=Rt.resolve(e),n=eC(),r=tC(),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(Rt.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 "${Qn}: $${Zn}" --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(Rt.dirname(r),{recursive:!0}),await Lt.writeFile(r,JSON.stringify(i,null,2)),await nC(t)}async function nC(e){try{let t=Rt.resolve(e),n=Rt.join(t,".bostrat","memory","claude"),r=Rt.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(Rt.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await Lt.mkdir(n,{recursive:!0}),await Lt.mkdir(Rt.dirname(r),{recursive:!0}),await Lt.writeFile(r,JSON.stringify(o,null,2)),await rC(t,["/.claude/settings.local.json"])}catch{}}async function rC(e,t){try{let{stdout:n}=await QM(ZM)("git",["-C",e,"rev-parse","--git-path","info/exclude"]),r=Rt.resolve(e,n.trim()),o=await Lt.readFile(r,"utf8").catch(()=>""),s=t.filter(a=>!o.split(`
|
|
2
|
+
`).includes(a));if(!s.length)return;await Lt.mkdir(Rt.dirname(r),{recursive:!0}),await Lt.appendFile(r,(o&&!o.endsWith(`
|
|
3
3
|
`)?`
|
|
4
4
|
`:"")+s.join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`)}catch{}}var
|
|
6
|
+
`)}catch{}}var li=W(()=>{Os()});import Af from"node:fs/promises";import oC from"node:os";import Bl from"node:path";function sC(){let e=process.env.CODEX_HOME||Bl.join(oC.homedir(),".codex");return Bl.join(e,"config.toml")}function rk(e){return`[projects."${e}"]`}function aC(e,t){let n=rk(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 Ms(e){if(!e||typeof e!="string")return;let t=Bl.resolve(e),n=sC(),r="";try{r=await Af.readFile(n,"utf8")}catch{}if(aC(r,t))return;let o=rk(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 Cl=q(()=>{});import xf from"node:fs/promises";import Rr from"node:path";import{execFile as GM}from"node:child_process";import{promisify as zM}from"node:util";async function QM(e){try{return(await xf.stat(Rr.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function eC(e){let t=i=>xf.readFile(Rr.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 Xn(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await QM(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:Rr.basename(o),branch:await eC(o)});return}if(s>=XM)return;let i;try{i=await xf.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||ZM.has(c.name)||await t(Rr.join(o,c.name),s+1)}for(let o of YM)await t(Rr.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=await _e(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 tC(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function _e(e){if(!e)return null;if(kf.has(e))return kf.get(e);let t=null;try{let{stdout:n}=await VM("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=tC(n)}catch{t=null}return kf.set(e,t),t}async function Cs(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await Xn())if((await _e(n.path))?.toLowerCase()===t)return n.path;return null}var VM,Qb,JM,YM,XM,ZM,kf,Or=q(()=>{VM=zM(GM),Qb=process.env.HOME||"/home/globo",JM=Rr.join(process.env.BOSTRAT_STATE_DIR||Rr.join(Qb,".bostrat"),"repos"),YM=[...(process.env.BOSTRAT_REPOS_ROOT||Rr.join(Qb,"Documents")).split(","),JM].map(e=>e.trim()).filter(Boolean),XM=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,ZM=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);kf=new Map});function Ns(e=Date.now()){if(Oo)return{tripped:!1,holdMs:1/0};if(Ro>e)return{tripped:!1,holdMs:Ro-e};if((!li||e-li>=Nl.windowMs)&&(li=e,ci=0),ci++,ci<Nl.trip)return{tripped:!1,holdMs:0};Pl++;let t=Math.min(Nl.minMs*2**(Pl-1),Nl.maxMs);return Ro=e+t,ci=0,li=0,{tripped:!0,holdMs:t}}function ek(){ci=0,li=0,Pl=0,Ro=0}function ui(e=Date.now()){return Oo?1/0:Ro>e?Ro-e:0}function Ps(e=Date.now()){return ui(e)>0}function vf(e={}){Oo={at:Date.now(),...e},Ro=1/0}function Dl(){return Oo}function tk(e=Date.now()){return{held:Ps(e),holdMs:Oo?null:ui(e),holds:Pl,removed:Oo?{...Oo}:null}}var nC,Nl,ci,li,Pl,Ro,Oo,Sf=q(()=>{nC={trip:3,windowMs:3e5,minMs:3e5,maxMs:216e5},Nl={...nC},ci=0,li=0,Pl=0,Ro=0,Oo=null});import rC from"node:fs";import nk from"node:fs/promises";import di from"node:path";import oC from"node:os";import{fileURLToPath as sC}from"node:url";import{execFile as aC}from"node:child_process";import{promisify as iC}from"node:util";function Re(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 Bl(e=process.env){return!!(Re(e)&&e.BOSTRAT_PLANE_TOKEN)}async function jl(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!ok.test(n))throw Mo("repo must be owner/name",400);let r=Af.get(n);if(r&&r.expiresAt-Date.now()>uC)return r;if(!Bl(t))throw Mo("node is not plane-linked \u2014 GitHub auth unavailable",503);if(Ps()){if(r&&r.expiresAt>Date.now())return r;throw Mo(Dl()?"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 sk(`${Re(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:ak(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw Mo(`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)&&Ns(),o.status>=500&&r&&r.expiresAt>Date.now())return r;throw Mo(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 Af.set(n,a),a}async function pi({env:e=process.env,force:t=!1}={}){if(!Bl(e))return{configured:!1,repos:[],username:null};if(!t&&Mr&&Date.now()-Mr.at<dC)return Mr.data;if(Ps())return Mr?.data??{configured:!0,repos:[],username:null,error:"plane refused this node's token"};try{let n=await sk(`${Re(e)}/v1/github/repos`,{headers:ak(e)});if((n.status===401||n.status===403||n.status===410)&&Ns(),!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Mr={at:Date.now(),data:r},r}catch(n){return Mr?Mr.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function ik(){Af.clear(),Mr=null}async function Sn({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await _e(e):null);if(!r)return{};let{token:o}=await jl(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Tf({env:e=process.env}={}){return(await pi({env:e})).username??null}function mC(e){let[t,n]=String(e).split("/");return di.join(lC,t,n)}async function Ef(e,{env:t=process.env}={}){let n=(...r)=>rk("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${fC()}" ${hC(t)}`),await n("config","credential.useHttpPath","true")}async function Ll(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!ok.test(String(e||"")))throw Mo(`bad repo slug "${e}"`,400);let r=mC(e);if(rC.existsSync(di.join(r,".git")))return r;let{token:o}=await jl(e,{env:t});await nk.mkdir(di.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await rk("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:pC,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await nk.rm(r,{recursive:!0,force:!0}).catch(()=>{}),Mo(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Ef(r,{env:t}),r}var rk,cC,lC,ok,uC,dC,pC,sk,Af,Mr,ak,Mo,fC,hC,mt=q(()=>{Or();Sf();rk=iC(aC),cC=process.env.BOSTRAT_STATE_DIR||di.join(process.env.HOME||oC.tmpdir(),".bostrat"),lC=di.join(cC,"repos"),ok=/^[^/\s]+\/[^/\s]+$/,uC=5*6e4,dC=6e4,pC=10*6e4,sk=fetch,Af=new Map,Mr=null;ak=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Mo=(e,t)=>Object.assign(new Error(e),{status:t});fC=()=>sC(new URL("../bin/bostrat-git-credential",import.meta.url)),hC=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function lk(e=process.env){return!!(Re(e)&&e.BOSTRAT_PLANE_TOKEN)}async function dk(e,t,n,r=ck){let o=Re(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await gC(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function pk(e,{env:t=process.env,force:n=!1,timeoutMs:r=ck}={}){if(!lk(t))return{facts:[],source:"unconfigured"};let o=_f.get(e);if(!n&&o&&Date.now()-o.at<wC)return{facts:o.facts,source:"cache"};try{let s=await dk(`/v1/memory/${encodeURIComponent(e)}`,{headers:uk(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 _f.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 Fl(e,t,{env:n=process.env}={}){if(!lk(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await dk(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...uk(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 _f.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 wC,ck,_f,gC,uk,fk=q(()=>{mt();wC=6e4,ck=5e3,_f=new Map,gC=(...e)=>fetch(...e);uk=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import If from"node:fs/promises";import Ds from"node:path";import yC from"node:os";import{EventEmitter as bC}from"node:events";import{randomUUID as kC}from"node:crypto";function Bs(e){return e?.cwd?Ds.join(e.cwd,".bostrat","events"):Ds.join($f,"events")}async function AC(e,t){if(!Ul.has(t)){let o=(await K(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);Ul.set(t,o)}let n=Ul.get(t)+1;return Ul.set(t,n),n}function Rf(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||!xC.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!vC.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!SC.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?Ds.join($f,"events"):Bs(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=Rf(u,Number(t.payload?.ts??t.ts)||Date.now())}let s=await AC(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||kC(),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 mk(c)&&(c=wk(c),l=JSON.parse(c)),await If.mkdir(r,{recursive:!0}),await If.appendFile(hk(r,n),c+`
|
|
15
|
-
`),
|
|
16
|
-
`))if(a.trim())try{s.push(JSON.parse(
|
|
13
|
+
`:"")}try{await Af.mkdir(Bl.dirname(n),{recursive:!0}),await Af.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var jl=W(()=>{});import ok from"node:fs/promises";import iC from"node:os";import ui from"node:path";function cC(e){let n=ui.resolve(e).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||ui.join(iC.homedir(),".cursor");return ui.join(r,"projects",n)}async function Cs(e){if(!e||typeof e!="string")return;let t=ui.resolve(e),n=cC(t),r=ui.join(n,".workspace-trusted");await ok.mkdir(n,{recursive:!0}),await ok.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:t},null,2)}
|
|
14
|
+
`,"utf8")}var Ll=W(()=>{});import Ef from"node:fs/promises";import Mr from"node:path";import{execFile as lC}from"node:child_process";import{promisify as uC}from"node:util";async function wC(e){try{return(await Ef.stat(Mr.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function gC(e){let t=i=>Ef.readFile(Mr.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 er(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await wC(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:Mr.basename(o),branch:await gC(o)});return}if(s>=mC)return;let i;try{i=await Ef.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||hC.has(c.name)||await t(Mr.join(o,c.name),s+1)}for(let o of fC)await t(Mr.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=await $e(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 yC(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function $e(e){if(!e)return null;if(Tf.has(e))return Tf.get(e);let t=null;try{let{stdout:n}=await dC("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=yC(n)}catch{t=null}return Tf.set(e,t),t}async function Ns(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await er())if((await $e(n.path))?.toLowerCase()===t)return n.path;return null}var dC,sk,pC,fC,mC,hC,Tf,Cr=W(()=>{dC=uC(lC),sk=process.env.HOME||"/home/globo",pC=Mr.join(process.env.BOSTRAT_STATE_DIR||Mr.join(sk,".bostrat"),"repos"),fC=[...(process.env.BOSTRAT_REPOS_ROOT||Mr.join(sk,"Documents")).split(","),pC].map(e=>e.trim()).filter(Boolean),mC=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,hC=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);Tf=new Map});function Ps(e=Date.now()){if(Mo)return{tripped:!1,holdMs:1/0};if(Oo>e)return{tripped:!1,holdMs:Oo-e};if((!pi||e-pi>=Fl.windowMs)&&(pi=e,di=0),di++,di<Fl.trip)return{tripped:!1,holdMs:0};Ul++;let t=Math.min(Fl.minMs*2**(Ul-1),Fl.maxMs);return Oo=e+t,di=0,pi=0,{tripped:!0,holdMs:t}}function ak(){di=0,pi=0,Ul=0,Oo=0}function fi(e=Date.now()){return Mo?1/0:Oo>e?Oo-e:0}function Ds(e=Date.now()){return fi(e)>0}function _f(e={}){Mo={at:Date.now(),...e},Oo=1/0}function Hl(){return Mo}function ik(e=Date.now()){return{held:Ds(e),holdMs:Mo?null:fi(e),holds:Ul,removed:Mo?{...Mo}:null}}var bC,Fl,di,pi,Ul,Oo,Mo,If=W(()=>{bC={trip:3,windowMs:3e5,minMs:3e5,maxMs:216e5},Fl={...bC},di=0,pi=0,Ul=0,Oo=0,Mo=null});import kC from"node:fs";import ck from"node:fs/promises";import mi from"node:path";import xC from"node:os";import{fileURLToPath as vC}from"node:url";import{execFile as SC}from"node:child_process";import{promisify as AC}from"node:util";function Ce(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 Wl(e=process.env){return!!(Ce(e)&&e.BOSTRAT_PLANE_TOKEN)}function fk(e){return e?.status===404&&/not granted/i.test(String(e?.message||""))}async function Bs(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!uk.test(n))throw Co("repo must be owner/name",400);let r=$f.get(n);if(r&&r.expiresAt-Date.now()>_C)return r;if(!Wl(t))throw Co("node is not plane-linked \u2014 GitHub auth unavailable",503);if(Ds()){if(r&&r.expiresAt>Date.now())return r;throw Co(Hl()?"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 dk(`${Ce(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:pk(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw Co(`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)&&Ps(),o.status>=500&&r&&r.expiresAt>Date.now())return r;throw Co(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 $f.set(n,a),a}async function hi({env:e=process.env,force:t=!1}={}){if(!Wl(e))return{configured:!1,repos:[],username:null};if(!t&&Nr&&Date.now()-Nr.at<IC)return Nr.data;if(Ds())return Nr?.data??{configured:!0,repos:[],username:null,error:"plane refused this node's token"};try{let n=await dk(`${Ce(e)}/v1/github/repos`,{headers:pk(e)});if((n.status===401||n.status===403||n.status===410)&&Ps(),!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Nr={at:Date.now(),data:r},r}catch(n){return Nr?Nr.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function mk(){$f.clear(),Nr=null}async function En({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await $e(e):null);if(!r)return{};let{token:o}=await Bs(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Rf({env:e=process.env}={}){return(await hi({env:e})).username??null}function MC(e){let[t,n]=String(e).split("/");return mi.join(EC,t,n)}async function Of(e,{env:t=process.env}={}){let n=(...r)=>lk("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${RC()}" ${OC(t)}`),await n("config","credential.useHttpPath","true")}async function ql(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!uk.test(String(e||"")))throw Co(`bad repo slug "${e}"`,400);let r=MC(e);if(kC.existsSync(mi.join(r,".git")))return r;let{token:o}=await Bs(e,{env:t});await ck.mkdir(mi.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await lk("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:$C,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await ck.rm(r,{recursive:!0,force:!0}).catch(()=>{}),Co(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Of(r,{env:t}),r}var lk,TC,EC,uk,_C,IC,$C,dk,$f,Nr,pk,Co,RC,OC,yt=W(()=>{Cr();If();lk=AC(SC),TC=process.env.BOSTRAT_STATE_DIR||mi.join(process.env.HOME||xC.tmpdir(),".bostrat"),EC=mi.join(TC,"repos"),uk=/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?\/[A-Za-z0-9._-]{1,100}$/,_C=5*6e4,IC=6e4,$C=10*6e4,dk=fetch,$f=new Map,Nr=null;pk=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Co=(e,t)=>Object.assign(new Error(e),{status:t});RC=()=>vC(new URL("../bin/bostrat-git-credential",import.meta.url)),OC=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function wk(e=process.env){return!!(Ce(e)&&e.BOSTRAT_PLANE_TOKEN)}async function yk(e,t,n,r=hk){let o=Ce(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await NC(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function bk(e,{env:t=process.env,force:n=!1,timeoutMs:r=hk}={}){if(!wk(t))return{facts:[],source:"unconfigured"};let o=Mf.get(e);if(!n&&o&&Date.now()-o.at<CC)return{facts:o.facts,source:"cache"};try{let s=await yk(`/v1/memory/${encodeURIComponent(e)}`,{headers:gk(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 Mf.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 Kl(e,t,{env:n=process.env}={}){if(!wk(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await yk(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...gk(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 Mf.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 CC,hk,Mf,NC,gk,kk=W(()=>{yt();CC=6e4,hk=5e3,Mf=new Map,NC=(...e)=>fetch(...e);gk=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import Cf from"node:fs/promises";import js from"node:path";import PC from"node:os";import{EventEmitter as DC}from"node:events";import{randomUUID as BC}from"node:crypto";function Ls(e){return e?.cwd?js.join(e.cwd,".bostrat","events"):js.join(Nf,"events")}async function UC(e,t){if(!Gl.has(t)){let o=(await q(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);Gl.set(t,o)}let n=Gl.get(t)+1;return Gl.set(t,n),n}function Pf(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||!jC.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!LC.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!FC.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?js.join(Nf,"events"):Ls(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=Pf(u,Number(t.payload?.ts??t.ts)||Date.now())}let s=await UC(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||BC(),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 vk(c)&&(c=Sk(c),l=JSON.parse(c)),await Cf.mkdir(r,{recursive:!0}),await Cf.appendFile(xk(r,n),c+`
|
|
15
|
+
`),Xe.emit("event",l),Xe.emit(`track:${n}`,l),l}async function q(e,t){let n,r;typeof e=="string"&&t?(n=e,r=t):(r=typeof e=="string"?e:e?.id,n=typeof e=="string"?js.join(Nf,"events"):Ls(e));let o;try{o=await Cf.readFile(xk(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
16
|
+
`))if(a.trim())try{s.push(JSON.parse(vk(a)?Sk(a):a))}catch{}return s}function Ak(e,t){let n=`track:${e}`;return Xe.on(n,t),()=>Xe.off(n,t)}var jC,LC,FC,Xe,Nf,xk,Gl,HC,vk,Sk,fe=W(()=>{jC=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"]),LC=new Set(["user","agent","orchestrator","system","peer"]),FC=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor","cli"]),Xe=new DC;Xe.setMaxListeners(0);Nf=process.env.BOSTRAT_STATE_DIR||js.join(process.env.HOME||PC.tmpdir(),".bostrat");xk=(e,t)=>js.join(e,`${t}.ndjson`),Gl=new Map;HC=/(?<!\\)((?:\\\\)*)\\u0000/g,vk=e=>e.includes("\\u0000"),Sk=e=>e.replace(HC,"$1\\ufffd")});import ie from"node:fs/promises";import WC from"node:os";import ve from"node:path";import{execFile as qC}from"node:child_process";import{promisify as KC}from"node:util";function zl(){return ve.join(Jl,"memory","repos")}function No(e){return e===tr?ve.join(Jl,"memory",tr):ve.join(zl(),e)}async function gi(e){try{return JSON.parse(await ie.readFile(ve.join(e,Ok),"utf8"))}catch{return{}}}async function Bf(e,t){let n=await gi(e);for(let[r,o]of t)n[r]=Math.max(Number(n[r])||0,o);await ie.mkdir(e,{recursive:!0}),await ie.writeFile(ve.join(e,Ok),JSON.stringify(n))}function Ek(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 _k(e){try{let{stdout:t}=await GC("git",["-C",e,"config","--get","remote.origin.url"]);return t.trim()||null}catch{return null}}async function jf(e){if(!e)return[];let t=Ek(await _k(e));if(t)return[t];let n=[],r=[];try{r=await ie.readdir(e,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=Ek(await _k(ve.join(e,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function Yl(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 Pr(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 ie.readFile(e,"utf8").catch(()=>""),{fm:r}=Yl(n),o=Pr(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await ie.stat(e)).mtimeMs}catch{return 0}}function Mk(e,t){let n=Pr(e,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===tr){let o=Pr(e,"trust");if(!o||/^operator/i.test(o))return tr}return t}function Lf(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=Us(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=Us(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 Ck(e,t){let n=Pr(t,"name")||e.replace(/\.md$/,""),r=Pr(t,"description")||"";return`- [${n}](${e})${r?` \u2014 ${r}`:""}`}async function yi(e){let t=[];try{t=await ie.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 Nk(e,t){let n=ve.join(e,".bostrat","memory"),r=await yi(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=Ik.get(e)??0,s=Date.now(),a=new Map,i=new Map,c=0,l=new Map;for(let d of r){let p=ve.join(n,d);if(o)try{if((await ie.stat(p)).mtimeMs<o-zC)continue}catch{continue}let f;try{f=await ie.readFile(p,"utf8")}catch{continue}let{fm:w}=Yl(f),m=Mk(w,t);if(!m)continue;let g=No(m),y=ve.join(g,d),k=await _n(p,f);if(l.has(g)||l.set(g,await gi(g)),(l.get(g)[d]||0)>=k)continue;let b=await _n(y).catch(()=>0);if(await ie.access(y).then(()=>!0).catch(()=>!1)&&b>=k)continue;await ie.mkdir(g,{recursive:!0}),await ie.writeFile(y,f),c++;let R=a.get(g);R||a.set(g,R={won:new Set,generated:new Map}),R.won.add(d),R.generated.set(d,Ck(d,w)),i.has(m)||i.set(m,new Set),i.get(m).add(d)}let u=await ie.readFile(ve.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[d,p]of a){let f=ve.join(d,"MEMORY.md"),w=await ie.readFile(f,"utf8").catch(()=>"");await ie.writeFile(f,Lf(w,u,p.won,p.generated))}return Ik.set(e,s),{harvested:c,changedByKey:i}}async function Pk(e){let t=0,n=new Map;for(let r of e||[]){if(!r||!await ie.access(ve.join(r,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[s]=await jf(r),a=await Nk(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 JC(){let e=ve.join(Jl,"workspaces"),t=[];try{t=await ie.readdir(e,{withFileTypes:!0})}catch{return{harvested:0,changedByKey:new Map}}return Pk(t.filter(n=>n.isDirectory()).map(n=>ve.join(e,n.name)))}async function ZC(e,t){let n=No(e),r=await gi(n),o=new Set,s=new Map,a=[],i=0;for(let c of t||[]){let l=String(c?.file||"");if(!YC.test(l)||l==="MEMORY.md")continue;let u=ve.join(n,l),d=await ie.access(u).then(()=>!0).catch(()=>!1),p=d?await _n(u):0,f=Number(c.modified)||0;if(c.deleted){f>p&&(d&&await ie.rm(u,{force:!0}),await Bf(n,[[l,f]]),d&&(a.push(l),i++));continue}d&&p>=f||(r[l]||0)>=f||(await ie.mkdir(n,{recursive:!0}),await ie.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=ve.join(n,"MEMORY.md"),l=await ie.readFile(c,"utf8").catch(()=>""),u=Lf(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
|
|
25
|
-
`);t.length>
|
|
26
|
-
`);for(;Buffer.byteLength(n,"utf8")>
|
|
27
|
-
`);return n}async function
|
|
23
|
+
`).filter(p=>!d.has(Us(p))).join(`
|
|
24
|
+
`)}await ie.mkdir(n,{recursive:!0}),await ie.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function Xl(e,t=null){let n=No(e),r=t?.length?t:await yi(n);if(!r.length)return{pushed:0,source:"empty"};let o=XC(await ie.readFile(ve.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=ve.join(n,a),c;try{c=await ie.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await _n(i,c),indexLine:o.get(a)??null})}return Kl(e,s)}async function QC(e,{timeoutMs:t}={}){let{facts:n,source:r}=await bk(e,t?{timeoutMs:t}:{});n.length?await ZC(e,n):r==="plane"&&await Xl(e)}function eN(e){let t=e.split(`
|
|
25
|
+
`);t.length>Tk&&(t=t.slice(0,Tk));let n=t.join(`
|
|
26
|
+
`);for(;Buffer.byteLength(n,"utf8")>VC&&t.length;)t.pop(),n=t.join(`
|
|
27
|
+
`);return n}async function nN(e,t){if(!e||!t?.length)return{materialized:!1,reason:"no-repo-key"};let n=ve.join(e,".bostrat","memory"),r=0,o=0,s=[];for(let u of t){let d=No(u),p=await gi(d);for(let[g,y]of Object.entries(p)){let k=ve.join(n,g);await ie.access(k).then(()=>!0).catch(()=>!1)&&await _n(k)<=y&&(await ie.rm(k,{force:!0}),o++)}let f=await yi(d);if(!f.length)continue;await ie.mkdir(n,{recursive:!0});for(let g of f){let y=ve.join(d,g),k=ve.join(n,g),b=await _n(y),I=await _n(k).catch(()=>0);await ie.access(k).then(()=>!0).catch(()=>!1)&&I>=b||(await ie.copyFile(y,k),r++)}let w=await ie.readFile(ve.join(d,"MEMORY.md"),"utf8").catch(()=>""),m=u===tr?"## 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
|
|
35
|
-
`).filter(l=>
|
|
36
|
-
`)).catch(()=>{});let c=await
|
|
37
|
-
`)){let r=
|
|
32
|
+
`,c=eN(i);await ie.mkdir(n,{recursive:!0}),await ie.writeFile(ve.join(n,"MEMORY.md"),c);let l=ve.join(n,"claude");return await ie.mkdir(l,{recursive:!0}),await ie.writeFile(ve.join(l,"MEMORY.md"),tN,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function Dk(e){try{let t=await jf(e);if(!t.length)return{materialized:!1,reason:"no-repo-key"};let n=[...t,tr];return await Promise.all(n.map(r=>QC(r,{timeoutMs:rN}))),await nN(e,n)}catch(t){return{materialized:!1,reason:"error",error:t?.message||String(t)}}}function Bk(){return{...at,pending:Fs.size,queued:Df.size}}function jk(e){if(Vl.has(e))return Vl.get(e);Df.add(e);let t=$k.then(async()=>{Df.delete(e),Vl.delete(e);let n=await Ff(e);return at.runs++,at.lastRunAt=Date.now(),n.error?(at.errors++,at.lastError=n.error):at.harvested+=n.harvested||0,n}).catch(n=>({harvested:0,error:n?.message||String(n)}));return Vl.set(e,t),$k=t.then(()=>{}),t}function Zl(e,{delayMs:t=oN}={}){if(!e||typeof e!="string")return;at.scheduled++;let n=Fs.get(e);n&&clearTimeout(n);let r=setTimeout(()=>{Fs.delete(e),jk(e)},t);r.unref?.(),Fs.set(e,r)}async function Ql(e,{timeoutMs:t=3e3}={}){if(!e)return{harvested:0,skipped:"no-cwd"};if(!await ie.access(ve.join(e,".bostrat","memory")).then(()=>!0,()=>!1))return{harvested:0,skipped:"no-memory-dir"};let r=Fs.get(e);r&&(clearTimeout(r),Fs.delete(e));let o,s=new Promise(a=>{o=setTimeout(()=>a({harvested:0,timedOut:!0}),t),o.unref?.()});try{return await Promise.race([jk(e),s])}finally{clearTimeout(o)}}async function Lk(e){return wi||(wi=(async()=>{try{Rk||(Rk=!0,await aN());let{harvested:t,changedByKey:n}=await Pk(e);for(let[r,o]of n)await Xl(r,[...o]);return at.reconciles++,at.lastReconcileAt=Date.now(),at.harvested+=t,{harvested:t,keys:[...n.keys()]}}catch(t){return at.errors++,at.lastError=t?.message||String(t),{harvested:0,error:at.lastError}}finally{wi=null}})(),wi)}function sN(){return ve.join(Jl,"memory",".full-sweep-done")}async function Fk(){let e=sN();try{let t=JSON.parse(await ie.readFile(e,"utf8"));return at.backfill={at:Number(t?.at)||0,harvested:Number(t?.harvested)||0,prior:!0},{ran:!1,...at.backfill}}catch{}try{let{harvested:t,changedByKey:n}=await JC();for(let[o,s]of n)await Xl(o,[...s]);let r={at:Date.now(),harvested:t};return await ie.mkdir(ve.dirname(e),{recursive:!0}),await ie.writeFile(e,JSON.stringify(r)),at.backfill=r,{ran:!0,...r}}catch(t){return at.errors++,at.lastError=t?.message||String(t),{ran:!1,error:at.lastError}}}async function aN(){let e=0,t=[];try{t=await ie.readdir(zl(),{withFileTypes:!0})}catch{return{moved:e}}for(let n of t){if(!n.isDirectory())continue;let r=ve.join(zl(),n.name),o=No(tr),s=new Set,a=new Map;for(let f of await yi(r)){let w=ve.join(r,f),m;try{m=await ie.readFile(w,"utf8")}catch{continue}let{fm:g}=Yl(m);if(Mk(g,n.name)!==tr)continue;let y=ve.join(o,f),k=await _n(y).catch(()=>0),b=await _n(w,m);await ie.mkdir(o,{recursive:!0}),b>k&&await ie.writeFile(y,m),await ie.rm(w,{force:!0}),s.add(f),a.set(f,Ck(f,g)),e++}if(!s.size)continue;let i=Date.now();await Bf(r,[...s].map(f=>[f,i])),await Kl(n.name,[...s].map(f=>({file:f,deleted:!0,modified:i})));let c=ve.join(r,"MEMORY.md"),l=await ie.readFile(c,"utf8").catch(()=>""),u=l.split(`
|
|
33
|
+
`).filter(f=>!s.has(Us(f))).join(`
|
|
34
|
+
`);await ie.writeFile(c,u).catch(()=>{});let d=ve.join(o,"MEMORY.md"),p=await ie.readFile(d,"utf8").catch(()=>"");await ie.writeFile(d,Lf(p,l,s,a))}return{moved:e}}async function Uk(){let e=[],t=[tr];try{for(let n of await ie.readdir(zl(),{withFileTypes:!0}))n.isDirectory()&&t.push(n.name)}catch{}for(let n of t){let r=No(n),o=await yi(r),s=await gi(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await ie.readFile(ve.join(r,i),"utf8").catch(()=>""),{fm:l}=Yl(c);a.push({file:i,name:Pr(l,"name")||i.replace(/\.md$/,""),description:Pr(l,"description")||"",scope:Pr(l,"scope")||null,modified:await _n(ve.join(r,i),c)})}e.push({key:n,facts:a,tombstones:Object.keys(s).length})}return e}async function Hk(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=No(e),r=ve.join(n,t);if(!await ie.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${t} not found in ${e}`);let s=Date.now();await ie.rm(r,{force:!0}),await Bf(n,[[t,s]]);let a=ve.join(n,"MEMORY.md"),i=await ie.readFile(a,"utf8").catch(()=>"");await ie.writeFile(a,i.split(`
|
|
35
|
+
`).filter(l=>Us(l)!==t).join(`
|
|
36
|
+
`)).catch(()=>{});let c=await Kl(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function Ff(e){try{let t=await jf(e),{harvested:n,changedByKey:r}=await Nk(e,t[0]||null);for(let[o,s]of r)await Xl(o,[...s]);return n>0&&await cN(e,r).catch(()=>{}),{harvested:n}}catch(t){return{harvested:0,error:t?.message||String(t)}}}async function cN(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,iN)}}await C({id:n,cwd:e},{type:"memory.harvested",actor:"orchestrator",payload:{harvested:o,keys:r,ts:Date.now()}})}var GC,Jl,Tk,VC,tr,Ok,Us,Ik,zC,YC,XC,tN,rN,oN,Fs,Df,$k,at,Vl,Rk,wi,iN,bi=W(()=>{kk();fe();GC=KC(qC),Jl=process.env.BOSTRAT_STATE_DIR||ve.join(process.env.HOME||WC.tmpdir(),".bostrat"),Tk=200,VC=25*1024;tr="operator";Ok=".tombstones.json";Us=e=>/\]\(([^)]+\.md)\)/.exec(e)?.[1]||null;Ik=new Map,zC=2e3;YC=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,XC=e=>{let t=new Map;for(let n of(e||"").split(`
|
|
37
|
+
`)){let r=Us(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};tN=`# 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
|
|
44
|
-
`,"utf8");let n=(await
|
|
45
|
-
`).filter(Boolean);n.length>
|
|
42
|
+
`;rN=Number(process.env.BOSTRAT_MEMORY_SPAWN_PULL_MS)||1500;oN=Number(process.env.BOSTRAT_MEMORY_HARVEST_DEBOUNCE_MS)||2e3,Fs=new Map,Df=new Set,$k=Promise.resolve(),at={scheduled:0,runs:0,harvested:0,errors:0,lastRunAt:null,lastError:null,reconciles:0,lastReconcileAt:null,backfill:null};Vl=new Map;Rk=!1,wi=null;iN=20});import eu from"node:fs/promises";import lN from"node:path";async function Ot(e,t,{mode:n}={}){await eu.mkdir(lN.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${++uN}.tmp`;try{await eu.writeFile(r,JSON.stringify(t,null,2)+`
|
|
43
|
+
`,n!=null?{mode:n}:void 0),await eu.rename(r,e)}catch(o){throw await eu.unlink(r).catch(()=>{}),o}}var uN,Dr=W(()=>{uN=0});import Vk from"node:fs/promises";import zk from"node:os";import Jk from"node:path";function Ke(e=process.env){return!!(Ce(e)&&e.BOSTRAT_PLANE_TOKEN)}async function vi(e,t={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),dN);try{return await wN(`${Ce(n)}${e}`,{...t,signal:r.signal})}finally{clearTimeout(o)}}async function Qk(){return nu||(Uf||=Vk.readFile(Yk(),"utf8").then(e=>JSON.parse(e)).catch(()=>({ops:[]})).then(e=>nu={ops:Array.isArray(e?.ops)?e.ops:[],parked:Array.isArray(e?.parked)?e.parked:[]}).finally(()=>Uf=null),Uf)}async function ru(){await Ot(Yk(),nu).catch(()=>{})}function Kk(e){return e.op==="blob"?`blob ${e.family} ${e.id} ${e.v} ${e.kind}`:`${e.op} ${e.family} ${e.id}`}async function Wf(e){let t=await Qk(),n=Kk(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=>Kk(o)===n);r>=0?t.ops[r]=e:t.ops.push(e)}t.ops.length>Wk&&(t.ops=t.ops.slice(-Wk)),await ru()}function Gk(){tu||(tu=setTimeout(()=>{tu=null,Hs().catch(()=>{})},gN),tu.unref?.())}function Hf(e){let t=new Error(`plane ${e}`);return t.refused=!0,t}async function bN(e,t=process.env){if(e.op==="record"){let n=await vi(`/v1/content/${encodeURIComponent(e.family)}`,{method:"POST",headers:{...xi(t),"content-type":"application/json"},body:JSON.stringify({records:[{id:e.id,trackId:e.trackId??null,data:e.data,updatedAt:e.updatedAt,originNode:Xk()}]})},t);if(!n.ok)throw Hf(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 Vk.readFile(e.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await vi(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}/blob?v=${e.v}&kind=${encodeURIComponent(e.kind)}`,{method:"POST",headers:{...xi(t),"content-type":"application/octet-stream"},body:n},t);if(!r.ok&&r.status!==400)throw Hf(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 vi(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}?deletedAt=${e.deletedAt}`,{method:"DELETE",headers:xi(t)},t);if(!n.ok)throw Hf(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function Hs({env:e=process.env,log:t=console,now:n=Date.now()}={}){return Ke(e)?ki||(ki=(async()=>{let r=await Qk();kN(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await bN(a,e)}catch(c){if(!c?.refused)return Gk(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<fN)return Gk(),await ru(),{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>qk&&(r.parked=r.parked.slice(-qk)),s++,await ru();continue}i.rejected&&t.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await ru()}return{flushed:o,parked:s,pending:0}})().finally(()=>{ki=null}),ki):{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)>=yN);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 Ke(r)?(await Wf({op:"record",family:e,id:t.id,trackId:n??t.trackId??null,data:t,updatedAt:Number(t.updatedAt)||Date.now()}),Hs({env:r})):{queued:!1,source:"unconfigured"}}async function Br(e,t,n,r,o,{env:s=process.env}={}){return Ke(s)?(await Wf({op:"blob",family:e,id:t,v:Number(n),kind:r,path:o}),Hs({env:s})):{queued:!1,source:"unconfigured"}}async function an(e,t,{deletedAt:n=Date.now(),env:r=process.env}={}){return Ke(r)?(await Wf({op:"delete",family:e,id:t,deletedAt:n}),Hs({env:r})):{queued:!1,source:"unconfigured"}}async function dt(e,{env:t=process.env}={}){if(!Ke(t))return{records:[],source:"unconfigured"};try{let n=await vi(`/v1/content/${encodeURIComponent(e)}?tombstones=1`,{headers:xi(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 Si(e,t,n,r,{env:o=process.env}={}){if(!Ke(o))return null;try{let s=await vi(`/v1/content/${encodeURIComponent(e)}/${encodeURIComponent(t)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:xi(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 dN,pN,Wk,fN,qk,mN,hN,Yk,Xk,Zk,wN,gN,yN,xi,nu,Uf,tu,ki,jr=W(()=>{yt();Dr();dN=1e4,pN=3e4,Wk=2e3,fN=5,qk=100,mN=60*6e4,hN=()=>process.env.BOSTRAT_STATE_DIR||Jk.join(process.env.HOME||zk.tmpdir(),".bostrat"),Yk=()=>Jk.join(hN(),"content-outbox.json"),Xk=()=>process.env.BOSTRAT_NODE_ID||zk.hostname(),Zk=()=>Xk(),wN=(...e)=>fetch(...e),gN=pN,yN=mN;xi=(e=process.env)=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`});nu=null,Uf=null,tu=null,ki=null});import qf from"node:fs/promises";import e0 from"node:path";async function t0(e,t,{box:n=null,agent:r=null}={}){if(!e||!Ke())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 Br("checkpoints",e,o,"md",t),Ai=null,{pushed:!0,v:o}}catch{return{pushed:!1}}}async function TN(){if(Ai&&Date.now()-Ai.at<AN)return{records:Ai.records,source:"plane"};let e=await dt("checkpoints");return e.source==="plane"&&(Ai={at:Date.now(),records:e.records}),e}async function ou(e){try{let t=SN(e);if(!t||!Ke())return{restored:!1};let n=e0.join(e,xN,vN);if(await qf.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await TN();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 Si("checkpoints",t,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await qf.mkdir(e0.dirname(n),{recursive:!0}),await qf.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var xN,vN,SN,AN,Ai,su=W(()=>{jr();xN=".bostrat/handoff",vN="latest.md",SN=e=>/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1]??null;AN=6e4,Ai=null});import Ti from"node:fs/promises";import EN from"node:os";import r0 from"node:path";function Po(e){let t=String(e??"");for(let n of _N)t=t.replace(n,"\u2039redacted\u203A");return t}function au(){let e=process.env.BOSTRAT_HANDOFF_DEBUG;return e==="1"||e==="true"}async function IN(e){try{await Ti.mkdir(Kf,{recursive:!0}),await Ti.appendFile(Ws,`${JSON.stringify(e)}
|
|
44
|
+
`,"utf8");let n=(await Ti.readFile(Ws,"utf8")).split(`
|
|
45
|
+
`).filter(Boolean);n.length>n0&&await Ti.writeFile(Ws,`${n.slice(-n0).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=
|
|
49
|
-
`,r=
|
|
47
|
+
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${Ws}: ${t?.message||t}`)}}async function de(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&&au())try{t.paneTail=Po((await Pe(t.sessionName,{lines:35})).slice(-4e3))}catch{t.paneTail=null}(au()||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 IN(t)}async function o0({limit:e=80}={}){try{return(await Ti.readFile(Ws,"utf8")).split(`
|
|
48
|
+
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Po(o.paneTail)),o}catch{return{raw:Po(r)}}})}catch{return[]}}var _N,Kf,Ws,n0,s0,In=W(()=>{mt();_N=[/[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];Kf=process.env.BOSTRAT_DEBUG_DIR||r0.join(EN.homedir(),".bostrat","debug"),Ws=r0.join(Kf,"handoff.jsonl"),n0=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;s0={DEBUG_DIR:Kf,LOG_FILE:Ws}});import iu from"node:fs/promises";import Gf from"node:path";import $N from"node:os";async function i0(e=a0){await iu.mkdir(e,{recursive:!0});for(let t of ON){let n=t==="prepare-commit-msg"?MN:t==="post-commit"?CN:`${Vf}exit 0
|
|
49
|
+
`,r=Gf.join(e,t),o=null;try{o=await iu.readFile(r,"utf8")}catch{}o!==n&&await iu.writeFile(r,n,{mode:493}),await iu.chmod(r,493).catch(()=>{})}return e}function c0({nodeUrl:e,dir:t=a0,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",`${Ei}=${e}`]}function l0(e=process.env){return e.BOSTRAT_NODE_URL||`http://127.0.0.1:${e.PORT||3362}`}var Ei,RN,a0,ON,Vf,MN,CN,zf=W(()=>{Os();Ei="BOSTRAT_NODE_URL",RN=process.env.BOSTRAT_STATE_DIR||Gf.join(process.env.HOME||$N.tmpdir(),".bostrat"),a0=Gf.join(RN,"git-hooks"),ON=["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"],Vf=`#!/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
|
+
`,MN=`${Vf}
|
|
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
|
-
[ -z "$${
|
|
68
|
-
[ -z "$${
|
|
67
|
+
[ -z "$${Zn}" ] && exit 0
|
|
68
|
+
[ -z "$${Ei}" ] && exit 0
|
|
69
69
|
[ "$2" = "merge" ] && exit 0
|
|
70
|
-
trailers=$(curl -sS -m 3 -H "${
|
|
70
|
+
trailers=$(curl -sS -m 3 -H "${Qn}: $${Zn}" "$${Ei}/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
|
+
`,CN=`${Vf}
|
|
77
77
|
# \u2500\u2500 bostrat: report the commit (code.committed + rework analysis, node-side).
|
|
78
|
-
[ -z "$${
|
|
79
|
-
[ -z "$${
|
|
78
|
+
[ -z "$${Zn}" ] && exit 0
|
|
79
|
+
[ -z "$${Ei}" ] && 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 "$${
|
|
83
|
-
-H 'content-type: application/json' -H "${
|
|
82
|
+
printf '{"sha":"%s","cwd":"%s"}' "$sha" "$cwd" | curl -sS -m 5 -X POST "$${Ei}/api/git/committed" \\
|
|
83
|
+
-H 'content-type: application/json' -H "${Qn}: $${Zn}" --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("|"),
|
|
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
|
|
90
|
-
`);for(let n=t.length-1;n>=0;n--){let r=t[n].trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a=(s.input_tokens||0)+(s.cache_creation_input_tokens||0)+(s.cache_read_input_tokens||0);if(a>0)return{occupancy:a,model:o.message.model||o.model||null}}return null}async function
|
|
91
|
-
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function
|
|
92
|
-
`,n);i!==-1&&(t=t.slice(0,i))}let r=
|
|
93
|
-
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function
|
|
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"&&
|
|
99
|
-
`).trim():"";
|
|
100
|
-
`).trim():""}function
|
|
101
|
-
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type==="attachment"){let y=l.attachment;y?.type==="queued_command"&&y.commandMode==="task-notification"&&typeof y.prompt=="string"&&y.prompt.trim()&&o.push({role:"user",text:y.prompt.trim(),ts:l.timestamp?Date.parse(l.timestamp):null,model:null});continue}let u=l.message;if(!u)continue;let d=u.role||l.type;if(d!=="user"&&d!=="assistant"||d==="assistant"&&u.model==="<synthetic>"&&!l.isApiErrorMessage)continue;if(d==="assistant"&&Array.isArray(u.content))for(let y of u.content){let
|
|
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 d0}from"node:child_process";import{promisify as NN}from"node:util";import lu from"node:fs";import{randomUUID as p0}from"node:crypto";function Do(e){return new Promise(t=>setTimeout(t,e))}function cu(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 f0(e){return`/tmp/tmux-${e}/default`}function PN({explicit:e,legacyExists:t,runtimeDir:n,uid:r}){if(e)return e;let o=f0(r);return t?o:n?`${n}/bostrat/tmux-default`:o}function Ii(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${_i}=1`]}function jN(){return BN.flatMap(e=>["-e",`${e}=`])}function $i(e,t,n){let r=Bo(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&&De(n)&&(s+=` --name ${n}`),s}function Bo(e){return/(^|\/|\s)claude(\s|$)/.test(String(e||""))}function Lr(e){return/(^|\/|\s)claude(\s|$)/.test(e)?"claude":/cursor/.test(e)?"cursor":/codex/.test(e)?"codex":/gemini/.test(e)?"gemini":"agent"}function FN(e){let t=String(e||"").trim();return!t||/fake-|\.sh(\s|$)/.test(t)?!1:Bo(t)||/(^|\/|\s)codex(\s|$)/.test(t)||/cursor-agent/.test(t)||/(^|\/|\s)gemini(\s|$)/.test(t)}async function m0(e,{loginShell:t=!1,failOpen:n=!1}={}){let r=await h0(e,{loginShell:t});return r===uu?!0:r===qs?!1:n}async function h0(e,{loginShell:t=!1,timeoutMs:n=Jf}={}){if(!e)return qs;let r=t?process.env.SHELL||"/bin/sh":"sh",o=t?"-lic":"-c";try{return await Re(r,[o,'command -v "$1" >/dev/null 2>&1',"_",e],{timeout:n}),uu}catch(s){return s?.killed||s?.signal?Ks:s?.code===1||s?.code===127?qs:Ks}}async function du(e,{retries:t=UN,sleep:n=HN,probe:r=h0}={}){let o=String(e||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:Jf});for(let a=0;s===Ks&&a<t.length;a++)await n(t[a]),s=await r(o,{timeoutMs:Jf*2});return s}function re(...e){return["-S",Ft,...e]}async function Fr(){if(!lu.existsSync(Ft))return!1;try{return await Re(ne,re("list-sessions")),!0}catch(e){let t=e&&e.stderr?String(e.stderr):"";return!/no server running/i.test(t)}}async function Ri(){if(await Fr())return!0;try{lu.mkdirSync(Ft.replace(/\/[^/]+$/,""),{recursive:!0}),await Re(ne,re("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await Fr()}function De(e){return typeof e=="string"&&WN.test(e)}async function cn(){if(!lu.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 Re(ne,re("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=>!LN.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function ke(e){return(await cn()).find(n=>n.name===e)||null}function GN(e){if(typeof e!="string")return!1;let t=e.trim();return t.length>=1&&t.length<=KN&&qN.test(t)}function pu(e){return e.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function w0(e){try{let{stdout:t}=await Re(ne,re("list-sessions","-F","#{session_name}"));return t.split(`
|
|
87
|
+
`).map(n=>n.trim()).includes(e)}catch{return!1}}function JN(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 YN(e,t){let n=Date.now()+Oi(),r=JN(t),o=!1,s=0,a=0;for(;;){if(await Do(VN),s+=1,!await w0(e))return;let i=await Pe(e,{lines:15}),c=!o&&zN.test(i)&&!!r?.test(i),l=!o&&!!r?.test(i);if(!c&&!l&&s>1&&!await nr(e)){if(a+=1,a>1)return}else c||(a=0);if(c){o=!0,await de({flow:"spawn",phase:"launch-retry",sessionName:e,agent:Lr(t),promptHead:t.slice(0,120)}).catch(()=>{}),await Re(ne,re("send-keys","-t",e,"C-u")),await Re(ne,re("send-keys","-t",e,"-l",t)),await Re(ne,re("send-keys","-t",e,"Enter"));continue}if(Date.now()>n){await de({flow:"spawn",phase:"launch-failed",sessionName:e,agent:Lr(t),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function $n(e,t=DN,n={}){if(!GN(e))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=e.trim(),o=pu(r);if(!De(o))throw new Error("resulting tmux session name is invalid");if(!await Ri())throw new Error(`host tmux server not running on ${Ft} and auto-start failed \u2014 start one: ${ne} -S ${Ft} new-session -d -s keeper "sleep 2147483647"`);if(await w0(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||Yf,a=n.agent||Lr(s),i=!!n.resumeSessionId,c=n.resumeSessionId||p0(),l=i?s:$i(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),d=a==="claude"&&Bo(s),p=n.firstRun===!0;p||(d&&await Or(t),a==="codex"&&await Ms(t),a==="cursor"&&await Cs(t),await Dk(t),n.freshWorkspace!==!0&&await ou(t));let f=d&&!p?Dl():null,w=Qb(),m=["-e",`${Zn}=${w}`];m.push(...c0({nodeUrl:l0()})),m.push(...jN()),f&&m.push("-e",`ANTHROPIC_API_KEY=${f}`),d&&m.push(...Ii());try{await Re(ne,re("new-session","-d","-s",o,"-c",t,...m))}catch(g){throw cu(f?cu(g,f):g,w)}return await jo(o),await Re(ne,re("set-option","-t",o,"@bostrat-session","1")),await Re(ne,re("set-option","-t",o,"@bostrat-title",r)),await Re(ne,re("set-option","-t",o,"@bostrat-sid",c)),await Re(ne,re("set-option","-t",o,"@bostrat-agent",a)),await Re(ne,re("send-keys","-t",o,l,"Enter")),FN(l)&&YN(o,l).catch(()=>{}),{name:o,title:r,cwd:t,sessionId:c,agent:a,pinned:u,hookToken:w}}async function Mt(e){if(!De(e))throw new Error("invalid session name");await Re(ne,re("kill-session","-t",e))}async function g0(e,t){if(De(e))try{let n=t?re("set-option","-t",e,"@bostrat-sid",t):re("set-option","-u","-t",e,"@bostrat-sid");await Re(ne,n)}catch{}}async function Pe(e,{lines:t=40}={}){if(!De(e))return"";try{let n=re("capture-pane","-t",e,"-p");Number.isFinite(t)&&t>0&&n.push("-S",`-${Math.floor(t)}`);let{stdout:r}=await Re(ne,n);return String(r||"")}catch{return""}}async function y0(e){if(!De(e))return[];try{let{stdout:t}=await Re(ne,re("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 b0(e){if(!De(e))return 0;try{let{stdout:t}=await Re(ne,re("list-clients","-t",e,"-F","1"));return String(t||"").trim().split(`
|
|
89
|
+
`).filter(Boolean).length}catch{return 0}}async function jo(e,{cols:t=QN,rows:n=eP}={}){if(!De(e))return!0;if(await b0(e))return!1;try{let r=await k0(e);if(r.cols===t&&r.rows===n)return!0;await Re(ne,re("resize-window","-t",e,"-x",String(t),"-y",String(n))),await Re(ne,re("set-window-option","-t",e,"window-size","latest")),await Do(80)}catch{}return!0}async function tP(e){if(!De(e))return 0;try{let{stdout:t}=await Re(ne,re("display-message","-t",e,"-p","#{pane_pid}"));return Number(String(t||"").trim())||0}catch{return 0}}async function nr(e){let t=await tP(e);if(!t)return!1;try{return await Re("pgrep",["-P",String(t)]),!1}catch(n){return n?.code===1}}async function k0(e){if(!De(e))return{cols:0,rows:0};try{let{stdout:t}=await Re(ne,re("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 fu(e,{cols:t=XN,rows:n=ZN}={}){if(De(e)&&!await b0(e))try{let r=await k0(e),o=Math.max(r.cols,t),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await Re(ne,re("resize-window","-t",e,"-x",String(o),"-y",String(s))),await Re(ne,re("set-window-option","-t",e,"window-size","latest")),await Do(80))}catch{}}async function ln(e){return await fu(e),Pe(e,{lines:et})}async function ze(e,t,{settleMs:n=40,kind:r}={}){if(!De(e))throw new Error("invalid session name");let o=(Array.isArray(t)?t:[t]).filter(s=>x0.has(s));for(let s=0;s<o.length;s++)await Re(ne,re("send-keys","-t",e,o[s])),n>0&&s<o.length-1&&await Do(n);return de({flow:"tmux",phase:"send-keys",sessionName:e,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function Qf(e,t,{settleMs:n=40,kind:r}={}){if(!De(e))throw new Error("invalid session name");let o=(Array.isArray(t)?t:[]).filter(s=>s&&(typeof s.key=="string"&&x0.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 Re(ne,re("send-keys","-t",e,"-l",a.text)):await Re(ne,re("send-keys","-t",e,a.key)),n>0&&s<o.length-1&&await Do(n)}return de({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 Gs(e,t,{kind:n}={}){if(!De(e))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(t)||0));return r>0&&await Re(ne,re("send-keys","-t",e,"-N",String(r),"BSpace")),de({flow:"tmux",phase:"send-keys",sessionName:e,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function hu(e,t,{perCharDelayMs:n=120,kind:r}={}){if(!De(e))throw new Error("invalid session name");let o=[...String(t)];for(let s of o)await Re(ne,re("send-keys","-t",e,"-l",s)),n>0&&await Do(n);de({flow:"tmux",phase:"type",sessionName:e,kind:r||"type",textLen:o.length,textHead:String(t).slice(0,120)}).catch(()=>{})}function nP(e,t){return new Promise((n,r)=>{let o=d0(ne,re(...e),(s,a,i)=>{s?r(s):n({stdout:a,stderr:i})});o.stdin.on("error",()=>{}),o.stdin.end(t)})}async function rP(e,t){let n=`bostrat-${p0().slice(0,8)}`;await nP(["load-buffer","-b",n,"-"],t),await Re(ne,re("paste-buffer","-p","-d","-b",n,"-t",e))}async function it(e,t,{enter:n=!0,submitDelayMs:r=0,kind:o,paste:s=!1}={}){if(!De(e))throw new Error("invalid session name");if(typeof t=="string"&&t.length>0&&(s?await rP(e,t):await Re(ne,re("send-keys","-t",e,"-l",t))),n){let a=Number(r);Number.isFinite(a)&&a>0&&await Do(a),await Re(ne,re("send-keys","-t",e,"Enter"))}de({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 Re,ne,u0,Ft,DN,Yf,_i,BN,Xf,LN,uu,qs,Ks,Jf,UN,HN,Zf,WN,qN,KN,Oi,VN,zN,XN,ZN,et,QN,eP,x0,mu,mt=W(()=>{li();jl();Ll();bi();su();In();Os();zf();Re=NN(d0);ne=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";u0=typeof process.getuid=="function"?process.getuid():0,Ft=PN({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return lu.existsSync(f0(u0))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:u0}),DN=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",Yf=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",_i="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";BN=Object.freeze(["BOSTRAT_PLANE_URL","BOSTRAT_PLANE_TOKEN"]);Xf="bostrat-usage-probe",LN=new Set([Xf]);uu="present",qs="absent",Ks="unknown",Jf=5e3,UN=[250,1e3,3e3],HN=e=>new Promise(t=>setTimeout(t,e));Zf={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"};WN=/^[A-Za-z0-9_.-]{1,64}$/;qN=/^[A-Za-z0-9 ._↻-]+$/,KN=64;Oi=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,VN=1e3,zN=/not found|Unknown command/i;XN=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,ZN=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,et=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,QN=Number(process.env.BOSTRAT_BOX_COLS)||120,eP=Number(process.env.BOSTRAT_BOX_ROWS)||40;x0=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);mu=1600});import Mi from"node:fs";import lP from"node:fs/promises";import Vs from"node:path";import uP from"node:os";function nm(e){return e.replace(/[^A-Za-z0-9]/g,"-")}function rm(e){let t=Vs.join(tm,nm(e)),n;try{n=Mi.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=Vs.join(t,o);try{let{mtimeMs:a}=Mi.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function un(e,t){if(!t)return null;let n=Vs.join(tm,nm(e),`${t}.jsonl`);return Mi.existsSync(n)?n:null}function I0(e){if(!e)return[];let t=Vs.join(tm,nm(e)),n;try{n=Mi.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=Mi.statSync(Vs.join(t,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function Ct(e,t=131072){let n=await lP.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 pP(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 Ci(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=un(e,t),o=!0,!r)return null}else if(n){if(r=rm(e),o=!1,!r)return null}else return null;let s;try{s=pP(await Ct(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(_0,s.model),i=_0[s.model]||dP;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 _0,dP,tm,dn=W(()=>{_0={"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},dP=2e5,tm=process.env.BOSTRAT_CLAUDE_PROJECTS||Vs.join(uP.homedir(),".claude","projects")});function fP(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 Ni(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 Pi(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 Be(e,t=new Set){let n=[];for(let r=1;r<e.length;r++){let o=e[r];if(o==="<<")break;if(O0(o)){t.has(o)&&r++;continue}n.push(o)}return n}function M0(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 wP(e){let t=M0(e),n=bt(Be(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 om(e){if(e.includes("--files")){let s=bt(Be(e,$0)[0]);return s&&s!=="."?`List files in ${zs(s)}`:"List files"}let t=Be(e,$0),n=t[0];if(!n)return null;let r=bt(t.slice(1).find(s=>s!==".")),o=Js(n,40);return r?`Search for \u201C${o}\u201D in ${zs(r)}`:`Search for \u201C${o}\u201D`}function sm(e){let t=Be(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 am(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=Be(e)[0];return n?`Run ${Je(n)}`:null}function gP(e){let t=Be(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 R0(e){return e.find(t=>/^https?:\/\//.test(t))||null}function kP(e){let t=Pi(Ni(e));if(!t.length)return null;if(/^(ba|z)?sh$/.test(Je(t[0]))&&(t[1]==="-lc"||t[1]==="-c")&&t[2])return im(t[2]);let n=Je(t[0]);if(bP.has(n))return null;if(t.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=yP[n];return r?r(t.map((o,s)=>s===0?n:o)):null}function im(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=fP(t);for(let i=0;i<r.length-1;i++){let c=Pi(Ni(r[i])),l=Pi(Ni(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(Je(c[0]))&&Je(l[0])==="sed"){let u=M0(l),d=Be(c)[0];if(u&&d)return Js(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(Pi(Ni(i))[0]||""),sub:Be(Pi(Ni(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":Js(s?s.d:o[0].d,88)}var Js,bt,Je,zs,O0,$0,mP,hP,yP,bP,C0=W(()=>{Js=(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,zs=e=>{let t=e.replace(/\/+$/,"").split("/").filter(Boolean);return t.length>3?t.slice(-2).join("/"):e},O0=e=>e.startsWith("-"),$0=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);mP={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"},hP={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"};yP={git:e=>{let t=Be(e,new Set(["-C","-c"])),n=mP[t[0]];return n?n(t,e):null},gh:e=>{let t=Be(e),n=hP[t[0]];return n?n(t):null},sed:wP,rg:om,grep:om,egrep:om,nl:e=>{let t=bt(Be(e)[0]);return t?`Read ${Je(t)}`:null},cat:e=>{let t=bt(Be(e)[0]);return t?`Read ${Je(t)}`:null},head:e=>{let t=bt(Be(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the start of ${Je(t)}`:null},tail:e=>{let t=bt(Be(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the end of ${Je(t)}`:null},wc:e=>{let t=bt(Be(e)[0]);return t?`Count lines in ${Je(t)}`:"Count input lines"},ls:e=>{let t=bt(Be(e)[0]);return t&&t!=="."?`List files in ${zs(t)}`:"List files"},find:e=>bt(e[1])&&!O0(e[1])&&e[1]!=="."?`Find files in ${zs(e[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:e=>{let t=bt(Be(e)[0]);return t?`Create directory ${zs(t)}`:null},touch:e=>{let t=bt(Be(e)[0]);return t?`Create ${Je(t)}`:null},rm:e=>{let t=bt(Be(e)[0]);return t?`Delete ${zs(t)}`:null},mv:e=>{let t=bt(Be(e)[0]);return t?`Move ${Je(t)}`:null},cp:e=>{let t=bt(Be(e)[0]);return t?`Copy ${Je(t)}`:null},chmod:e=>{let t=bt(Be(e)[1]);return t?`Change permissions on ${Je(t)}`:null},diff:e=>{let t=Be(e);return t.length>=2?`Compare ${Je(t[0])} and ${Je(t[1])}`:null},curl:e=>{let t=R0(e);return t?`Fetch ${Js(t,56)}`:"Fetch a URL"},wget:e=>{let t=R0(e);return t?`Fetch ${Js(t,56)}`:"Fetch a URL"},npm:sm,pnpm:sm,yarn:sm,npx:e=>{let t=Be(e)[0];return t?`Run ${t}`:null},node:e=>am(e,"Node"),python:e=>am(e,"Python"),python3:e=>am(e,"Python"),make:e=>{let t=Be(e)[0];return t?`Run make ${t}`:"Run make"},systemctl:gP,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=Be(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=Be(e)[0];return t?`Locate ${t}`:null},command:e=>e[1]==="-v"&&e[2]?`Locate ${e[2]}`:null,fly:e=>{let t=Be(e)[0];return t==="deploy"?"Deploy to Fly":t?`Run fly ${t}`:null}},bP=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"])});function Ys(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 Ur(e,t=""){let n=t.trim();return n||im(e)||Ys(e)}function cm(e,t){if(e){if(Array.isArray(e)){for(let n of e)cm(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&lm.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))cm(n,t)}}}async function N0(e,{maxBytes:t=1048576,limit:n=400}={}){let r;try{r=await Ct(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(cm(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:Ur(d.command,d.description),ts:u})}return o.slice(-n)}function P0(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(...xP(i,r.get(a)||[]));return s.sort((a,i)=>(a.ts||0)-(i.ts||0))}function xP(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 lm,Xs=W(()=>{dn();C0();lm=new Set(["Bash"])});import D0 from"node:fs";import dm from"node:fs/promises";import um from"node:path";import vP from"node:os";async function B0(e){try{return await dm.realpath(e)}catch{return e}}function AP(e=SP){let t=[],n=r=>{let o;try{o=D0.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=um.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:D0.statSync(a).mtimeMs})}catch{}}}};return n(e),t.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function TP(e,t=1048576){let n;try{n=await dm.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 EP(e){let t=await TP(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 Lo(e,t){if(!e&&!t)return null;let n=AP();if(t){let o=n.find(s=>s.id===t);return o||null}let r=await B0(e);for(let o of n){let s=await EP(o.file);if(s&&(s===e||await B0(s)===r))return o}return null}async function j0(e,t,{limit:n=80,maxBytes:r=524288}={}){let o=await Lo(e,t);if(!o)return[];let s;try{s=await Ct(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 k=w.payload?.type;k==="task_started"?(l=!0,u=!1,d=w.payload?.turn_id||null):k==="task_complete"&&l&&(l=!1,!u&&w.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:Fo,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,pm.has(m.name)||c.push(m.name);continue}if(m.type!=="message"||m.role!=="assistant")continue;u=!0;let g=IP(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 $P(e){if(typeof e?.cmd=="string"&&e.cmd.trim())return e.cmd.trim();let t=e?.command;return typeof t=="string"&&t.trim()?t.trim():Array.isArray(t)&&t.length?t.length===3&&/^(ba)?sh$/.test(t[0])&&t[1]==="-lc"?String(t[2]).trim():t.map(String).join(" ").trim():null}function RP(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 OP(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=RP(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function L0(e,t,{limit:n=400,maxBytes:r=524288}={}){let o=await Lo(e,t);if(!o)return[];let s;try{s=await Ct(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"&&Di.has(u.name)){let f;try{f=JSON.parse(u.arguments||"{}")}catch{continue}let w=$P(f);w&&(d=[w])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=OP(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:Ur(f),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function F0(e,t){let n=await Lo(e,t);if(!n)return null;try{return Math.round((await dm.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var SP,_P,IP,Di,pm,Fo,Uo=W(()=>{dn();Xs();SP=process.env.BOSTRAT_CODEX_SESSIONS||um.join(process.env.CODEX_HOME||um.join(vP.homedir(),".codex"),"sessions");_P=new Set(["output_text","text"]),IP=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&typeof t.text=="string"&&_P.has(t.type)).map(t=>t.text).join(`
|
|
99
|
+
`).trim():"";Di=new Set(["exec_command","shell","exec"]),pm=new Set(["wait","write_stdin"]),Fo="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 Bi({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&U0.has(r))return U0.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 H0(e){if(!e||e.role!=="assistant")return null;if(e.apiError){let t=e.apiError;return{cls:t.cls||Bi({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===Fo?"other":Bi({text:t}),code:null,status:null,requestId:null}}return null}function W0(e,{codexFailedText:t=null}={}){let n=String(e?.text||"").trim();return n?e?.model==="<synthetic>"?/^No response requested\.$/.test(n)?null:{cls:Bi({text:n}),code:null,status:MP(n),requestId:null,text:n}:t&&n===t?{cls:"other",code:null,status:null,requestId:null,text:n}:null:null}function MP(e){let t=/^API Error:\s*(\d{3})\b/.exec(e);return t?Number(t[1]):null}var Qz,U0,gu=W(()=>{Uo();Qz=Object.freeze(["entitlement","auth","rate_limit","overloaded","server","other"]),U0=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 CP(e,t){return!e||!t?null:un(e,t)}function NP(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 PP(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 DP(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&t.type==="tool_result")}function fm(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 q0(e,t){if(!e.uuid)return null;let n=fm(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 mm(e,{maxBytes:t=524288,limit:n=80}={}){let r;try{r=await Ct(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 k=y?.type==="tool_use"?y.input?.file_path||y.input?.path:null;typeof k=="string"&&BP.test(k)&&s.set(y.id,k.split("/").pop()),y?.type==="tool_use"&&y.name==="Skill"&&y.id&&a.set(y.id,String(y.input?.skill||"").trim().replace(/^\//,"")||null)}if(DP(u.content)){let y=q0(l,s);y&&o.push({role:"assistant",text:"",images:y,ts:l.timestamp?Date.parse(l.timestamp):null,model:null});continue}let p=NP(u.content),f=d==="assistant"?PP(u.content):null,w=d==="user"?q0(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:Bi({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 K0(e,t,{turns:n=16}={}){let r=CP(e,t);if(!r)return"";let o=await mm(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}:** ${jP(s.text||c,700)} ${i}`.trim()}).join(`
|
|
102
|
+
|
|
103
|
+
`):""}var BP,jP,yu=W(()=>{gu();dn();BP=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;jP=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e});import LP from"node:fs/promises";import{execFile as FP}from"node:child_process";import{promisify as UP}from"node:util";function J0(e){wm=typeof e=="function"?e:null}function gm(e){let t=String(e||""),n=t.indexOf("/lib/node_modules/");return n>0?t.slice(0,n):null}function Y0({sessionName:e,from:t,to:n}={}){let r=String(n||"").trim();!r||G0.has(r)||(G0.add(r),V0=V0.then(()=>WP({sessionName:e,from:t,to:r})).catch(()=>{}))}async function WP({sessionName:e,from:t,to:n}){await de({flow:"codex-update",phase:"start",sessionName:e,from:t,to:n}).catch(()=>{});try{let r=(await hm("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await LP.realpath(r),s=gm(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await hm("npm",["install","-g","@openai/codex"],{env:a,timeout:HP});let i=(await hm(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await de({flow:"codex-update",phase:"done",sessionName:e,from:t,to:n,version:i}).catch(()=>{}),await z0({sessionName:e,from:t,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await de({flow:"codex-update",phase:"failed",sessionName:e,from:t,to:n,error:o}).catch(()=>{}),await z0({sessionName:e,from:t,to:n,status:"failed",error:o})}}async function z0(e){if(wm)try{await wm(e)}catch{}}var hm,HP,G0,V0,wm,bu=W(()=>{In();hm=UP(FP),HP=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,G0=new Set,V0=Promise.resolve(),wm=null});function KP(e,{provider:t,box:n}){let r=String(e).replace(/\r/g,"").split(`
|
|
104
|
+
`);if(!X0.test(e))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(qP);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"&&X0.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 Hr(e){let t=String(e??"").replace(/\r/g,""),n=t.split(`
|
|
105
|
+
`);for(let r=n.length-1;r>=0;r--)if(ex.test(n[r]))return n.slice(r).join(`
|
|
106
|
+
`);return t}function GP(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 VP(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 zP(e,t=""){let n=Hr(e);if(ji.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(Z0);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(Z0.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&&!ex.test(r[l])&&GP(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=VP(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function XP(e,t=""){let n=Hr(e);if(ji.test(n)||!JP.test(n))return null;let r=n.trimEnd().split(`
|
|
108
|
+
`).pop()||"";return YP.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 QP(e,t=""){let n=Hr(e);if(ji.test(n)||!Q0.test(n))return null;let r=n.split(`
|
|
109
|
+
`),o=r.findIndex(i=>Q0.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(ZP);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=Hr(e);return ji.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 tD(e,t=""){let n=Hr(e);return ji.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 nD(e,t=""){let n=String(e);if(!/Press any key to log in/.test(n)||ym.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 oD(e,t=""){let n=String(e);if(!rD.test(n)||ym.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 sD(e,t=""){let n=String(e);return!/Scan this QR code to log in/.test(n)||ym.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 tx(e,t,n=""){if(e!=="cursor"&&e!=="codex")return null;if(e==="codex"){let r=XP(t,n)||QP(t,n)||eD(t,n)||tD(t,n)||zP(t,n);if(r)return r}if(e==="cursor"){let r=oD(t,n)||sD(t,n)||nD(t,n);if(r)return r}return KP(t,{provider:e,box:n})}function nx(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 qP,X0,ji,ex,Z0,JP,YP,Q0,ZP,ym,rD,ku=W(()=>{qP=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,X0=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;ji=/(^|\n)\s*›/,ex=/^\s*Welcome to Codex\b|^\s*(?:│\s*)?>_ OpenAI Codex\b/;Z0=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;JP=/Successfully logged in\./,YP=/^\s*Press Enter to continue\.\s*$/;Q0=/Enter this one-time code/,ZP=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;ym=/Run Everything|Add a follow-up|Plan, search, build anything/;rD=/click this link to log in|Signing in with the browser/});function lD(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(iD.test(r)||cD.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 mD(e){let t=String(e||"");if(!tt.codex.block.test(t))return null;let n=Hr(t);if(!tt.codex.block.test(n))return null;let r=n.match(fD);return{from:r?.[1]||null,to:r?.[2]||null}}function Rn(e){return new Promise(t=>setTimeout(t,e))}function bm(e,t){let n=Date.now(),r=hD();for(let[s,a]of vu)n-a>=r&&vu.delete(s);let o=`${e}::${t}`;return vu.has(o)?!1:(vu.set(o,n),!0)}function ax(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(sx.test(n[o])){r=o;break}return r<0?t:n.slice(r+1).join(`
|
|
115
|
+
`)}function gD(e){let t=0;for(let n of String(e).split(`
|
|
116
|
+
`))sx.test(n)&&(t=Math.max(t,n.trimEnd().length));return t}function km(e,t){let n=tt[e];if(!n?.busy)return{state:"idle",reason:null};if(vm(e,t))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(t))return{state:"idle",reason:null};if(n.busy.test(ax(n,t)))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(t))return{state:"busy",reason:null};let r=gD(t);return e==="claude"&&r>0&&r<wD?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function xm(e,t){return km(e,t).state}function ix(e,t){if(e!=="claude")return!1;let n=String(t||"").split(`
|
|
117
|
+
`).slice(-bD).join(`
|
|
118
|
+
`);return yD.test(n)}function Qs(e,t){return xm(e,t)==="busy"}function Li(e,t){return xm(e,t)!=="idle"}function Ho(e,t){let n=tt[e];return n?.up?n.up.test(t):!0}function kD(e,t){let n=tt[e];return n?vm(e,t)?!1:n.up.test(t)&&xm(e,t)==="idle":!0}function vm(e,t){let n=tt[e];if(!n)return!1;let r=e==="codex"?Hr(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 Sm(e,t){let n=String(t||"").trim();if(!n)return e;let r=String(e||"").split(`
|
|
119
119
|
`);for(let o=r.length-1;o>=0;o--)if(r[o].includes(n))return r.slice(o+1).join(`
|
|
120
|
-
`);return e}function
|
|
121
|
-
`)?!0:Buffer.byteLength(e,"utf8")>=
|
|
122
|
-
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(
|
|
123
|
-
`).map(o=>o.replace(/\s+$/,"")),n=-1;for(let o=t.length-1;o>=0;o--)if(t[o]){(
|
|
124
|
-
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return
|
|
125
|
-
${o}`.trim()||t.slice(0,2e3)}function
|
|
126
|
-
`):""}async function
|
|
120
|
+
`);return e}function cx(e){let t=String(e||"").trim();return t?t.slice(0,32):null}function rx(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 ox(e,t,n,{bailOnDialog:r=!1,launchEcho:o=null,onReady:s}={}){let a=Date.now()+n,i=0,c=null;for(await jo(e).catch(()=>{});;){let l=Sm(await Pe(e),o);if(kD(t,l))return await s?.(),await Rn(Zs),"up";if(tt[t]?.up&&!tt[t].up.test(l))if(await nr(e)){if(c??=Date.now(),Date.now()-c>=Oi()){let u=rx(t);throw de({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"&&pD.test(l)&&!tt.claude.up.test(l))return de({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"resume-confirm"}).catch(()=>{}),"dialog-bail";if(t==="codex"&&dD.test(l)&&!tt.codex.up.test(l)&&i<3){if(!bm(e,"codex-trust")){await Rn(xu);continue}i+=1,await de({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"codex-trust",attempt:i}).catch(()=>{}),await it(e,"",{enter:!0,kind:"codex-trust-accept"}),await Rn(Zs);continue}if(t==="cursor"&&uD.test(l)&&!tt.cursor.up.test(l)&&i<3){if(!bm(e,"cursor-trust")){await Rn(xu);continue}i+=1,await de({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i}).catch(()=>{}),await it(e,"a",{enter:!1,kind:"cursor-trust-accept"}),await Rn(Zs);continue}if(t==="codex"&&i<3){let u=mD(l);if(u){if(!bm(e,`codex-update:${u.from}->${u.to}`)){await Rn(xu);continue}i+=1,await de({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i,update:`${u.from} -> ${u.to}`}).catch(()=>{}),await it(e,"3",{enter:!1,kind:"codex-update-skip"}),Y0({sessionName:e,from:u.from,to:u.to}),await Rn(Zs);continue}}if(Date.now()>a){if(vm(t,l)){let u=`${t} is blocked on an interactive prompt`;throw t==="codex"?u=tt.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"&&tt.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"&&tt.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"&&tt.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'),de({flow:"agent-ready",phase:"blocked",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:u}).catch(()=>{}),new Error(u)}if(await nr(e)){let u=rx(t);throw de({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.`),de({flow:"agent-ready",phase:"timeout",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,paneBlocked:!!tt[t]?.block?.test(l),paneUp:!!tt[t]?.up?.test(l),paneBusy:!!tt[t]?.busy?.test(ax(tt[t],l))}).catch(()=>{}),await Rn(Zs),"timeout"}await Rn(xu)}}function Fi({agent:e,agentCmd:t}={}){let n=t?Lr(t):Lr(e||"");return{label:n,realClaude:t?Bo(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 lx({agent:e,agentCmd:t}={}){let{realClaude:n,realCodex:r,realCursor:o}=Fi({agent:e,agentCmd:t});return n||r||o||process.env.BOSTRAT_FAKE_POLL_READY==="1"}function Wo({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o,onBlocked:s}={}){let{realCodex:a,realCursor:i}=Fi({agent:n,agentCmd:r});return!t||!a&&!i?null:rr({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o}).then(()=>{},c=>{try{xD(c,{onBlocked:s,agent:n,sessionName:t})}catch{}})}function xD(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 rr({cwd:e,sessionId:t,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=aD(),bailOnDialog:a=!1,launchEcho:i=null,onReady:c}={}){let{label:l,realClaude:u,realCodex:d,realCursor:p}=Fi({agent:r,agentCmd:o});if(u&&e&&await Or(e),d&&e&&await Ms(e),p&&e&&await Cs(e),(u||d||p)&&n)return await ox(n,l,s,{bailOnDialog:a,launchEcho:i,onReady:c});let f=tt[r]?r:tt[l]?l:null;return process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&f?await ox(n,f,s,{launchEcho:i}):(await Rn(Zs),"settle")}var aD,xu,Zs,iD,cD,tt,uD,dD,pD,fD,hD,vu,wD,sx,yD,bD,ea=W(()=>{mt();li();jl();Ll();bu();ku();In();aD=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,xu=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,Zs=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,iD=/^\s{0,4}[^\s›•■◦╭╰│]\S*(?: \S+){0,3} · \S/,cD=/^\s*tab to queue message\b/;tt={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:lD},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/}},uD=/Trust this workspace|\[a\]\s*Trust/,dD=/Do you trust the contents of this directory\?[\s\S]*›\s*1\. Yes, continue[\s\S]*Press enter to continue/,pD=/Resume from summary/i,fD=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;hD=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,vu=new Map;wD=68,sx=/^\s*[─━]{10,}(?: [^─━\n]+ [─━]+)?\s*$/;yD=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,bD=15});function TD(e){return typeof e!="string"||!e?!1:e.includes(`
|
|
121
|
+
`)?!0:Buffer.byteLength(e,"utf8")>=AD}function On(e,t){return e==="codex"?{enter:!0,submitDelayMs:vD,...TD(t)?{paste:!0}:{}}:e==="claude"?{enter:!0,submitDelayMs:SD}:{enter:!0}}var vD,SD,AD,Ui=W(()=>{vD=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),SD=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),AD=(()=>{let e=Number(process.env.BOSTRAT_CODEX_PASTE_MIN_BYTES??512);return Number.isFinite(e)&&e>=0?e:512})()});function Hi(e,t){if(e==="codex")return RD(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(ux.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(ux.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(ED);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 RD(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]){(_D.test(t[o])||ID.test(t[o]))&&!Am.test(t[o])?n=o:Am.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(Am);if(a){r.unshift(a[1]);let i=r.map(c=>c.trim()).filter(Boolean);return i.length?i.join(" "):null}if($D.test(s))return null;r.unshift(s)}return null}function Tm(e,t){let n=String(t||"");if(!/\[Pasted Content \d+ chars\]/.test(n))return!1;let r=0,o=Ue(n.replace(OD,(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 dx(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 px(e,t,n,{busy:r=!1}={}){let o=Hi(e,n);return o&&Su(t,o)||o&&e==="codex"&&Tm(t,o)?"stuck":r&&e!=="codex"||o?"submitted":"unknown"}function Su(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 ux,ED,Am,_D,ID,$D,OD,Wi=W(()=>{ux=/^\s*─{8,}(?: [^─\n]+ ─+)?\s*$/,ED=/^❯[ ]?(.*)$/,Am=/^\s*›[ ]?(.*)$/,_D=/\S+ · \S/,ID=/^\s*(?:\? for shortcuts|tab to queue message)\b/,$D=/^\s*(?:[•■◦✶✻⚠]|╭|╰|│)/;OD=/\[Pasted Content (\d+) chars\](?: #\d+)?/g});async function Ki({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=(D,j,T={})=>(a&&typeof a=="object"&&Object.assign(a,{verdict:D,retries:T.retries??0,ms:(i.now||Date.now)()-c,sawUp:!!T.sawUp,reason:T.reason??null}),j);if(!MD.has(t))return l("skipped",!0);let u=i.waitForUp??lx({agent:t,agentCmd:n}),d=i.capture||(D=>Pe(D,{lines:et})),p=i.pressEnter||(D=>ze(D,["Enter"],{kind:"submit-retry"})),f=i.sleep||DD,w=i.now||Date.now,m=i.log||(D=>de(D).catch(()=>{})),g=i.maxMs??PD(),y=w()+g,k={flow:s,trackId:o,sessionName:e,agent:t,textHead:String(r).slice(0,120)},b=0,I=0,R=0,N=!1;for(await f(qi[0]);;){let D=await d(e).catch(()=>""),j=Ho(t,D),T=Qs(t,D),$=px(t,r,D,{busy:T});if($==="submitted")return l("submitted",!0,{retries:b,sawUp:!0});if(u&&(!j||$==="stuck"&&T)){if(w()>=y)return $==="stuck"?(m({...k,phase:"submit-failed",reason:"stuck-at-deadline",retries:b}),l("failed",!1,{retries:b,sawUp:N,reason:"stuck-at-deadline"})):(m({...k,phase:"submit-unverified",reason:N?"cli-down":"cli-never-up",retries:b,waitedMs:g}),l("unverified",!0,{retries:b,sawUp:N,reason:N?"cli-down":"cli-never-up"}));await f(CD);continue}if(N=!0,$==="stuck"){if(b>=ND)return m({...k,phase:"submit-failed",retries:b}),l("failed",!1,{retries:b,sawUp:!0,reason:"stuck"});b+=1,I=0,R=0,await p(e),m({...k,phase:"submit-retry",attempt:b}),await f(qi[0]);continue}if(I+=1,I>=qi.length||w()>=y)return l("assumed",!0,{retries:b,sawUp:!0});R+=1,await f(qi[Math.min(R,qi.length-1)])}}var MD,qi,CD,ND,PD,DD,Em=W(()=>{mt();ea();Wi();In();MD=new Set(["claude","codex"]),qi=[250,450,700],CD=500,ND=2,PD=()=>{let e=Number(process.env.BOSTRAT_SUBMIT_VERIFY_MAX_MS);return Number.isFinite(e)&&e>0?e:3e4},DD=e=>new Promise(t=>setTimeout(t,e))});function fx(e){return String(e||"").replace(/[\u2018\u2019\u201A\u02BC\u0060\u00B4]/g,"'").replace(/[\u201C\u201D\u201E]/g,'"')}function or(e){let t=fx(e).toLowerCase();if(!t.trim())return null;let n=null;for(let r of BD){let o=0;for(let s of jD[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function mx(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))LD.has(r)&&(t+=o||0),FD.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function Gi(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 hx(e){let t=fx(e).trim();return t?HD.test(t)?!0:t.split(/\s+/).length<=UD:!1}var BD,jD,LD,FD,UD,HD,Au=W(()=>{BD=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],jD={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/]};LD=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),FD=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);UD=12,HD=/^\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 bx(e){let t=Gi(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||WD.has(r)||n.push(r);return n}function qD(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(yx(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(yx(n,"assistant",o))}}return t}function yx(e,t,n){let r=or(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 Vi(e){let t=qD(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=bx(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 Tu(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(bx(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+wx*(1-gx+gx*(g/(e.avgdl||1))),k=f*(m*(wx+1)/y);c.set(w,(c.get(w)||0)+k)}}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 wx,gx,WD,_m=W(()=>{Au();wx=1.5,gx=.75,WD=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 Eu from"node:fs/promises";import kx from"node:path";async function KD(e){let t=kx.join(e,".bostrat","events"),n;try{n=await Eu.readdir(t)}catch{return Vi([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await Eu.readFile(kx.join(t,o),"utf8");for(let a of s.split(`
|
|
124
|
+
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return Vi(r)}function GD(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 VD(e,t,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=Tu(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?["",`## ${xx}, 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 vx(e,t,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await Eu.readFile(t,"utf8");if(a.includes(xx))return{appended:!1,reason:"already-enriched"};let i=await KD(e);if(!i.N)return{appended:!1,reason:"empty-index"};let c=VD(i,GD(a),{k:r,maxChars:o,exclude:s});return c?(await Eu.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 xx,Sx=W(()=>{_m();xx="Relevant prior discussion (verbatim, retrieved"});function QD(e){return zD.test(String(e||"").trimStart())}function Ex(e){let t=String(e||"");if(!QD(t.trimStart()))return null;let n=t.replace(JD,"").trim(),r=e1(n),o=[];for(let u of XD){let d=u.from.map(p=>ta(r.get(p))).find(p=>p&&!Ax(p));d&&o.push({h:u.h,text:zi(d,YD)})}let s=ta(r.get("Current Work"))||ta(r.get("Primary Request and Intent"))||n1(n),a=Iu(s),i=zi(_u(Rm(a[0]||"")),ZD)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(zi(_u(Rm(l)),$m));for(let u of["Pending Tasks","Optional Next Step"]){let d=Iu(ta(r.get(u)))[0]||"";if(!(!d||Ax(d))){c.push(zi(`Left to do: ${_u(Rm(d))}`,$m));break}}if(!c.length){let u=Iu(ta(r.get("Problem Solving"))||"")[0];u&&c.push(zi(_u(u),$m))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function e1(e){let t=new Map,n=[];Im.lastIndex=0;for(let r=Im.exec(e);r;r=Im.exec(e))n.push({name:t1(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 t1(e){return Tx.find(n=>n.toLowerCase()===e.toLowerCase())||e}function ta(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 n1(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return ta(t.split(/\n{2,}/).slice(0,2).join(" "))}function Iu(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 Rm(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 _u(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function Ax(e){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(e||"").trim())}function zi(e,t){let n=String(e||"").trim();if(n.length<=t)return n;let r=Iu(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 zD,Tx,Im,JD,YD,XD,ZD,$m,_x=W(()=>{zD=/^This session is being continued from a previous conversation that ran out of context\./,Tx=["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"],Im=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${Tx.join("|")})[:*\t ]*$`,"gim"),JD=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,YD=420,XD=[{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"]}],ZD=116,$m=168});function Rx(e){return Om.has(String(e||"").replace(/^\//,"").toLowerCase())}function Ji(e){let t=String(e||"").trim();if(!t)return null;let n=Ex(t);if(n)return n;let r=t.match(r1);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(o1);if(o)return{kind:"skill",name:o[1]};let s=t.match(s1);if(s)return{kind:"skill",name:s[1].toLowerCase()};let a=t.match(a1);if(a){let c=a[1].toLowerCase();return Om.has(c)?{kind:"suppress"}:{kind:"command",command:`/${c}`}}let i=t.match(w1);if(i&&Om.has(i[1].toLowerCase()))return{kind:"suppress"};if(i1.test(t))return{kind:"suppress"};if(l1.test(t))return{kind:"suppress"};if(c1.test(t))return{kind:"suppress"};if(u1.test(t)){let c=u=>k1(t.match(u)?.[1].trim()||"")||null,l=c(h1);return{kind:"task",summary:c(d1)||"",taskId:c(p1),toolUseId:c(f1),status:c(m1)?.toLowerCase()||null,resultHead:l?l.length>Ix?`${l.slice(0,Ix).trimEnd()}\u2026`:l:null}}return null}function Ox(e){let t=Ji(e?.text);return t||(e?.injected?{kind:"skill",name:e.injected.skill||"skill"}:null)}function Mx(e){let t=String(e||""),n=t.match(g1);if(n)return{kind:"shell-input",command:n[1].trim()};let r=t.match(y1);return r?{kind:"shell-output",stdout:$x(r[1]),stderr:$x(r[2])}:null}function k1(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function Cx(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 Nx(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var r1,o1,s1,a1,i1,c1,l1,u1,d1,p1,f1,m1,h1,Ix,Om,w1,g1,y1,b1,$x,$u=W(()=>{_x();r1=/^Base directory for this skill:\s*(\S+)/,o1=/^Run the "([^"]+)" workflow\./,s1=/^#\s*\/([a-z0-9:_-]+)\s*[—–:-]/i,a1=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,i1=/^<local-command-(?:stdout|caveat)>/i,c1=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,l1=/^Continue from where you left off\.$/,u1=/^<task-notification>/i,d1=/<summary>([\s\S]*?)<\/summary>/i,p1=/<task-id>([\s\S]*?)<\/task-id>/i,f1=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,m1=/<status>([\s\S]*?)<\/status>/i,h1=/<result>([\s\S]*?)(?:<\/result>|$)/i,Ix=600,Om=new Set(["model","clear","compact","login","exit","quit"]);w1=/^\/([a-z0-9:_-]+)$/i;g1=/^\s*<bash-input>([\s\S]*?)<\/bash-input>\s*$/i,y1=/^\s*<bash-stdout>([\s\S]*?)<\/bash-stdout>\s*(?:<bash-stderr>([\s\S]*?)<\/bash-stderr>)?\s*$/i,b1=/\x1b\[[0-9;?]*[ -/]*[@-~]/g,$x=e=>String(e||"").replace(b1,"").replace(/^\n+/,"").replace(/\s+$/,"")});import x1 from"node:crypto";function T1(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,Dx)){let o=na(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,v1)){let c=na(i?.label);if(!c)return null;let l=na(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<S1)return null;n.push({question:o,header:na(r?.header).slice(0,A1),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function Mm(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;Px.lastIndex=0;let t=null;for(let o;o=Px.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=T1(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function Cm(e,t,n){let r=x1.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${e}:ask:${t||"turn"}:${r}`}function Nm(e,{leadBoxId:t,provider:n,isAnswered:r,maxQuestions:o=Dx}={}){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||Mm(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=Cm(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 Bx(e,t,n,r){let o=[];(e?.questions||[]).forEach((i,c)=>{let l=(t?.[c]||[]).map(d=>i.options?.[d]?.label).filter(Boolean),u=na(Array.isArray(r)?r[c]:"");u&&l.push(u),l.length&&o.push({name:i.header||i.question,picks:l.join(", ")})});let s=na(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 Dx,v1,S1,A1,Px,na,jx,Pm=W(()=>{Dx=4,v1=4,S1=2,A1=24,Px=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,na=e=>typeof e=="string"?e.trim():"";jx=`
|
|
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,80 +150,82 @@ 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 l1(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 Ox(e,t,n){let r=Mh(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 $n(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,m)=>{d.set(k,m),t.push(m)},f=k=>{let m=d.get(k);m&&(m.retired=!0)},w=/^Agent "([\s\S]+)" finished$/,h=new Map;for(let k of e||[]){if(k.type!=="tool.called"||k.source!=="transcript")continue;let m=String(k.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(m))for(let v of m.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let P=v[0].split("/").pop().toLowerCase();h.has(P)||h.set(P,[]),h.get(P).push(k.payload?.ts??k.ts)}}let g=3*6e4,y=20*6e4,x=(k,m)=>!!(k?.name&&h.get(String(k.name).toLowerCase()))?.some(P=>P>=m-g&&P<=m+y);for(let k of e||[])if(k.type==="turn.retracted"){r.add(`${k.boxId}|${k.payload?.saidTs}`);let m=Le(k.payload?.text);m&&(o.has(k.boxId)||o.set(k.boxId,[]),o.get(k.boxId).push(m))}else if(k.type==="turn.user"&&k.source==="orchestrator"){let m=Le(k.payload?.text);m&&(s.has(k.boxId)||s.set(k.boxId,new Set),s.get(k.boxId).add(m))}else if(k.type==="job.stalled"&&k.payload?.id)i.set(k.payload.id,{ts:me(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 m=k.payload.note,v={status:m.status||null,summary:m.summary||null,resultHead:m.resultHead??null,ts:me(k)};m.toolUseId&&c.set(m.toolUseId,v);let P=String(m.summary||"").match(w);P&&u.set(P[1],v)}let b=new Map,I=new Map,_=null,N=new Map,U=new Map,j=new Map,T=new Map,R=new Map,L=new Map,V=new Map,O=new Map,S=new Map,M=new Map,H=new Map,W=new Map,te=new Map,pe=new Map,Ie=new Map,Oe=[],re=k=>Oe.push(k),Ne=k=>{if(!Oe.length)return;let m=me(k);for(let v of Oe){if(v.status!=="open"||v.box&&v.box!==k.boxId||(v.ts??0)>m)continue;let P=t.indexOf(v);P>=0&&t.splice(P,1),v.ts=m,t.push(v)}},X=new Map,G=new Map,fe=new Map,be=new Map,xe=new Map,Ke=new Map,le=new Map,$=new Map,Y=()=>{for(let k of $.values())k.questions.length&&t.push(k);$.clear()},Ce=null,Ve=new Map,xt=6e4,Er=new Map,_t=(k,m)=>{let v=o.get(k);if(!v?.length)return!1;let P=Le(m),ae=s.get(k);return v.some(ge=>{if(!P.startsWith(ge))return!1;let Me=Le(P.slice(ge.length));return!!Me&&!!ae?.has(Me)})};for(let k of e||[]){let m=k.payload||{};switch(k.type){case"turn.user":{if(k.source==="hook"||(Oe.length=0,!String(m.text||"").trim()&&!m.images?.length&&!m.attachments?.length))break;{let v=Sx(m.text);if(v){if(v.kind==="shell-input"){let P={id:k.id,kind:"shell",command:v.command,stdout:null,stderr:null,done:!1,ts:me(k),box:k.boxId,agent:k.provider};t.push(P),Ve.set(k.boxId,P)}else{let P=Ve.get(k.boxId);P?(P.stdout=v.stdout||null,P.stderr=v.stderr||null,P.done=!0,Ve.delete(k.boxId)):t.push({id:k.id,kind:"shell",command:null,stdout:v.stdout||null,stderr:v.stderr||null,done:!0,ts:me(k),box:k.boxId,agent:k.provider})}break}}{let v=m.note||Gi(m.text);if(v){if(v.kind==="task"){if(v.toolUseId&&a.has(v.toolUseId))break;let P=String(v.summary||"").match(w);if(!v.toolUseId&&P&&l.has(P[1]))break}if(v.kind==="command"&&xx(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:me(k),box:k.boxId,agent:k.provider});break}v.kind!=="suppress"&&t.push({id:k.id,kind:"event",type:Tx(v),name:v.name??null,command:v.command??null,summary:v.summary??null,status:v.status??null,ts:me(k),box:k.boxId,agent:k.provider});break}}if(r.has(`${k.boxId}|${me(k)}`)||k.source==="transcript"&&_t(k.boxId,m.text))break;if(m.kind==="merge-notify"||m.kind==="ship-notify"){t.push({id:k.id,kind:"event",type:m.kind==="ship-notify"?"ship-notified":"merge-notified",text:m.text,ts:me(k),box:k.boxId,agent:k.provider});break}if(m.kind==="mock-picked"||m.kind==="ci-fix"||m.kind==="reconcile"||m.kind==="pr-go"||m.kind==="push-go"||m.kind==="spin-up-view"||m.kind==="prompt-answer"||m.kind==="followup-go"){m.kind==="prompt-answer"&&(Ce=null);break}for(let v of N.values())v.status="stale";N.clear();for(let v of j.values())v.status="stale";j.clear();for(let v of te.values())v.status="stale";te.clear(),Ce&&Ce.box===k.boxId&&(t.push(l1(Ce)),Ce=null),t.push({id:k.id,kind:"message",role:"user",text:m.text,ts:me(k),box:k.boxId,agent:k.provider,boxRole:m.boxRole,...k.actor==="orchestrator"?{orchestration:m.kind||!0}:{},...m.sourceId?{sourceId:m.sourceId}:{},...m.attachments?.length?{attachments:m.attachments}:{},...m.images?.length?{images:m.images}:{}});break}case"peer.message":{let v=m.from||{};t.push({id:k.id,kind:"event",type:"peer-message",text:m.text,from:v.trackName||v.boxId||v.name||null,fromTrackId:v.trackId||null,ts:me(k),box:k.boxId,agent:k.provider});break}case"turn.agent":{if(m.model==="<synthetic>"&&/^No response requested\.$/.test(String(m.text||"").trim()))break;let v=N0(m,{codexFailedText:Lo});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:me(k),ts:me(k),box:k.boxId,agent:k.provider});break}Y();let P=(m.images||[]).filter(Qe=>!x(Qe,me(k)));if(!String(m.text||"").trim()&&!P.length)break;if(k.provider==="cursor"){let Qe=`${k.boxId}|${m.text}`;if(n.has(Qe))break;n.add(Qe)}(!Ce||Ce.box===k.boxId)&&(Ce=null);let ae=k.provider==="codex"?(m.tools||[]).filter(Qe=>!ih.has(Qe)&&!Ci.has(Qe)):m.tools||[],ge=m.text,Me=null,ht=Ih(String(m.text||""));ht&&(ge=ht.text,Me={questions:ht.questions}),t.push({id:k.id,kind:"message",role:"assistant",text:ge,ts:me(k),box:k.boxId,agent:k.provider,boxRole:m.boxRole,...Me?{ask:Me}:{},...ae.length?{tools:ae}:{},...m.model?{model:m.model}:{},...m.sourceId?{sourceId:m.sourceId}:{},...P.length?{images:P}:{}}),Ne(k),Me&&(Ce={id:k.id,groupId:$h(k.provider,m.sourceId||k.id,Me.questions),provider:k.provider,box:k.boxId,questions:Me.questions,ts:me(k)});break}case"job.started":{if(!m.id)break;let v=me(k),P=c.get(m.id);if(!P&&(m.kind||"agent")==="agent"&&m.name){let st=u.get(m.name);st&&st.ts>=v&&(P=st)}let ae=P?null:i.get(m.id)||null,ge={jobId:m.id,jobKind:m.kind||"agent",name:m.name||null,detail:m.detail||null,status:P?P.status==="killed"?"stopped":P.status&&P.status!=="completed"?"failed":"done":ae?"stalled":"running",startedTs:v,finishedTs:P?.ts??ae?.ts??null,quietMs:ae?.quietMs??null,summary:P?.summary??null,resultHead:P?.resultHead??null},Me=Er.get(k.boxId),ht=Me?.jobs[Me.jobs.length-1]?.startedTs;if(Me&&ht!=null&&v-ht<=xt)Me.jobs.push(ge);else{let st={id:k.id,kind:"job-dock",dockId:m.id,ts:v,box:k.boxId,agent:k.provider,jobs:[ge]};Er.set(k.boxId,st),t.push(st)}break}case"handoff.done":t.push(dt(k,"lead-handoff"));break;case"subtask.delegated":t.push({...dt(k,"subtask-delegated"),to:k.boxId,toAgent:k.provider});break;case"subtask.merged":t.push(dt(k,"subtask-merged"));break;case"pr.state.changed":{if(!m.change)break;if(m.change==="opened"){for(let P of N.values())P.status="stale";N.clear();for(let P of j.values())P.status="stale";j.clear()}let v=dt(k,`pr-${m.change}`);if(v.box=k.boxId??null,Rx.has(m.change)&&m.number!=null){let P=Ox(b,I,m);if(b.size>=2){for(let ge of I.values())for(let Me of ge)Me.grouped=!0;v.groupPrs=[...b.values()];let ae=[v];for(let ge of b.keys())I.set(ge,ae)}else{let ae=I.get(P)??[];ae.push(v),I.set(P,ae)}}else if(m.change==="merged"||m.change==="closed"||m.change==="lost"){let P=Mh(m),ae=_&&!_.complete||b.size>=2;if(b.has(P))b.delete(P),I.delete(P);else if(m.number!=null)for(let ge of[...b.keys()])ge.endsWith(`#${m.number}`)&&(b.delete(ge),I.delete(ge));if(ae&&m.number!=null){(!_||_.complete)&&(_={id:k.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:me(k),ts:me(k),box:k.boxId,agent:k.provider},t.push(_)),_.prs.push({number:m.number,url:m.url??null,title:m.title??null,repo:m.repo??null,branch:m.branch??null,change:m.change,ts:me(k)}),_.remaining=[...b.values()],_.complete=b.size===0;break}}t.push(v);break}case"pr.offered":{if(!m.offerId)break;for(let P of N.values())P.status="stale";N.clear();for(let P of j.values())P.status="stale";j.clear();let v={id:k.id,kind:"pr-offer",offerId:m.offerId,excerpt:m.excerpt||null,branch:m.branch||null,base:m.base||null,offered:Array.isArray(m.offered)&&m.offered.length?m.offered:null,toPr:m.toPr&&typeof m.toPr=="object"?{number:m.toPr.number??null}:null,available:Array.isArray(m.available)?m.available:null,defaults:Array.isArray(m.defaults)?m.defaults:null,prState:m.prState||null,openPr:m.openPr&&typeof m.openPr=="object"?m.openPr:null,repos:Array.isArray(m.repos)&&m.repos.length?m.repos:null,status:"open",source:k.source==="cli"||m.via==="cli"?"cli":"transcript",ts:me(k),box:k.boxId,agent:k.provider};N.set(m.offerId,v),U.set(m.offerId,v),t.push(v),v.source==="cli"&&re(v);break}case"pr.offer.answered":{let v=U.get(m.offerId);v&&(v.status=m.action||"dismiss",v.actions=Array.isArray(m.actions)?m.actions:null,v.notify=m.notify===!0,m.existingPr&&typeof m.existingPr=="object"&&(v.existingPr={number:m.existingPr.number??null}),N.delete(m.offerId));break}case"pr.exists.offered":{if(!m.offerId)break;for(let P of j.values())P.status="stale";j.clear();let v={id:k.id,kind:"pr-exists-offer",offerId:m.offerId,srcOfferId:m.srcOfferId??null,branch:m.branch||null,base:m.base||null,pr:m.pr&&typeof m.pr=="object"?{number:m.pr.number??null,url:m.pr.url??null,title:m.pr.title??null}:null,actions:Array.isArray(m.actions)&&m.actions.length?m.actions:null,notify:m.notify===!0,status:"open",ts:me(k),box:k.boxId,agent:k.provider};j.set(m.offerId,v),T.set(m.offerId,v),t.push(v);break}case"pr.exists.answered":{let v=T.get(m.offerId);v&&(v.status=m.choice||"dismiss",j.delete(m.offerId));break}case"box.state.changed":{let v=m.change==="model"?"model-change":m.change;if(!v)break;let P=dt(k,v);if(v==="model-change"&&(P.to=m.model),v==="node-update"&&P.latest){let ae=t.findIndex(ge=>ge.kind==="event"&&ge.type==="node-update"&&ge.latest===P.latest);if(ae>=0){let ge=t[ae];t[ae]={...P,ts:ge.ts,at:ge.at,firstAt:ge.firstAt??ge.ts,updatedAt:P.ts};break}}t.push(P);break}case"track.transferred":case"track.recovered":t.push(dt(k,"track-moved"));break;case"view.state.changed":{if(m.change==="deleted"){f(`view:${m.viewId}`);break}if(m.change!=="created"||m.restarted)break;p(`view:${m.viewId}`,{id:k.id,kind:"view-card",viewId:m.viewId,name:m.name,slug:m.slug,port:m.port,origin:m.origin,ts:me(k),box:k.boxId,agent:k.provider});break}case"view.spinup.failed":{t.push({id:k.id,kind:"view-spinup-failed",name:m.name||null,reason:m.reason||"no-view",ts:me(k),box:k.boxId,agent:k.provider});break}case"mock.state.changed":{if(m.change==="picked"){t.push({id:k.id,kind:"mock-picked",mockId:m.mockId,name:m.name,group:m.group??null,version:m.version??null,siblings:m.siblings??[],ts:me(k),box:k.boxId,agent:k.provider});break}if(m.change==="deleted"){f(`mock:${m.mockId}`);break}if(m.change!=="created")break;p(`mock:${m.mockId}`,{id:k.id,kind:"mock-card",mockId:m.mockId,name:m.name,group:m.group??null,ts:me(k),box:k.boxId,agent:k.provider});break}case"flow.state.changed":{if(m.change==="deleted"){f(`flow:${m.flowId}`);break}if(m.change!=="created")break;p(`flow:${m.flowId}`,{id:k.id,kind:"flow-card",flowId:m.flowId,name:m.name,stepCount:m.stepCount??null,mockIds:m.mockIds??[],ts:me(k),box:k.boxId,agent:k.provider});break}case"runbook.state.changed":{if(m.change==="deleted"){f(`runbook:${m.runbookId}`);break}if(m.change!=="created")break;p(`runbook:${m.runbookId}`,{id:k.id,kind:"runbook-card",runbookId:m.runbookId,name:m.name,ts:me(k),box:k.boxId,agent:k.provider});break}case"ticket.linked":{t.push({id:k.id,kind:"ticket-card",provider:m.provider||"linear",key:m.key,url:m.url,title:m.title??null,linkSource:m.linkSource??null,ts:me(k),box:k.boxId,agent:k.provider});break}case"box.fullscreen.detected":{if(!m.offerId)break;for(let P of V.values())P.box===(m.box||k.boxId)&&(P.status="stale",V.delete(P.offerId));let v={id:k.id,kind:"fullscreen-offer",offerId:m.offerId,status:"open",ts:me(k),box:m.box||k.boxId,agent:m.agent||k.provider};V.set(m.offerId,v),O.set(m.offerId,v),t.push(v);break}case"box.fullscreen.answered":{let v=O.get(m.offerId);v&&(v.status=m.action==="restarted"?"restarted":m.action==="cleared"?"cleared":"dismiss",V.delete(m.offerId));break}case"ticket.move.offered":{if(!m.offerId)break;for(let P of R.values())P.key===m.key&&(P.status="stale",R.delete(P.offerId));let v={id:k.id,kind:"ticket-offer",offerId:m.offerId,provider:m.provider||"linear",key:m.key,url:m.url??null,phase:m.phase??null,from:m.from??null,to:m.to?.name??null,prNumber:m.prNumber??null,status:"open",ts:me(k),box:k.boxId,agent:k.provider};R.set(m.offerId,v),L.set(m.offerId,v),t.push(v);break}case"ticket.move.answered":{let v=L.get(m.offerId);v&&(v.status=m.action==="moved"?"moved":"dismiss",R.delete(m.offerId));break}case"ticket.state.changed":{for(let v of R.values())v.key===m.key&&v.to===m.to&&(v.status="stale",R.delete(v.offerId));if(m.mode==="offer")break;t.push(dt(k,"ticket-state"));break}case"ticket.commented":{t.push({id:k.id,kind:"ticket-comment",key:m.key,url:m.url??null,excerpt:m.excerpt??null,ts:me(k),box:k.boxId,agent:k.provider});break}case"ticket.flow.changed":t.push(dt(k,"ticket-flow"));break;case"ticket.create.offered":{if(!m.proposalId)break;for(let P of S.values())P.status="stale";S.clear();let v={id:k.id,kind:"ticket-create-offer",proposalId:m.proposalId,provider:m.provider||"linear",epic:m.epic??null,issues:Array.isArray(m.issues)?m.issues:[],team:m.team??null,needsTeam:!!m.needsTeam,linkCurrentTrack:!!m.linkCurrentTrack,status:"open",ts:me(k),box:k.boxId,agent:k.provider};S.set(m.proposalId,v),M.set(m.proposalId,v),t.push(v),re(v);break}case"ticket.create.answered":{let v=M.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",v.createdCount=m.createdCount??null,v.failedCount=m.failedCount??null,S.delete(m.proposalId));break}case"loop.create.offered":{if(!m.proposalId)break;for(let P of H.values())P.status="stale";H.clear();let v={id:k.id,kind:"loop-create-offer",proposalId:m.proposalId,name:m.name??"",schedule:m.schedule??"",prompt:m.prompt??"",expectation:m.expectation??null,notify:m.notify??"off",assignment:m.assignment??{provider:"claude"},repo:m.repo??null,nodeId:m.nodeId??null,nodeName:m.nodeName??null,status:"open",ts:me(k),box:k.boxId,agent:k.provider};H.set(m.proposalId,v),W.set(m.proposalId,v),t.push(v),re(v);break}case"loop.create.answered":{let v=W.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",H.delete(m.proposalId));break}case"followup.armed":{if(!m.armId)break;let v=m.replaces?Ie.get(m.replaces):null;v&&v.status==="armed"&&(v.status="replaced");let P={id:k.id,kind:"followup-armed",armId:m.armId,trigger:m.trigger??"now",label:m.label??"",prompt:m.prompt??"",why:m.why??null,status:"armed",ts:me(k),box:k.boxId,agent:k.provider};Ie.set(m.armId,P),P.trigger!=="now"&&t.push(P);break}case"followup.armed.cancelled":{let v=Ie.get(m.armId);v&&v.status==="armed"&&(v.status="cancelled");break}case"followup.offered":{if(!m.offerId)break;for(let ae of te.values())ae.trigger===m.trigger&&(ae.status="stale",te.delete(ae.offerId));let v=Array.isArray(m.rows)?m.rows:[];for(let ae of v){let ge=ae.armId?Ie.get(ae.armId):null;ge&&ge.status==="armed"&&(ge.status="offered")}let P={id:k.id,kind:"followup-offer",offerId:m.offerId,trigger:m.trigger??"now",repo:m.repo??null,context:m.context&&typeof m.context=="object"?m.context:{},rows:v.map(ae=>({followupId:ae.followupId??null,armId:ae.armId??null,label:ae.label??"",prompt:ae.prompt??null,promptPreview:ae.promptPreview??null,promptHash:ae.promptHash??null,origin:ae.origin??"operator",why:ae.why??null})),status:"open",answer:null,ts:me(k),box:k.boxId,agent:k.provider};te.set(m.offerId,P),pe.set(m.offerId,P),t.push(P),P.trigger==="now"&&re(P);break}case"followup.answered":{let v=pe.get(m.offerId);v&&(v.status=m.action==="sent"?"sent":m.action==="removed"?"removed":"dismiss",v.answer={followupId:m.followupId??null,armId:m.armId??null,label:m.label??null,promoted:m.promoted??null,revived:m.revived===!0},te.delete(m.offerId));break}case"followup.create.offered":{if(!m.proposalId)break;for(let P of X.values())P.status="stale";X.clear();let v={id:k.id,kind:"followup-create-offer",proposalId:m.proposalId,label:m.label??"",prompt:m.prompt??"",trigger:m.trigger??"pr.merged",repo:m.repo??null,why:m.why??null,status:"open",created:null,ts:me(k),box:k.boxId,agent:k.provider};X.set(m.proposalId,v),G.set(m.proposalId,v),t.push(v),re(v);break}case"followup.create.answered":{let v=G.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",X.delete(m.proposalId));break}case"followup.created":{let v=m.proposalId?G.get(m.proposalId):null;v&&(v.created={followupId:m.followupId??null});break}case"loop.created":{let v=m.proposalId?W.get(m.proposalId):null;v&&(v.created={loopId:m.loopId??null,nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),t.push(dt(k,"loop-created"));break}case"loop.update.offered":case"loop.delete.offered":{if(!m.proposalId||!m.loopId)break;for(let ae of fe.values())ae.loopId===m.loopId&&(ae.status="stale",ae.staleReason="superseded",fe.delete(ae.proposalId));let v=k.type==="loop.update.offered",P={id:k.id,kind:v?"loop-update-offer":"loop-delete-offer",proposalId:m.proposalId,loopId:String(m.loopId),name:m.name??m.base?.name??"",base:m.base??null,...v?{patch:m.patch??{},changed:Array.isArray(m.changed)?m.changed:Object.keys(m.patch||{})}:{reason:m.reason??""},selfEdit:!!m.selfEdit,status:"open",staleReason:null,ts:me(k),box:k.boxId,agent:k.provider};fe.set(m.proposalId,P),be.set(m.proposalId,P),t.push(P),re(P);break}case"loop.update.answered":{let v=be.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="applied"?"applied":"dismiss",fe.delete(m.proposalId));break}case"loop.delete.answered":{let v=be.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="delete"?"deleted":m.action==="pause"?"paused":"keep",fe.delete(m.proposalId));break}case"loop.updated":{let v=m.proposalId?be.get(m.proposalId):null;v&&(v.applied={changed:Array.isArray(m.changed)?m.changed:[],nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),t.push(dt(k,"loop-updated"));break}case"loop.deleted":t.push(dt(k,"loop-deleted"));break;case"loop.paused":t.push(dt(k,"loop-paused"));break;case"planning.change.offered":{if(!m.proposalId||!Array.isArray(m.ops)||!m.ops.length)break;for(let P of xe.values())P.status="stale",P.staleReason="superseded",xe.delete(P.proposalId);let v={id:k.id,kind:"planning-offer",proposalId:m.proposalId,why:m.why??"",ops:m.ops,base:m.base??{},resolved:m.resolved??{},snapped:!!m.snapped,createsMeRow:!!m.createsMeRow,status:"open",staleReason:null,applied:null,ts:me(k),box:k.boxId,agent:k.provider};xe.set(m.proposalId,v),Ke.set(m.proposalId,v),t.push(v),re(v);break}case"planning.change.answered":{let v=Ke.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="applied"?"applied":"dismiss",xe.delete(m.proposalId));break}case"planning.changed":{let v=m.proposalId?Ke.get(m.proposalId):null;v&&(v.applied={results:Array.isArray(m.results)?m.results:[]}),t.push(dt(k,"planning-changed"));break}case"rework.cause.offered":{if(!m.sha||le.has(m.sha))break;let v={id:k.id,kind:"rework-cause-offer",sha:m.sha,subject:m.subject??"",linesReworked:Number(m.linesReworked)||0,origins:Array.isArray(m.origins)?m.origins:[],heuristic:m.heuristic??null,intent:m.intent??null,shas:Array.isArray(m.shas)?m.shas:null,pr:m.pr??null,retracts:0,status:"open",cause:null,ts:me(k),box:k.boxId,agent:k.provider};le.set(m.sha,v),t.push(v);break}case"rework.cause.answered":{let v=m.sha?le.get(m.sha):null;v&&(v.status="answered",v.cause=m.cause??null);break}case"rework.cause.retracted":{let v=m.sha?le.get(m.sha):null;v&&(v.status="open",v.cause=null,v.retracts=(v.retracts??0)+1);break}case"ticket.created":{let v={items:Array.isArray(m.items)?m.items:[],failed:Array.isArray(m.failed)?m.failed:[],team:m.team??null},P=m.proposalId?M.get(m.proposalId):null;if(P){P.created=v;break}t.push({id:k.id,kind:"ticket-created",...v,ts:me(k),box:k.boxId,agent:k.provider});break}case"error":{if(!m.kind)break;t.push(dt(k,m.kind));break}case"provider.error":t.push({...dt(k,"provider-error"),provider:k.provider,box:k.boxId,agent:k.provider});break;case"usage.cap.hit":t.push(dt(k,"usage-cap-hit"));break;case"usage.cap.warning":t.push(dt(k,"usage-cap-warning"));break;case"provider.connected":{t.push({id:k.id,kind:"event",type:"provider-connected",provider:m.provider||k.provider,ts:me(k),at:me(k),box:k.boxId,agent:k.provider});break}case"prompt.answered":{if(Ce=null,m.control||!m.question)break;let v=m.groupId||m.promptId;if(!v)break;let P=$.get(v);P||(P={id:k.id,kind:"prompt",groupId:v,provider:k.provider,box:k.boxId,agent:k.provider,questionTotal:m.questionTotal??1,questions:[],ts:me(k)},$.set(v,P));let ae=m.questionIndex??0,ge={index:ae,header:m.header||"",question:m.question,multiSelect:!!m.multiSelect,chosen:Array.isArray(m.chosen)?m.chosen:[]},Me=P.questions.findIndex(Qe=>Qe.index===ae&&Le(Qe.question)===Le(ge.question));Me>=0?P.questions[Me]=ge:P.questions.push(ge),P.questions.sort((Qe,st)=>Qe.index-st.index),P.questionTotal=Math.max(P.questionTotal,m.questionTotal??1),P.questions.length>=P.questionTotal&&(t.push(P),$.delete(v));break}case"prompt.dismissed":{Ce=null;let v=m.groupId||m.promptId;if(!v||!m.question)break;let ae=$.get(v)||{id:k.id,kind:"prompt",groupId:v,provider:k.provider,box:k.boxId,agent:k.provider,questionTotal:m.questionTotal??1,questions:[],ts:me(k)},ge=m.questionIndex??0;ae.questions.some(Me=>Me.index===ge&&Le(Me.question)===Le(m.question))||(ae.questions.push({index:ge,header:m.header||"",question:m.question,multiSelect:!!m.multiSelect,chosen:[]}),ae.questions.sort((Me,ht)=>Me.index-ht.index)),ae.answeredVia="chat",t.push(ae),$.delete(v);break}default:break}}let _r=d.size?t.filter(k=>!k.retired):t;return d1(_r.sort((k,m)=>(k.ts??0)-(m.ts??0)))}function d1(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<=u1){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 Ch(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:p1(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??$n(e)).filter(o=>o.kind==="message");return E0(n,r)}function p1(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==Vs(e.command)?t:Lr(e.command,e.description||"")}function dt(e,t){let{ts:n,change:r,...o}=e.payload||{};return{id:e.id,kind:"event",type:t,...o,at:me(e),ts:me(e)}}function Mx(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)+f1,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&h1.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function Cx(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!w1.has(r.type)||!r.box)continue;let o=(r.ts??0)+m1,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 Tu(e){return(e||[]).filter(t=>g1.has(t?.kind)&&t.status==="open")}function Nx(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(Rx.has(o.change)&&o.number!=null)Ox(t,n,o);else if(o.change==="merged"||o.change==="closed"||o.change==="lost"){let s=Mh(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 me,Rx,Mh,u1,f1,h1,m1,w1,g1,Eu=q(()=>{Js();Fo();pu();Fi();Au();Oh();me=e=>e.payload?.ts??e.ts;Rx=new Set(["opened","ci-fixed","reconciled","pushed"]),Mh=e=>`${e.repo??""}#${e.number}`;u1=2*6e4;f1=12e4,h1=new Set(["archived","unarchived","opened"]);m1=2500,w1=new Set(["pr-opened","pr-merged","pr-auto-armed"]);g1=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 v1(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 A1(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 T1(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<k1&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<x1&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function jx(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=v1(e),s=A1(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=$n(e??[]).filter(p=>p?.kind==="message"&&typeof p.text=="string"&&p.text.trim()).slice(-y1);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(Bx[r]??Bx.failover),l.push(""),o.lastPush?.sha){let p=Dx(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=Dx(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=T1(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"}:** ${S1(p.text,b1)}`),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 P1(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(Hx.test(s)){if(r.length>=Qx)continue;let a=N1(s.replace(Hx,"").replace(/[.;,:]+$/,""),ev,M1);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>Ux&&(o=`${o.slice(0,Ux-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function tv(e,t,{timeoutMs:n=9e4}={}){let r=Hr.join(t,R1),o=Hr.join(r,O1);await Ur.mkdir(r,{recursive:!0});let s=await _u(o),a=await ye(e);if(!a)throw new Error(`session "${e}" not found`);return await Iu(e,a,C1(o)),await cv(o,s,n),P1(await Ur.readFile(o,"utf8"))}function nv(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===Jx||n===Zx;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(Vx)||n===_1||r.startsWith(Yx)||r.startsWith(Xx)}function rv(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-D1;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 ov(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 sv(e,{windows:t=[]}={}){if(nv(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 $u(e){return Nh.get(e)||null}async function av(e){let t=Nh.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await ye(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:Hr.join(n.cwd,Kx,Gx)};return Nh.set(e,r),L1(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function L1(e,t){e.step="summarize",await iv(e.name,t),e.step="clear";let n=await ye(e.name);await Iu(e.name,n,"/clear"),await Ph(B1),e.step="resume",await Iu(e.name,n,$1),e.step="done",e.finishedAt=Date.now()}async function iv(e,t,{timeoutMs:n=j1}={}){let r=Hr.join(t,Kx),o=Hr.join(r,Gx);await Ur.mkdir(r,{recursive:!0});let s=await _u(o),a=await ye(e);await ue({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await Iu(e,a,I1(o)),await cv(o,s,n),await ue({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await _u(o)});let i=await fx(t,o,{exclude:u=>nv(u.role,u.text)});await ue({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 Ur.readFile(o,"utf8");await C({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...zx(u),ts:Date.now()}})}catch{}}await Pf(t);let l=await F1(r,o);return c&&await Gk(c,l||o,{box:e,agent:a?.agent??null}),o}async function Ru(e,{timeoutMs:t}={}){let n=await ye(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return iv(e,n.cwd,t?{timeoutMs:t}:void 0)}async function Ou(e,t,{trackName:n="this Track"}={}){let r=await K({id:e,cwd:t}),o=jx(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=Hr.join(t,Wx);return await Ur.mkdir(Hr.dirname(s),{recursive:!0}),await Ur.writeFile(s,o,"utf8"),await C({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...zx(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),Wx}async function _u(e){try{return(await Ur.stat(e)).mtimeMs}catch{return 0}}async function cv(e,t,n){let r=Date.now()+n;for(;;){if(await _u(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await Ph(2e3)}}async function F1(e,t){try{if(!E1.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=Hr.join(e,`${n}.md`);return await Ur.copyFile(t,r),r}catch{return null}}function Ph(e){return new Promise(t=>setTimeout(t,e))}function qx(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 Iu(e,t,n){if(t){let o=qx(t.agent);await er({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await ye(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await We(e,["Escape"],{kind:"drive-wipe"}),await Ph(150),await qs(e,lu,{kind:"drive-wipe"}),await ot(e,n,{kind:"orchestration-drive",...In(r.agent,n)}),await Hi({sessionName:e,agent:r.agent,agentCmd:qx(r.agent),text:n,flow:"orchestration-drive"})}var Kx,Gx,Fx,Vx,Jx,Yx,_1,I1,$1,Xx,Zx,R1,O1,Ux,Qx,ev,M1,C1,Hx,N1,D1,B1,j1,Nh,Wx,Dh=q(()=>{ut();Bi();Tn();ji();vh();hx();wi();he();eu();Lx();Kx=".bostrat/handoff",Gx="latest.md",Fx=32*1024;Vx="Checkpoint: write a work-in-progress handoff",Jx="HANDOFF WRITTEN",Yx="Resume from .bostrat/handoff/latest.md",_1="/clear",I1=e=>`${Vx} 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 ${Jx} and take no further action.`,$1=`${Yx}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,Xx="Beat: write a",Zx="BEAT WRITTEN",R1=".bostrat/arc",O1="beat.txt",Ux=200,Qx=4,ev=5,M1=36,C1=e=>`${Xx} 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 ${Qx} further lines, each starting with "- " and naming ONE piece in at most ${ev} 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 ${Zx} and take no further action.`,Hx=/^(?:[-–—*•·]|\d+[.)])\s+/,N1=(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};D1=6e5;B1=3500,j1=3e5,Nh=new Map;Wx=".bostrat/handoff/status-latest.md"});function qr(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!Mu.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 dv(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:qr(o.from,"from",n),toMs:qr(o.to,"to",n)}))}return t}function jh(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 H1(e){if(!e)return null;let t=String(e).trim();return Wr[t]?t:lv[t]?lv[t]:Object.keys(Wr).find(r=>t.startsWith(r))||null}function W1(e){if(!e||e===Wr)return U1;let t=uv.get(e);return t||(t=dv(e),uv.set(e,t)),t}function pv(e,t,n={}){let{table:r,speed:o}=n,s=W1(r),a=r&&r!==Wr?s[e]?e:null:H1(e);if(!a)return null;let i=jh(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 Rn(e,t={},n,r){let o=pv(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 q1(e=Wr){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:qr(a.from,"from",n),toMs:qr(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`),Mu.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 G1({now:e=Date.now(),table:t=Wr,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...K1,...n},a=[],i=jh(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:qr(d.from,"from",c),toMs:qr(d.to,"to",c),verifiedMs:Mu.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*Bh;p&&i-d.verifiedMs>r*Bh&&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*Bh&&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 ea({now:e=Date.now(),table:t=Wr,...n}={}){let r=jh(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=qr(l.from,"from",i),d=qr(l.to,"to",i);r>=u&&r<=d&&Mu.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=[...q1(t),...G1({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var Wr,lv,Mu,U1,uv,fv,K1,Bh,nr=q(()=>{Wr={"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)."}]},lv={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"},Mu=/^\d{4}-\d{2}-\d{2}$/;U1=dv(Wr);uv=new WeakMap;fv=(e,t,n)=>pv(e,t,n)!==null;K1=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),Bh=864e5});function z1(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 Jt(e){let t=e.payload?.model||"unknown",n=z1(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=Rn(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&&!fv(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function Kr(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=hv();for(let p of n){let{usd:f,model:w,tokens:h,repriced:g,unpriced:y,asRecorded:x}=Jt(p);i+=x;let b=p.provider||"unknown",I=p.boxId||"unattributed";if(g&&c++,y&&(l++,u.add(w)),a+=f,mv(d,h),r[w]||={usd:0,tokens:hv(),events:0},r[w].usd+=f,r[w].events+=1,mv(r[w].tokens,h),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:ta(a),asRecordedUsd:ta(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Cu(r,p=>({...p,usd:ta(p.usd)})),byProvider:Cu(o,p=>({...p,usd:ta(p.usd)})),byBox:Cu(s,p=>({usd:ta(p.usd),events:p.events,provider:p.provider,label:p.label,models:[...p.models],...p.epochs?{epochs:Cu(p.epochs,f=>({usd:ta(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var hv,ta,Cu,Gr=q(()=>{nr();hv=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});ta=e=>Math.round(e*1e4)/1e4,Cu=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function V1(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 wv(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(Lh(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Lh(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(`${Lh(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=Kr(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?V1(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Lh,gv=q(()=>{Gr();Lh=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as J1}from"node:child_process";import{promisify as Y1}from"node:util";import{randomUUID as X1}from"node:crypto";async function Fh(e,t){let n=Date.now()+t;for(;;){if(await Qn(e))return!0;if(Date.now()>n)return!1;await zi(Z1)}}async function eB(e,t){let n=yv[t]||yv.default;n.escape&&(await We(e,["Escape"],{kind:"swap-exit"}),await zi(150)),n.wipe&&await qs(e,lu,{kind:"swap-exit"}),await uu(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=In(t).submitDelayMs||0;await zi(Math.max(r,250)),await We(e,["Enter"],{kind:"swap-exit"})}async function bv(e,t){try{let{stdout:n}=await Wo(ee,ne("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function vv(e){await Wo(ee,ne("respawn-pane","-k","-t",e))}async function Uh(e,{exitTimeoutMs:t=kv,fromAgent:n=null}={}){if(!De(e))throw new Error("invalid session name");let r=!1;if(await Qn(e))return{forced:r};let o=n||(await ye(e))?.agent||"agent";if(await eB(e,o),!await Fh(e,t)&&(r=!0,await ue({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await vv(e),!await Fh(e,xv)))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);return{forced:r}}async function Hh(e,t={}){if(!De(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await ye(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||Br(n),a=Number(t.exitTimeoutMs)||kv;await ue({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let{forced:i}=await Uh(e,{exitTimeoutMs:a,fromAgent:o}),c=s==="claude"&&Do(n),l=c?Rl():null,u=await bv(e,"ANTHROPIC_API_KEY"),d=!1;try{l?(await Wo(ee,ne("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await Wo(ee,ne("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),d=!0)}catch(g){throw l?ru(g,l):g}if(d&&!i&&(await vv(e),!await Fh(e,xv)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let p=!!t.resumeSessionId,f=t.resumeSessionId||X1(),w=p?n:Ei(n,f,e),h=p?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(w);return c&&await $r(r.cwd),s==="codex"&&await Os(r.cwd),s==="cursor"&&await Ms(r.cwd),await Bo(e),await Wo(ee,ne("set-option","-t",e,"@bostrat-sid",f)),await Wo(ee,ne("set-option","-t",e,"@bostrat-agent",s)),await zi(Q1),c&&Ti().length&&(await bv(e,Ai)||await Wo(ee,ne("set-environment","-t",e,Ai,"1")).catch(()=>{}),await ot(e,`export ${Ai}=1`,{kind:"swap-env"}),await zi(150)),await ot(e,w,{kind:"swap-launch"}),await ue({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:h,forced:i,envRespawned:d,launchCmd:w}}var Wo,zi,kv,xv,Z1,Q1,yv,Sv=q(()=>{ut();ai();Ml();Cl();ji();Tn();Wo=Y1(J1),zi=e=>new Promise(t=>setTimeout(t,e)),kv=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,xv=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,Z1=250,Q1=400,yv={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 sB(e){if(e.type==="queue-operation"&&e.operation==="enqueue")return Av.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"&&Av.test(t)?t:null}async function Tv(e,t={}){return(await _v(e,t)).launches}async function Ev(e,t={}){let{launches:n,done:r}=await _v(e,t);return n.filter(o=>!r.has(o.id))}async function _v(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await Mt(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(
|
|
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(
|
|
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:
|
|
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
|
|
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 IS(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&&!ES.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=_S.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!sj.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function Xi(e,t,{timeoutMs:n=rj}={}){let r=Date.now()+n;for(;;){let o=await Pe(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await Ku(oj)}}async function cj(e,t){return await ot(e,`/model ${t.model}`,In("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function lj(e,t){await cu(e),await ot(e,"/model",{enter:!1}),await Ku(250),await We(e,["Enter"],{kind:"model-switch"});let n=await Xi(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 We(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=ij(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await ot(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await Xi(e,i=>SS(i)||!vS(i)&&o(i)&&{done:o(i)});if(!s)throw await We(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?ES.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 We(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await ot(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Xi(e,i=>!SS(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 uj(){let e=xS.join(process.env.CURSOR_CONFIG_DIR||xS.join(nj.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await tj.readFile(e,"utf8"))?.model||null}catch{return null}}async function dj(e,t){await cu(e),await uu(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await Xi(e,AS),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 Xi(e,AS,{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 We(e,["Down"],{kind:"model-switch"}),await Ku(250)}if(!n||!s||!o(s)){await We(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await We(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 We(e,["Enter"],{kind:"model-switch"}),await Ku(800);let a=await uj();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 $S(e,t,n){return e==="codex"?lj(t,n):e==="cursor"?dj(t,n):cj(t,n)}var Ku,rj,oj,TS,sj,aj,ES,_S,RS=q(()=>{ut();ji();Ku=e=>new Promise(t=>setTimeout(t,e)),rj=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,oj=300,TS=[{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"}],sj=new Set([...TS.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"]),aj=[{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"}],ES=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),_S=[{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 Vu(e){let t=e||{},n=pj(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function pj(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:pt(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:pt(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:pt(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:pt(e.toAgent),payload:{from:e.from,fromAgent:e.fromAgent,to:e.to,toAgent:e.toAgent,reason:e.reason,handoffStartedAt:e.handoffStartedAt,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,singleBox:e.singleBox,forced:e.forced,recovered:e.recovered,checkpoint:e.checkpoint}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:pt(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:pt(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:pt(e.agent),payload:{...Qr(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:pt(e.agent||e.toAgent),payload:{kind:"subtask-failed",...Qr(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:pt(e.agent),payload:{change:"subtask-resumed",...Qr(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",...Qr(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...Qr(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...Qr(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:pt(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:pt(e.agent),payload:{change:"lead-refreshed",box:e.box,agent:e.agent,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,reason:e.reason}};case"transferred-in":return{type:e.mode==="recover"?"track.recovered":"track.transferred",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:pt(e.agent),payload:{...Qr(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Qr(e)}};case"retag-failed":return{type:"error",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:pt(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:pt(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};default:return null}}function CS(e,t){let n=Vu(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;Ju(()=>C(r,n))}async function NS(e){if(!(!e?.id||OS.has(e.id))){OS.add(e.id);try{let t=await K(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=Vu(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await C(e,o)}}catch{}}}function Qr(e){let{type:t,at:n,...r}=e;return r}function PS(e,t,n){!n?.promptId||zu.get(e.id)===n.promptId||(zu.set(e.id,n.promptId),Ju(()=>C(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:pt(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 DS(e,t,n,r={}){zu.delete(e.id),Ju(()=>C(e,{type:"prompt.dismissed",actor:"user",source:"orchestrator",boxId:t?.name,provider:pt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}function vm(e,t,n,r={}){zu.delete(e.id),Ju(()=>C(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:pt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var pt,Gu,Ju,MS,OS,zu,Sm=q(()=>{he();pt=e=>e||null;Gu=Promise.resolve(),Ju=e=>(Gu=Gu.then(e).catch(()=>{}),Gu),MS=()=>Gu;OS=new Set;zu=new Map});function gj(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function HS(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of gj(n).reverse()){if(!fj.test(r))continue;let o=r.endsWith("?")&&r.length<=wj&&mj.test(r);if(hj.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function qS(e){let t=String(e||""),n=[];yj.test(t)&&n.push("commit"),WS.test(t)&&n.push("rebase"),bj.test(t)&&n.push("push");let r=xj.test(t);!r&&kj.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 KS(e){return WS.test(String(e||""))}function ua(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 GS(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 zS(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<=vj&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function VS(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+Sj||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function JS(e){let t=(Array.isArray(e)?e:[]).filter(r=>BS[r]).map(r=>BS[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 jS,LS,FS,US,fj,hj,mj,wj,eo,yj,WS,bj,kj,xj,vj,Sj,BS,Zo=q(()=>{jS=/(?<!\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,LS=/(?<!\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,FS=/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,US=/(?<!\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,fj=new RegExp(`\\b(?:${jS}|${LS}|${FS}|${US})\\b`,"i"),hj=/\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,mj=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,wj=90;eo=["commit","rebase","push","pr"],yj=new RegExp(`\\b(?:${jS})\\b`,"i"),WS=new RegExp(`\\b(?:${US})\\b`,"i"),bj=new RegExp(`\\b(?:${LS})\\b`,"i"),kj=new RegExp(`\\b(?:${FS})\\b`,"i"),xj=/\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;vj=3e4;Sj=2500;BS={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import Aj from"node:crypto";import Yu from"node:fs/promises";import YS from"node:path";import Tj from"node:os";async function qe(){return to||(Am??=Yu.readFile(Em,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(to=e,!t&&Ue()){let{records:n,source:r}=await it("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.repo||o.data?.number==null)continue;let{id:s,updatedAt:a,...i}=o.data;to[ce(i.repo,i.number)]=i}Object.keys(to).length&&await _m().catch(()=>{})}}return to}).finally(()=>Am=null),Am)}async function _m(){let e=JSON.stringify({links:to},null,2),t=`${Em}.${process.pid}.tmp`;await Yu.mkdir(XS,{recursive:!0}),await Yu.writeFile(t,e),await Yu.rename(t,Em)}async function et(e){Tm=Tm.then(_m,_m),await Tm.catch(()=>{});let t=to?.[e];t&&vt("prlinks",{id:ZS(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function Lt({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 qe(),c=ce(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:Ej.has(a),firstSeen:Date.now()},await et(ce(e,t)),{link:i[c],created:!0})}async function or(e,t){if(!e||t==null)return null;let n=(await qe())[ce(e,t)];return!n||n.announced?null:(n.announced=!0,await et(ce(e,t)),n)}async function QS(e,t){if(!e||t==null)return!1;let n=(await qe())[ce(e,t)];return n?.notifyOnMerge?(delete n.notifyOnMerge,await et(ce(e,t)),!0):!1}async function ct(e,t){return!e||t==null?null:(await qe())[ce(e,t)]??null}async function no(e,t,n){if(!e||t==null)return null;let r=(await qe())[ce(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 et(ce(e,t)),r):null}async function eA(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await qe())[ce(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await et(ce(e,t)),o):null}async function tA(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await qe())[ce(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await et(ce(e,t)),o):null}async function Xu(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await qe())[ce(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await et(ce(e,t)),r)}async function Zu(e,t){if(!e||t==null)return null;let n=(await qe())[ce(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await et(ce(e,t)),n)}async function Qu(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await qe())[ce(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await et(ce(e,t)),r)}async function ed(e,t){if(!e||t==null)return null;let n=(await qe())[ce(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await et(ce(e,t)),n)}async function nA(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await qe())[ce(e,t)];return r?(r.baseFoldAt=n,await et(ce(e,t)),r):null}async function rA(e,t){if(!e||t==null)return null;let n=(await qe())[ce(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await et(ce(e,t)),n)}async function oA(e,t,n){if(!e||t==null||!n)return null;let r=(await qe())[ce(e,t)];return r?(r.lastHeadSha=n,await et(ce(e,t)),r):null}async function sA(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await qe())[ce(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 et(ce(e,t))),{prev:o,link:r}}async function Zi(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await qe())[ce(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await et(ce(e,t)),r):null}async function Qo(e,t,n=!0){if(!e||t==null)return null;let r=(await qe())[ce(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await et(ce(e,t)),r):null}async function es(e,t,n=!0){if(!e||t==null)return null;let r=(await qe())[ce(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await et(ce(e,t)),r):null}async function aA(e,t,n){if(!e||t==null)return null;let r=(await qe())[ce(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 et(ce(e,t)),r}async function iA(e,t,n){if(!e||t==null)return null;let r=(await qe())[ce(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 et(ce(e,t)),r}async function Qi(e,t){if(!e||t==null)return null;let n=(await qe())[ce(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 et(ce(e,t)),n)}async function cA(){return Object.values(await qe()).filter(e=>!_j.has(e.state))}async function pn(){return Object.values(await qe()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function lA(){if(!Ue())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await it("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await qe(),r=0;for(let[o,s]of Object.entries(n))await vt("prlinks",{id:ZS(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var XS,Em,ce,ZS,to,Am,Tm,Ej,_j,Mn=q(()=>{Dr();XS=process.env.BOSTRAT_STATE_DIR||YS.join(process.env.HOME||Tj.tmpdir(),".bostrat"),Em=YS.join(XS,"pr-links.json"),ce=(e,t)=>`${e}#${Number(t)}`,ZS=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=Aj.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},to=null,Am=null,Tm=Promise.resolve();Ej=new Set(["api","observed","reconciled"]);_j=new Set(["merged","closed","lost"])});import Ij from"node:fs";import Im from"node:fs/promises";import fA from"node:path";import $j from"node:os";function wA(e,t=!1){if(!t&&e-dA<Rj)return;dA=e;let n=JSON.stringify({windowStart:da,byCaller:Object.fromEntries(sr),lastHour:ro});pA=pA.then(()=>Im.mkdir(hA,{recursive:!0})).then(()=>Im.writeFile(`${td}.tmp`,n)).then(()=>Im.rename(`${td}.tmp`,td)).catch(()=>{})}function gA(e){e-da<mA||(ro={start:da,total:nd(sr),byCaller:Object.fromEntries(sr)},ro.total&&console.log(`[github] ${ro.total} calls last hour`,ro.byCaller),da=e,sr=new Map,wA(e,!0))}function ar(e="other",t=Date.now()){gA(t),sr.set(e,(sr.get(e)||0)+1),wA(t)}function yA(e=Date.now()){return gA(e),{windowStart:da,total:nd(sr),byCaller:Object.fromEntries(sr),lastHour:ro,backoffUntil:ts>e?ts:null}}function oo(e){return Oj.test(String(e?.message||e||""))}function so(e,t=Date.now()){return ts<=t&&console.warn(`[github] rate limited \u2014 backing off ${uA/6e4} min:`,String(e?.message||e||"").slice(0,200)),ts=Math.max(ts,t+uA),ts}function rd(e=Date.now()){return Math.max(0,ts-e)}var hA,td,mA,uA,Rj,da,sr,ro,ts,dA,nd,pA,Oj,ec=q(()=>{hA=process.env.BOSTRAT_STATE_DIR||fA.join(process.env.HOME||$j.tmpdir(),".bostrat"),td=fA.join(hA,"github-metrics.json"),mA=60*6e4,uA=5*6e4,Rj=6e4,da=Date.now(),sr=new Map,ro=null,ts=0,dA=0,nd=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(Ij.readFileSync(td,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<mA?(da=e.windowStart,sr=new Map(Object.entries(e.byCaller||{})),ro=e.lastHour||null):nd(new Map(Object.entries(e.byCaller||{})))&&(ro={start:e.windowStart,total:nd(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}pA=Promise.resolve();Oj=/rate limit|too quickly|abuse detection|HTTP 429/i});import bA from"node:fs/promises";import kA from"node:path";import Mj from"node:os";function sd({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};od.push(l),od.length>Nj&&od.shift();let u=$m.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)),$m.set(e,u),Pj(l),l}async function Pj(e){try{await bA.mkdir(xA,{recursive:!0}),await bA.appendFile(Cj,JSON.stringify(e)+`
|
|
182
|
-
`)}catch{}}function vA(){let e={};for(let[t,n]of $m)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:od.slice(-20)}}var xA,Cj,Nj,od,$m,ad=q(()=>{xA=process.env.BOSTRAT_STATE_DIR||kA.join(process.env.HOME||Mj.tmpdir(),".bostrat"),Cj=kA.join(xA,"github-latency.ndjson"),Nj=100,od=[],$m=new Map});import{execFile as Dj}from"node:child_process";import{promisify as Bj}from"node:util";async function jj(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([fa("git",["-C",e,"rev-parse","HEAD"]),fa("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 Xt(e,t,n={}){let{caller:r="prs",...o}=n,s=await Sn({cwd:e});ar(r);try{let{stdout:a}=await fa("gh",t,{cwd:e,...Lj,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw oo(a)&&so(a),a}}async function Mm(){if(id&&Date.now()-id.at<6e4)return id.data;let e;try{ar("auth");let t=await fa("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 pi();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return id={at:Date.now(),data:e},e}async function Fj(e){try{let{stdout:t}=await fa("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function AA(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 Rm(e){let t=(e||[]).map(AA),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 Nm(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function EA(e,t){if(!t)return null;let n=await _e(e);if(!n)return null;try{let r=await Xt(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 _A(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 IA(e,t){let n=await EA(e,t);return n===null?{...Rm([]),unavailable:!0}:Rm(_A(n))}function cd(e,t=Rm(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 Hj(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){pa.delete(s);continue}r.add(s),pa.has(s)||pa.set(s,n),o.mergeabilityPending=n-pa.get(s)<Uj}for(let o of pa.keys())o.startsWith(`${e}#`)&&!r.has(o)&&pa.delete(o);return t}async function ha(e,t){let n=await ir(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await Xt(n.path,["pr","view",String(r),"--json",Cm]);return cd(JSON.parse(o))}catch(o){if(!Nm(o))throw o;let s=await Xt(n.path,["pr","view",String(r),"--json",TA]),a=JSON.parse(s);return cd(a,await IA(n.path,a.headRefOid))}}async function Cn(e,{caller:t="prs"}={}){try{let n=await Xt(e,["pr","list","--json",Cm,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>cd(r)).sort(SA)}catch(n){if(!Nm(n))throw n;let r=await Xt(e,["pr","list","--json",TA,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>cd(o,await IA(e,o.headRefOid))))).sort(SA)}}function Zt(){tc.clear()}async function Kj(e,t){if(!e)return t;for(let n of t){let r=await ct(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 Xu(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Qu(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function $A(){let e=await Mm();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await Xn())await Fj(a.path)&&t.push(a);let n=Date.now(),r=rd(n)>0,o=await Promise.all(t.map(async a=>{let i=tc.get(a.path),c=i?.pending?qj:Wj;if(i&&(r||n-i.at<c))return i.entry;try{let l=await _e(a.path).catch(()=>null),u=Hj(a.path,await Cn(a.path,{caller:"tab"}),n);if(await Kj(l,u),i?.entry?.prs)for(let f of u)i.entry.prs.find(h=>h.number===f.number)?.checks?.pending>0&&f.checks.total>0&&f.checks.pending===0&&sd({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 tc.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:Nn(l)}}})),s=new Set(t.map(a=>a.path));for(let a of tc.keys())s.has(a)||tc.delete(a);return{auth:e,repos:o}}async function ir(e){let t=(await Xn()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function ns(e){return Cs(e)}async function ma(e,t,{method:n="squash"}={}){let r=await ir(e);if(!nc.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 Sn({cwd:r.path})).GH_TOKEN){let a=await Tf().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Xt(r.path,s)}catch(a){throw new Error(Nn(a))}finally{Zt()}return{ok:!0,number:o}}async function rc(e,t){let n=await ir(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Xt(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Nn(s))}finally{Zt()}let o=await _e(n.path).catch(()=>null);return o&&await nA(o,r).catch(()=>{}),{ok:!0,number:r}}async function Pm(e,t){let n=await ir(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Xt(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Nn(o))}finally{Zt()}return{ok:!0,number:r}}function Dm(e){return e.green&&!e.isDraft}function Jj(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 RA(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:Dm(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function OA(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function oc(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 MA(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(oc))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function CA(){let e={};for(let[t,n]of Om)e[t]={...n};return e}async function NA(e,{method:t="merge",onMerged:n=null}={}){let r=await ir(e);if(!nc.has(t))throw new Error(`invalid merge method: ${t}`);if(Om.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Cn(r.path)).filter(Dm).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 Om.set(r.path,s),Xj(s,o,{listPrs:()=>Cn(r.path),merge:a=>ma(r.path,a,{method:t}),sleep:Vj,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function Yj(e,t){let n=Date.now()+(t.settleMaxMs??zj),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(Gj)}}async function Xj(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 Yj(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: ${Nn(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(!Dm(a)){e.skipped.push({number:a.number,title:a.title,reason:Jj(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:Nn(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),Zt()}}async function Bm(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await fa("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Nn(l)}`)}await jj(e);let o=n||"";if((await Sn({cwd:e})).GH_TOKEN){let l=await Tf().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
|
|
153
|
+
`});function E1(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 Fx(e,t,n){let r=Dm(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=(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 P=v[0].split("/").pop().toLowerCase();m.has(P)||m.set(P,[]),m.get(P).push(x.payload?.ts??x.ts)}}let g=3*6e4,y=20*6e4,k=(x,h)=>!!(x?.name&&m.get(String(x.name).toLowerCase()))?.some(P=>P>=h-g&&P<=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:me(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:me(x)};h.toolUseId&&c.set(h.toolUseId,v);let P=String(h.summary||"").match(w);P&&u.set(P[1],v)}let b=new Map,I=new Map,R=null,N=new Map,D=new Map,j=new Map,T=new Map,$=new Map,F=new Map,J=new Map,M=new Map,S=new Map,_=new Map,H=new Map,K=new Map,X=new Map,pe=new Map,Oe=new Map,Me=[],Q=x=>Me.push(x),qe=x=>{if(!Me.length)return;let h=me(x);for(let v of Me){if(v.status!=="open"||v.box&&v.box!==x.boxId||(v.ts??0)>h)continue;let P=t.indexOf(v);P>=0&&t.splice(P,1),v.ts=h,t.push(v)}},Ae=new Map,Z=new Map,he=new Map,Te=new Map,z=new Map,ge=new Map,Ge=new Map,O=new Map,Y=()=>{for(let x of O.values())x.questions.length&&t.push(x);O.clear()},Le=null,Qe=new Map,oe=6e4,ye=new Map,Fe=(x,h)=>{let v=o.get(x);if(!v?.length)return!1;let P=Ue(h),ce=s.get(x);return v.some(be=>{if(!P.startsWith(be))return!1;let Ne=Ue(P.slice(be.length));return!!Ne&&!!ce?.has(Ne)})};for(let x of e||[]){let h=x.payload||{};switch(x.type){case"turn.user":{if(x.source==="hook"||(Me.length=0,!String(h.text||"").trim()&&!h.images?.length&&!h.attachments?.length))break;{let v=Mx(h.text);if(v){if(v.kind==="shell-input"){let P={id:x.id,kind:"shell",command:v.command,stdout:null,stderr:null,done:!1,ts:me(x),box:x.boxId,agent:x.provider};t.push(P),Qe.set(x.boxId,P)}else{let P=Qe.get(x.boxId);P?(P.stdout=v.stdout||null,P.stderr=v.stderr||null,P.done=!0,Qe.delete(x.boxId)):t.push({id:x.id,kind:"shell",command:null,stdout:v.stdout||null,stderr:v.stderr||null,done:!0,ts:me(x),box:x.boxId,agent:x.provider})}break}}{let v=h.note||Ji(h.text);if(v){if(v.kind==="task"){if(v.toolUseId&&a.has(v.toolUseId))break;let P=String(v.summary||"").match(w);if(!v.toolUseId&&P&&l.has(P[1]))break}if(v.kind==="command"&&Rx(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:me(x),box:x.boxId,agent:x.provider});break}v.kind!=="suppress"&&t.push({id:x.id,kind:"event",type:Nx(v),name:v.name??null,command:v.command??null,summary:v.summary??null,status:v.status??null,ts:me(x),box:x.boxId,agent:x.provider});break}}if(r.has(`${x.boxId}|${me(x)}`)||x.source==="transcript"&&Fe(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:me(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"&&(Le=null);break}for(let v of N.values())v.status="stale";N.clear();for(let v of j.values())v.status="stale";j.clear();for(let v of X.values())v.status="stale";X.clear(),Le&&Le.box===x.boxId&&(t.push(E1(Le)),Le=null),t.push({id:x.id,kind:"message",role:"user",text:h.text,ts:me(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:me(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=W0(h,{codexFailedText:Fo});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:me(x),ts:me(x),box:x.boxId,agent:x.provider});break}Y();let P=(h.images||[]).filter(rt=>!k(rt,me(x)));if(!String(h.text||"").trim()&&!P.length)break;if(x.provider==="cursor"){let rt=`${x.boxId}|${h.text}`;if(n.has(rt))break;n.add(rt)}(!Le||Le.box===x.boxId)&&(Le=null);let ce=x.provider==="codex"?(h.tools||[]).filter(rt=>!pm.has(rt)&&!Di.has(rt)):h.tools||[],be=h.text,Ne=null,gt=Mm(String(h.text||""));gt&&(be=gt.text,Ne={questions:gt.questions}),t.push({id:x.id,kind:"message",role:"assistant",text:be,ts:me(x),box:x.boxId,agent:x.provider,boxRole:h.boxRole,...Ne?{ask:Ne}:{},...ce.length?{tools:ce}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},...P.length?{images:P}:{}}),qe(x),Ne&&(Le={id:x.id,groupId:Cm(x.provider,h.sourceId||x.id,Ne.questions),provider:x.provider,box:x.boxId,questions:Ne.questions,ts:me(x)});break}case"job.started":{if(!h.id)break;let v=me(x),P=c.get(h.id);if(!P&&(h.kind||"agent")==="agent"&&h.name){let lt=u.get(h.name);lt&<.ts>=v&&(P=lt)}let ce=P?null:i.get(h.id)||null,be={jobId:h.id,jobKind:h.kind||"agent",name:h.name||null,detail:h.detail||null,status:P?P.status==="killed"?"stopped":P.status&&P.status!=="completed"?"failed":"done":ce?"stalled":"running",startedTs:v,finishedTs:P?.ts??ce?.ts??null,quietMs:ce?.quietMs??null,summary:P?.summary??null,resultHead:P?.resultHead??null},Ne=ye.get(x.boxId),gt=Ne?.jobs[Ne.jobs.length-1]?.startedTs;if(Ne&>!=null&&v-gt<=oe)Ne.jobs.push(be);else{let lt={id:x.id,kind:"job-dock",dockId:h.id,ts:v,box:x.boxId,agent:x.provider,jobs:[be]};ye.set(x.boxId,lt),t.push(lt)}break}case"handoff.done":t.push(ht(x,"lead-handoff"));break;case"subtask.delegated":t.push({...ht(x,"subtask-delegated"),to:x.boxId,toAgent:x.provider});break;case"subtask.merged":t.push(ht(x,"subtask-merged"));break;case"pr.state.changed":{if(!h.change)break;if(h.change==="opened"){for(let P of N.values())P.status="stale";N.clear();for(let P of j.values())P.status="stale";j.clear()}let v=ht(x,`pr-${h.change}`);if(v.box=x.boxId??null,Lx.has(h.change)&&h.number!=null){let P=Fx(b,I,h);if(b.size>=2){for(let be of I.values())for(let Ne of be)Ne.grouped=!0;v.groupPrs=[...b.values()];let ce=[v];for(let be of b.keys())I.set(be,ce)}else{let ce=I.get(P)??[];ce.push(v),I.set(P,ce)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let P=Dm(h),ce=R&&!R.complete||b.size>=2;if(b.has(P))b.delete(P),I.delete(P);else if(h.number!=null)for(let be of[...b.keys()])be.endsWith(`#${h.number}`)&&(b.delete(be),I.delete(be));if(ce&&h.number!=null){(!R||R.complete)&&(R={id:x.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:me(x),ts:me(x),box:x.boxId,agent:x.provider},t.push(R)),R.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:me(x)}),R.remaining=[...b.values()],R.complete=b.size===0;break}}t.push(v);break}case"pr.offered":{if(!h.offerId)break;for(let P of N.values())P.status="stale";N.clear();for(let P of j.values())P.status="stale";j.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:me(x),box:x.boxId,agent:x.provider};N.set(h.offerId,v),D.set(h.offerId,v),t.push(v),v.source==="cli"&&Q(v);break}case"pr.offer.answered":{let v=D.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}),N.delete(h.offerId));break}case"pr.exists.offered":{if(!h.offerId)break;for(let P of j.values())P.status="stale";j.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:me(x),box:x.boxId,agent:x.provider};j.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",j.delete(h.offerId));break}case"box.state.changed":{let v=h.change==="model"?"model-change":h.change;if(!v)break;let P=ht(x,v);if(v==="model-change"&&(P.to=h.model),v==="node-update"&&P.latest){let ce=t.findIndex(be=>be.kind==="event"&&be.type==="node-update"&&be.latest===P.latest);if(ce>=0){let be=t[ce];t[ce]={...P,ts:be.ts,at:be.at,firstAt:be.firstAt??be.ts,updatedAt:P.ts};break}}t.push(P);break}case"track.transferred":case"track.recovered":t.push(ht(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:me(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:me(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:me(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:me(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:me(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:me(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:me(x),box:x.boxId,agent:x.provider});break}case"box.fullscreen.detected":{if(!h.offerId)break;for(let P of J.values())P.box===(h.box||x.boxId)&&(P.status="stale",J.delete(P.offerId));let v={id:x.id,kind:"fullscreen-offer",offerId:h.offerId,status:"open",ts:me(x),box:h.box||x.boxId,agent:h.agent||x.provider};J.set(h.offerId,v),M.set(h.offerId,v),t.push(v);break}case"box.fullscreen.answered":{let v=M.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 P of $.values())P.key===h.key&&(P.status="stale",$.delete(P.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:me(x),box:x.boxId,agent:x.provider};$.set(h.offerId,v),F.set(h.offerId,v),t.push(v);break}case"ticket.move.answered":{let v=F.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(ht(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:me(x),box:x.boxId,agent:x.provider});break}case"ticket.flow.changed":t.push(ht(x,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let P of S.values())P.status="stale";S.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:me(x),box:x.boxId,agent:x.provider};S.set(h.proposalId,v),_.set(h.proposalId,v),t.push(v),Q(v);break}case"ticket.create.answered":{let v=_.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",v.createdCount=h.createdCount??null,v.failedCount=h.failedCount??null,S.delete(h.proposalId));break}case"loop.create.offered":{if(!h.proposalId)break;for(let P of H.values())P.status="stale";H.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:me(x),box:x.boxId,agent:x.provider};H.set(h.proposalId,v),K.set(h.proposalId,v),t.push(v),Q(v);break}case"loop.create.answered":{let v=K.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",H.delete(h.proposalId));break}case"followup.armed":{if(!h.armId)break;let v=h.replaces?Oe.get(h.replaces):null;v&&v.status==="armed"&&(v.status="replaced");let P={id:x.id,kind:"followup-armed",armId:h.armId,trigger:h.trigger??"now",label:h.label??"",prompt:h.prompt??"",why:h.why??null,status:"armed",ts:me(x),box:x.boxId,agent:x.provider};Oe.set(h.armId,P),P.trigger!=="now"&&t.push(P);break}case"followup.armed.cancelled":{let v=Oe.get(h.armId);v&&v.status==="armed"&&(v.status="cancelled");break}case"followup.offered":{if(!h.offerId)break;for(let ce of X.values())ce.trigger===h.trigger&&(ce.status="stale",X.delete(ce.offerId));let v=Array.isArray(h.rows)?h.rows:[];for(let ce of v){let be=ce.armId?Oe.get(ce.armId):null;be&&be.status==="armed"&&(be.status="offered")}let P={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:{},rows:v.map(ce=>({followupId:ce.followupId??null,armId:ce.armId??null,label:ce.label??"",prompt:ce.prompt??null,promptPreview:ce.promptPreview??null,promptHash:ce.promptHash??null,origin:ce.origin??"operator",why:ce.why??null})),status:"open",answer:null,ts:me(x),box:x.boxId,agent:x.provider};X.set(h.offerId,P),pe.set(h.offerId,P),t.push(P),P.trigger==="now"&&Q(P);break}case"followup.answered":{let v=pe.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},X.delete(h.offerId));break}case"followup.create.offered":{if(!h.proposalId)break;for(let P of Ae.values())P.status="stale";Ae.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:me(x),box:x.boxId,agent:x.provider};Ae.set(h.proposalId,v),Z.set(h.proposalId,v),t.push(v),Q(v);break}case"followup.create.answered":{let v=Z.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",Ae.delete(h.proposalId));break}case"followup.created":{let v=h.proposalId?Z.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(ht(x,"loop-created"));break}case"loop.update.offered":case"loop.delete.offered":{if(!h.proposalId||!h.loopId)break;for(let ce of he.values())ce.loopId===h.loopId&&(ce.status="stale",ce.staleReason="superseded",he.delete(ce.proposalId));let v=x.type==="loop.update.offered",P={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:me(x),box:x.boxId,agent:x.provider};he.set(h.proposalId,P),Te.set(h.proposalId,P),t.push(P),Q(P);break}case"loop.update.answered":{let v=Te.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="applied"?"applied":"dismiss",he.delete(h.proposalId));break}case"loop.delete.answered":{let v=Te.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="delete"?"deleted":h.action==="pause"?"paused":"keep",he.delete(h.proposalId));break}case"loop.updated":{let v=h.proposalId?Te.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(ht(x,"loop-updated"));break}case"loop.deleted":t.push(ht(x,"loop-deleted"));break;case"loop.paused":t.push(ht(x,"loop-paused"));break;case"planning.change.offered":{if(!h.proposalId||!Array.isArray(h.ops)||!h.ops.length)break;for(let P of z.values())P.status="stale",P.staleReason="superseded",z.delete(P.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:me(x),box:x.boxId,agent:x.provider};z.set(h.proposalId,v),ge.set(h.proposalId,v),t.push(v),Q(v);break}case"planning.change.answered":{let v=ge.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?ge.get(h.proposalId):null;v&&(v.applied={results:Array.isArray(h.results)?h.results:[]}),t.push(ht(x,"planning-changed"));break}case"rework.cause.offered":{if(!h.sha||Ge.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:me(x),box:x.boxId,agent:x.provider};Ge.set(h.sha,v),t.push(v);break}case"rework.cause.answered":{let v=h.sha?Ge.get(h.sha):null;v&&(v.status="answered",v.cause=h.cause??null);break}case"rework.cause.retracted":{let v=h.sha?Ge.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},P=h.proposalId?_.get(h.proposalId):null;if(P){P.created=v;break}t.push({id:x.id,kind:"ticket-created",...v,ts:me(x),box:x.boxId,agent:x.provider});break}case"error":{if(!h.kind)break;t.push(ht(x,h.kind));break}case"provider.error":t.push({...ht(x,"provider-error"),provider:x.provider,box:x.boxId,agent:x.provider});break;case"usage.cap.hit":t.push(ht(x,"usage-cap-hit"));break;case"usage.cap.warning":t.push(ht(x,"usage-cap-warning"));break;case"provider.connected":{t.push({id:x.id,kind:"event",type:"provider-connected",provider:h.provider||x.provider,ts:me(x),at:me(x),box:x.boxId,agent:x.provider});break}case"prompt.answered":{if(Le=null,h.control||!h.question)break;let v=h.groupId||h.promptId;if(!v)break;let P=O.get(v);P||(P={id:x.id,kind:"prompt",groupId:v,provider:x.provider,box:x.boxId,agent:x.provider,questionTotal:h.questionTotal??1,questions:[],ts:me(x)},O.set(v,P));let ce=h.questionIndex??0,be={index:ce,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},Ne=P.questions.findIndex(rt=>rt.index===ce&&Ue(rt.question)===Ue(be.question));Ne>=0?P.questions[Ne]=be:P.questions.push(be),P.questions.sort((rt,lt)=>rt.index-lt.index),P.questionTotal=Math.max(P.questionTotal,h.questionTotal??1),P.questions.length>=P.questionTotal&&(t.push(P),O.delete(v));break}case"prompt.dismissed":{Le=null;let v=h.groupId||h.promptId;if(!v||!h.question)break;let ce=O.get(v)||{id:x.id,kind:"prompt",groupId:v,provider:x.provider,box:x.boxId,agent:x.provider,questionTotal:h.questionTotal??1,questions:[],ts:me(x)},be=h.questionIndex??0;ce.questions.some(Ne=>Ne.index===be&&Ue(Ne.question)===Ue(h.question))||(ce.questions.push({index:be,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:[]}),ce.questions.sort((Ne,gt)=>Ne.index-gt.index)),ce.answeredVia="chat",t.push(ce),O.delete(v);break}default:break}}let jt=d.size?t.filter(x=>!x.retired):t;return I1(jt.sort((x,h)=>(x.ts??0)-(h.ts??0)))}function I1(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<=_1){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 Bm(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:$1(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 P0(n,r)}function $1(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==Ys(e.command)?t:Ur(e.command,e.description||"")}function ht(e,t){let{ts:n,change:r,...o}=e.payload||{};return{id:e.id,kind:"event",type:t,...o,at:me(e),ts:me(e)}}function Ux(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)+R1,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&O1.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function Hx(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!C1.has(r.type)||!r.box)continue;let o=(r.ts??0)+M1,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 Ru(e){return(e||[]).filter(t=>N1.has(t?.kind)&&t.status==="open")}function Wx(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(Lx.has(o.change)&&o.number!=null)Fx(t,n,o);else if(o.change==="merged"||o.change==="closed"||o.change==="lost"){let s=Dm(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 me,Lx,Dm,_1,R1,O1,M1,C1,N1,Ou=W(()=>{Xs();Uo();gu();Wi();$u();Pm();me=e=>e.payload?.ts??e.ts;Lx=new Set(["opened","ci-fixed","reconciled","pushed"]),Dm=e=>`${e.repo??""}#${e.number}`;_1=2*6e4;R1=12e4,O1=new Set(["archived","unarchived","opened"]);M1=2500,C1=new Set(["pr-opened","pr-merged","pr-auto-armed"]);N1=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 L1(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 U1(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 H1(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<B1&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<j1&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function Vx(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=L1(e),s=U1(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(-P1);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(Gx[r]??Gx.failover),l.push(""),o.lastPush?.sha){let p=Kx(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=Kx(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=H1(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"}:** ${F1(p.text,D1)}`),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<=qx?d:`${d.slice(0,qx)}
|
|
155
|
+
|
|
156
|
+
[\u2026 brief truncated]`}var P1,D1,qx,B1,j1,F1,Kx,Gx,zx=W(()=>{Ou();P1=12,D1=2e3,qx=48e3,B1=30,j1=10;F1=(e,t)=>{let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1)}\u2026`},Kx=(e,t)=>e&&t?Math.max(1,Math.round((e-t)/6e4)):null;Gx={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 W1 from"node:fs";import Wr from"node:fs/promises";import qr from"node:path";function nv(e){let t=Po(String(e??"")),n=Buffer.byteLength(t,"utf8");return n<=Jx?{content:t,bytes:n,truncated:!1}:{content:`${Buffer.from(t,"utf8").subarray(0,Jx).toString("utf8").replace(/�+$/,"")}
|
|
157
|
+
|
|
158
|
+
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function Z1(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(Xx.test(s)){if(r.length>=cv)continue;let a=X1(s.replace(Xx,"").replace(/[.;,:]+$/,""),lv,J1);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>Yx&&(o=`${o.slice(0,Yx-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function uv(e,t,{timeoutMs:n=9e4}={}){let r=qr.join(t,V1),o=qr.join(r,z1);await Wr.mkdir(r,{recursive:!0});let s=await Mu(o),a=await ke(e);if(!a)throw new Error(`session "${e}" not found`);return await Cu(e,a,Y1(o)),await gv(o,s,n),Z1(await Wr.readFile(o,"utf8"))}function dv(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===ov||n===iv;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(rv)||n===q1||r.startsWith(sv)||r.startsWith(av)}function pv(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-Q1;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 fv(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 mv(e,{windows:t=[]}={}){if(dv(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 Nu(e){return jm.get(e)||null}async function hv(e){let t=jm.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await ke(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:qr.join(n.cwd,ev,tv)};return jm.set(e,r),nB(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function nB(e,t){e.step="summarize",await wv(e.name,t),e.step="clear";let n=await ke(e.name);await Cu(e.name,n,"/clear"),await Lm(eB),e.step="resume",await Cu(e.name,n,G1),e.step="done",e.finishedAt=Date.now()}async function wv(e,t,{timeoutMs:n=tB}={}){let r=qr.join(t,ev),o=qr.join(r,tv);await Wr.mkdir(r,{recursive:!0});let s=await Mu(o),a=await ke(e);await de({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await Cu(e,a,K1(o)),await gv(o,s,n),await de({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await Mu(o)});let i=await vx(t,o,{exclude:u=>dv(u.role,u.text)});await de({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 Wr.readFile(o,"utf8");await C({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...nv(u),ts:Date.now()}})}catch{}}await Ff(t);let l=await rB(r,o);return c&&await t0(c,l||o,{box:e,agent:a?.agent??null}),o}async function Pu(e,{timeoutMs:t}={}){let n=await ke(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return wv(e,n.cwd,t?{timeoutMs:t}:void 0)}async function Du(e,t,{trackName:n="this Track"}={}){let r=await q({id:e,cwd:t}),o=Vx(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=qr.join(t,Zx);return await Wr.mkdir(qr.dirname(s),{recursive:!0}),await Wr.writeFile(s,o,"utf8"),await C({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...nv(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),Zx}async function Mu(e){try{return(await Wr.stat(e)).mtimeMs}catch{return 0}}async function gv(e,t,n){let r=Date.now()+n;for(;;){if(await Mu(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await Lm(2e3)}}async function rB(e,t){try{if(!W1.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=qr.join(e,`${n}.md`);return await Wr.copyFile(t,r),r}catch{return null}}function Lm(e){return new Promise(t=>setTimeout(t,e))}function Qx(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 Cu(e,t,n){if(t){let o=Qx(t.agent);await rr({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await ke(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await ze(e,["Escape"],{kind:"drive-wipe"}),await Lm(150),await Gs(e,mu,{kind:"drive-wipe"}),await it(e,n,{kind:"orchestration-drive",...On(r.agent,n)}),await Ki({sessionName:e,agent:r.agent,agentCmd:Qx(r.agent),text:n,flow:"orchestration-drive"})}var ev,tv,Jx,rv,ov,sv,q1,K1,G1,av,iv,V1,z1,Yx,cv,lv,J1,Y1,Xx,X1,Q1,eB,tB,jm,Zx,Fm=W(()=>{mt();ea();In();Ui();Em();Sx();bi();fe();su();zx();ev=".bostrat/handoff",tv="latest.md",Jx=32*1024;rv="Checkpoint: write a work-in-progress handoff",ov="HANDOFF WRITTEN",sv="Resume from .bostrat/handoff/latest.md",q1="/clear",K1=e=>`${rv} 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 ${ov} and take no further action.`,G1=`${sv}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,av="Beat: write a",iv="BEAT WRITTEN",V1=".bostrat/arc",z1="beat.txt",Yx=200,cv=4,lv=5,J1=36,Y1=e=>`${av} 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 ${cv} further lines, each starting with "- " and naming ONE piece in at most ${lv} 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 ${iv} and take no further action.`,Xx=/^(?:[-–—*•·]|\d+[.)])\s+/,X1=(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};Q1=6e5;eB=3500,tB=3e5,jm=new Map;Zx=".bostrat/handoff/status-latest.md"});function Gr(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!Bu.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 kv(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:Gr(o.from,"from",n),toMs:Gr(o.to,"to",n)}))}return t}function Hm(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 sB(e){if(!e)return null;let t=String(e).trim();return Kr[t]?t:yv[t]?yv[t]:Object.keys(Kr).find(r=>t.startsWith(r))||null}function aB(e){if(!e||e===Kr)return oB;let t=bv.get(e);return t||(t=kv(e),bv.set(e,t)),t}function xv(e,t,n={}){let{table:r,speed:o}=n,s=aB(r),a=r&&r!==Kr?s[e]?e:null:sB(e);if(!a)return null;let i=Hm(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=xv(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 iB(e=Kr){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:Gr(a.from,"from",n),toMs:Gr(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`),Bu.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 lB({now:e=Date.now(),table:t=Kr,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...cB,...n},a=[],i=Hm(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:Gr(d.from,"from",c),toMs:Gr(d.to,"to",c),verifiedMs:Bu.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*Um;p&&i-d.verifiedMs>r*Um&&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*Um&&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 ra({now:e=Date.now(),table:t=Kr,...n}={}){let r=Hm(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=Gr(l.from,"from",i),d=Gr(l.to,"to",i);r>=u&&r<=d&&Bu.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=[...iB(t),...lB({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var Kr,yv,Bu,oB,bv,vv,cB,Um,sr=W(()=>{Kr={"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)."}]},yv={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"},Bu=/^\d{4}-\d{2}-\d{2}$/;oB=kv(Kr);bv=new WeakMap;vv=(e,t,n)=>xv(e,t,n)!==null;cB=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),Um=864e5});function uB(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function Av(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=uB(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&&!vv(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function Vr(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=Sv();for(let p of n){let{usd:f,model:w,tokens:m,repriced:g,unpriced:y,asRecorded:k}=Xt(p);i+=k;let b=p.provider||"unknown",I=p.boxId||"unattributed";if(g&&c++,y&&(l++,u.add(w)),a+=f,Av(d,m),r[w]||={usd:0,tokens:Sv(),events:0},r[w].usd+=f,r[w].events+=1,Av(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 R=(s[I].epochs||={})[p.epoch]||={usd:0,events:0,provider:b,models:new Set};R.usd+=f,R.events+=1,R.provider=b,w!=="unknown"&&R.models.add(w)}}return{totalUsd:oa(a),asRecordedUsd:oa(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:ju(r,p=>({...p,usd:oa(p.usd)})),byProvider:ju(o,p=>({...p,usd:oa(p.usd)})),byBox:ju(s,p=>({usd:oa(p.usd),events:p.events,provider:p.provider,label:p.label,models:[...p.models],...p.epochs?{epochs:ju(p.epochs,f=>({usd:oa(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var Sv,oa,ju,zr=W(()=>{sr();Sv=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});oa=e=>Math.round(e*1e4)/1e4,ju=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function dB(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 Tv(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(Wm(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Wm(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(`${Wm(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=Vr(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?dB(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Wm,Ev=W(()=>{zr();Wm=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as pB}from"node:child_process";import{promisify as fB}from"node:util";import{randomUUID as mB}from"node:crypto";async function qm(e,t){let n=Date.now()+t;for(;;){if(await nr(e))return!0;if(Date.now()>n)return!1;await Yi(hB)}}async function gB(e,t){let n=_v[t]||_v.default;n.escape&&(await ze(e,["Escape"],{kind:"swap-exit"}),await Yi(150)),n.wipe&&await Gs(e,mu,{kind:"swap-exit"}),await hu(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=On(t).submitDelayMs||0;await Yi(Math.max(r,250)),await ze(e,["Enter"],{kind:"swap-exit"})}async function Iv(e,t){try{let{stdout:n}=await qo(ne,re("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function Ov(e){await qo(ne,re("respawn-pane","-k","-t",e))}async function Km(e,{exitTimeoutMs:t=$v,fromAgent:n=null}={}){if(!De(e))throw new Error("invalid session name");let r=!1;if(await nr(e))return{forced:r};let o=n||(await ke(e))?.agent||"agent";if(await gB(e,o),!await qm(e,t)&&(r=!0,await de({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await Ov(e),!await qm(e,Rv)))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);return{forced:r}}async function Gm(e,t={}){if(!De(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await ke(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||Lr(n),a=Number(t.exitTimeoutMs)||$v;await de({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let{forced:i}=await Km(e,{exitTimeoutMs:a,fromAgent:o}),c=s==="claude"&&Bo(n),l=c?Dl():null,u=await Iv(e,"ANTHROPIC_API_KEY"),d=!1;try{l?(await qo(ne,re("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await qo(ne,re("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),d=!0)}catch(g){throw l?cu(g,l):g}if(d&&!i&&(await Ov(e),!await qm(e,Rv)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let p=!!t.resumeSessionId,f=t.resumeSessionId||mB(),w=p?n:$i(n,f,e),m=p?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(w);return c&&await Or(r.cwd),s==="codex"&&await Ms(r.cwd),s==="cursor"&&await Cs(r.cwd),await jo(e),await qo(ne,re("set-option","-t",e,"@bostrat-sid",f)),await qo(ne,re("set-option","-t",e,"@bostrat-agent",s)),await Yi(wB),c&&Ii().length&&(await Iv(e,_i)||await qo(ne,re("set-environment","-t",e,_i,"1")).catch(()=>{}),await it(e,`export ${_i}=1`,{kind:"swap-env"}),await Yi(150)),await it(e,w,{kind:"swap-launch"}),await de({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 qo,Yi,$v,Rv,hB,wB,_v,Mv=W(()=>{mt();li();jl();Ll();Ui();In();qo=fB(pB),Yi=e=>new Promise(t=>setTimeout(t,e)),$v=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Rv=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,hB=250,wB=400,_v={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 Vm(e=process.env){return!!(Ce(e)&&e.BOSTRAT_PLANE_TOKEN)}async function zm({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!Vm(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),Cv);try{let a=await Nv(`${Ce(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 Xi(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(!Vm(a))return{ok:!1,source:"unconfigured"};let c=Lu.get(e.id)||0;if(i()-c<yB)return{ok:!1,source:"debounced"};Lu.set(e.id,i());let l=await Zi({env:a}),u=l.loops.find(f=>f.trackId===e.id);if(!u)return Lu.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||Lu.delete(e.id),{...p,loop:{id:u.id,name:u.name}}}async function Zi({env:e=process.env}={}){if(!Vm(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),Cv);try{let r=await Nv(`${Ce(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 Cv,Nv,yB,Lu,Fu=W(()=>{yt();Cv=1e4,Nv=(...e)=>fetch(...e);yB=10*6e4,Lu=new Map});import{execFile as bB}from"node:child_process";import Uu from"node:fs/promises";import Jm from"node:path";import{promisify as kB}from"node:util";function _B(e){let n=String(e||"").trim().match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.+-]+)?/);return n?n[0]:null}async function IB(e,{env:t=process.env}={}){let n=String(e||"").trim();if(!n)return null;let r=n.includes("/")?[Jm.resolve(n)]:String(t.PATH||"").split(Jm.delimiter).filter(Boolean).map(o=>Jm.join(o,n));for(let o of r)try{await Uu.access(o,Uu.constants.X_OK);let s=await Uu.realpath(o),a=await Uu.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[AB[e]]||SB[e]:"")||"").trim();if(!r)return null;let{realClaude:o,realCodex:s,realCursor:a}=Fi({agent:e,agentCmd:r});if(!(o||s||a))return null;let i=await IB(r.split(/\s+/)[0],{env:n});if(!i)return null;let c=`${i.path}@${i.mtimeMs}`;return Ym.has(c)||Ym.set(c,Promise.resolve().then(()=>EB(i.path,{agent:e})).then(l=>_B(l)).catch(()=>null)),Ym.get(c)}var xB,vB,SB,AB,Ym,TB,EB,Pv=W(()=>{ea();xB=kB(bB),vB=8e3,SB={claude:"claude",codex:"codex",cursor:"cursor-agent"},AB={claude:"BOSTRAT_AGENT_CMD",codex:"BOSTRAT_CODEX_CMD",cursor:"BOSTRAT_CURSOR_CMD"},Ym=new Map,TB=async e=>(await xB(e,["--version"],{timeout:vB})).stdout,EB=TB});function CB(e){if(e.type==="queue-operation"&&e.operation==="enqueue")return Dv.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"&&Dv.test(t)?t:null}async function Bv(e,t={}){return(await Lv(e,t)).launches}async function jv(e,t={}){let{launches:n,done:r}=await Lv(e,t);return n.filter(o=>!r.has(o.id))}async function Lv(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await Ct(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=CB(d);if(p){let m=p.match(MB)?.[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?Ys(g):null),detail:g?Ys(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=NB(m.content);if($B.test(g)&&s.add(m.tool_use_id),g.match(RB)){let k=i.get(m.tool_use_id);k&&(i.delete(m.tool_use_id),k.proven=!0,k.outputFile=g.match(OB)?.[1]?.replace(/\.$/,"")||null,o.set(k.id,k))}}}}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 $B,RB,OB,Dv,MB,NB,Fv=W(()=>{dn();Xs();$B=/^Async (?:agent|task) launched/i,RB=/^Command did not complete within its [^\n]*?moved to the background \(ID: ([^)]+)\)/i,OB=/Output is being written to:\s*(\S+)/i,Dv=/^\s*<task-notification>/i,MB=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i;NB=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 Jr(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 PB(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 Uv(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:pn(`read ${Jr(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${sa(r)}`:`Read ${sa(r)}`}}case"Grep":{let r=t?.pattern;if(!r)return null;let o=t?.path?Jr(t.path,n):null,s=t?.glob?` --glob ${Xm(t.glob)}`:"",a=pn(String(r),40);return{command:pn(`grep ${Xm(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?Jr(t.target_directory,n):null;return{command:pn(`glob ${Xm(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${pn(String(r),40)}\u201D`}}case"StrReplace":return t?.path?{command:pn(`edit ${Jr(t.path,n)}`,140),description:`Edit ${sa(t.path)}`}:null;case"Write":return t?.path?{command:pn(`write ${Jr(t.path,n)}`,140),description:`Write ${sa(t.path)}`}:null;case"Delete":return t?.path?{command:pn(`delete ${Jr(t.path,n)}`,140),description:`Delete ${sa(t.path)}`}:null;case"ReadLints":{let o=(Array.isArray(t?.paths)?t.paths:[]).map(s=>Jr(s,n)).filter(Boolean);return{command:pn(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=PB(t);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:pn(`apply_patch ${Jr(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${sa(r[0])}`}}case"Await":{let r=t?.shell_id??t?.task_id;return{command:pn(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var Hu,pn,sa,Xm,Hv=W(()=>{Hu=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),pn=(e,t=88)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,sa=e=>String(e).replace(/\/+$/,"").split("/").pop()||String(e);Xm=e=>`"${String(e).replace(/"/g,'\\"')}"`});import Qi from"node:fs";import DB from"node:fs/promises";import Go from"node:path";import BB from"node:os";import jB from"node:crypto";import LB from"node:zlib";function Kv(e){let t=jB.createHash("md5").update(String(e)).digest("hex");return Go.join(FB,t)}function Gv(e){try{let t=JSON.parse(Qi.readFileSync(Go.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 UB(e){let t=Kv(e),n;try{n=Qi.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=Go.join(t,o.name),a=Go.join(s,"store.db");if(!Qi.existsSync(a))continue;let i=Gv(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 Wu(e,t){if(!e)return null;if(t){let n=Go.join(Kv(e),t),r=Go.join(n,"store.db");if(Qi.existsSync(r))return{id:t,dir:n,store:r,...Gv(n)||{createdAtMs:null,updatedAtMs:null}}}return UB(e)}function Wv(e,t){let n=e.prepare("SELECT data FROM blobs WHERE id = ?").get(t);return n?Buffer.from(n.data):null}function HB(e){if(!e)return null;let t=e;if(t.length>1&&t[0]===31&&t[1]===139)try{t=LB.gunzipSync(t)}catch{}try{return JSON.parse(t.toString("utf8"))}catch{return null}}function WB(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 qB(e,t){if(!Array.isArray(e))return[];let n=[];for(let r of e){if(!r||r.type!=="tool-call"||!Hu.has(r.toolName))continue;let o=Uv(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 Zm(e,t){if(!qv)return aa;let{DatabaseSync:n}=qv,r;try{r=new n(e,{readOnly:!0})}catch{return aa}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 aa;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return aa}let a=s.lastUsedModel||null,i=s.latestRootBlobId?Wv(r,s.latestRootBlobId):null;if(!i)return{...aa,model:a};let c=[],l=[],u=[],d=null,p=[];for(let f of WB(i)){let w=HB(Wv(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 k of w.content){let b=k?.providerOptions?.cursor?.modelName;b&&(d=b)}l.push(...qB(w.content,t).map((k,b)=>({...k,id:k.id||`${f}:${b}`}))),u.push(...GB(w.content));let g=KB(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 aa}finally{try{r.close()}catch{}}}function Vv(e,t){let n=e.updatedAtMs;if(!n)try{n=Math.round(Qi.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 zv(e,t,{limit:n=80}={}){let r=Wu(e,t);if(!r)return[];let{turns:o,model:s}=await Zm(r.store,e);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=Vv(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 Jv(e,t){let n=Wu(e,t);if(!n)return null;let{turns:r,requestIds:o,model:s}=await Zm(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 Yv(e,t,{limit:n=80}={}){let r=Wu(e,t);if(!r)return[];let{turns:o,trailingCommands:s}=await Zm(r.store,e);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=Vv(r,a.length||1),l=[];a.forEach((u,d)=>{for(let p of u.commands)l.push({...p,summary:Ur(p.command,p.description),ts:c(d)})});for(let u of s)l.push({...u,summary:Ur(u.command,u.description),ts:i+1});return l}async function Xv(e,t){let n=Wu(e,t);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await DB.stat(n.store)).mtimeMs)}catch{return null}}var FB,KB,GB,qv,aa,Qm=W(()=>{Xs();Hv();FB=process.env.BOSTRAT_CURSOR_CHATS||Go.join(BB.homedir(),".cursor","chats");KB=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():"",GB=e=>Array.isArray(e)?e.filter(t=>t&&t.type==="tool-call"&&t.toolName).map(t=>t.toolName):[],qv=process.getBuiltinModule?.("node:sqlite"),aa={turns:[],trailingCommands:[],model:null,requestIds:[]}});import VB from"node:fs/promises";function rh(e){return Zv.set(e.agent,e),e}function Yr(e){return Zv.get(e)||zB}async function oh(e,t={}){return(await Yr(e.agent).readMessages(e,t)).map(qu(e))}async function Qv(e,t={}){return(await Yr(e.agent).readCommands(e,t)).map(qu(e))}async function eS(e,t={}){return(await Yr(e.agent).readJobs(e,t)).map(qu(e))}async function tS(e,t={}){return(await Yr(e.agent).readOpenJobs(e,t)).map(qu(e))}var ia,eh,th,nh,zB,Zv,qu,sh=W(()=>{dn();yu();Xs();Fv();Qm();Uo();ia=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}},eh=class extends ia{transcript(t){return!t?.cwd||!t?.sessionId?null:un(t.cwd,t.sessionId)}async readMessages(t,n){let r=this.transcript(t);return r?mm(r,n):[]}async readCommands(t,n){let r=this.transcript(t);return r?N0(r,n):[]}async readJobs(t,n){let r=this.transcript(t);return r?Bv(r,n):[]}async readOpenJobs(t,n){let r=this.transcript(t);return r?jv(r,n):[]}async updatedAt(t){let n=this.transcript(t);if(!n)return null;try{return Math.round((await VB.stat(n)).mtimeMs)}catch{return null}}},th=class extends ia{async readMessages(t,n){return zv(t.cwd,t.sessionId,n)}async readCommands(t,n){return Yv(t.cwd,t.sessionId,n)}async updatedAt(t){return Xv(t.cwd,t.sessionId)}},nh=class extends ia{sessionId(t){return t?.pinned?t.sessionId:null}async readMessages(t,n){return j0(t.cwd,this.sessionId(t),n)}async readCommands(t,n){return L0(t.cwd,this.sessionId(t),n)}async updatedAt(t){return F0(t.cwd,this.sessionId(t))}},zB=new ia("unknown"),Zv=new Map;rh(new eh("claude",lm));rh(new th("cursor",Hu));rh(new nh("codex",Di));qu=e=>t=>({...t,box:e.name,agent:e.agent,boxRole:e.role})});function Zr(e){let t=e.search(lh);return(t>=0?e.slice(0,t):e).trim()}function JB(e){if(!e)return"";let t=e.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!t||/hidden|\d+\s+lines?/i.test(t)?"":t}function YB(e){let t=String(e).trim();return!!(!t||Xr.test(e)||lh.test(t)||/hidden|\d+\s+lines?/i.test(t)||/notespress|press n to add/i.test(t)||/^\s*notes?\s*$/i.test(t))}function XB(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=ZB(n,t);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function ZB(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 nS(e){return e.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function QB(e,t=""){let n=String(e);if(!oS.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(Vo);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&&!Xr.test(r[u])&&a.push(p)}}if(s.length<2)return null;let i=s.map(u=>({label:Zr(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 tj(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||!sS.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(Vo);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])||Xr.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:Zr(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 nj(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(Vo);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 oj(e,t=""){let n=String(e);if(!ah.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
164
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(rj.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(Vo);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&&!Xr.test(r[u])&&a.push(p)}}if(s.length<2||!s.some(u=>ah.test(u.label)))return null;let i=s.map(u=>({label:Zr(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 uh(e){let t=[];for(let r=0;r<e.length;r++){let o=e[r].match(Vo);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 sj(e,t=""){let n=String(e);if(!aS.test(n)||!iS.test(n)||zo.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
165
|
+
`),o=uh(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:Zr(s.label),description:""}))}]}:null}function aj(e,t=""){let n=String(e);if(!cS.test(n)||zo.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
166
|
+
`),o=uh(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:Zr(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 cj(e,t){let n=e[t].trim();for(let r=t+1;r<e.length;r++){let o=e[r].trim();if(!o||!ij.test(o)||dh.test(e[r]))break;n+=o}return n}function lj(e,t=""){let n=String(e);if(!dh.test(n)||zo.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=cj(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 dj(e,t=""){let n=String(e);if(!ih.test(n)||!uj.test(n)||zo.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
168
|
+
`)].reverse().find(a=>ih.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 pj(e,t=""){let n=String(e);if(!lS.test(n)||zo.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
169
|
+
`),o=uh(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:Zr(s.label),description:""}))}]}:null}function fj(e,t=""){let n=String(e);if(zo.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(uS);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(Xr.test(r[l])||lh.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 mj(e){let t=-1;for(let r=e.length-1;r>=0;r--)if(ch.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 hj(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(Ku.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&&(Xr.test(o)||/^submit\b/i.test(o))?r:null}function wj(e,t=""){let n=String(e);if(!rS.test(n)||!ch.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
171
|
+
`),o=mj(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let $=i;$<c;$++){let F=r[$].match(Vo);F&&(u<0&&(u=$),l.push({line:$,cursor:!!F[1],checked:F[3]!=null&&F[3]!==" ",hasCheckbox:F[3]!=null,label:F[4].trim(),desc:""}))}if(!l.length)return null;for(let $ of l)for(let F=$.line+1;F<Math.min(r.length,$.line+4)&&!(Vo.test(r[F])||Xr.test(r[F]));F++){let J=r[F].trim();if(!(!J||YB(r[F])||Ku.test(J))){$.desc=JB(J);break}}let d=hj(l,r),p=l.filter($=>$!==d&&!Ku.test($.label));if(!p.length)return null;let f=p.some($=>$.hasCheckbox),w=d?.cursor?p.length:Math.max(0,p.findIndex($=>$.cursor)),m=d&&!Ku.test(d.label)?Zr(d.label):"",g=s>=0?s+1:0,y=[];for(let $=g;$<u;$++){let F=r[$].trim();F&&!Xr.test(r[$])&&!/[☐☒]/.test(r[$])&&y.push(F)}let k=y.join(" ")||(s>=0?nS(r[s]):"")||"The agent is asking a question",b=s>=0?XB(r[s],k):null,I=b?.currentTabLabel||(s>=0?nS(r[s]):""),R=p.map($=>({label:Zr($.label),description:$.desc})),N=R.map($=>$.label).join("|"),j=`claude:${b?`q${b.questionIndex+1}/${b.questionTotal}`:"q1/1"}:${I}:${k}:${N}`.slice(0,240),T=b?`claude:ask:${b.tabs.map($=>$.label).join("|")}`.slice(0,240):j;return{provider:"claude",box:t,multiSelect:f,cursorIndex:w,promptId:j,groupId:T,...d?{custom:!0,customIndex:p.length,customText:m}:{},dismissable:!0,...b?{questionIndex:b.questionIndex,questionTotal:b.questionTotal,currentTabLabel:b.currentTabLabel,tabs:b.tabs}:{},questions:[{question:k,header:I,multiSelect:f,options:R}]}}function Qr(e,t){let n=String(t||"");return n?e==="claude"?rS.test(n)&&ch.test(n)||oS.test(n)||ah.test(n)||sS.test(n.trimEnd().split(`
|
|
172
|
+
`).pop()||"")?!0:zo.test(n)?!1:aS.test(n)&&iS.test(n)||cS.test(n)||dh.test(n)||ih.test(n)||lS.test(n)||uS.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 Nn(e,t){return!e||!t?null:e.agent==="claude"?nj(t,e.name)||wj(t,e.name)||QB(t,e.name)||oj(t,e.name)||dj(t,e.name)||lj(t,e.name)||aj(t,e.name)||sj(t,e.name)||pj(t,e.name)||fj(t,e.name)||tj(t,e.name):e.agent==="cursor"||e.agent==="codex"?tx(e.agent,t,e.name):null}async function ph(e){if(!e)return null;let t=await ln(e.name).catch(()=>"");return Nn(e,t)}function fh(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 dS(e,t,n=""){let r=typeof n=="string"?n.trim():"";if(!r||!e?.custom)return{pre:fh(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 pS(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 rS,ch,Vo,Ku,Xr,lh,oS,sS,ej,ah,rj,zo,aS,iS,cS,dh,ij,ih,uj,lS,uS,mh=W(()=>{mt();ku();rS=/to navigate\b.*\bEsc to cancel|Enter to select\b/,ch=/Chat about this/i,Vo=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Ku=/^(type something|chat about this|submit)\b/i,Xr=/^[─—-]{6,}\s*$/,lh=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;oS=/^\s*Switch model\?\s*$/m;sS=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,ej=/^\s*[─—▔-]{6,}\s*$/;ah=/Resume from summary/i,rj=/^\s*This session is\b/;zo=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;aS=/Choose the text style that looks best with your terminal/,iS=/Let's get started\./;cS=/Select login method:/;dh=/Paste code here if prompted/,ij=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;ih=/OAuth error:/,uj=/Press Enter to retry\./;lS=/Quick safety check: Is this a project you created or one you trust/;uS=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function Gu(e,t,n){if(!e||!t)return null;for(let r of gj)if(r.provider===e.agent)try{if(r.match(t,n))return r}catch{}return null}function bj(e){let t=hh.get(e);return t||(t={sentAt:[],last:0,emitted:new Set},hh.set(e,t)),t}function hS(e,t,n=Date.now()){let r=bj(e);if(r.sentAt=r.sentAt.filter(s=>n-s<mS),r.sentAt.length>=fS)return{action:"give-up"};if(n-r.last<yj)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 wS(e,t=Date.now()){let n=hh.get(e);return n?n.sentAt.filter(r=>t-r<mS).length>=fS:!1}var gj,yj,fS,mS,hh,wh=W(()=>{gj=[{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"]}];yj=5e3,fS=5,mS=3600*1e3,hh=new Map});function ca(e){return typeof e=="string"?e.trim():""}var gS=W(()=>{});import{execFile as kj}from"node:child_process";import{promisify as xj}from"node:util";async function Ye(e,t,{input:n}={}){let r=yh("git",["-C",e,...t],{maxBuffer:yS});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function la(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 bS(e){let{stdout:t}=await Ye(e,["rev-parse","--show-toplevel"]);return t.trim()}async function vj(e){try{return await Ye(e,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function ua(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)=>yh("git",["-C",e,...a],{maxBuffer:yS,env:o,...i});try{let a=await vj(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 yh("rm",["-f",r])}catch{}}}async function bh(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 Jo(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 Yo(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 Xo(e,t,n,r){return await Ye(e,["worktree","add","--no-track","-b",n,t,r]),t}async function kh(e,t){try{return await Ye(e,["rev-parse","--verify","--quiet",`refs/heads/${t}`]),!0}catch{return!1}}async function ec(e,t,n){return await Ye(e,["worktree","add","--end-of-options",t,n]),t}async function Zo(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 xh(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 Sj(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 vh(e,t){return!t||!t.length?e:[...e,"--",".",...t.map(n=>`:(exclude)${n}`)]}async function Sh(e,t,n,r){let{stdout:o}=await Ye(e,vh(["diff","--shortstat",t,n],r));return Sj(o)}async function Ah(e,t,n,r){let{stdout:o}=await Ye(e,vh(["diff","--name-only",t,n],r));return o.split(`
|
|
176
|
+
`).map(s=>s.trim()).filter(Boolean)}async function Th(e,t,n,r){let{stdout:o}=await Ye(e,vh(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await gh(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await gh(e,o,["--3way","--whitespace=nowarn"]),a=await Aj(e);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await gh(e,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function gh(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 Aj(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 yh,yS,Vu=W(()=>{yh=xj(kj),yS=64*1024*1024});function zu(e,{at:t=Date.now(),reason:n="node-transfer"}={}){let r=!1;for(let o of e?.boxes||[]){if(!o||typeof o!="object")continue;!Tj.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 je,Tj,Zt=W(()=>{je=e=>!!(e&&(e.archivedAt||e.transferredTo)),Tj=new Set(["merged","conflict","canceled","failed","handed-off"])});import Eh from"node:fs/promises";import xS from"node:path";import Ej from"node:os";function TS(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function ES(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function _S(e){let t=new Date(TS(e));return t.setDate(t.getDate()+7),t.getTime()}function IS(e){let t=new Date(ES(e));return t.setMonth(t.getMonth()+1),t.getTime()}function Yu(e=Date.now()){for(let[t,n]of Object.entries(da)){let r=n.startOf(e);if(r!==ar[t]){ar[t]=r;for(let o of Qo.values())o[t]=0;for(let o of pa)o.includes(`:${t}:`)&&!o.endsWith(`:${r}`)&&pa.delete(o)}}}function $S(e,t){let n=fn?.caps?.[e]?.[da[t].capKey];return Number.isFinite(n)&&n>0?n:null}function eo(e,t=Date.now()){if(!e||!Ih||!fn?.caps)return null;Yu(t);let n=Qo.get(e);for(let r of Object.keys(da)){let o=$S(e,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:e,window:r,limitUsd:o,spentUsd:Ju(s),resetsAt:da[r].nextStart(t)}}return null}function RS(e,t){let n=`hit:${e}:${t.provider}:${t.window}:${ar[t.window]}`;return pa.has(n)?!1:(pa.add(n),!0)}function kS(e,t,n,r=Date.now()){if(!e||!t)return;Yu(r);let o=_j(e);for(let[s,a]of Object.entries(da))n>=ar[s]&&n<a.nextStart(r)&&(o[s]+=t)}async function Ij(e,t,n=Date.now()){if(!fn?.caps||!_h)return;let r=Qo.get(e);if(r)for(let[o,s]of Object.entries(da)){let a=$S(e,o);if(a==null)continue;let i=r[o]||0;if(i<a*AS||i>=a)continue;let c=`warn:${e}:${o}:${ar[o]}`;if(!pa.has(c)){pa.add(c);try{let l=await _h(t);if(!l)continue;await C(l,{type:"usage.cap.warning",actor:"system",provider:e,payload:{provider:e,window:o,limitUsd:a,spentUsd:Ju(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function OS(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 Xu(e,{source:t="local",updatedAt:n=Date.now()}={}){fn={caps:OS(e),updatedAt:n,source:t};try{await Eh.mkdir(vS,{recursive:!0}),await Eh.writeFile(SS(),JSON.stringify(fn,null,2))}catch{}return tc()}function tc(e=Date.now()){Yu(e);let t=new Set([...Qo.keys(),...Object.keys(fn?.caps||{})]),n={};for(let r of t){let o=Qo.get(r);n[r]={weeklyUsd:Ju(o?.weekly||0),monthlyUsd:Ju(o?.monthly||0)}}return{caps:fn?.caps||{},configured:!!fn,seeded:Ih,updatedAt:fn?.updatedAt??null,spent:n,windows:{weekly:{start:ar.weekly,resetsAt:_S(e)},monthly:{start:ar.monthly,resetsAt:IS(e)}},warnPct:AS}}async function MS({listTracks:e,getTrack:t}={}){_h=t||null,Yu();try{let o=JSON.parse(await Eh.readFile(SS(),"utf8"));fn={caps:OS(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{fn=null}let n=Date.now();Xe.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=Xt(o);kS(o.provider||"unknown",s,o.ts??Date.now()),Ij(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(ar.weekly,ar.monthly);for(let o of await e?.()||[]){let s;try{s=await q(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||kS(a.provider||"unknown",Xt(a).usd,i,n)}}return Ih=!0,tc()}var vS,SS,AS,da,fn,Ih,ar,Qo,pa,_h,_j,Ju,Zu=W(()=>{fe();zr();vS=process.env.BOSTRAT_STATE_DIR||xS.join(process.env.HOME||Ej.tmpdir(),".bostrat"),SS=()=>xS.join(vS,"usage-caps.json"),AS=.8;da={weekly:{startOf:TS,nextStart:_S,capKey:"weeklyUsd"},monthly:{startOf:ES,nextStart:IS,capKey:"monthlyUsd"}},fn=null,Ih=!1,ar={weekly:0,monthly:0},Qo=new Map,pa=new Set,_h=null,_j=e=>{let t=Qo.get(e);return t||(t={weekly:0,monthly:0},Qo.set(e,t)),t};Ju=e=>Math.round(e*1e4)/1e4});import $j from"node:fs/promises";import Rj from"node:os";import CS from"node:path";function $h(e){return e==="codex"?Nj:e==="cursor"?LS:BS}function NS(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 PS(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 Pj(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 DS(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 FS(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&&!jS.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=LS.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!Cj.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function nc(e,t,{timeoutMs:n=Oj}={}){let r=Date.now()+n;for(;;){let o=await Pe(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await Qu(Mj)}}async function Dj(e,t){return await it(e,`/model ${t.model}`,On("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function Bj(e,t){await fu(e),await it(e,"/model",{enter:!1}),await Qu(250),await ze(e,["Enter"],{kind:"model-switch"});let n=await nc(e,NS);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await ze(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=Pj(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await it(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await nc(e,i=>PS(i)||!NS(i)&&o(i)&&{done:o(i)});if(!s)throw await ze(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?jS.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 ze(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await it(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await nc(e,i=>!PS(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 jj(){let e=CS.join(process.env.CURSOR_CONFIG_DIR||CS.join(Rj.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await $j.readFile(e,"utf8"))?.model||null}catch{return null}}async function Lj(e,t){await fu(e),await hu(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await nc(e,DS),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 nc(e,DS,{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 ze(e,["Down"],{kind:"model-switch"}),await Qu(250)}if(!n||!s||!o(s)){await ze(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await ze(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 ze(e,["Enter"],{kind:"model-switch"}),await Qu(800);let a=await jj();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 US(e,t,n){return e==="codex"?Bj(t,n):e==="cursor"?Lj(t,n):Dj(t,n)}var Qu,Oj,Mj,BS,Cj,Nj,jS,LS,HS=W(()=>{mt();Ui();Qu=e=>new Promise(t=>setTimeout(t,e)),Oj=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,Mj=300,BS=[{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"}],Cj=new Set([...BS.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"]),Nj=[{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"}],jS=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),LS=[{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 nd(e){let t=e||{},n=Fj(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function Fj(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:ct(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:ct(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:ct(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:ct(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:ct(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:ct(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:ct(e.agent),payload:{...Pn(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:ct(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:ct(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:ct(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:ct(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:ct(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:ct(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:ct(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};case"delivery-held":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{kind:"delivery-held",...Pn(e)}};case"startup-gate":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{kind:"startup-gate",...Pn(e)}};default:return null}}function KS(e,t){let n=nd(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;rd(()=>C(r,n))}async function GS(e){if(!(!e?.id||WS.has(e.id))){WS.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=nd(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 VS(e,t,n){!n?.promptId||td.get(e.id)===n.promptId||(td.set(e.id,n.promptId),rd(()=>C(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:ct(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 zS(e,t,n,r={}){td.delete(e.id),rd(()=>C(e,{type:"prompt.dismissed",actor:"user",source:"orchestrator",boxId:t?.name,provider:ct(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}function Rh(e,t,n,r={}){td.delete(e.id),rd(()=>C(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:ct(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var ct,ed,rd,qS,WS,td,Oh=W(()=>{fe();ct=e=>e||null;ed=Promise.resolve(),rd=e=>(ed=ed.then(e).catch(()=>{}),ed),qS=()=>ed;WS=new Set;td=new Map});function Kj(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function eA(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of Kj(n).reverse()){if(!Uj.test(r))continue;let o=r.endsWith("?")&&r.length<=qj&&Wj.test(r);if(Hj.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function nA(e){let t=String(e||""),n=[];Gj.test(t)&&n.push("commit"),tA.test(t)&&n.push("rebase"),Vj.test(t)&&n.push("push");let r=Jj.test(t);!r&&zj.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 rA(e){return tA.test(String(e||""))}function fa(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 oA(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 sA(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<=Yj&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function aA(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+Xj||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function iA(e){let t=(Array.isArray(e)?e:[]).filter(r=>JS[r]).map(r=>JS[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 YS,XS,ZS,QS,Uj,Hj,Wj,qj,to,Gj,tA,Vj,zj,Jj,Yj,Xj,JS,es=W(()=>{YS=/(?<!\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,XS=/(?<!\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,ZS=/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,QS=/(?<!\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,Uj=new RegExp(`\\b(?:${YS}|${XS}|${ZS}|${QS})\\b`,"i"),Hj=/\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,Wj=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,qj=90;to=["commit","rebase","push","pr"],Gj=new RegExp(`\\b(?:${YS})\\b`,"i"),tA=new RegExp(`\\b(?:${QS})\\b`,"i"),Vj=new RegExp(`\\b(?:${XS})\\b`,"i"),zj=new RegExp(`\\b(?:${ZS})\\b`,"i"),Jj=/\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;Yj=3e4;Xj=2500;JS={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import Zj from"node:crypto";import od from"node:fs/promises";import cA from"node:path";import Qj from"node:os";function pA(e){dA=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 ir||(Mh??=od.readFile(Nh,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(ir=e,!t&&Ke()){let{records:n,source:r}=await dt("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){let o=Zk(),s=await dA().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;ir[ae(u.repo,u.number)]=u}a&&console.log(`[pr-links] restore: ${Object.keys(ir).length} link(s) re-seeded, ${a} other-node record(s) left on the plane`),Object.keys(ir).length&&await Ph().catch(()=>{})}}return ir}).finally(()=>Mh=null),Mh)}async function Ph(){let e=JSON.stringify({links:ir},null,2),t=`${Nh}.${process.pid}.tmp`;await od.mkdir(lA,{recursive:!0}),await od.writeFile(t,e),await od.rename(t,Nh)}async function Ze(e){Ch=Ch.then(Ph,Ph),await Ch.catch(()=>{});let t=ir?.[e];t&&At("prlinks",{id:uA(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=ae(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:tL.has(a),firstSeen:Date.now()},await Ze(ae(e,t)),{link:i[c],created:!0})}async function cr(e,t){if(!e||t==null)return null;let n=(await He())[ae(e,t)];return!n||n.announced?null:(n.announced=!0,await Ze(ae(e,t)),n)}async function fA(e,t){if(!e||t==null)return!1;let n=(await He())[ae(e,t)];return n?.notifyOnMerge?(delete n.notifyOnMerge,await Ze(ae(e,t)),!0):!1}async function pt(e,t){return!e||t==null?null:(await He())[ae(e,t)]??null}async function no(e,t,n){if(!e||t==null)return null;let r=(await He())[ae(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(ae(e,t)),r):null}async function mA(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await He())[ae(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Ze(ae(e,t)),o):null}async function hA(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await He())[ae(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Ze(ae(e,t)),o):null}async function sd(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await He())[ae(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Ze(ae(e,t)),r)}async function ad(e,t){if(!e||t==null)return null;let n=(await He())[ae(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Ze(ae(e,t)),n)}async function id(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await He())[ae(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Ze(ae(e,t)),r)}async function cd(e,t){if(!e||t==null)return null;let n=(await He())[ae(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Ze(ae(e,t)),n)}async function wA(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await He())[ae(e,t)];return r?(r.baseFoldAt=n,await Ze(ae(e,t)),r):null}async function gA(e,t,{misses:n,until:r}){if(!e||t==null)return null;let o=(await He())[ae(e,t)];return o?(o.brokerRefused={misses:Number(n)||0,until:Number(r)||0},await Ze(ae(e,t)),o):null}async function yA(e,t){if(!e||t==null)return null;let n=(await He())[ae(e,t)];return n?.brokerRefused?(delete n.brokerRefused,await Ze(ae(e,t)),n):null}async function bA(e,t){if(!e||t==null)return null;let n=(await He())[ae(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Ze(ae(e,t)),n)}async function kA(e,t,n){if(!e||t==null||!n)return null;let r=(await He())[ae(e,t)];return r?(r.lastHeadSha=n,await Ze(ae(e,t)),r):null}async function xA(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await He())[ae(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(ae(e,t))),{prev:o,link:r}}async function rc(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await He())[ae(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Ze(ae(e,t)),r):null}async function ts(e,t,n=!0){if(!e||t==null)return null;let r=(await He())[ae(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Ze(ae(e,t)),r):null}async function ns(e,t,n=!0){if(!e||t==null)return null;let r=(await He())[ae(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Ze(ae(e,t)),r):null}async function vA(e,t,n){if(!e||t==null)return null;let r=(await He())[ae(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(ae(e,t)),r}async function SA(e,t,n){if(!e||t==null)return null;let r=(await He())[ae(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(ae(e,t)),r}async function oc(e,t){if(!e||t==null)return null;let n=(await He())[ae(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(ae(e,t)),n)}async function AA(){return Object.values(await He()).filter(e=>!nL.has(e.state))}async function mn(){return Object.values(await He()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function TA(){if(!Ke())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:uA(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var lA,Nh,ae,uA,ir,Mh,Ch,dA,tL,nL,Dn=W(()=>{jr();lA=process.env.BOSTRAT_STATE_DIR||cA.join(process.env.HOME||Qj.tmpdir(),".bostrat"),Nh=cA.join(lA,"pr-links.json"),ae=(e,t)=>`${e}#${Number(t)}`,uA=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=Zj.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},ir=null,Mh=null,Ch=Promise.resolve(),dA=async()=>new Set;tL=new Set(["api","observed","reconciled"]);nL=new Set(["merged","closed","lost"])});import rL from"node:fs";import Dh from"node:fs/promises";import $A from"node:path";import oL from"node:os";function MA(e,t=!1){if(!t&&e-_A<sL)return;_A=e;let n=JSON.stringify({windowStart:ma,byCaller:Object.fromEntries(lr),lastHour:ro});IA=IA.then(()=>Dh.mkdir(RA,{recursive:!0})).then(()=>Dh.writeFile(`${ld}.tmp`,n)).then(()=>Dh.rename(`${ld}.tmp`,ld)).catch(()=>{})}function CA(e){e-ma<OA||(ro={start:ma,total:ud(lr),byCaller:Object.fromEntries(lr)},ro.total&&console.log(`[github] ${ro.total} calls last hour`,ro.byCaller),ma=e,lr=new Map,MA(e,!0))}function ur(e="other",t=Date.now()){CA(t),lr.set(e,(lr.get(e)||0)+1),MA(t)}function NA(e=Date.now()){return CA(e),{windowStart:ma,total:ud(lr),byCaller:Object.fromEntries(lr),lastHour:ro,backoffUntil:rs>e?rs:null}}function oo(e){return aL.test(String(e?.message||e||""))}function so(e,t=Date.now()){return rs<=t&&console.warn(`[github] rate limited \u2014 backing off ${EA/6e4} min:`,String(e?.message||e||"").slice(0,200)),rs=Math.max(rs,t+EA),rs}function dd(e=Date.now()){return Math.max(0,rs-e)}var RA,ld,OA,EA,sL,ma,lr,ro,rs,_A,ud,IA,aL,sc=W(()=>{RA=process.env.BOSTRAT_STATE_DIR||$A.join(process.env.HOME||oL.tmpdir(),".bostrat"),ld=$A.join(RA,"github-metrics.json"),OA=60*6e4,EA=5*6e4,sL=6e4,ma=Date.now(),lr=new Map,ro=null,rs=0,_A=0,ud=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(rL.readFileSync(ld,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<OA?(ma=e.windowStart,lr=new Map(Object.entries(e.byCaller||{})),ro=e.lastHour||null):ud(new Map(Object.entries(e.byCaller||{})))&&(ro={start:e.windowStart,total:ud(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}IA=Promise.resolve();aL=/rate limit|too quickly|abuse detection|HTTP 429/i});import PA from"node:fs/promises";import DA from"node:path";import iL from"node:os";function fd({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};pd.push(l),pd.length>lL&&pd.shift();let u=Bh.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)),Bh.set(e,u),uL(l),l}async function uL(e){try{await PA.mkdir(BA,{recursive:!0}),await PA.appendFile(cL,JSON.stringify(e)+`
|
|
182
|
+
`)}catch{}}function jA(){let e={};for(let[t,n]of Bh)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:pd.slice(-20)}}var BA,cL,lL,pd,Bh,md=W(()=>{BA=process.env.BOSTRAT_STATE_DIR||DA.join(process.env.HOME||iL.tmpdir(),".bostrat"),cL=DA.join(BA,"github-latency.ndjson"),lL=100,pd=[],Bh=new Map});import{execFile as dL}from"node:child_process";import{promisify as pL}from"node:util";async function fL(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([wa("git",["-C",e,"rev-parse","HEAD"]),wa("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});ur(r);try{let{stdout:a}=await wa("gh",t,{cwd:e,...mL,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw oo(a)&&so(a),a}}async function Fh(){if(hd&&Date.now()-hd.at<6e4)return hd.data;let e;try{ur("auth");let t=await wa("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 hi();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return hd={at:Date.now(),data:e},e}async function hL(e){try{let{stdout:t}=await wa("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function FA(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 jh(e){let t=(e||[]).map(FA),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 Hh(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function HA(e,t){if(!t)return null;let n=await $e(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 WA(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 qA(e,t){let n=await HA(e,t);return n===null?{...jh([]),unavailable:!0}:jh(WA(n))}function wd(e,t=jh(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 gL(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){ha.delete(s);continue}r.add(s),ha.has(s)||ha.set(s,n),o.mergeabilityPending=n-ha.get(s)<wL}for(let o of ha.keys())o.startsWith(`${e}#`)&&!r.has(o)&&ha.delete(o);return t}async function ga(e,t){let n=await dr(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",Uh]);return wd(JSON.parse(o))}catch(o){if(!Hh(o))throw o;let s=await Qt(n.path,["pr","view",String(r),"--json",UA]),a=JSON.parse(s);return wd(a,await qA(n.path,a.headRefOid))}}async function Bn(e,{caller:t="prs"}={}){try{let n=await Qt(e,["pr","list","--json",Uh,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>wd(r)).sort(LA)}catch(n){if(!Hh(n))throw n;let r=await Qt(e,["pr","list","--json",UA,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>wd(o,await qA(e,o.headRefOid))))).sort(LA)}}function en(){ac.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 sd(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await id(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function KA(){let e=await Fh();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await er())await hL(a.path)&&t.push(a);let n=Date.now(),r=dd(n)>0,o=await Promise.all(t.map(async a=>{let i=ac.get(a.path),c=i?.pending?bL:yL;if(i&&(r||n-i.at<c))return i.entry;try{let l=await $e(a.path).catch(()=>null),u=gL(a.path,await Bn(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&&fd({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 ac.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 ac.keys())s.has(a)||ac.delete(a);return{auth:e,repos:o}}async function dr(e){let t=(await er()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function os(e){return Ns(e)}async function ya(e,t,{method:n="squash"}={}){let r=await dr(e);if(!ic.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 Rf().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 cc(e,t){let n=await dr(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 $e(n.path).catch(()=>null);return o&&await wA(o,r).catch(()=>{}),{ok:!0,number:r}}async function Wh(e,t){let n=await dr(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 qh(e){return e.green&&!e.isDraft}function AL(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 GA(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:qh(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function VA(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function lc(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 zA(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(lc))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function JA(){let e={};for(let[t,n]of Lh)e[t]={...n};return e}async function YA(e,{method:t="merge",onMerged:n=null}={}){let r=await dr(e);if(!ic.has(t))throw new Error(`invalid merge method: ${t}`);if(Lh.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Bn(r.path)).filter(qh).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 Lh.set(r.path,s),EL(s,o,{listPrs:()=>Bn(r.path),merge:a=>ya(r.path,a,{method:t}),sleep:SL,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function TL(e,t){let n=Date.now()+(t.settleMaxMs??vL),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(xL)}}async function EL(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 TL(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(!qh(a)){e.skipped.push({number:a.number,title:a.title,reason:AL(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 Kh(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await wa("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${jn(l)}`)}await fL(e);let o=n||"";if((await En({cwd:e})).GH_TOKEN){let l=await Rf().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(jn(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 XA(e,t){let n=await dr(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(jn(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!Hh(i))throw i;let c=await Qt(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await HA(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(jn(i));o=WA(l).map(FA).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=_L(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await Qt(n.path,["run","view",c,"--log-failed"]).then(u=>IL(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function _L(e){return String(e||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function IL(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 jn(e){return String(e?.stderr||e?.message||e||"").trim().split(`
|
|
189
|
+
`).find(n=>n.trim())||"gh failed"}var wa,mL,hd,Uh,UA,wL,ha,LA,yL,bL,ac,ic,xL,vL,SL,Lh,ss=W(()=>{Cr();yt();Dn();sc();md();fe();wa=pL(dL),mL={timeout:3e4,maxBuffer:8*1024*1024};hd=null;Uh="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt,state,mergedAt,closedAt",UA=Uh.replace("statusCheckRollup,","");wL=9e4,ha=new Map;LA=(e,t)=>e.number-t.number;yL=3e4,bL=1500,ac=new Map;ic=new Set(["squash","merge","rebase"]);xL=2500,vL=9e4,SL=e=>new Promise(t=>setTimeout(t,e));Lh=new Map});import{execFile as $L}from"node:child_process";import{promisify as RL}from"node:util";async function ML(e,t){try{let{stdout:n}=await OL("git",["-C",e,"rev-list","--count",t]);return Number(n.trim())||0}catch{return null}}async function CL(e,t,n,{rebase:r=!1,listPrs:o=Bn}={}){let s=await Jo(e.dir,{ignorePlumbing:!0}).catch(()=>!1),a=await Yo(e.dir,n).catch(()=>0),i=t?await ML(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:gd.filter(w=>p.includes(w)),defaults:gd.filter(w=>f.includes(w))}}async function yd(e,{branch:t,rebase:n=!1,listPrs:r=Bn}={}){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 la(s[0].dir).catch(()=>!1))return null;let a=[];for(let p of s)a.push(await CL(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=gd.filter(p=>c.some(f=>f.available.includes(p))),u=gd.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 OL,gd,Gh=W(()=>{Vu();ss();OL=RL($L),gd=["commit","rebase","push","pr"]});function bd(e,t,n){let{state:r,reason:o}=km(t,n);if(r!=="unknown"){if(e){let a=Date.now();for(let[i,c]of uc)a-c.at>=ZA()&&uc.delete(i);uc.set(e,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!e)return!1;let s=uc.get(e);return s?Date.now()-s.at>=ZA()?(uc.delete(e),!1):s.busy:!1}async function Vh(e,t){let n={working:!1,prompted:!1};if(!e)return n;let r=await Pe(e,{lines:et}).catch(()=>"");if(!bd(e,t,r))return ix(t,r)?{working:!0,prompted:!1}:n;if(Qr(t,r)){let o=await ln(e).catch(()=>r),s=Nn({name:e,agent:t},o);if(s)return!Gu({name:e,agent:t},s,o)||wS(e)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function as(e,t){return(await Vh(e,t)).working}var ZA,uc,dc=W(()=>{mt();ea();mh();wh();ZA=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,uc=new Map});import{execFile as NL}from"node:child_process";import{promisify as PL}from"node:util";async function ao(e){if(!e)return null;let t=QA.get(e);if(t&&Date.now()-t.at<BL)return t.branch;let n=null;try{let{stdout:r}=await DL("git",["-C",e,"symbolic-ref","--short","-q","HEAD"],{timeout:5e3});n=r.trim()||null}catch{n=null}return QA.set(e,{at:Date.now(),branch:n}),n}function eT(e){return String(e||"").match(jL)?.[1]??null}function tT(e,t){let n=eT(e);return!!n&&!!t&&n===t}function nT(e,t){let n=eT(e);return!!n&&n!==t}function LL(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 ba(e){let t=[],n=r=>{r&&!t.includes(r)&&t.push(r)};n(e?.workspace?.branch??null);for(let r of LL(e))n(await ao(r));return t}async function kd(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=>tT(t,a.id));if(s)return s;for(let a of r)if(!je(a)&&(await ba(a)).includes(t))return a;return null}function FL(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 xd(e,{branch:t=null,repo:n=null,slug:r=null}={}){return tT(t,e?.id)?!0:FL(e,n,r)}var DL,BL,QA,jL,is=W(()=>{Zt();DL=PL(NL),BL=5e3,QA=new Map;jL=/^track\/.+-([0-9a-f]{8})$/});import{execFile as UL}from"node:child_process";import{promisify as HL}from"node:util";function ka({trackId:e,boxId:t,epoch:n}){return`${e}/${t}/${Number.isFinite(Number(n))?Number(n):0}`}function Sd(e){let t=/^([^/\s]+)\/([^/\s]+)\/(\d+)$/.exec(String(e||"").trim());return t?{trackId:t[1],boxId:t[2],epoch:Number(t[3])}:null}function Td({trackId:e,boxId:t,epoch:n=0,provider:r=null,model:o=null}){let s=[],a=r?zL[r]:null;return a&&s.push(a),s.push(`${iT}: ${ka({trackId:e,boxId:t,epoch:n})}`),o&&s.push(`${cT}: ${String(o).trim()}`),s}function rT(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===iT.toLowerCase()&&!t.epoch)t.epoch=Sd(a);else if(s===cT.toLowerCase()&&!t.model)t.model=a;else if(s===lT.toLowerCase()&&!t.cause){let i=a.toLowerCase();pc.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 JL(e,t=io){return e<=t?"rework":"drift"}function YL(e,{window:t=io,end:n=Zh}={}){return!Number.isFinite(e)||e<=t?1:e>=n?0:1-(e-t)/(n-t)}function Ed({subject:e="",trailerCause:t=null,wallReason:n=null,fixerCategory:r=null,ciFix:o=!1}={}){let s=String(e||"");return XL.test(s)?{cause:"defect",level:"A",basis:"revert"}:o?{cause:"defect",level:"A",basis:"ci-fix"}:t?{cause:t,level:"E",basis:`${lT} trailer`}:QL.test(s)&&!oT.test(s)?{cause:"style",level:"C",basis:"subject"}:oT.test(s)?{cause:"defect",level:"C",basis:"subject"}:ZL.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 ew({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&&tF.has(c)?{intent:"fix",basis:`subject prefix: ${c}:`}:c&&nF.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"}:sT.test(String(n||""))||sT.test(a)?{intent:"planned",basis:"blueprint item"}:o&&rF.has(o)?{intent:"planned",basis:`prompt: ${o}`}:{intent:"unknown",basis:null}}function iF(e){let t=String(e||"");return oF.some(n=>n.test(t))?"excluded":sF.some(n=>n.test(t))?"docs":aF.some(n=>n.test(t))?"config":"code"}function cF(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 lF(e,t){let n=Xh(e),r=Xh(t);if(n===r)return 1;if(!n.length||!r.length)return 0;n.length>vd&&(n=n.slice(0,vd)),r.length>vd&&(r=r.slice(0,vd));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 uF(e,t=uT){let n=new Set,r=e?.del||[],o=e?.add||[];for(let s=0;s<r.length;s++){let a=e.oldStart+s;if(!Xh(r[s]).length){n.add(a);continue}s<o.length&&lF(r[s],o[s])>=t&&n.add(a)}return n}function dF(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 pF(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 aT(e,t){let{stdout:n}=await Yh(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 JA(e,t,{fixKey:n=null,window:r=io,resolveOrigin:o=null,context:s={}}={}){let a=await qA(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=UA(a.message),l=n?md(n):c.epoch,u=l?ga(l):null,{stdout:d}=await Hm(e,["diff","-U0","--no-color","-M","-C",i,t]),p=CL(d),f=0,w=0,h=0,g=0,y=0,x=0,b=0,I=new Map;for(let R of p){if(R.binary)continue;let L=R.oldPath||R.newPath||"",V=R.deleted.reduce((W,[,te])=>W+te,0),O=ML(L);if(O==="excluded"){h+=V;continue}if(O==="docs"){g+=V,y+=R.added;continue}if(O==="config"){x+=V;continue}f+=R.added;let S=new Set;for(let W of R.hunks||[])for(let te of PL(W))S.add(te);b+=S.size;let M=[];for(let[W,te]of R.deleted)for(let pe=W;pe<W+te;pe++)S.has(pe)||M.push(pe);if(w+=M.length,!R.oldPath||!M.length)continue;let H;try{let W=["blame","-w","-M","-C","--line-porcelain"];for(let[te,pe]of DL(M))W.push("-L",`${te},${te+pe-1}`);W.push(i,"--",R.oldPath),H=BL((await Hm(e,W)).stdout)}catch{continue}for(let{sha:W,time:te}of H.values()){let pe=I.get(W)??{lines:0,time:te};pe.lines+=1,I.set(W,pe)}}let _=[],N=0,U=new Map;for(let[R,{lines:L,time:V}]of I){let O=U.get(R);if(!O){try{O=await qA(e,R)}catch{O=null}U.set(R,O)}let S=O?UA(O.message):{epoch:null},M=S.epoch,H=S.provider,W=S.model;if(!M&&o){let re=await o(R,O);re?.trackId&&re?.boxId!=null&&(M={trackId:re.trackId,boxId:re.boxId,epoch:re.epoch??0},H=H??re.provider??null,W=W??re.model??null)}if(!M){N+=L;continue}let te=Number.isFinite(V)?V:O?.ts??a.ts,pe=Math.max(0,(a.ts-te)/wL),Ie=ga(M),Oe=Ie===u?"self":kL(pe,r);_.push({sha:R,key:Ie,trackId:M.trackId,boxId:M.boxId,epoch:M.epoch,provider:H??null,model:W??null,lines:L,ageDays:Math.round(pe*100)/100,class:Oe,decay:Oe==="rework"?1:Oe==="drift"?Math.round(xL(pe,{window:r})*1e3)/1e3:0})}_.sort((R,L)=>L.lines-R.lines);let j=yd({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0}),T=Gm({heuristic:j,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:h,cosmeticDeleted:b,docsDeleted:g,docsAdded:y,configDeleted:x,codeDelta:f+w+b>0,origins:_,unattributedLines:N,ghost:w===0&&f>0,heuristic:j,intent:T}}function YA(e,{minLines:t=Km}={}){if(!e)return!1;let n=e.intent?.intent??"unknown";return ya.has(n)?cc(e)>=t:!1}function cc(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function Um(e){let t=e.genUsd;return{...e,genUsd:fn(t),reworkChargedUsd:fn(e.reworkChargedUsd),reworkPendingUsd:fn(e.reworkPendingUsd),trueUsd:fn(t+e.reworkChargedUsd),multiplier:t>0?fn((t+e.reworkChargedUsd)/t):null}}function XA(e,{window:t=io,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 R of i(T.payload))l.add(R);else if(T.type==="rework.cause.answered"&&T.payload?.cause)for(let R of i(T.payload))c.set(R,T.payload.cause);else if(T.type==="rework.cause.retracted")for(let R of i(T.payload))c.delete(R);let u={},d=(T,R,L)=>ga({trackId:T,boxId:R,epoch:L??0}),p=(T,R={})=>{if(!u[T]){let V=md(T)||{};u[T]={key:T,...V,provider:null,model:null,...Fm()}}let L=u[T];return R.provider&&!L.provider&&(L.provider=R.provider),R.model&&!L.model&&(L.model=R.model),L};for(let T of o){if(!T.boxId)continue;let R=d(T.trackId,T.boxId,T.epoch),{usd:L,model:V}=Jt(T),O=p(R,{provider:T.provider,model:V!=="unknown"?V:null});O.genUsd+=L}let f=new Map;for(let T of s){let R=`${T.trackId}/${T.boxId}`;(f.get(R)??f.set(R,[]).get(R)).push({...T,ts:T.payload?.ts??T.ts})}for(let T of f.values())T.sort((R,L)=>R.ts-L.ts);let w=(T,R,L,V)=>{let O=f.get(`${T}/${R}`)||[],S=-1/0;for(let W of O)W.ts<V&&(S=W.ts);let M=0,H=0;for(let W of o)W.trackId!==T||W.boxId!==R||(W.epoch??0)===(L??0)&&(W.ts<=S||W.ts>V||(M+=Jt(W).usd,H++));return{usd:M,turns:H,from:Number.isFinite(S)?S:null}},h={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=[],_={},N={},U=new Set;for(let T of a){let R=T.payload||{};if(!R.sha||U.has(R.sha))continue;if(U.add(R.sha),R.codeDelta===!1){x++;continue}let L=md(R.fixKey)||{trackId:T.trackId,boxId:T.boxId,epoch:T.epoch??0},{usd:V,turns:O}=w(L.trackId,L.boxId,L.epoch,R.ts??T.ts),S=yL.includes(R.intent?.intent)?R.intent.intent:"unknown";y[S]+=1;let M=null;c.has(R.sha)?M={cause:c.get(R.sha),level:"B",basis:"operator"}:(R.heuristic?.level&&R.heuristic.level!=="C"||R.heuristic)&&(M=R.heuristic);let H=M?.level??"none";g[H]=(g[H]||0)+1;let W=gL.has(H),te=Number(R.deleted)||0,pe=Array.isArray(R.origins)?R.origins:[],Ie=pe.filter(re=>re.class==="rework").reduce((re,Ne)=>re+Ne.lines,0),Oe={sha:R.sha,subject:R.subject??"",ts:R.ts??T.ts,trackId:T.trackId,fixKey:R.fixKey??null,fixProvider:R.fixProvider??T.provider??null,fixUsd:fn(V),fixTurns:O,linesReworked:Ie,unattributedLines:Number(R.unattributedLines)||0,ghost:R.ghost===!0,intent:{intent:S,basis:R.intent?.basis??null},verdict:M?{cause:M.cause,level:H,basis:M.basis??null}:null,charged:fn(0),origins:[]};if(S==="planned"||M?.cause===wd){h.planned+=V;for(let re of pe)re.class==="rework"&&Oe.origins.push({key:re.key,provider:re.provider??null,model:re.model??null,lines:re.lines,ageDays:re.ageDays,class:re.class,usd:0})}else if(R.ghost||te===0)h.ghost+=V;else{let re=0;for(let X of pe){let G=X.lines/te,fe=V*G;if(re+=fe,X.class==="self"){h.self+=fe;continue}let be=X.class==="rework"?1:Number(X.decay)||0,xe=fe*be;if(h.decayed+=fe-xe,xe<=0)continue;let Ke=p(X.key,{provider:X.provider,model:X.model});Ke.fixes+=1,Ke.linesReworked+=X.class==="rework"?X.lines:0;let le=M?.cause&&ic.includes(M.cause)?M.cause:"unclassified";Ke.causes[le]+=1,W?(Ke.reworkChargedUsd+=xe,h.charged+=xe,Oe.charged=fn(Oe.charged+xe)):(Ke.reworkPendingUsd+=xe,h.pending+=xe),Oe.origins.push({key:X.key,provider:Ke.provider,model:Ke.model,lines:X.lines,ageDays:X.ageDays,class:X.class,usd:fn(xe)})}let Ne=Math.max(0,V-re);h.unattributed+=Ne}b.push(Oe),!c.has(R.sha)&&l.has(R.sha)&&I.push(R.sha)}for(let T of Object.values(u)){let R=T.provider||"unknown",L=T.model||"unknown",V=O=>{O.genUsd+=T.genUsd,O.reworkChargedUsd+=T.reworkChargedUsd,O.reworkPendingUsd+=T.reworkPendingUsd,O.linesReworked+=T.linesReworked,O.fixes+=T.fixes;for(let S of Object.keys(O.causes))O.causes[S]+=T.causes[S]};V(_[R]||=Fm()),V(N[L]||=Fm())}b.sort((T,R)=>R.ts-T.ts);let j=Object.values(u).map(T=>{let R=n[`${T.trackId}/${T.boxId}`]||{};return Um({...T,trackName:R.trackName??null,label:R.label??null})}).sort((T,R)=>R.reworkChargedUsd+R.reworkPendingUsd-(T.reworkChargedUsd+T.reworkPendingUsd)||R.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${VA} 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(j.map(T=>[T.key,T])),epochs:j,byProvider:Object.fromEntries(Object.entries(_).map(([T,R])=>[T,Um(R)])),byModel:Object.fromEntries(Object.entries(N).map(([T,R])=>[T,Um(R)])),buckets:Object.fromEntries(Object.entries(h).map(([T,R])=>[T,fn(R)])),fixUsdTotal:fn(Object.values(h).reduce((T,R)=>T+R,0)),fixes:b,verdicts:g,pendingTaps:I,commits:s.length}}var hL,mL,Hm,KA,GA,zA,wL,io,qm,Km,ic,wd,gL,yL,ya,VA,bL,vL,HA,SL,AL,TL,EL,_L,WA,IL,$L,RL,OL,Wm,hd,fn,Fm,bd=q(()=>{Gr();hL=fL(pL),mL=64*1024*1024,Hm=(e,t)=>hL("git",["-C",e,...t],{maxBuffer:mL,timeout:6e4}),KA="Bostrat-Epoch",GA="Bostrat-Model",zA="Bostrat-Fix-Cause",wL=24*36e5,io=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,qm=90,Km=3,ic=Object.freeze(["defect","design","requirements","style"]),wd="not-a-fix",gL=new Set(["A","B","E"]),yL=Object.freeze(["fix","likely-fix","planned","unknown"]),ya=new Set(["fix","likely-fix"]),VA=.8;bL={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};vL=/^revert\b|^revert "/i,HA=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,SL=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,AL=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;TL=/^([a-z]+)(\([^)]*\))?!?:\s/i,EL=new Set(["fix","hotfix","bugfix","revert"]),_L=new Set(["feat","feature","docs","doc","test","tests","build","ci","chore"]),WA=/\bBP-[A-Z]*\d+\b/i,IL=new Set(["feature-build","docs-writing","research","design","ops-infra","orchestration"]);$L=[/(^|\/)\.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)\//],RL=[/\.(md|mdx|markdown|rst|txt|adoc|asciidoc)$/i,/(^|\/)(docs?|notes|business|design)\//i,/(^|\/)(README|CHANGELOG|CHANGES|LICENSE|CONTRIBUTING|AUTHORS|NOTICE)(\.[a-z]+)?$/i],OL=[/\.(json|jsonc|json5|ya?ml|toml|ini|cfg|conf|properties|env|example)$/i,/(^|\/)\.[^/]+$/,/(^|\/)\.github\//,/(^|\/)(Dockerfile|Makefile|Procfile|fly\.toml)$/];Wm=e=>String(e??"").replace(/\s+/g," ").trim(),hd=400;fn=e=>Math.round(e*1e4)/1e4,Fm=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import{execFile as jL}from"node:child_process";import{promisify as LL}from"node:util";function ZA(e,t=Date.now()){return Rf(e,t)??0}function UL(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 QA(e,t,{now:n=Date.now()}={}){let r=ZA(t,n),o=await K(e);return gd({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:UL(o,t.name,r)})}function HL(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 WL(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=HL(n,t.name),c=i?tr(i.text)?.category??null:null;return{wallReason:a,fixerCategory:c,promptKind:i?.kind??null}}async function qL(e,t){try{let{stdout:n}=await zm(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 KL(e){try{let{stdout:t}=await zm(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function eT(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 K(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 zm(r,["show","-s","--format=%ct",n]),h=Number(w.trim())*1e3;Number.isFinite(h)&&h>0&&(i=h)}catch{return{recorded:!1,reason:"unknown-sha"}}let c=ZA(t,i),l=await qL(r,n),u=await KL(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 JA(r,n,{fixKey:ga({trackId:e.id,boxId:t.name,epoch:c}),window:io,context:{...WL(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)&&YA(w))){let g=cc(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 nT(e,t,n){if(!ic.includes(n)&&n!==wd)throw new Error(`bad cause "${n}" (${ic.join("|")}|${wd})`);let r=await K(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 rT(e,t){let n=await K(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 kd(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 K(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=Gm({heuristic:yd({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";(ya.has(I)||I==="unknown"&&ya.has(d.intent))&&cc(b)!==0&&p.push(b)}let f=p.reduce((x,b)=>x+cc(b),0);if(f<Km)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 h=p.map(x=>x.sha),g=p.map(x=>x.heuristic).find(x=>x&&x.level!=="C")??yd({subject:r})??p.map(x=>x.heuristic).find(Boolean)??null,y=ya.has(d.intent)?d:p.find(x=>ya.has(x.intent?.intent))?.intent??d;return await C(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:null,payload:{sha:h[h.length-1],shas:h,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:h}}catch(c){return a.warn?.(`[rework] ${e?.id}: PR-level offer for #${n} failed: ${c?.message||c}`),{offered:!1,reason:"error"}}}var FL,zm,xd=q(()=>{he();bu();bd();FL=LL(jL),zm=(e,t)=>FL("git",["-C",e,...t],{maxBuffer:16*1024*1024,timeout:3e4})});import{execFile as GL}from"node:child_process";import{promisify as zL}from"node:util";async function Vm(e,t,{midTurn:n=os,pollMs:r=VL,capMs:o=JL}={}){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 lT(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){oT.lastIndex=n;let o=oT.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*":"")+nF(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 Jm(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function dT(e){let t=lT(e),n=t.match(QL);if(!n)return null;let r=Jm(t,n.index);return tF.test(r)?null:{repo:r.match(uT)?.[1]??null,head:r.match(rF)?.[1]??null}}function pT(e){let t=lT(e),n=t.match(eF);if(!n)return null;let r=Jm(t,n.index),o=Jm(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("-")){sF.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(uT)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function iF(e,t,{exclude:n=()=>!1}={}){let r=async i=>{ar("observer");let{stdout:c}=await sT("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",aF],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw oo(l)&&so(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-YL&&!n(c)),s=await Sn({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 cF(e,t){if(t)return[t];let n=[];for(let r of fT(e)){let o=await _e(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await Xn().catch(()=>[])){let o=await _e(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function fT(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 lF(e,t,n){let r=await wa(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)||oF(c.headRefName,e.id),i=[];for(let c of await cF(e,t?.repo??null)){let l;try{l=await iF(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await ct(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 hT(e){if(!e)return 0;let t=0;for(let n of cr)n.startsWith(`${e}|`)&&t++;return t}function mT(){for(let e of[...Ym])e()}function wT(e,t,{delays:n=aT,turnEnd:r}={}){let o=dT(t.command);if(!o)return null;let s=!!pT(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(cr.has(a))return null;cr.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(h=>{let g=()=>{clearTimeout(y),Ym.delete(g),h()},y=setTimeout(g,l);Ym.add(g)});let u=await lF(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:d,pr:p}=u,{link:f,created:w}=await Lt({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 or(d,p.number))?null:(await Vm(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{cr.delete(a)}})();return c.catch(()=>cr.delete(a)),c}function gT(e,t,{delays:n=aT,turnEnd:r,openedWait:o}={}){let s=pT(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(cr.has(a))return null;cr.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=fT(e);if(w.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let h of w){let g=await _e(h).catch(()=>null);g&&!d.includes(g)&&d.push(g)}}if(l||=(await wa(e).catch(()=>[])).filter(w=>w!==e.workspace?.branch)[0]||e.workspace?.branch,!d.length||!l)return null;c=d[0];let p=async(w,h)=>{let g=await ct(c,w.number);return g&&g.trackId!==e.id?null:(g||await Lt({repo:c,number:w.number,trackId:e.id,url:w.url,headRef:w.headRefName,title:w.title,source:"merge-observed"}),await Zi(c,w.number),h&&((await K(e).catch(()=>[])).some(b=>b.type==="pr.state.changed"&&(b.payload?.change==="auto-armed"||b.payload?.change==="merged")&&b.payload?.number===w.number)||(await Vm(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(N=>setTimeout(N,w));let h=[];for(let N of d)try{ar("observer");let{stdout:U}=await sT("gh",["pr","view",String(l),"--repo",N,"--json",uF],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await Sn({slug:N})}}),j=JSON.parse(U||"null");j?.number&&h.push({slug:N,pr:j})}catch(U){oo(U)&&so(U)}let g=h.find(N=>N.pr.state==="MERGED")??(h.length===1?h[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 ct(c,y.number);if(x&&x.trackId!==e.id)return null;x||await Lt({repo:c,number:y.number,trackId:e.id,url:y.url,headRef:y.headRefName,title:y.title,source:"merge-observed"}),await Vm(t.box,t.agent,r);let b=await K(e).catch(()=>[]);if(b.some(N=>N.type==="pr.state.changed"&&N.payload?.change==="merged"&&N.payload?.number===y.number))return null;let I=N=>N.some(U=>U.type==="pr.state.changed"&&U.payload?.change==="opened"&&U.payload?.number===y.number),_=0;if(!I(b)){if(dT(t.command)&&await or(c,y.number)){let N=Date.now();_=N+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:N,change:"opened",number:y.number,url:y.url,title:y.title,branch:y.headRefName,repo:c,selfMerge:!0}})}else if((await ct(c,y.number))?.announced){let N=o?.capMs??XL,U=o?.pollMs??ZL,j=Date.now()+N;for(;Date.now()<j&&(await new Promise(T=>setTimeout(T,U)),!I(await K(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 kd(e,{repo:c,number:y.number,title:y.title,branch:y.headRefName,url:y.url}).catch(()=>{}),await ur(c,y.number).catch(()=>{}),await lr(c,y.number).catch(()=>{}),await no(c,y.number,"merged"),y}return f?p(f,!1):null}finally{cr.delete(a)}})();return i.catch(()=>cr.delete(a)),i}var sT,aT,VL,JL,YL,XL,ZL,iT,cT,QL,eF,tF,oT,nF,uT,rF,oF,sF,aF,cr,Ym,uF,vd=q(()=>{he();Or();mt();ec();Mn();dr();ac();ss();xd();sT=zL(GL),aT=[4e3,15e3,45e3],VL=2500,JL=5*6e4;YL=15e3,XL=9e4,ZL=2e3,iT=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,cT=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,QL=new RegExp(`${iT}${cT}create\\b`),eF=new RegExp(`${iT}${cT}merge\\b`),tF=/\s--web\b/,oT=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,nF=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");uT=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,rF=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,oF=FA;sF=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);aF="number,url,title,headRefName,createdAt";cr=new Set;Ym=new Set;uF="number,url,title,headRefName,state"});import dF from"node:fs/promises";import Zm from"node:path";import pF from"node:os";function Xm(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function Sd(e){let t=fF.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=Xm(n,"from"),s=o?o.replace(/^uds:/,""):null,a=yT(s);return{socket:s,pid:a,name:Xm(n,"from-name"),mode:Xm(n,"from-mode"),text:r.trim()}}function yT(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function hF(){return process.env.CLAUDE_CONFIG_DIR?Zm.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):Zm.join(pF.homedir(),".claude","sessions")}async function mF(e){let t=yT(e);if(!t)return null;try{let n=await dF.readFile(Zm.join(hF(),`${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 bT(e,t){let n=await mF(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 fF,Qm=q(()=>{fF=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function ew(e=process.env){return!!(Re(e)&&e.BOSTRAT_PLANE_TOKEN)}async function tw({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!ew(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),kT);try{let a=await xT(`${Re(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 vT(e,{provider:t=null,cls:n="other",status:r=null,text:o=""}={},{env:s=process.env,now:a=Date.now}={}){if(!e?.id||!String(e.name||"").startsWith("\u21BB"))return{ok:!1,source:"not-a-loop-track"};if(!ew(s))return{ok:!1,source:"unconfigured"};let i=Ad.get(e.id)||0;if(a()-i<wF)return{ok:!1,source:"debounced"};Ad.set(e.id,a());let c=await lc({env:s}),l=c.loops.find(p=>p.trackId===e.id);if(!l)return Ad.delete(e.id),{ok:!1,source:c.source==="plane"?"no-loop":c.source};let u=`${t||"provider"} ${r?r+" ":""}${n}: ${String(o||"").replace(/\s+/g," ").trim()}`.slice(0,500),d=await tw({loopId:l.id,outcome:"errored",note:u,env:s});return d.ok||Ad.delete(e.id),{...d,loop:{id:l.id,name:l.name}}}async function lc({env:e=process.env}={}){if(!ew(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),kT);try{let r=await xT(`${Re(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 kT,xT,wF,Ad,nw=q(()=>{mt();kT=1e4,xT=(...e)=>fetch(...e);wF=10*6e4,Ad=new Map});function ST(e=process.env){return!!(Re(e)&&e.BOSTRAT_PLANE_TOKEN)}async function AT(e,t,n=rw){let r=new AbortController,o=setTimeout(()=>r.abort(),n);try{return await yF(`${Re(t)}${e}`,{headers:bF(t),signal:r.signal})}finally{clearTimeout(o)}}async function uc({env:e=process.env,force:t=!1,timeoutMs:n=rw}={}){if(!ST(e))return{followups:[],source:"unconfigured"};if(!t&&as&&Date.now()-as.at<gF)return{followups:as.followups,source:"cache"};try{let r=await AT("/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 as={at:Date.now(),followups:s},{followups:s,source:"plane"}}catch(r){return as?{followups:as.followups,source:"stale",error:r?.message}:{followups:[],source:"unreachable",error:r?.message}}}async function TT(e,{env:t=process.env,timeoutMs:n=rw}={}){if(!e)return{followup:null,source:"unconfigured"};if(!ST(t))return{followup:null,source:"unconfigured"};try{let r=await AT(`/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=as?.followups.find(s=>s.id===e)??null;return o?{followup:o,source:"stale",error:r?.message}:{followup:null,source:"unreachable",error:r?.message}}}var gF,rw,as,yF,bF,ET=q(()=>{mt();gF=6e4,rw=5e3,as=null,yF=(...e)=>fetch(...e);bF=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});async function co(e,t){if(ow.has(e)){let n=new Error("offer already answered (answer in flight)");throw n.code="answer-in-flight",n}ow.add(e);try{return await t()}finally{ow.delete(e)}}var ow,dc=q(()=>{ow=new Set});import{randomUUID as ka,createHash as kF}from"node:crypto";function RT(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function TF(e){for(let[t,n]of AF)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 OT(e,{standing:t=!1}={}){let n=e&&typeof e=="object"?e:{},r=ba(n.label).replace(/\s+/g," ");if(!r)throw new Error('label required (\u226460 chars, e.g. "Deploy to Pi")');if(r.length>_T)throw new Error(`label must be \u2264${_T} characters`);let o=ba(n.trigger)||(n.ship?"now":""),s=t?vF:$T.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"?_F(n.ship):null,i=ba(n.prompt);if(!a&&!i)throw new Error("prompt required \u2014 the full instruction a fresh session could execute");if(Buffer.byteLength(i,"utf8")>IT)throw new Error(`prompt must be \u2264${IT} bytes`);if(t){let u=TF(i);if(u)throw new Error(u)}let c=ba(n.why).slice(0,xF)||null,l=t?EF(n.repo):null;return{label:r,prompt:i,trigger:o,why:c,...l?{repo:l}:{},...a?{ship:a}:{}}}function EF(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 _F(e){let t=e&&typeof e=="object"?e:{},n=Array.isArray(t.actions)?t.actions.map(i=>String(i).trim().toLowerCase()):sw.slice();for(let i of n)if(!sw.includes(i))throw new Error(`ship.actions may only contain ${sw.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=ba(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=>ba(i)).filter(Boolean),a.length||(a=null)}return{actions:[...new Set(n)],rebase:r,toPr:o,base:s,repos:a}}function Td(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 MT(e,t,{pull:n=uc,ship:r={}}={}){if(cw(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=OT(t);if(o.ship)return{kind:"ship",...await OF(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=ka(),y=ka();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:aw(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=RT(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 K(s),f=Td(p,{trigger:o.trigger}),w=f.length?f[f.length-1].armId:null,h=ka();return await C(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:{ts:Date.now(),armId:h,trigger:o.trigger,label:o.label,prompt:o.prompt,why:o.why,...w?{replaces:w}:{}}}),{kind:"armed",armId:h,replaces:w,...o}}async function CT(e,t){if(!t)throw new Error("armId required");let n={id:e.id,cwd:e.cwd},r=await K(n);if(!Td(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 NT(e,t,{pull:n=uc}={}){if(cw(e))throw new Error("propose from a lead Track, not a loop's run Track");let r=OT(t,{standing:!0}),o=r.repo??e.workspace?.repo??null,s={id:e.id,cwd:e.cwd};if((await K(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=RT(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}" (${iw[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=ka(),h=(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:h?.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 PT(e,t,n,{followupId:r=null}={}){if(!t)throw new Error("proposalId required");let o={id:e.id,cwd:e.cwd},s=await K(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 pc(e,t,n={},{pull:r=uc}={}){if(!e||cw(e))return null;let o={id:e.id,cwd:e.cwd},s=await K(o).catch(()=>[]),a=n.repo??e.workspace?.repo??null;if(t==="pr.merged"){let p=h=>{let g=(h?.prs||[]).filter(y=>Number.isFinite(Number(y?.number))).map(y=>`${y?.repo??h?.repo??""}#${Number(y.number)}`);return Number.isFinite(Number(h?.prNumber))&&g.push(`${h?.repo??""}#${Number(h.prNumber)}`),g},f=new Set(p(n));if(s.some(h=>h.type==="followup.offered"&&h.payload?.trigger==="pr.merged"&&p(h.payload?.context).some(g=>f.has(g))))return null}let i=Td(s,{trigger:t}).map(p=>({armId:p.armId,label:p.label,prompt:p.prompt,promptHash:aw(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 h=g=>g.stats?.offered?(g.stats.accepted||0)/g.stats.offered:0;return h(w)-h(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:IF(f.prompt),promptHash:aw(f.prompt),origin:f.origin,repo:f.repo??null}))}let l=[...i,...c].slice(0,SF);if(!l.length)return null;let u=ka(),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 $F(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 DT(e,{offerId:t,followupId:n=null,armId:r=null,promoted:o=null}={},s={}){if(!t)throw new Error("offerId required");return co(`fu:${e.id}:${t}`,()=>RF(e,{offerId:t,followupId:n,armId:r,promoted:o},s))}async function RF(e,{offerId:t,followupId:n,armId:r,promoted:o},s){let{sayToLead:a,unarchiveTrack:i,getFollowup:c=TT}=s,l={id:e.id,cwd:e.cwd},u=await K(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,h=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,h=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,h=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=`${$F(d.payload)}
|
|
194
|
+
`)}}async function dT(e,t,{fixKey:n=null,window:r=io,resolveOrigin:o=null,context:s={}}={}){let a=await aT(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=rT(a.message),l=n?Sd(n):c.epoch,u=l?ka(l):null,{stdout:d}=await Yh(e,["diff","-U0","--no-color","-M","-C",i,t]),p=cF(d),f=0,w=0,m=0,g=0,y=0,k=0,b=0,I=new Map;for(let $ of p){if($.binary)continue;let F=$.oldPath||$.newPath||"",J=$.deleted.reduce((K,[,X])=>K+X,0),M=iF(F);if(M==="excluded"){m+=J;continue}if(M==="docs"){g+=J,y+=$.added;continue}if(M==="config"){k+=J;continue}f+=$.added;let S=new Set;for(let K of $.hunks||[])for(let X of uF(K))S.add(X);b+=S.size;let _=[];for(let[K,X]of $.deleted)for(let pe=K;pe<K+X;pe++)S.has(pe)||_.push(pe);if(w+=_.length,!$.oldPath||!_.length)continue;let H;try{let K=["blame","-w","-M","-C","--line-porcelain"];for(let[X,pe]of dF(_))K.push("-L",`${X},${X+pe-1}`);K.push(i,"--",$.oldPath),H=pF((await Yh(e,K)).stdout)}catch{continue}for(let{sha:K,time:X}of H.values()){let pe=I.get(K)??{lines:0,time:X};pe.lines+=1,I.set(K,pe)}}let R=[],N=0,D=new Map;for(let[$,{lines:F,time:J}]of I){let M=D.get($);if(!M){try{M=await aT(e,$)}catch{M=null}D.set($,M)}let S=M?rT(M.message):{epoch:null},_=S.epoch,H=S.provider,K=S.model;if(!_&&o){let Q=await o($,M);Q?.trackId&&Q?.boxId!=null&&(_={trackId:Q.trackId,boxId:Q.boxId,epoch:Q.epoch??0},H=H??Q.provider??null,K=K??Q.model??null)}if(!_){N+=F;continue}let X=Number.isFinite(J)?J:M?.ts??a.ts,pe=Math.max(0,(a.ts-X)/KL),Oe=ka(_),Me=Oe===u?"self":JL(pe,r);R.push({sha:$,key:Oe,trackId:_.trackId,boxId:_.boxId,epoch:_.epoch,provider:H??null,model:K??null,lines:F,ageDays:Math.round(pe*100)/100,class:Me,decay:Me==="rework"?1:Me==="drift"?Math.round(YL(pe,{window:r})*1e3)/1e3:0})}R.sort(($,F)=>F.lines-$.lines);let j=Ed({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0}),T=ew({heuristic:j,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:k,codeDelta:f+w+b>0,origins:R,unattributedLines:N,ghost:w===0&&f>0,heuristic:j,intent:T}}function pT(e,{minLines:t=Qh}={}){if(!e)return!1;let n=e.intent?.intent??"unknown";return xa.has(n)?fc(e)>=t:!1}function fc(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function Jh(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 fT(e,{window:t=io,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,$,F)=>ka({trackId:T,boxId:$,epoch:F??0}),p=(T,$={})=>{if(!u[T]){let J=Sd(T)||{};u[T]={key:T,...J,provider:null,model:null,...zh()}}let F=u[T];return $.provider&&!F.provider&&(F.provider=$.provider),$.model&&!F.model&&(F.model=$.model),F};for(let T of o){if(!T.boxId)continue;let $=d(T.trackId,T.boxId,T.epoch),{usd:F,model:J}=Xt(T),M=p($,{provider:T.provider,model:J!=="unknown"?J:null});M.genUsd+=F}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(($,F)=>$.ts-F.ts);let w=(T,$,F,J)=>{let M=f.get(`${T}/${$}`)||[],S=-1/0;for(let K of M)K.ts<J&&(S=K.ts);let _=0,H=0;for(let K of o)K.trackId!==T||K.boxId!==$||(K.epoch??0)===(F??0)&&(K.ts<=S||K.ts>J||(_+=Xt(K).usd,H++));return{usd:_,turns:H,from:Number.isFinite(S)?S: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},k=0,b=[],I=[],R={},N={},D=new Set;for(let T of a){let $=T.payload||{};if(!$.sha||D.has($.sha))continue;if(D.add($.sha),$.codeDelta===!1){k++;continue}let F=Sd($.fixKey)||{trackId:T.trackId,boxId:T.boxId,epoch:T.epoch??0},{usd:J,turns:M}=w(F.trackId,F.boxId,F.epoch,$.ts??T.ts),S=VL.includes($.intent?.intent)?$.intent.intent:"unknown";y[S]+=1;let _=null;c.has($.sha)?_={cause:c.get($.sha),level:"B",basis:"operator"}:($.heuristic?.level&&$.heuristic.level!=="C"||$.heuristic)&&(_=$.heuristic);let H=_?.level??"none";g[H]=(g[H]||0)+1;let K=GL.has(H),X=Number($.deleted)||0,pe=Array.isArray($.origins)?$.origins:[],Oe=pe.filter(Q=>Q.class==="rework").reduce((Q,qe)=>Q+qe.lines,0),Me={sha:$.sha,subject:$.subject??"",ts:$.ts??T.ts,trackId:T.trackId,fixKey:$.fixKey??null,fixProvider:$.fixProvider??T.provider??null,fixUsd:hn(J),fixTurns:M,linesReworked:Oe,unattributedLines:Number($.unattributedLines)||0,ghost:$.ghost===!0,intent:{intent:S,basis:$.intent?.basis??null},verdict:_?{cause:_.cause,level:H,basis:_.basis??null}:null,charged:hn(0),origins:[]};if(S==="planned"||_?.cause===Ad){m.planned+=J;for(let Q of pe)Q.class==="rework"&&Me.origins.push({key:Q.key,provider:Q.provider??null,model:Q.model??null,lines:Q.lines,ageDays:Q.ageDays,class:Q.class,usd:0})}else if($.ghost||X===0)m.ghost+=J;else{let Q=0;for(let Ae of pe){let Z=Ae.lines/X,he=J*Z;if(Q+=he,Ae.class==="self"){m.self+=he;continue}let Te=Ae.class==="rework"?1:Number(Ae.decay)||0,z=he*Te;if(m.decayed+=he-z,z<=0)continue;let ge=p(Ae.key,{provider:Ae.provider,model:Ae.model});ge.fixes+=1,ge.linesReworked+=Ae.class==="rework"?Ae.lines:0;let Ge=_?.cause&&pc.includes(_.cause)?_.cause:"unclassified";ge.causes[Ge]+=1,K?(ge.reworkChargedUsd+=z,m.charged+=z,Me.charged=hn(Me.charged+z)):(ge.reworkPendingUsd+=z,m.pending+=z),Me.origins.push({key:Ae.key,provider:ge.provider,model:ge.model,lines:Ae.lines,ageDays:Ae.ageDays,class:Ae.class,usd:hn(z)})}let qe=Math.max(0,J-Q);m.unattributed+=qe}b.push(Me),!c.has($.sha)&&l.has($.sha)&&I.push($.sha)}for(let T of Object.values(u)){let $=T.provider||"unknown",F=T.model||"unknown",J=M=>{M.genUsd+=T.genUsd,M.reworkChargedUsd+=T.reworkChargedUsd,M.reworkPendingUsd+=T.reworkPendingUsd,M.linesReworked+=T.linesReworked,M.fixes+=T.fixes;for(let S of Object.keys(M.causes))M.causes[S]+=T.causes[S]};J(R[$]||=zh()),J(N[F]||=zh())}b.sort((T,$)=>$.ts-T.ts);let j=Object.values(u).map(T=>{let $=n[`${T.trackId}/${T.boxId}`]||{};return Jh({...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${uT} 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:k,byEpoch:Object.fromEntries(j.map(T=>[T.key,T])),epochs:j,byProvider:Object.fromEntries(Object.entries(R).map(([T,$])=>[T,Jh($)])),byModel:Object.fromEntries(Object.entries(N).map(([T,$])=>[T,Jh($)])),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 WL,qL,Yh,iT,cT,lT,KL,io,Zh,Qh,pc,Ad,GL,VL,xa,uT,zL,XL,oT,ZL,QL,eF,tF,nF,sT,rF,oF,sF,aF,Xh,vd,hn,zh,_d=W(()=>{zr();WL=HL(UL),qL=64*1024*1024,Yh=(e,t)=>WL("git",["-C",e,...t],{maxBuffer:qL,timeout:6e4}),iT="Bostrat-Epoch",cT="Bostrat-Model",lT="Bostrat-Fix-Cause",KL=24*36e5,io=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,Zh=90,Qh=3,pc=Object.freeze(["defect","design","requirements","style"]),Ad="not-a-fix",GL=new Set(["A","B","E"]),VL=Object.freeze(["fix","likely-fix","planned","unknown"]),xa=new Set(["fix","likely-fix"]),uT=.8;zL={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};XL=/^revert\b|^revert "/i,oT=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,ZL=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,QL=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;eF=/^([a-z]+)(\([^)]*\))?!?:\s/i,tF=new Set(["fix","hotfix","bugfix","revert"]),nF=new Set(["feat","feature","docs","doc","test","tests","build","ci","chore"]),sT=/\bBP-[A-Z]*\d+\b/i,rF=new Set(["feature-build","docs-writing","research","design","ops-infra","orchestration"]);oF=[/(^|\/)\.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)\//],sF=[/\.(md|mdx|markdown|rst|txt|adoc|asciidoc)$/i,/(^|\/)(docs?|notes|business|design)\//i,/(^|\/)(README|CHANGELOG|CHANGES|LICENSE|CONTRIBUTING|AUTHORS|NOTICE)(\.[a-z]+)?$/i],aF=[/\.(json|jsonc|json5|ya?ml|toml|ini|cfg|conf|properties|env|example)$/i,/(^|\/)\.[^/]+$/,/(^|\/)\.github\//,/(^|\/)(Dockerfile|Makefile|Procfile|fly\.toml)$/];Xh=e=>String(e??"").replace(/\s+/g," ").trim(),vd=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 fF}from"node:child_process";import{promisify as mF}from"node:util";function mT(e,t=Date.now()){return Pf(e,t)??0}function wF(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 hT(e,t,{now:n=Date.now()}={}){let r=mT(t,n),o=await q(e);return Td({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:wF(o,t.name,r)})}function gF(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 yF(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=gF(n,t.name),c=i?or(i.text)?.category??null:null;return{wallReason:a,fixerCategory:c,promptKind:i?.kind??null}}async function bF(e,t){try{let{stdout:n}=await tw(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 tw(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function wT(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 tw(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=mT(t,i),l=await bF(r,n),u=await kF(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 dT(r,n,{fixKey:ka({trackId:e.id,boxId:t.name,epoch:c}),window:io,context:{...yF(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)&&pT(w))){let g=fc(w),y=w.origins.filter(k=>k.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(k=>({key:k.key,provider:k.provider,model:k.model,lines:k.lines,ageDays:k.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 gT(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 yT(e,t,n){if(!pc.includes(n)&&n!==Ad)throw new Error(`bad cause "${n}" (${pc.join("|")}|${Ad})`);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(gT(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 bT(e,t){let n=await q(e);if(!gT(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 Id(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(k=>k.type==="rework.cause.offered"&&k.payload?.pr?.number===n&&(t==null||k.payload.pr.repo===t)))return{offered:!1,reason:"duplicate"};let l=new Set;for(let k of c)if(k.type==="rework.cause.offered")for(let b of Array.isArray(k.payload?.shas)?k.payload.shas:[k.payload?.sha])b&&l.add(b);let u=new Map;for(let k of c)k.type==="code.committed"&&k.payload?.sha&&u.set(k.payload.sha,k.payload.ref??null);let d=ew({heuristic:Ed({subject:r}),subject:r,ref:o}),p=[];for(let k of c){if(k.type!=="code.rework")continue;let b=k.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";(xa.has(I)||I==="unknown"&&xa.has(d.intent))&&fc(b)!==0&&p.push(b)}let f=p.reduce((k,b)=>k+fc(b),0);if(f<Qh)return{offered:!1,reason:"below-threshold"};let w=new Map;for(let k of p)for(let b of k.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(k=>k.sha),g=p.map(k=>k.heuristic).find(k=>k&&k.level!=="C")??Ed({subject:r})??p.map(k=>k.heuristic).find(Boolean)??null,y=xa.has(d.intent)?d:p.find(k=>xa.has(k.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((k,b)=>b.lines-k.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 hF,tw,$d=W(()=>{fe();Au();_d();hF=mF(fF),tw=(e,t)=>hF("git",["-C",e,...t],{maxBuffer:16*1024*1024,timeout:3e4})});import{execFile as xF}from"node:child_process";import{promisify as vF}from"node:util";async function nw(e,t,{midTurn:n=as,pollMs:r=SF,capMs:o=AF}={}){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 TT(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){kT.lastIndex=n;let o=kT.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*":"")+OF(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 rw(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function _T(e){let t=TT(e),n=t.match(IF);if(!n)return null;let r=rw(t,n.index);return RF.test(r)?null:{repo:r.match(ET)?.[1]??null,head:r.match(MF)?.[1]??null}}function IT(e){let t=TT(e),n=t.match($F);if(!n)return null;let r=rw(t,n.index),o=rw(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("-")){NF.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(ET)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function DF(e,t,{exclude:n=()=>!1}={}){let r=async i=>{ur("observer");let{stdout:c}=await xT("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",PF],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw oo(l)&&so(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-TF&&!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 BF(e,t){if(t)return[t];let n=[];for(let r of $T(e)){let o=await $e(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await er().catch(()=>[])){let o=await $e(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function $T(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 jF(e,t,n){let r=await ba(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)||CF(c.headRefName,e.id),i=[];for(let c of await BF(e,t?.repo??null)){let l;try{l=await DF(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 RT(e){if(!e)return 0;let t=0;for(let n of pr)n.startsWith(`${e}|`)&&t++;return t}function OT(){for(let e of[...ow])e()}function MT(e,t,{delays:n=vT,turnEnd:r}={}){let o=_T(t.command);if(!o)return null;let s=!!IT(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(pr.has(a))return null;pr.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(m=>{let g=()=>{clearTimeout(y),ow.delete(g),m()},y=setTimeout(g,l);ow.add(g)});let u=await jF(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 cr(d,p.number))?null:(await nw(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{pr.delete(a)}})();return c.catch(()=>pr.delete(a)),c}function CT(e,t,{delays:n=vT,turnEnd:r,openedWait:o}={}){let s=IT(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(pr.has(a))return null;pr.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=$T(e);if(w.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let m of w){let g=await $e(m).catch(()=>null);g&&!d.includes(g)&&d.push(g)}}if(l||=(await ba(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 rc(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 nw(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(N=>setTimeout(N,w));let m=[];for(let N of d)try{ur("observer");let{stdout:D}=await xT("gh",["pr","view",String(l),"--repo",N,"--json",LF],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await En({slug:N})}}),j=JSON.parse(D||"null");j?.number&&m.push({slug:N,pr:j})}catch(D){oo(D)&&so(D)}let g=m.find(N=>N.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 k=await pt(c,y.number);if(k&&k.trackId!==e.id)return null;k||await Ut({repo:c,number:y.number,trackId:e.id,url:y.url,headRef:y.headRefName,title:y.title,source:"merge-observed"}),await nw(t.box,t.agent,r);let b=await q(e).catch(()=>[]);if(b.some(N=>N.type==="pr.state.changed"&&N.payload?.change==="merged"&&N.payload?.number===y.number))return null;let I=N=>N.some(D=>D.type==="pr.state.changed"&&D.payload?.change==="opened"&&D.payload?.number===y.number),R=0;if(!I(b)){if(_T(t.command)&&await cr(c,y.number)){let N=Date.now();R=N+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:N,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 N=o?.capMs??EF,D=o?.pollMs??_F,j=Date.now()+N;for(;Date.now()<j&&(await new Promise(T=>setTimeout(T,D)),!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(),R),change:"merged",number:y.number,url:y.url,title:y.title,branch:y.headRefName,repo:c}}),await Id(e,{repo:c,number:y.number,title:y.title,branch:y.headRefName,url:y.url}).catch(()=>{}),await mr(c,y.number).catch(()=>{}),await fr(c,y.number).catch(()=>{}),await no(c,y.number,"merged"),y}return f?p(f,!1):null}finally{pr.delete(a)}})();return i.catch(()=>pr.delete(a)),i}var xT,vT,SF,AF,TF,EF,_F,ST,AT,IF,$F,RF,kT,OF,ET,MF,CF,NF,PF,pr,ow,LF,Rd=W(()=>{fe();Cr();yt();sc();Dn();hr();dc();is();$d();xT=vF(xF),vT=[4e3,15e3,45e3],SF=2500,AF=5*6e4;TF=15e3,EF=9e4,_F=2e3,ST=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,AT=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,IF=new RegExp(`${ST}${AT}create\\b`),$F=new RegExp(`${ST}${AT}merge\\b`),RF=/\s--web\b/,kT=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,OF=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");ET=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,MF=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,CF=nT;NF=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);PF="number,url,title,headRefName,createdAt";pr=new Set;ow=new Set;LF="number,url,title,headRefName,state"});import FF from"node:fs/promises";import aw from"node:path";import UF from"node:os";function sw(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function Od(e){let t=HF.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=sw(n,"from"),s=o?o.replace(/^uds:/,""):null,a=NT(s);return{socket:s,pid:a,name:sw(n,"from-name"),mode:sw(n,"from-mode"),text:r.trim()}}function NT(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function WF(){return process.env.CLAUDE_CONFIG_DIR?aw.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):aw.join(UF.homedir(),".claude","sessions")}async function qF(e){let t=NT(e);if(!t)return null;try{let n=await FF.readFile(aw.join(WF(),`${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 PT(e,t){let n=await qF(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 HF,iw=W(()=>{HF=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function DT(e=process.env){return!!(Ce(e)&&e.BOSTRAT_PLANE_TOKEN)}async function BT(e,t,n=cw){let r=new AbortController,o=setTimeout(()=>r.abort(),n);try{return await GF(`${Ce(t)}${e}`,{headers:VF(t),signal:r.signal})}finally{clearTimeout(o)}}async function mc({env:e=process.env,force:t=!1,timeoutMs:n=cw}={}){if(!DT(e))return{followups:[],source:"unconfigured"};if(!t&&cs&&Date.now()-cs.at<KF)return{followups:cs.followups,source:"cache"};try{let r=await BT("/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 cs={at:Date.now(),followups:s},{followups:s,source:"plane"}}catch(r){return cs?{followups:cs.followups,source:"stale",error:r?.message}:{followups:[],source:"unreachable",error:r?.message}}}async function jT(e,{env:t=process.env,timeoutMs:n=cw}={}){if(!e)return{followup:null,source:"unconfigured"};if(!DT(t))return{followup:null,source:"unconfigured"};try{let r=await BT(`/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=cs?.followups.find(s=>s.id===e)??null;return o?{followup:o,source:"stale",error:r?.message}:{followup:null,source:"unreachable",error:r?.message}}}var KF,cw,cs,GF,VF,LT=W(()=>{yt();KF=6e4,cw=5e3,cs=null,GF=(...e)=>fetch(...e);VF=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});async function co(e,t){if(lw.has(e)){let n=new Error("offer already answered (answer in flight)");throw n.code="answer-in-flight",n}lw.add(e);try{return await t()}finally{lw.delete(e)}}var lw,hc=W(()=>{lw=new Set});import{randomUUID as Sa,createHash as zF}from"node:crypto";function WT(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function QF(e){for(let[t,n]of ZF)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 qT(e,{standing:t=!1}={}){let n=e&&typeof e=="object"?e:{},r=va(n.label).replace(/\s+/g," ");if(!r)throw new Error('label required (\u226460 chars, e.g. "Deploy to Pi")');if(r.length>FT)throw new Error(`label must be \u2264${FT} characters`);let o=va(n.trigger)||(n.ship?"now":""),s=t?YF:HT.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"?tU(n.ship):null,i=va(n.prompt);if(!a&&!i)throw new Error("prompt required \u2014 the full instruction a fresh session could execute");if(Buffer.byteLength(i,"utf8")>UT)throw new Error(`prompt must be \u2264${UT} bytes`);if(t){let u=QF(i);if(u)throw new Error(u)}let c=va(n.why).slice(0,JF)||null,l=t?eU(n.repo):null;return{label:r,prompt:i,trigger:o,why:c,...l?{repo:l}:{},...a?{ship:a}:{}}}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 tU(e){let t=e&&typeof e=="object"?e:{},n=Array.isArray(t.actions)?t.actions.map(i=>String(i).trim().toLowerCase()):uw.slice();for(let i of n)if(!uw.includes(i))throw new Error(`ship.actions may only contain ${uw.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=va(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=>va(i)).filter(Boolean),a.length||(a=null)}return{actions:[...new Set(n)],rebase:r,toPr:o,base:s,repos:a}}function Md(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 KT(e,t,{pull:n=mc,ship:r={}}={}){if(fw(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=qT(t);if(o.ship)return{kind:"ship",...await sU(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=Sa(),y=Sa();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:dw(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=WT(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=Md(p,{trigger:o.trigger}),w=f.length?f[f.length-1].armId:null,m=Sa();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 GT(e,t){if(!t)throw new Error("armId required");let n={id:e.id,cwd:e.cwd},r=await q(n);if(!Md(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 VT(e,t,{pull:n=mc}={}){if(fw(e))throw new Error("propose from a lead Track, not a loop's run Track");let r=qT(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=WT(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}" (${pw[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=Sa(),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 zT(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 wc(e,t,n={},{pull:r=mc}={}){if(!e||fw(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=Md(s,{trigger:t}).map(p=>({armId:p.armId,label:p.label,prompt:p.prompt,promptHash:dw(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:nU(f.prompt),promptHash:dw(f.prompt),origin:f.origin,repo:f.repo??null}))}let l=[...i,...c].slice(0,XF);if(!l.length)return null;let u=Sa(),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 rU(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 JT(e,{offerId:t,followupId:n=null,armId:r=null,promoted:o=null}={},s={}){if(!t)throw new Error("offerId required");return co(`fu:${e.id}:${t}`,()=>oU(e,{offerId:t,followupId:n,armId:r,promoted:o},s))}async function oU(e,{offerId:t,followupId:n,armId:r,promoted:o},s){let{sayToLead:a,unarchiveTrack:i,getFollowup:c=jT}=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(R=>R.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 R=new Error("removed in Settings since it was offered");throw R.code="removed",R}w=b.prompt,m=b.label}else{let b=p.find(R=>R.armId===r);if(!b)throw new Error("that arm is not on this card");if(w=u.find(R=>R.type==="followup.armed"&&R.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=`${rU(d.payload)}
|
|
196
196
|
|
|
197
|
-
${w}`,
|
|
197
|
+
${w}`,k=await a(e.id,y,{kind:"followup-go"});return await f({action:"sent",...n?{followupId:n}:{armId:r},label:m,...o?{promoted:String(o)}:{},...g?{revived:!0}:{},source:n?`followup:${n}`:`arm:${r}`}),{offerId:t,action:"sent",followupId:n,armId:r,promoted:o,revived:g,queued:!!k?.queued}}async function sU(e,t,{changedRepos:n=null,probe:r=yd,branchOf:o=ao}={}){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(k=>s.repos.includes(k.name)):g;l=y.length?y.map(k=>({name:k.name,slug:k.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=>to.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=Sa(),m=t.why?`${t.label} \u2014 ${t.why}`:t.label;return await C(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 YT(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 XT(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 ZT(e,{pull:t=mc}={}){let{followups:n,source:r,error:o}=await t().catch(a=>({followups:[],source:"unreachable",error:a?.message})),s=e?await q({id:e.id,cwd:e.cwd}).catch(()=>[]):[];return{followups:n,source:r,error:o,arms:e?Md(s):[]}}var FT,UT,JF,HT,YF,pw,XF,uw,dw,va,ZF,fw,nU,Cd=W(()=>{fe();es();Gh();is();LT();hc();FT=60,UT=8*1024,JF=200,HT=["now","pr.merged","ship.done","pr.opened","checks.green","track.opened"],YF=HT.filter(e=>e!=="now"&&e!=="track.opened"),pw={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"},XF=3,uw=["commit","push","pr"];dw=e=>zF("sha256").update(String(e)).digest("hex").slice(0,16),va=e=>typeof e=="string"?e.trim():"",ZF=[[/(^|[^\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"]];fw=e=>String(e?.name||"").startsWith("\u21BB");nU=e=>String(e||"").split(`
|
|
198
198
|
`).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 NF(e,t){let n=`${e.id}:${t.name}`;if(!dw.has(n)){let r=await K(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);dw.set(n,o)}return dw.get(n)}async function GT(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await jo(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await NF(e,t),o=CF(await Mt(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:Rn(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 dw,zT=q(()=>{he();Fo();ln();nr();dw=new Map});import Id from"node:fs";import fw from"node:path";import PF from"node:os";async function jF({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(BF,{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:VT})});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<VT)break}return r}function LF(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 YT(){try{return JSON.parse(Id.readFileSync($d(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function UF(e){try{Id.mkdirSync(fw.dirname($d()),{recursive:!0});let t=$d()+".tmp";Id.writeFileSync(t,JSON.stringify(e)),Id.renameSync(t,$d())}catch{}}async function WF({fetchImpl:e,now:t=Date.now()}={}){if(lo.rows&&t-lo.at<FF)return lo.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=YT(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await jF({sinceMs:o,cookie:n,fetchImpl:e});lo={at:t,rows:s.map(LF).filter(Boolean),error:null}}catch(s){lo={at:t,rows:null,error:String(s?.message||s)}}return lo.rows}async function qF(e,t){let n=`${e.id}:${t.name}`;if(!pw.has(n)){let r=await K(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);pw.set(n,o)}return pw.get(n)}async function XT(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!JT())return;let r=await WF({fetchImpl:n});if(!r||!r.length)return;let o=await Bv(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await qF(e,t),a=YT(),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=Rn(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}})}UF({sinceMs:c,seen:[...i].slice(-HF)})}catch{}}var DF,JT,BF,VT,FF,lo,$d,HF,hw,pw,mw=q(()=>{he();Kh();nr();DF=process.env.BOSTRAT_STATE_DIR||fw.join(process.env.HOME||PF.tmpdir(),".bostrat"),JT=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,BF="https://cursor.com/api/dashboard/get-filtered-usage-events",VT=100;FF=5*6e4,lo={at:0,rows:null,error:null},$d=()=>fw.join(DF,"cursor-usage.json");HF=5e3;hw=()=>({enabled:JT(),lastPollAt:lo.at||null,error:lo.error}),pw=new Map});import{randomUUID as KF,createHmac as GF}from"node:crypto";function QT(e){gw=typeof e=="string"&&e?e:null}function zF(e){if(!gw)return null;let t=Le(e);return t?GF("sha256",Buffer.from(gw,"base64url")).update(t,"utf8").digest("hex").slice(0,16):null}function nE(){let e={};for(let t of tE)e[String(t)]=0;return e.inf=0,{buckets:e,sumMs:0,count:0}}function ZT(e,t){let n=Math.max(0,Number(t)||0);for(let r of tE)n<=r&&(e.buckets[String(r)]+=1);e.buckets.inf+=1,e.sumMs+=n,e.count+=1}function rE(){let e={};for(let t of eE)e[t]=0;return e}function JF(){Pn={total:new Map,holds:rE(),queueWaitMs:nE(),readyWaitMs:new Map,verify:new Map,retries:new Map}}function oE({queueDepth:e=0,queueOldestAgeMs:t=0}={}){let n=Pn;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]=r.split("|");return{provider:s,verdict:a,n:o}}),retries:[...n.retries].map(([r,o])=>({provider:r,n:o})),queueDepth:Number(e)||0,queueOldestAgeMs:Number(t)||0}}function mc({kind:e=null,producer:t=null,orchestration:n=!1}={}){return n?`orchestration:${e||"orchestration"}`:e?`scripted:${e}`:VF.has(t)?t:"composer"}function wc({text:e="",producer:t="composer",kind:n=null,provider:r=null,attachments:o=0,now:s=Date.now}={}){let a=s();return{id:KF(),producer:t,kind:n||null,provider:r||null,textBytes:Buffer.byteLength(String(e||""),"utf8"),textHmac16:zF(e),attachments:Number(o)||0,paste:null,t0:a,queuedAt:null,dequeuedAt:null,holds:rE(),requeued:0,readyWaitMs:null,readyOutcome:null,verify:null,settled:!1,outcome:null}}async function gc(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 sE(e,t){eE.includes(t)&&(e?.holds&&(e.holds[t]+=1),Pn.holds[t]+=1)}function yc(e,{ms:t,outcome:n}){e&&(e.readyWaitMs=Math.max(0,Number(t)||0),e.readyOutcome=n||null)}async function Dn(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(ww(Pn.total,`${s}|${t.producer}|${n}`),a&&ZT(Pn.queueWaitMs,i),t.readyWaitMs!=null){let l=`${s}|${t.readyOutcome||"unknown"}`;Pn.readyWaitMs.has(l)||Pn.readyWaitMs.set(l,nE()),ZT(Pn.readyWaitMs.get(l),t.readyWaitMs)}t.verify?.verdict&&(ww(Pn.verify,`${s}|${t.verify.verdict}`),t.verify.retries&&ww(Pn.retries,s,t.verify.retries));let c={ts:o,deliveryId:t.id,producer:t.producer,...t.kind?{kind:t.kind}:{},cliVersion:null,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:c}).catch(()=>{}),c}function xa(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 gw,eE,tE,VF,Pn,ww,yw=q(()=>{he();Fi();gw=null;eE=["busy","prompt","handoff","noSession","cap","grace"],tE=[500,2e3,1e4,6e4,6e5],VF=new Set(["composer","voice","loop"]);Pn=null;JF();ww=(e,t,n=1)=>e.set(t,(e.get(t)||0)+n)});import xc from"node:fs/promises";import Ct from"node:path";import YF from"node:os";import XF from"node:net";import{randomUUID as ZF}from"node:crypto";import{execFile as QF}from"node:child_process";import{promisify as eU}from"node:util";function iU(e){for(let t of Tw(e))if(!dE.test(t))return t;return null}function pE(e){let t=Tw(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function cU(e,t){let n=pE(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Ct.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return Ct.resolve(e,n[r+1])}return e}function uU(e){let t=!1,n=!1;for(let r of Tw(e)){if(!t){if(dE.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&bw(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,bw(r))return`serves the filesystem root ("${r}")`;continue}if(bw(r))return`serves the filesystem root ("${r}")`;if(lU(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function pU(e,t){if(Ct.resolve(e)===Ct.resolve(t))return!0;let[n,r]=await Promise.all([Rd(e),Rd(t)]);return n===r}async function fU(e){let n=(await xc.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 fE(Ct.join(e,o.name,vw))?`./${o.name}`:null))).filter(Boolean)}async function hU(e,t){let[n,r]=await Promise.all([Rd(e),Rd(t)]),o=Ct.relative(n,r);if(o&&(o.startsWith("..")||Ct.isAbsolute(o)))throw bc(`serve directory escapes the Track workspace: ${t}`)}async function mU(e,t){let n=uU(t);if(n)throw bc(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await dU(e))throw bc(`serve directory does not exist: ${e}`);let r=iU(t);if(!r)return;if(!r.includes("/")&&!await s0(r,{loginShell:!0,failOpen:!0}))throw bc(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(oU.test(t)||sU.has(r)||!rU.has(r)||!pE(t).includes("run")||aU.test(t))return;let o=cU(e,t);if(await fE(Ct.join(o,vw)))return;let s=await fU(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw bc(`no ${vw} in ${o}${a}`)}async function hr(){return Bn||(kw||=(async()=>{try{Bn=JSON.parse(await xc.readFile(uE,"utf8"))}catch{Bn={views:[],adoptWatermarks:{}}}finally{kw=null}return Bn.views||=[],Bn.adoptWatermarks||={},Bn})(),kw)}async function Ln(){await Rt(uE,Bn)}function fr(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 _w(e){return kc.get(e)||null}function mE(e,{name:t,box:n}={}){let r=kc.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 kc.set(e,o),o}function Sw(e,t){let n=kc.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function Od(e,{error:t=null}={}){let n=kc.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function wE(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 Aw(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function wU(e,t){let n=Aw(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!cE.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!cE.has(s))return s}}function vc(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=XF.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 Iw(e){if(process.platform!=="darwin"){await jn("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await jn("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 gE(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function gU(e,t){let n=new Set(e.views.map(o=>o.port)),r=gE();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=aE;o<=iE;o++)if(!(n.has(o)||r.has(o))&&!await vc(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${aE}-${iE}`)}async function yU(e){try{let{stdout:t}=await jn(ee,ne("list-sessions","-F","#{session_name}"));return t.split(`
|
|
201
|
-
`).some(n=>n.trim()===
|
|
199
|
+
`).slice(0,400)});import{randomUUID as aU}from"node:crypto";function gc(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function QT(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function eE(e){return`job|${e.box}|id:${e.id}`}async function hw(e){return mw.has(e.id)?mw.get(e.id):(Nd.has(e.id)||Nd.set(e.id,q(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(gc({role:o,sourceId:r.payload.sourceId})),n.keys.add(gc({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(gc({role:"user",sourceId:r.payload.sourceId})),n.keys.add(gc({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(QT({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(eE({id:r.payload.id,box:r.boxId}));return mw.set(e.id,n),n}).finally(()=>Nd.delete(e.id))),Nd.get(e.id))}async function tE(e,t){let n=new Set;try{let r=(await hw(e)).keys;for(let o of t||[]){if(!o||o.ts==null||!String(o.text||"").trim()&&!o.images?.length)continue;let s=gc(o);if(r.has(s))continue;r.add(s);let a=o.role==="user"?Od(o.text):null;if(a){await C(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 PT(a,yc),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=H0(o);if(i){await C(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}}),Xi(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 C(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=eA(o.text);if(c&&YT(await q(e).catch(()=>[])))continue;if(c){let l=rA(c),u;if(e.workspace?.kind==="multi"){let w=await ls(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 ao(e.cwd).catch(()=>null))||e.workspace.branch,p=await yd(e,{branch:d,rebase:l}).catch(()=>null),f=p?null:nA(c);if(p&&p.available.length===0)continue;await C(e,{type:"pr.offered",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{offerId:aU(),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 nE(e,t){try{let n=await hw(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=QT(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 C(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}}),MT(e,o),CT(e,o)}}catch{}}async function rE(e,t){try{let n=await hw(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=eE(o);n.keys.has(s)||(n.keys.add(s),await C(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 mw,Nd,oE=W(()=>{fe();es();Gh();Rd();is();iw();hr();gu();Fu();Cd();mw=new Map,Nd=new Map});function iU(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
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 cU(e,t){let n=`${e.id}:${t.name}`;if(!ww.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);ww.set(n,o)}return ww.get(n)}async function sE(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await Lo(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await cU(e,t),o=iU(await Ct(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 ww,aE=W(()=>{fe();Uo();dn();sr();ww=new Map});import Pd from"node:fs";import yw from"node:path";import lU from"node:os";async function pU({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(dU,{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:iE})});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<iE)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 lE(){try{return JSON.parse(Pd.readFileSync(Dd(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function hU(e){try{Pd.mkdirSync(yw.dirname(Dd()),{recursive:!0});let t=Dd()+".tmp";Pd.writeFileSync(t,JSON.stringify(e)),Pd.renameSync(t,Dd())}catch{}}async function gU({fetchImpl:e,now:t=Date.now()}={}){if(lo.rows&&t-lo.at<mU)return lo.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=lE(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await pU({sinceMs:o,cookie:n,fetchImpl:e});lo={at:t,rows:s.map(fU).filter(Boolean),error:null}}catch(s){lo={at:t,rows:null,error:String(s?.message||s)}}return lo.rows}async function yU(e,t){let n=`${e.id}:${t.name}`;if(!gw.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);gw.set(n,o)}return gw.get(n)}async function uE(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!cE())return;let r=await gU({fetchImpl:n});if(!r||!r.length)return;let o=await Jv(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await yU(e,t),a=lE(),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}})}hU({sinceMs:c,seen:[...i].slice(-wU)})}catch{}}var uU,cE,dU,iE,mU,lo,Dd,wU,bw,gw,kw=W(()=>{fe();Qm();sr();uU=process.env.BOSTRAT_STATE_DIR||yw.join(process.env.HOME||lU.tmpdir(),".bostrat"),cE=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,dU="https://cursor.com/api/dashboard/get-filtered-usage-events",iE=100;mU=5*6e4,lo={at:0,rows:null,error:null},Dd=()=>yw.join(uU,"cursor-usage.json");wU=5e3;bw=()=>({enabled:cE(),lastPollAt:lo.at||null,error:lo.error}),gw=new Map});import{randomUUID as bU,createHmac as kU}from"node:crypto";function pE(e){vw=typeof e=="string"&&e?e:null}function xU(e){if(!vw)return null;let t=Ue(e);return t?kU("sha256",Buffer.from(vw,"base64url")).update(t,"utf8").digest("hex").slice(0,16):null}function hE(){let e={};for(let t of mE)e[String(t)]=0;return e.inf=0,{buckets:e,sumMs:0,count:0}}function dE(e,t){let n=Math.max(0,Number(t)||0);for(let r of mE)n<=r&&(e.buckets[String(r)]+=1);e.buckets.inf+=1,e.sumMs+=n,e.count+=1}function wE(){let e={};for(let t of fE)e[t]=0;return e}function SU(){Ln={total:new Map,holds:wE(),queueWaitMs:hE(),readyWaitMs:new Map,verify:new Map,retries:new Map}}function gE({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 bc({kind:e=null,producer:t=null,orchestration:n=!1}={}){return n?`orchestration:${e||"orchestration"}`:e?`scripted:${e}`:vU.has(t)?t:"composer"}function kc({text:e="",producer:t="composer",kind:n=null,provider:r=null,attachments:o=0,now:s=Date.now}={}){let a=s();return{id:bU(),producer:t,kind:n||null,provider:r||null,textBytes:Buffer.byteLength(String(e||""),"utf8"),textHmac16:xU(e),attachments:Number(o)||0,paste:null,t0:a,queuedAt:null,dequeuedAt:null,holds:wE(),requeued:0,readyWaitMs:null,readyOutcome:null,verify:null,settled:!1,outcome:null}}async function xc(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 yE(e,t){fE.includes(t)&&(e?.holds&&(e.holds[t]+=1),Ln.holds[t]+=1)}function vc(e,{ms:t,outcome:n}){e&&(e.readyWaitMs=Math.max(0,Number(t)||0),e.readyOutcome=n||null)}function AU(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(xw(Ln.total,`${s}|${t.producer}|${n}`),a&&dE(Ln.queueWaitMs,i),t.readyWaitMs!=null){let u=`${s}|${t.readyOutcome||"unknown"}`;Ln.readyWaitMs.has(u)||Ln.readyWaitMs.set(u,hE()),dE(Ln.readyWaitMs.get(u),t.readyWaitMs)}let c=AU(e);t.verify?.verdict&&(xw(Ln.verify,`${s}|${t.verify.verdict}|${c||""}`),t.verify.retries&&xw(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 Aa(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 vw,fE,mE,vU,Ln,xw,Sw=W(()=>{fe();Wi();vw=null;fE=["busy","prompt","handoff","noSession","cap","grace"],mE=[500,2e3,1e4,6e4,6e5],vU=new Set(["composer","voice","loop"]);Ln=null;SU();xw=(e,t,n=1)=>e.set(t,(e.get(t)||0)+n)});import Tc from"node:fs/promises";import Nt from"node:path";import TU from"node:os";import EU from"node:net";import{randomUUID as _U}from"node:crypto";import{execFile as IU}from"node:child_process";import{promisify as $U}from"node:util";function DU(e){for(let t of Rw(e))if(!AE.test(t))return t;return null}function TE(e){let t=Rw(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function BU(e,t){let n=TE(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Nt.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return Nt.resolve(e,n[r+1])}return e}function LU(e){let t=!1,n=!1;for(let r of Rw(e)){if(!t){if(AE.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&Aw(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,Aw(r))return`serves the filesystem root ("${r}")`;continue}if(Aw(r))return`serves the filesystem root ("${r}")`;if(jU(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function UU(e,t){if(Nt.resolve(e)===Nt.resolve(t))return!0;let[n,r]=await Promise.all([Bd(e),Bd(t)]);return n===r}async function HU(e){let n=(await Tc.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 EE(Nt.join(e,o.name,_w))?`./${o.name}`:null))).filter(Boolean)}async function WU(e,t){let[n,r]=await Promise.all([Bd(e),Bd(t)]),o=Nt.relative(n,r);if(o&&(o.startsWith("..")||Nt.isAbsolute(o)))throw Sc(`serve directory escapes the Track workspace: ${t}`)}async function qU(e,t){let n=LU(t);if(n)throw Sc(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await FU(e))throw Sc(`serve directory does not exist: ${e}`);let r=DU(t);if(!r)return;if(!r.includes("/")&&!await m0(r,{loginShell:!0,failOpen:!0}))throw Sc(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(CU.test(t)||NU.has(r)||!MU.has(r)||!TE(t).includes("run")||PU.test(t))return;let o=BU(e,t);if(await EE(Nt.join(o,_w)))return;let s=await HU(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Sc(`no ${_w} in ${o}${a}`)}async function yr(){return Fn||(Tw||=(async()=>{try{Fn=JSON.parse(await Tc.readFile(SE,"utf8"))}catch{Fn={views:[],adoptWatermarks:{}}}finally{Tw=null}return Fn.views||=[],Fn.adoptWatermarks||={},Fn})(),Tw)}async function Hn(){await Ot(SE,Fn)}function gr(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 Mw(e){return Ac.get(e)||null}function IE(e,{name:t,box:n}={}){let r=Ac.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 Ac.set(e,o),o}function Iw(e,t){let n=Ac.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function jd(e,{error:t=null}={}){let n=Ac.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function $E(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 $w(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function KU(e,t){let n=$w(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!xE.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!xE.has(s))return s}}function Ec(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=EU.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 Cw(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 RE(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function GU(e,t){let n=new Set(e.views.map(o=>o.port)),r=RE();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=bE;o<=kE;o++)if(!(n.has(o)||r.has(o))&&!await Ec(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${bE}-${kE}`)}async function VU(e){try{let{stdout:t}=await Un(ne,re("list-sessions","-F","#{session_name}"));return t.split(`
|
|
201
|
+
`).some(n=>n.trim()===Ta(e))}catch{return!1}}async function _c(e){try{await Un(ne,re("kill-session","-t",Ta(e)))}catch{}}async function Nw(e){if(!await Ri())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=Ta(e.slug),n=e.serveCwd??e.cwd,r=Nt.dirname(process.execPath);await Un(ne,re("new-session","-d","-s",t,"-c",n)),await Un(ne,re("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await Un(ne,re("send-keys","-t",t,"Enter")),await Un(ne,re("send-keys","-t",t,"-l",e.cmd)),await Un(ne,re("send-keys","-t",t,"Enter"))}async function vE(e,t=12){try{let{stdout:n}=await Un(ne,re("capture-pane","-t",Ta(e),"-p"));return String(n||"").trimEnd().split(`
|
|
202
202
|
`).filter(Boolean).slice(-t).join(`
|
|
203
|
-
`)}catch{return""}}async function kU(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await yU(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await lE(e.slug)||"server exited before listening"),pr.delete(e.id),await fr(e,e.state,e.error?{error:e.error}:{}),!0}if(await vc(e.port))return pr.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await fr(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>bU?(e.state="error",e.error=await lE(e.slug)||"never started listening",await fr(e,"error",{error:e.error}),!0):!1;let o=(pr.get(e.id)||0)+1;return pr.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function Md(){if(!xw){xw=!0;try{let e=await hr(),t=!1;for(let n of e.views)await kU(n)&&(t=!0);t&&await Ln()}finally{xw=!1}}}async function Rw({trackId:e}={}){await Md();let t=await hr();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function yE(e){let n=(await hr()).views.find(r=>r.id===e);return n?{...n}:null}function Ow(e){return Bn&&Bn.views.find(t=>t.slug===e)||null}async function Mw({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=Ct.resolve(r,o??"."),d=i==="detected"?null:async()=>{await hU(r,u),await mU(u,n)},p=await hr();if(i!=="detected"){let h=Aw(t),g=String(n).trim(),y=p.views.find(x=>x.trackId===e&&(x.slug===h||Aw(x.name)===h||x.cmd===g&&Ct.resolve(x.serveCwd??x.cwd)===u));if(y)return xU(p,y,{cmd:g,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),p.views.filter(h=>h.state==="live"||h.state==="starting").length>=uo)throw new Error(`view cap reached (${uo} live) \u2014 stop one first`);let w={id:ZF().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:wU(p,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await gU(p,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await $w(w),p.views.push(w),await Ln(),await fr(w,"created",{origin:i}),Sw(e,w.id),i==="detected"&&await vc(w.port)&&(w.state="live",w.lastHealthyAt=Date.now(),await Ln(),await fr(w,"live")),{...w}}async function xU(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 pU(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await vc(t.port))return await Ln(),Sw(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>=uo)throw new Error(`view cap reached (${uo} live) \u2014 stop one first`);return t.origin==="detected"?(await Iw(t.port),t.origin="agent"):await Sc(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),pr.delete(t.id),await $w(t),await Ln(),await fr(t,"created",{restarted:!0,reused:!0}),Sw(t.trackId,t.id),{...t,reused:!0}}async function bE(e){let n=(await hr()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await Iw(n.port):await Sc(n.slug),n.state!=="stopped"&&(n.state="stopped",pr.delete(n.id),await Ln(),await fr(n,"stopped")),{...n}}async function kE(e){let t=await hr(),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>=uo)throw new Error(`view cap reached (${uo} live) \u2014 stop one first`);return await Sc(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),pr.delete(n.id),await $w(n),await Ln(),await fr(n,"created",{restarted:!0}),{...n}}async function xE(e){let t=await hr(),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 Iw(r.port):await Sc(r.slug),t.views.splice(n,1),pr.delete(r.id),await Ln(),await fr(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function Cw(e){let t=await hr(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Sc(r.slug),pr.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await Ln()),n.length}async function vE(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await hr(),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 Ln()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(vU)){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(!gE().has(c)&&!n.views.some(l=>l.port===c)&&await vc(c))try{i.push(await Mw({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 jn,tU,uE,aE,iE,uo,nU,va,cE,bc,rU,vw,oU,sU,aU,Tw,dE,bw,lU,dU,fE,Rd,Bn,kw,kc,Ew,hE,pr,bU,xw,vU,Cd=q(()=>{ut();he();Nr();jn=eU(QF),tU=process.env.BOSTRAT_STATE_DIR||Ct.join(process.env.HOME||YF.tmpdir(),".bostrat"),uE=Ct.join(tU,"views.json"),[aE,iE]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),uo=Number(process.env.BOSTRAT_VIEW_MAX)||3,nU="bostrat-view-",va=e=>`${nU}${e}`,cE=new Set(["bostrat","www","api","views","app"]),bc=e=>Object.assign(new Error(e),{status:400}),rU=new Set(["npm","pnpm","yarn","bun"]),vw="package.json",oU=/[|;&`]|\$\(/,sU=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),aU=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Tw=e=>String(e).trim().split(/\s+/).filter(Boolean),dE=/^[A-Za-z_][A-Za-z0-9_]*=/;bw=e=>e==="/",lU=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");dU=e=>xc.stat(e).then(t=>t.isDirectory(),()=>!1),fE=e=>xc.stat(e).then(t=>t.isFile(),()=>!1),Rd=e=>xc.realpath(e).catch(()=>Ct.resolve(e));Bn=null,kw=null;kc=new Map,Ew=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,hE=12e3;pr=new Map,bU=12e4;xw=!1;vU=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function Sa(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>Pw?null:n}function AU(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function Nd(e,{bypassThrottle:t=!1}={}){let n=e?.workspace?.branch;if(!n)return;let r;try{r=await K(e)}catch{return}let o=Sa(r);if(o==null)return;let s=(e.boxes||[]).find(d=>d.name===e.leadBox);if(await os(e.leadBox,s?.agent).catch(()=>!1))return;let a=Date.now();if(!t&&a-(Nw.get(e.id)||0)<SU)return;Nw.set(e.id,a);let i;try{i=await Cn(e.cwd,{caller:"reconcile"})}catch{return}let c=await wa(e).catch(()=>[n]),l=(i||[]).find(d=>c.includes(d.headRefName));if(!l)return;let u=await _e(e.cwd).catch(()=>null);if(u){let{link:d,created:p}=await Lt({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 or(u,l.number))){if(!AU(d,o))return;let f=await K(e).catch(()=>null);if(f&&Sa(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(()=>{}),Nw.delete(e.id)}var SU,Nw,Pw,Ac=q(()=>{he();rs();Or();Mn();ac();ss();SU=6e3,Nw=new Map,Pw=30*6e4});import Dw from"node:os";import{execFile as TU}from"node:child_process";import{promisify as EU}from"node:util";async function Pd(){if(process.platform!=="darwin")return Dw.freemem();try{let{stdout:e}=await _U("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:Dw.freemem()}catch{return Dw.freemem()}}var _U,Bw=q(()=>{_U=EU(TU)});import Ze from"node:fs/promises";import tt from"node:path";import EE from"node:os";import{randomUUID as IU}from"node:crypto";import{execFile as $U}from"node:child_process";import{promisify as RU}from"node:util";import{chromium as OU}from"playwright-core";async function Hn(){return cs||(jw||=(async()=>{try{cs=JSON.parse(await Ze.readFile(IE,"utf8"))}catch{cs={mocks:[]}}finally{jw=null}return cs.mocks||=[],cs})(),jw)}async function Qt(){await Rt(IE,cs)}function ME(e,t){Un(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&Un(e,t.v,"png")}function At(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 en(e,t){let n=Ec(e.cwd);return{html:tt.join(n,`${e.id}.v${t}.html`),png:tt.join(n,`${e.id}.v${t}.png`)}}function Tc(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?tt.join(e.cwd,r):null}function hn(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 Uw(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function Dd(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(jU)}function LU(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 FU(e){let t=Buffer.alloc(24),n=await Ze.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function UU({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 HU(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((w,h)=>{r.onload=w,r.onerror=()=>h(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 h=w*o*4,g=0;g<o;g++,h+=4)Math.abs(c[h]-l[0])+Math.abs(c[h+1]-l[1])+Math.abs(c[h+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 WU(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 qU({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await OU.launch({executablePath:$E,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Ft("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:Aa})).newPage();a.setDefaultTimeout(Ta),await a.goto(`file://${e}`,{waitUntil:"load",timeout:Ta}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Ta});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await HU(a,i),u=l&&UU({...l,pad:NU*Aa,align:Aa});u&&(i=await WU(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<RE)throw new Ft(`capture produced a ${i.length}-byte image`);let c=Uw(i);if(!c)throw new Ft("output is not a PNG");return await Ze.writeFile(t,i),{width:Math.round(c.width/Aa),height:Math.round(c.height/Aa),dpr:Aa,renderMs:Date.now()-r}}catch(s){throw s instanceof Ft?s:new Ft(/timeout/i.test(s?.message||"")?`render timed out after ${Ta}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function TE({htmlPath:e,outPath:t,width:n}){let r=await Ze.mkdtemp(tt.join(EE.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Fw(SE,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Ta,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new Ft(`firefox not found (${SE})`,""):new Ft(c?.killed?`render timed out after ${Ta}ms`:"firefox exited non-zero",s)}let a=await Ze.stat(t).catch(()=>null);if(!a||a.size<RE)throw new Ft(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Uw(await FU(t));if(!i)throw new Ft("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await Ze.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function KU(e){if(MU==="firefox")return TE(e);try{return await qU(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await TE(e)}catch(n){throw/not found/.test(n?.message||"")?new Ft(`no browser installed \u2014 chromium missing at ${$E} 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 CE(e){return Lw=Lw.then(e,e),Lw}async function NE(e,t){let n=await Hn(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await Qt(),await At(r,"rendering",{version:t});try{let o=await Pd();if(o<PU)throw new Ft(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=en(r,t),a=await GU({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await Ze.rm(s.png,{force:!0}).catch(()=>{});return}Hw(r,{v:t,source:"html",htmlRel:tt.relative(r.cwd,s.html),pngRel:tt.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await Qt(),await At(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),Fn(r),Un(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await Qt(),await At(r,"error",{version:t,error:r.error}),Fn(r)}}}function Hw(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 zU({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?CU:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function _c({trackId:e,group:t}={}){let n=await Hn(),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 mr(e){let n=(await Hn()).mocks.find(r=>r.id===e);return n?{...n}:null}function PE({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&&!Dd(t))throw new Error("image must be a PNG");if(n){let o=LU(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>AE)throw new Error(`video must be ${AE/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function DE({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=zU({trackId:e,name:t,cwd:s,width:a}),p=typeof n=="string"&&n.trim(),f=PE({html:n,image:r,video:o}),w=await Hn(),h={id:IU().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 Ze.mkdir(Ec(s),{recursive:!0}),w.mocks.push(h),p?(await Ze.writeFile(en(h,1).html,n),await Qt(),await At(h,"created"),Fn(h),Un(h,1,"html"),CE(()=>NE(h.id,1))):o?(await jE(h,1,o,f),await Qt(),await At(h,"created"),await At(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr,durationS:h.versions[0]?.durationS??null}),Fn(h),ME(h,h.versions[0])):(await LE(h,1,r),await Qt(),await At(h,"created"),await At(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr}),Fn(h),Un(h,1,"png")),{...h}}async function BE(e,{html:t,image:n,video:r,width:o}={}){let a=(await Hn()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=PE({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 Ze.mkdir(Ec(a.cwd),{recursive:!0}),i?(a.state="queued",await Ze.writeFile(en(a,l).html,t),await Qt(),await At(a,"updated",{version:l}),Fn(a),Un(a,l,"html"),CE(()=>NE(a.id,l))):r?(await jE(a,l,r,c),await Qt(),await At(a,"updated",{version:l}),await At(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:hn(a)?.durationS??null}),Fn(a),ME(a,a.versions.find(u=>u.v===l))):(await LE(a,l,n),await Qt(),await At(a,"updated",{version:l}),await At(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Fn(a),Un(a,l,"png")),{...a}}async function VU(e){try{let{stdout:t}=await Fw(DU,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:OE,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 JU(e,t){try{await Fw(BU,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:OE,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await Ze.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function jE(e,t,n,r){let o=Ec(e.cwd),s=tt.join(o,`${e.id}.v${t}.${r.ext}`),a=tt.join(o,`${e.id}.v${t}.poster.png`);await Ze.writeFile(s,n);let i=await VU(s),c=await JU(s,a);Hw(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:tt.relative(e.cwd,s),posterRel:c?tt.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 LE(e,t,n){let r=Uw(n);if(!r)throw new Error("image must be a PNG");let o=en(e,t);await Ze.writeFile(o.png,n),Hw(e,{v:t,source:"image",htmlRel:null,pngRel:tt.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function FE(e){let t=await Hn(),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 At(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function UE(e){let t=await Hn(),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=en(r,o);await Ze.rm(s.html,{force:!0}).catch(()=>{}),await Ze.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await Ze.rm(tt.join(r.cwd,s),{force:!0}).catch(()=>{});return await Qt(),await At(r,"deleted"),on("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function HE(e){let n=(await Hn()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=tt.join(_E,"mock-archive",e.id),o=Ec(e.cwd),s=tt.join(r,".bostrat","mocks");await Ze.mkdir(s,{recursive:!0});let a=await Ze.readdir(o).catch(()=>[]);for(let i of a)await Ze.copyFile(tt.join(o,i),tt.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 Ze.stat(tt.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(),Fn(i)}return await Qt(),n.length}async function WE(e){let t=await Hn(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await Qt();for(let r of n)on("mocks",r.id).catch(()=>{})}return n.length}async function qE(){if(!Ue())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await it("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Hn();for(let r of n.mocks){await Fn(r);for(let o of r.versions||[])o.htmlRel&&await Un(r,o.v,"html"),(o.pngRel||o.posterRel)&&await Un(r,o.v,"png"),o.videoRel&&await Un(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var Fw,_E,IE,MU,$E,SE,CU,Aa,NU,RE,Ta,PU,AE,DU,BU,OE,Ft,cs,jw,Fn,Un,Ec,jU,GU,Lw,Ea=q(()=>{he();Nr();Bw();Dr();Fw=RU($U),_E=process.env.BOSTRAT_STATE_DIR||tt.join(process.env.HOME||EE.tmpdir(),".bostrat"),IE=tt.join(_E,"mocks.json"),MU=process.env.BOSTRAT_MOCK_ENGINE||"chromium",$E=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",SE=process.env.BOSTRAT_FIREFOX_BIN||"firefox",CU=1300,Aa=2,NU=32,RE=1500,Ta=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,PU=700*1024*1024,AE=100*1024*1024,DU=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",BU=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",OE=2e4,Ft=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
203
|
+
`)}catch{return""}}async function JU(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await VU(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await vE(e.slug)||"server exited before listening"),wr.delete(e.id),await gr(e,e.state,e.error?{error:e.error}:{}),!0}if(await Ec(e.port))return wr.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await gr(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>zU?(e.state="error",e.error=await vE(e.slug)||"never started listening",await gr(e,"error",{error:e.error}),!0):!1;let o=(wr.get(e.id)||0)+1;return wr.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function Ld(){if(!Ew){Ew=!0;try{let e=await yr(),t=!1;for(let n of e.views)await JU(n)&&(t=!0);t&&await Hn()}finally{Ew=!1}}}async function Pw({trackId:e}={}){await Ld();let t=await yr();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function OE(e){let n=(await yr()).views.find(r=>r.id===e);return n?{...n}:null}function Dw(e){return Fn&&Fn.views.find(t=>t.slug===e)||null}async function Bw({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=Nt.resolve(r,o??"."),d=i==="detected"?null:async()=>{await WU(r,u),await qU(u,n)},p=await yr();if(i!=="detected"){let m=$w(t),g=String(n).trim(),y=p.views.find(k=>k.trackId===e&&(k.slug===m||$w(k.name)===m||k.cmd===g&&Nt.resolve(k.serveCwd??k.cwd)===u));if(y)return YU(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>=uo)throw new Error(`view cap reached (${uo} live) \u2014 stop one first`);let w={id:_U().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:KU(p,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await GU(p,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Nw(w),p.views.push(w),await Hn(),await gr(w,"created",{origin:i}),Iw(e,w.id),i==="detected"&&await Ec(w.port)&&(w.state="live",w.lastHealthyAt=Date.now(),await Hn(),await gr(w,"live")),{...w}}async function YU(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 UU(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Ec(t.port))return await Hn(),Iw(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>=uo)throw new Error(`view cap reached (${uo} live) \u2014 stop one first`);return t.origin==="detected"?(await Cw(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(),wr.delete(t.id),await Nw(t),await Hn(),await gr(t,"created",{restarted:!0,reused:!0}),Iw(t.trackId,t.id),{...t,reused:!0}}async function ME(e){let n=(await yr()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await Cw(n.port):await _c(n.slug),n.state!=="stopped"&&(n.state="stopped",wr.delete(n.id),await Hn(),await gr(n,"stopped")),{...n}}async function CE(e){let t=await yr(),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>=uo)throw new Error(`view cap reached (${uo} live) \u2014 stop one first`);return await _c(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),wr.delete(n.id),await Nw(n),await Hn(),await gr(n,"created",{restarted:!0}),{...n}}async function NE(e){let t=await yr(),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 Cw(r.port):await _c(r.slug),t.views.splice(n,1),wr.delete(r.id),await Hn(),await gr(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function jw(e){let t=await yr(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await _c(r.slug),wr.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await Hn()),n.length}async function PE(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await yr(),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(XU)){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(!RE().has(c)&&!n.views.some(l=>l.port===c)&&await Ec(c))try{i.push(await Bw({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,RU,SE,bE,kE,uo,OU,Ta,xE,Sc,MU,_w,CU,NU,PU,Rw,AE,Aw,jU,FU,EE,Bd,Fn,Tw,Ac,Ow,_E,wr,zU,Ew,XU,Fd=W(()=>{mt();fe();Dr();Un=$U(IU),RU=process.env.BOSTRAT_STATE_DIR||Nt.join(process.env.HOME||TU.tmpdir(),".bostrat"),SE=Nt.join(RU,"views.json"),[bE,kE]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),uo=Number(process.env.BOSTRAT_VIEW_MAX)||3,OU="bostrat-view-",Ta=e=>`${OU}${e}`,xE=new Set(["bostrat","www","api","views","app"]),Sc=e=>Object.assign(new Error(e),{status:400}),MU=new Set(["npm","pnpm","yarn","bun"]),_w="package.json",CU=/[|;&`]|\$\(/,NU=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),PU=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Rw=e=>String(e).trim().split(/\s+/).filter(Boolean),AE=/^[A-Za-z_][A-Za-z0-9_]*=/;Aw=e=>e==="/",jU=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");FU=e=>Tc.stat(e).then(t=>t.isDirectory(),()=>!1),EE=e=>Tc.stat(e).then(t=>t.isFile(),()=>!1),Bd=e=>Tc.realpath(e).catch(()=>Nt.resolve(e));Fn=null,Tw=null;Ac=new Map,Ow=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,_E=12e3;wr=new Map,zU=12e4;Ew=!1;XU=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function Ea(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>Fw?null:n}function QU(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function Ud(e,{bypassThrottle:t=!1}={}){let n=e?.workspace?.branch;if(!n)return;let r;try{r=await q(e)}catch{return}let o=Ea(r);if(o==null)return;let s=(e.boxes||[]).find(d=>d.name===e.leadBox);if(await as(e.leadBox,s?.agent).catch(()=>!1))return;let a=Date.now();if(!t&&a-(Lw.get(e.id)||0)<ZU)return;Lw.set(e.id,a);let i;try{i=await Bn(e.cwd,{caller:"reconcile"})}catch{return}let c=await ba(e).catch(()=>[n]),l=(i||[]).find(d=>c.includes(d.headRefName));if(!l)return;let u=await $e(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 cr(u,l.number))){if(!QU(d,o))return;let f=await q(e).catch(()=>null);if(f&&Ea(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(()=>{}),Lw.delete(e.id)}var ZU,Lw,Fw,Ic=W(()=>{fe();ss();Cr();Dn();dc();is();ZU=6e3,Lw=new Map,Fw=30*6e4});import Uw from"node:os";import{execFile as e4}from"node:child_process";import{promisify as t4}from"node:util";async function Hd(){if(process.platform!=="darwin")return Uw.freemem();try{let{stdout:e}=await n4("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:Uw.freemem()}catch{return Uw.freemem()}}var n4,Hw=W(()=>{n4=t4(e4)});import nt from"node:fs/promises";import ot from"node:path";import LE from"node:os";import{randomUUID as r4}from"node:crypto";import{execFile as o4}from"node:child_process";import{promisify as s4}from"node:util";import{chromium as a4}from"playwright-core";async function Kn(){return us||(Ww||=(async()=>{try{us=JSON.parse(await nt.readFile(UE,"utf8"))}catch{us={mocks:[]}}finally{Ww=null}return us.mocks||=[],us})(),Ww)}async function tn(){await Ot(UE,us)}function KE(e,t){qn(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&qn(e,t.v,"png")}function Et(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=Rc(e.cwd);return{html:ot.join(n,`${e.id}.v${t}.html`),png:ot.join(n,`${e.id}.v${t}.png`)}}function $c(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?ot.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 Gw(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function Wd(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(f4)}function m4(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 nt.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 g4(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 y4(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 b4({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await a4.launch({executablePath:HE,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(Ia),await a.goto(`file://${e}`,{waitUntil:"load",timeout:Ia}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Ia});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await g4(a,i),u=l&&w4({...l,pad:l4*_a,align:_a});u&&(i=await y4(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<WE)throw new Ht(`capture produced a ${i.length}-byte image`);let c=Gw(i);if(!c)throw new Ht("output is not a PNG");return await nt.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 ${Ia}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function jE({htmlPath:e,outPath:t,width:n}){let r=await nt.mkdtemp(ot.join(LE.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Kw(DE,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Ia,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new Ht(`firefox not found (${DE})`,""):new Ht(c?.killed?`render timed out after ${Ia}ms`:"firefox exited non-zero",s)}let a=await nt.stat(t).catch(()=>null);if(!a||a.size<WE)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=Gw(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 nt.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function k4(e){if(i4==="firefox")return jE(e);try{return await b4(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await jE(e)}catch(n){throw/not found/.test(n?.message||"")?new Ht(`no browser installed \u2014 chromium missing at ${HE} 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 GE(e){return qw=qw.then(e,e),qw}async function VE(e,t){let n=await Kn(),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 Hd();if(o<u4)throw new Ht(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=nn(r,t),a=await x4({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await nt.rm(s.png,{force:!0}).catch(()=>{});return}Vw(r,{v:t,source:"html",htmlRel:ot.relative(r.cwd,s.html),pngRel:ot.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await tn(),await Et(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 Et(r,"error",{version:t,error:r.error}),Wn(r)}}}function Vw(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 v4({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?c4:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Oc({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 br(e){let n=(await Kn()).mocks.find(r=>r.id===e);return n?{...n}:null}function zE({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&&!Wd(t))throw new Error("image must be a PNG");if(n){let o=m4(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>BE)throw new Error(`video must be ${BE/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function JE({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=v4({trackId:e,name:t,cwd:s,width:a}),p=typeof n=="string"&&n.trim(),f=zE({html:n,image:r,video:o}),w=await Kn(),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 nt.mkdir(Rc(s),{recursive:!0}),w.mocks.push(m),p?(await nt.writeFile(nn(m,1).html,n),await tn(),await Et(m,"created"),Wn(m),qn(m,1,"html"),GE(()=>VE(m.id,1))):o?(await XE(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}),Wn(m),KE(m,m.versions[0])):(await ZE(m,1,r),await tn(),await Et(m,"created"),await Et(m,"ready",{version:1,width:m.width,height:m.height,dpr:m.dpr}),Wn(m),qn(m,1,"png")),{...m}}async function YE(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=zE({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 nt.mkdir(Rc(a.cwd),{recursive:!0}),i?(a.state="queued",await nt.writeFile(nn(a,l).html,t),await tn(),await Et(a,"updated",{version:l}),Wn(a),qn(a,l,"html"),GE(()=>VE(a.id,l))):r?(await XE(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:gn(a)?.durationS??null}),Wn(a),KE(a,a.versions.find(u=>u.v===l))):(await ZE(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}),Wn(a),qn(a,l,"png")),{...a}}async function S4(e){try{let{stdout:t}=await Kw(d4,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:qE,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 A4(e,t){try{await Kw(p4,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:qE,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await nt.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function XE(e,t,n,r){let o=Rc(e.cwd),s=ot.join(o,`${e.id}.v${t}.${r.ext}`),a=ot.join(o,`${e.id}.v${t}.poster.png`);await nt.writeFile(s,n);let i=await S4(s),c=await A4(s,a);Vw(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:ot.relative(e.cwd,s),posterRel:c?ot.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 ZE(e,t,n){let r=Gw(n);if(!r)throw new Error("image must be a PNG");let o=nn(e,t);await nt.writeFile(o.png,n),Vw(e,{v:t,source:"image",htmlRel:null,pngRel:ot.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function QE(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 Et(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function e_(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 nt.rm(s.html,{force:!0}).catch(()=>{}),await nt.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await nt.rm(ot.join(r.cwd,s),{force:!0}).catch(()=>{});return await tn(),await Et(r,"deleted"),an("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function t_(e){let n=(await Kn()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=ot.join(FE,"mock-archive",e.id),o=Rc(e.cwd),s=ot.join(r,".bostrat","mocks");await nt.mkdir(s,{recursive:!0});let a=await nt.readdir(o).catch(()=>[]);for(let i of a)await nt.copyFile(ot.join(o,i),ot.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await nt.stat(ot.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=e.name||e.id,i.updatedAt=Date.now(),Wn(i)}return await tn(),n.length}async function n_(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 r_(){if(!Ke())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 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 Kw,FE,UE,i4,HE,DE,c4,_a,l4,WE,Ia,u4,BE,d4,p4,qE,Ht,us,Ww,Wn,qn,Rc,f4,x4,qw,$a=W(()=>{fe();Dr();Hw();jr();Kw=s4(o4),FE=process.env.BOSTRAT_STATE_DIR||ot.join(process.env.HOME||LE.tmpdir(),".bostrat"),UE=ot.join(FE,"mocks.json"),i4=process.env.BOSTRAT_MOCK_ENGINE||"chromium",HE=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",DE=process.env.BOSTRAT_FIREFOX_BIN||"firefox",c4=1300,_a=2,l4=32,WE=1500,Ia=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,u4=700*1024*1024,BE=100*1024*1024,d4=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",p4=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",qE=2e4,Ht=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
204
204
|
${n.trim().split(`
|
|
205
205
|
`).slice(-6).join(`
|
|
206
|
-
`)}`:t),this.name="MockRenderError"}},cs=null,jw=null;Fn=e=>vt("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),Un=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?Tc(e,r,"video"):r&&Tc(e,r,n)||(n==="html"?en(e,t).html:null);return o?Pr("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Ec=e=>tt.join(e,".bostrat","mocks");jU=Buffer.from([137,80,78,71,13,10,26,10]);GU=KU,Lw=Promise.resolve()});import YU from"node:fs/promises";import JE from"node:path";import XU from"node:os";import{randomUUID as YE}from"node:crypto";async function gr(){return wr||(Ww||=(async()=>{let e=!0;try{wr=JSON.parse(await YU.readFile(XE,"utf8"))}catch{wr={flows:[]},e=!1}finally{Ww=null}if(wr.flows||=[],!e&&Ue()){let{records:t,source:n}=await it("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&wr.flows.push(r.data);wr.flows.length&&await _a().catch(()=>{})}}return wr})(),Ww)}async function _a(){await Rt(XE,wr)}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 ZE({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>KE)throw new Error(`steps must be an array of 1-${KE}`);let r=await _c({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>zE)throw new Error(`${l}: caption over ${zE} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>VE)throw new Error(`${l}: title over ${VE} 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],h=null;if(c.v!=null&&(h=Number(c.v),!Number.isInteger(h)||!(w.versions||[]).some(x=>x.v===h)))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:YE().slice(0,8);s.push({id:y,mockId:w.id,caption:u,title:d,v:h})}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 QU(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>GE)throw new Error(`name over ${GE} chars`);return e.trim()}async function QE({trackId:e}={}){let t=await gr();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function e4(e){let n=(await gr()).flows.find(r=>r.id===e);return n?{...n}:null}async function e_(e){let t=await e4(e);if(!t)return null;let n=new Map((await _c({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=hn(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 t_({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=QU(t),i=await gr(),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 ZE({trackId:e,steps:n}),d=Date.now(),p={id:YE().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 _a(),await qw(p,"created"),Bd(p),{...p}}async function n_(e,{steps:t,note:n}={}){let o=(await gr()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await ZE({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 _a(),await qw(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Bd(o),{...o}}async function r_(e){let t=await gr(),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 _a(),await qw(r,"deleted"),on("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function o_(e){return(await gr()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function s_(e){let n=(await gr()).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(),Bd(r);return n.length&&await _a(),n.length}async function a_(e){let t=await gr(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await _a();for(let r of n)on("flows",r.id).catch(()=>{})}return n.length}async function i_(){if(!Ue())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await it("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await gr();for(let r of n.flows)await Bd(r);return{pushed:n.flows.length,source:"plane"}}var ZU,XE,KE,GE,zE,VE,wr,Ww,Bd,Kw=q(()=>{he();Nr();Ea();Dr();ZU=process.env.BOSTRAT_STATE_DIR||JE.join(process.env.HOME||XU.tmpdir(),".bostrat"),XE=JE.join(ZU,"flows.json"),KE=20,GE=80,zE=500,VE=80,wr=null,Ww=null;Bd=e=>vt("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import kr from"node:fs/promises";import xr from"node:path";import t4 from"node:os";import{randomUUID as n4}from"node:crypto";function Ic(e){return r4.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function Wn(){return yr||(Gw||=(async()=>{let e=!0;try{yr=JSON.parse(await kr.readFile(l_,"utf8"))}catch{yr={runbooks:[]},e=!1}finally{Gw=null}return yr.runbooks||=[],!e&&Ue()&&await o4().catch(()=>{}),yr})(),Gw)}async function us(){await Rt(l_,yr)}function Ld(e){return $c(vt("runbooks",{...e},{trackId:e.trackId}))}async function o4(){let{records:e,source:t}=await it("runbooks");if(!(t!=="plane"||!e.length)){await kr.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 ki("runbooks",r.id,o.v,"md");typeof s=="string"&&await kr.writeFile(br(r,o.v),s).catch(()=>{})}yr.runbooks.push(r)}yr.runbooks.length&&await us()}}function Ia(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 br(e,t){return xr.join(jd,`${e.id}.v${t}.md`)}function u_(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 d_(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>c_)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${c_/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Ic(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 s4(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function a4({trackId:e,status:t}={}){let n=await Wn(),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 p_({trackId:e,status:t}={}){let n=await Wn(),r=await a4({trackId:e,status:t});if(!Ue())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await it("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 zw(e){let t=await i4(e);if(t)return t;if(!Ue())return null;let{records:n,source:r}=await it("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 f_(e,t){let n=await ki("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function i4(e){let n=(await Wn()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function h_({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=d_(r),c=await Wn(),l={id:n4().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 kr.mkdir(jd,{recursive:!0}),await kr.writeFile(br(l,1),r),l.versions.push({v:1,mdRel:xr.relative(ls,br(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await us(),await Ia(l,"created",{version:1}),Ld(l),$c(Pr("runbooks",l.id,1,"md",br(l,1))),{...l}}async function m_(e,{md:t,note:n}={}){let o=(await Wn()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");s4(o,"update");let s=d_(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await kr.mkdir(jd,{recursive:!0}),await kr.writeFile(br(o,a),t),o.versions.push({v:a,mdRel:xr.relative(ls,br(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await us(),await Ia(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Ld(o),$c(Pr("runbooks",o.id,a,"md",br(o,a))),{...o}}async function w_(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await Wn()).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 us(),await Ia(r,t==="done"?"done":"reopened"),Ld(r),{...r})}async function g_(e){let t=await Wn(),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 kr.rm(xr.join(ls,o.mdRel),{force:!0}).catch(()=>{});return await us(),r.trackDeletedAt||await Ia(r,"deleted"),$c(on("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function y_(e){let n=(await Wn()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Ia(r,"orphaned"),r.cwd=null,Ld(r);return n.length&&await us(),n.length}async function b_(e){let t=await Wn(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await Ia(r,"deleted");for(let o of r.versions||[])await kr.rm(xr.join(ls,o.mdRel),{force:!0}).catch(()=>{});$c(on("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await us()),n.length}async function k_(){if(!Ue())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await it("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Wn();for(let r of n.runbooks){await vt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await Pr("runbooks",r.id,o.v,"md",xr.join(ls,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var ls,l_,jd,c_,r4,yr,Gw,$c,Fd=q(()=>{Nr();he();Dr();ls=process.env.BOSTRAT_STATE_DIR||xr.join(process.env.HOME||t4.tmpdir(),".bostrat"),l_=xr.join(ls,"runbooks.json"),jd=xr.join(ls,"runbooks"),c_=256*1024,r4=[{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/}];yr=null,Gw=null;$c=e=>Promise.resolve(e).catch(()=>{})});import Vw from"node:fs/promises";import Rc from"node:path";import{randomUUID as c4}from"node:crypto";function Yw(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return l4.has(t)?"image":u4.has(t)?"doc":null}function d4(e){let r=Rc.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 S_(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>x_)throw new Error(`file too large \u2014 max ${Math.floor(x_/(1024*1024))} MB`);let r=d4(t),o=Rc.extname(r).slice(1),s=Yw(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=c4().slice(0,8),i=Jw(e);await Vw.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Vw.writeFile(Rc.join(i,c),n,{mode:384}),await Vw.chmod(Rc.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${v_}/${c}`,kind:s,size:n.length}}function A_(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function Xw(e){return e.role==="user"&&p4.test((e.text||"").trim())}var v_,Jw,x_,l4,u4,p4,Zw=q(()=>{v_=".bostrat/uploads",Jw=e=>Rc.join(e,v_),x_=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,l4=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),u4=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"]);p4=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function ds(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}var T_=q(()=>{});import{execFile as f4}from"node:child_process";import{promisify as h4}from"node:util";function Qw(e){return`refs/bostrat/wip/${e}`}function g4(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 y4(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:w4}function $_(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 b4(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 po(s,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await po(s,["remote","get-url","origin"])}catch{return E_.has(s)||(E_.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 ia(s,"bostrat: wip snapshot"),{stdout:c}=await po(s,["rev-parse",`${i}^{tree}`]),l=c.trim();if(!n&&Ud.get(s)===l)return{pushed:!1,reason:"unchanged"};let u=Qw(o);return await po(s,["push","--force","origin",`${i}:${u}`],__),Ud.set(s,l),await C(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:u,sha:i,wip:!0,...R_(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
|
|
206
|
+
`)}`:t),this.name="MockRenderError"}},us=null,Ww=null;Wn=e=>At("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),qn=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?$c(e,r,"video"):r&&$c(e,r,n)||(n==="html"?nn(e,t).html:null);return o?Br("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Rc=e=>ot.join(e,".bostrat","mocks");f4=Buffer.from([137,80,78,71,13,10,26,10]);x4=k4,qw=Promise.resolve()});import T4 from"node:fs/promises";import c_ from"node:path";import E4 from"node:os";import{randomUUID as l_}from"node:crypto";async function xr(){return kr||(zw||=(async()=>{let e=!0;try{kr=JSON.parse(await T4.readFile(u_,"utf8"))}catch{kr={flows:[]},e=!1}finally{zw=null}if(kr.flows||=[],!e&&Ke()){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&&kr.flows.push(r.data);kr.flows.length&&await Ra().catch(()=>{})}}return kr})(),zw)}async function Ra(){await Ot(u_,kr)}function Jw(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 d_({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>o_)throw new Error(`steps must be an array of 1-${o_}`);let r=await Oc({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>a_)throw new Error(`${l}: caption over ${a_} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>i_)throw new Error(`${l}: title over ${i_} chars`);let p=String(c.mock??"").trim().replace(/^mock:/,"");if(!p)throw new Error(`${l}: mock (name or id) required`);let f=r.filter(k=>k.id===p);if(f.length||(f=r.filter(k=>k.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(k=>`mock:${k.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(k=>k.v===m)))throw new Error(`${l}: "${w.name}" has no captured v${c.v} to pin`);let g=o.findIndex(k=>k.mockId===w.id),y=g>=0?o.splice(g,1)[0].id:l_().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 I4(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>s_)throw new Error(`name over ${s_} chars`);return e.trim()}async function p_({trackId:e}={}){let t=await xr();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function $4(e){let n=(await xr()).flows.find(r=>r.id===e);return n?{...n}:null}async function f_(e){let t=await $4(e);if(!t)return null;let n=new Map((await Oc({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 m_({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=I4(t),i=await xr(),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 d_({trackId:e,steps:n}),d=Date.now(),p={id:l_().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 Ra(),await Jw(p,"created"),qd(p),{...p}}async function h_(e,{steps:t,note:n}={}){let o=(await xr()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await d_({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 Ra(),await Jw(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),qd(o),{...o}}async function w_(e){let t=await xr(),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 Ra(),await Jw(r,"deleted"),an("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function g_(e){return(await xr()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function y_(e){let n=(await xr()).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(),qd(r);return n.length&&await Ra(),n.length}async function b_(e){let t=await xr(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await Ra();for(let r of n)an("flows",r.id).catch(()=>{})}return n.length}async function k_(){if(!Ke())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 xr();for(let r of n.flows)await qd(r);return{pushed:n.flows.length,source:"plane"}}var _4,u_,o_,s_,a_,i_,kr,zw,qd,Yw=W(()=>{fe();Dr();$a();jr();_4=process.env.BOSTRAT_STATE_DIR||c_.join(process.env.HOME||E4.tmpdir(),".bostrat"),u_=c_.join(_4,"flows.json"),o_=20,s_=80,a_=500,i_=80,kr=null,zw=null;qd=e=>At("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import Ar from"node:fs/promises";import Tr from"node:path";import R4 from"node:os";import{randomUUID as O4}from"node:crypto";function Mc(e){return M4.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function Gn(){return vr||(Xw||=(async()=>{let e=!0;try{vr=JSON.parse(await Ar.readFile(v_,"utf8"))}catch{vr={runbooks:[]},e=!1}finally{Xw=null}return vr.runbooks||=[],!e&&Ke()&&await C4().catch(()=>{}),vr})(),Xw)}async function ps(){await Ot(v_,vr)}function Gd(e){return Cc(At("runbooks",{...e},{trackId:e.trackId}))}async function C4(){let{records:e,source:t}=await dt("runbooks");if(!(t!=="plane"||!e.length)){await Ar.mkdir(Kd,{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 Si("runbooks",r.id,o.v,"md");typeof s=="string"&&await Ar.writeFile(Sr(r,o.v),s).catch(()=>{})}vr.runbooks.push(r)}vr.runbooks.length&&await ps()}}function Oa(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 Sr(e,t){return Tr.join(Kd,`${e.id}.v${t}.md`)}function S_(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 A_(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>x_)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${x_/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Mc(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 N4(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function P4({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 T_({trackId:e,status:t}={}){let n=await Gn(),r=await P4({trackId:e,status:t});if(!Ke())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 Zw(e){let t=await D4(e);if(t)return t;if(!Ke())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 E_(e,t){let n=await Si("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function D4(e){let n=(await Gn()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function __({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=A_(r),c=await Gn(),l={id:O4().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 Ar.mkdir(Kd,{recursive:!0}),await Ar.writeFile(Sr(l,1),r),l.versions.push({v:1,mdRel:Tr.relative(ds,Sr(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await ps(),await Oa(l,"created",{version:1}),Gd(l),Cc(Br("runbooks",l.id,1,"md",Sr(l,1))),{...l}}async function I_(e,{md:t,note:n}={}){let o=(await Gn()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");N4(o,"update");let s=A_(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Ar.mkdir(Kd,{recursive:!0}),await Ar.writeFile(Sr(o,a),t),o.versions.push({v:a,mdRel:Tr.relative(ds,Sr(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await ps(),await Oa(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Gd(o),Cc(Br("runbooks",o.id,a,"md",Sr(o,a))),{...o}}async function $_(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 ps(),await Oa(r,t==="done"?"done":"reopened"),Gd(r),{...r})}async function R_(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 Ar.rm(Tr.join(ds,o.mdRel),{force:!0}).catch(()=>{});return await ps(),r.trackDeletedAt||await Oa(r,"deleted"),Cc(an("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function O_(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 Oa(r,"orphaned"),r.cwd=null,Gd(r);return n.length&&await ps(),n.length}async function M_(e){let t=await Gn(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await Oa(r,"deleted");for(let o of r.versions||[])await Ar.rm(Tr.join(ds,o.mdRel),{force:!0}).catch(()=>{});Cc(an("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await ps()),n.length}async function C_(){if(!Ke())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 Gn();for(let r of n.runbooks){await At("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await Br("runbooks",r.id,o.v,"md",Tr.join(ds,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var ds,v_,Kd,x_,M4,vr,Xw,Cc,Vd=W(()=>{Dr();fe();jr();ds=process.env.BOSTRAT_STATE_DIR||Tr.join(process.env.HOME||R4.tmpdir(),".bostrat"),v_=Tr.join(ds,"runbooks.json"),Kd=Tr.join(ds,"runbooks"),x_=256*1024,M4=[{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/}];vr=null,Xw=null;Cc=e=>Promise.resolve(e).catch(()=>{})});import Qw from"node:fs/promises";import Nc from"node:path";import{randomUUID as B4}from"node:crypto";function tg(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return j4.has(t)?"image":L4.has(t)?"doc":null}function F4(e){let r=Nc.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 D_(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>N_)throw new Error(`file too large \u2014 max ${Math.floor(N_/(1024*1024))} MB`);let r=F4(t),o=Nc.extname(r).slice(1),s=tg(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=B4().slice(0,8),i=eg(e);await Qw.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Qw.writeFile(Nc.join(i,c),n,{mode:384}),await Qw.chmod(Nc.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${P_}/${c}`,kind:s,size:n.length}}function B_(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function ng(e){return e.role==="user"&&U4.test((e.text||"").trim())}var P_,eg,N_,j4,L4,U4,rg=W(()=>{P_=".bostrat/uploads",eg=e=>Nc.join(e,P_),N_=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,j4=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),L4=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"]);U4=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function fs(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}function og(){let e=Number(process.env.BOSTRAT_LOOP_QUEUE_HOLD_MS);return Number.isFinite(e)&&e>0?e:H4}function q4(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 j_(e,t,{capMs:n=og(),now:r=Date.now()}={}){if(!e)return!1;let o=q4(e,t,r);return e.delivery?.producer!=="loop"||!W4.has(t)?!1:o>=n}var H4,W4,L_=W(()=>{H4=5*6e4,W4=new Set(["busy","noSession"])});import{execFile as K4}from"node:child_process";import{promisify as G4}from"node:util";function sg(e){return`refs/bostrat/wip/${e}`}function J4(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function H_(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function Y4(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:z4}function W_(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 X4(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 po(s,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await po(s,["remote","get-url","origin"])}catch{return F_.has(s)||(F_.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 ua(s,"bostrat: wip snapshot"),{stdout:c}=await po(s,["rev-parse",`${i}^{tree}`]),l=c.trim();if(!n&&zd.get(s)===l)return{pushed:!1,reason:"unchanged"};let u=sg(o);return await po(s,["push","--force","origin",`${i}:${u}`],U_),zd.set(s,l),await C(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:u,sha:i,wip:!0,...q_(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 ag(e,{log:t=console,force:n=!1}={}){let r=[],o=[];for(let s of W_(e)){let a=await X4(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 Z4(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 po(s,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await po(s,["remote","get-url","origin"])}catch{return{pushed:!1,reason:"no-origin"}}try{let i=await ua(s,"bostrat: transfer rescue snapshot"),{stdout:c}=await po(s,["rev-parse",`${i}^{tree}`]),l=c.trim();if(zd.get(s)===l)return{pushed:!1,reason:"unchanged"};let u=J4(o,r);return await po(s,["push","origin",`${i}:${u}`],U_),zd.set(s,l),await C(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:u,sha:i,rescue:!0,...q_(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(`
|
|
208
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function ig(e,{log:t=console,ts:n=Date.now()}={}){let r=[],o=[];for(let s of W_(e)){let a=await Z4(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 K_({listTracks:e,sweepMs:t=Y4(),log:n=console}){let r=!1,o=async()=>{if(!r){r=!0;try{let a=await e()??[];for(let i of a)je(i)||i?.kind==="setup"||await ag(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 V4,z4,U_,po,zd,F_,q_,cg=W(()=>{Vu();fe();Zt();V4=G4(K4),z4=12e4,U_=6e4;po=(e,t,n=15e3)=>V4("git",["-C",e,...t],{timeout:n,maxBuffer:4*1024*1024}),zd=new Map,F_=new Set,q_=e=>e?.name||e?.repo?{name:e.name??null,repo:e.repo??null}:{}});import Q4 from"node:fs";import lg from"node:fs/promises";import e2 from"node:path";function o2(e=process.env){return!!(Ce(e)&&e.BOSTRAT_PLANE_TOKEN)}async function G_(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=Ls(e),a=e2.join(s,`${o}.ndjson`);try{if(Q4.statSync(a).size>0)return{restored:!1,reason:"log not empty"}}catch{}let i=Ce(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=${t2}`,{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>n2)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 lg.mkdir(s,{recursive:!0});let d=`${a}.pull-${process.pid}-${Date.now()}`;return await lg.writeFile(d,l.join(`
|
|
209
209
|
`)+`
|
|
210
|
-
`),await
|
|
210
|
+
`),await lg.rename(d,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var t2,n2,r2,V_=W(()=>{yt();fe();t2=1e3,n2=5e5,r2=3e4});function z_(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 J_(e){let t=(e||[]).filter(n=>!s2.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 Y_({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,s2,X_=W(()=>{yn=(e,t,n,r={})=>({kind:e,source:t,since:n||0,...r});s2=new Set(["merged","closed","lost"])});function a2(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 i2(e,t,n){let r=a2(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 ug(e){if(Jd.has(e.id))return Jd.get(e.id);let t=await q(e).catch(()=>[]),n=[];try{n=Mn(t)}catch{}let r=Ru(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 Jd.set(e.id,p),!je(e)&&e.kind!=="setup"&&i2(e,t,p),p}function Z_({trackOf:e,debounceMs:t=c2,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(Pc.get(s));let a=setTimeout(()=>{Pc.delete(s),Promise.resolve(e(s)).then(i=>i&&!je(i)&&i.kind!=="setup"?ug(i):null).catch(i=>n.warn?.(`[obligations] fold ${s} failed: ${i?.message||i}`))},t);a.unref?.(),Pc.set(s,a)};return Xe.on("event",r),()=>{Xe.off("event",r);for(let o of Pc.values())clearTimeout(o);Pc.clear()}}var Jd,c2,Pc,dg=W(()=>{fe();Ou();Zt();Jd=new Map;Xe.on("event",e=>{e?.trackId&&Jd.delete(e.trackId)});c2=750,Pc=new Map});import{randomUUID as l2}from"node:crypto";async function Xd(e,t,n={}){let r={operationId:l2(),kind:t,startedAt:Date.now(),...n};return Yd.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 pg(e,t,n="completed",r=null){Yd.get(e.id)?.operationId===t?.operationId&&Yd.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 Yd,Q_,fg=W(()=>{fe();Yd=new Map;Q_=e=>Yd.get(e)||null});var MI={};xf(MI,{HANDOFF_LATEST_REL:()=>ho,KNOWN_AGENTS:()=>Vc,TRACK_NAME_MAX:()=>Tg,addTrackRepo:()=>fp,adoptTrack:()=>iy,agentAvailability:()=>cp,announceNodeUpdate:()=>zn,answerFullscreenOffer:()=>ey,answerPrompt:()=>up,archiveTrack:()=>gs,archiveTrackForMergedPr:()=>fr,augmentFirstPromptAfterHandoff:()=>lI,buildAgentCmd:()=>Eg,canResumeSubtask:()=>$I,cancelQueued:()=>Jc,cancelQueuedToDraft:()=>Ng,cancelStopJob:()=>Fa,cancelSubtask:()=>Ap,changedRepos:()=>ls,commitTransferOut:()=>xp,createSetupTrack:()=>Gg,createTrack:()=>hp,deleteTrack:()=>zg,demoteTransferredTracks:()=>ay,ensureLeadEpochs:()=>mo,forceMergeSubtask:()=>uy,getHandoffJob:()=>Ha,getReshapeJob:()=>Yc,getSubtaskJob:()=>vH,getTrack:()=>le,getTransferOutJob:()=>oy,healDeadLead:()=>Kc,interruptLead:()=>pp,isArchiving:()=>cH,isHandoffNoteEcho:()=>kg,isInterruptEcho:()=>xg,isModelSwitchEcho:()=>vg,leadCliDead:()=>Yg,listSubtaskJobs:()=>bs,listTracks:()=>we,matchTranscriptsToBoxes:()=>fI,mergeNotifyMessage:()=>TI,navigatePrompt:()=>Mg,notifyTrackForMergedPr:()=>mr,notifyTrackForResolvedPr:()=>wp,offerFollowupsForMergedPr:()=>ys,pendingOffers:()=>Rg,queueGauges:()=>Cg,queueShipCompleteNotify:()=>Dg,queuedMessages:()=>ms,rawTrack:()=>$g,reapArchivedOrphans:()=>Qg,reconcileDemotedTracks:()=>Zg,reconcileHandoffOutcome:()=>EI,reconcileInterruptedHandoffs:()=>ny,reconcileInterruptedReshapes:()=>qg,reconcileInterruptedSubtasks:()=>cy,reconcileNodeUpdateNotices:()=>Ig,reconcileSubtaskOutcome:()=>II,refreshStandingInstructions:()=>Kg,refuseIfTransferred:()=>kp,rehydrateLead:()=>Xc,removeTrackRepo:()=>mp,renameTrack:()=>Xg,reposOf:()=>Bt,reshapeTrack:()=>Wg,resolveTrackBox:()=>yc,resolveTrackBoxByHookToken:()=>zc,restartLead:()=>yp,resumeAgentCmd:()=>ip,sayToLead:()=>qt,sendOptionsForAgent:()=>On,setTrackDraft:()=>Lg,setTrackModel:()=>Fg,startLeadHandoff:()=>bp,startSpinUpView:()=>Bg,startSubtask:()=>ly,startTransferOut:()=>sy,stopJobActive:()=>wI,subtaskMessages:()=>Og,trackMessages:()=>lp,unarchiveTrack:()=>gp});import V from"node:fs/promises";import G from"node:path";import{randomUUID as op}from"node:crypto";import{execFile as u2}from"node:child_process";import{promisify as d2}from"node:util";function Eg(e,{skipPermissions:t=!0,model:n=null}={}){let r=ap[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=$h(e).find(i=>i.alias===n);if(!a)throw new Error(`unknown ${e} launch model "${n}"`);s+=` --model ${a.alias}`}return s}function ip(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=ap[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&&De(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 ws(e){let t=ap[e];if(t)return process.env[t.envVar]||t.base}async function cp({maxAgeMs:e=6e4}={}){if(fo.value&&Date.now()-fo.at<e)return fo.value;let t=fo.value,n=await Promise.all(Vc.map(async r=>{let o=await du(ws(r),{retries:[]});return o===Ks?[r,t?.[r]??!0]:[r,o===uu]}));return fo.value=Object.fromEntries(n),fo.at=Date.now(),fo.value}async function La(e,t=!0,n=null){let r=Eg(e,{skipPermissions:t,model:n});if(await du(r)===qs){fo.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.":Zf[o]?`Install it on this node: ${Zf[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 lI(e,t=ho){return`${e.trim()}
|
|
211
211
|
|
|
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 lg=lg.then(()=>J.mkdir(qn,{recursive:!0})).then(()=>J.appendFile(N4,n)).catch(()=>{}),lg}async function Kn({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 J.readFile(ug,"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)je(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 se().catch(()=>{}),s==="available")try{await J.mkdir(z.dirname(ug),{recursive:!0}),await J.writeFile(ug,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function vg({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)||F4(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 se().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function F4(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 Y_(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=>U4.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 X_(e,t,n,{kind:r}={}){let o=await ye(t);if(!o)throw new Error(`box "${t}" is not running`);let s=ms(o.agent),a=wc({text:n,producer:mc({kind:r,orchestration:!0}),kind:r||"orchestration",provider:o.agent}),i=de(await Z(),e)||null,c=Date.now(),l=null;try{l=await er({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s})}catch(g){throw yc(a,{ms:Date.now()-c,outcome:g?.code==="cli-not-running"?"cli-not-running":"blocked"}),await Dn(i,a,xa(g)),g}yc(a,{ms:Date.now()-c,outcome:l||"up"});let u=In(o.agent,n);a.paste=!!u.paste;try{await ot(t,n,{...u,kind:r||"orchestration"})}catch(g){throw await Dn(i,a,xa(g)),g}let d={},p=await Hi({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration",stats:d});a.verify=d,await Dn(i,a,p?d.verdict==="unverified"?"unverified":"delivered":"unsubmitted"),await ue({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 h=w.boxes.find(g=>g.name===t);Ae(w,"orchestration-prompt",{box:t,agent:h?.agent||o.agent,boxRole:h?.role,prompt:n,kind:r||"orchestration",deliveryId:a.id}),await se()}async function H4(e){let t=z.join(Bs(e),`${e.id}.ndjson`),n;try{n=await J.stat(t)}catch{return null}let r=F_.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await K(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return F_.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function W4(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Lm(n.name,n.agent)}async function Ut(e,t=null,n=null){let r=t??new Map((await sn()).map(b=>[b.name,b])),o=!!e.transferredTo,s=e.boxes.map(b=>{let I=o?null:r.get(b.name),_=vr.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([H4(e),W4(e,s),og(e),n??pn().catch(()=>[])]),d=Fc.has(e.id),p=(u||[]).filter(b=>b.trackId===e.id&&!["merged","closed","lost"].includes(b.state)),f=L_(e.id),w=f&&{status:"waiting",since:f.startedAt,summary:f.summary??null,count:f.count??null}||N_(p),h=s.find(b=>b.name===e.leadBox)?.state==="live",g=h?ps(e.id).filter(b=>b.kind!=="ship-notify"&&b.kind!=="prompt-answer"):[],y=h&&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=D_({archivedAt:e.archivedAt,archiving:d,prompted:c,openActions:l.openActions,openActionSince:l.openActionSince,prDecisions:P_(p),operation:w,phase:l.phase,working:i,workingSince:l.currentTurnAt,pending:y,completedTurnAt:l.completedTurnAt||a,live:h});return{...e,boxes:s,lastResponseAt:a,working:i,prompted:c,archiving:d,status:x}}function Z_(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-q4,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 Q_(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=b0(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=Z_(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 c0(p.box,p.sessionId),n=!0}}return n}async function we(){let e=await Z();await Q_(e)&&await se();let t=new Map((await sn()).map(r=>[r.name,r])),n=await pn().catch(()=>[]);return Promise.all(e.tracks.map(r=>Ut(r,t,n)))}async function hc(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 qc(e){let t=$o(e);if(!t)return null;let n=await Z();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Vb(o.hookTokenHash,t))return{track:r,box:o};return null}async function Sg(e){return de(await Z(),e)??null}async function ie(e){let t=await Z(),n=de(t,e);return n?(await Q_(t)&&await se(),Ut(n)):null}async function Ag(e){let t=await K(e).catch(()=>[]),n;try{n=$n(t)}catch{return[]}let r=Tu(n).map(s=>({kind:s.kind,id:s.offerId??s.proposalId??s.id,ts:s.ts??null})),o=[];try{o=Nx(t)}catch{o=[]}return r.concat(o.map(s=>({kind:"pr-open",id:`${s.repo??""}#${s.number}`,ts:null})))}function mg(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(K4[e.agent]||[]).some(n=>n.test(t))}function wg(e){let t=(e.text||"").trim();return t?G4.some(n=>n.test(t)):!1}async function np(e,t={}){let n=await ie(e);if(!n)return null;let r=new Set([...n.events.filter($=>$.type==="orchestration-prompt"&&$.prompt).map($=>String($.prompt).trim()),...n.events.filter($=>$.type==="said"&&$.text).map($=>String($.text).trim())]),o=La(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=_w(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...wE(n),...l],p={windows:[...rv(n),...ov(n,$u),...a,...u]},w=(await Promise.all(n.boxes.map($=>Yh($,t)))).flat(),h=w.map($=>{let Y={kind:"message",...$};if(Y.role==="assistant"&&(Y.text||"").trim()&&Y.tools?.length){let Ce=Vr(Y.agent).cliTools,Ve=Y.tools.filter(xt=>!Ce.has(xt));return{...Y,tools:Ve.length?Ve:void 0}}return Y}).map($=>{if($.role!=="user")return $;let Y=vx($);return Y?Y.kind==="suppress"?null:{...$,note:Y,text:Ax(Y)}:$}).filter(Boolean).filter($=>!($.role==="user"&&r.has(($.text||"").trim()))).filter($=>!mg($)).filter($=>!wg($)).filter($=>!hg($)).filter($=>!Xw($)).filter($=>$.role!=="assistant"||!!($.text||"").trim()||!!$.images?.length).filter($=>!sv($,p)),g=h.filter($=>$.failed&&$.text===Lo);if(g.length){let $=new Map;for(let Y of g){if(!$.has(Y.box)){let xt=[...(await Pe(Y.box,{lines:Ye}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Er=>Er[1]);$.set(Y.box,xt.length?xt[xt.length-1]:null)}let Ce=$.get(Y.box);Ce&&(Y.text=`\u25A0 ${Ce}`)}}let y=await Promise.all(n.boxes.map($=>Hv($,t)));await qT(n,y.flat());let{agentTurnBoxes:x}=await HT(n,h);for(let $ of n.boxes)x.has($.name)&&$.cwd&&zl($.cwd);await NS(n),await vE(n,h).catch(()=>{});for(let $ of n.boxes)$.agent==="codex"&&$.state==="live"&&await GT(n,$),$.agent==="cursor"&&$.state==="live"&&await XT(n,$);let b=await Promise.all(n.boxes.map($=>Uv($,t)));await WT(n,b.flat().filter($=>!dg($.box,$.ts,u)));let I=await K(n),_=c&&i.viewId?i.viewId:null,N=zS(I),U=$n(I).filter($=>!($.kind==="message"&&(mg($)||wg($)||hg($)||Xw($)))&&!($.kind==="view-card"&&_&&$.viewId===_)),j=VS(U,N),T=U.filter($=>!($.kind==="message"&&$.role==="assistant"&&!j.has($)&&dg($.box,$.ts,N))),R=Ch(I,T).map($=>({kind:"command-batch",id:$.id,ts:$.pending?$.ts:($.anchorTs??$.ts)-1,anchorTs:$.anchorTs,anchorBox:$.anchorBox,agent:$.agent,boxRole:$.boxRole,commands:$.commands,pending:$.pending})).filter($=>!dg($.anchorBox,$.anchorTs??$.ts,u)),L=Mx(Cx([...T,...R].filter($=>$.ts!=null).sort(($,Y)=>$.ts-Y.ts))),V=V4(n),O=V.size?L.filter($=>$.kind==="event"||!V.has($.box??$.anchorBox)):L,S=n.boxes.find($=>$.name===n.leadBox)||null,M=S?await Ri(S.cwd,S.sessionId,{fallbackNewest:!1}):null;if(!M&&S){let $=[...I].reverse().find(Y=>Y.type==="context.usage"&&Y.boxId===S.name);$?.payload?.window&&(M={pct:$.payload.pct??($.payload.occupancy!=null?$.payload.occupancy/$.payload.window:null),occupancy:$.payload.occupancy??null,window:$.payload.window,windowKnown:!0,model:$.payload.model||null})}let H=[...w].reverse().find($=>$.role==="assistant"&&$.model),W=H?.model||null,te=[...n.events||[]].reverse().find($=>$.type==="model-change"),pe=M?.model||W;te&&(!H?.ts||te.at>=H.ts)&&(pe=te.to);let Ie=S?await Pe(S.name,{lines:Ye}).catch(()=>""):"",Oe=S&&Xr(S.agent,Ie)?await an(S.name).catch(()=>Ie):Ie,re=S?On(S,Oe):null;if(re&&S){let $=ju(S,re,Oe);if($){let Y=rS(S.name,re.promptId);Y.action!=="give-up"&&(Y.action==="send"&&(We(S.name,$.keys,{kind:"auto-reject"}).catch(()=>{}),Y.first&&C(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:S.name,provider:S.agent,payload:{ts:Date.now(),promptId:re.promptId,rule:$.id}}).catch(()=>{})),re=null)}}!re&&S&&!Xs(S.agent,Ie)&&!ps(n.id).length&&(re=Rh(O,{leadBoxId:S.name,provider:S.agent,isAnswered:$=>I.some(Y=>Y.type==="prompt.answered"&&Y.payload?.promptId===$)})),re&&PS(n,S,re);let Ne=O.filter($=>!($.kind==="message"&&$.role==="assistant"&&$.ask&&!String($.text||"").trim()&&!$.images?.length)),G=!!S&&dd(S.name,S.agent,Ie)&&!re;S&&!S.readyAt&&(G||re||Ie&&Uo(S.agent,Ie))&&(S.readyAt=Date.now(),await se());let fe=!!S&&!S.readyAt&&Date.now()-(n.createdAt||0)<z4;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&S&&Uo(S.agent,Ie)&&!re){let $=await Z(),Y=de($,n.id);Y?.setup&&!Y.setup.connectedAt&&(Y.setup.connectedAt=Date.now(),n.setup.connectedAt=Y.setup.connectedAt,await C(Y,{type:"provider.connected",actor:"system",source:"pane",boxId:S.name,provider:S.agent,payload:{provider:S.agent,ts:Y.setup.connectedAt}}).catch(()=>{}),await se())}let be=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,xe=Gc(n.id),Ke=xe&&!xe.finishedAt?{running:!0,step:xe.step||null,added:xe.added||[],removed:xe.removed||[]}:null,le=null;if(c){let $=Date.now()-i.startedAt;if(i.viewId&&!G){let Y=Od(n.id);Y&&(Ae(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:Y.finishedAt}),await se())}else if(re||$>hE&&!G&&!i.viewId||$>Ew){let Y=re?"needs-input":$>Ew?"timeout":"no-view";Od(n.id,{error:Y})&&await C(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:S?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:Y}}).catch(()=>{})}else le={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:Ne,context:M?{pct:M.pct,occupancy:M.occupancy,window:M.window,windowKnown:M.windowKnown,model:M.model}:null,models:{current:pe,options:xm(S?.agent)},pendingPrompt:re,working:G,stopping:tI(n.id),leadStarting:fe,handoff:be,reshape:Ke,view:le,queued:ps(n.id).filter($=>$.kind!=="ship-notify"&&$.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function V4(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 Tg(e,t){let n=await ie(e);if(!n)return null;let r=ys(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 K(n),a=$n(s).filter(d=>d.box===o&&!(d.kind==="message"&&d.role==="assistant"&&d.ask&&!String(d.text||"").trim()&&!d.images?.length)),i=Ch(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 Pe(o,{lines:Ye}).catch(()=>"");u=dd(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}function J4(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>U_)throw new Error(`custom answer is too long (max ${U_} characters)`);return r})}function Y4(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 rp(e,t={}){let n=Mc.get(e);n&&await n.catch(()=>{});let r=X4(e,t);Mc.set(e,r);try{return await r}finally{Mc.get(e)===r&&Mc.delete(e)}}async function X4(e,{promptId:t,answers:n,text:r,custom:o}={}){let s=de(await Z(),e);if(!s)throw new Error(`track "${e}" not found`);Ba(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 ye(s.leadBox))throw new Error(`lead box "${s.leadBox}" is not running`);let c=await om(a);if(c||(c=await Z4(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=J4(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 ot(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 We(s.leadBox,["Escape"],{kind:"answer-prompt"}),d={mode:"keys",value:"Escape",count:1};else if(c.answerMode==="say"){let y=Ix(c,n||[],typeof r=="string"?r:"",l);if(!y)throw new Error("no answer selected \u2014 pick an option or reply in chat");await Ht(s.id,y,{kind:"prompt-answer"}),d={mode:"say",value:y,count:1}}else if(l[0]&&c.provider==="claude"){let y=Qv(c,n||[],l[0]);await Vf(s.leadBox,[...y.pre,{text:y.text}],{kind:"answer-prompt"});let x=!1;for(let I of[150,300,600]){await Sr(I);let _=await an(s.leadBox).catch(()=>""),N=On(a,_);if(N?.customText&&Y4(N.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 Vf(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=sm(c,n||[]);await We(s.leadBox,y,{kind:"answer-prompt"}),d={mode:"keys",value:y.join(" "),count:y.length}}else{let y=z0(c,n||[]);await ot(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(U=>b.options?.[U]).filter(Boolean).map(U=>({label:U.label,description:U.description||""}));l[x]&&_.push({label:l[x],description:"",custom:!0});let N=(b.options||[]).map((U,j)=>I.has(j)?null:U?.label).filter(Boolean);vm(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:N})}return await ue({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 h=(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:h,...g?{control:g}:{}},await ue({flow:"prompt",phase:"answered",sessionName:s.leadBox,agent:a.agent,provider:c.provider,promptId:c.promptId,...d}).catch(()=>{}),vm(s,a,c,d),{ok:!0,promptId:c.promptId,sent:d.count}}async function Z4(e,t){if(!t)return null;let n=await K(e).catch(()=>[]);return Rh($n(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function Eg(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 ye(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await om(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=eS(a,n);return c.length?(await We(r.leadBox,c,{kind:"navigate-prompt"}),await ue({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 op(e){let t=Ca.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Ca.set(e,t)),t}function ps(e){return(Ca.get(e)?.items??[]).map(({delivery:t,...n})=>({...n}))}function _g(e=Date.now()){let t=0,n=0;for(let r of Ca.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 e2(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 Dn(o,s.delivery,`dropped:${n}`)})().catch(()=>{})}function Kc(e,t="cancel"){let n=Ca.get(e);if(!n)return[];let r=n.items.splice(0);return n.idleSince=null,n.timer&&(clearInterval(n.timer),n.timer=null),e2(e,r,t),r}async function Ig(e){let t=Kc(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=Na(o,n),await se(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function $g(e){let t=op(e);t.timer||(t.timer=setInterval(()=>t2(e).catch(()=>{}),Q4))}async function t2(e){let t=Ca.get(e);if(!(!t||t.flushing)){if(!t.items.length){t.timer&&(clearInterval(t.timer),t.timer=null);return}t.flushing=!0;try{let n=de(await Z(),e);if(!n||n.archivedAt){Kc(e,"archived");return}let r=l=>{for(let u of t.items)sE(u.delivery,l)},o=La(e);if(o&&!o.finishedAt&&o.step!=="done"&&!o.error){ds(t,!1),r("handoff");return}let s=await ye(n.leadBox),a=n.boxes.find(l=>l.name===n.leadBox);if(s&&await qg(s)&&(s=await Uc(n,a,"queue")?await ye(n.leadBox):null,await se()),!s){ds(t,!1),r("noSession");return}if(Zr(a?.agent)){ds(t,!1),r("cap");return}let i=await Pe(n.leadBox,{lines:Ye}).catch(()=>"");if(Di(a?.agent,i)){ds(t,!1),r("busy");return}if(Xr(a?.agent,i)){let l=await an(n.leadBox).catch(()=>i);if(On(a,l)){ds(t,!1),r("prompt");return}}if(!ds(t,!0)){r("grace");return}let c=t.items.shift();t.idleSince=null,c.delivery&&(c.delivery.dequeuedAt=Date.now());try{await gg(n,a,s,c.text,c.kind??null,c.attachments??null,c.delivery??null)}catch(l){if(l?.code!=="cli-not-running")throw c.delivery&&await Dn(n,c.delivery,xa(l)),l;t.items.unshift(c),c.delivery&&(c.delivery.requeued+=1,c.delivery.dequeuedAt=null,await gc(n,c.delivery,{position:1,reason:"requeue"})),await Uc(n,a,"queue-gate")}await se()}finally{t.flushing=!1}}}async function eI(e,t,n,r){let o=Zr(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(Na(e,[n]),await se()),yS(e.id,o))try{await C(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 n2(e,t){let n=await Pe(e.leadBox,{lines:Ye}).catch(()=>"");if(!Xr(t?.agent,n))return!1;let r=await an(e.leadBox).catch(()=>n);return!!On(t,r)}async function gg(e,t,n,r,o=null,s=null,a=null){a||(a=wc({text:r,producer:mc({kind:o}),kind:o,provider:t?.agent??null,attachments:Array.isArray(s)?s.length:0})),a.provider??=t?.agent??null,await eI(e,t,r,o);let i=Bc.get(e.id)||0,c=r,l=!1,u=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(c=J_(r,e.pendingHandoffHydrate.artifact||ho),delete e.pendingHandoffHydrate,l=!0);let d=Array.isArray(s)?s.filter(h=>h&&h.rel):[];if(d.length){let h=A_(d.map(g=>g.rel));c=c.trim()?`${c.trim()}
|
|
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 kg(e){return e.role==="user"&&m2.test((e.text||"").trim())}async function ee(){return Pt||(mg||=(async()=>{try{Pt=JSON.parse(await V.readFile(cI,"utf8"))}catch{Pt={tracks:[]}}finally{mg=null}return Pt})(),mg)}async function se(){await Ot(cI,Pt)}function g2(e,t){let n=JSON.stringify({trackId:e,...t})+`
|
|
213
|
+
`;return hg=hg.then(()=>V.mkdir(Vn,{recursive:!0})).then(()=>V.appendFile(p2,n)).catch(()=>{}),hg}async function uI(e,{box:t,agent:n,message:r}){let o=ue(await ee(),e);!o||o.archivedAt||(Se(o,"startup-gate",{box:t,agent:n??null,message:r}),await se(),Xi(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(wg,"utf8"))}catch{}if(d?.latest===t&&d?.current===e)return{announced:0,deduped:!0}}let l=await ee(),u=0;for(let d of l.tracks)je(d)||!d.leadBox||(Se(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 se().catch(()=>{}),s==="available")try{await V.mkdir(G.dirname(wg),{recursive:!0}),await V.writeFile(wg,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function Ig({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await ee(),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)||y2(e,c)||(Se(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 se().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function y2(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 dI(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=>b2.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 pI(e,t,n,{kind:r}={}){let o=await ke(t);if(!o)throw new Error(`box "${t}" is not running`);let s=ws(o.agent),a=kc({text:n,producer:bc({kind:r,orchestration:!0}),kind:r||"orchestration",provider:o.agent}),i=ue(await ee(),e)||null,c=Date.now(),l=null;try{l=await rr({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s})}catch(g){throw vc(a,{ms:Date.now()-c,outcome:g?.code==="cli-not-running"?"cli-not-running":"blocked"}),await wn(i,a,Aa(g)),g}vc(a,{ms:Date.now()-c,outcome:l||"up"});let u=On(o.agent,n);a.paste=!!u.paste;try{await it(t,n,{...u,kind:r||"orchestration"})}catch(g){throw await wn(i,a,Aa(g)),g}let d={},p=await Ki({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 de({flow:"orchestration",phase:"sent",submitted:p,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let f=await ee(),w=ue(f,e);if(!w)return;let m=w.boxes.find(g=>g.name===t);Se(w,"orchestration-prompt",{box:t,agent:m?.agent||o.agent,boxRole:m?.role,prompt:n,kind:r||"orchestration",deliveryId:a.id}),await se()}async function k2(e){let t=G.join(Ls(e),`${e.id}.ndjson`),n;try{n=await V.stat(t)}catch{return null}let r=eI.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 eI.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function x2(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Vh(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),R=Er.has(b.state)?b.state:I?b.role==="lead"?"live":b.state:"stopped";return{...b,state:R,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([k2(e),x2(e,s),ug(e),n??mn().catch(()=>[])]),d=qc.has(e.id),p=(u||[]).filter(b=>b.trackId===e.id&&!["merged","closed","lost"].includes(b.state)),f=Q_(e.id),w=f&&{status:"waiting",since:f.startedAt,summary:f.summary??null,count:f.count??null}||z_(p),m=s.find(b=>b.name===e.leadBox)?.state==="live",g=m?ms(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,k=Y_({archivedAt:e.archivedAt,archiving:d,prompted:c,openActions:l.openActions,openActionSince:l.openActionSince,prDecisions:J_(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:k}}function fI(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-v2,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 mI(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=I0(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=fI(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 g0(p.box,p.sessionId),n=!0}}return n}async function we(){let e=await ee();await mI(e)&&await se();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 yc(e,t){let n=await ee();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 zc(e){let t=Ro(e);if(!t)return null;let n=await ee();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&ek(o.hookTokenHash,t))return{track:r,box:o};return null}async function $g(e){return ue(await ee(),e)??null}async function le(e){let t=await ee(),n=ue(t,e);return n?(await mI(t)&&await se(),Wt(n)):null}async function Rg(e){let t=await q(e).catch(()=>[]),n;try{n=Mn(t)}catch{return[]}let r=Ru(n).map(s=>({kind:s.kind,id:s.offerId??s.proposalId??s.id,ts:s.ts??null})),o=[];try{o=Wx(t)}catch{o=[]}return r.concat(o.map(s=>({kind:"pr-open",id:`${s.repo??""}#${s.number}`,ts:null})))}function xg(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(S2[e.agent]||[]).some(n=>n.test(t))}function vg(e){let t=(e.text||"").trim();return t?A2.some(n=>n.test(t)):!1}async function lp(e,t={}){let n=await le(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=Ha(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=Mw(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...$E(n),...l],p={windows:[...pv(n),...fv(n,Nu),...a,...u]},w=(await Promise.all(n.boxes.map(O=>oh(O,t)))).flat(),m=w.map(O=>{let Y={kind:"message",...O};if(Y.role==="assistant"&&(Y.text||"").trim()&&Y.tools?.length){let Le=Yr(Y.agent).cliTools,Qe=Y.tools.filter(oe=>!Le.has(oe));return{...Y,tools:Qe.length?Qe:void 0}}return Y}).map(O=>{if(O.role!=="user")return O;let Y=Ox(O);return Y?Y.kind==="suppress"?null:{...O,note:Y,text:Cx(Y)}:O}).filter(Boolean).filter(O=>!(O.role==="user"&&r.has((O.text||"").trim()))).filter(O=>!xg(O)).filter(O=>!vg(O)).filter(O=>!kg(O)).filter(O=>!ng(O)).filter(O=>O.role!=="assistant"||!!(O.text||"").trim()||!!O.images?.length).filter(O=>!mv(O,p)),g=m.filter(O=>O.failed&&O.text===Fo);if(g.length){let O=new Map;for(let Y of g){if(!O.has(Y.box)){let oe=[...(await Pe(Y.box,{lines:et}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(ye=>ye[1]);O.set(Y.box,oe.length?oe[oe.length-1]:null)}let Le=O.get(Y.box);Le&&(Y.text=`\u25A0 ${Le}`)}}let y=await Promise.all(n.boxes.map(O=>eS(O,t)));await rE(n,y.flat());let{agentTurnBoxes:k}=await tE(n,m);for(let O of n.boxes)k.has(O.name)&&O.cwd&&Zl(O.cwd);await GS(n),await PE(n,m).catch(()=>{});for(let O of n.boxes)O.agent==="codex"&&O.state==="live"&&await sE(n,O),O.agent==="cursor"&&O.state==="live"&&await uE(n,O);let b=await Promise.all(n.boxes.map(O=>Qv(O,t)));await nE(n,b.flat().filter(O=>!gg(O.box,O.ts,u)));let I=await q(n),R=c&&i.viewId?i.viewId:null,N=sA(I),D=Mn(I).filter(O=>!(O.kind==="message"&&(xg(O)||vg(O)||kg(O)||ng(O)))&&!(O.kind==="view-card"&&R&&O.viewId===R)),j=aA(D,N),T=D.filter(O=>!(O.kind==="message"&&O.role==="assistant"&&!j.has(O)&&gg(O.box,O.ts,N))),$=Bm(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=>!gg(O.anchorBox,O.anchorTs??O.ts,u)),F=Ux(Hx([...T,...$].filter(O=>O.ts!=null).sort((O,Y)=>O.ts-Y.ts))),J=E2(n),M=J.size?F.filter(O=>O.kind==="event"||!J.has(O.box??O.anchorBox)):F,S=n.boxes.find(O=>O.name===n.leadBox)||null,_=S?await Ci(S.cwd,S.sessionId,{fallbackNewest:!1}):null;if(!_&&S){let O=[...I].reverse().find(Y=>Y.type==="context.usage"&&Y.boxId===S.name);O?.payload?.window&&(_={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 H=[...w].reverse().find(O=>O.role==="assistant"&&O.model),K=H?.model||null,X=[...n.events||[]].reverse().find(O=>O.type==="model-change"),pe=_?.model||K;X&&(!H?.ts||X.at>=H.ts)&&(pe=X.to);let Oe=S?await Pe(S.name,{lines:et}).catch(()=>""):"",Me=S&&Qr(S.agent,Oe)?await ln(S.name).catch(()=>Oe):Oe,Q=S?Nn(S,Me):null;if(Q&&S){let O=Gu(S,Q,Me);if(O){let Y=hS(S.name,Q.promptId);Y.action!=="give-up"&&(Y.action==="send"&&(ze(S.name,O.keys,{kind:"auto-reject"}).catch(()=>{}),Y.first&&C(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:S.name,provider:S.agent,payload:{ts:Date.now(),promptId:Q.promptId,rule:O.id}}).catch(()=>{})),Q=null)}}!Q&&S&&!Qs(S.agent,Oe)&&!ms(n.id).length&&(Q=Nm(M,{leadBoxId:S.name,provider:S.agent,isAnswered:O=>I.some(Y=>Y.type==="prompt.answered"&&Y.payload?.promptId===O)})),Q&&VS(n,S,Q);let qe=M.filter(O=>!(O.kind==="message"&&O.role==="assistant"&&O.ask&&!String(O.text||"").trim()&&!O.images?.length)),Z=!!S&&bd(S.name,S.agent,Oe)&&!Q;S&&!S.readyAt&&(Z||Q||Oe&&Ho(S.agent,Oe))&&(S.readyAt=Date.now(),await se());let he=!!S&&!S.readyAt&&Date.now()-(n.createdAt||0)<T2;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&S&&Ho(S.agent,Oe)&&!Q){let O=await ee(),Y=ue(O,n.id);Y?.setup&&!Y.setup.connectedAt&&(Y.setup.connectedAt=Date.now(),n.setup.connectedAt=Y.setup.connectedAt,await C(Y,{type:"provider.connected",actor:"system",source:"pane",boxId:S.name,provider:S.agent,payload:{provider:S.agent,ts:Y.setup.connectedAt}}).catch(()=>{}),await se())}let Te=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,z=Yc(n.id),ge=z&&!z.finishedAt?{running:!0,step:z.step||null,added:z.added||[],removed:z.removed||[]}:null,Ge=null;if(c){let O=Date.now()-i.startedAt;if(i.viewId&&!Z){let Y=jd(n.id);Y&&(Se(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:Y.finishedAt}),await se())}else if(Q||O>_E&&!Z&&!i.viewId||O>Ow){let Y=Q?"needs-input":O>Ow?"timeout":"no-view";jd(n.id,{error:Y})&&await C(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:S?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:Y}}).catch(()=>{})}else Ge={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:qe,context:_?{pct:_.pct,occupancy:_.occupancy,window:_.window,windowKnown:_.windowKnown,model:_.model}:null,models:{current:pe,options:$h(S?.agent)},pendingPrompt:Q,working:Z,stopping:wI(n.id),leadStarting:he,handoff:Te,reshape:ge,view:Ge,queued:ms(n.id).filter(O=>O.kind!=="ship-notify"&&O.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function E2(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 Og(e,t){let n=await le(e);if(!n)return null;let r=bs(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=Bm(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 Pe(o,{lines:et}).catch(()=>"");u=bd(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}function _2(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>tI)throw new Error(`custom answer is too long (max ${tI} characters)`);return r})}function I2(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 up(e,t={}){let n=Dc.get(e);n&&await n.catch(()=>{});let r=$2(e,t);Dc.set(e,r);try{return await r}finally{Dc.get(e)===r&&Dc.delete(e)}}async function $2(e,{promptId:t,answers:n,text:r,custom:o}={}){let s=ue(await ee(),e);if(!s)throw new Error(`track "${e}" not found`);Fa(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 ke(s.leadBox))throw new Error(`lead box "${s.leadBox}" is not running`);let c=await ph(a);if(c||(c=await R2(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=_2(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,k)=>l[k]&&!y.multiSelect?[]:Array.isArray(n?.[k])?n[k]:[]));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 it(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 ze(s.leadBox,["Escape"],{kind:"answer-prompt"}),d={mode:"keys",value:"Escape",count:1};else if(c.answerMode==="say"){let y=Bx(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=dS(c,n||[],l[0]);await Qf(s.leadBox,[...y.pre,{text:y.text}],{kind:"answer-prompt"});let k=!1;for(let I of[150,300,600]){await _r(I);let R=await ln(s.leadBox).catch(()=>""),N=Nn(a,R);if(N?.customText&&I2(N.customText,y.text)){k=!0;break}}if(!k)throw new Error("custom answer didn't take \u2014 the widget moved; reload and answer again");await Qf(s.leadBox,y.post,{kind:"answer-prompt"});let b=I=>I.map(R=>R.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=fh(c,n||[]);await ze(s.leadBox,y,{kind:"answer-prompt"}),d={mode:"keys",value:y.join(" "),count:y.length}}else{let y=nx(c,n||[]);await it(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 k=0;k<y.length;k++){let b=y[k],I=new Set(Array.isArray(n?.[k])?n[k]:[]),R=[...I].map(D=>b.options?.[D]).filter(Boolean).map(D=>({label:D.label,description:D.description||""}));l[k]&&R.push({label:l[k],description:"",custom:!0});let N=(b.options||[]).map((D,j)=>I.has(j)?null:D?.label).filter(Boolean);Rh(s,a,c,{...d,groupId:c.groupId||c.promptId,questionIndex:k,questionTotal:c.questionTotal??y.length,header:b.header||"",question:b.question||"",multiSelect:!!b.multiSelect,chosen:R,rejected:N})}return await de({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,k)=>f.has(k)?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 de({flow:"prompt",phase:"answered",sessionName:s.leadBox,agent:a.agent,provider:c.provider,promptId:c.promptId,...d}).catch(()=>{}),Rh(s,a,c,d),{ok:!0,promptId:c.promptId,sent:d.count}}async function R2(e,t){if(!t)return null;let n=await q(e).catch(()=>[]);return Nm(Mn(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function Mg(e,{promptId:t,questionIndex:n}={}){let r=ue(await ee(),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 ke(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await ph(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=pS(a,n);return c.length?(await ze(r.leadBox,c,{kind:"navigate-prompt"}),await de({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 dp(e){let t=Da.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Da.set(e,t)),t}function ms(e){return(Da.get(e)?.items??[]).map(({delivery:t,...n})=>({...n}))}function Cg(e=Date.now()){let t=0,n=0;for(let r of Da.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 M2(e,t,n){let r=t.filter(o=>o.delivery&&!o.delivery.settled);r.length&&(async()=>{let o=ue(await ee(),e)||null;for(let s of r)await wn(o,s.delivery,`dropped:${n}`)})().catch(()=>{})}function Jc(e,t="cancel"){let n=Da.get(e);if(!n)return[];let r=n.items.splice(0);return n.idleSince=null,n.timer&&(clearInterval(n.timer),n.timer=null),M2(e,r,t),r}async function Ng(e){let t=Jc(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=ue(await ee(),e);if(o)return r=Ba(o,n),await se(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function Pg(e){let t=dp(e);t.timer||(t.timer=setInterval(()=>C2(e).catch(()=>{}),O2))}async function C2(e){let t=Da.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=ue(await ee(),e);if(!n||n.archivedAt){Jc(e,"archived");return}let r=async(l,u="")=>{let d=[];for(let g of t.items)yE(g.delivery,l),j_(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(og()/6e4)),w=String(u||"").split(`
|
|
214
|
+
`).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 wn(n,g.delivery,l==="noSession"?"failed:not-running":"failed:blocked");Se(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 se(),Xi(n,{provider:o?.agent??null,cls:"delivery-held",note:m}).catch(()=>{})},o=n.boxes.find(l=>l.name===n.leadBox),s=Ha(e);if(s&&!s.finishedAt&&s.step!=="done"&&!s.error){fs(t,!1),await r("handoff");return}let a=await ke(n.leadBox);if(a&&await Yg(a)&&(a=await Kc(n,o,"queue")?await ke(n.leadBox):null,await se()),!a){fs(t,!1),await r("noSession");return}if(eo(o?.agent)){fs(t,!1),await r("cap");return}let i=await Pe(n.leadBox,{lines:et}).catch(()=>"");if(Li(o?.agent,i)){fs(t,!1),await r("busy",i);return}if(Qr(o?.agent,i)){let l=await ln(n.leadBox).catch(()=>i);if(Nn(o,l)){fs(t,!1),await r("prompt");return}}if(!fs(t,!0)){await r("grace");return}let c=t.items.shift();t.idleSince=null,c.delivery&&(c.delivery.dequeuedAt=Date.now());try{await Sg(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 wn(n,c.delivery,Aa(l)),l;t.items.unshift(c),c.delivery&&(c.delivery.requeued+=1,c.delivery.dequeuedAt=null,await xc(n,c.delivery,{position:1,reason:"requeue"})),await Kc(n,o,"queue-gate")}await se()}finally{t.flushing=!1}}}async function hI(e,t,n,r){let o=eo(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(Ba(e,[n]),await se()),RS(e.id,o))try{await C(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 N2(e,t){let n=await Pe(e.leadBox,{lines:et}).catch(()=>"");if(!Qr(t?.agent,n))return!1;let r=await ln(e.leadBox).catch(()=>n);return!!Nn(t,r)}async function Sg(e,t,n,r,o=null,s=null,a=null){a||(a=kc({text:r,producer:bc({kind:o}),kind:o,provider:t?.agent??null,attachments:Array.isArray(s)?s.length:0})),a.provider??=t?.agent??null,await hI(e,t,r,o);let i=Uc.get(e.id)||0,c=r,l=!1,u=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(c=lI(r,e.pendingHandoffHydrate.artifact||ho),delete e.pendingHandoffHydrate,l=!0);let d=Array.isArray(s)?s.filter(m=>m&&m.rel):[];if(d.length){let m=B_(d.map(g=>g.rel));c=c.trim()?`${c.trim()}
|
|
214
216
|
|
|
215
|
-
${
|
|
217
|
+
${m}`:m}let p=Se(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,deliveryId:a.id,...o?{kind:o}:{},...d.length?{attachments:d}:{}});o||Pa(e,null);try{let m=Date.now(),g=null;try{g=await rr({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:ws(n.agent),bailOnDialog:!0})}catch(k){throw vc(a,{ms:Date.now()-m,outcome:k?.code==="cli-not-running"?"cli-not-running":"blocked"}),k}if(vc(a,{ms:Date.now()-m,outcome:g||"up"}),(Uc.get(e.id)||0)!==i){!o&&r.trim()&&(Ba(e,[r]),await se());let k=new Error("delivery stopped \u2014 the message was handed back as the draft");throw k.interrupted=!0,k}if(await N2(e,t)){let k=dp(e.id);a.requeued+=1,a.dequeuedAt=null;let b={id:a.id,text:r,ts:Date.now(),...o?{kind:o}:{},...d.length?{attachments:d}:{},delivery:a};k.items.unshift(b),Pg(e.id),await xc(e,a,{position:1,reason:"prompt"});let I=new Error("lead is blocked on a prompt \u2014 message queued");throw I.requeued={id:b.id,position:k.items.length},I}let y=On(t?.agent,c);a.paste=!!y.paste,await it(e.leadBox,c,{...y,kind:l?"lead-handoff-first-prompt":"say"})}catch(m){if(u&&!e.pendingHandoffHydrate&&(e.pendingHandoffHydrate=u),Se(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 wn(e,a,Aa(m)),m}let f={},w=await Y2(e,t,c,f);a.verify=f,w?await wn(e,a,f.verdict==="unverified"?"unverified":"delivered"):(await gI(e.leadBox,t?.agent,c.length+8,c).catch(()=>{}),Se(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:p.at,text:r,reason:"unsubmitted"}),o||Ba(e,[r]),await wn(e,a,"unsubmitted")),l&&await de({flow:"lead-handoff",phase:"first-prompt",trackId:e.id,sessionName:e.leadBox,agent:t?.agent,userTextLen:r.length})}function Dg(e,{actions:t}={}){let n=dp(e),r=iA(t),o=kc({text:r,producer:bc({kind:"ship-notify"}),kind:"ship-notify"});n.items.push({id:o.id,text:r,ts:Date.now(),kind:"ship-notify",delivery:o}),Pg(e),(async()=>{let s=ue(await ee(),e);s&&(o.provider=s.boxes.find(a=>a.name===s.leadBox)?.agent??null,await xc(s,o,{position:n.items.length,reason:"queue"}))})().catch(()=>{})}async function P2(e,t,n,r){await ze(e.leadBox,["Escape"],{kind:"dismiss-prompt"});let o="",s=!1;for(let c of[250,400,600,900]){if(await _r(c),o=await Pe(e.leadBox,{lines:et}).catch(()=>""),!Qr(t?.agent,o)){s=!0;break}let l=await ln(e.leadBox).catch(()=>o);if(!Nn(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 de({flow:"prompt",phase:"dismissed",sessionName:e.leadBox,agent:t?.agent,provider:n.provider,promptId:n.promptId,...i}).catch(()=>{}),s&&zS(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"||!ca(t))&&!a.length)throw new Error("text (string) or an attachment required");let i=ue(await ee(),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`);kp(i);let c=i.boxes.find(d=>d.name===i.leadBox),l=ca(t),u=kc({text:l,producer:bc({kind:n,producer:o}),kind:n,provider:c?.agent??null,attachments:a.length});try{return await D2(e,i,c,l,{kind:n,atts:a,d:u,dismissPrompt:s})}catch(d){throw await wn(i,u,Aa(d)),d}}async function D2(e,t,n,r,{kind:o,atts:s,d:a,dismissPrompt:i=!1}){await hI(t,n,r,o),Fa(e,"say");let c=await ke(t.leadBox);if(c&&await Yg(c)?await Kc(t,n,"say")?(await se(),c=await ke(t.leadBox)):c=null:!c&&await Xc(t)&&(await se(),c=await ke(t.leadBox)),!c)throw new Error(`lead box "${t.leadBox}" is not running`);let l=Dc.get(e);l&&await l.catch(()=>{});let u=dp(e),d=await Pe(t.leadBox,{lines:et}).catch(()=>""),p=u.items.length>0?"queue":Li(n?.agent,d)?"busy":null,f=p!=null,w=i&&!o;if((!f||p==="queue"&&w)&&Qr(n?.agent,d)){let g=await ln(t.leadBox).catch(()=>d),y=Nn(n,g);if(f||(f=!!y,f&&(p="prompt")),y&&w&&y.dismissable){let k=await P2(t,n,y,a);k.gone&&p==="prompt"&&(d=k.pane,f=Li(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),Pg(e),o||Pa(t,null),await se(),await xc(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 Sg(t,n,c,r,o,s,a)}catch(g){if(g?.code!=="cli-not-running"||!await Kc(t,n,"say-gate")||(await se(),c=await ke(t.leadBox),!c))throw g;m=await Sg(t,n,c,r,o,s,a)}return await se(),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 Bg(e,{hint:t}={}){let n=ue(await ee(),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=Mw(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";IE(n.id,{name:s,box:n.leadBox});let a=String(t||"").trim()?`${nI}
|
|
216
218
|
|
|
217
|
-
What to preview: ${String(t).trim()}`:
|
|
219
|
+
What to preview: ${String(t).trim()}`:nI;try{await qt(e,a,{kind:"spin-up-view"})}catch(i){throw jd(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function pp(e){let t=ue(await ee(),e);if(!t)throw new Error(`track "${e}" not found`);if(t.archivedAt)throw new Error(`track "${e}" is archived`);Uc.set(e,(Uc.get(e)||0)+1);let n=Jc(e,"interrupt"),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=Na.get(e);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=Ba(t,r),await se()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:t.draftRev||0}}let s=H2(e,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,_r(B2)]);let a=ue(await ee(),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 wI(e){let t=Na.get(e);return!!t&&!t.canceled}function Fa(e,t){let n=Na.get(e);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=t||null)}function jg(e){Fa(e,"track-ended"),Uc.delete(e),Jc(e,"track-ended")}function H2(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}),Na.set(e,r),(async()=>{let s=ue(await ee(),e),a=s?.boxes.find(g=>g.name===s.leadBox),i=s?await ke(s.leadBox):null,c=!1,l=!1;if(i){e:for(let g=0;g<j2&&!r.canceled;g++){await ze(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let y of L2){await _r(y);let k=await Pe(s.leadBox,{lines:et}).catch(()=>"");if(!Qs(a?.agent,k)){c=!0;break e}}}if(!c){l=!0;for(let g=0;g<U2&&!r.canceled&&(await _r(F2),!r.canceled);g++){let y=await Pe(s.leadBox,{lines:et}).catch(()=>"");if(!Qs(a?.agent,y)){c=!0;break}await ze(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=ue(await ee(),e)||s,d=u?.boxes.find(g=>g.name===u.leadBox)||a,p=null,f=null;u&&d&&i&&c&&!r.canceled&&(p=await G2(u,d).catch(()=>null),p||(f=await J2(u,d).catch(()=>null)));let w=u&&i&&!r.canceled?await q2(u).catch(()=>null):null,m=[p||f,...r.queuedTexts].filter(Boolean);u&&m.length&&(r.restored=Ba(u,m),await se()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await W2(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 de({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"),Na.get(e)===r&&Na.delete(e),o()}),r}async function W2(e,t,n){await C(e,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:e.leadBox,provider:t?.agent||null,payload:n}).catch(()=>{})}async function q2(e){let t=Ea(await q(e));if(!t?.offerId)return null;let n=t.offerType==="exists";return await C(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 G2(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 K2){await _r(r);let o=await Pe(e.leadBox,{lines:et}).catch(()=>""),s=Hi(t.agent,o);if(s&&Su(n.text,s))return await gI(e.leadBox,t.agent,n.text.length+8),Se(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:n.at,text:n.text}),n.text}return null}async function J2(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>V2||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!Ue(r.text))return null;for(let s of z2){await _r(s);let a;try{a=await oh(t,{})}catch{return null}if(dx(a,r))return null;let i=await Pe(e.leadBox,{lines:et}).catch(()=>"");if(Hi(t.agent,i))return null}return Se(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}async function Y2(e,t,n,r=null){return Ki({sessionName:e.leadBox,agent:t?.agent,agentCmd:ws(t?.agent),text:n,trackId:e.id,flow:"say",stats:r})}async function gI(e,t,n,r=null){for(let o=0;o<3;o++){await Gs(e,n,{kind:"unsend-wipe"}).catch(()=>{}),await _r(200);let s=await Pe(e,{lines:et}).catch(()=>""),a=Hi(t,s);if(!a||r&&!Su(r,a)&&!(t==="codex"&&Tm(r,a)))return!0;n=a.length+8}return!1}function Pa(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 Ba(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?Pa(e,n.join(`
|
|
218
220
|
|
|
219
|
-
`),{source:"restore"})?.text??null:null}function
|
|
221
|
+
`),{source:"restore"})?.text??null:null}function Z2(e,t){let n=Ue(t);if(!n)return!1;if(ms(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>X2)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 Lg(e,t,{rev:n=null}={}){if(typeof t!="string")throw new Error("text (string) required");let r=ue(await ee(),e);if(!r)throw new Error(`track "${e}" not found`);let o=t,s=ca(t);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&Z2(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))?Pa(r,o):Pa(r,`${i}
|
|
220
222
|
|
|
221
|
-
${o}`,{source:"restore"})}else Ma(r,o);return await se(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Ng(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 ye(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=IS(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await Ri(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await er({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:ms(o.agent)});let u=await $S(a,r.leadBox,i);return Ae(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await se(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function rI(e){for(let t of e){if(!t||!await aa(t))continue;let n=await iS(t).catch(()=>null);if(!n)continue;if((await J.stat(z.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function Dt(e){let t=e;return t?t.kind==="multi"?Array.isArray(t.repos)?t.repos.filter(Boolean):[]:t.base?[{name:z.basename(t.base),dir:t.dir??null,base:t.base,baseRef:t.baseRef??null,repo:t.repo??null}]:[]:[]}async function is(e){let t=[];for(let n of Dt(e?.workspace)){if(!n?.dir)continue;let r=await zo(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await Vo(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function Pg(e){let t=e;typeof t=="string"&&!z.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await Cs(t)??await Ll(t));let n=await rI([t]);if(!n)throw new Error(`tagged repo "${e}" is not a usable base (need a primary checkout or a synced owner/name)`);return n}async function x2(e,t,n,r){let o=`track/${wp(t)}-${e}`,s=[];try{await J.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await Pg(i);if(s.some(f=>f.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await _e(c).catch(()=>null),u=z.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let f=2;a.has(u);f++)u=`${z.basename(c)}-${f}`;a.add(u);let d=z.join(n,u),p=await Yo(c);await Jo(c,d,o,p),s.push({name:u,dir:d,base:c,baseRef:p,repo:l})}return await ja(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await St(i.base,i.dir).catch(()=>{});throw await J.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function ap(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 Pg(typeof t=="string"?t.trim():t);if((o.repos??[]).some(p=>p?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await _e(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(p=>p?.name).filter(Boolean)]),c=z.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let p=2;i.has(c);p++)c=`${z.basename(s)}-${p}`;let l=z.join(o.dir,c),u=await Yo(s);await pm(s,o.branch)?await Ji(s,l,o.branch):await Jo(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 ja(o.dir,o.repos,o.branch),Ae(r,"retagged",{action:"add",repo:a??s,name:c,workspace:Dg(o)}),await se(),d}async function ip(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 Pt.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 zo(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await Vo(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 St(s.base,s.dir).catch(()=>{}),await J.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await ja(o.dir,o.repos,o.branch),Ae(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:Dg(o)}),await se(),{removed:s.name}}async function zd(e,t){let n=await Z(),r=de(n,e);r&&(r.reshape={...r.reshape||{},...t},await se())}async function v2(e){let t=await J.readdir(e).catch(()=>[]);for(let n of t)if(!(n===".git"||n===".bostrat")){if(n==="AGENTS.md"){let r=await J.readFile(z.join(e,n),"utf8").catch(()=>"");if(Nc(r))continue}return!0}try{let{stdout:n}=await Zd("git",["-C",e,"rev-list","--count","HEAD"]);if(Number(n.trim())>1)return!0}catch{}return!1}function W_(e,t,n){let r=z.basename(e);n.has(r)&&t&&(r=t.replace("/","-"));for(let o=2;n.has(r);o++)r=`${z.basename(e)}-${o}`;return n.add(r),r}async function oI(e,{added:t=[]}={}){let{shape:n,dir:r,staging:o,sub:s,base:a,scratch:i}=e;for(let l of t)await St(l.base,l.dir).catch(()=>{});let c=async(l,u)=>{await wn(l)&&!await wn(u)&&await J.rename(l,u)};if(n==="worktree"&&s){let l=z.join(r,s);await wn(l)&&(await c(z.join(r,".bostrat"),z.join(l,".bostrat")),await Vd(a,l,o))}if(n==="repo"&&i){let l=z.join(r,i);await wn(l)&&(await wn(o)&&await J.rm(o,{recursive:!0,force:!0}),await J.rename(l,o))}return await wn(o)?(await wn(r)&&(await c(z.join(r,".bostrat"),z.join(o,".bostrat")),await J.rm(r,{recursive:!0,force:!0})),n==="worktree"?await Vd(a,o,r):await J.rename(o,r),!0):!1}async function Bg(e,t,{skipPermissions:n=!0}={}){let r=await Z(),o=de(r,e);if(!o)throw gt(`track "${e}" not found`,404);if(o.archivedAt)throw gt("track is archived \u2014 unarchive it first");try{fp(o)}catch(g){throw Object.assign(g,{status:409})}if(o.kind==="setup")throw gt("setup boxes have no repos to reshape");if(String(o.name||"").startsWith("\u21BB"))throw gt("this Track's repos are the loop's \u2014 propose-update the loop instead");let s=yg.get(e);if(s&&!s.finishedAt)throw gt("a reshape is already running for this Track");let a=hs.get(e);if(a&&!a.finishedAt)throw gt("a lead handoff is running \u2014 retry when it settles");let i=Yd.get(e);if(i&&!i.finishedAt)throw gt("a node transfer is running \u2014 retry when it settles");if(Kd.has(e))throw gt("an adopt is running for this Track \u2014 retry when it settles");for(let g of Pt.values())if(g.trackId===e&&!g.finishedAt)throw gt("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 gt("repos (non-empty list of paths or owner/name slugs) required",400);let l=[];for(let g of c){let y;try{y=await Pg(g)}catch(x){throw gt(x?.message||String(x),400)}if(l.some(x=>x.base===y))throw gt(`repo tagged twice: ${g}`,400);l.push({sel:g,base:y,slug:await _e(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 zo(I.dir,{ignorePlumbing:!0}).catch(()=>!1),N=await Vo(I.dir,I.baseRef).catch(()=>0);if(_||N>0)throw gt(`"${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 ap(e,I.base);b.added.push(_.repo??_.base)}for(let I of y)await ip(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 gt(`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 zo(u.dir??o.cwd,{ignorePlumbing:!0}).catch(()=>!1),y=await Vo(u.dir??o.cwd,u.baseRef).catch(()=>0);if(g||y>0)throw gt(`"${z.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`)}Ba(e,"reshape");let p=o.boxes.find(g=>g.name===o.leadBox)||null;if(!p)throw gt("this Track has no lead box");let f=p.agent||"claude",w=await Da(f,n),h={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 yg.set(e,h),await zd(e,{step:h.step,startedAt:h.startedAt,finishedAt:null,error:null,added:h.added,removed:h.removed,layout:null}),S2(h,o,{desired:l,keep:d,lead:p,agent:f,agentCmd:w,skipPermissions:n}).catch(async g=>{h.error=g?.message||String(g),h.finishedAt=Date.now(),console.error(`[reshape ${e}] FAILED at step "${h.step}": ${h.error}`),await zd(e,{step:h.step,error:h.error,finishedAt:h.finishedAt}).catch(()=>{})}),h}async function S2(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(O,S={})=>{e.step=O,console.log(`[reshape ${c}] step \u2192 ${O}`),await zd(c,{step:O,...S})},p=await ye(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(Zr(s))e.checkpoint="skipped-capped";else try{await Ru(o.name,{timeoutMs:xg})}catch{e.checkpoint="reconstructed"}let h=null;e.checkpoint!=="llm"&&(h=await Ou(c,u,{trackName:t.name||c}).catch(()=>null)),await d("quiesce"),e.views=await Cw(c).catch(()=>0),await Vl(u).catch(()=>{}),p&&(await Uh(o.name,{fromAgent:s}).catch(()=>{}),await Ot(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?W_(l.base,l.repo??null,y):null,scratch:null},b=l.branch||`track/${wp(t.name||c)}-${c}`,I=[],_=[],N=null;try{if(await d("move",{layout:x}),l.kind==="worktree")if(await Vd(l.base,u,g),await J.mkdir(u,{recursive:!0}),r){let S=z.join(u,x.sub);await Vd(l.base,g,S),await wn(z.join(S,".bostrat"))&&await J.rename(z.join(S,".bostrat"),z.join(u,".bostrat"));let M=z.join(S,"AGENTS.md");Nc(await J.readFile(M,"utf8").catch(()=>null))&&(await J.rm(M,{force:!0}).catch(()=>{}),await iI(S,"/AGENTS.md").catch(()=>{})),I.push({name:x.sub,dir:S,base:l.base,baseRef:l.baseRef??null,repo:l.repo??null})}else await wn(z.join(g,".bostrat"))&&await J.rename(z.join(g,".bostrat"),z.join(u,".bostrat"));else await J.rename(u,g),await J.mkdir(u,{recursive:!0}),await wn(z.join(g,".bostrat"))&&await J.rename(z.join(g,".bostrat"),z.join(u,".bostrat"));await d("materialize");for(let S of n){if(l.kind==="worktree"&&S.base===l.base)continue;let M=W_(S.base,S.slug,y),H=z.join(u,M),W=await Yo(S.base);await pm(S.base,b)?await Ji(S.base,H,b):await Jo(S.base,H,b,W);let te={name:M,dir:H,base:S.base,baseRef:W,repo:S.slug};I.push(te),_.push(te)}if(l.kind==="repo"&&await v2(g)){let S="scratch";for(let M=2;y.has(S);M++)S=`scratch-${M}`;y.add(S),x.scratch=S,await zd(c,{layout:x}),await J.rename(g,z.join(u,S)),e.scratch=!0}await ja(u,I,b,{scratch:x.scratch}),await d("relaunch"),N=await En(o.name,u,{agentCmd:a,agent:s}),await er({cwd:u,sessionName:o.name,agent:s,agentCmd:ms(s)}).catch(S=>{if(S?.code!=="cli-not-running")throw S});let O=await Pe(o.name,{lines:Ye}).catch(()=>"");if(!Uo(s,O))throw new Error(`relaunched ${s} CLI never drew its composer in the hub`)}catch(O){await d("rollback",{error:O?.message||String(O)}),await Ot(o.name).catch(()=>{}),await oI(x,{added:_}).catch(W=>{console.error(`[reshape ${c}] rollback move failed: ${W?.message||W}`)});let S=await Z(),M=de(S,c),H=M?.boxes.find(W=>W.name===o.name)||null;try{let W=ep(s,f,{skipPermissions:i,cwd:u,peerName:o.name}),te=await En(o.name,u,{agentCmd:W,agent:s,resumeSessionId:f,pinned:w});H&&(H.hookTokenHash=$o(te.hookToken),H.state="live"),Ho({cwd:u,sessionName:o.name,agent:s,agentCmd:W})}catch(W){console.error(`[reshape ${c}] rollback relaunch failed: ${W?.message||W}`)}throw M&&(Ae(M,"retag-failed",{action:"reshape",box:o.name,agent:s,reason:O?.message||String(O),rolledBack:!0}),M.reshape={...M.reshape||{},step:"error",error:O?.message||String(O),finishedAt:Date.now(),recovered:"rolled-back"},await se()),e.step="error",e.recovered="rolled-back",e.error=O?.message||String(O),e.finishedAt=Date.now(),O}await d("commit");let U=await Z(),j=de(U,c);if(!j)throw new Error(`track "${c}" vanished mid-reshape`);let T=j.boxes.find(O=>O.name===o.name);if(!T)throw new Error(`lead box row "${o.name}" missing during reshape commit`);let R=Date.now(),L=fs(T),V=L[L.length-1];V.endedAt==null&&(V.endedAt=R),V.endReason==null&&(V.endReason="reshape"),L.push({agent:s,sessionId:N.sessionId,pinned:N.pinned===!0,startedAt:R,endedAt:null}),T.cwd=u,T.agent=s,T.sessionId=N.sessionId,T.pinned=N.pinned===!0,T.hookTokenHash=$o(N.hookToken),T.state="live",T.role="lead",delete T.readyAt,j.cwd=u,j.workspace={dir:u,kind:"multi",branch:b,repos:I},j.pendingHandoffHydrate={artifact:h||ho,at:R},Ae(j,"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:N.sessionId,epoch:L.length-1,workspace:Dg(j.workspace)}),j.reshape={...j.reshape||{},step:"done",finishedAt:R,error:null,checkpoint:e.checkpoint,scratch:e.scratch,views:e.views,layout:null},await se(),Ho({cwd:u,sessionName:o.name,agent:s,agentCmd:a,onReady:async()=>{T.readyAt||(T.readyAt=Date.now(),await se())}}),await wn(g)&&(l.kind==="worktree"&&await St(l.base,g).catch(()=>{}),await J.rm(g,{recursive:!0,force:!0}).catch(()=>{})),e.step="done",e.finishedAt=R}async function jg(){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 Ot(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 J.readdir(c).catch(()=>[])){if(d.has(f))continue;let w=z.join(c,f),h=await J.readFile(z.join(w,".git"),"utf8").catch(()=>null),g=h&&/^gitdir:\s*(.+?)\/\.git\/worktrees\//m.exec(h);g&&p.push({base:g[1],dir:w})}s=await oI(r.layout,{added:p})}catch(c){a=c?.message||String(c),console.error(`[reshape ${n.id}] boot rollback failed: ${a}`)}o&&!vr.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 se(),t}async function A2(e,t,{repo:n,repos:r,cwd:o}={}){await J.mkdir(Pc,{recursive:!0});let s=z.join(Pc,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return x2(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!z.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await Cs(n)??await Ll(n));let i=await rI([n,o,P4]);if(i){let c=`track/${wp(t)}-${e}`;try{let l=await Yo(i);await Jo(i,s,c,l),await Lc(s);let u=await _e(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await J.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await J.mkdir(s,{recursive:!0}),await fm(s),await Lc(s),{dir:s,base:null,branch:null,kind:"repo"}}async function Lc(e){try{await J.writeFile(z.join(e,"AGENTS.md"),Cc,{flag:"wx"})}catch{return}await I2(e,["/AGENTS.md"])}function _2(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(`
|
|
223
|
+
${o}`,{source:"restore"})}else Pa(r,o);return await se(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Fg(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=ue(await ee(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await ke(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=FS(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await Ci(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await rr({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:ws(o.agent)});let u=await US(a,r.leadBox,i);return Se(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await se(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function yI(e){for(let t of e){if(!t||!await la(t))continue;let n=await bS(t).catch(()=>null);if(!n)continue;if((await V.stat(G.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function Bt(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 ls(e){let t=[];for(let n of Bt(e?.workspace)){if(!n?.dir)continue;let r=await Jo(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await Yo(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function Ug(e){let t=e;typeof t=="string"&&!G.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await Ns(t)??await ql(t));let n=await yI([t]);if(!n)throw new Error(`tagged repo "${e}" is not a usable base (need a primary checkout or a synced owner/name)`);return n}async function Q2(e,t,n,r){let o=`track/${Sp(t)}-${e}`,s=[];try{await V.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await Ug(i);if(s.some(f=>f.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await $e(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 Zo(c);await Xo(c,d,o,p),s.push({name:u,dir:d,base:c,baseRef:p,repo:l})}return await Ua(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 V.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function fp(e,t){let n=await ee(),r=ue(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 Ug(typeof t=="string"?t.trim():t);if((o.repos??[]).some(p=>p?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await $e(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 Zo(s);await kh(s,o.branch)?await ec(s,l,o.branch):await Xo(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 Ua(o.dir,o.repos,o.branch),Se(r,"retagged",{action:"add",repo:a??s,name:c,workspace:Hg(o)}),await se(),d}async function mp(e,t){let n=await ee(),r=ue(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 Dt.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 Jo(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await Yo(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 V.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await Ua(o.dir,o.repos,o.branch),Se(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:Hg(o)}),await se(),{removed:s.name}}async function ep(e,t){let n=await ee(),r=ue(n,e);r&&(r.reshape={...r.reshape||{},...t},await se())}async function eH(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(jc(r))continue}return!0}try{let{stdout:n}=await sp("git",["-C",e,"rev-list","--count","HEAD"]);if(Number(n.trim())>1)return!0}catch{}return!1}function rI(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 bI(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 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 tp(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 tp(a,o,r):await V.rename(o,r),!0):!1}async function Wg(e,t,{skipPermissions:n=!0}={}){let r=await ee(),o=ue(r,e);if(!o)throw kt(`track "${e}" not found`,404);if(o.archivedAt)throw kt("track is archived \u2014 unarchive it first");try{kp(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=Ag.get(e);if(s&&!s.finishedAt)throw kt("a reshape is already running for this Track");let a=hs.get(e);if(a&&!a.finishedAt)throw kt("a lead handoff is running \u2014 retry when it settles");let i=rp.get(e);if(i&&!i.finishedAt)throw kt("a node transfer is running \u2014 retry when it settles");if(Zd.has(e))throw kt("an adopt is running for this Track \u2014 retry when it settles");for(let g of Dt.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 Ug(g)}catch(k){throw kt(k?.message||String(k),400)}if(l.some(k=>k.base===y))throw kt(`repo tagged twice: ${g}`,400);l.push({sel:g,base:y,slug:await $e(y).catch(()=>null)})}let u=o.workspace;if(u?.kind==="multi"){let g=(u.repos??[]).filter(Boolean),y=g.filter(I=>!l.some(R=>R.base===I.base)),k=l.filter(I=>!g.some(R=>R.base===I.base));if(!y.length&&!k.length)return{trackId:e,step:"done",inPlace:!0,added:[],removed:[],startedAt:Date.now(),finishedAt:Date.now(),error:null};for(let I of y){let R=await Jo(I.dir,{ignorePlumbing:!0}).catch(()=>!1),N=await Yo(I.dir,I.baseRef).catch(()=>0);if(R||N>0)throw kt(`"${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 k){let R=await fp(e,I.base);b.added.push(R.repo??R.base)}for(let I of y)await mp(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 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 Jo(u.dir??o.cwd,{ignorePlumbing:!0}).catch(()=>!1),y=await Yo(u.dir??o.cwd,u.baseRef).catch(()=>0);if(g||y>0)throw kt(`"${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`)}Fa(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 La(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 Ag.set(e,m),await ep(e,{step:m.step,startedAt:m.startedAt,finishedAt:null,error:null,added:m.added,removed:m.removed,layout:null}),tH(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 ep(e,{step:m.step,error:m.error,finishedAt:m.finishedAt}).catch(()=>{})}),m}async function tH(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(S,_={})=>{e.step=S,console.log(`[reshape ${c}] step \u2192 ${S}`),await ep(c,{step:S,..._})},p=await ke(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(eo(s))e.checkpoint="skipped-capped";else try{await Pu(o.name,{timeoutMs:_g})}catch{e.checkpoint="reconstructed"}let m=null;e.checkpoint!=="llm"&&(m=await Du(c,u,{trackName:t.name||c}).catch(()=>null)),await d("quiesce"),e.views=await jw(c).catch(()=>0),await Ql(u).catch(()=>{}),p&&(await Km(o.name,{fromAgent:s}).catch(()=>{}),await Mt(o.name).catch(()=>{}));let g=`${u}.reshape-${Date.now()}`,y=new Set([".bostrat"]),k={shape:l.kind,dir:u,staging:g,base:l.kind==="worktree"?l.base:null,sub:l.kind==="worktree"&&r?rI(l.base,l.repo??null,y):null,scratch:null},b=l.branch||`track/${Sp(t.name||c)}-${c}`,I=[],R=[],N=null;try{if(await d("move",{layout:k}),l.kind==="worktree")if(await tp(l.base,u,g),await V.mkdir(u,{recursive:!0}),r){let _=G.join(u,k.sub);await tp(l.base,g,_),await bn(G.join(_,".bostrat"))&&await V.rename(G.join(_,".bostrat"),G.join(u,".bostrat"));let H=G.join(_,"AGENTS.md");jc(await V.readFile(H,"utf8").catch(()=>null))&&(await V.rm(H,{force:!0}).catch(()=>{}),await vI(_,"/AGENTS.md").catch(()=>{})),I.push({name:k.sub,dir:_,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 _ of n){if(l.kind==="worktree"&&_.base===l.base)continue;let H=rI(_.base,_.slug,y),K=G.join(u,H),X=await Zo(_.base);await kh(_.base,b)?await ec(_.base,K,b):await Xo(_.base,K,b,X);let pe={name:H,dir:K,base:_.base,baseRef:X,repo:_.slug};I.push(pe),R.push(pe)}if(l.kind==="repo"&&await eH(g)){let _="scratch";for(let H=2;y.has(_);H++)_=`scratch-${H}`;y.add(_),k.scratch=_,await ep(c,{layout:k}),await V.rename(g,G.join(u,_)),e.scratch=!0}await Ua(u,I,b,{scratch:k.scratch}),await d("relaunch"),N=await $n(o.name,u,{agentCmd:a,agent:s}),await rr({cwd:u,sessionName:o.name,agent:s,agentCmd:ws(s)}).catch(_=>{if(_?.code!=="cli-not-running")throw _});let S=await Pe(o.name,{lines:et}).catch(()=>"");if(!Ho(s,S))throw new Error(`relaunched ${s} CLI never drew its composer in the hub`)}catch(S){await d("rollback",{error:S?.message||String(S)}),await Mt(o.name).catch(()=>{}),await bI(k,{added:R}).catch(X=>{console.error(`[reshape ${c}] rollback move failed: ${X?.message||X}`)});let _=await ee(),H=ue(_,c),K=H?.boxes.find(X=>X.name===o.name)||null;try{let X=ip(s,f,{skipPermissions:i,cwd:u,peerName:o.name}),pe=await $n(o.name,u,{agentCmd:X,agent:s,resumeSessionId:f,pinned:w});K&&(K.hookTokenHash=Ro(pe.hookToken),K.state="live"),Wo({cwd:u,sessionName:o.name,agent:s,agentCmd:X})}catch(X){console.error(`[reshape ${c}] rollback relaunch failed: ${X?.message||X}`)}throw H&&(Se(H,"retag-failed",{action:"reshape",box:o.name,agent:s,reason:S?.message||String(S),rolledBack:!0}),H.reshape={...H.reshape||{},step:"error",error:S?.message||String(S),finishedAt:Date.now(),recovered:"rolled-back"},await se()),e.step="error",e.recovered="rolled-back",e.error=S?.message||String(S),e.finishedAt=Date.now(),S}await d("commit");let D=await ee(),j=ue(D,c);if(!j)throw new Error(`track "${c}" vanished mid-reshape`);let T=j.boxes.find(S=>S.name===o.name);if(!T)throw new Error(`lead box row "${o.name}" missing during reshape commit`);let $=Date.now(),F=mo(T),J=F[F.length-1];J.endedAt==null&&(J.endedAt=$),J.endReason==null&&(J.endReason="reshape");let M=await Ko({agent:s,agentCmd:a});F.push({agent:s,sessionId:N.sessionId,pinned:N.pinned===!0,cliVersion:M,startedAt:$,endedAt:null}),T.cwd=u,T.agent=s,T.cliVersion=M,T.sessionId=N.sessionId,T.pinned=N.pinned===!0,T.hookTokenHash=Ro(N.hookToken),T.state="live",T.role="lead",delete T.readyAt,j.cwd=u,j.workspace={dir:u,kind:"multi",branch:b,repos:I},j.pendingHandoffHydrate={artifact:m||ho,at:$},Se(j,"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:N.sessionId,epoch:F.length-1,workspace:Hg(j.workspace)}),j.reshape={...j.reshape||{},step:"done",finishedAt:$,error:null,checkpoint:e.checkpoint,scratch:e.scratch,views:e.views,layout:null},await se(),Wo({cwd:u,sessionName:o.name,agent:s,agentCmd:a,onReady:async()=>{T.readyAt||(T.readyAt=Date.now(),await se())}}),await bn(g)&&(l.kind==="worktree"&&await Tt(l.base,g).catch(()=>{}),await V.rm(g,{recursive:!0,force:!0}).catch(()=>{})),e.step="done",e.finishedAt=$}async function qg(){let e=await ee(),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 Mt(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 bI(r.layout,{added:p})}catch(c){a=c?.message||String(c),console.error(`[reshape ${n.id}] boot rollback failed: ${a}`)}o&&!Er.has(o.state)&&(o.state="stopped");let i=`interrupted by a node restart during "${r.step}"${s?" \u2014 moved back":""}${a?` (${a})`:""}`;Se(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 se(),t}async function nH(e,t,{repo:n,repos:r,cwd:o}={}){await V.mkdir(Lc,{recursive:!0});let s=G.join(Lc,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return Q2(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!G.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await Ns(n)??await ql(n));let i=await yI([n,o,f2]);if(i){let c=`track/${Sp(t)}-${e}`;try{let l=await Zo(i);await Xo(i,s,c,l),await Wc(s);let u=await $e(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 xh(s),await Wc(s),{dir:s,base:null,branch:null,kind:"repo"}}async function Wc(e){try{await V.writeFile(G.join(e,"AGENTS.md"),Bc,{flag:"wx"})}catch{return}await aH(e,["/AGENTS.md"])}function sH(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(`
|
|
222
224
|
`),o=n?`
|
|
223
225
|
\`${n}/\` is this Track's pre-repo workspace, carried over when the
|
|
224
226
|
Track was reshaped onto these repos. It is not part of any tagged repo and
|
|
225
227
|
cannot be shipped \u2014 move anything worth keeping into the repo it belongs to.
|
|
226
|
-
`:"";return`${
|
|
228
|
+
`:"";return`${oH}
|
|
227
229
|
|
|
228
230
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
229
231
|
git worktree per tagged repo, ALL on the same branch \`${t}\`:
|
|
@@ -235,20 +237,20 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
235
237
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
236
238
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
237
239
|
or preview an app, run it from that app's own repo directory.
|
|
238
|
-
${o}`}async function
|
|
239
|
-
`).includes(s));o.length&&await
|
|
240
|
+
${o}`}async function Ua(e,t,n,r={}){let o=sH(t,n,r);for(let s of["CLAUDE.md","AGENTS.md"])await V.writeFile(G.join(e,s),o).catch(()=>{})}async function xI(e){let{stdout:t}=await sp("git",["-C",e,"rev-parse","--git-path","info/exclude"]);return G.resolve(e,t.trim())}async function aH(e,t){try{let n=await xI(e);await V.mkdir(G.dirname(n),{recursive:!0});let r=await V.readFile(n,"utf8").catch(()=>""),o=t.filter(s=>!r.split(`
|
|
241
|
+
`).includes(s));o.length&&await V.appendFile(n,(r.endsWith(`
|
|
240
242
|
`)||!r?"":`
|
|
241
243
|
`)+o.join(`
|
|
242
244
|
`)+`
|
|
243
|
-
`)}catch{}}async function
|
|
245
|
+
`)}catch{}}async function vI(e,t){try{let n=await xI(e),r=await V.readFile(n,"utf8"),o=r.split(`
|
|
244
246
|
`).filter(s=>s!==t);o.length!==r.split(`
|
|
245
|
-
`).length&&await
|
|
246
|
-
`))}catch{}}async function
|
|
247
|
+
`).length&&await V.writeFile(n,o.join(`
|
|
248
|
+
`))}catch{}}async function Kg(){let e={refreshed:0,removed:0,codexGlobal:null};try{await V.mkdir(Vn,{recursive:!0}),await V.writeFile(G.join(Vn,"CLAUDE.md"),rH).catch(()=>{}),await V.writeFile(G.join(Vn,"AGENTS.md"),Bc).catch(()=>{});let t=process.env.CODEX_HOME||G.join(process.env.HOME||"/tmp",".codex");if((await V.stat(t).catch(()=>null))?.isDirectory()){let r=G.join(t,"AGENTS.md"),o=await V.readFile(r,"utf8").catch(()=>null);o===null?(await V.writeFile(r,yg,{flag:"wx"}).catch(()=>{}),e.codexGlobal="created"):jc(o)&&o!==yg&&(await V.writeFile(r,yg).catch(()=>{}),e.codexGlobal="refreshed")}for(let r of[Lc,ja]){let o=await V.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=G.join(r,s.name),i=G.join(a,"AGENTS.md"),c=await V.readFile(i,"utf8").catch(()=>null);c!==null&&jc(c)&&c!==Bc&&(await V.writeFile(i,Bc).catch(()=>{}),e.refreshed++);let l=G.join(a,"CLAUDE.md"),u=await V.readFile(l,"utf8").catch(()=>null);u!==null&&jc(u)&&(await V.rm(l,{force:!0}).catch(()=>{}),await vI(a,"/CLAUDE.md"),e.removed++)}}}catch{}return e}async function SI(e){let t=e.workspace;if(!(!t||!t.dir))try{if(t.kind==="multi"){for(let n of Bt(t))n.base&&n.dir&&await Tt(n.base,n.dir).catch(()=>{});await V.rm(t.dir,{recursive:!0,force:!0})}else t.kind==="worktree"&&t.base?await Tt(t.base,t.dir):await V.rm(t.dir,{recursive:!0,force:!0})}catch{}}async function hp({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 La(o,a,s),c=Ko({agent:o,agentCmd:i}),l=await ee(),u=op().slice(0,8),d=await nH(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(pu(f))&&(f=`${e.trim().slice(0,50).trim()} ${u} lead`);let w=await $n(f,d.dir,{agentCmd:i,agent:o,freshWorkspace:!0}),m={id:u,name:e.trim(),cwd:w.cwd,workspace:d,leadBox:w.name,boxes:[ty(w,"lead","live")],events:[],createdAt:Date.now()};return m.boxes[0].cliVersion=await c,Se(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:k,repo:b})=>({name:g,base:y,baseRef:k,repo:b}))}:{}}}),l.tracks.push(m),await se(),Wo({cwd:w.cwd,sessionName:w.name,agent:o,agentCmd:i,onBlocked:({message:g})=>uI(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 se())}}),Wt(m)}async function Gg({provider:e="claude",fresh:t=!1}={}){if(!iH.has(e))throw new Error(`unknown setup provider "${e}" (claude | codex | cursor)`);let r=(await ee()).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 gs(r.id).catch(()=>{})}let o=(await La(e,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=op().slice(0,8),a=G.join(Vn,"setup",s);await V.mkdir(a,{recursive:!0});let i=await $n(`Connect ${e} ${s}`,a,{agentCmd:o,agent:e,firstRun:!0});e==="codex"&&Wo({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:[ty(i,"lead","live")],events:[],createdAt:Date.now()};return Se(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 ee()).tracks.push(c),await se(),Wt(c)}async function Vg(e,t,n){await Ql(t.cwd).catch(()=>{}),await jw(e).catch(()=>{});let r=hs.get(e);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),hs.delete(e),t.handoff&&!t.handoff.finishedAt&&(t.handoff.step="error",t.handoff.error=n,t.handoff.finishedAt=Date.now());for(let a of bs(e))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await dy(a).catch(()=>{}),Dt.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 ee(),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 Mt(a.name).catch(()=>{})}async function zg(e,{cascadeMocks:t=!0,cascadeRunbooks:n=!1}={}){let r=await ee(),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 t_(s),jg(e),await Vg(e,s,"track deleted"),n?await M_(e).catch(()=>{}):await O_(e).catch(()=>{}),await qS(),await SI(s),t&&await n_(e).catch(()=>{}),t?await b_(e).catch(()=>{}):await y_(s).catch(()=>{}),Se(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await se(),{ok:!0,id:e,name:s.name,boxes:s.boxes.length}}async function gs(e){let t=await ee(),n=ue(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(),jg(e),Se(n,"archived",{boxes:n.boxes.length}),await se(),n.kind==="setup"?(await AI(e),Wt(n)):(qc.add(e),uH(e).catch(()=>{}).finally(()=>qc.delete(e)),{...await Wt(n),archiving:!0})}async function uH(e){try{await dH(e)}catch{}await AI(e).catch(()=>{})}async function AI(e){let t=await ee(),n=ue(t,e);if(!(!n||!n.archivedAt)){await Vg(e,n,"track archived");for(let r of n.boxes)Er.has(r.state)||(r.state="stopped");await se()}}async function dH(e){let t=await ee(),n=ue(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 ke(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await uv(n.leadBox,i.cwd,{timeoutMs:lH})),s="lead-cli"}catch{}if(r||(r=Tv(n,await q(n).catch(()=>[]))),!r)return;let c={text:r,source:s,at:Date.now()};o.length&&(c.items=o),n.beat=c,await C(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 se()}async function fr(e,t){let n=await pt(e,t).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await Jg(n);if(r.length){for(let o of r)await ts(o.repo,o.number,!0).catch(()=>{});return!1}try{return await gs(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function Jg(e){return(await mn().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 mr(e,t){return wp(e,t,"merged")}async function ys(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 Jg(n);if(o.length){let c=[...n.followupPending||[],r];for(let l of o)await SA(l.repo,l.number,c).catch(()=>{});return null}let s=[...n.followupPending||[],r],a=ue(await ee(),n.trackId);if(!a)return null;let i=s[0];return wc(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 wp(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 Jg(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await vA(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];if(!await fA(r.repo,r.number).catch(()=>!0))return!1;try{return await qt(r.trackId,TI(a),{kind:"merge-notify"}),!0}catch{return await ns(r.repo,r.number,!0).catch(()=>{}),!1}}function TI(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 ${oI[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 ${oI[i.outcome]??i.outcome}`).join(`
|
|
247
249
|
`),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}
|
|
248
250
|
${s}
|
|
249
|
-
${a}`}async function qg(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<Ii()?!1:Qn(e.name)}async function Uc(e,t,n){return!t||e.archivedAt||vr.has(t.state)?!1:(await ue({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 Ot(t.name).catch(()=>{}),t.state="stopped",zc(e))}async function zc(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=fg.get(r.name);if(o)return o;let s=N2(e,r,{freshSession:t,reason:n}).finally(()=>fg.delete(r.name));return fg.set(r.name,s),s}async function N2(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||vr.has(t.state)||await ye(t.name))return!1;let o=n===!0,s=o?Xd():null,a=o?Ei(kg(t.agent),s,t.name):ep(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await au(a);if(i===Hs)return Ae(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===Ws&&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(fs(t)))}:null,l=Date.now();if(o){let u=fs(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=await En(t.name,t.cwd,{agentCmd:a,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=$o(u.hookToken),Ho({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a}),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"}),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 up(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,Fc.delete(e),Ae(r,"unarchived",{}),await zc(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await se(),Ut(r)}async function Kg(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 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 Ut(o);let s=o.name;return o.name=n,Ae(o,"renamed",{from:s,to:n}),await se(),Ut(o)}async function Gg(){let e=await Z(),t=0;for(let n of e.tracks||[])n.transferredTo&&Fu(n,{at:n.transferredAt??Date.now()})&&t++;return t&&await se(),t}async function zg(){let e=await Z(),t=new Set((await sn()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!je(s)).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(je(s))for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Ot(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),!vr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await se(),r}async function dp(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`);fp(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(vr.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await ye(r.leadBox)){let i=await Pe(r.leadBox,{lines:Ye}).catch(()=>"");if(Di(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Ot(r.leadBox),o.state="stopped"}let a=await zc(r,t?{freshSession:!0,reason:"loop-fire"}:{});if(await se(),!a)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Ut(r)}async function Vg(e,t,n){let r=await ie(e);if(!r)throw new Error(`track "${e}" not found`);if(!t)throw new Error("unknown offer");let o=await K(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 dp(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 K_(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return gd({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function fs(e){return(!Array.isArray(e.epochs)||e.epochs.length===0)&&(e.epochs=[{agent:e.agent??null,sessionId:e.sessionId??null,pinned:e.pinned===!0,startedAt:e.createdAt??null,endedAt:null}]),e.epochs}async function Jd(e,t){let n=await Z(),r=de(n,e);r&&(r.handoff={...r.handoff||{},...t},await se())}function dI(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 Yg(){let e=await Z(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=dI(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await ye(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=fs(l),p=d[d.length-1],f=p.agent,w=p.sessionId??null;p.endedAt=u,r.reason&&(p.endReason=r.reason);let h=c.agent==="claude"&&!!c.sessionId;d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:h,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=h,l.state="live",n.pendingHandoffHydrate={artifact:ho,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 ue({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 ue({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Ot(i).catch(()=>{}),a&&await Ot(a).catch(()=>{})}return t&&await se(),t}async function pp(e,t,n=!0,r=null){let o=hs.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`);Ba(e,"handoff");let a=await Da(t,n),i=Zr(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 ye(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:Y_(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return hs.set(e,l),await Jd(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),P2(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 Jd(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),ue({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),ue({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function P2(e,t,n,r,o){let s=async(_,N={})=>{e.step=_,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${_}`),await Jd(e.trackId,{step:_,toBox:e.toBox}),await ue({flow:"lead-handoff",phase:_,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:N.sessionName,...N})};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(_=>_.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",Zr(c))e.checkpoint="skipped-capped";else try{await Ru(e.fromBox,{timeoutMs:xg})}catch(_){e.checkpoint="reconstructed",await ue({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:_?.message||String(_)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await Ou(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=await Hh(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let f=Q0(p.launchCmd);await er({cwd:n.cwd,sessionId:p.sessionId,sessionName:e.fromBox,agent:o,agentCmd:ms(o),launchEcho:f}).catch(_=>{if(_?.code!=="cli-not-running")throw _});let w=yh(await Pe(e.fromBox,{lines:Ye}).catch(()=>""),f);if(!Uo(o,w)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let _=ep(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await Hh(e.fromBox,{agentCmd:_,agent:c,resumeSessionId:l,pinned:u});let N=await Z(),U=de(N,e.trackId);if(U){let j=U.boxes.find(T=>T.name===e.fromBox);j&&(j.state="live"),Ae(U,"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 se()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let h=await Z(),g=de(h,e.trackId);if(!g)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let y=g.boxes.find(_=>_.name===e.fromBox);if(!y)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let x=Date.now(),b=fs(y),I=b[b.length-1];I.endedAt=x,e.reason&&(I.endReason=e.reason),b.push({agent:o,sessionId:p.sessionId,pinned:p.pinned,startedAt:x,endedAt:null}),y.agent=o,y.sessionId=p.sessionId,y.pinned=p.pinned,y.state="live",y.role="lead",g.pendingHandoffHydrate={artifact:e.statusArtifact||ho,at:x},Ae(g,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:p.sessionId,epoch:b.length-1,singleBox:!0,forced:p.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await se(),await s("done",{sessionName:e.fromBox}),await Jd(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function Qg(e){let t=Yd.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=hs.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of Pt.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 Yd.set(e,s),D2(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 D2(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await ye(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&Zr(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await Ru(t.leadBox,{timeoutMs:xg})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await Ou(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 Dt(t.workspace)){if(!u?.dir)continue;let d={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await gn(u.dir,["push","origin",o],Xg);let{stdout:p}=await gn(u.dir,["rev-parse","HEAD"]);d.baseSha=p.trim(),d.pushed=!0}catch(p){d.error=String(p?.message||p).split(`
|
|
250
|
-
`)[0]}s.push(d)}e.step="wip";let a=await
|
|
251
|
-
`)[0],advanced:null}}}function F2(e){if(typeof e!="string"||!L2.test(e)||e.includes("..")||e.includes("@{")||e.endsWith(".lock"))throw new Error("branch fails the safe-ref check")}async function ty(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&&F2(e.branch),Kd.has(t))throw new Error("an adopt is already running for this Track");Kd.set(t,{startedAt:Date.now()});try{return await U2({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{Kd.delete(t)}}async function U2({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:h=null,downSince:g=null}){let y=await Da(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 tg(b).catch(()=>{}),await cI(b),x.tracks.splice(x.tracks.indexOf(b),1),await se()),await J.mkdir(Pc,{recursive:!0});let I=z.join(Pc,e);await J.rm(I,{recursive:!0,force:!0}).catch(()=>{});let _=null,N=!1,U=[];try{let j=[];if(i)for(let X of Array.isArray(c)?c:[]){let G=typeof X=="string"?X:X?.repo;if(!G||!/^[^/\s]+\/[^/\s]+$/.test(G))continue;let fe=await Cs(G)??await Ll(G);await gn(fe,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],Xg),j.push({slug:G,base:fe,name:typeof X=="object"?X?.name??null:null})}if(i&&j.length===1){let{base:X,slug:G}=j[0];await G_(X,I,i),U.push({base:X,dir:I}),await Lc(I);let fe=await Yo(X);_={dir:I,base:X,branch:i,kind:"worktree",baseRef:fe,repo:G}}else if(i&&j.length>=2){await J.mkdir(I,{recursive:!0});let X=new Set([".bostrat"]),G=[];for(let fe of j){let be=fe.name||z.basename(fe.base);X.has(be)&&(be=fe.slug.replace("/","-"));for(let le=2;X.has(be);le++)be=`${z.basename(fe.base)}-${le}`;X.add(be);let xe=z.join(I,be);await G_(fe.base,xe,i),U.push({base:fe.base,dir:xe});let Ke=await Yo(fe.base);G.push({name:be,dir:xe,base:fe.base,baseRef:Ke,repo:fe.slug})}await ja(I,G,i),_={dir:I,kind:"multi",branch:i,repos:G}}else N=!0,await J.mkdir(I,{recursive:!0}),await fm(I),await Lc(I),_={dir:I,base:null,branch:null,kind:"repo"};let T=[];for(let X of B2(_,{wip:l,wips:u})){let G=await j2(X.dir,e);T.push({name:X.name,repo:X.repo,applied:G.applied,skipped:G.skipped,...G.advanced?{advanced:G.advanced}:{}})}let R=T.some(X=>X.applied),L=T.length===1?T[0].skipped:T.filter(X=>X.skipped).map(X=>`${X.name??X.repo}: ${X.skipped}`).join("; ")||null,V=T.length===1?T[0].advanced??null:null;await M_({id:e,cwd:I}).catch(()=>({restored:!1}));let O=z.join(I,ho),S=null;(await Ql(I)).restored?S="checkpoint":typeof d=="string"&&d.trim()&&(await J.mkdir(z.dirname(O),{recursive:!0}),await J.writeFile(O,d),S="brief");let H=new Set(x.tracks.flatMap(X=>(X.boxes||[]).map(G=>G.name))),W=null;w&&!H.has(w)&&!await ye(w).catch(()=>null)?W=w:(W=`${t.trim()} lead`,H.has(iu(W))&&(W=`${t.trim().slice(0,50).trim()} ${e} lead`));let te=await En(W,I,{agentCmd:y,agent:n});Ho({cwd:te.cwd,sessionName:te.name,agent:n,agentCmd:y});let pe=Date.now(),Ie=Jg(te,"lead","live"),Oe=(Array.isArray(p)?p:[]).filter(X=>X&&typeof X=="object").map(X=>({...X})),re=Oe[Oe.length-1];re&&re.endedAt==null&&(re.endedAt=pe,re.endReason==null&&(re.endReason=o==="recover"?"node-offline":"node-transfer")),Oe.push({agent:n,sessionId:te.sessionId??null,pinned:te.pinned===!0,startedAt:pe,endedAt:null}),Ie.epochs=Oe;let Ne={id:e,name:t.trim(),cwd:te.cwd,workspace:_,leadBox:te.name,boxes:[Ie],events:[],createdAt:Number.isFinite(f)?f:pe,...S?{pendingHandoffHydrate:{artifact:ho,at:pe}}:{}};return Ae(Ne,"transferred-in",{box:te.name,agent:n,fromNode:s,toNode:a,mode:o,reason:h,...o==="recover"?{downSince:Number.isFinite(g)?g:null}:{},branch:_.branch??null,wip:R,...V?{wipAdvanced:V}:{},..._.kind==="multi"&&T.length?{wipRepos:T}:{},epoch:Oe.length-1,name:Ne.name,cwd:Ne.cwd,workspace:{base:_.base??null,branch:_.branch??null,kind:_.kind,..._.baseRef?{baseRef:_.baseRef}:{},..._.repo?{repo:_.repo}:{},..._.repos?{repos:_.repos.map(({name:X,base:G,baseRef:fe,repo:be})=>({name:X,base:G,baseRef:fe,repo:be}))}:{}}}),x.tracks.push(Ne),await se(),{...await Ut(Ne),adopt:{wipApplied:R,wipSkipped:L,...V?{wipAdvanced:V}:{},..._.kind==="multi"?{wipRepos:T}:{},codeless:N,artifactSource:S}}}catch(j){for(let T of U)await St(T.base,T.dir).catch(()=>{});throw await J.rm(I,{recursive:!0,force:!0}).catch(()=>{}),j}}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 se())}async function Ra(e,t){e.step=t,await Vc(e)}function fI(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 hI(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function K2(e,t,n){if(!hI(n)||Pt.has(t)||!await ye(n.box).catch(()=>null))return!1;try{await J.access(n.dir)}catch{return!1}let o=await ye(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return Pt.set(t,s),wI(s,o).catch(a=>mI(s,a)),!0}async function ny(){let e=await Z(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Pt.has(r))continue;if(await K2(n,r,o)){t=!0,Ae(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await ue({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=fI(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!vr.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 ue({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await z_(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||vr.has(r.state)||Pt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?z.join(Pa,`${n.id}-${r.subtaskId}`):null;await ue({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await z_(n,r.name,o)}}return t=await G2(e)||t,t&&await se(),t}async function z_(e,t,n){if(t&&await Ot(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of Dt(e.workspace)){let s=z.join(n,o.name);await St(o.base,s).then(()=>!0,()=>!1)||await St(o.dir,s).catch(()=>{})}await J.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await ye(e.leadBox).catch(()=>null);r?.cwd&&await St(r.cwd,n).catch(()=>{})}async function G2(e){let t;try{t=await J.readdir(Pa)}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 Pt.values())o.dir&&n.add(z.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=z.join(Pa,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of Dt(a.workspace))await St(i.base,z.join(s,i.name)).catch(()=>{});await J.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await ye(a.leadBox).catch(()=>null):null;i?.cwd?await St(i.cwd,s).catch(()=>{}):await J.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await ue({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function ry(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!sa(n))throw new Error("task (string) required");let s=await Da(t,r),a=Zr(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 ye(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=Dt(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 aa(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 aa(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=Xd().slice(0,8),p={id:d,trackId:e,trackName:i.name,agent:t,task:sa(n),skipPermissions:r,reason:Y_(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${wp(i.name)}-${d}`,base:null,repos:u,dir:z.join(Pa,`${e}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Pt.set(d,p),await Vc(p),z2(p,c,s).catch(f=>mI(p,f)),p}async function mI(e,t){e.failedStep=e.step,e.error=t instanceof Hc?"canceled":t?.message||String(t),e.step=t instanceof Hc?"canceled":"error",e.finishedAt=Date.now(),await sy(e).catch(()=>{}),await Vc(e).catch(()=>{}),await X2(e).catch(()=>{})}function gp(e){let t=Pt.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 oy(e){let t=Pt.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 z2(e,t,n){if(await Ra(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await ia(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await Vc(e)}else e.base=await ia(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await Ra(e,"spawn"),await J.mkdir(Pa,{recursive:!0}),e.repos){await J.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await Jo(a.leadDir,z.join(e.dir,a.name),e.branch,a.base);let s=Dt((de(await Z(),e.trackId)||{}).workspace);await ja(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 Jo(t.cwd,e.dir,e.branch,e.base),await Lc(e.dir);await V2(e,t);let r=e.trackName.slice(0,38).trim(),o=await En(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await J2(e,o),await X_(e.trackId,o.name,B4(e.task,!!e.repos),{kind:"subtask"}),await wI(e,t)}async function wI(e,t){await Ra(e,"working");let n=z.join(e.dir,Gd);if(await Q2(n,e),e.canceled)throw new Hc;if(await Ra(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=z.join(e.dir,l.name);await dm(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:K_({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await mm(l.leadDir,l.base,e.branch,$a),l.files=await wm(l.leadDir,l.base,e.branch,$a);let d=await gm(l.leadDir,l.base,e.branch,$a);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=>z.join(l.name,p))),i.push(...l.conflicts.map(p=>p.startsWith("patch did not apply cleanly")?`${l.name}: ${p}`:z.join(l.name,p))),d.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await dm(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:K_({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await mm(t.cwd,e.base,e.branch,$a),e.files=await wm(t.cwd,e.base,e.branch,$a);let s=await gm(t.cwd,e.base,e.branch,$a);e.applied=s.applied,e.conflicts=s.conflicts}await Vc(e);let r=await Z2(e,t,n);await Ra(e,"resume"),await X_(e.trackId,e.leadBox,j4(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await Ra(e,"reap"),await sy(e),e.step="done";let o=Date.now();await pI(e.trackId,e.id,{...mp(e),finishedAt:o}),await Y2(e,o),e.finishedAt=o}async function V2(e,t){let n=z.join(e.dir,z.dirname(Dc));await J.mkdir(n,{recursive:!0});let r="";try{r=await D0(t.cwd,t.sessionId)}catch{}if(!r)try{r=await J.readFile(z.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}
|
|
251
|
+
${a}`}async function Yg(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<Oi()?!1:nr(e.name)}async function Kc(e,t,n){return!t||e.archivedAt||Er.has(t.state)?!1:(await de({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 Mt(t.name).catch(()=>{}),t.state="stopped",Xc(e))}async function Xc(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=bg.get(r.name);if(o)return o;let s=pH(e,r,{freshSession:t,reason:n}).finally(()=>bg.delete(r.name));return bg.set(r.name,s),s}async function pH(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||Er.has(t.state)||await ke(t.name))return!1;let o=n===!0,s=o?op():null,a=o?$i(Eg(t.agent),s,t.name):ip(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await du(a);if(i===qs)return Se(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===Ks&&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(mo(t)))}:null,l=Date.now();if(o){let u=mo(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 $n(t.name,t.cwd,{agentCmd:a,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=Ro(d.hookToken),t.cliVersion=await u,mo(t)[t.epochs.length-1].cliVersion=t.cliVersion,Wo({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a,onBlocked:({message:p})=>uI(e.id,{box:t.name,agent:t.agent,message:p}).catch(()=>{})}),t.state="live",o&&Se(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}),Se(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),Se(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function gp(e,{freshSession:t=!1}={}){let n=await ee(),r=ue(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,qc.delete(e),Se(r,"unarchived",{}),await Xc(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await se(),Wt(r)}async function Xg(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>Tg)throw new Error(`name too long (max ${Tg} chars)`);let r=await ee(),o=ue(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,Se(o,"renamed",{from:s,to:n}),await se(),Wt(o)}async function Zg(){let e=await ee(),t=0;for(let n of e.tracks||[])n.transferredTo&&zu(n,{at:n.transferredAt??Date.now()})&&t++;return t&&await se(),t}async function Qg(){let e=await ee(),t=new Set((await cn()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!je(s)).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(je(s))for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Mt(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):Se(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!Er.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await se(),r}async function yp(e,{freshSession:t=!1}={}){let n=await ee(),r=ue(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`);kp(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(Er.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await ke(r.leadBox)){let i=await Pe(r.leadBox,{lines:et}).catch(()=>"");if(Li(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Mt(r.leadBox),o.state="stopped"}let a=await Xc(r,t?{freshSession:!0,reason:"loop-fire"}:{});if(await se(),!a)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Wt(r)}async function ey(e,t,n){let r=await le(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 yp(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 sI(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return Td({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function mo(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 np(e,t){let n=await ee(),r=ue(n,e);r&&(r.handoff={...r.handoff||{},...t},await se())}function EI(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 ny(){let e=await ee(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=EI(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await ke(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=mo(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:ho,at:u},Se(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 de({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 de({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Mt(i).catch(()=>{}),a&&await Mt(a).catch(()=>{})}return t&&await se(),t}async function bp(e,t,n=!0,r=null){let o=hs.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=ue(await ee(),e);if(!s)throw new Error(`track "${e}" not found`);Fa(e,"handoff");let a=await La(t,n),i=eo(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 ke(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:dI(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return hs.set(e,l),await np(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),fH(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 np(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),de({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),de({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function fH(e,t,n,r,o){let s=async(D,j={})=>{e.step=D,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${D}`),await np(e.trackId,{step:D,toBox:e.toBox}),await de({flow:"lead-handoff",phase:D,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:j.sessionName,...j})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await ee(),i=ue(a,e.trackId)?.boxes.find(D=>D.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",eo(c))e.checkpoint="skipped-capped";else try{await Pu(e.fromBox,{timeoutMs:_g})}catch(D){e.checkpoint="reconstructed",await de({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:D?.message||String(D)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await Du(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 Gm(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let w=cx(f.launchCmd);await rr({cwd:n.cwd,sessionId:f.sessionId,sessionName:e.fromBox,agent:o,agentCmd:ws(o),launchEcho:w}).catch(D=>{if(D?.code!=="cli-not-running")throw D});let m=Sm(await Pe(e.fromBox,{lines:et}).catch(()=>""),w);if(!Ho(o,m)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let D=ip(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await Gm(e.fromBox,{agentCmd:D,agent:c,resumeSessionId:l,pinned:u});let j=await ee(),T=ue(j,e.trackId);if(T){let $=T.boxes.find(F=>F.name===e.fromBox);$&&($.state="live"),Se(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 se()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let g=await ee(),y=ue(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 k=y.boxes.find(D=>D.name===e.fromBox);if(!k)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let b=Date.now(),I=mo(k),R=I[I.length-1];R.endedAt=b,e.reason&&(R.endReason=e.reason);let N=await p;I.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,cliVersion:N,startedAt:b,endedAt:null}),k.agent=o,k.sessionId=f.sessionId,k.pinned=f.pinned,k.cliVersion=N,k.state="live",k.role="lead",y.pendingHandoffHydrate={artifact:e.statusArtifact||ho,at:b},Se(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:N,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await se(),await s("done",{sessionName:e.fromBox}),await np(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function sy(e){let t=rp.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await ee(),r=ue(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=hs.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of Dt.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 rp.set(e,s),mH(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 mH(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await ke(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&eo(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await Pu(t.leadBox,{timeoutMs:_g})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await Du(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 Bt(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],ry);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 ag(t,{force:!0}),i=await ee(),c=ue(i,t.id),l=c?.boxes.find(u=>u.name===c.leadBox)||null;l&&mo(l),await se(),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 kp(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 xp(e,{toNode:t,mode:n="transfer"}={}){if(!t||typeof t!="string")throw new Error("toNode required");let r=await ee(),o=ue(r,e);if(!o)throw new Error(`track "${e}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};jg(e),await Vg(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();zu(o,{at:s,reason:n==="recover"?"node-offline":"node-transfer"});let a=await ig(o,{ts:s}),i=a.pushed[0]??null;return o.transferredTo=t,o.transferredAt=s,Se(o,"transferred-out",{toNode:t,mode:n,box:o.leadBox,...i?{rescueRef:i.ref,rescueSha:i.sha}:{},...a.pushed.length?{rescues:a.pushed}:{}}),await se(),{ok:!0,toNode:t,rescue:i?{ref:i.ref,sha:i.sha}:null,rescues:a.pushed}}async function ay(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await xp(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 aI(e,t,n){try{await ec(e,t,n)}catch{await kn(e,["worktree","prune"]).catch(()=>{}),await ec(e,t,n)}return t}function hH(e,{wip:t=null,wips:n=null}={}){let r=Bt(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 wH(e,t){try{await kn(e,["fetch","origin",sg(t)],ry);let{stdout:n}=await kn(e,["rev-parse","FETCH_HEAD^"]),{stdout:r}=await kn(e,["rev-parse","HEAD"]),o=null,s=n.trim()===r.trim();return s||await kn(e,["merge-base","--is-ancestor",r.trim(),n.trim()]).then(()=>!0,()=>!1)&&(await kn(e,["reset","--hard",n.trim()]),o={from:r.trim(),to:n.trim()},s=!0),s?(await kn(e,["read-tree","--reset","-u","FETCH_HEAD"]),await kn(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(`
|
|
253
|
+
`)[0],advanced:null}}}function yH(e){if(typeof e!="string"||!gH.test(e)||e.includes("..")||e.includes("@{")||e.endsWith(".lock"))throw new Error("branch fails the safe-ref check")}async function iy(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&&yH(e.branch),Zd.has(t))throw new Error("an adopt is already running for this Track");Zd.set(t,{startedAt:Date.now()});try{return await bH({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{Zd.delete(t)}}async function bH({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 La(n,r),k=await ee(),b=ue(k,e);if(b&&!b.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);b&&(await ig(b).catch(()=>{}),await SI(b),k.tracks.splice(k.tracks.indexOf(b),1),await se()),await V.mkdir(Lc,{recursive:!0});let I=G.join(Lc,e);await V.rm(I,{recursive:!0,force:!0}).catch(()=>{});let R=null,N=!1,D=[];try{let j=[];if(i)for(let Z of Array.isArray(c)?c:[]){let he=typeof Z=="string"?Z:Z?.repo;if(!he||!/^[^/\s]+\/[^/\s]+$/.test(he))continue;let Te=await Ns(he)??await ql(he);await kn(Te,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],ry),j.push({slug:he,base:Te,name:typeof Z=="object"?Z?.name??null:null})}if(i&&j.length===1){let{base:Z,slug:he}=j[0];await aI(Z,I,i),D.push({base:Z,dir:I}),await Wc(I);let Te=await Zo(Z);R={dir:I,base:Z,branch:i,kind:"worktree",baseRef:Te,repo:he}}else if(i&&j.length>=2){await V.mkdir(I,{recursive:!0});let Z=new Set([".bostrat"]),he=[];for(let Te of j){let z=Te.name||G.basename(Te.base);Z.has(z)&&(z=Te.slug.replace("/","-"));for(let O=2;Z.has(z);O++)z=`${G.basename(Te.base)}-${O}`;Z.add(z);let ge=G.join(I,z);await aI(Te.base,ge,i),D.push({base:Te.base,dir:ge});let Ge=await Zo(Te.base);he.push({name:z,dir:ge,base:Te.base,baseRef:Ge,repo:Te.slug})}await Ua(I,he,i),R={dir:I,kind:"multi",branch:i,repos:he}}else N=!0,await V.mkdir(I,{recursive:!0}),await xh(I),await Wc(I),R={dir:I,base:null,branch:null,kind:"repo"};let T=[];for(let Z of hH(R,{wip:l,wips:u})){let he=await wH(Z.dir,e);T.push({name:Z.name,repo:Z.repo,applied:he.applied,skipped:he.skipped,...he.advanced?{advanced:he.advanced}:{}})}let $=T.some(Z=>Z.applied),F=T.length===1?T[0].skipped:T.filter(Z=>Z.skipped).map(Z=>`${Z.name??Z.repo}: ${Z.skipped}`).join("; ")||null,J=T.length===1?T[0].advanced??null:null;await G_({id:e,cwd:I}).catch(()=>({restored:!1}));let M=G.join(I,ho),S=null;(await ou(I)).restored?S="checkpoint":typeof d=="string"&&d.trim()&&(await V.mkdir(G.dirname(M),{recursive:!0}),await V.writeFile(M,d),S="brief");let H=new Set(k.tracks.flatMap(Z=>(Z.boxes||[]).map(he=>he.name))),K=null;w&&!H.has(w)&&!await ke(w).catch(()=>null)?K=w:(K=`${t.trim()} lead`,H.has(pu(K))&&(K=`${t.trim().slice(0,50).trim()} ${e} lead`));let X=await $n(K,I,{agentCmd:y,agent:n});Wo({cwd:X.cwd,sessionName:X.name,agent:n,agentCmd:y});let pe=Date.now(),Oe=ty(X,"lead","live"),Me=(Array.isArray(p)?p:[]).filter(Z=>Z&&typeof Z=="object").map(Z=>({...Z})),Q=Me[Me.length-1];Q&&Q.endedAt==null&&(Q.endedAt=pe,Q.endReason==null&&(Q.endReason=o==="recover"?"node-offline":"node-transfer"));let qe=await Ko({agent:n,agentCmd:y});Me.push({agent:n,sessionId:X.sessionId??null,pinned:X.pinned===!0,cliVersion:qe,startedAt:pe,endedAt:null}),Oe.epochs=Me,Oe.cliVersion=qe;let Ae={id:e,name:t.trim(),cwd:X.cwd,workspace:R,leadBox:X.name,boxes:[Oe],events:[],createdAt:Number.isFinite(f)?f:pe,...S?{pendingHandoffHydrate:{artifact:ho,at:pe}}:{}};return Se(Ae,"transferred-in",{box:X.name,agent:n,fromNode:s,toNode:a,mode:o,reason:m,...o==="recover"?{downSince:Number.isFinite(g)?g:null}:{},branch:R.branch??null,wip:$,...J?{wipAdvanced:J}:{},...R.kind==="multi"&&T.length?{wipRepos:T}:{},epoch:Me.length-1,name:Ae.name,cwd:Ae.cwd,workspace:{base:R.base??null,branch:R.branch??null,kind:R.kind,...R.baseRef?{baseRef:R.baseRef}:{},...R.repo?{repo:R.repo}:{},...R.repos?{repos:R.repos.map(({name:Z,base:he,baseRef:Te,repo:z})=>({name:Z,base:he,baseRef:Te,repo:z}))}:{}}}),k.tracks.push(Ae),await se(),{...await Wt(Ae),adopt:{wipApplied:$,wipSkipped:F,...J?{wipAdvanced:J}:{},...R.kind==="multi"?{wipRepos:T}:{},codeless:N,artifactSource:S}}}catch(j){for(let T of D)await Tt(T.base,T.dir).catch(()=>{});throw await V.rm(I,{recursive:!0,force:!0}).catch(()=>{}),j}}async function _I(e,t,n){let r=await ee(),o=ue(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await se())}async function Ca(e,t){e.step=t,await Zc(e)}function II(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 $I(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function SH(e,t,n){if(!$I(n)||Dt.has(t)||!await ke(n.box).catch(()=>null))return!1;try{await V.access(n.dir)}catch{return!1}let o=await ke(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return Dt.set(t,s),OI(s,o).catch(a=>RI(s,a)),!0}async function cy(){let e=await ee(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Dt.has(r))continue;if(await SH(n,r,o)){t=!0,Se(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await de({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=II(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!Er.has(i.state)&&(i.state="canceled"),Se(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 de({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await iI(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||Er.has(r.state)||Dt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?G.join(ja,`${n.id}-${r.subtaskId}`):null;await de({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await iI(n,r.name,o)}}return t=await AH(e)||t,t&&await se(),t}async function iI(e,t,n){if(t&&await Mt(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of Bt(e.workspace)){let s=G.join(n,o.name);await Tt(o.base,s).then(()=>!0,()=>!1)||await Tt(o.dir,s).catch(()=>{})}await V.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await ke(e.leadBox).catch(()=>null);r?.cwd&&await Tt(r.cwd,n).catch(()=>{})}async function AH(e){let t;try{t=await V.readdir(ja)}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 Dt.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(ja,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of Bt(a.workspace))await Tt(i.base,G.join(s,i.name)).catch(()=>{});await V.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await ke(a.leadBox).catch(()=>null):null;i?.cwd?await Tt(i.cwd,s).catch(()=>{}):await V.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await de({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function ly(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!ca(n))throw new Error("task (string) required");let s=await La(t,r),a=eo(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=ue(await ee(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await ke(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=Bt(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 la(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 la(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=op().slice(0,8),p={id:d,trackId:e,trackName:i.name,agent:t,task:ca(n),skipPermissions:r,reason:dI(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${Sp(i.name)}-${d}`,base:null,repos:u,dir:G.join(ja,`${e}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Dt.set(d,p),await Zc(p),TH(p,c,s).catch(f=>RI(p,f)),p}async function RI(e,t){e.failedStep=e.step,e.error=t instanceof Gc?"canceled":t?.message||String(t),e.step=t instanceof Gc?"canceled":"error",e.finishedAt=Date.now(),await dy(e).catch(()=>{}),await Zc(e).catch(()=>{}),await $H(e).catch(()=>{})}function Ap(e){let t=Dt.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 uy(e){let t=Dt.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 TH(e,t,n){if(await Ca(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await ua(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await Zc(e)}else e.base=await ua(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await Ca(e,"spawn"),await V.mkdir(ja,{recursive:!0}),e.repos){await V.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await Xo(a.leadDir,G.join(e.dir,a.name),e.branch,a.base);let s=Bt((ue(await ee(),e.trackId)||{}).workspace);await Ua(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 Xo(t.cwd,e.dir,e.branch,e.base),await Wc(e.dir);await EH(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 _H(e,o),await pI(e.trackId,o.name,h2(e.task,!!e.repos),{kind:"subtask"}),await OI(e,t)}async function OI(e,t){await Ca(e,"working");let n=G.join(e.dir,Qd);if(await OH(n,e),e.canceled)throw new Gc;if(await Ca(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 bh(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:sI({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await Sh(l.leadDir,l.base,e.branch,Ma),l.files=await Ah(l.leadDir,l.base,e.branch,Ma);let d=await Th(l.leadDir,l.base,e.branch,Ma);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 bh(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:sI({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await Sh(t.cwd,e.base,e.branch,Ma),e.files=await Ah(t.cwd,e.base,e.branch,Ma);let s=await Th(t.cwd,e.base,e.branch,Ma);e.applied=s.applied,e.conflicts=s.conflicts}await Zc(e);let r=await RH(e,t,n);await Ca(e,"resume"),await pI(e.trackId,e.leadBox,w2(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await Ca(e,"reap"),await dy(e),e.step="done";let o=Date.now();await _I(e.trackId,e.id,{...vp(e),finishedAt:o}),await IH(e,o),e.finishedAt=o}async function EH(e,t){let n=G.join(e.dir,G.dirname(Fc));await V.mkdir(n,{recursive:!0});let r="";try{r=await K0(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}
|
|
252
254
|
|
|
253
255
|
**Delegated task:** ${e.task}
|
|
254
256
|
|
|
@@ -259,15 +261,15 @@ ${a}`}async function qg(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<
|
|
|
259
261
|
## Conversation so far (lead \u2192 you)
|
|
260
262
|
|
|
261
263
|
${r}
|
|
262
|
-
`;await
|
|
264
|
+
`;await V.writeFile(G.join(e.dir,Fc),o)}async function _H(e,t){let n=await ee(),r=ue(n,e.trackId);r&&(r.boxes.push({name:t.name,agent:t.agent,role:"subtask",state:"live",cwd:t.cwd,sessionId:t.sessionId,hookTokenHash:Ro(t.hookToken),createdAt:Date.now(),subtaskId:e.id,branch:e.branch,task:e.task}),Se(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]||{},...vp(e)},await se())}async function IH(e,t=e.finishedAt){let n=await ee(),r=ue(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),Se(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 se()}async function $H(e){let t=await ee(),n=ue(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"),Se(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]||{},...vp(e)},await se()}async function RH(e,t,n){let r=`.bostrat/handoff/subtask-${e.id}.md`;try{let o=G.join(t.cwd,r);await V.mkdir(G.dirname(o),{recursive:!0}),await V.copyFile(n,o)}catch{}return r}async function dy(e){if(e.dir&&await Ql(e.dir).catch(()=>{}),e.box&&await Mt(e.box).catch(()=>{}),e.repos&&e.dir){for(let t of e.repos)await Tt(t.leadDir,G.join(e.dir,t.name)).catch(()=>{});await V.rm(e.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(e.dir){let n=(await ke(e.leadBox).catch(()=>null))?.cwd;n&&await Tt(n,e.dir).catch(()=>{})}}async function OH(e,t){let n=(t.startedAt||Date.now())+kH;for(;;){if(t.canceled||t.forceMerge)return;try{await V.access(e);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await _r(xH)}}function _r(e){return new Promise(t=>setTimeout(t,e))}var sp,Vn,cI,p2,Lc,f2,ap,Vc,fo,ho,_g,m2,Qd,Fc,h2,w2,Pt,mg,ue,hg,Se,wg,gg,b2,Er,eI,v2,S2,A2,T2,tI,Dc,O2,Da,nI,B2,j2,L2,F2,U2,Na,Uc,K2,V2,z2,X2,Hg,Ag,Yc,kt,bn,tp,Hc,kI,rH,Bc,jc,oH,yg,iH,qc,cH,lH,oI,bg,Tg,ty,hs,Ha,ry,kn,rp,oy,gH,Zd,kH,xH,ja,Ma,Dt,vH,bs,vp,Zc,Sp,Gc,hr=W(()=>{bi();mt();Fm();Ev();ea();Dr();bu();Mv();yt();In();Fu();Pv();Os();yu();sh();Uo();mh();wh();gS();Wi();Em();ku();Pm();dn();Vu();Cr();Zt();Ui();Zu();HS();Oh();oE();Ou();$u();aE();kw();fe();Sw();_d();Fd();dc();es();Ic();$a();Yw();Vd();rg();Dn();Cd();L_();cg();V_();su();X_();dg();fg();sp=d2(u2),Vn=process.env.BOSTRAT_STATE_DIR||G.join(process.env.HOME||"/tmp",".bostrat"),cI=G.join(Vn,"tracks.json"),p2=G.join(Vn,"events.jsonl"),Lc=G.join(Vn,"workspaces"),f2=process.env.BOSTRAT_DEFAULT_REPO||null,ap={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"}},Vc=Object.keys(ap);fo={at:0,value:null};ho=".bostrat/handoff/latest.md",_g=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;m2=/\(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\.\)$/;Qd=".bostrat/handoff/subtask-result.md",Fc=".bostrat/handoff/lead-context.md",h2=(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 ${Fc} \u2014 read that first for context. Your scoped task:
|
|
263
265
|
|
|
264
266
|
${e}
|
|
265
267
|
|
|
266
|
-
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 ${
|
|
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 ${Qd} (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 ${Fc} \u2014 read that first for context. Your scoped task:
|
|
267
269
|
|
|
268
270
|
${e}
|
|
269
271
|
|
|
270
|
-
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 ${
|
|
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 ${Qd} 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.`,w2=(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."),Pt=null,mg=null;ue=(e,t)=>e.tracks.find(n=>n.id===t)||null,hg=Promise.resolve();Se=(e,t,n={})=>{let r={at:Date.now(),type:t,...n};return e.events.push(r),g2(e.id,r),KS(e,r),r};J0(async({sessionName:e,from:t,to:n,version:r,status:o,error:s})=>{let i=(await ee()).tracks.find(c=>c.leadBox===e||(c.boxes||[]).some(l=>l.name===e));i&&(Se(i,"codex-update",{box:e,from:t,to:r||n,status:o,...s?{error:s}:{}}),await se().catch(()=>{}))});wg=G.join(Vn,"plane-link","update-notice.json");gg=(e,t,n)=>t!=null&&n.some(r=>e===r.box&&t>=r.from&&t<=r.to),b2=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);Er=new Set(["merged","conflict","canceled","failed","handed-off"]),eI=new Map;v2=5e3;S2={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};A2=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];T2=10*6e4;tI=400;Dc=new Map;O2=1500,Da=new Map;nI="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.";B2=4e3,j2=3,L2=[300,350,400],F2=1e3,U2=12,Na=new Map,Uc=new Map;K2=[250,400,500,600];V2=3e4,z2=[350,650,900];X2=12e4;Hg=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}))});Ag=new Map,Yc=e=>Ag.get(e)||(Pt?ue(Pt,e)?.reshape:null)||null,kt=(e,t=409)=>Object.assign(new Error(e),{status:t});bn=e=>V.access(e).then(()=>!0,()=>!1),tp=(e,t,n)=>sp("git",["-C",e,"worktree","move","--end-of-options",t,n],{timeout:6e4});Hc="# bostrat workspace",kI=`${Hc}
|
|
271
273
|
|
|
272
274
|
TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
|
|
273
275
|
before substantive work \u2014 it is the shared cross-provider memory for this repo
|
|
@@ -645,16 +647,16 @@ for the same follow-up at the same moment twice; run bostrat-followup ls first
|
|
|
645
647
|
specifics (no PR numbers, URLs, SHAs, tags \u2014 the card supplies them), and HOLD
|
|
646
648
|
HERE after proposing until the operator answers the card. Bostrat never runs a
|
|
647
649
|
follow-up on its own; the operator's tap is the gate.
|
|
648
|
-
`,
|
|
649
|
-
`)),
|
|
650
|
-
`,`${
|
|
650
|
+
`,rH=kI,Bc=kI+jx;jc=e=>typeof e=="string"&&(e===Hc||e.startsWith(`${Hc}
|
|
651
|
+
`)),oH="# bostrat multi-repo workspace";yg=Bc.replace(`${Hc}
|
|
652
|
+
`,`${Hc}
|
|
651
653
|
|
|
652
654
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
653
655
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
654
656
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
655
657
|
When working anywhere else, ignore everything below.
|
|
656
|
-
`);$2=new Set(["claude","codex","cursor"]);Fc=new Set,R2=e=>Fc.has(e);O2=9e4;q_={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};fg=new Map;bg=120;Jg=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:$o(e.hookToken),createdAt:Date.now()});hs=new Map,La=e=>hs.get(e)||(Nt?de(Nt,e)?.handoff:null)||null;Xg=6e4,gn=(e,t,n=15e3)=>Zd("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),Yd=new Map,Zg=e=>Yd.get(e)||null;L2=/^[A-Za-z0-9][A-Za-z0-9._/-]{0,200}$/;Kd=new Map;H2=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,W2=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,Pa=z.join(qn,"worktrees"),$a=[Dc,Gd],Pt=new Map,q2=e=>Pt.get(e)||(Nt?Nt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,ys=e=>{let t=new Map([...Pt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Nt?de(Nt,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)},mp=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});Vc=e=>pI(e.trackId,e.id,mp(e));wp=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Hc=class extends Error{}});ut();import Ee from"node:path";import kt from"node:fs";import JK from"node:readline";import{fileURLToPath as YK}from"node:url";import zt from"express";import XK from"node:http";import ZK from"node:https";import vl from"node:os";import{randomUUID as QK}from"node:crypto";import{WebSocketServer as eG}from"ws";ut();import HN from"node-pty";async function h0(e,t,n={}){if(!De(t)){du(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await jr()){du(e,`host tmux server not running on ${jt}`),e.close(1011,"tmux server unavailable");return}if(!await ye(t)){du(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=f0(n.cols,120,m0),o=f0(n.rows,32,w0),s;try{s=HN.spawn(ee,["-S",jt,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){du(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&Jf(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(Jf(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"&&GN(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{}KN(t)})}var WN=6,qN=250;async function KN(e,t=WN,n=qN){for(let r=0;r<t;r++){if(await Bo(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var m0=20,w0=6;function GN(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=m0&&Number(t)>=w0}function f0(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function Jf(e,t){try{e.send(JSON.stringify(t))}catch{}}function du(e,t){Jf(e,{type:"error",message:t})}function g0(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}}ln();fu();Dh();dr();import{createRequire as tG}from"node:module";var eH=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(eH)&&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}}he();Xh();ut();import oH from"node:fs/promises";import iy from"node:fs/promises";var tH=100;async function nH(e){let t;try{t=await iy.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/tH*1e3,ioBytes:await rH(e)}}async function rH(e){let t;try{t=await iy.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 yI(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 iy.readdir("/proc")}catch{return null}let r=new Map;await Promise.all(n.map(async l=>{if(!/^\d+$/.test(l))return;let u=await nH(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 sH=Number(process.env.BOSTRAT_JOB_CHECK_MS)||15*6e4,aH=Number(process.env.BOSTRAT_JOB_STALL_CHECKS)||3,iH=Number(process.env.BOSTRAT_JOB_CPU_RATE_MIN)||25,cH=Number(process.env.BOSTRAT_JOB_IO_RATE_MIN)||64*1024,lH=5*6e4,kI={readOpenJobs:Wv,statFile:async e=>{try{let t=await oH.stat(e);return{size:t.size,mtimeMs:Math.round(t.mtimeMs)}}catch{return null}},updatedAt:e=>Vr(e.agent).updatedAt(e),panePids:l0,treeStats:yI,appendEvent:C},yn=new Map,bI=new Map,cy=new Map,ly=(e,t,n)=>`${e}|${t}|${n}`;function xI(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 vI(e,t,n){let r=null;try{r=await n.updatedAt(e)}catch{r=null}let o=bI.get(e.name);if(o&&o.mtime===r&&t-o.at<lH)return o.jobs;let s=[];try{s=await n.readOpenJobs(e)}catch{s=[]}return bI.set(e.name,{at:t,mtime:r,jobs:s}),s}async function uH(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 dH(e,t){return e?.outputFile?await t.statFile(e.outputFile):null}function pH(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>=iH||t.ioBytes!=null&&e.ioBytes!=null&&(t.ioBytes-e.ioBytes)/n>=cH}function SI(e,t,n,r){let o=[];for(let s of n){let a=ly(e.id,t.name,s.id),i=yn.get(a);i?i.job=s:yn.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 AI(e,{now:t=Date.now(),deps:n=kI}={}){let r=new Map,o=new Set;for(let{track:s,box:a}of xI(e)){let i=await vI(a,t,n);for(let l of i)o.add(ly(s.id,a.name,l.id));let c=SI(s,a,i,t).filter(l=>!yn.get(l).stalled).map(l=>yn.get(l).job);c.length&&r.set(s.id,[...r.get(s.id)??[],...c])}for(let s of[...yn.keys()])o.has(s)||yn.delete(s);return r}async function fH(e,{now:t=Date.now(),deps:n=kI}={}){let r={checked:0,progressing:0,quiet:0,stalled:[]},o=xI(e),s=new Set(o.map(({box:a})=>a.name));for(let[a,i]of[...yn])s.has(i.boxName)||yn.delete(a);for(let{track:a,box:i}of o){let c=await vI(i,t,n),l=new Set(c.map(w=>ly(a.id,i.name,w.id)));for(let[w,h]of[...yn])h.boxName===i.name&&!l.has(w)&&yn.delete(w);if(!c.length){cy.delete(i.name);continue}let u=SI(a,i,c,t),d=await uH(i,t,n),p=cy.get(i.name);cy.set(i.name,d);let f=pH(p,d);for(let w of u){let h=yn.get(w);r.checked++;let g=await dH(h.job,n),y=!!g&&!!h.file&&(g.size>h.file.size||g.mtimeMs>h.file.mtimeMs),x=!!g&&!h.file;if(h.file=g??h.file,f||y||x){h.quiet=0,h.lastProgressAt=t,h.stalled=!1,r.progressing++;continue}if(h.stalled||(h.quiet++,r.quiet++,h.quiet<aH))continue;h.stalled=!0,h.stalledAt=t,r.stalled.push(w);let I=t-(h.lastProgressAt??h.firstSeenAt);try{await n.appendEvent(a,{type:"job.stalled",actor:"orchestrator",source:"orchestrator",boxId:i.name,provider:i.agent||null,payload:{id:h.job.id,kind:h.job.kind||"agent",name:h.job.name||null,detail:h.job.detail||null,startedTs:h.job.ts??h.firstSeenAt,quietMs:I,checks:h.quiet}})}catch{}console.log(`[job-watch] ${a.id}/${i.name} job ${h.job.id} (${h.job.name||h.job.kind}) quiet for ${Math.round(I/6e4)}m over ${h.quiet} check(s) \u2014 released; the node may sleep`)}}return r}function TI({listTracks:e,intervalMs:t=sH}={}){let n=!1,o=setInterval(async()=>{if(!n){n=!0;try{await fH(await e())}catch(s){console.error(`[job-watch] sweep: ${s?.message||s}`)}finally{n=!1}}},t);return o.unref?.(),o}he();Ac();Mn();vd();Yt();var yp=60*6e4,hH=1440*6e4,mH=new Set(["merged","closed","lost"]),Jc=(e,t,n)=>Number.isFinite(Number(e))&&n-Number(e)<=t;function wH({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)<=Pw&&s.push("ship"),r>0&&s.push("create-net");for(let a of n||[]){if(!a||mH.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&&Jc(a.ciFix.delegatedAt,yp,o)&&s.push(`ci-fix#${l}`),a.reconcile&&Jc(a.reconcile.delegatedAt,yp,o)&&s.push(`reconcile#${l}`),a.ciFixAwait&&Jc(a.ciFixAwait.since,yp,o)&&s.push(`ci-fix-await#${l}`),a.reconcileAwait&&Jc(a.reconcileAwait.since,yp,o)&&s.push(`reconcile-await#${l}`);let u=a.autoMerge?.armedAt??a.armedAt??null;a.trackId===e&&(a.autoMerge||a.armedAt)&&Jc(u,hH,o)&&s.push(`auto-merge#${l}`)}return s}var bp=new Map;Je.on("event",e=>{e?.trackId&&bp.delete(e.trackId)});async function gH(e){if(bp.has(e.id))return bp.get(e.id);let t=await K(e).catch(()=>[]),n=Sa(t,-1/0);return bp.set(e.id,n),n}async function EI(e,{now:t=Date.now()}={}){let n=new Map,r=await pn().catch(()=>[]);for(let o of e||[]){if(!o||je(o))continue;let s=wH({trackId:o.id,shipPending:await gH(o),links:r,observers:hT(o.id),now:t});s.length&&n.set(o.id,s)}return n}sg();import jI from"node:fs";import wo from"node:fs/promises";import PH from"node:os";import ft from"node:path";import{spawn as DH}from"node:child_process";import{createRequire as BH}from"node:module";he();qu();import Fa from"node:fs";import Xc from"node:fs/promises";import kH from"node:http";import Wt from"node:path";import xp from"node:os";import{createRequire as xH}from"node:module";import dy from"ws";import yH from"node:path";import bH from"node:os";var Yc=process.env.BOSTRAT_STATE_DIR||yH.join(process.env.HOME||bH.tmpdir(),".bostrat");function uy(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 _I(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=uy(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function II(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}Sf();var bn=(()=>{try{return xH(import.meta.url)("../package.json").version||null}catch{return null}})(),fy=process.env.BOSTRAT_STATE_DIR||Wt.join(process.env.HOME||xp.tmpdir(),".bostrat"),PI=Wt.join(fy,"plane-link","removed.json");function $I(e){return typeof e!="string"||e.length<8?"\u2022\u2022\u2022\u2022":`${e.slice(0,6)}\u2026${e.slice(-4)}`}function vH(e=PI){try{let t=JSON.parse(Fa.readFileSync(e,"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function bs({env:e=process.env,pkgDir:t=Wt.resolve(Wt.dirname(new URL(import.meta.url).pathname),".."),exists:n=Fa.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(Wt.join(t,"..",".git"))?"git-checkout":"dev"}function Zc(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 kp(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 SH({home:e=process.env.HOME||xp.homedir(),env:t=process.env}={}){let n=r=>{try{return Fa.statSync(r),!0}catch{return!1}};return{claude:n(Wt.join(e,".claude",".credentials.json")),codex:n(Wt.join(t.CODEX_HOME||Wt.join(e,".codex"),"auth.json"))}}var lt=1,RI=1e3,OI=3e4,AH=6e4,TH=1500,EH=500,mo=100,_H=2e3,IH=25e3,$H=6e4,DI=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},RH=DI(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),OH=3e4,MI=10*6e4,MH=400,CI=250,CH=DI(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),py=4,NH=6e4,NI=6e4;function BI({url:e,token:t,nodeId:n=xp.hostname(),name:r=xp.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,deliverySample:c=null,onHashKey:l=null,metricsMs:u=NH,onUpdateAvailable:d=null,onUpdatePolicy:p=null,onHelloRefused:f=null,updatePolicy:w=null,installKind:h=bs(),selfUrl:g=`http://127.0.0.1:${process.env.PORT||3362}`,apiToken:y=null,removedMarkerFile:x=PI,onRemoved:b=null,onDegraded:I=null,onRecovered:_=null,onConnected:N=null,ingestMode:U=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:j=RH,unackedRetryMs:T=$H,pingMs:R=IH,maxInflightEvents:L=MH,httpTimeoutMs:V=CH,stallBackoffMinMs:O=OH,fetchImpl:S=globalThis.fetch,log:M=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=II(e);let H=g.replace(/^http/,"ws"),W=new Set,te=new Map,pe=new Map,Ie=Wt.join(fy,"plane-link","cursor.json"),Oe=Wt.join(fy,"plane-link","dead-letter.ndjson"),re=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),Ne={},X={},G=new Map,fe=null,be=null,xe=0,Ke=0,le=0,$=Promise.resolve(),Y="ws",Ce=0,Ve=!1,xt=0,Er=!1,_t=mo,_r=new Set,k=[],m=null,v=null,P=!1,ae=!1,ge=RI,Me=null,ht=null,Qe=null,st=null,Vn=null,uf=!1,$l=0,ti=!1,Ir=vH(x);if(Ir)if(Ir.tokenPreview===$I(t))ti=!0,vf({reason:Ir.reason??"removed",at:Ir.at??null,nodeId:Ir.nodeId??n}),M.error(`[plane-link] this node was REMOVED from the plane (${Ir.reason??"removed"}${Ir.at?` at ${new Date(Ir.at).toISOString()}`:""}) \u2014 not dialing. Re-provision it with a new node token, or delete ${x} to force a dial.`);else try{Fa.unlinkSync(x),M.log("[plane-link] stale removed-marker discarded \u2014 the node token changed")}catch{}function df({reason:D="removed",at:F=Date.now(),via:A="ctl"}={}){if(!ti){ti=!0,vf({reason:D,at:F,nodeId:n,via:A});try{Fa.mkdirSync(Wt.dirname(x),{recursive:!0}),Fa.writeFileSync(x,JSON.stringify({at:F,reason:D,via:A,nodeId:n,tokenPreview:$I(t)},null,2))}catch(B){M.error(`[plane-link] could not write ${x}: ${B?.message||B}`)}M.error(`[plane-link] the plane REMOVED this node (${D}, via ${A}) \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{b?.({reason:D,at:F,via:A})}catch{}Kb({bye:!1})}}async function gM(){try{Ne=JSON.parse(await Xc.readFile(Ie,"utf8"))}catch{Ne={}}}function yM(){uf=!0,!Qe&&(Qe=setTimeout(async()=>{if(Qe=null,!!uf){uf=!1;try{await Xc.mkdir(Wt.dirname(Ie),{recursive:!0}),await Xc.writeFile(Ie,JSON.stringify(Ne))}catch(D){M.error("[plane-link] cursor flush failed:",D?.message||D)}}},_H),Qe.unref?.())}function at(D){return!v||v.readyState!==dy.OPEN?!1:(v.send(JSON.stringify(D)),!0)}function ni(D){let F=Date.now();for(let{trackId:A,event:B}of D){let Q=G.get(B.id);G.set(B.id,{trackId:A,seq:B.seq,event:B,sentAt:F,firstSentAt:Q?.firstSentAt??F,retryAt:F+T,tries:(Q?.tries??0)+1,nacks:Q?.nacks??0})}return Y==="http"?(vM(D),!0):at({v:lt,ch:"events",type:"append",events:D})}function bM(D){let F=null;for(let A of G.values())A.trackId===D&&(F==null||A.seq<F)&&(F=A.seq);return F}function pf(D){let F=X[D];if(F==null)return;let A=bM(D),B=A==null?F:Math.min(F,A-1);B>(Ne[D]??0)&&(Ne[D]=B,yM())}function ri(){if(fe=Date.now(),be!=null){let D=Date.now()-be;M.log(`[plane-link] spine sync recovered after ${Math.round(D/1e3)}s degraded (${xe} stall redial(s))`);let F=be;be=null,xe=0,Ke=0,Ve=!1,xt=0;try{_?.({since:F,downMs:D})}catch(A){M.error(`[plane-link] onRecovered failed: ${A?.message||A}`)}}}function ff(D){if(!Array.isArray(D)||!D.length)return;let F=new Set;for(let A of D){let B=G.get(A);B&&(G.delete(A),(X[B.trackId]??0)<B.seq&&(X[B.trackId]=B.seq),F.add(B.trackId))}for(let A of F)pf(A);ri()}function Bb(D,F){le++;let A={at:Date.now(),trackId:D.trackId,seq:D.seq,id:D.event?.id??null,type:D.event?.type??null,reason:F,event:D.event??null};M.error(`[plane-link] DEAD-LETTER ${D.trackId} seq ${D.seq} (${A.type??"?"}): plane rejected it permanently (${F}) \u2014 kept in ${Oe}`),$=$.then(async()=>{await Xc.mkdir(Wt.dirname(Oe),{recursive:!0}),await Xc.appendFile(Oe,JSON.stringify(A)+`
|
|
657
|
-
`)}).catch(B=>M.error(`[plane-link] dead-letter write failed: ${B?.message||B}`))}function jb(D,F,A,B=null){if(!Array.isArray(D)||!D.length)return;let Q=new Set,ke=Date.now();for(let Se of D){let He=G.get(Se);He&&(A?(G.delete(Se),Bb(He,F),(X[He.trackId]??0)<He.seq&&(X[He.trackId]=He.seq),Q.add(He.trackId)):(He.nacks++,He.retryAt=ke+Math.max(1e3,Number(B)||T)))}for(let Se of Q)pf(Se);ri()}function Lb(D=Date.now()){let F=1/0;for(let A of G.values())A.firstSentAt<F&&(F=A.firstSentAt);return F===1/0?0:D-F}function Fb(){if(!P||!G.size)return;let D=Date.now(),F=Lb(D);if(F<j||fe!=null&&D-fe<j||Y==="http"&&Ve||Ke>D)return;xe++;let A=Math.min(O*2**(xe-1),MI);if(be==null){be=D-F,M.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(F/1e3)}s with ${G.size} event(s) in flight (${Y} door) \u2014 the plane is not answering appends`);try{I?.({since:be,unacked:G.size,oldestAgeMs:F,mode:Y})}catch(B){M.error(`[plane-link] onDegraded failed: ${B?.message||B}`)}}if(Y==="http"){Ve=!0,xt=D+A,M.error(`[plane-link] ingest circuit OPEN (stall #${xe}); probing /v1/ingest/health in ${Math.round(A/1e3)}s`);return}Ke=D+A,M.error(`[plane-link] closing the link and redialing in ${Math.round(A/1e3)}s (stall redial #${xe})`);try{v?.terminate()}catch{}}function hf(){if(!P)return;let D=Date.now();if(Y==="http"&&Ve)return;let F=[...G.values()].filter(B=>B.event&&B.retryAt<=D&&!(Y==="http"&&_r.has(B.trackId)));if(!F.length)return;F.sort((B,Q)=>B.trackId===Q.trackId?B.seq-Q.seq:B.trackId<Q.trackId?-1:1),M.log(`[plane-link] re-sending ${F.length} unacked event(s) the plane never confirmed`);let A=Y==="http"?_t:mo;if(Y==="http"){let B=new Map;for(let Q of F)(B.get(Q.trackId)??B.set(Q.trackId,[]).get(Q.trackId)).push(Q);for(let[,Q]of B){if(Ce>=py)return;ni(Q.slice(0,A).map(ke=>({trackId:ke.trackId,event:ke.event})))}return}for(let B=0;B<F.length;B+=A){let Q=F.slice(B,B+A).map(ke=>({trackId:ke.trackId,event:ke.event}));if(!ni(Q))return}}async function kM(D=null){for(;;){if(!P)return!1;let F=G.size<L,A=Y!=="http"||Ce<py&&!Ve&&(!D||!_r.has(D));if(F&&A)return!0;await new Promise(B=>setTimeout(B,100))}}function mf(){if(m=null,!P||!k.length)return;let D=k;if(k=[],Y!=="http"){for(let A=0;A<D.length;A+=mo)ni(D.slice(A,A+mo));return}let F=new Map;for(let A of D)(F.get(A.trackId)??F.set(A.trackId,[]).get(A.trackId)).push(A);for(let[A,B]of F){if(_r.has(A)||Ce>=py||Ve){k.push(...B);continue}for(let Q=0;Q<B.length;Q+=_t)ni(B.slice(Q,Q+_t))}k.length&&!m&&(m=setTimeout(mf,CI),m.unref?.())}let xM=D=>{let F=new Map;for(let{trackId:A,event:B}of D)(F.get(A)??F.set(A,[]).get(A)).push(B);return[...F].map(([A,B])=>({trackId:A,events:B}))},Io=(D,F,A,{progress:B})=>{let Q=Date.now();for(let{event:ke}of D){let Se=G.get(ke.id);Se&&(Se.nacks++,Se.retryAt=Q+Math.max(1e3,A||T))}B&&ri()};async function vM(D){let F=new Set(D.map(A=>A.trackId));for(let A of F)_r.add(A);Ce++;try{let A=await S(`${re}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:xM(D)}),signal:AbortSignal.timeout(V)}),B=(Number(A.headers.get("retry-after"))||0)*1e3;if(A.status===200){let Q=await A.json();_t=mo;for(let ke of Q.results??[]){ff(ke.acked??[]);let Se=(He,It)=>{let Vt=new Map;for(let nt of He??[])(Vt.get(nt.reason)??Vt.set(nt.reason,[]).get(nt.reason)).push(nt.id);for(let[nt,Gb]of Vt)jb(Gb,nt,It,Q.retryAfterMs??null)};Se(ke.rejected,!0),Se(ke.deferred,!1)}ff(Q.discarded??[]),ri();return}if(A.status===429){Io(D,"overloaded",B||5e3,{progress:!0});return}if(A.status===413&&D.length>1){_t=Math.max(1,Math.floor(D.length/2)),M.error(`[plane-link] ingest request too large (${D.length} events) \u2014 halving batches to ${_t}`),Io(D,"too_large",1e3,{progress:!0});return}if(A.status===413){let Q=G.get(D[0].event.id);Q&&(G.delete(Q.event.id),Bb(Q,"too_large"),(X[Q.trackId]??0)<Q.seq&&(X[Q.trackId]=Q.seq),pf(Q.trackId)),ri();return}if(A.status===410){df({reason:"removed",via:"410"}),Io(D,"http_410",6e4,{progress:!1});return}if(A.status===401||A.status===403){let{tripped:Q,holdMs:ke}=Ns();if(Q&&M.error(`[plane-link] holding every plane call for ${Math.round(ke/6e4)} min \u2014 the ingest door refused this node's token three times in a row`),Date.now()-$l>=NI){$l=Date.now();let Se="";try{Se=(await A.json())?.error??""}catch{}M.error(`[plane-link] plane refused ingest (HTTP ${A.status}${Se?`: ${Se}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}Io(D,`http_${A.status}`,Math.max(6e4,ui()),{progress:!1});return}if(A.status===400){M.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${D.length} event(s) for retry; this is a bug, not data`),Io(D,"http_400",6e4,{progress:!1});return}Io(D,`http_${A.status}`,B||5e3,{progress:!1})}catch(A){let B=A?.name==="TimeoutError"||A?.name==="AbortError";B&&D.length>1&&(_t=Math.max(1,Math.floor(D.length/2)),M.error(`[plane-link] ingest request of ${D.length} event(s) outlived its ${V} ms deadline \u2014 halving batches to ${_t}`)),Io(D,B?"timeout":"network",5e3,{progress:!1})}finally{Ce--;for(let A of F)_r.delete(A)}}async function SM(){if(Y!=="http"||!Ve||!P||Er)return;let D=Date.now();if(!(xt>D||Ps(D))){Er=!0;try{let F=await S(`${re}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(V)});if(!F.ok)throw new Error(`HTTP ${F.status}`);M.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),Ve=!1,xt=0,Ke=Date.now()+V+T+1e3;for(let A of G.values())A.retryAt=Math.min(A.retryAt,Date.now());hf()}catch(F){xe++;let A=Math.min(O*2**(xe-1),MI);xt=Date.now()+A,M.error(`[plane-link] ingest door still not answering (${F?.message||F}); next probe in ${Math.round(A/1e3)}s`)}finally{Er=!1}}}function Ub(){let D=Date.now();return{mode:Y,unacked:G.size,oldestUnackedAgeMs:Lb(D),lastProgressAt:fe,degradedSince:be,stallRedials:xe,deadLettered:le,http:Y==="http"?{inFlight:Ce,circuitOpen:Ve,nextProbeInMs:Ve?Math.max(0,xt-D):0,batchCap:_t}:null}}async function Hb(){if(!i)return;let D;try{D=await i()}catch(A){M.error("[plane-link] metrics sample failed:",A?.message||A);return}let F=null;try{F=c?.()??null}catch{}D&&at({v:lt,ch:"ctl",type:"metrics",metrics:{...D,planeLink:Ub(),...F?{delivery:F}:{}}})}function AM(){i&&(Vn&&clearInterval(Vn),Hb(),Vn=setInterval(Hb,u),Vn.unref?.())}async function TM(){let D;try{D=await o()}catch(F){M.error("[plane-link] backfill listTracks failed:",F?.message||F);return}at({v:lt,ch:"ctl",type:"tracks",ids:D.filter(F=>!F.transferredTo).map(F=>F.id).filter(F=>typeof F=="string")});for(let F of D){if(!P)return;let A;try{A=await K(F)}catch{continue}let B=Ne[F.id]??0,Q=A.filter(Se=>Se.seq>B&&typeof Se.id=="string"),ke=Y==="http"?_t:mo;for(let Se=0;Se<Q.length;Se+=ke){if(!await kM(F.id))return;let He=Q.slice(Se,Se+Math.min(ke,Y==="http"?_t:mo)).map(It=>({trackId:F.id,event:It}));if(!ni(He))return}}}function Wb(D){!P||!D?.trackId||typeof D.id!="string"||(k.push({trackId:D.trackId,event:D}),m||(m=setTimeout(mf,CI),m.unref?.()))}let oi=new Map;async function EM(D){let{reqId:F,method:A,path:B,headers:Q={},body:ke}=D,Se=Object.fromEntries(Object.entries(Q).filter(([,It])=>It!=null)),He=new AbortController;oi.set(F,He);try{if(D.host){await _M(D,Se,He);return}y&&(Se["x-bostrat-token"]=y);let It=await fetch(g+B,{method:A,headers:Se,body:ke?Buffer.from(ke,"base64"):void 0,signal:He.signal}),Vt=Object.fromEntries(It.headers);for(let nt of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Vt[nt];if(Vt["content-type"]||(Vt["content-type"]="application/octet-stream"),at({v:lt,ch:"control",type:"http_head",reqId:F,status:It.status,headers:Vt}),It.body){for await(let nt of It.body)if(!at({v:lt,ch:"control",type:"http_data",reqId:F,data:Buffer.from(nt).toString("base64")}))break}at({v:lt,ch:"control",type:"http_end",reqId:F,error:null})}finally{oi.delete(F)}}function _M(D,F,A){let{reqId:B,method:Q,path:ke,body:Se}=D,He=new URL(g);return new Promise(It=>{let Vt=kH.request({host:He.hostname,port:He.port,method:Q,path:ke,headers:{...F,host:D.host},signal:A.signal},nt=>{let{connection:Gb,"keep-alive":bG,"transfer-encoding":kG,upgrade:xG,...IM}=nt.headers;at({v:lt,ch:"control",type:"http_head",reqId:B,status:nt.statusCode||502,headers:IM}),nt.on("data",$M=>{at({v:lt,ch:"control",type:"http_data",reqId:B,data:Buffer.from($M).toString("base64")})||nt.destroy()}),nt.on("end",()=>{at({v:lt,ch:"control",type:"http_end",reqId:B,error:null}),It()}),nt.on("error",()=>{at({v:lt,ch:"control",type:"http_end",reqId:B,error:"view upstream error"}),It()})});Vt.on("error",nt=>{at({v:lt,ch:"control",type:"http_end",reqId:B,error:nt?.message||"view request failed"}),It()}),Se&&Vt.write(Buffer.from(Se,"base64")),Vt.end()})}function qb(){if(ae||ti)return;v=new dy(e,{headers:{authorization:`Bearer ${t}`}}),v.on("open",async()=>{let F=null;try{F=await s?.()||null}catch{F=null}at({v:lt,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:bn,installKind:h,...typeof w=="function"?{updatePolicy:w()}:w?{updatePolicy:w}:{},providers:SH(),...F?{agents:F}:{}})}),v.on("message",F=>{let A;try{A=JSON.parse(F)}catch{return}if(A.ch==="ctl"&&A.type==="hello_ok"){P=!0,ge=RI,Me=null,ek(),M.log(`[plane-link] connected to plane as ${n}`);try{N?.()}catch{}if(typeof A.hashKey=="string"&&A.hashKey)try{l?.(A.hashKey)}catch{}A.minNodeVersion&&bn&&Zc(bn,A.minNodeVersion)&&M.error(`[plane-link] this node is bostrat-node ${bn}; the plane asks for \u2265${A.minNodeVersion} \u2014 update with: ${kp(h)}`);try{p?.(typeof A.updatePolicy=="string"?A.updatePolicy:null)}catch{}let B=typeof A.latestNodeVersion=="string"?A.latestNodeVersion:null;if(B&&bn&&Zc(bn,B)&&!W.has(B)){W.add(B),M.log(`[plane-link] bostrat-node ${B} is available (this node runs ${bn}) \u2014 ${kp(h)}`);try{d?.({current:bn,latest:B,min:A.minNodeVersion??null,installKind:h,required:!!(A.minNodeVersion&&Zc(bn,A.minNodeVersion))})}catch(ke){M.error(`[plane-link] update notice failed: ${ke?.message||ke}`)}}let Q=A.ingest==="http";Y=U==="ws"?"ws":U==="http"||Q?"http":"ws",U==="http"&&!Q&&M.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),Y==="http"&&M.log("[plane-link] spine rides the HTTPS ingest door"),Ve=!1,xt=0,_t=mo,st&&clearInterval(st),st=setInterval(()=>{at({v:lt,ch:"ctl",type:"ping"}),Fb(),SM().catch(()=>{}),hf(),k.length&&!m&&mf()},R),st.unref?.(),AM(),TM().catch(ke=>M.error("[plane-link] backfill failed:",ke?.message||ke));return}if(A.ch==="ctl"&&A.type==="hello_refused"){let B=typeof A.minNodeVersion=="string"?A.minNodeVersion:null,Q=typeof A.latestNodeVersion=="string"?A.latestNodeVersion:null;M.error(`[plane-link] plane REFUSED this node: bostrat-node ${bn??"?"} is below its minimum ${B??"?"} \u2014 ${A.fix||kp(h)}`),ge=OI,Me=0;try{f?.({current:bn,min:B,latest:Q,fix:A.fix||kp(h),installKind:h})}catch(ke){M.error(`[plane-link] refusal handler failed: ${ke?.message||ke}`)}return}if(A.ch==="ctl"&&A.type==="removed"){at({v:lt,ch:"ctl",type:"removed_ack",nodeId:n}),df({reason:typeof A.reason=="string"?A.reason:"removed",at:Number(A.at)||Date.now(),via:"ctl"});return}if(A.ch==="ctl"&&A.type==="update_policy"){try{p?.(typeof A.mode=="string"?A.mode:null)}catch{}return}if(A.ch==="ctl"&&A.type==="usage_caps"){Wu(A.caps??{},{source:"plane",updatedAt:A.updatedAt??Date.now()}).then(B=>M.log(`[plane-link] spend caps ${Object.keys(B.caps).length?`updated (${Object.keys(B.caps).join(", ")})`:"cleared"} from plane`)).catch(B=>M.error("[plane-link] spend-caps push rejected:",B?.message||B));return}if(A.ch==="ctl"&&A.type==="transferred"&&Array.isArray(A.tracks)){Promise.resolve().then(()=>a?.(A.tracks)).catch(B=>M.error("[plane-link] transfer notice failed:",B?.message||B));return}if(A.ch==="events"&&A.type==="ack"&&Array.isArray(A.ids)){ff(A.ids);return}if(A.ch==="events"&&A.type==="nack"&&Array.isArray(A.ids)){jb(A.ids,A.reason,A.permanent===!0,A.retryAfterMs??null);return}if(A.ch==="control"&&A.type==="http_open"){EM(A).catch(B=>at({v:lt,ch:"control",type:"http_end",reqId:A.reqId,error:B?.message||"tunnel error"}));return}if(A.ch==="control"&&A.type==="http_cancel"){oi.get(A.reqId)?.abort();return}if(A.ch==="attach"&&A.type==="open"){let B=new dy(H+A.path,Array.isArray(A.protocols)?A.protocols:[],A.host?{headers:{host:A.host}}:void 0);te.set(A.streamId,B),pe.set(A.streamId,[]),B.on("open",()=>{let ke=pe.get(A.streamId)||[];pe.delete(A.streamId);for(let{buf:Se,bin:He}of ke)B.send(Se,{binary:He})}),B.on("message",(ke,Se)=>{let He=Se?ke:Buffer.from(String(ke));at({v:lt,ch:"attach",type:"data",streamId:A.streamId,data:He.toString("base64"),...Se?{bin:!0}:{}})});let Q=()=>{pe.delete(A.streamId),te.delete(A.streamId)&&at({v:lt,ch:"attach",type:"close",streamId:A.streamId})};B.on("close",Q),B.on("error",Q);return}if(A.ch==="attach"&&A.type==="data"){let B=te.get(A.streamId);if(!B)return;let Q=Buffer.from(A.data,"base64"),ke=A.bin===!0,Se=pe.get(A.streamId);Se?Se.push({buf:Q,bin:ke}):B.send(Q,{binary:ke});return}if(A.ch==="attach"&&A.type==="close"){let B=te.get(A.streamId);te.delete(A.streamId),pe.delete(A.streamId),B?.close();return}});let D=()=>{P&&M.log("[plane-link] disconnected from plane"),P=!1,st&&(clearInterval(st),st=null),Vn&&(clearInterval(Vn),Vn=null),G.clear(),X={},k=[],m&&(clearTimeout(m),m=null),_r.clear();for(let B of te.values())B.close();te.clear(),pe.clear();for(let B of oi.values())B.abort();if(oi.clear(),ae||ht)return;Me==null&&(Me=Date.now());let F=Date.now()-Me<AH,A=Math.max(F?TH+Math.random()*EH:ge,Ke-Date.now(),ui());ht=setTimeout(()=>{ht=null,qb()},A),ht.unref?.(),F||(ge=Math.min(ge*2,OI))};v.on("close",D),v.on("error",F=>{let A=/Unexpected server response: (\d+)/.exec(F?.message||"")?.[1];if(A==="410"){df({reason:"removed",via:"410"});return}if(A==="401"||A==="403"){let{tripped:B,holdMs:Q}=Ns();B?M.error(`[plane-link] holding every plane call for ${Math.round(Q/6e4)} min \u2014 three credential refusals in a row (HTTP ${A}) 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(Q/6e4)} min.`):Date.now()-$l>=NI&&($l=Date.now(),M.error(`[plane-link] plane refused this node's credentials (HTTP ${A}) \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)`))}D()})}Je.on("event",Wb),gM().then(qb);function Kb({bye:D=!0}={}){ae=!0,Je.off("event",Wb),ht&&clearTimeout(ht),Qe&&clearTimeout(Qe),st&&clearInterval(st),Vn&&clearInterval(Vn),m&&clearTimeout(m),D&&at({v:lt,ch:"ctl",type:"bye",reason:"shutdown"}),v?.close()}return{stop:()=>Kb(),status:()=>({connected:P,nodeId:n,cursor:{...Ne},inflight:G.size,...Ub(),auth:tk(),dormant:ti,removed:Dl()}),_tick:()=>{Fb(),hf()}}}hu();var vp=(()=>{try{return BH(import.meta.url)("../package.json").version||null}catch{return null}})(),jH=process.env.BOSTRAT_STATE_DIR||ft.join(process.env.HOME||PH.tmpdir(),".bostrat"),LI=ft.join(jH,"self-update"),Sp=ft.join(LI,"status.json"),LH=1440*6e4,FH=6e4,Qc=new Set(["npm-global","git-checkout"]);function FI({kind:e=bs(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:Qc.has(e)?e==="git-checkout"||UH(t)?"auto":"prompt":"off"}function UH(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var qt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function HH({kind:e,to:t,from:n=vp,statusFile:r=Sp,logFile:o=ft.join(LI,"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||ft.resolve(ft.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!Qc.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 ${qt(g)} ${y}`,p=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${qt(i)}`,f=`
|
|
658
|
+
`);iH=new Set(["claude","codex","cursor"]);qc=new Set,cH=e=>qc.has(e);lH=9e4;oI={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};bg=new Map;Tg=120;ty=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:Ro(e.hookToken),createdAt:Date.now()});hs=new Map,Ha=e=>hs.get(e)||(Pt?ue(Pt,e)?.handoff:null)||null;ry=6e4,kn=(e,t,n=15e3)=>sp("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),rp=new Map,oy=e=>rp.get(e)||null;gH=/^[A-Za-z0-9][A-Za-z0-9._/-]{0,200}$/;Zd=new Map;kH=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,xH=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,ja=G.join(Vn,"worktrees"),Ma=[Fc,Qd],Dt=new Map,vH=e=>Dt.get(e)||(Pt?Pt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,bs=e=>{let t=new Map([...Dt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Pt?ue(Pt,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)},vp=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});Zc=e=>_I(e.trackId,e.id,vp(e));Sp=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Gc=class extends Error{}});mt();import Ie from"node:path";import St from"node:fs";import _G from"node:readline";import{fileURLToPath as IG}from"node:url";import Jt from"express";import $G from"node:http";import RG from"node:https";import Il from"node:os";import{randomUUID as OG}from"node:crypto";import{WebSocketServer as MG}from"ws";mt();import oP from"node-pty";async function S0(e,t,n={}){if(!De(t)){wu(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await Fr()){wu(e,`host tmux server not running on ${Ft}`),e.close(1011,"tmux server unavailable");return}if(!await ke(t)){wu(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=v0(n.cols,120,A0),o=v0(n.rows,32,T0),s;try{s=oP.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){wu(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&em(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(em(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"&&cP(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{}iP(t)})}var sP=6,aP=250;async function iP(e,t=sP,n=aP){for(let r=0;r<t;r++){if(await jo(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var A0=20,T0=6;function cP(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=A0&&Number(t)>=T0}function v0(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function em(e,t){try{e.send(JSON.stringify(t))}catch{}}function wu(e,t){em(e,{type:"error",message:t})}function E0(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();yu();Fm();hr();import{createRequire as CG}from"node:module";var MH=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function py({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(MH)&&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}}fe();sh();mt();import DH from"node:fs/promises";import fy from"node:fs/promises";var CH=100;async function NH(e){let t;try{t=await fy.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/CH*1e3,ioBytes:await PH(e)}}async function PH(e){let t;try{t=await fy.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 CI(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 fy.readdir("/proc")}catch{return null}let r=new Map;await Promise.all(n.map(async l=>{if(!/^\d+$/.test(l))return;let u=await NH(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 BH=Number(process.env.BOSTRAT_JOB_CHECK_MS)||15*6e4,jH=Number(process.env.BOSTRAT_JOB_STALL_CHECKS)||3,LH=Number(process.env.BOSTRAT_JOB_CPU_RATE_MIN)||25,FH=Number(process.env.BOSTRAT_JOB_IO_RATE_MIN)||64*1024,UH=5*6e4,PI={readOpenJobs:tS,statFile:async e=>{try{let t=await DH.stat(e);return{size:t.size,mtimeMs:Math.round(t.mtimeMs)}}catch{return null}},updatedAt:e=>Yr(e.agent).updatedAt(e),panePids:y0,treeStats:CI,appendEvent:C},xn=new Map,NI=new Map,my=new Map,hy=(e,t,n)=>`${e}|${t}|${n}`;function DI(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 BI(e,t,n){let r=null;try{r=await n.updatedAt(e)}catch{r=null}let o=NI.get(e.name);if(o&&o.mtime===r&&t-o.at<UH)return o.jobs;let s=[];try{s=await n.readOpenJobs(e)}catch{s=[]}return NI.set(e.name,{at:t,mtime:r,jobs:s}),s}async function HH(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 WH(e,t){return e?.outputFile?await t.statFile(e.outputFile):null}function qH(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>=LH||t.ioBytes!=null&&e.ioBytes!=null&&(t.ioBytes-e.ioBytes)/n>=FH}function jI(e,t,n,r){let o=[];for(let s of n){let a=hy(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 LI(e,{now:t=Date.now(),deps:n=PI}={}){let r=new Map,o=new Set;for(let{track:s,box:a}of DI(e)){let i=await BI(a,t,n);for(let l of i)o.add(hy(s.id,a.name,l.id));let c=jI(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 KH(e,{now:t=Date.now(),deps:n=PI}={}){let r={checked:0,progressing:0,quiet:0,stalled:[]},o=DI(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 BI(i,t,n),l=new Set(c.map(w=>hy(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){my.delete(i.name);continue}let u=jI(a,i,c,t),d=await HH(i,t,n),p=my.get(i.name);my.set(i.name,d);let f=qH(p,d);for(let w of u){let m=xn.get(w);r.checked++;let g=await WH(m.job,n),y=!!g&&!!m.file&&(g.size>m.file.size||g.mtimeMs>m.file.mtimeMs),k=!!g&&!m.file;if(m.file=g??m.file,f||y||k){m.quiet=0,m.lastProgressAt=t,m.stalled=!1,r.progressing++;continue}if(m.stalled||(m.quiet++,r.quiet++,m.quiet<jH))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 FI({listTracks:e,intervalMs:t=BH}={}){let n=!1,o=setInterval(async()=>{if(!n){n=!0;try{await KH(await e())}catch(s){console.error(`[job-watch] sweep: ${s?.message||s}`)}finally{n=!1}}},t);return o.unref?.(),o}fe();Ic();Dn();Rd();Zt();var Tp=60*6e4,GH=1440*6e4,VH=new Set(["merged","closed","lost"]),Qc=(e,t,n)=>Number.isFinite(Number(e))&&n-Number(e)<=t;function zH({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)<=Fw&&s.push("ship"),r>0&&s.push("create-net");for(let a of n||[]){if(!a||VH.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&&Qc(a.ciFix.delegatedAt,Tp,o)&&s.push(`ci-fix#${l}`),a.reconcile&&Qc(a.reconcile.delegatedAt,Tp,o)&&s.push(`reconcile#${l}`),a.ciFixAwait&&Qc(a.ciFixAwait.since,Tp,o)&&s.push(`ci-fix-await#${l}`),a.reconcileAwait&&Qc(a.reconcileAwait.since,Tp,o)&&s.push(`reconcile-await#${l}`);let u=a.autoMerge?.armedAt??a.armedAt??null;a.trackId===e&&(a.autoMerge||a.armedAt)&&Qc(u,GH,o)&&s.push(`auto-merge#${l}`)}return s}var Ep=new Map;Xe.on("event",e=>{e?.trackId&&Ep.delete(e.trackId)});async function JH(e){if(Ep.has(e.id))return Ep.get(e.id);let t=await q(e).catch(()=>[]),n=Ea(t,-1/0);return Ep.set(e.id,n),n}async function UI(e,{now:t=Date.now()}={}){let n=new Map,r=await mn().catch(()=>[]);for(let o of e||[]){if(!o||je(o))continue;let s=zH({trackId:o.id,shipPending:await JH(o),links:r,observers:RT(o.id),now:t});s.length&&n.set(o.id,s)}return n}dg();import QI from"node:fs";import go from"node:fs/promises";import fW from"node:os";import wt from"node:path";import{spawn as mW}from"node:child_process";import{createRequire as hW}from"node:module";fe();Zu();import Wa from"node:fs";import tl from"node:fs/promises";import ZH from"node:http";import Kt from"node:path";import Ip from"node:os";import{createRequire as QH}from"node:module";import gy from"ws";import YH from"node:path";import XH from"node:os";var el=process.env.BOSTRAT_STATE_DIR||YH.join(process.env.HOME||XH.tmpdir(),".bostrat");function wy(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 HI(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=wy(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function WI(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}If();var vn=(()=>{try{return QH(import.meta.url)("../package.json").version||null}catch{return null}})(),by=process.env.BOSTRAT_STATE_DIR||Kt.join(process.env.HOME||Ip.tmpdir(),".bostrat"),YI=Kt.join(by,"plane-link","removed.json");function qI(e){return typeof e!="string"||e.length<8?"\u2022\u2022\u2022\u2022":`${e.slice(0,6)}\u2026${e.slice(-4)}`}function eW(e=YI){try{let t=JSON.parse(Wa.readFileSync(e,"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function ks({env:e=process.env,pkgDir:t=Kt.resolve(Kt.dirname(new URL(import.meta.url).pathname),".."),exists:n=Wa.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 nl(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 _p(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 tW({home:e=process.env.HOME||Ip.homedir(),env:t=process.env}={}){let n=r=>{try{return Wa.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 ft=1,KI=1e3,GI=3e4,nW=6e4,rW=1500,oW=500,wo=100,sW=2e3,aW=25e3,iW=6e4,XI=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},cW=XI(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),lW=3e4,VI=10*6e4,uW=400,zI=250,dW=XI(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),yy=4,pW=6e4,JI=6e4;function ZI({url:e,token:t,nodeId:n=Ip.hostname(),name:r=Ip.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,deliverySample:c=null,onHashKey:l=null,metricsMs:u=pW,onUpdateAvailable:d=null,onUpdatePolicy:p=null,onHelloRefused:f=null,updatePolicy:w=null,installKind:m=ks(),selfUrl:g=`http://127.0.0.1:${process.env.PORT||3362}`,apiToken:y=null,removedMarkerFile:k=YI,onRemoved:b=null,onDegraded:I=null,onRecovered:R=null,onConnected:N=null,ingestMode:D=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:j=cW,unackedRetryMs:T=iW,pingMs:$=aW,maxInflightEvents:F=uW,httpTimeoutMs:J=dW,stallBackoffMinMs:M=lW,fetchImpl:S=globalThis.fetch,log:_=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=WI(e);let H=g.replace(/^http/,"ws"),K=new Set,X=new Map,pe=new Map,Oe=Kt.join(by,"plane-link","cursor.json"),Me=Kt.join(by,"plane-link","dead-letter.ndjson"),Q=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),qe={},Ae={},Z=new Map,he=null,Te=null,z=0,ge=0,Ge=0,O=Promise.resolve(),Y="ws",Le=0,Qe=!1,oe=0,ye=!1,Fe=wo,jt=new Set,x=[],h=null,v=null,P=!1,ce=!1,be=KI,Ne=null,gt=null,rt=null,lt=null,Xn=null,hf=!1,Pl=0,oi=!1,Rr=eW(k);if(Rr)if(Rr.tokenPreview===qI(t))oi=!0,_f({reason:Rr.reason??"removed",at:Rr.at??null,nodeId:Rr.nodeId??n}),_.error(`[plane-link] this node was REMOVED from the plane (${Rr.reason??"removed"}${Rr.at?` at ${new Date(Rr.at).toISOString()}`:""}) \u2014 not dialing. Re-provision it with a new node token, or delete ${k} to force a dial.`);else try{Wa.unlinkSync(k),_.log("[plane-link] stale removed-marker discarded \u2014 the node token changed")}catch{}function wf({reason:B="removed",at:U=Date.now(),via:A="ctl"}={}){if(!oi){oi=!0,_f({reason:B,at:U,nodeId:n,via:A});try{Wa.mkdirSync(Kt.dirname(k),{recursive:!0}),Wa.writeFileSync(k,JSON.stringify({at:U,reason:B,via:A,nodeId:n,tokenPreview:qI(t)},null,2))}catch(L){_.error(`[plane-link] could not write ${k}: ${L?.message||L}`)}_.error(`[plane-link] the plane REMOVED this node (${B}, via ${A}) \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{b?.({reason:B,at:U,via:A})}catch{}Xb({bye:!1})}}async function NM(){try{qe=JSON.parse(await tl.readFile(Oe,"utf8"))}catch{qe={}}}function PM(){hf=!0,!rt&&(rt=setTimeout(async()=>{if(rt=null,!!hf){hf=!1;try{await tl.mkdir(Kt.dirname(Oe),{recursive:!0}),await tl.writeFile(Oe,JSON.stringify(qe))}catch(B){_.error("[plane-link] cursor flush failed:",B?.message||B)}}},sW),rt.unref?.())}function ut(B){return!v||v.readyState!==gy.OPEN?!1:(v.send(JSON.stringify(B)),!0)}function si(B){let U=Date.now();for(let{trackId:A,event:L}of B){let te=Z.get(L.id);Z.set(L.id,{trackId:A,seq:L.seq,event:L,sentAt:U,firstSentAt:te?.firstSentAt??U,retryAt:U+T,tries:(te?.tries??0)+1,nacks:te?.nacks??0})}return Y==="http"?(LM(B),!0):ut({v:ft,ch:"events",type:"append",events:B})}function DM(B){let U=null;for(let A of Z.values())A.trackId===B&&(U==null||A.seq<U)&&(U=A.seq);return U}function gf(B){let U=Ae[B];if(U==null)return;let A=DM(B),L=A==null?U:Math.min(U,A-1);L>(qe[B]??0)&&(qe[B]=L,PM())}function ai(){if(he=Date.now(),Te!=null){let B=Date.now()-Te;_.log(`[plane-link] spine sync recovered after ${Math.round(B/1e3)}s degraded (${z} stall redial(s))`);let U=Te;Te=null,z=0,ge=0,Qe=!1,oe=0;try{R?.({since:U,downMs:B})}catch(A){_.error(`[plane-link] onRecovered failed: ${A?.message||A}`)}}}function yf(B){if(!Array.isArray(B)||!B.length)return;let U=new Set;for(let A of B){let L=Z.get(A);L&&(Z.delete(A),(Ae[L.trackId]??0)<L.seq&&(Ae[L.trackId]=L.seq),U.add(L.trackId))}for(let A of U)gf(A);ai()}function Wb(B,U){Ge++;let A={at:Date.now(),trackId:B.trackId,seq:B.seq,id:B.event?.id??null,type:B.event?.type??null,reason:U,event:B.event??null};_.error(`[plane-link] DEAD-LETTER ${B.trackId} seq ${B.seq} (${A.type??"?"}): plane rejected it permanently (${U}) \u2014 kept in ${Me}`),O=O.then(async()=>{await tl.mkdir(Kt.dirname(Me),{recursive:!0}),await tl.appendFile(Me,JSON.stringify(A)+`
|
|
659
|
+
`)}).catch(L=>_.error(`[plane-link] dead-letter write failed: ${L?.message||L}`))}function qb(B,U,A,L=null){if(!Array.isArray(B)||!B.length)return;let te=new Set,xe=Date.now();for(let Ee of B){let Ve=Z.get(Ee);Ve&&(A?(Z.delete(Ee),Wb(Ve,U),(Ae[Ve.trackId]??0)<Ve.seq&&(Ae[Ve.trackId]=Ve.seq),te.add(Ve.trackId)):(Ve.nacks++,Ve.retryAt=xe+Math.max(1e3,Number(L)||T)))}for(let Ee of te)gf(Ee);ai()}function Kb(B=Date.now()){let U=1/0;for(let A of Z.values())A.firstSentAt<U&&(U=A.firstSentAt);return U===1/0?0:B-U}function Gb(){if(!P||!Z.size)return;let B=Date.now(),U=Kb(B);if(U<j||he!=null&&B-he<j||Y==="http"&&Qe||ge>B)return;z++;let A=Math.min(M*2**(z-1),VI);if(Te==null){Te=B-U,_.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(U/1e3)}s with ${Z.size} event(s) in flight (${Y} door) \u2014 the plane is not answering appends`);try{I?.({since:Te,unacked:Z.size,oldestAgeMs:U,mode:Y})}catch(L){_.error(`[plane-link] onDegraded failed: ${L?.message||L}`)}}if(Y==="http"){Qe=!0,oe=B+A,_.error(`[plane-link] ingest circuit OPEN (stall #${z}); probing /v1/ingest/health in ${Math.round(A/1e3)}s`);return}ge=B+A,_.error(`[plane-link] closing the link and redialing in ${Math.round(A/1e3)}s (stall redial #${z})`);try{v?.terminate()}catch{}}function bf(){if(!P)return;let B=Date.now();if(Y==="http"&&Qe)return;let U=[...Z.values()].filter(L=>L.event&&L.retryAt<=B&&!(Y==="http"&&jt.has(L.trackId)));if(!U.length)return;U.sort((L,te)=>L.trackId===te.trackId?L.seq-te.seq:L.trackId<te.trackId?-1:1),_.log(`[plane-link] re-sending ${U.length} unacked event(s) the plane never confirmed`);let A=Y==="http"?Fe:wo;if(Y==="http"){let L=new Map;for(let te of U)(L.get(te.trackId)??L.set(te.trackId,[]).get(te.trackId)).push(te);for(let[,te]of L){if(Le>=yy)return;si(te.slice(0,A).map(xe=>({trackId:xe.trackId,event:xe.event})))}return}for(let L=0;L<U.length;L+=A){let te=U.slice(L,L+A).map(xe=>({trackId:xe.trackId,event:xe.event}));if(!si(te))return}}async function BM(B=null){for(;;){if(!P)return!1;let U=Z.size<F,A=Y!=="http"||Le<yy&&!Qe&&(!B||!jt.has(B));if(U&&A)return!0;await new Promise(L=>setTimeout(L,100))}}function kf(){if(h=null,!P||!x.length)return;let B=x;if(x=[],Y!=="http"){for(let A=0;A<B.length;A+=wo)si(B.slice(A,A+wo));return}let U=new Map;for(let A of B)(U.get(A.trackId)??U.set(A.trackId,[]).get(A.trackId)).push(A);for(let[A,L]of U){if(jt.has(A)||Le>=yy||Qe){x.push(...L);continue}for(let te=0;te<L.length;te+=Fe)si(L.slice(te,te+Fe))}x.length&&!h&&(h=setTimeout(kf,zI),h.unref?.())}let jM=B=>{let U=new Map;for(let{trackId:A,event:L}of B)(U.get(A)??U.set(A,[]).get(A)).push(L);return[...U].map(([A,L])=>({trackId:A,events:L}))},$o=(B,U,A,{progress:L})=>{let te=Date.now();for(let{event:xe}of B){let Ee=Z.get(xe.id);Ee&&(Ee.nacks++,Ee.retryAt=te+Math.max(1e3,A||T))}L&&ai()};async function LM(B){let U=new Set(B.map(A=>A.trackId));for(let A of U)jt.add(A);Le++;try{let A=await S(`${Q}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:jM(B)}),signal:AbortSignal.timeout(J)}),L=(Number(A.headers.get("retry-after"))||0)*1e3;if(A.status===200){let te=await A.json();Fe=wo;for(let xe of te.results??[]){yf(xe.acked??[]);let Ee=(Ve,$t)=>{let Yt=new Map;for(let st of Ve??[])(Yt.get(st.reason)??Yt.set(st.reason,[]).get(st.reason)).push(st.id);for(let[st,Zb]of Yt)qb(Zb,st,$t,te.retryAfterMs??null)};Ee(xe.rejected,!0),Ee(xe.deferred,!1)}yf(te.discarded??[]),ai();return}if(A.status===429){$o(B,"overloaded",L||5e3,{progress:!0});return}if(A.status===413&&B.length>1){Fe=Math.max(1,Math.floor(B.length/2)),_.error(`[plane-link] ingest request too large (${B.length} events) \u2014 halving batches to ${Fe}`),$o(B,"too_large",1e3,{progress:!0});return}if(A.status===413){let te=Z.get(B[0].event.id);te&&(Z.delete(te.event.id),Wb(te,"too_large"),(Ae[te.trackId]??0)<te.seq&&(Ae[te.trackId]=te.seq),gf(te.trackId)),ai();return}if(A.status===410){wf({reason:"removed",via:"410"}),$o(B,"http_410",6e4,{progress:!1});return}if(A.status===401||A.status===403){let{tripped:te,holdMs:xe}=Ps();if(te&&_.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()-Pl>=JI){Pl=Date.now();let Ee="";try{Ee=(await A.json())?.error??""}catch{}_.error(`[plane-link] plane refused ingest (HTTP ${A.status}${Ee?`: ${Ee}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}$o(B,`http_${A.status}`,Math.max(6e4,fi()),{progress:!1});return}if(A.status===400){_.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${B.length} event(s) for retry; this is a bug, not data`),$o(B,"http_400",6e4,{progress:!1});return}$o(B,`http_${A.status}`,L||5e3,{progress:!1})}catch(A){let L=A?.name==="TimeoutError"||A?.name==="AbortError";L&&B.length>1&&(Fe=Math.max(1,Math.floor(B.length/2)),_.error(`[plane-link] ingest request of ${B.length} event(s) outlived its ${J} ms deadline \u2014 halving batches to ${Fe}`)),$o(B,L?"timeout":"network",5e3,{progress:!1})}finally{Le--;for(let A of U)jt.delete(A)}}async function FM(){if(Y!=="http"||!Qe||!P||ye)return;let B=Date.now();if(!(oe>B||Ds(B))){ye=!0;try{let U=await S(`${Q}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(J)});if(!U.ok)throw new Error(`HTTP ${U.status}`);_.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),Qe=!1,oe=0,ge=Date.now()+J+T+1e3;for(let A of Z.values())A.retryAt=Math.min(A.retryAt,Date.now());bf()}catch(U){z++;let A=Math.min(M*2**(z-1),VI);oe=Date.now()+A,_.error(`[plane-link] ingest door still not answering (${U?.message||U}); next probe in ${Math.round(A/1e3)}s`)}finally{ye=!1}}}function Vb(){let B=Date.now();return{mode:Y,unacked:Z.size,oldestUnackedAgeMs:Kb(B),lastProgressAt:he,degradedSince:Te,stallRedials:z,deadLettered:Ge,http:Y==="http"?{inFlight:Le,circuitOpen:Qe,nextProbeInMs:Qe?Math.max(0,oe-B):0,batchCap:Fe}:null}}async function zb(){if(!i)return;let B;try{B=await i()}catch(A){_.error("[plane-link] metrics sample failed:",A?.message||A);return}let U=null;try{U=c?.()??null}catch{}B&&ut({v:ft,ch:"ctl",type:"metrics",metrics:{...B,planeLink:Vb(),...U?{delivery:U}:{}}})}function UM(){i&&(Xn&&clearInterval(Xn),zb(),Xn=setInterval(zb,u),Xn.unref?.())}async function HM(){let B;try{B=await o()}catch(U){_.error("[plane-link] backfill listTracks failed:",U?.message||U);return}ut({v:ft,ch:"ctl",type:"tracks",ids:B.filter(U=>!U.transferredTo).map(U=>U.id).filter(U=>typeof U=="string")});for(let U of B){if(!P)return;let A;try{A=await q(U)}catch{continue}let L=qe[U.id]??0,te=A.filter(Ee=>Ee.seq>L&&typeof Ee.id=="string"),xe=Y==="http"?Fe:wo;for(let Ee=0;Ee<te.length;Ee+=xe){if(!await BM(U.id))return;let Ve=te.slice(Ee,Ee+Math.min(xe,Y==="http"?Fe:wo)).map($t=>({trackId:U.id,event:$t}));if(!si(Ve))return}}}function Jb(B){!P||!B?.trackId||typeof B.id!="string"||(x.push({trackId:B.trackId,event:B}),h||(h=setTimeout(kf,zI),h.unref?.()))}let ii=new Map;async function WM(B){let{reqId:U,method:A,path:L,headers:te={},body:xe}=B,Ee=Object.fromEntries(Object.entries(te).filter(([,$t])=>$t!=null)),Ve=new AbortController;ii.set(U,Ve);try{if(B.host){await qM(B,Ee,Ve);return}y&&(Ee["x-bostrat-token"]=y);let $t=await fetch(g+L,{method:A,headers:Ee,body:xe?Buffer.from(xe,"base64"):void 0,signal:Ve.signal}),Yt=Object.fromEntries($t.headers);for(let st of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Yt[st];if(Yt["content-type"]||(Yt["content-type"]="application/octet-stream"),ut({v:ft,ch:"control",type:"http_head",reqId:U,status:$t.status,headers:Yt}),$t.body){for await(let st of $t.body)if(!ut({v:ft,ch:"control",type:"http_data",reqId:U,data:Buffer.from(st).toString("base64")}))break}ut({v:ft,ch:"control",type:"http_end",reqId:U,error:null})}finally{ii.delete(U)}}function qM(B,U,A){let{reqId:L,method:te,path:xe,body:Ee}=B,Ve=new URL(g);return new Promise($t=>{let Yt=ZH.request({host:Ve.hostname,port:Ve.port,method:te,path:xe,headers:{...U,host:B.host},signal:A.signal},st=>{let{connection:Zb,"keep-alive":XG,"transfer-encoding":ZG,upgrade:QG,...KM}=st.headers;ut({v:ft,ch:"control",type:"http_head",reqId:L,status:st.statusCode||502,headers:KM}),st.on("data",GM=>{ut({v:ft,ch:"control",type:"http_data",reqId:L,data:Buffer.from(GM).toString("base64")})||st.destroy()}),st.on("end",()=>{ut({v:ft,ch:"control",type:"http_end",reqId:L,error:null}),$t()}),st.on("error",()=>{ut({v:ft,ch:"control",type:"http_end",reqId:L,error:"view upstream error"}),$t()})});Yt.on("error",st=>{ut({v:ft,ch:"control",type:"http_end",reqId:L,error:st?.message||"view request failed"}),$t()}),Ee&&Yt.write(Buffer.from(Ee,"base64")),Yt.end()})}function Yb(){if(ce||oi)return;v=new gy(e,{headers:{authorization:`Bearer ${t}`}}),v.on("open",async()=>{let U=null;try{U=await s?.()||null}catch{U=null}ut({v:ft,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:vn,installKind:m,...typeof w=="function"?{updatePolicy:w()}:w?{updatePolicy:w}:{},providers:tW(),...U?{agents:U}:{}})}),v.on("message",U=>{let A;try{A=JSON.parse(U)}catch{return}if(A.ch==="ctl"&&A.type==="hello_ok"){P=!0,be=KI,Ne=null,ak(),_.log(`[plane-link] connected to plane as ${n}`);try{N?.()}catch{}if(typeof A.hashKey=="string"&&A.hashKey)try{l?.(A.hashKey)}catch{}A.minNodeVersion&&vn&&nl(vn,A.minNodeVersion)&&_.error(`[plane-link] this node is bostrat-node ${vn}; the plane asks for \u2265${A.minNodeVersion} \u2014 update with: ${_p(m)}`);try{p?.(typeof A.updatePolicy=="string"?A.updatePolicy:null)}catch{}let L=typeof A.latestNodeVersion=="string"?A.latestNodeVersion:null;if(L&&vn&&nl(vn,L)&&!K.has(L)){K.add(L),_.log(`[plane-link] bostrat-node ${L} is available (this node runs ${vn}) \u2014 ${_p(m)}`);try{d?.({current:vn,latest:L,min:A.minNodeVersion??null,installKind:m,required:!!(A.minNodeVersion&&nl(vn,A.minNodeVersion))})}catch(xe){_.error(`[plane-link] update notice failed: ${xe?.message||xe}`)}}let te=A.ingest==="http";Y=D==="ws"?"ws":D==="http"||te?"http":"ws",D==="http"&&!te&&_.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),Y==="http"&&_.log("[plane-link] spine rides the HTTPS ingest door"),Qe=!1,oe=0,Fe=wo,lt&&clearInterval(lt),lt=setInterval(()=>{ut({v:ft,ch:"ctl",type:"ping"}),Gb(),FM().catch(()=>{}),bf(),x.length&&!h&&kf()},$),lt.unref?.(),UM(),HM().catch(xe=>_.error("[plane-link] backfill failed:",xe?.message||xe));return}if(A.ch==="ctl"&&A.type==="hello_refused"){let L=typeof A.minNodeVersion=="string"?A.minNodeVersion:null,te=typeof A.latestNodeVersion=="string"?A.latestNodeVersion:null;_.error(`[plane-link] plane REFUSED this node: bostrat-node ${vn??"?"} is below its minimum ${L??"?"} \u2014 ${A.fix||_p(m)}`),be=GI,Ne=0;try{f?.({current:vn,min:L,latest:te,fix:A.fix||_p(m),installKind:m})}catch(xe){_.error(`[plane-link] refusal handler failed: ${xe?.message||xe}`)}return}if(A.ch==="ctl"&&A.type==="removed"){ut({v:ft,ch:"ctl",type:"removed_ack",nodeId:n}),wf({reason:typeof A.reason=="string"?A.reason:"removed",at:Number(A.at)||Date.now(),via:"ctl"});return}if(A.ch==="ctl"&&A.type==="update_policy"){try{p?.(typeof A.mode=="string"?A.mode:null)}catch{}return}if(A.ch==="ctl"&&A.type==="usage_caps"){Xu(A.caps??{},{source:"plane",updatedAt:A.updatedAt??Date.now()}).then(L=>_.log(`[plane-link] spend caps ${Object.keys(L.caps).length?`updated (${Object.keys(L.caps).join(", ")})`:"cleared"} from plane`)).catch(L=>_.error("[plane-link] spend-caps push rejected:",L?.message||L));return}if(A.ch==="ctl"&&A.type==="transferred"&&Array.isArray(A.tracks)){Promise.resolve().then(()=>a?.(A.tracks)).catch(L=>_.error("[plane-link] transfer notice failed:",L?.message||L));return}if(A.ch==="events"&&A.type==="ack"&&Array.isArray(A.ids)){yf(A.ids);return}if(A.ch==="events"&&A.type==="nack"&&Array.isArray(A.ids)){qb(A.ids,A.reason,A.permanent===!0,A.retryAfterMs??null);return}if(A.ch==="control"&&A.type==="http_open"){WM(A).catch(L=>ut({v:ft,ch:"control",type:"http_end",reqId:A.reqId,error:L?.message||"tunnel error"}));return}if(A.ch==="control"&&A.type==="http_cancel"){ii.get(A.reqId)?.abort();return}if(A.ch==="attach"&&A.type==="open"){let L=new gy(H+A.path,Array.isArray(A.protocols)?A.protocols:[],A.host?{headers:{host:A.host}}:void 0);X.set(A.streamId,L),pe.set(A.streamId,[]),L.on("open",()=>{let xe=pe.get(A.streamId)||[];pe.delete(A.streamId);for(let{buf:Ee,bin:Ve}of xe)L.send(Ee,{binary:Ve})}),L.on("message",(xe,Ee)=>{let Ve=Ee?xe:Buffer.from(String(xe));ut({v:ft,ch:"attach",type:"data",streamId:A.streamId,data:Ve.toString("base64"),...Ee?{bin:!0}:{}})});let te=()=>{pe.delete(A.streamId),X.delete(A.streamId)&&ut({v:ft,ch:"attach",type:"close",streamId:A.streamId})};L.on("close",te),L.on("error",te);return}if(A.ch==="attach"&&A.type==="data"){let L=X.get(A.streamId);if(!L)return;let te=Buffer.from(A.data,"base64"),xe=A.bin===!0,Ee=pe.get(A.streamId);Ee?Ee.push({buf:te,bin:xe}):L.send(te,{binary:xe});return}if(A.ch==="attach"&&A.type==="close"){let L=X.get(A.streamId);X.delete(A.streamId),pe.delete(A.streamId),L?.close();return}});let B=()=>{P&&_.log("[plane-link] disconnected from plane"),P=!1,lt&&(clearInterval(lt),lt=null),Xn&&(clearInterval(Xn),Xn=null),Z.clear(),Ae={},x=[],h&&(clearTimeout(h),h=null),jt.clear();for(let L of X.values())L.close();X.clear(),pe.clear();for(let L of ii.values())L.abort();if(ii.clear(),ce||gt)return;Ne==null&&(Ne=Date.now());let U=Date.now()-Ne<nW,A=Math.max(U?rW+Math.random()*oW:be,ge-Date.now(),fi());gt=setTimeout(()=>{gt=null,Yb()},A),gt.unref?.(),U||(be=Math.min(be*2,GI))};v.on("close",B),v.on("error",U=>{let A=/Unexpected server response: (\d+)/.exec(U?.message||"")?.[1];if(A==="410"){wf({reason:"removed",via:"410"});return}if(A==="401"||A==="403"){let{tripped:L,holdMs:te}=Ps();L?_.error(`[plane-link] holding every plane call for ${Math.round(te/6e4)} min \u2014 three credential refusals in a row (HTTP ${A}) 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()-Pl>=JI&&(Pl=Date.now(),_.error(`[plane-link] plane refused this node's credentials (HTTP ${A}) \u2014 the token was revoked or is unknown to the plane; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN (still retrying)`))}B()})}Xe.on("event",Jb),NM().then(Yb);function Xb({bye:B=!0}={}){ce=!0,Xe.off("event",Jb),gt&&clearTimeout(gt),rt&&clearTimeout(rt),lt&&clearInterval(lt),Xn&&clearInterval(Xn),h&&clearTimeout(h),B&&ut({v:ft,ch:"ctl",type:"bye",reason:"shutdown"}),v?.close()}return{stop:()=>Xb(),status:()=>({connected:P,nodeId:n,cursor:{...qe},inflight:Z.size,...Vb(),auth:ik(),dormant:oi,removed:Hl()}),_tick:()=>{Gb(),bf()}}}bu();var $p=(()=>{try{return hW(import.meta.url)("../package.json").version||null}catch{return null}})(),wW=process.env.BOSTRAT_STATE_DIR||wt.join(process.env.HOME||fW.tmpdir(),".bostrat"),e$=wt.join(wW,"self-update"),Rp=wt.join(e$,"status.json"),gW=1440*6e4,yW=6e4,rl=new Set(["npm-global","git-checkout"]);function t$({kind:e=ks(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:rl.has(e)?e==="git-checkout"||bW(t)?"auto":"prompt":"off"}function bW(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var Gt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function kW({kind:e,to:t,from:n=$p,statusFile:r=Rp,logFile:o=wt.join(e$,"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||wt.resolve(wt.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!rl.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=`
|
|
658
660
|
health() {
|
|
659
661
|
for i in $(seq 1 45); do
|
|
660
662
|
sleep 1
|
|
@@ -665,20 +667,20 @@ health() {
|
|
|
665
667
|
# bostrat-node self-update one-shot \u2014 written by agent/src/self-update.js.
|
|
666
668
|
# ${n??"?"} \u2192 ${t} (${e}). Status: ${r}
|
|
667
669
|
set -u
|
|
668
|
-
FROM=${
|
|
669
|
-
TO=${
|
|
670
|
-
STATUS_FILE=${
|
|
670
|
+
FROM=${Gt(n??"")}
|
|
671
|
+
TO=${Gt(t)}
|
|
672
|
+
STATUS_FILE=${Gt(r)}
|
|
671
673
|
STARTED_AT=${Number(u)}
|
|
672
674
|
write_status() { # <status> [error]
|
|
673
675
|
local err="\${2:-}"
|
|
674
676
|
err="\${err//\\\\/\\\\\\\\}"; err="\${err//\\"/\\\\\\"}"
|
|
675
677
|
printf '{"status":"%s","from":"%s","to":"%s","kind":"%s","startedAt":%s,"updatedAt":%s%s}\\n' \\
|
|
676
|
-
"$1" "$FROM" "$TO" ${
|
|
678
|
+
"$1" "$FROM" "$TO" ${Gt(e)} "$STARTED_AT" "$(date +%s000)" \\
|
|
677
679
|
"\${err:+,\\"error\\":\\"$err\\"}" > "$STATUS_FILE.tmp" && mv "$STATUS_FILE.tmp" "$STATUS_FILE"
|
|
678
680
|
}
|
|
679
681
|
${f}
|
|
680
|
-
`;if(e==="npm-global"){let g=a?
|
|
681
|
-
`:"",
|
|
682
|
+
`;if(e==="npm-global"){let g=a?wt.join(a,"bin","npm"):"npm",y=a?`export PATH=${Gt(wt.join(a,"bin"))}:"$PATH"
|
|
683
|
+
`:"",k=a?` --prefix ${Gt(a)}`:"",b=I=>`${Gt(g)} install -g "bostrat-node@${I}" --no-audit --no-fund${k}`;return w+y+`
|
|
682
684
|
${d("updating",'"" ')}
|
|
683
685
|
if ! ${b("$TO")}; then
|
|
684
686
|
${d("failed",'"npm install failed"')}
|
|
@@ -701,83 +703,83 @@ if [ -n "$FROM" ] && ${b("$FROM")} && ${p} && health; then
|
|
|
701
703
|
fi
|
|
702
704
|
${d("failed",'"agent did not answer after the update AND the rollback"')}
|
|
703
705
|
exit 1
|
|
704
|
-
`}let
|
|
705
|
-
LIVE=${
|
|
706
|
+
`}let m=g=>`( cd ${Gt(l)} && git fetch -q origin main --tags && git show origin/main:scripts/deploy-pi.sh > "$TMP_SCRIPT" && bash "$TMP_SCRIPT" "v${g}" )`;return w+`
|
|
707
|
+
LIVE=${Gt(l)}
|
|
706
708
|
TMP_SCRIPT="$(mktemp)"
|
|
707
709
|
trap 'rm -f "$TMP_SCRIPT"' EXIT
|
|
708
710
|
# "restarting" is written BEFORE deploy-pi.sh runs (it does fetch \u2192 build \u2192
|
|
709
711
|
# restart \u2192 health in one call): the agent it restarts settles "done" on boot,
|
|
710
712
|
# and nothing here may write after that boot or the settle would repeat.
|
|
711
713
|
${d("restarting")}
|
|
712
|
-
if ${
|
|
714
|
+
if ${m("$TO")}; then
|
|
713
715
|
exit 0
|
|
714
716
|
fi
|
|
715
717
|
${d("rolling-back",'"deploy-pi.sh v$TO failed"')}
|
|
716
|
-
if [ -n "$FROM" ] && ${
|
|
718
|
+
if [ -n "$FROM" ] && ${m("$FROM")}; then
|
|
717
719
|
${d("rolled-back",'"deploy-pi.sh v$TO failed; rolled back to v$FROM"')}
|
|
718
720
|
exit 1
|
|
719
721
|
fi
|
|
720
722
|
${d("failed",'"deploy-pi.sh v$TO failed AND the rollback failed"')}
|
|
721
723
|
exit 1
|
|
722
|
-
`}async function el(e=Sp){try{return JSON.parse(await wo.readFile(e,"utf8"))}catch{return null}}var WH=new Set(["updating","restarting","rolling-back"]),Ap=e=>!!(e&&WH.has(e.status));async function Tp({to:e,from:t=vp,kind:n=bs(),reason:r="manual",statusFile:o=Sp,spawnImpl:s=DH,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!Qc.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!Zc(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await el(o);if(Ap(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 wo.mkdir(ft.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?ph(await qH())||ft.resolve(ft.dirname(process.execPath),".."):null,d=HH({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),p=ft.join(ft.dirname(o),`update-${e}.sh`),f=ft.join(ft.dirname(o),"last.log");await wo.writeFile(p,d,{mode:448}),await wo.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let w=jI.existsSync("/run/systemd/system")&&await KH("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",p]]:["setsid",["-f","bash","-c",`exec bash ${qt(p)} >> ${qt(f)} 2>&1`]];try{s(w[0],w[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(h){return await wo.writeFile(o,JSON.stringify({status:"failed",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a(),error:`could not spawn updater: ${h?.message||h}`})),{ok:!1,reason:`could not spawn updater: ${h?.message||h}`}}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 qH(){try{return await wo.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function KH(e){for(let t of String(process.env.PATH||"").split(ft.delimiter))try{return await wo.access(ft.join(t,e),jI.constants.X_OK),!0}catch{}return!1}var GH=45*6e4;async function UI({statusFile:e=Sp,current:t=vp,now:n=Date.now,staleMs:r=GH}={}){let o=await el(e);if(!o)return null;let s=a=>wo.writeFile(e,JSON.stringify(a)).catch(()=>{});if(Ap(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 HI({isBusy:e,start:t=Tp,onStuck:n=null,onStarted:r=null,tickMs:o=FH,stuckMs:s=LH,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 h=await t({to:w.to,from:w.from,kind:w.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${h.ok?"started":`not started: ${h.reason}`}`);try{await r?.({...w,result:h})}catch{}};return{arm({to:f,from:w=vp,kind:h=bs()}){return u?.to===f?!1:(d(),u={to:f,from:w,kind:h,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}}}he();mt();var WI=(...e)=>fetch(...e);function qI(e=process.env){return!!(Re(e)&&e.BOSTRAT_PLANE_TOKEN)}async function hy(e=process.env){if(!qI(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 WI(`${Re(e)}/v1/circuits`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN}`},signal:t.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return Array.isArray(o.circuits)?o.circuits:[]}finally{clearTimeout(n)}}async function my(e=process.env){if(!qI(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 WI(`${Re(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 Ua(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await hy(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 Ep(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}})}he();mt();var zH=(...e)=>fetch(...e);async function _p(e,t,n,r=process.env){if(!(Re(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 zH(`${Re(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 VH(e,t=process.env){return _p("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function KI({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await VH(t,o):await Ua(e,o),a=await _p("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function wy(e,t=process.env){let n=await Ua(e,t),r=await _p("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function gy(e,t=process.env){return(await _p("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function GI(e,t,n,r=process.env){let{blueprint:o,items:s}=await wy(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}}he();mt();var JH=(...e)=>fetch(...e);async function YH(e,t,n,r=process.env){if(!(Re(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 JH(`${Re(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 zI=72,yy=160;async function VI(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>zI)throw new Error(`the title is the choice as a LABEL \u2014 ${zI} 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 ${yy} chars) a reader can act on without opening the record`);if(c.length>yy)throw new Error(`the summary is one sentence \u2014 ${yy} 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 JI(e,t=process.env){return YH("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}Zw();he();import al from"node:fs/promises";import Dp from"node:path";import UW from"node:os";Nr();import XH from"node:fs/promises";import YI from"node:path";import ZH from"node:os";var XI=()=>YI.join(process.env.BOSTRAT_STATE_DIR||YI.join(process.env.HOME||ZH.tmpdir(),".bostrat"),"integrations.json");async function Ar(){try{return JSON.parse(await XH.readFile(XI(),"utf8"))||{}}catch{return{}}}var Ip=["linked","prOpened","prMerged","create"],by=["prompt","auto","off"],QH={create:["prompt","off"]},ZI=e=>QH[e]??by;function $p(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of Ip){let s=ZI(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 ks(){let e=await Ar(),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:$p(e),defaultTeam:n}:null}async function Rp(){let e=await Ar(),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:$p(e)}}async function ky(){return $p(await Ar())}async function QI(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${Ip.join(", ")}`);for(let[r,o]of t){if(!Ip.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Ip.join(", ")}`);let s=ZI(r);if(!s.includes(o))throw new Error(s.length<by.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${by.join(", ")}`)}let n=await Ar();return n.ticketFlow={...$p(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Rt(XI(),n,{mode:384}),n.ticketFlow}async function tl(){let e=(await Ar()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function eW(){let e=(await Ar()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var tW={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Op(){let t=(await Ar()).voiceChat||{},n=t.provider||"openai",r=tW[n];if(!r)return null;let o=n==="openai"?await eW():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function e$(){let e=(await Ar()).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 xs(){let e=(await Ar()).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}}he();Dr();Gr();Ea();import rl from"node:fs/promises";import l$ from"node:path";import EW from"node:os";import{randomUUID as u$}from"node:crypto";var vy={};wf(vy,{createComment:()=>uW,findIssueUrl:()=>sW,issueStates:()=>fW,mdToAdf:()=>r$,moveIssue:()=>hW,parseIssueRef:()=>oW,resolveIssue:()=>aW,uploadImage:()=>dW,upsertCard:()=>iW,whoami:()=>xy});var Ha=e=>String(e.baseUrl||"").replace(/\/$/,""),n$=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function vs(e,t,n,r){let o=await fetch(`${Ha(e)}${n}`,{method:t,headers:{authorization:n$(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 nW=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,rW=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function oW(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(nW);return r?r[1].toUpperCase():null}let n=t.match(rW);return n?n[1]:null}function sW(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 aW(e,t){let n;try{n=await vs(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:`${Ha(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function iW(e,t,{url:n,title:r,subtitle:o}){await vs(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 cW=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function lW(e){let t=[],n=0;for(let r of e.matchAll(cW)){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 r$(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
723
|
-
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...
|
|
724
|
+
`}async function ol(e=Rp){try{return JSON.parse(await go.readFile(e,"utf8"))}catch{return null}}var xW=new Set(["updating","restarting","rolling-back"]),Op=e=>!!(e&&xW.has(e.status));async function Mp({to:e,from:t=$p,kind:n=ks(),reason:r="manual",statusFile:o=Rp,spawnImpl:s=mW,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!rl.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!nl(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await ol(o);if(Op(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 go.mkdir(wt.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?gm(await vW())||wt.resolve(wt.dirname(process.execPath),".."):null,d=kW({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),p=wt.join(wt.dirname(o),`update-${e}.sh`),f=wt.join(wt.dirname(o),"last.log");await go.writeFile(p,d,{mode:448}),await go.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let w=QI.existsSync("/run/systemd/system")&&await SW("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 go.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 vW(){try{return await go.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function SW(e){for(let t of String(process.env.PATH||"").split(wt.delimiter))try{return await go.access(wt.join(t,e),QI.constants.X_OK),!0}catch{}return!1}var AW=45*6e4;async function n$({statusFile:e=Rp,current:t=$p,now:n=Date.now,staleMs:r=AW}={}){let o=await ol(e);if(!o)return null;let s=a=>go.writeFile(e,JSON.stringify(a)).catch(()=>{});if(Op(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 r$({isBusy:e,start:t=Mp,onStuck:n=null,onStarted:r=null,tickMs:o=yW,stuckMs:s=gW,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=$p,kind:m=ks()}){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}}}fe();yt();var o$=(...e)=>fetch(...e);function s$(e=process.env){return!!(Ce(e)&&e.BOSTRAT_PLANE_TOKEN)}async function ky(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 o$(`${Ce(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 xy(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 o$(`${Ce(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 qa(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await ky(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 Cp(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}})}fe();yt();var TW=(...e)=>fetch(...e);async function Np(e,t,n,r=process.env){if(!(Ce(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 TW(`${Ce(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 EW(e,t=process.env){return Np("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 EW(t,o):await qa(e,o),a=await Np("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function vy(e,t=process.env){let n=await qa(e,t),r=await Np("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function Sy(e,t=process.env){return(await Np("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function i$(e,t,n,r=process.env){let{blueprint:o,items:s}=await vy(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}}fe();yt();var _W=(...e)=>fetch(...e);async function IW(e,t,n,r=process.env){if(!(Ce(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 _W(`${Ce(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 c$=72,Ay=160;async function l$(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>c$)throw new Error(`the title is the choice as a LABEL \u2014 ${c$} 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 ${Ay} chars) a reader can act on without opening the record`);if(c.length>Ay)throw new Error(`the summary is one sentence \u2014 ${Ay} 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 u$(e,t=process.env){return IW("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}rg();fe();import ul from"node:fs/promises";import Wp from"node:path";import bq from"node:os";Dr();import $W from"node:fs/promises";import d$ from"node:path";import RW from"node:os";var p$=()=>d$.join(process.env.BOSTRAT_STATE_DIR||d$.join(process.env.HOME||RW.tmpdir(),".bostrat"),"integrations.json");async function Ir(){try{return JSON.parse(await $W.readFile(p$(),"utf8"))||{}}catch{return{}}}var Pp=["linked","prOpened","prMerged","create"],Ty=["prompt","auto","off"],OW={create:["prompt","off"]},f$=e=>OW[e]??Ty;function Dp(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of Pp){let s=f$(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 xs(){let e=await Ir(),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:Dp(e),defaultTeam:n}:null}async function Bp(){let e=await Ir(),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:Dp(e)}}async function Ey(){return Dp(await Ir())}async function m$(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${Pp.join(", ")}`);for(let[r,o]of t){if(!Pp.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Pp.join(", ")}`);let s=f$(r);if(!s.includes(o))throw new Error(s.length<Ty.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Ty.join(", ")}`)}let n=await Ir();return n.ticketFlow={...Dp(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Ot(p$(),n,{mode:384}),n.ticketFlow}async function sl(){let e=(await Ir()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function MW(){let e=(await Ir()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var CW={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function jp(){let t=(await Ir()).voiceChat||{},n=t.provider||"openai",r=CW[n];if(!r)return null;let o=n==="openai"?await MW():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function h$(){let e=(await Ir()).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 vs(){let e=(await Ir()).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}}fe();jr();zr();$a();import il from"node:fs/promises";import A$ from"node:path";import oq from"node:os";import{randomUUID as T$}from"node:crypto";var Iy={};xf(Iy,{createComment:()=>HW,findIssueUrl:()=>BW,issueStates:()=>KW,mdToAdf:()=>y$,moveIssue:()=>GW,parseIssueRef:()=>DW,resolveIssue:()=>jW,uploadImage:()=>WW,upsertCard:()=>LW,whoami:()=>_y});var Ka=e=>String(e.baseUrl||"").replace(/\/$/,""),g$=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function Ss(e,t,n,r){let o=await fetch(`${Ka(e)}${n}`,{method:t,headers:{authorization:g$(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 NW=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,PW=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function DW(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(NW);return r?r[1].toUpperCase():null}let n=t.match(PW);return n?n[1]:null}function BW(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 jW(e,t){let n;try{n=await Ss(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 LW(e,t,{url:n,title:r,subtitle:o}){await Ss(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 FW=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function UW(e){let t=[],n=0;for(let r of e.matchAll(FW)){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 y$(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(...UW(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 HW(e,t,n){let r=await Ss(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/comment`,{body:y$(n)});return r?.id?`${Ka(e)}/browse/${t.key}?focusedCommentId=${r.id}`:null}async function _y(e){let t=await Ss(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 WW(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:g$(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 qW={new:"unstarted",indeterminate:"started",done:"completed"},w$=e=>qW[e?.key]??"started";async function KW(e,t){let n=encodeURIComponent(t.key),[r,o]=await Promise.all([Ss(e,"GET",`/rest/api/3/issue/${n}?fields=status`),Ss(e,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:w$(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:w$(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function GW(e,t,n){await Ss(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/transitions`,{transition:{id:String(n)}})}var My={};xf(My,{createComment:()=>eq,createIssue:()=>Oy,createProject:()=>Ry,findIssueUrl:()=>XW,issueStates:()=>nq,moveIssue:()=>rq,parseIssueRef:()=>YW,resolveIssue:()=>ZW,teams:()=>Ga,uploadImage:()=>tq,upsertCard:()=>QW,whoami:()=>$y});var VW=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function Jn(e,t,n={}){let r=await fetch(VW(),{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 zW=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,JW=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function YW(e){let t=String(e||"").trim(),n=t.match(zW);if(n)return n[1].toUpperCase();let r=t.match(JW);return r?r[1]:null}function XW(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 ZW(e,t){let r=(await Jn(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 QW(e,t,{url:n,title:r,subtitle:o}){let s=await Jn(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 eq(e,t,n){let r=await Jn(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 $y(e){let t=await Jn(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 tq(e,{filename:t,contentType:n,buffer:r}){let o=await Jn(e,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
724
726
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
725
727
|
success
|
|
726
728
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
727
729
|
}
|
|
728
|
-
}`,{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 AW(e,t){let n=await Gn(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 TW(e,t,n){if(!(await Gn(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 Wa(e){return((await Gn(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var o$=255;async function Ay(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>o$,a=s?`${o.slice(0,o$-1).trimEnd()}\u2026`:o,i=await Gn(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 Ty(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await Gn(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}}dc();var d$={linear:{api:Ey,config:ks},jira:{api:vy,config:Rp}};async function Cy(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(d$)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Ss(e){let t=d$[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function Ny(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 p$=()=>process.env.BOSTRAT_STATE_DIR||l$.join(process.env.HOME||EW.tmpdir(),".bostrat"),Ry=()=>l$.join(p$(),"tickets.json"),f$=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Mp=e=>`${f$()}/track/${e}`,go=null,_y=null,Iy=Promise.resolve();async function sl(){return go||(_y??=rl.readFile(Ry(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(go=e,!t&&Ue()){let{records:n,source:r}=await it("tickets").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.id)continue;let{id:s,updatedAt:a,...i}=o.data;go[s]=i}Object.keys(go).length&&await Oy().catch(()=>{})}}return go}).finally(()=>_y=null),_y)}async function Oy(){let e=JSON.stringify({tickets:go},null,2),t=`${Ry()}.${process.pid}.tmp`;await rl.mkdir(p$(),{recursive:!0}),await rl.writeFile(t,e),await rl.rename(t,Ry())}async function yo(e=null){if(Iy=Iy.then(Oy,Oy),await Iy.catch(()=>{}),!e)return;let t=go?.[e];t?vt("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):on("tickets",e).catch(()=>{})}async function zn(e){return(await sl())[e]??null}async function h$(){return{...await sl()}}async function m$(){return(await Cy()).length>0}async function qa(e,t,{source:n="api"}={}){let r=await Cy();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(),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 sl())[e.id]=c,await yo(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 ol(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 nl(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await y$(e.id,c,"linked"),c}async function Py(e){let t=await sl();return t[e]?(delete t[e],await yo(e),!0):!1}var My=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function w$(e,t){if(!e.track?.cwd)return null;let n=Kr(await K({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function _W(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${My(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${My(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function IW(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(My(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function ol(e,t){let n=await Ss(t);if(!n)return;let r=await w$(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:Mp(e),title:`Bostrat Track: ${t.track.name}`,subtitle:IW(t,r)})}function Cp(e){let t=new URL(f$());return`${t.protocol}//${e}.${t.host}/`}var $W={linked:"linked",opened:"prOpened",merged:"prMerged"};function RW(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 g$(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await yo(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 y$(e,t,n){let r=await Ss(t);if(!r)return;let o=r.cfg.ticketFlow?.[$W[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=Ny(s,a),c=RW(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 g$(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:u$(),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 b$(e){let t=await zn(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Ss(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=Ny(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await yo(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 Dy(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await zn(e);if(!r)throw new Error("no ticket linked to this Track");let o=await Ss(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Ny(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 yo(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await g$(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function k$(e,t){let n=await zn(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Ss(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 x$(e,t,n){if(!t)throw new Error("offerId required");return co(`tkt:${e.id}:${t}`,()=>OW(e,t,n))}async function OW(e,t,n){let r=await K({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 Dy(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 s$=50;async function v$(){let e=await ks();if(!e)throw new Error("no ticketing integration configured");return Wa(e)}function MW(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>s$)throw new Error(`too many issues (${n.length}); cap is ${s$} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function CW(e,t,n,r){let o=r??await Wa(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 zn(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 S$(e,t){let n=await ks();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=MW(t),o=await Wa(n),s=await CW(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=u$();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 A$(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await K({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 ks();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let w=(await Wa(a)).find(h=>h.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 Ay(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 h=await Ty(a,{teamId:c.id,title:w.title,description:w.description,projectId:d,priority:w.priority});l.push({kind:"issue",key:h.key,title:h.title,url:h.url})}catch(h){u.push({title:w.title,message:h?.message||String(h)})}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 zn(e.id)&&(f=await qa(e,p[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:f?f.key:null}}async function nl(e,t){let n=await Ss(e);n&&await n.api.createComment(n.cfg,e,t)}async function NW(e,t,n){let r=await Ss(e);if(!r)return null;let o=await mr(t);if(!o)return null;let s=hn(o,n);if(!s)return null;let a=await rl.readFile(en(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 a$=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),$y=new Map;function PW(e,t){if(a$()<=0)return ol(e,t);if($y.has(e))return;let n=setTimeout(()=>{$y.delete(e),zn(e).then(r=>r?ol(e,r):null).catch(()=>{})},a$());n.unref?.(),$y.set(e,n)}var i$=new Map;async function c$(e,t,n){let r=i$.get(e)??0;Date.now()-r<600*1e3||(i$.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 DW(e,{resolveTrack:t=null}={}){let n=await zn(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await Cy())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 qa(s,o,{source:"chat"})}catch(a){await c$(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 yo(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await yo(e.trackId),await ol(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 yo(e.trackId),await ol(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await w$(n,e.trackId).catch(()=>null):null;await nl(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
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 nq(e,t){let n=await Jn(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 Jn(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 Ga(e){return((await Jn(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 Ry(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 Jn(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 Oy(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await Jn(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}}hc();var E$={linear:{api:My,config:xs},jira:{api:Iy,config:Bp}};async function Ly(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(E$)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function As(e){let t=E$[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function Fy(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 _$=()=>process.env.BOSTRAT_STATE_DIR||A$.join(process.env.HOME||oq.tmpdir(),".bostrat"),Dy=()=>A$.join(_$(),"tickets.json"),I$=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Lp=e=>`${I$()}/track/${e}`,yo=null,Cy=null,Ny=Promise.resolve();async function ll(){return yo||(Cy??=il.readFile(Dy(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(yo=e,!t&&Ke()){let{records:n,source:r}=await dt("tickets").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.id)continue;let{id:s,updatedAt:a,...i}=o.data;yo[s]=i}Object.keys(yo).length&&await By().catch(()=>{})}}return yo}).finally(()=>Cy=null),Cy)}async function By(){let e=JSON.stringify({tickets:yo},null,2),t=`${Dy()}.${process.pid}.tmp`;await il.mkdir(_$(),{recursive:!0}),await il.writeFile(t,e),await il.rename(t,Dy())}async function bo(e=null){if(Ny=Ny.then(By,By),await Ny.catch(()=>{}),!e)return;let t=yo?.[e];t?At("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):an("tickets",e).catch(()=>{})}async function Yn(e){return(await ll())[e]??null}async function $$(){return{...await ll()}}async function R$(){return(await Ly()).length>0}async function Va(e,t,{source:n="api"}={}){let r=await Ly();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(),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 ll())[e.id]=c,await bo(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 cl(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 al(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await C$(e.id,c,"linked"),c}async function Uy(e){let t=await ll();return t[e]?(delete t[e],await bo(e),!0):!1}var jy=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function O$(e,t){if(!e.track?.cwd)return null;let n=Vr(await q({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function sq(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${jy(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${jy(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function aq(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(jy(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function cl(e,t){let n=await As(t);if(!n)return;let r=await O$(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:Lp(e),title:`Bostrat Track: ${t.track.name}`,subtitle:aq(t,r)})}function Fp(e){let t=new URL(I$());return`${t.protocol}//${e}.${t.host}/`}var iq={linked:"linked",opened:"prOpened",merged:"prMerged"};function cq(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 M$(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await bo(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 C$(e,t,n){let r=await As(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=Fy(s,a),c=cq(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 M$(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:T$(),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 N$(e){let t=await Yn(e);if(!t)throw new Error("no ticket linked to this Track");let n=await As(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=Fy(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await bo(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 Hy(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await Yn(e);if(!r)throw new Error("no ticket linked to this Track");let o=await As(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Fy(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 bo(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await M$(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function P$(e,t){let n=await Yn(e);if(!n)throw new Error("no ticket linked to this Track");let r=await As(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 D$(e,t,n){if(!t)throw new Error("offerId required");return co(`tkt:${e.id}:${t}`,()=>lq(e,t,n))}async function lq(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 Hy(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 k$=50;async function B$(){let e=await xs();if(!e)throw new Error("no ticketing integration configured");return Ga(e)}function uq(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>k$)throw new Error(`too many issues (${n.length}); cap is ${k$} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function dq(e,t,n,r){let o=r??await Ga(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 Yn(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 j$(e,t){let n=await xs();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=uq(t),o=await Ga(n),s=await dq(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=T$();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 L$(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 xs();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let w=(await Ga(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 Ry(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 Oy(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 Yn(e.id)&&(f=await Va(e,p[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:f?f.key:null}}async function al(e,t){let n=await As(e);n&&await n.api.createComment(n.cfg,e,t)}async function pq(e,t,n){let r=await As(e);if(!r)return null;let o=await br(t);if(!o)return null;let s=gn(o,n);if(!s)return null;let a=await il.readFile(nn(o,s.v).png).catch(()=>null);return a?r.api.uploadImage(r.cfg,{issue:e.key,filename:`${o.name.replace(/[^\w.-]+/g,"-")}.v${s.v}.png`,contentType:"image/png",buffer:a}):null}var x$=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),Py=new Map;function fq(e,t){if(x$()<=0)return cl(e,t);if(Py.has(e))return;let n=setTimeout(()=>{Py.delete(e),Yn(e).then(r=>r?cl(e,r):null).catch(()=>{})},x$());n.unref?.(),Py.set(e,n)}var v$=new Map;async function S$(e,t,n){let r=v$.get(e)??0;Date.now()-r<600*1e3||(v$.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 mq(e,{resolveTrack:t=null}={}){let n=await Yn(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await Ly())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 Va(s,o,{source:"chat"})}catch(a){await S$(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 bo(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await bo(e.trackId),await cl(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 bo(e.trackId),await cl(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await O$(n,e.trackId).catch(()=>null):null;await al(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
729
731
|
|
|
730
|
-
Agent cost for this Track: ${
|
|
732
|
+
Agent cost for this Track: ${sq(o)}.`:"")),await C$(e.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await pq(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await al(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
731
733
|
|
|
732
|
-
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await
|
|
733
|
-
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await
|
|
734
|
-
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${
|
|
734
|
+
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await al(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 al(n,`**Bostrat** \u2014 live preview **${r.name}**: ${Fp(r.slug)}
|
|
735
|
+
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await fq(e.trackId,n);break;default:break}}catch(o){let s=t?await t(e.trackId).catch(()=>null):null;await S$(e.trackId,s?.cwd??null,o)}}function F$({resolveTrack:e}){Xe.on("event",t=>{mq(t,{resolveTrack:e}).catch(()=>{})})}async function U$(){if(!Ke())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 ll(),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"}}es();$a();import hq from"ws";var H$=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function Up(e,t,n={}){let r=await fetch(`${H$()}/${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 Wy=(e,t)=>Up("chat.postMessage",e,t),Ts=(e,t)=>Up("chat.update",e,t);async function _t(e,t){await fetch(e,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(t)}).catch(()=>{})}async function wq(e,t,n={}){let r=await fetch(`${H$()}/${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 W$(e,{filename:t,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await wq("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 Up("files.completeUploadExternal",e,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var gq=2200;function q$({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 Up("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new hq(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()},gq);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 V$=()=>process.env.BOSTRAT_STATE_DIR||Wp.join(process.env.HOME||bq.tmpdir(),".bostrat"),zy=()=>Wp.join(V$(),"slack.json"),Hp=null,qy=null,Ky=Promise.resolve();async function Yy(){return Hp||(qy??=ul.readFile(zy(),"utf8").then(e=>JSON.parse(e)?.threads??{}).catch(()=>({})).then(e=>Hp=e).finally(()=>qy=null),qy)}async function K$(){let e=JSON.stringify({threads:Hp},null,2),t=`${zy()}.${process.pid}.tmp`;await ul.mkdir(V$(),{recursive:!0}),await ul.writeFile(t,e),await ul.rename(t,zy())}async function Sn(){Ky=Ky.then(K$,K$),await Ky.catch(()=>{})}async function Xy(e){return(await Yy())[e]??null}async function kq(e,t){let n=await Yy();for(let[r,o]of Object.entries(n))if(o.channel===e&&o.ts===t)return{trackId:r,rec:o};return null}var _e=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");function dl(e,t){let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function xq(e,t=500){let n=String(e??"").trim().split(/\n\s*\n/)[0]??"";return dl(n.replace(/\n+/g," "),t)}function z$(e){let t=`*${_e(e.name)}*${e.archivedAt?" \xB7 _archived_":""}`,n=`${_e(e.repoLabel)} \xB7 \`${_e(e.branch||"?")}\` \xB7 lead *${_e(e.lead)}*`,r=e.kickoff?`> ${_e(dl(e.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${t}
|
|
736
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${Lp(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Jy(e,t){await Ts(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:z$(t)})}async function xo(e,t,n,r=void 0){return Wy(e.botToken,{channel:t.channel,thread_ts:t.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var G$=new Set;function ko(e){G$.add(e),e.catch(()=>{}).finally(()=>G$.delete(e))}var We=e=>({response_type:"ephemeral",text:e}),vq="*/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._",Sq=["claude","cursor","codex"];function Gy(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 Vy=(e,t)=>We(`No live Track matches \u201C${_e(t)}\u201D. Live: ${e.map(n=>`\`${_e(n.name)}\``).join(" \xB7 ")||"none"}`);async function J$(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 _t(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 _t(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?Wp.basename(c.base):"fresh workspace"),lead:"claude"},u=await Wy(t.botToken,{channel:t.channel,text:`Track started: ${l.name}`,blocks:z$(l),unfurl_links:!1});l.ts=u.ts,(await Yy())[i.id]=l,await Sn(),await _t(o,{...We(`\u2713 *${_e(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await _t(o,{...We(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function Aq(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.archiveTrack(n.id);let s=await Xy(n.id);s&&(s.archivedAt=Date.now(),await Sn(),await Jy(t,s).catch(()=>{})),await _t(r,{...We(`\u2713 *${_e(n.name)}* archived.`),replace_original:!0})}catch(s){await _t(r,{...We(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function Tq(e,t,{track:n,agent:r,responseUrl:o}){try{await e.deps.startLeadHandoff(n.id,r),await _t(o,{...We(`\u2713 Handing *${_e(n.name)}* to ${_e(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await _t(o,{...We(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function Eq(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 _t(r,{...We(`\u2713 Stopped *${_e(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 _t(r,{...We(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function _q(e,t){let n=[...t.slice(0,99).map(r=>({text:{type:"plain_text",text:dl(`${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 *${_e(e)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function Iq(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?Wp.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 *${_e(s.name)}* \u2014 ${_e(i)}${a.branch?` \`${_e(a.branch)}\``:""} \xB7 lead ${_e(c)} \xB7 ${l}`}),o=t.length-n.length;return r.join(`
|
|
735
737
|
`)+(o?`
|
|
736
|
-
_${o} archived_`:"")}async function
|
|
737
|
-
\u2713 *${
|
|
738
|
-
\u2713 _answered_`}}]})}async function
|
|
739
|
-
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return
|
|
740
|
-
`)){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
|
|
741
|
-
`)}he();Sm();import Rq from"node:fs/promises";import Oq from"node:path";bu();Au();Gr();var jp=e=>e.payload?.ts??e.ts,Aq=12e4,tn=e=>Math.round(e*1e4)/1e4,Tq=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{},byTrack:new Map});function Eq(e,{since:t}){let{track:n,events:r,brief:o}=e,s=b=>jp(b)>=t,a=tr([Wi(n?.name),Wi(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=tr(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:N=null,ts:U=null}={})=>({textCat:b,shape:I,continuation:_,text:N,ts:U,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,h={turns:0};for(let b of d){let I=l(b.boxId),_=I.segments[I.segments.length-1],N=jp(b);switch(I.lastTs!=null&&N>I.lastTs&&s(b)&&(_.activeMs+=Math.min(N-I.lastTs,Aq)),I.lastTs=N,b.provider&&(I.provider=b.provider),b.type){case"turn.user":{let U=b.payload?.text;if(b.source==="hook"||!String(U||"").trim()||b.payload?.note||Gi(U))break;let j=tr(U),T=!!b.payload?.kind,R=T||ax(U),L=u(j,{shape:T?"injected":R?"follow-up":"novel-ask",continuation:!j&&R,text:U$(U),ts:N});I.segments.push(L),s(b)&&(L.turns+=1);break}case"peer.message":{let U=b.payload?.text;if(!String(U||"").trim())break;let j=tr(U),T=u(j,{shape:"peer",continuation:!j,text:U$(U),ts:N});I.segments.push(T),s(b)&&(T.turns+=1);break}case"tool.called":{if(b.source==="transcript")break;let U=b.payload?.tool;U&&(_.tools[U]=(_.tools[U]||0)+1),s(b)&&(_.toolCalls+=1);break}case"cost.recorded":{if(!s(b))break;let{usd:U,tokens:j,model:T,unpriced:R}=Jt(b);R&&(h.turns+=1),_.usd+=U,j&&(_.tokens.input+=j.input,_.tokens.output+=j.output,_.tokens.cacheRead+=j.cacheRead,_.tokens.cacheWrite+=j.cacheWrite),_.models[T]=(_.models[T]||0)+U,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 N of I.segments){let U=null,j=null;if(N.textCat)U=N.textCat.category,j="text";else if(i.get(b))U=i.get(b).category,j="boxTask";else if(N.continuation&&_)U=_,j="inherited";else{let O=sx(N.tools);O?(U=O.category,j="toolMix"):a&&(U=a.category,j="hint")}if(U&&(_=U),!(N.usd>0||N.activeMs>0||N.turns>0||N.toolCalls>0))continue;let R=U??"uncategorized";g.has(R)||g.set(R,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{}});let L=g.get(R);L.usd+=N.usd,L.activeMs+=N.activeMs,L.turns+=N.turns,L.toolCalls+=N.toolCalls,j&&(L.provenance[j]=(L.provenance[j]||0)+N.usd);for(let[O,S]of Object.entries(N.models))L.models[O]=(L.models[O]||0)+S;let V=N.provider||I.provider;V&&(L.providers[V]=(L.providers[V]||0)+N.usd),V==="cursor"&&N.usd===0&&(x=!0),U||(y.usd+=N.usd,y.turns+=N.turns,y.byShape[N.shape]=(y.byShape[N.shape]||0)+N.usd,N.usd>0&&N.text&&y.samples.push({trackId:n?.id,track:n?.name||n?.id,shape:N.shape,usd:tn(N.usd),ts:N.ts,text:N.text}))}}return{perCategory:g,uncategorized:y,handoffs:p,merged:f,failed:w,sawCursorActivity:x,unpricedTurns:h.turns}}function zy(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:N,merged:U,failed:j,sawCursorActivity:T,unpricedTurns:R}=Eq(y,{since:r});T&&(i=!0),c+=R,l.usd+=_.usd,l.turns+=_.turns;for(let[V,O]of Object.entries(_.byShape))l.byShape[V]=(l.byShape[V]||0)+O;l.samples.push(..._.samples);let L={trackId:x?.id,name:x?.name||x?.id,archived:!!x?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:N,subtasksMerged:U,subtasksFailed:j};for(let[V,O]of I){L.usd+=O.usd,L.activeMs+=O.activeMs,L.turns+=O.turns,L.categories[V]={usd:tn(O.usd),activeMs:O.activeMs};for(let[H,W]of Object.entries(O.models))L.models[H]=tn((L.models[H]||0)+W);for(let[H,W]of Object.entries(O.providers))L.providers[H]=tn((L.providers[H]||0)+W);o.has(V)||o.set(V,Tq());let S=o.get(V);S.usd+=O.usd,S.activeMs+=O.activeMs,S.turns+=O.turns,S.toolCalls+=O.toolCalls;for(let[H,W]of Object.entries(O.models))S.models[H]=(S.models[H]||0)+W;for(let[H,W]of Object.entries(O.providers))S.providers[H]=(S.providers[H]||0)+W;for(let[H,W]of Object.entries(O.provenance))S.provenance[H]=(S.provenance[H]||0)+W;let M=S.byTrack.get(x?.id)||{usd:0,activeMs:0};M.usd+=O.usd,M.activeMs+=O.activeMs,S.byTrack.set(x?.id,M)}(L.usd>0||L.activeMs>0||L.turns>0)&&(L.usd=tn(L.usd),L.dominantCategory=$q(L.categories),s.push(L))}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])),h=[...o.entries()].map(([y,x])=>({category:y,sharePct:f>0?Math.round((p==="usd"?x.usd:x.activeMs)/f*100):0,shareBasis:p,usd:tn(x.usd),activeMs:x.activeMs,turns:x.turns,toolCalls:x.toolCalls,models:ll(x.models),providers:ll(x.providers),provenance:ll(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:tn(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:tn(u),activeMs:d,turns:[...o.values()].reduce((y,x)=>y+x.turns,0),bullets:h,workstreams:s,provenance:ll(g),gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c,uncategorized:{usd:tn(l.usd),turns:l.turns,sharePct:u>0?Math.round(l.usd/u*100):0,byShape:ll(l.byShape),top:l.samples.sort((y,x)=>x.usd-y.usd).slice(0,_q)}}}}var _q=25,F$=160,U$=e=>{let t=String(e||"").replace(/\s+/g," ").trim();return t.length>F$?`${t.slice(0,F$).trimEnd()}\u2026`:t},Iq=10;function H$(e,{thresholdPct:t=Iq}={}){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:tn(o),totalUsd:tn(r),turns:n.turns||0,inheritedPct:r>0?Math.round((e?.provenance?.inherited||0)/r*100):0,problems:i}}var $q=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},ll=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,tn(n)]));var Es={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function Mq(e){let t=String(e||"7d");return t in Es?{key:t,ms:Es[t]}:{key:"7d",ms:Es["7d"]}}function Cq(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=Vu(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 Nq(e){if(!e?.cwd)return null;try{let n=(await Rq.readFile(Oq.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 Vy(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}:Mq(t),d=[];for(let w of e||[]){let h=await K(w),g=Cq(w,h);i&&c<o&&(g=g.filter(y=>(y.ts??0)<=c)),d.push({track:w,events:g,brief:await Nq(w)})}let p=zy(d,{windowMs:u,now:c}),f=l==="7d"&&c===o?p:zy(d,{windowMs:Es["7d"],now:o});return{window:l,...p,categorization:H$(f)}}var ul=e=>`$${Number(e||0).toFixed(2)}`,W$=e=>{let t=Math.round((e||0)/6e4);return t>=60?`${Math.floor(t/60)}h${String(t%60).padStart(2,"0")}`:`${t}m`},Ts=(e,t)=>String(e).padEnd(t);function q$(e){let t=[];t.push(`USAGE ROUNDUP \xB7 ${e.window} \xB7 ${ul(e.totalUsd)} \xB7 ${W$(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(` ${Ts(`${a.shareBasis==="time"?"~":""}${a.sharePct}%`,5)} ${Ts(a.category,15)} ${Ts(ul(a.usd),10)} ${Ts(W$(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 ${ul(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} ${ul(i)}`).join(" \xB7 ")||"none")),s.top?.length||t.push(" (nothing unmatched carried cost in this window)");for(let a of s.top||[])t.push(` ${Ts(ul(a.usd),8)} ${Ts(a.shape,10)} ${Ts(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(`
|
|
742
|
-
`)}
|
|
743
|
-
`).slice(-3).join(" | ").slice(0,300);d!==
|
|
744
|
-
`).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
|
|
745
|
-
`).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
|
|
746
|
-
`),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
|
|
747
|
-
`,{mode:384}),
|
|
738
|
+
_${o} archived_`:"")}async function $q(e,t){let n=await vs();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?(ko(J$(e,n,{name:u,repo:l.path,responseUrl:t.response_url})),We(`Starting *${_e(u)}* on ${_e(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:_q(u,i)}}case"archive":{if(!a.length)return We("Usage: `/bostrat archive <track>`");let{match:i,live:c}=Gy(await r.listTracks(),a.join(" "));return i?(ko(Aq(e,n,{track:i,responseUrl:t.response_url})),We(`Archiving *${_e(i.name)}*\u2026`)):Vy(c,a.join(" "))}case"handoff":{let i=r.knownAgents??Sq,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}=Gy(await r.listTracks(),l);return u?(ko(Tq(e,n,{track:u,agent:c,responseUrl:t.response_url})),We(`Handing *${_e(u.name)}* to ${_e(c)}\u2026`)):Vy(d,l)}case"stop":{if(!a.length)return We("Usage: `/bostrat stop <track>`");let{match:i,live:c}=Gy(await r.listTracks(),a.join(" "));return i?(ko(Eq(e,n,{track:i,responseUrl:t.response_url})),We(`Stopping *${_e(i.name)}*\u2026`)):Vy(c,a.join(" "))}case"list":return We(await Iq(r));default:return We(vq)}}async function Rq(e,t,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Xy(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 Sn(),await Ts(t.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${_e(l||"question")}~
|
|
739
|
+
\u2713 *${_e(a)}*`}}]}).catch(()=>{})}}catch(c){await _t(n.response_url,We(`Couldn't answer: ${c?.message||c}`))}}async function Oq(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)?to.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,fa(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 _t(n.response_url,We(`Couldn't act on the offer: ${c?.message||c}`))}}async function Mq(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 _t(n.response_url,We(`Couldn't act on the offer: ${i?.message||i}`))}}async function Cq(e,t){let n=t.actions?.[0];if(!n?.action_id)return;let r=await vs();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 _t(t.response_url,{...We(`Starting *${_e(s)}*\u2026`),replace_original:!0}),ko(J$(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"?ko(Rq(e,r,t,o)):n.action_id==="bostrat-ship"?ko(Oq(e,r,t,o)):n.action_id==="bostrat-pr-exists"&&ko(Mq(e,r,t,o))}async function Nq(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 kq(t.channel,t.thread_ts);if(!n)return;let r=await vs();if(r)try{await e.deps.sayToLead(n.trackId,t.text.trim())}catch(o){await xo(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function Pq(e){return async t=>{if(t.type==="slash_commands")return $q(e,t.payload);if(t.type==="interactive"){await Cq(e,t.payload);return}if(t.type==="events_api"){await Nq(e,t.payload?.event);return}}}var Dq=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 Bq(e,t){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${_e(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:dl(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 jq(e,t,n){let r=n.payload||{};if(!r.promptId||t.prompt?.promptId===r.promptId)return;t.prompt&&await Ts(e.botToken,{channel:t.channel,ts:t.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${_e(t.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await xo(e,t,r.question||"The lead needs an answer",Bq(n.trackId,r));t.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await Sn()}async function Lq(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 Sn(),await Ts(e.botToken,{channel:t.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${_e(s||"question")}~
|
|
740
|
+
\u2713 _answered_`}}]})}async function Fq(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 xo(e,t,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${_e(dl(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await Sn()}async function Uq(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await Sn();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 Ts(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function Hq(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 xo(e,t,`PR ${o} already open \u2014 push or new PR?`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${o}${r.branch?` (${_e(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 Sn()}async function Wq(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await Sn();let s=r.choice==="dismiss"?"dismissed":r.choice==="new"?"\u2713 opening a new PR":"\u2713 pushing to the open PR";await Ts(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function qq(e,t,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??Kq)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await W$(e.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:t.channel,threadTs:t.ts,initialComment:`\u{1F3A8} mock *${_e(o.name)}*${a}${o.group?` (group \`${_e(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${_e(a.name)}*`).join(", ");await xo(e,t,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${_e(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function Kq(e,t){let n=await br(e);if(!n)return null;let r=gn(n,t);if(!r)return null;let o=await ul.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 Gq(e,t){if(!Dq.has(t.type))return;let n=await Xy(t.trackId);if(!n)return;let r=await vs();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 Sn(),await Jy(r,n);return;case"turn.agent":{if(!o.text)return;let s=xq(o.text);if(!s)return;await xo(r,n,s,[{type:"section",text:{type:"mrkdwn",text:_e(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${Lp(t.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return jq(r,n,t);case"prompt.answered":return Lq(r,n,t);case"pr.offered":return Fq(r,n,t);case"pr.offer.answered":return Uq(r,n,t);case"pr.exists.offered":return Hq(r,n,t);case"pr.exists.answered":return Wq(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 xo(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 Sn(),await Jy(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await xo(r,n,`Live preview ${o.name}: ${Fp(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${_e(o.name)}*: ${Fp(o.slug)}
|
|
741
|
+
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return qq(r,n,t,e.deps)}}function Y$(e){let t={deps:e};Xe.on("event",s=>{Gq(t,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await vs()||(r&&clearInterval(r),r=null,n=q$({getToken:async()=>(await vs())?.appToken,onEnvelope:Pq(t),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}dn();fe();In();iw();sr();import Vq from"node:fs/promises";function zq(e){let t=new Map;for(let n of String(e||"").split(`
|
|
742
|
+
`)){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 Jq(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 Yq=new Set(["Write","Edit","NotebookEdit"]),Xq=220,Zq=160;function Qq(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(Yq.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,Xq)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=Po(o.command.trim().replace(/\s+/g," ")).slice(0,Zq));return r}function e5(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?un(t.cwd,t.sessionId):t?.cwd?rm(t.cwd):null}async function X$(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 C(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=Od(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:Qq(e,o)});break;case"Stop":await t5(r,o,e,i);break;default:break}return a}async function t5(e,t,n,r){let o=[],s=e5(n,t);if(s)try{o=zq(await Vq.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=Jq(await q(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:Cn(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}})}Os();zf();$d();fe();fe();zr();fe();zr();sr();function pl(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 Z$=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),Q$=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},qp=e=>Math.round(e*1e4)/1e4;async function Zy(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=Z$(),d=0,p=[];for(let f of e||[]){let w=await q(f),m=Vr(w,{boxLabels:pl(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);Q$(u,m.totalTokens);for(let[g,y]of Object.entries(m.byModel))n[g]||={usd:0,events:0,tokens:Z$()},n[g].usd=qp(n[g].usd+y.usd),n[g].events+=y.events,Q$(n[g].tokens,y.tokens);for(let[g,y]of Object.entries(m.byProvider))r[g]||={usd:0,events:0},r[g].usd=qp(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:ra(),fleet:{totalUsd:qp(o),asRecordedUsd:qp(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 vo=e=>`$${e.toFixed(4)}`,za=(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 ${vo(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 ${vo(t.asRecordedUsd)} \xB7 current-rates total ${vo(t.totalUsd)} (${t.totalUsd>=t.asRecordedUsd?"+":""}${vo(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(` ${za(a,26)} ${za(vo(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(` ${za(a.name||a.id,30)} ${vo(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${za(c.label,40)} ${za(vo(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))}${za(vo(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
743
|
+
`)}fe();Oh();import c5 from"node:fs/promises";import l5 from"node:path";Au();$u();zr();var Kp=e=>e.payload?.ts??e.ts,n5=12e4,rn=e=>Math.round(e*1e4)/1e4,r5=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{},byTrack:new Map});function o5(e,{since:t}){let{track:n,events:r,brief:o}=e,s=b=>Kp(b)>=t,a=or([Gi(n?.name),Gi(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=or(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:R=!1,text:N=null,ts:D=null}={})=>({textCat:b,shape:I,continuation:R,text:N,ts:D,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)=>Kp(b)-Kp(I)),p=0,f=0,w=0,m={turns:0};for(let b of d){let I=l(b.boxId),R=I.segments[I.segments.length-1],N=Kp(b);switch(I.lastTs!=null&&N>I.lastTs&&s(b)&&(R.activeMs+=Math.min(N-I.lastTs,n5)),I.lastTs=N,b.provider&&(I.provider=b.provider),b.type){case"turn.user":{let D=b.payload?.text;if(b.source==="hook"||!String(D||"").trim()||b.payload?.note||Ji(D))break;let j=or(D),T=!!b.payload?.kind,$=T||hx(D),F=u(j,{shape:T?"injected":$?"follow-up":"novel-ask",continuation:!j&&$,text:nR(D),ts:N});I.segments.push(F),s(b)&&(F.turns+=1);break}case"peer.message":{let D=b.payload?.text;if(!String(D||"").trim())break;let j=or(D),T=u(j,{shape:"peer",continuation:!j,text:nR(D),ts:N});I.segments.push(T),s(b)&&(T.turns+=1);break}case"tool.called":{if(b.source==="transcript")break;let D=b.payload?.tool;D&&(R.tools[D]=(R.tools[D]||0)+1),s(b)&&(R.toolCalls+=1);break}case"cost.recorded":{if(!s(b))break;let{usd:D,tokens:j,model:T,unpriced:$}=Xt(b);$&&(m.turns+=1),R.usd+=D,j&&(R.tokens.input+=j.input,R.tokens.output+=j.output,R.tokens.cacheRead+=j.cacheRead,R.tokens.cacheWrite+=j.cacheWrite),R.models[T]=(R.models[T]||0)+D,b.provider&&(R.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:[]},k=!1;for(let[b,I]of c){let R=null;for(let N of I.segments){let D=null,j=null;if(N.textCat)D=N.textCat.category,j="text";else if(i.get(b))D=i.get(b).category,j="boxTask";else if(N.continuation&&R)D=R,j="inherited";else{let M=mx(N.tools);M?(D=M.category,j="toolMix"):a&&(D=a.category,j="hint")}if(D&&(R=D),!(N.usd>0||N.activeMs>0||N.turns>0||N.toolCalls>0))continue;let $=D??"uncategorized";g.has($)||g.set($,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{}});let F=g.get($);F.usd+=N.usd,F.activeMs+=N.activeMs,F.turns+=N.turns,F.toolCalls+=N.toolCalls,j&&(F.provenance[j]=(F.provenance[j]||0)+N.usd);for(let[M,S]of Object.entries(N.models))F.models[M]=(F.models[M]||0)+S;let J=N.provider||I.provider;J&&(F.providers[J]=(F.providers[J]||0)+N.usd),J==="cursor"&&N.usd===0&&(k=!0),D||(y.usd+=N.usd,y.turns+=N.turns,y.byShape[N.shape]=(y.byShape[N.shape]||0)+N.usd,N.usd>0&&N.text&&y.samples.push({trackId:n?.id,track:n?.name||n?.id,shape:N.shape,usd:rn(N.usd),ts:N.ts,text:N.text}))}}return{perCategory:g,uncategorized:y,handoffs:p,merged:f,failed:w,sawCursorActivity:k,unpricedTurns:m.turns}}function Qy(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:k,events:b}=y;if(!b?.length){a+=1;continue}let{perCategory:I,uncategorized:R,handoffs:N,merged:D,failed:j,sawCursorActivity:T,unpricedTurns:$}=o5(y,{since:r});T&&(i=!0),c+=$,l.usd+=R.usd,l.turns+=R.turns;for(let[J,M]of Object.entries(R.byShape))l.byShape[J]=(l.byShape[J]||0)+M;l.samples.push(...R.samples);let F={trackId:k?.id,name:k?.name||k?.id,archived:!!k?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:N,subtasksMerged:D,subtasksFailed:j};for(let[J,M]of I){F.usd+=M.usd,F.activeMs+=M.activeMs,F.turns+=M.turns,F.categories[J]={usd:rn(M.usd),activeMs:M.activeMs};for(let[H,K]of Object.entries(M.models))F.models[H]=rn((F.models[H]||0)+K);for(let[H,K]of Object.entries(M.providers))F.providers[H]=rn((F.providers[H]||0)+K);o.has(J)||o.set(J,r5());let S=o.get(J);S.usd+=M.usd,S.activeMs+=M.activeMs,S.turns+=M.turns,S.toolCalls+=M.toolCalls;for(let[H,K]of Object.entries(M.models))S.models[H]=(S.models[H]||0)+K;for(let[H,K]of Object.entries(M.providers))S.providers[H]=(S.providers[H]||0)+K;for(let[H,K]of Object.entries(M.provenance))S.provenance[H]=(S.provenance[H]||0)+K;let _=S.byTrack.get(k?.id)||{usd:0,activeMs:0};_.usd+=M.usd,_.activeMs+=M.activeMs,S.byTrack.set(k?.id,_)}(F.usd>0||F.activeMs>0||F.turns>0)&&(F.usd=rn(F.usd),F.dominantCategory=i5(F.categories),s.push(F))}let u=[...o.values()].reduce((y,k)=>y+k.usd,0),d=[...o.values()].reduce((y,k)=>y+k.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,k])=>({category:y,sharePct:f>0?Math.round((p==="usd"?k.usd:k.activeMs)/f*100):0,shareBasis:p,usd:rn(k.usd),activeMs:k.activeMs,turns:k.turns,toolCalls:k.toolCalls,models:fl(k.models),providers:fl(k.providers),provenance:fl(k.provenance),topWorkstreams:[...k.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,k)=>k.usd-y.usd||k.activeMs-y.activeMs);s.sort((y,k)=>k.usd-y.usd||k.activeMs-y.activeMs);let g={};for(let y of o.values())for(let[k,b]of Object.entries(y.provenance))g[k]=(g[k]||0)+b;return{since:r,capturedAt:n,totalUsd:rn(u),activeMs:d,turns:[...o.values()].reduce((y,k)=>y+k.turns,0),bullets:m,workstreams:s,provenance:fl(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:fl(l.byShape),top:l.samples.sort((y,k)=>k.usd-y.usd).slice(0,s5)}}}}var s5=25,tR=160,nR=e=>{let t=String(e||"").replace(/\s+/g," ").trim();return t.length>tR?`${t.slice(0,tR).trimEnd()}\u2026`:t},a5=10;function rR(e,{thresholdPct:t=a5}={}){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},fl=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,rn(n)]));var _s={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function u5(e){let t=String(e||"7d");return t in _s?{key:t,ms:_s[t]}:{key:"7d",ms:_s["7d"]}}function d5(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=nd(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 p5(e){if(!e?.cwd)return null;try{let n=(await c5.readFile(l5.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 eb(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}:u5(t),d=[];for(let w of e||[]){let m=await q(w),g=d5(w,m);i&&c<o&&(g=g.filter(y=>(y.ts??0)<=c)),d.push({track:w,events:g,brief:await p5(w)})}let p=Qy(d,{windowMs:u,now:c}),f=l==="7d"&&c===o?p:Qy(d,{windowMs:_s["7d"],now:o});return{window:l,...p,categorization:rR(f)}}var ml=e=>`$${Number(e||0).toFixed(2)}`,oR=e=>{let t=Math.round((e||0)/6e4);return t>=60?`${Math.floor(t/60)}h${String(t%60).padStart(2,"0")}`:`${t}m`},Es=(e,t)=>String(e).padEnd(t);function sR(e){let t=[];t.push(`USAGE ROUNDUP \xB7 ${e.window} \xB7 ${ml(e.totalUsd)} \xB7 ${oR(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(` ${Es(`${a.shareBasis==="time"?"~":""}${a.sharePct}%`,5)} ${Es(a.category,15)} ${Es(ml(a.usd),10)} ${Es(oR(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 ${ml(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} ${ml(i)}`).join(" \xB7 ")||"none")),s.top?.length||t.push(" (nothing unmatched carried cost in this window)");for(let a of s.top||[])t.push(` ${Es(ml(a.usd),8)} ${Es(a.shape,10)} ${Es(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(`
|
|
744
|
+
`)}sr();var So=36e5,lR=24*So,f5=400,m5=12,Is=e=>Math.round(e*1e4)/1e4;function tb({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 _s?String(e||"7d"):"7d",i=_s[a];return{key:a,since:i==null?null:r-i,until:r}}var h5=(e,t)=>e!=null&&t-e<=48*So?So:lR;function aR(e,t){if(t===So)return Math.floor(e/So)*So;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function w5(e,t){if(t===So)return e+So;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var iR=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),cR=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},g5=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Is(n)]));async function uR(e,t={}){let n=t.now??Date.now(),r=tb({...t,now:n}),o=r.since==null?lR:h5(r.since,r.until),s=T=>(r.since==null||T>=r.since)&&T<r.until,a=r.since!=null?r.until-r.since:null,i=T=>a!=null&&T>=r.since-a&&T<r.since,c={},l={},u=iR(),d=0,p=0,f=0,w=0,m=0,g=0,y=new Set,k=new Map,b=[],I=[],R=[],N=null;for(let T of e||[]){let F=(await q(T)).filter(S=>S.type==="cost.recorded");if(!F.length){T.archivedAt||R.push(T.name||T.id);continue}let J=[];for(let S of F){let _=S.ts??0;s(_)?J.push(S):i(_)&&(g+=Xt(S).usd)}if(!J.length)continue;let M=Vr(J,{boxLabels:pl(T)});d+=M.totalUsd,p+=M.turns,f+=M.repricedTurns||0,w+=M.asRecordedUsd||0,m+=M.unpricedTurns||0;for(let S of M.unpricedModels||[])y.add(S);cR(u,M.totalTokens);for(let[S,_]of Object.entries(M.byModel))c[S]||={usd:0,events:0,tokens:iR()},c[S].usd=Is(c[S].usd+_.usd),c[S].events+=_.events,cR(c[S].tokens,_.tokens);for(let[S,_]of Object.entries(M.byProvider))l[S]||={usd:0,events:0},l[S].usd=Is(l[S].usd+_.usd),l[S].events+=_.events;for(let S of J){let{usd:_}=Xt(S);if(!_)continue;let H=aR(S.ts??0,o),K=k.get(H)??{t:H,total:0,byProvider:{}};K.total+=_;let X=S.provider||"unknown";K.byProvider[X]=(K.byProvider[X]||0)+_,k.set(H,K),(N==null||H<N)&&(N=H)}b.push({id:T.id,name:T.name||T.id,archived:!!T.archivedAt,usd:M.totalUsd,turns:M.turns,providers:Object.keys(M.byProvider)});for(let[S,_]of Object.entries(M.byBox))I.push({trackId:T.id,trackName:T.name||T.id,boxId:S,..._})}let D=r.since!=null?aR(r.since,o):N,j=[];for(let T=D,$=0;T!=null&&T<r.until&&$<f5;T=w5(T,o),$++){let F=k.get(T);j.push(F?{t:T,total:Is(F.total),byProvider:g5(F.byProvider)}:{t:T,total:0,byProvider:{}})}return b.sort((T,$)=>$.usd-T.usd),I.sort((T,$)=>$.usd-T.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:ra(),window:r.key,since:r.since,until:r.until,bucketMs:o,series:j,fleet:{totalUsd:Is(d),asRecordedUsd:Is(w),turns:p,repricedTurns:f,unpricedTurns:m,unpricedModels:[...y],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:b.length,tracksSilentLive:R},prior:a!=null?{totalUsd:Is(g),since:r.since-a,until:r.since}:null,tracks:b.slice(0,50),boxes:I.slice(0,m5)}}_d();var y5=new Set(["cost.recorded","code.committed","code.rework","rework.cause.offered","rework.cause.answered"]),b5=20,k5=40;async function dR(e,t={}){let n=t.now??Date.now(),r=tb({...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 q(l),d=0,p=pl(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=fT(s,{boxMeta:a});return{generatedAt:new Date().toISOString(),window:r.key,since:r.since,until:r.until,reworkWindowDays:io,decayEndDays:Zh,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,b5),fixes:c.fixes.slice(0,k5)}}import x5 from"node:fs";import pR from"node:fs/promises";import hl from"node:os";import v5 from"node:path";Hw();mt();var S5=1024*1024,Gp=e=>Math.round(e/S5);async function A5(e){try{let t=await pR.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:Gp(r),diskTotalMb:Gp(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function T5(){try{return(await cn()).filter(t=>t.isBostrat).length}catch{return null}}async function E5(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await pR.readdir(v5.join(el,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function fR({stateDir:e=el,now:t=Date.now}={}){let n=hl.totalmem(),[r,o,s,a]=await Promise.all([Hd().catch(()=>null),A5(x5.existsSync(e)?e:hl.tmpdir()),T5(),E5()]),i=hl.cpus()?.length||null,c=hl.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?Gp(n-r):null,memTotalMb:Gp(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(hl.uptime())}}Sw();fe();_m();bi();sr();fe();sr();var _5=864e5;function I5(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=Cn(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 mR(e,{days:t=14,tolerance:n=.005,now:r=Date.now()}={}){let o=r-t*_5,s={},a=0,i=0,c=[];for(let u of e||[]){let d=await q(u);for(let p of d){if(!p.ts||p.ts<o)continue;let f=I5(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}}kw();fg();Zu();yt();Zt();import Ja from"node:fs/promises";import $5 from"node:fs";import xt from"node:path";import R5 from"node:os";import{execFile as O5}from"node:child_process";import{promisify as M5}from"node:util";var C5=M5(O5),hR=process.env.BOSTRAT_STATE_DIR||xt.join(process.env.HOME||R5.tmpdir(),".bostrat"),Vp=!1,nb=null;async function rb(e,t){let n=await Ja.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await Ja.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await Ja.mkdir(xt.dirname(t),{recursive:!0}),await Ja.copyFile(e,t),!0)}var N5=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 zp(e,t,{skip:n=null}={}){let r=0,o=await Ja.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(N5.has(s.name)||n?.has(s.name))continue;let a=xt.join(e,s.name);s.isDirectory()?r+=await zp(a,xt.join(t,s.name)):s.isFile()&&(r+=await rb(a,xt.join(t,s.name))?1:0)}return r}async function wR({stateDir:e=hR,dest:t=process.env.BOSTRAT_SYNC_DIR||`${hR}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await Ja.mkdir(t,{recursive:!0});let o=0;o+=await rb(xt.join(e,"tracks.json"),xt.join(t,"tracks.json"))?1:0,o+=await rb(xt.join(e,"events.jsonl"),xt.join(t,"events.jsonl"))?1:0,o+=await zp(xt.join(e,"events"),xt.join(t,"events")),o+=await zp(xt.join(e,"memory"),xt.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await zp(xt.join(u.cwd,".bostrat"),xt.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>C5("git",["-C",t,...u]);$5.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 Of(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||Vp))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,Vp&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),Vp=!1,nb=null}catch(u){Vp=!0;let d=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
745
|
+
`).slice(-3).join(" | ").slice(0,300);d!==nb&&(nb=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)}}}cg();dn();Uo();mt();li();import{execFile as P5}from"node:child_process";import{promisify as D5}from"node:util";import{randomUUID as B5}from"node:crypto";import j5 from"node:fs";import L5 from"node:os";import F5 from"node:path";var To=D5(P5),Jp=e=>new Promise(t=>setTimeout(t,e)),Eo=Xf,ob=process.env.BOSTRAT_USAGE_PROBE_CWD||F5.join(L5.homedir(),".bostrat","usage-probe"),U5=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,H5=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,W5=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,gR=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Ao=null,wl=null,gl=null;async function q5(){try{return await To(ne,re("has-session","-t",Eo)),!0}catch{return!1}}function K5(){Ao&&clearTimeout(Ao),Ao=setTimeout(()=>G5().catch(()=>{}),U5),Ao.unref&&Ao.unref()}async function G5(){Ao&&(clearTimeout(Ao),Ao=null);try{await To(ne,re("kill-session","-t",Eo))}catch{}}async function V5(){let e=Date.now()+W5;for(;;){let t=await sb().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(t)){await Jp(500);return}if(Date.now()>e)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await Jp(500)}}async function z5(){if(await q5())return;if(!await Fr())throw new Error("host tmux server not running \u2014 start globo-tmux.service");j5.mkdirSync(ob,{recursive:!0}),await Or(ob);let e=B5();await To(ne,re("new-session","-d","-s",Eo,"-c",ob,"-x","200","-y","60",...Ii()));let t=`${Yf} --session-id ${e}`;await To(ne,re("send-keys","-t",Eo,t,"Enter")),await V5()}async function sb(){let{stdout:e}=await To(ne,re("capture-pane","-t",Eo,"-p"));return e}async function J5(){await z5(),await To(ne,re("send-keys","-t",Eo,"-l","/usage")),await To(ne,re("send-keys","-t",Eo,"Enter"));let e=Date.now()+H5,t="";for(;await Jp(400),t=await sb(),!(/Current\s+(session|week)/i.test(t)||/\d+%\s*used/i.test(t)||Date.now()>e););return await Jp(900),t=await sb(),await To(ne,re("send-keys","-t",Eo,"Escape")).catch(()=>{}),K5(),t}function Y5(e){let t=String(e||"").split(`
|
|
746
|
+
`).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 X5(e){let t=String(e||"").split(`
|
|
747
|
+
`).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 yR({fresh:e=!1}={}){return!e&&wl&&Date.now()-wl.at<gR?wl.data:gl||(gl=(async()=>{try{let t=await J5(),n=Y5(t),r={...n,contributors:X5(t),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return wl={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 wl={at:Date.now()-(gR-3e3),data:n},n}finally{gl=null}})(),gl)}var Z5=262144;function Q5(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 e6=new Set([300]),t6=new Set([10080]);function n6(e){let t=Number(e);return!Number.isFinite(t)||t<=0?null:e6.has(t)?"session":t6.has(t)?"week":"other"}function r6(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 o6(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?Q5((o-n)/1e3):null,resetsAt:o}}var yl=e=>({session:null,week:null,other:null,planType:null,credits:null,limitReached:null,sawTokenCount:e});function s6(e,t=Date.now()){let n=String(e||"").split(`
|
|
748
|
+
`),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 yl(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=yl(r);[c.primary,c.secondary].forEach((f,w)=>{let m=o6(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:r6(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{...yl(r),session:l(c.primary_used_percent),week:l(c.secondary_used_percent)}}return yl(r)}return yl(r)}async function ab(e){let t={provider:"codex",supported:!0,contributors:null};try{let n=await Lo(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 Ct(n.file,Z5),o=s6(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 bR(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 Yp({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 ab(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await ab(null))}return n.has("cursor")&&r.push(await Yp({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function Yp(e,{fresh:t=!1}={}){let n=e?.agent||"claude";return n==="codex"?ab(e):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await yR({fresh:t})}}Cr();yt();yt();function a6(e=process.env){return!!(Ce(e)&&e.BOSTRAT_PLANE_TOKEN)}var i6=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Xp=(e,t)=>Object.assign(new Error(e),{status:t});async function kR(e,t){if(!a6(t))throw Xp("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${Ce(t)}${e}`,{headers:i6(t)})}catch(o){throw Xp(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw Xp(r?.error||`plane replied ${n.status}`,n.status);return r}async function xR(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 vR(e,{env:t=process.env}={}){if(!e)throw Xp("message id required",400);return(await kR(`/v1/gmail/messages/${encodeURIComponent(e)}`,t)).message}sc();md();function ib(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 c6(e){let[t,n]=e.split("/"),r=ib(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 SR(e){return(e||"").split(",").map(t=>t.trim()).filter(Boolean).map(c6).filter(Boolean)}function l6(e){let t=e.headers["fly-client-ip"];return typeof t=="string"&&t.trim()?t.trim():(e.socket&&e.socket.remoteAddress||"").replace(/^::ffff:/,"")}function AR(e){let t=SR(e),n=String(e||"").trim().toLowerCase()==="open";return{open:n,clientIp:l6,allowed(r){if(n)return!0;let o=ib((r||"").replace(/^::ffff:/,""));return o==null?!1:t.some(s=>s(o))}}}function TR(e,t){let n=SR(e);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=ib(o);if(s==null||!n.some(i=>i(s)))return!1;let a=t(r);return!!(a&&a.view)}}}import ER from"node:crypto";import Zp from"node:fs";import u6 from"node:path";function _R(e=process.env,{stateDir:t=el,log:n=console}={}){let r=String(e.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=u6.join(t,"api-token");try{let a=Zp.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=ER.randomBytes(32).toString("base64url");try{Zp.mkdirSync(t,{recursive:!0}),Zp.writeFileSync(o,s+`
|
|
749
|
+
`,{mode:384}),Zp.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function bl(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 IR(e,t){if(!e||!t)return!1;let n=Buffer.from(String(e)),r=Buffer.from(String(t));return n.length!==r.length?!1:ER.timingSafeEqual(n,r)}function $R(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 d6=[/^\/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/],p6=e=>d6.some(t=>t.test(e));function RR({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=$R(o);return IR(c,e)?a():p6(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||bl(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 OR(e,t,{enforce:n=!0}={}){return!n||IR($R(e),t)?!0:bl(e)}In();Fd();Fd();import f6 from"node:http";import m6 from"node:net";function Qp(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 ef(e){let t=Qp(e.headers.host);return t?{slug:t,view:Dw(t)}:null}var h6=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),w6=e=>{let t=h6(e.name);return`<!doctype html><meta charset="utf-8">
|
|
748
750
|
<meta http-equiv="refresh" content="3">
|
|
749
751
|
<title>${t} \u2014 starting</title>
|
|
750
752
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
751
753
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
752
|
-
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function
|
|
754
|
+
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function MR(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 CR(e,t,n){let r=ef(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(w6(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=f6.request({host:"127.0.0.1",port:o.port,method:e.method,path:MR(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 NR(e,t,n){let r=ef(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
|
|
753
755
|
\r
|
|
754
756
|
bostrat: no live view "${s}"\r
|
|
755
|
-
`),t.destroy(),!0;let a=
|
|
757
|
+
`),t.destroy(),!0;let a=m6.connect(o.port,"127.0.0.1",()=>{let c=[`${e.method} ${MR(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
|
|
756
758
|
`)+`\r
|
|
757
759
|
\r
|
|
758
|
-
`),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 kR(e,t){let n=typeof t=="string"?{host:t}:t||{},r=n["x-bostrat-view-base"];return typeof r=="string"&&r?`${n["x-forwarded-proto"]==="https"?"https":"http"}://${e.slug}.${r.replace(/:\d+$/,"")}/`:`http://${e.slug}.${L5(n.host)}/`}function L5(e){if(process.env.BOSTRAT_PUBLIC_HOST)return String(process.env.BOSTRAT_PUBLIC_HOST).replace(/:\d+$/,"");let t=String(e||"").replace(/:\d+$/,"").toLowerCase();if(!t||!t.includes(".")||/^[\d.]+$/.test(t))return"bostrat.globopi";let n=t.split(".");return Gp(t)&&Ow(n[0])?n.slice(1).join("."):t}Ea();Kw();Fd();nw();he();import xR from"node:os";import{randomUUID as rb}from"node:crypto";var F5=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||xR.hostname(),nodeName:e.BOSTRAT_NODE_NAME||xR.hostname()});var vR=32768,SR=120,AR=1e3,TR=new Set(["claude","codex","cursor"]),U5=new Set(["off","transition","nag"]),H5=[[0,59],[0,23],[1,31],[1,12],[0,6]];function W5(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=H5[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 xn=e=>String(e??"").trim(),Is={name(e){let t=xn(e);if(!t)throw new Error("a loop needs a name");if(t.length>SR)throw new Error(`name is too long (${t.length}); cap is ${SR}`);return t},schedule(e){let t=xn(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!W5(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=xn(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>vR)throw new Error(`prompt is too long (${t.length} chars); cap is ${vR}`);return t},expectation(e){let t=xn(e)||null;if(t&&t.length>AR)throw new Error(`expectation is too long (${t.length}); cap is ${AR}`);return t},notify(e){let t=xn(e)||"off";if(!U5.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=xn(typeof t=="string"?t:t?.provider)||"claude";if(!TR.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...TR].join(", ")}`);let r=typeof t=="object"&&t&&xn(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 q5(e,t){let n=e&&typeof e=="object"?e:{},r=Is.name(n.name),o=Is.schedule(n.schedule),s=Is.prompt(n.prompt),a=Is.expectation(n.expectation),i=Is.notify(n.notify),c=Is.assignment(n.assignment),l=xn(t?.workspace?.repo)||xn(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}=F5();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:d||null}}async function _R(e,t){let n=q5(t,e),r=rb();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 IR(e,t,n,{loopId:r=null,nextFireAt:o=null}={}){if(!t)throw new Error("proposalId required");let s=await K({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 nb=["name","schedule","prompt","expectation","notify","assignment","enabled"],K5=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function $R(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 RR(e,t){let n=xn(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 G5(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=>!nb.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: ${nb.join(", ")}`)}let o={};for(let a of nb){if(!(a in n))continue;let i=Is[a](n[a]);(a==="assignment"?K5(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 OR(e,t,n){let r=$R(t),{patch:o,changed:s}=G5(n,r),a=rb(),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 ER=600;async function MR(e,t,n){let r=xn(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>ER)throw new Error(`reason is too long (${r.length}); cap is ${ER}`);let o=$R(t),s=rb(),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 CR=async(e,t,n)=>{if(!n)throw new Error("proposalId required");let r=await K({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 NR(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 CR(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 PR(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 CR(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}}he();import{randomUUID as z5}from"node:crypto";var ab=20,V5=300,ob=120,DR=120,BR=2e3,Vp=new Set(["build","ops","debt","spike"]),jR=new Set(["active","done","abandoned"]),ib=new Set(["circuit.create","circuit.update","circuit.delete"]),J5=new Set(["person.create","slot.create","slot.update","slot.delete","reservation.create","reservation.update","reservation.delete"]),wl=new Set([...ib,...J5]),Y5=["name","description","status","type"],X5=["person","startDate","endDate","estUsd"],Z5=["person","label","startDate","endDate"],Q5=/^\d{4}-\d{2}-\d{2}$/,Kt=e=>String(e??"").trim(),cb=e=>typeof e=="string"&&e.startsWith("@"),UR=864e5;function HR(e){let t=Date.parse(`${e}T00:00:00Z`),n=new Date(t).getUTCDay();return new Date(t-(n+6)%7*UR).toISOString().slice(0,10)}function e6(e){return new Date(Date.parse(`${HR(e)}T00:00:00Z`)+6*UR).toISOString().slice(0,10)}var LR=(e,t)=>{let n=Kt(e);if(!Q5.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 t6(e,t){let n=LR(e,"startDate"),r=LR(t,"endDate");if(r<n)throw new Error(`endDate ${r} is before startDate ${n}`);let o=HR(n),s=e6(r);return{startDate:o,endDate:s,snapped:o!==n||s!==r}}var FR=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},Eo=(e,t,n)=>{let r=Kt(e);if(r.length>t)throw new Error(`${n} is too long (${r.length}); cap is ${t}`);return r};function sb(e,t){let n=Kt(t);if(!n)throw new Error("a circuit name or id is required");if(cb(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 n6(e,t){let n=Kt(t);if(!n)throw new Error('a person is required \u2014 "me", a person id, or a row label');if(n==="me"||cb(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 r6(e,t,{allow:n=wl}={}){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>ab)throw new Error(`too many ops (${o.length}); cap is ${ab} \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)},h=b=>{b.id&&(u[b.id]=b.label)},g=(b,I)=>{let _=Kt(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:{},N=Kt(_.op),U=`op ${I+1}`;if(!wl.has(N))throw new Error(`${U}: unknown op "${N}" \u2014 one of ${[...wl].join(", ")}`);if(!n.has(N))throw new Error(`${U}: "${N}" isn't a circuit op \u2014 roadmap changes go through \`bostrat-roadmap propose\` (one card, either CLI)`);let j=O=>{throw new Error(`${U} (${N}): ${O}`)},T=O=>{let S=_.set&&typeof _.set=="object"&&!Array.isArray(_.set)?_.set:null;S||j("needs a `set` object naming the fields to change");let M=Object.keys(S).filter(H=>!O.includes(H));return M.length&&j(`unknown field(s) ${M.join(", ")} \u2014 settable: ${O.join(", ")}`),Object.keys(S).length||j("`set` names no fields"),S},R=O=>{let{startDate:S,endDate:M,snapped:H}=t6(O.startDate,O.endDate);return H&&(p=!0),{startDate:S,endDate:M,snapped:H}},L=O=>{let S=n6(a,O);return Kt(O)==="me"&&(f=!0),h(S),{key:S.id??Kt(O),label:S.label}},V=(O,S,M,H,W)=>{let te=Kt(O);te||j(`needs a ${H} id`);let pe=S.find(Ie=>Ie[M]===te);return pe||j(`no ${H} "${te}" \u2014 see \`bostrat-roadmap ls\``),W[te]={...pe},pe};switch(N){case"circuit.create":{let O=Eo(_.name,DR,"name");O||j("a circuit needs a name");let S=Kt(_.type)||"build";Vp.has(S)||j(`bad type "${S}" (${[...Vp].join(" | ")})`);let M=s.find(W=>String(W.name).toLowerCase()===O.toLowerCase());M&&j(`a circuit named "${M.name}" already exists (${M.id}) \u2014 update it instead of making a twin`);let H=g(_.ref,O);return{op:N,...H?{ref:H}:{},name:O,description:Eo(_.description,BR,"description"),type:S}}case"circuit.update":{let O=sb(s,_.circuit);O.id||j("can't update a circuit created in the same plan \u2014 set its fields on the create op"),w(O);let S=T(Y5),M={};if(S.name!==void 0){let H=Eo(S.name,DR,"name");H||j("name can't be blank"),H!==O.name&&(M.name=H)}return S.description!==void 0&&(M.description=Eo(S.description,BR,"description")),S.status!==void 0&&(jR.has(S.status)||j(`bad status "${S.status}" (${[...jR].join(" | ")})`),S.status!==O.status&&(M.status=S.status)),S.type!==void 0&&(Vp.has(S.type)||j(`bad type "${S.type}" (${[...Vp].join(" | ")})`),S.type!==(O.type??"build")&&(M.type=S.type)),Object.keys(M).length||j("nothing would change \u2014 the set equals the circuit's current values"),{op:N,circuit:O.id,name:O.name,set:M}}case"circuit.delete":{let O=sb(s,_.circuit);O.id||j("can't delete a circuit created in the same plan"),w(O);let S=o.some((M,H)=>H<I&&Kt(M?.op)==="circuit.update"&&M?.set?.status&&M.set.status!=="active"&&Kt(M?.circuit)===Kt(_.circuit));return O.status==="active"&&!S&&j(`\u201C${O.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:N,circuit:O.id,name:O.name,status:O.status}}case"person.create":{let O=Eo(_.label,ob,"label");O||j("a person row needs a label");let S=a.find(H=>String(H.label).toLowerCase()===O.toLowerCase());S&&j(`a roadmap row labelled "${S.label}" already exists (${S.personId})`);let M=g(_.ref,O);return{op:N,...M?{ref:M}:{},label:O}}case"slot.create":{let O=sb(s,_.circuit);w(O);let S=L(_.person),M=R(_);return{op:N,circuit:O.id??O.ref,circuitName:O.name,person:S.key,personLabel:S.label,...M,estUsd:FR(_.estUsd)}}case"slot.update":{let O=V(_.slot,i,"slotId","slot",l.slots),S=T(X5),M={};if(S.person!==void 0){let W=L(S.person);M.person=W.key,M.personLabel=W.label}if(S.startDate!==void 0||S.endDate!==void 0){let W=R({startDate:S.startDate??O.startDate,endDate:S.endDate??O.endDate});W.startDate!==O.startDate&&(M.startDate=W.startDate),W.endDate!==O.endDate&&(M.endDate=W.endDate)}S.estUsd!==void 0&&(M.estUsd=FR(S.estUsd)),Object.keys(M).length||j("nothing would change");let H=s.find(W=>W.id===O.circuitId);return{op:N,slot:O.slotId,circuitName:H?.name??null,set:M}}case"slot.delete":{let O=V(_.slot,i,"slotId","slot",l.slots),S=s.find(M=>M.id===O.circuitId);return{op:N,slot:O.slotId,circuitName:S?.name??null,startDate:O.startDate,endDate:O.endDate}}case"reservation.create":{let O=L(_.person),S=Eo(_.label,ob,"label");return S||j("a reservation needs a label (Leave, Support rotation, \u2026)"),{op:N,person:O.key,personLabel:O.label,label:S,...R(_)}}case"reservation.update":{let O=V(_.reservation,c,"reservationId","reservation",l.reservations),S=T(Z5),M={};if(S.person!==void 0){let H=L(S.person);M.person=H.key,M.personLabel=H.label}if(S.label!==void 0){let H=Eo(S.label,ob,"label");H||j("label can't be blank"),H!==O.label&&(M.label=H)}if(S.startDate!==void 0||S.endDate!==void 0){let H=R({startDate:S.startDate??O.startDate,endDate:S.endDate??O.endDate});H.startDate!==O.startDate&&(M.startDate=H.startDate),H.endDate!==O.endDate&&(M.endDate=H.endDate)}return Object.keys(M).length||j("nothing would change"),{op:N,reservation:O.reservationId,label:O.label,set:M}}case"reservation.delete":{let O=V(_.reservation,c,"reservationId","reservation",l.reservations);return{op:N,reservation:O.reservationId,label:O.label,startDate:O.startDate,endDate:O.endDate}}default:return j("unhandled op")}});y.forEach((b,I)=>{for(let _ of[b.circuit,b.person,b.set?.person])if(cb(_)&&!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:Eo(r.why,V5,"why"),ops:y,base:l,resolved:u,snapped:p,createsMeRow:f&&!x}}async function WR(e,t,n,r={}){let o=r6(t,n,r),s=z5(),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 o6=async(e,t)=>{if(!t)throw new Error("proposalId required");let n=await K({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 qR(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 o6(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,ab+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}}Ed();he();Dr();he();Mn();Fd();var KR=10,GR=200,s6=120,a6=24,VR=/^\d{4}-\d{2}-\d{2}$/;function lb(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var i6=(e=Date.now())=>new Date(e).toISOString().slice(0,10),bt=(e,t="i")=>new RegExp(e,t),c6=[{re:bt("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:bt("\\bSPC\\b",""),reason:"business (SPC)"},{re:bt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:bt("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:bt("\\bReplicas\\b",""),reason:"competitor research"},{re:bt("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:bt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:bt("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],l6=[bt("\\bSPC\\b",""),bt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),bt("competitor|cloudcli|\\bkiro\\b"),bt("\\bReplicas\\b",""),bt("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),bt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),bt("(^|[^\\w&])#\\d+\\b"),bt("\\btrack/[\\w.-]+")];function u6({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of c6)if(o.test(n)||o.test(r))return s;return null}function zR(e){let t=String(e||"");for(let n of l6){let r=n.exec(t);if(r)return r[0].trim()}return null}function JR(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 d6=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function p6(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Ic(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(`
|
|
759
|
-
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,s6)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${a6}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=d6.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=zR(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>GR)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${GR}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=zR(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>KR)throw new Error(`${r.length} bullets \u2014 over the ${KR}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function f6(e,{repos:t=lb()}={}){let n=new Set(t);return(await pn()).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:u6({title:r.title,headRef:r.headRef})}))}async function YR(e,t){let n=await f6(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function XR(e,{md:t,day:n=null}){let r=p6(t),o=n==null||n===""?i6():String(n);if(!VR.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 ZR(e,{day:t}){let n=String(t||"");if(!VR.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}}rs();Mn();ss();he();rs();mt();dr();Mn();ec();xd();ad();ac();Yt();import{execFile as T6}from"node:child_process";import{promisify as E6}from"node:util";var h6=new Set(["pull_request","check_suite","workflow_run","push"]),m6=/^[^/\s]+\/[^/\s]+$/,w6=500,ub=new Set,db=[],Jp=new Map,nn={hourStart:0,byEvent:{},duplicates:0,rejected:0};function pb(e,t,n){let r=Math.floor(e/36e5)*36e5;r!==nn.hourStart&&(nn.hourStart=r,nn.byEvent={},nn.duplicates=0,nn.rejected=0),t==="accepted"?nn.byEvent[n]=(nn.byEvent[n]||0)+1:nn[t]+=1}function g6(e){if(!e||typeof e!="object"||e.forge!=="github"||typeof e.event!="string"||!h6.has(e.event)||typeof e.repo!="string"||!m6.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 QR(e,{now:t=Date.now(),onAccept:n=null,log:r=console}={}){let o=g6(e);if(!o)return pb(t,"rejected"),{ok:!1,error:"malformed github event envelope"};if(o.deliveryId){if(ub.has(o.deliveryId))return pb(t,"duplicates"),{ok:!0,duplicate:!0,envelope:o};for(ub.add(o.deliveryId),db.push(o.deliveryId);db.length>w6;)ub.delete(db.shift())}let s=o.repo.toLowerCase(),a=Jp.get(s)||{lastDeliveryAt:0,count:0};a.lastDeliveryAt=t,a.count+=1,Jp.set(s,a),pb(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 y6(e,t=Date.now()){let n=Jp.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 eO=1440*6e4;function fb(e,t=Date.now()){let n=y6(e,t);return n.covered&&n.ageMs<eO}function tO(e=Date.now()){let t={};for(let[n,r]of Jp)t[n]={lastDeliveryAt:r.lastDeliveryAt,count:r.count,covered:e-r.lastDeliveryAt<eO};return{hourStart:nn.hourStart,byEvent:{...nn.byEvent},duplicates:nn.duplicates,rejected:nn.rejected,repos:t}}he();Mn();Or();dr();Yt();function nO(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function b6(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var k6=new Set(["merged","closed","lost"]);function x6(e,t){if(!e?.blocked)return null;let n=nO(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 v6(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function S6(e,t,n,r,o){return(await K(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 A6(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 Yp(e,t={}){let{now:n=Date.now(),slugOf:r=_e,lookup:o=ct,note:s=sA,tracks:a=async()=>(await we()).filter(d=>!je(d)),asked:i=S6,emitChange:c=A6}=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(!b6(f))continue;let w=await o(p,f.number).catch(()=>null);if(!w?.trackId||k6.has(w.state))continue;let{prev:h}=await s(p,f.number,{blocked:nO(f),sha:f.headSha??null,at:n});if(!h)continue;let g=x6(h,f);if(!g||v6(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,h.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 wb=E6(T6),lO=3e4,_6=30*6e4,I6=lO,$6=10*6e4,R6=10080*6e4,O6=60*6e4,M6=8,uO=1440*6e4,C6=3,dO="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",mb=60*6e4,N6=15*6e4,P6=9e4,D6=1440*6e4,Xp=new Map,Zp=new Map,B6=/could not resolve|not found|no pull requests?/i,j6=e=>B6.test(String(e?.message||e||""));function L6(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<mb)}var rO={fast:I6,slow:$6,decay:O6};function oO(e,t,n=fb){return pO(e,t,{grace:0})||!n(e.repo,t)&&(e.armedAt&&t-e.armedAt<_6||L6(e,t))?"fast":t-(e.firstSeen||0)>R6?"decay":"slow"}function yl(e,t,n,r){let o=e.lanes?.get(ce(t.repo,t.number))||{};return sd({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function F6(e,t){ar("watch");try{let{stdout:n}=await wb("gh",["pr","view",String(t),"--repo",e,"--json",dO],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Sn({slug:e})}});return JSON.parse(n||"null")}catch(n){throw oo(n)&&so(n),n}}async function U6(e){ar("watch");try{let{stdout:t}=await wb("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",dO],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await Sn({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw oo(t)&&so(t),t}}async function H6(){let e=new Map;for(let t of await we().catch(()=>[]))e.set(t.id,t);return e}async function W6(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 kd(e,t).catch(()=>{})}async function q6(e,t){let n=await ns(e);return n?ha(n,t):null}async function K6(e,t,n){let r=await ns(e);if(!r)throw new Error(`no local checkout for ${e}`);return ma(r,t,{method:n})}async function G6(e,t){let n=await ns(e);if(!n)throw new Error(`no local checkout for ${e}`);return rc(n,t)}async function z6(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 V6=10*6e4;async function J6(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>uO){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<V6)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 fO(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function Va(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 Y6(e,t){try{return await wb("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function gl(e,t,n,r=()=>!0){return(await K(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var Ja=(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 pO(e,t,{grace:n=P6}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=D6}async function X6(e,t,n,r){let o=r.now??Date.now();if(!pO(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||je(s))return!1;let a=(s.boxes||[]).find(d=>d.name===s.leadBox);return await(r.midTurn??os)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await K(s).catch(()=>[])).some(d=>d.type==="pr.state.changed"&&d.payload?.number===Number(e.number))?(await(r.claim??or)(e.repo,e.number).catch(()=>null),!1):await(r.claim??or)(e.repo,e.number).catch(()=>null)?(await Va(s,"opened",{...Ja(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 Z6(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 Xu(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await Qu(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<N6;await oA(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await rA(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await gl(a,"pushed",e.number,d=>d.payload?.sha===s)&&await Va(a,"pushed",{...Ja(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>mb)await Zu(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 Zu(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=yl(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await gl(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await Va(a,"ci-fixed",{...Ja(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await Zu(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>mb)await ed(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await ed(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){yl(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await gl(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await Va(a,"reconciled",{...Ja(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await ed(e.repo,e.number).catch(()=>{})}}}}async function Q6(e,t,n,r){let o=yl(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await gl(s,"closed",e.number)&&await Va(s,"closed",{...Ja(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 eK(e,t,n){yl(n,e,"lost",null);let r=t.get(e.trackId);r&&!await gl(r,"lost",e.number)&&await Va(r,"lost",Ja(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function fO(e,t,n,r,o={}){let s=yl(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await K(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 sO(e={}){let{now:t=Date.now(),auth:n=Mm,view:r=F6,list:o=U6,links:s=cA,trackMap:a=H6,emitMerged:i=W6,mark:c=no,archiveIfArmed:l=lr,notifyIfArmed:u=ur,notifyIfResolved:d=lp,followupsIfMerged:p=gs,batchThreshold:f=C6,detail:w=q6,merge:h=K6,updateBranch:g=G6,lookup:y=ct,disarm:x=Qi,emitDisarmed:b=z6,localHasCommit:I=Y6,claim:_=or,midTurn:N=os,only:U=null,wantDetail:j=null,covered:T=fb,verdicts:R=null}=e;if(!(await n().catch(()=>null))?.authenticated||rd()>0)return 0;let V=await s().catch(()=>[]),O=MA(V),S=G=>{try{return!!j?.(G)}catch{return!1}},M=U?V.filter(G=>{try{return U(G)}catch{return!1}}):V.filter(G=>t-(Xp.get(ce(G.repo,G.number))||0)>=rO[oO(G,t,T)]);if(!M.length)return 0;let H=Date.now(),W=new Map,te=new Map;for(let G of[...M].sort(oc)){let fe=U?"event":oO(G,t,T);W.set(ce(G.repo,G.number),{lane:fe,intervalMs:rO[fe]??null}),Xp.set(ce(G.repo,G.number),t),te.has(G.repo)||te.set(G.repo,[]),te.get(G.repo).push(G)}let pe=await a().catch(()=>new Map),Ie=new Map,Oe=(G,fe)=>{let be=ce(G,fe);if(!Ie.has(be)){let xe=Promise.resolve().then(()=>w(G,fe));xe.catch(()=>{}),Ie.set(be,xe)}return Ie.get(be)},re=R??((G,fe)=>Yp([{path:G,prs:[fe]}],{now:t,slugOf:async()=>G,tracks:async()=>[...pe.values()].filter(be=>!je(be))}).catch(()=>{})),Ne={now:t,lanes:W,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,followupsIfMerged:p,detail:Oe,merge:h,updateBranch:g,lookup:y,disarm:x,emitDisarmed:b,localHasCommit:I,claim:_,midTurn:N};for(let[G,fe]of te){let be,xe=new Set,Ke=le=>le.ciFixAwait||le.reconcileAwait||S(le)?!0:!le.autoMerge||le.trackId&&O.get(le.trackId)!==ce(le.repo,le.number)?!1:t-(le.autoMerge.armedAt||0)<=uO;if(fe.length>f)be=await o(G).catch(()=>null);else{be=new Map;for(let le of fe){if(Ke(le)){let $=await Oe(G,le.number).catch(()=>null);if($?.state){be.set(Number(le.number),{number:Number(le.number),url:$.url,title:$.title,headRefName:$.headRefName,headRefOid:$.headSha??null,state:$.state,mergedAt:$.mergedAt??null,closedAt:$.closedAt??null});continue}}try{let $=await r(G,le.number);$?.number!=null&&be.set(Number($.number),$)}catch($){j6($)&&xe.add(ce(le.repo,le.number))}}}if(be)for(let le of fe){let $=ce(le.repo,le.number),Y=be.get(Number(le.number));if(!Y){if(xe.has($)){let Ce=(Zp.get($)||0)+1;Zp.set($,Ce),Ce>=M6&&(await eK(le,pe,Ne),Zp.delete($),Xp.delete($))}continue}if(Zp.delete($),Y.state==="MERGED"||Y.state==="CLOSED")Y.state==="MERGED"?await fO(le,Y,pe,Ne):await Q6(le,Y,pe,Ne),Xp.delete($);else{await X6(le,Y,pe,Ne).catch(()=>{}),await Z6(le,Y,pe,Ne).catch(()=>{});let Ce=!le.trackId||O.get(le.trackId)===$;le.autoMerge&&Ce&&await J6(le,pe,Ne).catch(()=>{});try{if(S(le)&&!Ie.has($)&&Oe(le.repo,le.number),Ie.has($)){let Ve=await Ie.get($).catch(()=>null);Ve?.checks&&await re(le.repo,Ve)}}catch{}}}}let X={};for(let{lane:G}of W.values())X[G]=(X[G]||0)+1;return console.log(`[pr-watch] probed ${M.length} link(s) (${Object.entries(X).map(([G,fe])=>`${G} ${fe}`).join(", ")}) in ${Date.now()-H}ms`),M.length}function aO(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 hb=!1,Qp=!1,ef=[],iO=Promise.resolve();async function tK(){for(;Qp||ef.length;){if(Qp){Qp=!1;try{await sO()}catch{}continue}let e=ef;ef=[];let t=e.map(r=>r.spec),n=Object.assign({},...e.map(r=>r.deps||{}));try{await sO({...n,only:r=>t.some(o=>aO(o,r)),wantDetail:r=>t.some(o=>o.detail&&aO(o,r))})}catch{}}}function tf(e,t){return e===null?Qp=!0:ef.push({spec:e,deps:t}),hb||(hb=!0,iO=tK().finally(()=>{hb=!1})),iO}function hO(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 tf({repo:n,numbers:r,branch:e.branch||null,event:e.event||null,detail:e.detail===!0},t)}var nK=6e4,cO=0;function mO(e=void 0){let t=e?.now??Date.now();return t-cO<nK?Promise.resolve():(cO=t,tf({all:!0},e))}function wO(){tf(null),setInterval(()=>tf(null),lO)}rs();vd();Ac();dr();Yt();var rK=new Set(["opened","reopened","closed","synchronize","ready_for_review","converted_to_draft","edited"]);async function oK({tracks:e=we,reconcile:t=Nd}={}){for(let n of(await e().catch(()=>[])).filter(r=>!je(r)))await t(n,{bypassThrottle:!0}).catch(()=>{})}function gO(e,t={}){let{invalidate:n=Zt,probe:r=hO,poke:o=mT,reconcileOpened:s=oK}=t;try{n()}catch{}let a={probe:null,poked:!1,reconciled:!1};if(!e?.repo)return a;if(e.event==="pull_request"&&rK.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}ut();he();dr();Yt();import{randomUUID as sK}from"node:crypto";var aK=2e4,yO=3,Ya=new Map,gb=new Set;async function bO(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 iK(e={}){let{sessions:t=null,tracks:n=null,append:r=C,read:o=K,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await sn(),i=n??await we()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(je(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=(Ya.get(d.name)||0)+1;Ya.set(d.name,f),f===yO&&(gb.add(d.name),await bO(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:sK(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let f=(Ya.get(d.name)||0)>=yO;if(Ya.delete(d.name),f||!gb.has(d.name)){gb.add(d.name);let w=await bO(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[...Ya.keys()])l.has(u)||Ya.delete(u)}var bl=null,yb=!1;function kO(){return bl?()=>{}:(bl=setInterval(()=>{yb||(yb=!0,iK().catch(()=>{}).finally(()=>{yb=!1}))},aK),bl.unref?.(),()=>{clearInterval(bl),bl=null})}Zo();Ac();he();rs();ss();Zo();dc();import{randomUUID as cK}from"node:crypto";async function lK(){let e=await Promise.resolve().then(()=>(dr(),gI));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var bb={listRepoPrs:Cn,readEvents:K,appendEvent:C,sayDeps:lK,currentBranch:ao};async function uK(e,t=bb,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??ao)(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 kb(e,t,{actions:n,notify:r=!1}={},o=bb){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 uK(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=cK();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 xb(e,{offerId:t,choice:n}={},r=bb){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');return co(`prx:${e.id}:${t}`,()=>dK(e,{offerId:t,choice:n},r))}async function dK(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=eo.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(e.id,ua(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,GS(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}}dc();import Tr from"node:fs/promises";import Xa from"node:path";import _O from"node:os";import xO from"node:fs/promises";import nf from"node:path";import{execFile as pK}from"node:child_process";import{promisify as fK}from"node:util";import{fileURLToPath as hK}from"node:url";var mK=fK(pK),SO=nf.dirname(hK(import.meta.url)),wK=nf.resolve(SO,"..","..","client","dist"),gK="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",yK=new Map([...gK].map((e,t)=>[e,t]));function bK(e){let t=[],n=0,r=0;for(let o of e){let s=yK.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 kK(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=bK(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 xK(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 vO=new Map;async function vK(e){let t;try{t=await xO.stat(e)}catch{return null}let n=vO.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await xO.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:kK(r.mappings||"")};return vO.set(e,o),o}catch{return null}}var vb=null;function SK(){return vb||(vb=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):mK("git",["rev-parse","--short","HEAD"],{cwd:nf.resolve(SO,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),vb}var AK=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function TK(e,{distDir:t=wK}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(AK)){let[a,i,c,l]=s,u=await vK(nf.join(t,"assets",`${i}.map`)),d=u?xK(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 AO(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await SK();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await TK(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var EK=()=>process.env.BOSTRAT_STATE_DIR||Xa.join(process.env.HOME||_O.tmpdir(),".bostrat"),_o=()=>Xa.join(EK(),"telemetry"),IO=()=>Xa.join(_o(),".relay-offsets.json"),_K=50,IK=14,$K=3e4,TO=500,Gt=(e,t)=>typeof e=="string"?e.slice(0,t):"";function RK(e,t){if(!e||typeof e!="object")return null;let n=Gt(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:Gt(e.anonId,64),sessionId:Gt(e.sessionId,64),userId:Gt(e.userId,64),release:Gt(e.release,64),appVersion:Gt(e.appVersion,32),route:Gt(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:Gt(e.error.kind,256),message:Gt(e.error.message,8192),stack:Gt(e.error.stack,32768),fingerprint:Gt(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:Gt(s?.n,64),r:Gt(s?.r,128)}))),o}var $O=e=>new Date(e).toISOString().slice(0,10),EO=0;async function RO(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,_K):[]).map(s=>RK(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await AO(s);await Tr.mkdir(_o(),{recursive:!0});let o=Xa.join(_o(),`${$O(t)}.ndjson`);return await Tr.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
760
|
-
`).join(""),"utf8"),t-
|
|
761
|
-
`).filter(Boolean),d=[];for(let p of u)try{d.push(JSON.parse(p))}catch{}for(let p=0;p<d.length;p+=TO){let f=d.slice(p,p+TO).map(h=>MK(h)),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 CO(r)}return{sent:o,disabled:!1}}var Sb=null,Ab=!1;function NO(){Sb||(Sb=setInterval(async()=>{if(!Ab){Ab=!0;try{await CK()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{Ab=!1}}},$K),Sb.unref?.())}import sf from"node:fs/promises";import LO from"node:path";import NK from"node:os";var FO=()=>process.env.BOSTRAT_STATE_DIR||LO.join(process.env.HOME||NK.tmpdir(),".bostrat"),UO=()=>LO.join(FO(),"dictation-glossary.json"),PK=100,DK=500,BK=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],af=null,Tb=null,Eb=Promise.resolve();function PO(){let e={},t=Date.now();for(let n of BK)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function HO(){return af||(Tb??=sf.readFile(UO(),"utf8").then(e=>JSON.parse(e)?.terms??PO()).catch(()=>PO()).then(e=>af=e).finally(()=>Tb=null),Tb)}async function DO(){let e=JSON.stringify({terms:af},null,2),t=UO(),n=`${t}.${process.pid}.tmp`;await sf.mkdir(FO(),{recursive:!0}),await sf.writeFile(n,e),await sf.rename(n,t)}function jK(){return Eb=Eb.then(DO,DO),Eb.catch(()=>{})}async function _b(){return Object.values(await HO()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var LK=e=>Math.max(1,Math.ceil(e.length/4));async function FK(){let e=[],t=0;for(let{term:n}of await _b()){let r=LK(n);if(!(t+r>DK)&&(e.push(n),t+=r,e.length>=PK))break}return e}async function UK(e,t){let n=await HO(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var BO=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),of=e=>e.toLowerCase();function jO(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function WO(e,t){let n=BO(e),r=BO(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]=of(n[u])===of(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;)of(n[c])===of(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(jO(r[l])&&i.add(r[l]),l++);for(;l<s;l++)jO(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await UK(u,"learned");return await jK(),[...i]}var kl=null;function HK(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 qO(){if(!kl)return null;let e=await tl();if(!e||e.apiKey.slice(-6)!==kl.keyTail)return null;let{keyTail:t,...n}=kl;return n}async function KO(e,t){let n=await tl();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 FK())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=HK(o.status,i);c.persistent&&(kl={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 kl=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}he();var Ib={"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 WK(e){if(!e)return null;let t=String(e).trim();if(Ib[t])return t;let n=null;for(let r of Object.keys(Ib))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function qK(e,t={}){let n=Ib[WK(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 KK(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 xl=null;async function GK(){if(!xl)return null;let e=await Op();if(!e||e.apiKey.slice(-6)!==xl.keyTail)return null;let{keyTail:t,...n}=xl;return n}async function zK(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=KK(t.status,o);s.persistent&&(xl={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}xl=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 VK={openai:zK};async function GO(){let e=await Op();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await GK()}:{configured:!1}}async function zO(){let e=await Op();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 VK[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function VO(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 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}}Yt();var nM=Ee.dirname(YK(import.meta.url)),JO=Number(process.env.PORT)||3362,cf=uy(process.env),rM=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,Za=lR(process.env.BOSTRAT_ALLOW_IPS),Cb=uR(process.env.BOSTRAT_VIEW_FRONT_CIDRS,zp),Nb=pR(),oM=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",E=zt();E.disable("x-powered-by");E.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),Gp(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(ml(e)||Za.allowed(Za.clientIp(e))||Cb.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});E.use(yR);E.use(mR({token:Nb,enforce:oM}));E.use("/api/mocks",zt.json({limit:"1mb"}));E.use("/api/mocks",zt.raw({type:"image/png",limit:"24mb"}));E.use("/api/mocks",zt.raw({type:["video/mp4","video/webm"],limit:"105mb"}));E.use("/api/runbooks",zt.json({limit:"1mb"}));E.use("/api/tracks/adopt",zt.json({limit:"1mb"}));E.use("/api/telemetry",zt.json({limit:"256kb"}));E.use("/api/hook",zt.json({limit:"2mb"}));E.use("/api/dictation",zt.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));E.use("/api/tracks/:id/upload",zt.raw({type:()=>!0,limit:"25mb"}));E.use(zt.json({limit:"64kb"}));E.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:jt,hostTmuxAlive:await jr(),autoTriggerPct:rM,plane:Qa?Qa.status():null})});E.get("/api/sessions",async(e,t)=>{t.json({sessions:await sn()})});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 En(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(!De(n))return t.status(400).json({error:"invalid session name"});if(!await ye(n))return t.status(404).json({error:"session not found"});try{await Ot(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(!De(n))return t.status(400).json({error:"invalid session name"});let r=await ye(n);if(!r)return t.status(404).json({error:"session not found"});let o=await Ri(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(!De(n))return t.status(400).json({error:"invalid session name"});try{let r=await av(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=$u(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 YO=10;async function sM(){let e=await we(),[t,n]=await Promise.all([AI(e),EI(e)]);return{tracks:e,views:await Rw(),queuedOf:r=>ps(r),jobsOf:r=>t.get(r)??[],prPendingOf:r=>n.get(r)??[]}}E.get("/api/idle-guard",async(e,t)=>{try{t.json(ay(await sM()))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var rn=bs(),nG=FI({kind:rn}),Sl=null,Et=()=>Sl&&["auto","prompt","off"].includes(Sl)?Sl:nG,Mb=null,rG=Qc.has(rn),aM=async({to:e,from:t})=>!rG||Ap(await el())?!1:(Tl.arm({to:e,from:t,kind:rn}),!0);async function oG(e){let t=Et();Sl=e??null;let n=Et();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){Tl.cancel();return}Mb&&await aM(Mb)}}var iM=(()=>{try{return tG(import.meta.url)("../package.json").version||null}catch{return null}})(),Tl=HI({isBusy:async()=>ay(await sM()).busy,onStuck:({to:e,from:t})=>Kn({current:t,latest:e,installKind:rn,policy:Et(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?Kn({current:t,latest:e,installKind:rn,policy:Et(),status:"updating",reason:"auto"}):Kn({current:t,latest:e,installKind:rn,policy:Et(),status:"failed",error:n?.reason})).catch(()=>{})});E.get("/api/node/self-update",async(e,t)=>{t.json({installKind:rn,policy:Et(),policySource:Sl?"plane":"node",current:iM,status:await el(),armed:Tl.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 Tp({to:n,kind:rn,reason:"manual"});if(r.ok)return Tl.cancel(),Kn({current:r.from,latest:n,installKind:rn,policy:Et(),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 cM(){try{let e=await UI();e&&await Kn({current:e.from,latest:e.to,installKind:e.kind||rn,policy:Et(),status:e.status,error:e.error,reason:e.reason}),await vg({current:iM,installKind:rn,policy:Et()})}catch{}}cM();setInterval(cM,3e4).unref();E.get("/api/tracks",async(e,t)=>{let n=(await we()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>YO){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,YO).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:Wc,agentAvailability:await tp(),tickets:await h$()})});E.get("/api/cost",async(e,t)=>{try{let n=await we();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await Gy(n);if(r.cursorUsage=hw(),e.query.format==="text")return t.type("text/plain").send(L$(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 Vy(await we(),{window:e.query.window,from:e.query.from,to:e.query.to});if(e.query.format==="text")return t.type("text/plain").send(q$(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 J$(await we(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=hw(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=Yi(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/usage/caps",(e,t)=>{t.json(Yi())});E.post("/api/usage/caps",async(e,t)=>{try{t.json(await Wu(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 oR(await we(),{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 Jb(e.get(Yn),{byToken:qc,legacy:()=>hc(e.body?.session_id,e.body?.cwd)});if(n==="refused")return si("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return si("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&&si("mismatch",{track:r.track.id,box:r.box.name}),si(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 D$(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&zl(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 qc(e.get(Yn));if(!n?.track||!n?.box)return t.status(204).end();t.send((await QA(n.track,n.box)).join(`
|
|
760
|
+
`),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 PR(e,t){let n=typeof t=="string"?{host:t}:t||{},r=n["x-bostrat-view-base"];return typeof r=="string"&&r?`${n["x-forwarded-proto"]==="https"?"https":"http"}://${e.slug}.${r.replace(/:\d+$/,"")}/`:`http://${e.slug}.${g6(n.host)}/`}function g6(e){if(process.env.BOSTRAT_PUBLIC_HOST)return String(process.env.BOSTRAT_PUBLIC_HOST).replace(/:\d+$/,"");let t=String(e||"").replace(/:\d+$/,"").toLowerCase();if(!t||!t.includes(".")||/^[\d.]+$/.test(t))return"bostrat.globopi";let n=t.split(".");return Qp(t)&&Dw(n[0])?n.slice(1).join("."):t}$a();Yw();Vd();Fu();fe();import DR from"node:os";import{randomUUID as lb}from"node:crypto";var y6=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||DR.hostname(),nodeName:e.BOSTRAT_NODE_NAME||DR.hostname()});var BR=32768,jR=120,LR=1e3,FR=new Set(["claude","codex","cursor"]),b6=new Set(["off","transition","nag"]),k6=[[0,59],[0,23],[1,31],[1,12],[0,6]];function x6(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=k6[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(),$s={name(e){let t=An(e);if(!t)throw new Error("a loop needs a name");if(t.length>jR)throw new Error(`name is too long (${t.length}); cap is ${jR}`);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(!x6(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>BR)throw new Error(`prompt is too long (${t.length} chars); cap is ${BR}`);return t},expectation(e){let t=An(e)||null;if(t&&t.length>LR)throw new Error(`expectation is too long (${t.length}); cap is ${LR}`);return t},notify(e){let t=An(e)||"off";if(!b6.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(!FR.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...FR].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 v6(e,t){let n=e&&typeof e=="object"?e:{},r=$s.name(n.name),o=$s.schedule(n.schedule),s=$s.prompt(n.prompt),a=$s.expectation(n.expectation),i=$s.notify(n.notify),c=$s.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}=y6();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:d||null}}async function HR(e,t){let n=v6(t,e),r=lb();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 WR(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 cb=["name","schedule","prompt","expectation","notify","assignment","enabled"],S6=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function qR(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 KR(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 A6(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=>!cb.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: ${cb.join(", ")}`)}let o={};for(let a of cb){if(!(a in n))continue;let i=$s[a](n[a]);(a==="assignment"?S6(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 GR(e,t,n){let r=qR(t),{patch:o,changed:s}=A6(n,r),a=lb(),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 UR=600;async function VR(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>UR)throw new Error(`reason is too long (${r.length}); cap is ${UR}`);let o=qR(t),s=lb(),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 zR=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 JR(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 zR(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 YR(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 zR(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}}fe();import{randomUUID as T6}from"node:crypto";var pb=20,E6=300,ub=120,XR=120,ZR=2e3,tf=new Set(["build","ops","debt","spike"]),QR=new Set(["active","done","abandoned"]),fb=new Set(["circuit.create","circuit.update","circuit.delete"]),_6=new Set(["person.create","slot.create","slot.update","slot.delete","reservation.create","reservation.update","reservation.delete"]),kl=new Set([...fb,..._6]),I6=["name","description","status","type"],$6=["person","startDate","endDate","estUsd"],R6=["person","label","startDate","endDate"],O6=/^\d{4}-\d{2}-\d{2}$/,Vt=e=>String(e??"").trim(),mb=e=>typeof e=="string"&&e.startsWith("@"),nO=864e5;function rO(e){let t=Date.parse(`${e}T00:00:00Z`),n=new Date(t).getUTCDay();return new Date(t-(n+6)%7*nO).toISOString().slice(0,10)}function M6(e){return new Date(Date.parse(`${rO(e)}T00:00:00Z`)+6*nO).toISOString().slice(0,10)}var eO=(e,t)=>{let n=Vt(e);if(!O6.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 C6(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=rO(n),s=M6(r);return{startDate:o,endDate:s,snapped:o!==n||s!==r}}var tO=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 db(e,t){let n=Vt(t);if(!n)throw new Error("a circuit name or id is required");if(mb(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 N6(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"||mb(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 P6(e,t,{allow:n=kl}={}){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>pb)throw new Error(`too many ops (${o.length}); cap is ${pb} \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 R=Vt(b);if(!R)return null;if(d.has(R))throw new Error(`ref "${R}" is used by two ops \u2014 each ref names one thing`);return d.add(R),u[`@${R}`]=I,R},y=o.map((b,I)=>{let R=b&&typeof b=="object"?b:{},N=Vt(R.op),D=`op ${I+1}`;if(!kl.has(N))throw new Error(`${D}: unknown op "${N}" \u2014 one of ${[...kl].join(", ")}`);if(!n.has(N))throw new Error(`${D}: "${N}" isn't a circuit op \u2014 roadmap changes go through \`bostrat-roadmap propose\` (one card, either CLI)`);let j=M=>{throw new Error(`${D} (${N}): ${M}`)},T=M=>{let S=R.set&&typeof R.set=="object"&&!Array.isArray(R.set)?R.set:null;S||j("needs a `set` object naming the fields to change");let _=Object.keys(S).filter(H=>!M.includes(H));return _.length&&j(`unknown field(s) ${_.join(", ")} \u2014 settable: ${M.join(", ")}`),Object.keys(S).length||j("`set` names no fields"),S},$=M=>{let{startDate:S,endDate:_,snapped:H}=C6(M.startDate,M.endDate);return H&&(p=!0),{startDate:S,endDate:_,snapped:H}},F=M=>{let S=N6(a,M);return Vt(M)==="me"&&(f=!0),m(S),{key:S.id??Vt(M),label:S.label}},J=(M,S,_,H,K)=>{let X=Vt(M);X||j(`needs a ${H} id`);let pe=S.find(Oe=>Oe[_]===X);return pe||j(`no ${H} "${X}" \u2014 see \`bostrat-roadmap ls\``),K[X]={...pe},pe};switch(N){case"circuit.create":{let M=_o(R.name,XR,"name");M||j("a circuit needs a name");let S=Vt(R.type)||"build";tf.has(S)||j(`bad type "${S}" (${[...tf].join(" | ")})`);let _=s.find(K=>String(K.name).toLowerCase()===M.toLowerCase());_&&j(`a circuit named "${_.name}" already exists (${_.id}) \u2014 update it instead of making a twin`);let H=g(R.ref,M);return{op:N,...H?{ref:H}:{},name:M,description:_o(R.description,ZR,"description"),type:S}}case"circuit.update":{let M=db(s,R.circuit);M.id||j("can't update a circuit created in the same plan \u2014 set its fields on the create op"),w(M);let S=T(I6),_={};if(S.name!==void 0){let H=_o(S.name,XR,"name");H||j("name can't be blank"),H!==M.name&&(_.name=H)}return S.description!==void 0&&(_.description=_o(S.description,ZR,"description")),S.status!==void 0&&(QR.has(S.status)||j(`bad status "${S.status}" (${[...QR].join(" | ")})`),S.status!==M.status&&(_.status=S.status)),S.type!==void 0&&(tf.has(S.type)||j(`bad type "${S.type}" (${[...tf].join(" | ")})`),S.type!==(M.type??"build")&&(_.type=S.type)),Object.keys(_).length||j("nothing would change \u2014 the set equals the circuit's current values"),{op:N,circuit:M.id,name:M.name,set:_}}case"circuit.delete":{let M=db(s,R.circuit);M.id||j("can't delete a circuit created in the same plan"),w(M);let S=o.some((_,H)=>H<I&&Vt(_?.op)==="circuit.update"&&_?.set?.status&&_.set.status!=="active"&&Vt(_?.circuit)===Vt(R.circuit));return M.status==="active"&&!S&&j(`\u201C${M.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:N,circuit:M.id,name:M.name,status:M.status}}case"person.create":{let M=_o(R.label,ub,"label");M||j("a person row needs a label");let S=a.find(H=>String(H.label).toLowerCase()===M.toLowerCase());S&&j(`a roadmap row labelled "${S.label}" already exists (${S.personId})`);let _=g(R.ref,M);return{op:N,..._?{ref:_}:{},label:M}}case"slot.create":{let M=db(s,R.circuit);w(M);let S=F(R.person),_=$(R);return{op:N,circuit:M.id??M.ref,circuitName:M.name,person:S.key,personLabel:S.label,..._,estUsd:tO(R.estUsd)}}case"slot.update":{let M=J(R.slot,i,"slotId","slot",l.slots),S=T($6),_={};if(S.person!==void 0){let K=F(S.person);_.person=K.key,_.personLabel=K.label}if(S.startDate!==void 0||S.endDate!==void 0){let K=$({startDate:S.startDate??M.startDate,endDate:S.endDate??M.endDate});K.startDate!==M.startDate&&(_.startDate=K.startDate),K.endDate!==M.endDate&&(_.endDate=K.endDate)}S.estUsd!==void 0&&(_.estUsd=tO(S.estUsd)),Object.keys(_).length||j("nothing would change");let H=s.find(K=>K.id===M.circuitId);return{op:N,slot:M.slotId,circuitName:H?.name??null,set:_}}case"slot.delete":{let M=J(R.slot,i,"slotId","slot",l.slots),S=s.find(_=>_.id===M.circuitId);return{op:N,slot:M.slotId,circuitName:S?.name??null,startDate:M.startDate,endDate:M.endDate}}case"reservation.create":{let M=F(R.person),S=_o(R.label,ub,"label");return S||j("a reservation needs a label (Leave, Support rotation, \u2026)"),{op:N,person:M.key,personLabel:M.label,label:S,...$(R)}}case"reservation.update":{let M=J(R.reservation,c,"reservationId","reservation",l.reservations),S=T(R6),_={};if(S.person!==void 0){let H=F(S.person);_.person=H.key,_.personLabel=H.label}if(S.label!==void 0){let H=_o(S.label,ub,"label");H||j("label can't be blank"),H!==M.label&&(_.label=H)}if(S.startDate!==void 0||S.endDate!==void 0){let H=$({startDate:S.startDate??M.startDate,endDate:S.endDate??M.endDate});H.startDate!==M.startDate&&(_.startDate=H.startDate),H.endDate!==M.endDate&&(_.endDate=H.endDate)}return Object.keys(_).length||j("nothing would change"),{op:N,reservation:M.reservationId,label:M.label,set:_}}case"reservation.delete":{let M=J(R.reservation,c,"reservationId","reservation",l.reservations);return{op:N,reservation:M.reservationId,label:M.label,startDate:M.startDate,endDate:M.endDate}}default:return j("unhandled op")}});y.forEach((b,I)=>{for(let R of[b.circuit,b.person,b.set?.person])if(mb(R)&&!d.has(String(R).slice(1)))throw new Error(`op ${I+1} (${b.op}): unknown ref "${R}" \u2014 no earlier op in this plan creates it`)});let k=a.some(b=>b.kind==="user"&&b.userId);return{why:_o(r.why,E6,"why"),ops:y,base:l,resolved:u,snapped:p,createsMeRow:f&&!k}}async function oO(e,t,n,r={}){let o=P6(t,n,r),s=T6(),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 D6=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 sO(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 D6(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,pb+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}}Cd();fe();jr();fe();Dn();Vd();var aO=10,iO=200,B6=120,j6=24,lO=/^\d{4}-\d{2}-\d{2}$/;function hb(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var L6=(e=Date.now())=>new Date(e).toISOString().slice(0,10),vt=(e,t="i")=>new RegExp(e,t),F6=[{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"}],U6=[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 H6({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of F6)if(o.test(n)||o.test(r))return s;return null}function cO(e){let t=String(e||"");for(let n of U6){let r=n.exec(t);if(r)return r[0].trim()}return null}function uO(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 W6=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function q6(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Mc(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(`
|
|
761
|
+
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,B6)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${j6}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=W6.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=cO(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>iO)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${iO}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=cO(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>aO)throw new Error(`${r.length} bullets \u2014 over the ${aO}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function K6(e,{repos:t=hb()}={}){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:H6({title:r.title,headRef:r.headRef})}))}async function dO(e,t){let n=await K6(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function pO(e,{md:t,day:n=null}){let r=q6(t),o=n==null||n===""?L6():String(n);if(!lO.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 fO(e,{day:t}){let n=String(t||"");if(!lO.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}}ss();Dn();is();fe();ss();yt();hr();Dn();sc();$d();md();dc();Zt();import{execFile as rK}from"node:child_process";import{promisify as oK}from"node:util";var G6=new Set(["pull_request","check_suite","workflow_run","push"]),V6=/^[^/\s]+\/[^/\s]+$/,z6=500,wb=new Set,gb=[],nf=new Map,on={hourStart:0,byEvent:{},duplicates:0,rejected:0};function yb(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 J6(e){if(!e||typeof e!="object"||e.forge!=="github"||typeof e.event!="string"||!G6.has(e.event)||typeof e.repo!="string"||!V6.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 mO(e,{now:t=Date.now(),onAccept:n=null,log:r=console}={}){let o=J6(e);if(!o)return yb(t,"rejected"),{ok:!1,error:"malformed github event envelope"};if(o.deliveryId){if(wb.has(o.deliveryId))return yb(t,"duplicates"),{ok:!0,duplicate:!0,envelope:o};for(wb.add(o.deliveryId),gb.push(o.deliveryId);gb.length>z6;)wb.delete(gb.shift())}let s=o.repo.toLowerCase(),a=nf.get(s)||{lastDeliveryAt:0,count:0};a.lastDeliveryAt=t,a.count+=1,nf.set(s,a),yb(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 Y6(e,t=Date.now()){let n=nf.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 hO=1440*6e4;function bb(e,t=Date.now()){let n=Y6(e,t);return n.covered&&n.ageMs<hO}function wO(e=Date.now()){let t={};for(let[n,r]of nf)t[n]={lastDeliveryAt:r.lastDeliveryAt,count:r.count,covered:e-r.lastDeliveryAt<hO};return{hourStart:on.hourStart,byEvent:{...on.byEvent},duplicates:on.duplicates,rejected:on.rejected,repos:t}}fe();Dn();Cr();hr();Zt();function gO(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function X6(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var Z6=new Set(["merged","closed","lost"]);function Q6(e,t){if(!e?.blocked)return null;let n=gO(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 eK(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function tK(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 nK(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 rf(e,t={}){let{now:n=Date.now(),slugOf:r=$e,lookup:o=pt,note:s=xA,tracks:a=async()=>(await we()).filter(d=>!je(d)),asked:i=tK,emitChange:c=nK}=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(!X6(f))continue;let w=await o(p,f.number).catch(()=>null);if(!w?.trackId||Z6.has(w.state))continue;let{prev:m}=await s(p,f.number,{blocked:gO(f),sha:f.headSha??null,at:n});if(!m)continue;let g=Q6(m,f);if(!g||eK(w,g.change))continue;u??=new Map((await a()).map(I=>[I.id,I]));let y=u.get(w.trackId);if(!y)continue;let k=f.headSha??null;if(await i(y,g.change,f.number,k,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:k,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 vb=oK(rK),EO=3e4,sK=30*6e4,aK=EO,iK=10*6e4,cK=10080*6e4,lK=60*6e4,yO=8,_O=1440*6e4,uK=3,IO="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",xb=60*6e4,dK=15*6e4,pK=9e4,fK=1440*6e4,xl=new Map,vl=new Map,mK=360*6e4,hK=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}),wK=/could not resolve|not found|no pull requests?/i,gK=e=>wK.test(String(e?.message||e||""));function yK(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<xb)}var bO={fast:aK,slow:iK,decay:lK};function kO(e,t,n=bb){return $O(e,t,{grace:0})||!n(e.repo,t)&&(e.armedAt&&t-e.armedAt<sK||yK(e,t))?"fast":t-(e.firstSeen||0)>cK?"decay":"slow"}function Al(e,t,n,r){let o=e.lanes?.get(ae(t.repo,t.number))||{};return fd({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function bK(e,t){ur("watch");try{let{stdout:n}=await vb("gh",["pr","view",String(t),"--repo",e,"--json",IO],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await En({slug:e})}});return JSON.parse(n||"null")}catch(n){throw oo(n)&&so(n),n}}async function kK(e){ur("watch");try{let{stdout:t}=await vb("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",IO],{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 oo(t)&&so(t),t}}async function xK(){let e=new Map;for(let t of await we().catch(()=>[]))e.set(t.id,t);return e}async function vK(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 Id(e,t).catch(()=>{})}async function SK(e,t){let n=await os(e);return n?ga(n,t):null}async function AK(e,t,n){let r=await os(e);if(!r)throw new Error(`no local checkout for ${e}`);return ya(r,t,{method:n})}async function TK(e,t){let n=await os(e);if(!n)throw new Error(`no local checkout for ${e}`);return cc(n,t)}async function EK(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 Ya(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 _K=10*6e4;async function IK(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>_O){await Ya(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 Ya(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await Ya(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<_K)return;await Ya(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 Ya(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 Ya(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await RO(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function Xa(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 $K(e,t){try{return await vb("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Sl(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 Za=(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 $O(e,t,{grace:n=pK}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=fK}async function RK(e,t,n,r){let o=r.now??Date.now();if(!$O(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||je(s))return!1;let a=(s.boxes||[]).find(d=>d.name===s.leadBox);return await(r.midTurn??as)(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??cr)(e.repo,e.number).catch(()=>null),!1):await(r.claim??cr)(e.repo,e.number).catch(()=>null)?(await Xa(s,"opened",{...Za(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 OK(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 sd(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await id(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<dK;await kA(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await bA(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Sl(a,"pushed",e.number,d=>d.payload?.sha===s)&&await Xa(a,"pushed",{...Za(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>xb)await ad(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 ad(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Al(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await Sl(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await Xa(a,"ci-fixed",{...Za(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await ad(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>xb)await cd(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await cd(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Al(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await Sl(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await Xa(a,"reconciled",{...Za(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await cd(e.repo,e.number).catch(()=>{})}}}}async function MK(e,t,n,r){let o=Al(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await Sl(s,"closed",e.number)&&await Xa(s,"closed",{...Za(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 xO(e,t,n){Al(n,e,"lost",null);let r=t.get(e.trackId);r&&!await Sl(r,"lost",e.number)&&await Xa(r,"lost",Za(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function RO(e,t,n,r,o={}){let s=Al(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 vO(e={}){let{now:t=Date.now(),auth:n=Fh,view:r=bK,list:o=kK,links:s=AA,trackMap:a=xK,emitMerged:i=vK,mark:c=no,archiveIfArmed:l=fr,notifyIfArmed:u=mr,notifyIfResolved:d=wp,followupsIfMerged:p=ys,batchThreshold:f=uK,detail:w=SK,merge:m=AK,updateBranch:g=TK,lookup:y=pt,disarm:k=oc,emitDisarmed:b=EK,localHasCommit:I=$K,claim:R=cr,midTurn:N=as,only:D=null,wantDetail:j=null,covered:T=bb,verdicts:$=null,tokenFor:F=z=>Bs(z),noteRefusal:J=gA,clearRefusal:M=yA}=e;if(!(await n().catch(()=>null))?.authenticated||dd()>0)return 0;let _=await s().catch(()=>[]),H=zA(_),K=z=>{try{return!!j?.(z)}catch{return!1}},X=D?_.filter(z=>{try{return D(z)}catch{return!1}}):_.filter(z=>t-(xl.get(ae(z.repo,z.number))||0)>=bO[kO(z,t,T)]);if(!X.length)return 0;let pe=Date.now(),Oe=new Map,Me=new Map;for(let z of[...X].sort(lc)){let ge=D?"event":kO(z,t,T);Oe.set(ae(z.repo,z.number),{lane:ge,intervalMs:bO[ge]??null}),xl.set(ae(z.repo,z.number),t),Me.has(z.repo)||Me.set(z.repo,[]),Me.get(z.repo).push(z)}let Q=await a().catch(()=>new Map),qe=new Map,Ae=(z,ge)=>{let Ge=ae(z,ge);if(!qe.has(Ge)){let O=Promise.resolve().then(()=>w(z,ge));O.catch(()=>{}),qe.set(Ge,O)}return qe.get(Ge)},Z=$??((z,ge)=>rf([{path:z,prs:[ge]}],{now:t,slugOf:async()=>z,tracks:async()=>[...Q.values()].filter(Ge=>!je(Ge))}).catch(()=>{})),he={now:t,lanes:Oe,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,followupsIfMerged:p,detail:Ae,merge:m,updateBranch:g,lookup:y,disarm:k,emitDisarmed:b,localHasCommit:I,claim:R,midTurn:N};for(let[z,ge]of Me){let Ge=hK(ge);if(Ge.until>t)continue;let O=!1;try{if(await F(z),Ge.misses)for(let oe of ge)await M(oe.repo,oe.number).catch(()=>{})}catch(oe){O=fk(oe)}if(O){let oe=Ge.misses+1;if(oe>=yO){console.log(`[pr-watch] ${z}: not granted ${oe}\xD7 \u2014 retiring ${ge.length} link(s) as lost`);for(let ye of ge){await xO(ye,Q,he);let Fe=ae(ye.repo,ye.number);vl.delete(Fe),xl.delete(Fe)}}else for(let ye of ge)await J(ye.repo,ye.number,{misses:oe,until:t+mK}).catch(()=>{});continue}let Y,Le=new Set,Qe=oe=>oe.ciFixAwait||oe.reconcileAwait||K(oe)?!0:!oe.autoMerge||oe.trackId&&H.get(oe.trackId)!==ae(oe.repo,oe.number)?!1:t-(oe.autoMerge.armedAt||0)<=_O;if(ge.length>f)Y=await o(z).catch(()=>null);else{Y=new Map;for(let oe of ge){if(Qe(oe)){let ye=await Ae(z,oe.number).catch(()=>null);if(ye?.state){Y.set(Number(oe.number),{number:Number(oe.number),url:ye.url,title:ye.title,headRefName:ye.headRefName,headRefOid:ye.headSha??null,state:ye.state,mergedAt:ye.mergedAt??null,closedAt:ye.closedAt??null});continue}}try{let ye=await r(z,oe.number);ye?.number!=null&&Y.set(Number(ye.number),ye)}catch(ye){gK(ye)&&Le.add(ae(oe.repo,oe.number))}}}if(Y)for(let oe of ge){let ye=ae(oe.repo,oe.number),Fe=Y.get(Number(oe.number));if(!Fe){if(Le.has(ye)){let jt=(vl.get(ye)||0)+1;vl.set(ye,jt),jt>=yO&&(await xO(oe,Q,he),vl.delete(ye),xl.delete(ye))}continue}if(vl.delete(ye),Fe.state==="MERGED"||Fe.state==="CLOSED")Fe.state==="MERGED"?await RO(oe,Fe,Q,he):await MK(oe,Fe,Q,he),xl.delete(ye);else{await RK(oe,Fe,Q,he).catch(()=>{}),await OK(oe,Fe,Q,he).catch(()=>{});let jt=!oe.trackId||H.get(oe.trackId)===ye;oe.autoMerge&&jt&&await IK(oe,Q,he).catch(()=>{});try{if(K(oe)&&!qe.has(ye)&&Ae(oe.repo,oe.number),qe.has(ye)){let x=await qe.get(ye).catch(()=>null);x?.checks&&await Z(oe.repo,x)}}catch{}}}}let Te={};for(let{lane:z}of Oe.values())Te[z]=(Te[z]||0)+1;return console.log(`[pr-watch] probed ${X.length} link(s) (${Object.entries(Te).map(([z,ge])=>`${z} ${ge}`).join(", ")}) in ${Date.now()-pe}ms`),X.length}function SO(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 kb=!1,of=!1,sf=[],AO=Promise.resolve();async function CK(){for(;of||sf.length;){if(of){of=!1;try{await vO()}catch{}continue}let e=sf;sf=[];let t=e.map(r=>r.spec),n=Object.assign({},...e.map(r=>r.deps||{}));try{await vO({...n,only:r=>t.some(o=>SO(o,r)),wantDetail:r=>t.some(o=>o.detail&&SO(o,r))})}catch{}}}function af(e,t){return e===null?of=!0:sf.push({spec:e,deps:t}),kb||(kb=!0,AO=CK().finally(()=>{kb=!1})),AO}function OO(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 af({repo:n,numbers:r,branch:e.branch||null,event:e.event||null,detail:e.detail===!0},t)}var NK=6e4,TO=0;function MO(e=void 0){let t=e?.now??Date.now();return t-TO<NK?Promise.resolve():(TO=t,af({all:!0},e))}function CO(){af(null),setInterval(()=>af(null),EO)}ss();Rd();Ic();hr();Zt();var PK=new Set(["opened","reopened","closed","synchronize","ready_for_review","converted_to_draft","edited"]);async function DK({tracks:e=we,reconcile:t=Ud}={}){for(let n of(await e().catch(()=>[])).filter(r=>!je(r)))await t(n,{bypassThrottle:!0}).catch(()=>{})}function NO(e,t={}){let{invalidate:n=en,probe:r=OO,poke:o=OT,reconcileOpened:s=DK}=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}mt();fe();hr();Zt();import{randomUUID as BK}from"node:crypto";var jK=2e4,PO=3,Qa=new Map,Sb=new Set;async function DO(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 LK(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 we()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(je(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=(Qa.get(d.name)||0)+1;Qa.set(d.name,f),f===PO&&(Sb.add(d.name),await DO(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:BK(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let f=(Qa.get(d.name)||0)>=PO;if(Qa.delete(d.name),f||!Sb.has(d.name)){Sb.add(d.name);let w=await DO(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[...Qa.keys()])l.has(u)||Qa.delete(u)}var Tl=null,Ab=!1;function BO(){return Tl?()=>{}:(Tl=setInterval(()=>{Ab||(Ab=!0,LK().catch(()=>{}).finally(()=>{Ab=!1}))},jK),Tl.unref?.(),()=>{clearInterval(Tl),Tl=null})}es();Ic();fe();ss();is();es();hc();import{randomUUID as FK}from"node:crypto";async function UK(){let e=await Promise.resolve().then(()=>(hr(),MI));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var Tb={listRepoPrs:Bn,readEvents:q,appendEvent:C,sayDeps:UK,currentBranch:ao};async function HK(e,t=Tb,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??ao)(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 Eb(e,t,{actions:n,notify:r=!1}={},o=Tb){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 HK(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=FK();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 _b(e,{offerId:t,choice:n}={},r=Tb){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');return co(`prx:${e.id}:${t}`,()=>WK(e,{offerId:t,choice:n},r))}async function WK(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=to.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(e.id,fa(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,oA(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}}hc();import $r from"node:fs/promises";import ei from"node:path";import qO from"node:os";import jO from"node:fs/promises";import cf from"node:path";import{execFile as qK}from"node:child_process";import{promisify as KK}from"node:util";import{fileURLToPath as GK}from"node:url";var VK=KK(qK),FO=cf.dirname(GK(import.meta.url)),zK=cf.resolve(FO,"..","..","client","dist"),JK="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",YK=new Map([...JK].map((e,t)=>[e,t]));function XK(e){let t=[],n=0,r=0;for(let o of e){let s=YK.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 ZK(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=XK(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 QK(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 LO=new Map;async function eG(e){let t;try{t=await jO.stat(e)}catch{return null}let n=LO.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await jO.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:ZK(r.mappings||"")};return LO.set(e,o),o}catch{return null}}var Ib=null;function tG(){return Ib||(Ib=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):VK("git",["rev-parse","--short","HEAD"],{cwd:cf.resolve(FO,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),Ib}var nG=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function rG(e,{distDir:t=zK}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(nG)){let[a,i,c,l]=s,u=await eG(cf.join(t,"assets",`${i}.map`)),d=u?QK(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 UO(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await tG();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await rG(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var oG=()=>process.env.BOSTRAT_STATE_DIR||ei.join(process.env.HOME||qO.tmpdir(),".bostrat"),Io=()=>ei.join(oG(),"telemetry"),KO=()=>ei.join(Io(),".relay-offsets.json"),sG=50,aG=14,iG=3e4,HO=500,zt=(e,t)=>typeof e=="string"?e.slice(0,t):"";function cG(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 GO=e=>new Date(e).toISOString().slice(0,10),WO=0;async function VO(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,sG):[]).map(s=>cG(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await UO(s);await $r.mkdir(Io(),{recursive:!0});let o=ei.join(Io(),`${GO(t)}.ndjson`);return await $r.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
762
|
+
`).join(""),"utf8"),t-WO>6*3600*1e3&&(WO=t,lG(t).catch(()=>{})),r.length}async function lG(e=Date.now()){let t=GO(e-aG*24*3600*1e3),n=[];try{n=await $r.readdir(Io())}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 $r.rm(ei.join(Io(),s),{force:!0});let o=await JO();for(let s of r)delete o[s];await YO(o)}var lf=new Map;function zO(e,t=Date.now()){let n=e||"unknown",r=lf.get(n);return r||(lf.size>512&&lf.clear(),r={tokens:40,at:t},lf.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 JO(){try{return JSON.parse(await $r.readFile(KO(),"utf8"))||{}}catch{return{}}}async function YO(e){await $r.mkdir(Io(),{recursive:!0}),await $r.writeFile(KO(),JSON.stringify(e),"utf8")}function uG(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||qO.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 dG({fetchImpl:e=fetch}={}){let t=await h$();if(!t)return{sent:0,disabled:!0};let n;try{n=(await $r.readdir(Io())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await JO(),o=0;for(let s of n){let a=ei.join(Io(),s),i=r[s]||0,c;try{let p=await $r.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(`
|
|
763
|
+
`).filter(Boolean),d=[];for(let p of u)try{d.push(JSON.parse(p))}catch{}for(let p=0;p<d.length;p+=HO){let f=d.slice(p,p+HO).map(m=>uG(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 YO(r)}return{sent:o,disabled:!1}}var $b=null,Rb=!1;function XO(){$b||($b=setInterval(async()=>{if(!Rb){Rb=!0;try{await dG()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{Rb=!1}}},iG),$b.unref?.())}import df from"node:fs/promises";import nM from"node:path";import pG from"node:os";var rM=()=>process.env.BOSTRAT_STATE_DIR||nM.join(process.env.HOME||pG.tmpdir(),".bostrat"),oM=()=>nM.join(rM(),"dictation-glossary.json"),fG=100,mG=500,hG=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],pf=null,Ob=null,Mb=Promise.resolve();function ZO(){let e={},t=Date.now();for(let n of hG)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function sM(){return pf||(Ob??=df.readFile(oM(),"utf8").then(e=>JSON.parse(e)?.terms??ZO()).catch(()=>ZO()).then(e=>pf=e).finally(()=>Ob=null),Ob)}async function QO(){let e=JSON.stringify({terms:pf},null,2),t=oM(),n=`${t}.${process.pid}.tmp`;await df.mkdir(rM(),{recursive:!0}),await df.writeFile(n,e),await df.rename(n,t)}function wG(){return Mb=Mb.then(QO,QO),Mb.catch(()=>{})}async function Cb(){return Object.values(await sM()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var gG=e=>Math.max(1,Math.ceil(e.length/4));async function yG(){let e=[],t=0;for(let{term:n}of await Cb()){let r=gG(n);if(!(t+r>mG)&&(e.push(n),t+=r,e.length>=fG))break}return e}async function bG(e,t){let n=await sM(),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),uf=e=>e.toLowerCase();function tM(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function aM(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]=uf(n[u])===uf(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;)uf(n[c])===uf(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(tM(r[l])&&i.add(r[l]),l++);for(;l<s;l++)tM(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await bG(u,"learned");return await wG(),[...i]}var El=null;function kG(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 iM(){if(!El)return null;let e=await sl();if(!e||e.apiKey.slice(-6)!==El.keyTail)return null;let{keyTail:t,...n}=El;return n}async function cM(e,t){let n=await sl();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 yG())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=kG(o.status,i);c.persistent&&(El={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 El=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}fe();var Nb={"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 xG(e){if(!e)return null;let t=String(e).trim();if(Nb[t])return t;let n=null;for(let r of Object.keys(Nb))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function vG(e,t={}){let n=Nb[xG(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 SG(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 _l=null;async function AG(){if(!_l)return null;let e=await jp();if(!e||e.apiKey.slice(-6)!==_l.keyTail)return null;let{keyTail:t,...n}=_l;return n}async function TG(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=SG(t.status,o);s.persistent&&(_l={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}_l=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 EG={openai:TG};async function lM(){let e=await jp();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await AG()}:{configured:!1}}async function uM(){let e=await jp();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 EG[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function dM(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=vG(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();pA(async()=>new Set((await we()).map(e=>e.id)));var bM=Ie.dirname(IG(import.meta.url)),pM=Number(process.env.PORT)||3362,ff=wy(process.env),kM=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,ti=AR(process.env.BOSTRAT_ALLOW_IPS),Lb=TR(process.env.BOSTRAT_VIEW_FRONT_CIDRS,ef),Fb=_R(),xM=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"),Qp(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(bl(e)||ti.allowed(ti.clientIp(e))||Lb.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});E.use(CR);E.use(RR({token:Fb,enforce:xM}));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 Fr(),autoTriggerPct:kM,plane:ni?ni.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(!De(n))return t.status(400).json({error:"invalid session name"});if(!await ke(n))return t.status(404).json({error:"session not found"});try{await Mt(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(!De(n))return t.status(400).json({error:"invalid session name"});let r=await ke(n);if(!r)return t.status(404).json({error:"session not found"});let o=await Ci(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(!De(n))return t.status(400).json({error:"invalid session name"});try{let r=await hv(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=Nu(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 fM=10;async function vM(){let e=await we(),[t,n]=await Promise.all([LI(e),UI(e)]);return{tracks:e,views:await Pw(),queuedOf:r=>ms(r),jobsOf:r=>t.get(r)??[],prPendingOf:r=>n.get(r)??[]}}E.get("/api/idle-guard",async(e,t)=>{try{t.json(py(await vM()))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var sn=ks(),NG=t$({kind:sn}),$l=null,It=()=>$l&&["auto","prompt","off"].includes($l)?$l:NG,jb=null,PG=rl.has(sn),SM=async({to:e,from:t})=>!PG||Op(await ol())?!1:(Ol.arm({to:e,from:t,kind:sn}),!0);async function DG(e){let t=It();$l=e??null;let n=It();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){Ol.cancel();return}jb&&await SM(jb)}}var AM=(()=>{try{return CG(import.meta.url)("../package.json").version||null}catch{return null}})(),Ol=r$({isBusy:async()=>py(await vM()).busy,onStuck:({to:e,from:t})=>zn({current:t,latest:e,installKind:sn,policy:It(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?zn({current:t,latest:e,installKind:sn,policy:It(),status:"updating",reason:"auto"}):zn({current:t,latest:e,installKind:sn,policy:It(),status:"failed",error:n?.reason})).catch(()=>{})});E.get("/api/node/self-update",async(e,t)=>{t.json({installKind:sn,policy:It(),policySource:$l?"plane":"node",current:AM,status:await ol(),armed:Ol.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 Mp({to:n,kind:sn,reason:"manual"});if(r.ok)return Ol.cancel(),zn({current:r.from,latest:n,installKind:sn,policy:It(),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 TM(){try{let e=await n$();e&&await zn({current:e.from,latest:e.to,installKind:e.kind||sn,policy:It(),status:e.status,error:e.error,reason:e.reason}),await Ig({current:AM,installKind:sn,policy:It()})}catch{}}TM();setInterval(TM,3e4).unref();E.get("/api/tracks",async(e,t)=>{let n=(await we()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>fM){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,fM).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:Vc,agentAvailability:await cp(),tickets:await $$()})});E.get("/api/cost",async(e,t)=>{try{let n=await we();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await Zy(n);if(r.cursorUsage=bw(),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)})}});E.get("/api/usage/roundup",async(e,t)=>{try{let n=await eb(await we(),{window:e.query.window,from:e.query.from,to:e.query.to});if(e.query.format==="text")return t.type("text/plain").send(sR(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 uR(await we(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=bw(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=tc(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/usage/caps",(e,t)=>{t.json(tc())});E.post("/api/usage/caps",async(e,t)=>{try{t.json(await Xu(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 bR(await we(),{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 tk(e.get(Qn),{byToken:zc,legacy:()=>yc(e.body?.session_id,e.body?.cwd)});if(n==="refused")return ci("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return ci("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&&ci("mismatch",{track:r.track.id,box:r.box.name}),ci(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 X$(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&Zl(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 zc(e.get(Qn));if(!n?.track||!n?.box)return t.status(204).end();t.send((await hT(n.track,n.box)).join(`
|
|
762
764
|
`)+`
|
|
763
|
-
`)}catch{t.status(204).end()}});E.post("/api/git/committed",async(e,t)=>{try{let n=await qc(e.get(Yn));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?Ee.resolve(n.box.cwd):null,s=typeof e.body?.cwd=="string"&&e.body.cwd?Ee.resolve(e.body.cwd):null,a=o&&s&&(s===o||s.startsWith(o+Ee.sep))?s:o;eT(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 ie(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 nT(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 ie(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,event:await rT(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 Y$(await we(),{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(!OO(Za.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await RO(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 Xn(),r=new Set((await Promise.all(n.map(a=>_e(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await pi()).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 jl(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:Bl(),calls:yA(),latency:vA(),webhook:tO()})});E.post("/api/github/event",zt.json({limit:"64kb"}),(e,t)=>{let n=QR(e.body,{onAccept:r=>gO(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)=>{ik(),t.json({ok:!0})});E.get("/api/gmail/search",async(e,t)=>{try{let n=await aR(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 iR(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});async function Al({repo:e=null,number:t=null,branch:n=null}){let r=e?await _e(e):null;if(r&&t!=null){let s=await ct(r,t),a=s&&await ie(s.trackId);if(a)return a}if(!n)return null;let o=await we();return pd(o,n,s=>fd(s,{branch:n,repo:e,slug:r}))}async function lM({repo:e=null,number:t=null,branch:n=null}){let r=await Al({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!je(r))return r;if(!n)return null;let o=e?await _e(e).catch(()=>null):null,s=await we();return pd(s,n,a=>!je(a)&&fd(a,{branch:n,repo:e,slug:o}))}async function vn(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Al({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await _e(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 sG(e){let t=(await we()).filter(n=>!je(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await _e(n.path);if(r)for(let o of n.prs){if(await ct(r,o.number))continue;let s=await pd(t,o.headRefName,a=>fd(a,{branch:o.headRefName,repo:n.path,slug:r}));s&&await Lt({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 $A();await sG(n.repos).catch(()=>{}),await Yp(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(!nc.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 _e((await ir(n)).path);if(!I)throw new Error("repo has no GitHub remote");let _=await Al({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 qd(_,"merge",{count:1,summary:`arming auto-merge on PR #${b}`}),a&&await rc(n,b),await Lt({repo:I,number:b,trackId:_.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Zi(I,b,{merge:{method:x}}))throw new Error("couldn't record the auto-merge arm");return await Qo(I,b,i===!0),await es(I,b,c===!0&&i!==!0),Zt(),await vn("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 Al({repo:n,number:r,branch:l}).catch(()=>null),p&&(f=await qd(p,"merge",{count:1,summary:`merging PR #${r}`}));let h=await ma(n,r,{method:o||"squash"});await vn("merged",{number:h.number,url:d,title:u,branch:l,repo:n});let g=await _e(n).catch(()=>null),y=!1;if(g&&(i===!0&&await Qo(g,h.number,!0).catch(()=>{}),c===!0&&i!==!0&&await es(g,h.number,!0).catch(()=>{}),await ur(g,h.number).catch(()=>{}),await gs(g,h.number).catch(()=>{}),y=await lr(g,h.number).catch(()=>!1),await no(g,h.number,"merged").catch(()=>{})),i===!0&&!y){let x=await Al({repo:n,number:h.number,branch:l}).catch(()=>null);x&&!x.archivedAt&&await ws(x.id).catch(()=>{})}t.json(h)}catch(h){w=h;let g=h?.message||String(h),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(()=>{})}});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 _e((await ir(n)).path).catch(()=>null),l=c?await Qi(c,i).catch(()=>null):null;try{await Pm(n,i)}catch(u){if(!l)throw u}Zt(),await vn("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 _e(n).catch(()=>null),s=await NA(n,{method:r||"merge",onMerged:a=>{vn("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&ur(o,a.number).catch(()=>{}).then(()=>gs(o,a.number)).catch(()=>{}).then(()=>lr(o,a.number)).catch(()=>{}).then(()=>no(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:CA()})});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(!nc.has(u))throw new Error(`invalid merge method: ${u}`);if(!n)return t.status(400).json({error:"trackId required"});let d=await ie(n);if(!d)return t.status(404).json({error:`Track ${n} isn't on this node`});i=d;let p=(await pn()).filter(h=>h.trackId===d.id&&h.state!=="merged"&&h.state!=="closed").sort(oc);if(!p.length)return t.status(409).json({error:"no open PRs on this Track"});c=await qd(d,"merge",{count:p.length,summary:p.length===1?"merging 1 PR":`merging ${p.length} PRs`});for(let h of p)await Qo(h.repo,h.number,o===!0).catch(()=>{}),await es(h.repo,h.number,s===!0&&o!==!0).catch(()=>{});let f=[],w=[];for(let h of p){let g={repo:h.repo,number:h.number,ok:!0,action:"armed"};f.push(g);try{let y=await ns(h.repo);if(!y)throw new Error(`no local checkout for ${h.repo}`);let x=await ha(y,h.number);w.push({r:g,l:h,repoPath:y,pr:x,plan:RA(x,{update:r!==!1})})}catch(y){g.ok=!1,g.action="failed",g.reason=y?.message||String(y)}}for(let{r:h,l:g,repoPath:y,pr:x,plan:b}of OA(w))try{if(b.action==="skip")h.action="skipped",h.reason=b.reason;else if(b.action==="merge")await ma(y,g.number,{method:u}),await vn("merged",{number:g.number,url:x.url,title:x.title,branch:x.headRefName,repo:y,track:d}),await ur(g.repo,g.number).catch(()=>{}),await gs(g.repo,g.number).catch(()=>{}),await lr(g.repo,g.number).catch(()=>{}),await no(g.repo,g.number,"merged").catch(()=>{}),h.action="merged";else{if(b.action==="update-arm"&&(await rc(y,g.number).catch(()=>{}),h.action="updated-armed"),!await Zi(g.repo,g.number,{merge:{method:u}}))throw new Error("couldn't record the auto-merge arm");await vn("auto-armed",{number:g.number,url:x.url,title:x.title,branch:x.headRefName,repo:y,track:d,...h.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(I){h.ok=!1,h.action="failed",h.reason=I?.message||String(I)}if(Zt(),!f.some(h=>h.ok)){if(o===!0)for(let h of p)await Qo(h.repo,h.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let h of p)await es(h.repo,h.number,!1).catch(()=>{});return t.status(502).json({error:f.map(h=>`${h.repo}#${h.number}: ${h.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(()=>{})}});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 ie(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await pn()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await Qi(a.repo,a.number).catch(()=>null),c=await ns(a.repo).catch(()=>null);c&&await Pm(c,a.number).catch(()=>{}),i&&(await vn("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}))}Zt(),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 ie(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=Dt(i.workspace),d,p=Array.isArray(a)?a.filter(Boolean):[];if(p.length)d=p.map(h=>{let g=u.find(y=>y.name===h||y.repo===h||y.base===h||y.dir===h);if(!g)throw new Error(`invalid repo "${h}" \u2014 not tagged on this Track`);return g});else if(d=await is(i),!d.length)return t.status(409).json({error:"no tagged repo has work to ship"});let f=[];for(let h of d)try{let g=await Bm(h.dir,{title:r,body:o,base:s}),y=h.repo||await _e(h.dir).catch(()=>null);y&&g.number&&await Lt({repo:y,number:g.number,trackId:i.id,url:g.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await vn("opened",{number:g.number,url:g.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:y??h.name}}),f.push({repo:h.name,slug:y??null,ok:!0,url:g.url,number:g.number})}catch(g){f.push({repo:h.name,slug:h.repo??null,ok:!1,error:g?.message||String(g)})}let w=f.filter(h=>h.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(h=>`${h.repo}: ${h.error}`).join("; "),results:f})}let c=await Bm(i.cwd,{title:r,body:o,base:s}),l=await _e(i.cwd).catch(()=>null);l&&c.number&&await Lt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await vn("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 ie(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await is(n),o=new Map(r.map(a=>[a.base,a])),s=Dt(n.workspace).map(a=>{let i=o.get(a.base);return{name:a.name,base:a.base,baseRef:a.baseRef??null,repo:a.repo??null,dirty:i?.dirty??!1,ahead:i?.ahead??0,changed:!!i}});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 ap(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 ip(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 Bg(e.params.id,n,{skipPermissions:r!==!1});t.status(o.finishedAt?200:202).json({ok:!0,...Pb(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(Pb(Gc(e.params.id))??{running:!1})});function Pb(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 ha(n,r),s=await _e(n).catch(()=>null),a=await lM({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=Dt(a.workspace).find(y=>y.base===n||s&&y.repo===s)??null,c=a.workspace?.kind==="multi",l=await PA(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?Ee.join("ci",i.name):"ci",d=Ee.join(a.cwd,".bostrat",u),p=Ee.join(".bostrat",u,`pr-${o.number}-fail.log`);await kt.promises.mkdir(d,{recursive:!0}),await kt.promises.writeFile(Ee.join(a.cwd,p),l.map(y=>`\u2550\u2550\u2550\u2550 ${y.name}${y.link?` \xB7 ${y.link}`:""} \u2550\u2550\u2550\u2550
|
|
765
|
+
`)}catch{t.status(204).end()}});E.post("/api/git/committed",async(e,t)=>{try{let n=await zc(e.get(Qn));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?Ie.resolve(n.box.cwd):null,s=typeof e.body?.cwd=="string"&&e.body.cwd?Ie.resolve(e.body.cwd):null,a=o&&s&&(s===o||s.startsWith(o+Ie.sep))?s:o;wT(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 le(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 yT(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 le(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,event:await bT(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 dR(await we(),{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(!zO(ti.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await VO(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 er(),r=new Set((await Promise.all(n.map(a=>$e(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await hi()).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 Bs(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:Wl(),calls:NA(),latency:jA(),webhook:wO()})});E.post("/api/github/event",Jt.json({limit:"64kb"}),(e,t)=>{let n=mO(e.body,{onAccept:r=>NO(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)=>{mk(),t.json({ok:!0})});E.get("/api/gmail/search",async(e,t)=>{try{let n=await xR(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 vR(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});async function Rl({repo:e=null,number:t=null,branch:n=null}){let r=e?await $e(e):null;if(r&&t!=null){let s=await pt(r,t),a=s&&await le(s.trackId);if(a)return a}if(!n)return null;let o=await we();return kd(o,n,s=>xd(s,{branch:n,repo:e,slug:r}))}async function EM({repo:e=null,number:t=null,branch:n=null}){let r=await Rl({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!je(r))return r;if(!n)return null;let o=e?await $e(e).catch(()=>null):null,s=await we();return kd(s,n,a=>!je(a)&&xd(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 Rl({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await $e(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 BG(e){let t=(await we()).filter(n=>!je(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await $e(n.path);if(r)for(let o of n.prs){if(await pt(r,o.number))continue;let s=await kd(t,o.headRefName,a=>xd(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 KA();await BG(n.repos).catch(()=>{}),await rf(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 k=o||"merge";if(!ic.has(k))throw new Error(`invalid merge method: ${k}`);let b=Number(r);if(!Number.isInteger(b)||b<=0)throw new Error("invalid PR number");let I=await $e((await dr(n)).path);if(!I)throw new Error("repo has no GitHub remote");let R=await Rl({repo:n,number:b,branch:l}).catch(()=>null);if(!R)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(p=R,f=await Xd(R,"merge",{count:1,summary:`arming auto-merge on PR #${b}`}),a&&await cc(n,b),await Ut({repo:I,number:b,trackId:R.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await rc(I,b,{merge:{method:k}}))throw new Error("couldn't record the auto-merge arm");return await ts(I,b,i===!0),await ns(I,b,c===!0&&i!==!0),en(),await Tn("auto-armed",{number:b,url:d,title:u,branch:l,repo:n,track:R,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:b,auto:!0})}p=await Rl({repo:n,number:r,branch:l}).catch(()=>null),p&&(f=await Xd(p,"merge",{count:1,summary:`merging PR #${r}`}));let m=await ya(n,r,{method:o||"squash"});await Tn("merged",{number:m.number,url:d,title:u,branch:l,repo:n});let g=await $e(n).catch(()=>null),y=!1;if(g&&(i===!0&&await ts(g,m.number,!0).catch(()=>{}),c===!0&&i!==!0&&await ns(g,m.number,!0).catch(()=>{}),await mr(g,m.number).catch(()=>{}),await ys(g,m.number).catch(()=>{}),y=await fr(g,m.number).catch(()=>!1),await no(g,m.number,"merged").catch(()=>{})),i===!0&&!y){let k=await Rl({repo:n,number:m.number,branch:l}).catch(()=>null);k&&!k.archivedAt&&await gs(k.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 pg(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 $e((await dr(n)).path).catch(()=>null),l=c?await oc(c,i).catch(()=>null):null;try{await Wh(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 $e(n).catch(()=>null),s=await YA(n,{method:r||"merge",onMerged:a=>{Tn("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&mr(o,a.number).catch(()=>{}).then(()=>ys(o,a.number)).catch(()=>{}).then(()=>fr(o,a.number)).catch(()=>{}).then(()=>no(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:JA()})});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(!ic.has(u))throw new Error(`invalid merge method: ${u}`);if(!n)return t.status(400).json({error:"trackId required"});let d=await le(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(lc);if(!p.length)return t.status(409).json({error:"no open PRs on this Track"});c=await Xd(d,"merge",{count:p.length,summary:p.length===1?"merging 1 PR":`merging ${p.length} PRs`});for(let m of p)await ts(m.repo,m.number,o===!0).catch(()=>{}),await ns(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 os(m.repo);if(!y)throw new Error(`no local checkout for ${m.repo}`);let k=await ga(y,m.number);w.push({r:g,l:m,repoPath:y,pr:k,plan:GA(k,{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:k,plan:b}of VA(w))try{if(b.action==="skip")m.action="skipped",m.reason=b.reason;else if(b.action==="merge")await ya(y,g.number,{method:u}),await Tn("merged",{number:g.number,url:k.url,title:k.title,branch:k.headRefName,repo:y,track:d}),await mr(g.repo,g.number).catch(()=>{}),await ys(g.repo,g.number).catch(()=>{}),await fr(g.repo,g.number).catch(()=>{}),await no(g.repo,g.number,"merged").catch(()=>{}),m.action="merged";else{if(b.action==="update-arm"&&(await cc(y,g.number).catch(()=>{}),m.action="updated-armed"),!await rc(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:k.url,title:k.title,branch:k.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 ts(m.repo,m.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let m of p)await ns(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 pg(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 le(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 oc(a.repo,a.number).catch(()=>null),c=await os(a.repo).catch(()=>null);c&&await Wh(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 le(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=Bt(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 ls(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 Kh(m.dir,{title:r,body:o,base:s}),y=m.repo||await $e(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 Kh(i.cwd,{title:r,body:o,base:s}),l=await $e(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 le(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await ls(n),o=new Map(r.map(a=>[a.base,a])),s=Bt(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 fp(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 mp(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 Wg(e.params.id,n,{skipPermissions:r!==!1});t.status(o.finishedAt?200:202).json({ok:!0,...Ub(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(Ub(Yc(e.params.id))??{running:!1})});function Ub(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 ga(n,r),s=await $e(n).catch(()=>null),a=await EM({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=Bt(a.workspace).find(y=>y.base===n||s&&y.repo===s)??null,c=a.workspace?.kind==="multi",l=await XA(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?Ie.join("ci",i.name):"ci",d=Ie.join(a.cwd,".bostrat",u),p=Ie.join(".bostrat",u,`pr-${o.number}-fail.log`);await St.promises.mkdir(d,{recursive:!0}),await St.promises.writeFile(Ie.join(a.cwd,p),l.map(y=>`\u2550\u2550\u2550\u2550 ${y.name}${y.link?` \xB7 ${y.link}`:""} \u2550\u2550\u2550\u2550
|
|
764
766
|
${y.log??"(no fetchable log \u2014 see link)"}
|
|
765
767
|
`).join(`
|
|
766
|
-
`));let f=l.map(y=>y.name).join(", "),w=c&&i?` in the ${i.name}/ checkout`:"",h=await Ht(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 Lt({repo:g,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await eA(g,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await vn("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:h.leadBox,queued:!!h.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 ha(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await _e(n).catch(()=>null),a=await lM({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=Dt(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 Ht(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 Lt({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await tA(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await vn("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=eo.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 co(`pr:${e.params.id}:${n}`,async()=>{let a=await ie(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await K(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 kb(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 Ht(a.id,ua(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&&Rg(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 ie(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await xb(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 El=e=>t=>({...t,url:kR(t,e.headers)});E.get("/api/views",async(e,t)=>{try{let n=await Rw({trackId:e.query.track||void 0});t.json({views:n.map(El(e)),maxLive:uo})}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.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:El(e)(n),session:va(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 ie(n):null;if(!i&&s&&(i=(await we()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Ee.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await Mw({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:El(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:El(e)(await bE(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:El(e)(await kE(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 xE(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var aG=[["linear",ks,Sy],["jira",Rp,xy]],$b=new Map;E.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await ky(),r={};for(let[o,s,a]of aG){let i=await s();if(!i){$b.delete(o),r[o]={configured:!1};continue}let c=$b.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},$b.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 ky()})}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 QI(o),i=n?await ie(n):null;!i&&r&&(i=(await we()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Ee.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 $s=async(e,t)=>{let n=e?await ie(e):null;return!n&&t&&(n=(await we()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Ee.sep)))||null),n};E.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await hy()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});E.get("/api/roadmap",async(e,t)=>{try{t.json(await my())}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 $s(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 Ua(o);await Ep(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 $s(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await Ep(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var uM=async(e,t,n)=>{if(e)return{ref:e,track:await $s(t,n)};let r=await $s(t,n),o=r?await gy(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 uM(o,n,r),l=await KI({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 uM(e.query.circuit,e.query.track,e.query.cwd);t.json(await wy(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 $s(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=await VI(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 $s(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await JI(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 $s(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await gy(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 GI(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 ie(n):null;if(!s&&r&&(s=(await we()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+Ee.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 qa(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 ie(r):null;if(!o&&n&&(o=(await we()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+Ee.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 m$(),ticket:await zn(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 zn(e.params.id)})});E.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await ie(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await qa(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 Py(e.params.id)})});var lf=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 b$(e.params.id))}catch(n){let r=n?.message||String(n);t.status(lf(r)).json({error:r})}});E.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await Dy(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(lf(r)).json({error:r})}});E.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await k$(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(lf(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 ie(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await x$(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(lf(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 Vg(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 ie(n):null;if(!s&&r&&(s=(await we()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Ee.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 S$(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 v$()})}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 ie(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await A$(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 _c({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 mr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var _l={dotfiles:"allow"};E.get("/api/mocks/:id/png",async(e,t)=>{let n=await mr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=hn(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Tc(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,_l,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});E.get("/api/mocks/:id/media",async(e,t)=>{let n=await mr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=hn(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Tc(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,_l,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});E.get("/api/mocks/:id/html",async(e,t)=>{let n=await mr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=hn(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(en(n,r.v).html,_l,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&&Dd(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 ie(a):null;if(!d&&c&&(d=(await we()).find(h=>h.cwd&&(c===h.cwd||c.startsWith(h.cwd+Ee.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 DE({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&&Dd(n)?n:null;try{let o=await BE(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 FE(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await Ht(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 o_(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await UE(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 QE({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 e_(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 ie(n):null;if(!a&&s&&(a=(await we()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Ee.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 t_({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 n_(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 r_(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 we(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+Ee.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await p_({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 zw(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 zw(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=u_(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 f_(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(br(n,r.v),_l,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});E.get("/api/loops",async(e,t)=>{t.json(await lc())});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 we()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+Ee.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await lc();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 tw({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 ie(n):null;if(!s&&r&&(s=(await we()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Ee.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 _R(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var ei=async(e,t)=>{let n=e?await ie(e):null;return!n&&t&&(n=(await we()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Ee.sep)))||null),n};E.post("/api/followups/offer",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await ei(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 MT(s,o,{ship:{changedRepos:is}})})}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 ei(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 NT(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 ei(null,n).catch(()=>null);t.json({ok:!0,trackId:r?.id??null,triggers:iw,...await LT(r)})});var XO=new Set(["reconcile","fixing-ci","rebase","merge"]);E.get("/api/status",async(e,t)=>{let n=await ei(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 ei(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&&!XO.has(l))throw new Error(`phase must be one of ${[...XO].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=QK();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 ie(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await DT(a,{offerId:n,followupId:r||null,armId:o||null,promoted:s||null},{sayToLead:Ht,unarchiveTrack:up})})}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 ie(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await CT(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 ie(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await PT(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"||ie(e.trackId).then(async s=>{if(s&&(n&&await pc(s,n,o),t==="opened"||t==="pushed")){let a=jT(await K(s).catch(()=>[]));a&&await pc(s,"ship.done",{...o,shipOfferId:a})}}).catch(()=>{})});var dM=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await ie(n):null;if(!s&&r&&(s=(await we()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Ee.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"}),null;let a=await lc();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=RR(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 dM(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await OR(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 dM(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await MR(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 ei(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i;try{i=await my()}catch(c){return t.status(503).json({error:c?.message||String(c)})}try{let c=s==="circuit"?ib:wl;t.json({ok:!0,trackId:a.id,proposal:await WR(a,o,i,{allow:c})})}catch(c){t.status(400).json({error:c?.message||String(c)})}});var Db=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 ie(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await qR(s,n,r,{results:o||[]})})}catch(s){let a=s?.message||String(s);t.status(Db(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 ie(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await NR(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(Db(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 ie(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await PR(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(Db(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 ie(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await IR(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 ie(n):null;if(!a&&s&&(a=(await we()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Ee.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 h_({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 m_(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 w_(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 g_(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=JR(e.query.since||null);t.json({since:n,repos:lb(),...await YR(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var pM=async({trackId:e,cwd:t})=>{let n=e?await ie(e):null;return!n&&t&&(n=(await we()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+Ee.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 pM({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 XR(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 pM({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 ZR(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var iG=120,Rb=400,Qa=null,ZO=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>Rb)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${Rb} cap; a notification is a nudge, put the detail in chat`});let c=Ic(`${n||""}
|
|
767
|
-
${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
|
|
768
|
+
`));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 mA(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 ga(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await $e(n).catch(()=>null),a=await EM({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=Bt(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 hA(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=to.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 co(`pr:${e.params.id}:${n}`,async()=>{let a=await le(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 Eb(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,fa(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&&Dg(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 le(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await _b(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 Ml=e=>t=>({...t,url:PR(t,e.headers)});E.get("/api/views",async(e,t)=>{try{let n=await Pw({trackId:e.query.track||void 0});t.json({views:n.map(Ml(e)),maxLive:uo})}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/views/:id",async(e,t)=>{let n=await OE(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:Ml(e)(n),session:Ta(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 le(n):null;if(!i&&s&&(i=(await we()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Ie.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await Bw({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:Ml(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:Ml(e)(await ME(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:Ml(e)(await CE(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 NE(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var jG=[["linear",xs,$y],["jira",Bp,_y]],Pb=new Map;E.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await Ey(),r={};for(let[o,s,a]of jG){let i=await s();if(!i){Pb.delete(o),r[o]={configured:!1};continue}let c=Pb.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},Pb.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 Ey()})}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 m$(o),i=n?await le(n):null;!i&&r&&(i=(await we()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Ie.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 Rs=async(e,t)=>{let n=e?await le(e):null;return!n&&t&&(n=(await we()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Ie.sep)))||null),n};E.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await ky()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});E.get("/api/roadmap",async(e,t)=>{try{t.json(await xy())}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 Rs(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 qa(o);await Cp(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 Rs(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await Cp(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var _M=async(e,t,n)=>{if(e)return{ref:e,track:await Rs(t,n)};let r=await Rs(t,n),o=r?await Sy(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 _M(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})}});E.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await _M(e.query.circuit,e.query.track,e.query.cwd);t.json(await vy(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 Rs(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=await l$(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 Rs(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await u$(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 Rs(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await Sy(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 i$(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 le(n):null;if(!s&&r&&(s=(await we()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+Ie.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 Va(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 le(r):null;if(!o&&n&&(o=(await we()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+Ie.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 R$(),ticket:await Yn(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 Yn(e.params.id)})});E.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await le(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await Va(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 Uy(e.params.id)})});var mf=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 N$(e.params.id))}catch(n){let r=n?.message||String(n);t.status(mf(r)).json({error:r})}});E.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await Hy(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(mf(r)).json({error:r})}});E.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await P$(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(mf(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 le(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await D$(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(mf(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 ey(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 le(n):null;if(!s&&r&&(s=(await we()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Ie.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 j$(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 B$()})}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 le(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await L$(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 Oc({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 br(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var Cl={dotfiles:"allow"};E.get("/api/mocks/:id/png",async(e,t)=>{let n=await br(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=$c(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,Cl,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});E.get("/api/mocks/:id/media",async(e,t)=>{let n=await br(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=$c(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,Cl,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});E.get("/api/mocks/:id/html",async(e,t)=>{let n=await br(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,Cl,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&&Wd(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 le(a):null;if(!d&&c&&(d=(await we()).find(m=>m.cwd&&(c===m.cwd||c.startsWith(m.cwd+Ie.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 JE({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&&Wd(n)?n:null;try{let o=await YE(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 QE(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 g_(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await e_(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 p_({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 f_(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 le(n):null;if(!a&&s&&(a=(await we()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Ie.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 m_({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 h_(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 w_(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 we(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+Ie.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await T_({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 Zw(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 Zw(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=S_(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 E_(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(Sr(n,r.v),Cl,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});E.get("/api/loops",async(e,t)=>{t.json(await Zi())});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 we()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+Ie.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await Zi();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 le(n):null;if(!s&&r&&(s=(await we()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Ie.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 HR(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var ri=async(e,t)=>{let n=e?await le(e):null;return!n&&t&&(n=(await we()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Ie.sep)))||null),n};E.post("/api/followups/offer",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await ri(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 KT(s,o,{ship:{changedRepos:ls}})})}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 ri(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 VT(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 ri(null,n).catch(()=>null);t.json({ok:!0,trackId:r?.id??null,triggers:pw,...await ZT(r)})});var mM=new Set(["reconcile","fixing-ci","rebase","merge"]);E.get("/api/status",async(e,t)=>{let n=await ri(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 ri(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&&!mM.has(l))throw new Error(`phase must be one of ${[...mM].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=OG();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 le(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await JT(a,{offerId:n,followupId:r||null,armId:o||null,promoted:s||null},{sayToLead:qt,unarchiveTrack:gp})})}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 le(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await GT(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 le(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await zT(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"||le(e.trackId).then(async s=>{if(s&&(n&&await wc(s,n,o),t==="opened"||t==="pushed")){let a=XT(await q(s).catch(()=>[]));a&&await wc(s,"ship.done",{...o,shipOfferId:a})}}).catch(()=>{})});var IM=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await le(n):null;if(!s&&r&&(s=(await we()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Ie.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"}),null;let a=await Zi();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=KR(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 IM(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await GR(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 IM(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await VR(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 ri(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i;try{i=await xy()}catch(c){return t.status(503).json({error:c?.message||String(c)})}try{let c=s==="circuit"?fb:kl;t.json({ok:!0,trackId:a.id,proposal:await oO(a,o,i,{allow:c})})}catch(c){t.status(400).json({error:c?.message||String(c)})}});var Hb=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 le(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await sO(s,n,r,{results:o||[]})})}catch(s){let a=s?.message||String(s);t.status(Hb(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 le(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await JR(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(Hb(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 le(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await YR(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(Hb(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 le(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await WR(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 le(n):null;if(!a&&s&&(a=(await we()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Ie.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 __({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 I_(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 $_(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 R_(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=uO(e.query.since||null);t.json({since:n,repos:hb(),...await dO(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var $M=async({trackId:e,cwd:t})=>{let n=e?await le(e):null;return!n&&t&&(n=(await we()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+Ie.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 $M({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 pO(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 $M({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 fO(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var LG=120,Db=400,ni=null,hM=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>Db)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${Db} cap; a notification is a nudge, put the detail in chat`});let c=Mc(`${n||""}
|
|
769
|
+
${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 le(a):null;if(!l&&s&&(l=(await we()).find(w=>w.cwd&&(s===w.cwd||s.startsWith(w.cwd+Ie.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,LG):l.name,body:d(i,Db),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await C(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:p}),t.json({ok:!0,notification:p,delivered:ni?ni.status().connected:null})}catch(i){t.status(500).json({error:i?.message||String(i)})}});E.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 cM(e.body,e.headers["content-type"]))}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});E.get("/api/dictation/glossary",async(e,t)=>{t.json({configured:!!await sl(),terms:await Cb(),problem:await iM()})});E.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 aM(n,r)})});E.get("/api/tracks/:id/voice-chat",async(e,t)=>{if(!await le(e.params.id))return t.status(404).json({error:"track not found"});t.json(await lM())});E.post("/api/tracks/:id/voice-chat/session",async(e,t)=>{try{let n=await le(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 uM())}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});E.post("/api/tracks/:id/voice-chat/usage",async(e,t)=>{try{let n=await le(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 dM(n,{model:r,usage:o,seconds:s})})}catch(n){t.status(n?.status||500).json({error:n?.message||String(n)})}});E.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 hp({name:n,cwd:r,repo:o,repos:s,agent:a,model:i,skipPermissions:c!==!1}),p=null;if(l)try{await Va(d,l,{source:"create"})}catch(w){p=w?.message||String(w)}let f=null;if(u)try{await Cp(d,await qa(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})}});E.get("/api/tracks/:id",async(e,t)=>{let n=await le(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({track:n,handoff:RM(Ha(e.params.id)),reshape:Ub(Yc(e.params.id)),subtasks:bs(e.params.id).map(Nl),pendingOffers:await Rg(n)})});E.delete("/api/tracks/:id",async(e,t)=>{try{let n=await zg(e.params.id,{cascadeMocks:e.query.mocks!=="0",cascadeRunbooks:e.query.runbooks==="1"});await Uy(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})}});E.post("/api/tracks/:id/archive",async(e,t)=>{try{t.json({ok:!0,track:await gs(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})}});E.post("/api/tracks/:id/rename",async(e,t)=>{try{t.json({ok:!0,track:await Xg(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})}});E.post("/api/tracks/:id/unarchive",async(e,t)=>{try{t.json({ok:!0,track:await gp(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})}});E.get("/api/tracks/:id/messages",async(e,t)=>{let n=await lp(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json(n)});E.get("/api/tracks/:id/events",async(e,t)=>{let n=await le(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}
|
|
768
770
|
data: ${JSON.stringify(i)}
|
|
769
771
|
|
|
770
|
-
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await
|
|
772
|
+
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await q(n))i.seq>o&&r(i);let s=Ak(n.id,r),a=setInterval(()=>t.write(`: hb
|
|
771
773
|
|
|
772
|
-
`),25e3);e.on("close",()=>{clearInterval(a),s()})});E.get("/api/tracks/:id/search",async(e,t)=>{let n=await
|
|
773
|
-
`);E.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await
|
|
774
|
+
`),25e3);e.on("close",()=>{clearInterval(a),s()})});E.get("/api/tracks/:id/search",async(e,t)=>{let n=await le(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=Vi(await q(n)),a=Tu(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}))})});E.get("/api/memory",async(e,t)=>{t.json({stores:await Uk(),harvest:Bk()})});E.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 Hk(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 FG=["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(`
|
|
775
|
+
`);E.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await qt(e.params.id,FG)})}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})}});E.post("/api/tracks/:id/lead/restart",async(e,t)=>{try{t.json({ok:!0,track:await yp(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}:{}})}});E.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})}});E.post("/api/tracks/:id/upload",async(e,t)=>{try{let n=await le(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 D_(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})}});E.get("/api/tracks/:id/uploads/:file",async(e,t)=>{try{let n=await le(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=Ie.basename(String(e.params.file||""));if(!r||!tg(Ie.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=eg(n.cwd),s,a;try{s=await St.promises.realpath(o),a=await St.promises.realpath(Ie.join(o,r))}catch{return t.status(404).json({error:"upload not found"})}if(!a.startsWith(s+Ie.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 UG=/^[0-9a-fA-F-]{8,64}$/;E.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(e,t)=>{try{let n=await le(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(!UG.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=un(a.cwd,a.sessionId);if(!i)return t.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=_G.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?fm(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)})}});E.post("/api/tracks/:id/interrupt",async(e,t)=>{try{t.json(await pp(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})}});E.delete("/api/tracks/:id/queue",async(e,t)=>{try{let{removed:n,restored:r,draftRev:o}=await Ng(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)})}});E.post("/api/tracks/:id/answer-prompt",async(e,t)=>{let{promptId:n,answers:r,text:o,custom:s}=e.body||{};try{t.json(await up(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})}});E.post("/api/setup/:provider/start",async(e,t)=>{try{let n=await Gg({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})}});E.post("/api/tracks/:id/navigate-prompt",async(e,t)=>{let{promptId:n,questionIndex:r}=e.body||{};try{t.json(await Mg(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})}});E.put("/api/tracks/:id/draft",async(e,t)=>{let{text:n,rev:r}=e.body||{};try{t.json(await Lg(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})}});E.get("/api/tracks/:id/usage",async(e,t)=>{let n=await le(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 Yp(r,{fresh:o}))});E.post("/api/tracks/:id/model",async(e,t)=>{let{model:n}=e.body||{};try{t.json(await Fg(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})}});E.post("/api/tracks/:id/subtask",async(e,t)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=e.body||{};try{let a=await ly(e.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});t.json({ok:!0,subtask:Nl(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})}});E.get("/api/tracks/:id/subtasks",(e,t)=>{t.json({subtasks:bs(e.params.id).map(Nl)})});E.get("/api/tracks/:id/subtask/:sid/messages",async(e,t)=>{let n=await Og(e.params.id,e.params.sid);if(!n)return t.status(404).json({error:"subtask not found"});t.json(n)});E.post("/api/tracks/:id/subtask/:sid/cancel",(e,t)=>{try{t.json({ok:!0,subtask:Nl(Ap(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});E.post("/api/tracks/:id/subtask/:sid/merge-now",(e,t)=>{try{t.json({ok:!0,subtask:Nl(uy(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});function Nl(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}E.post("/api/tracks/:id/handoff-lead",async(e,t)=>{let{toAgent:n,skipPermissions:r,reason:o}=e.body||{};try{let s=await bp(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})}});E.post("/api/tracks/:id/spin-up-view",async(e,t)=>{try{t.json(await Bg(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})}});E.get("/api/tracks/:id/handoff-lead",(e,t)=>{t.json(RM(Ha(e.params.id))??{running:!1})});E.post("/api/tracks/:id/transfer-out",async(e,t)=>{try{let n=await sy(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})}});E.get("/api/tracks/:id/transfer-out",(e,t)=>{let n=oy(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??{}})});E.post("/api/tracks/:id/transfer-out/commit",async(e,t)=>{try{t.json(await xp(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})}});E.post("/api/tracks/adopt",async(e,t)=>{let{trackId:n}=e.body||{};try{t.json(await iy({...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 RM(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}E.get("/api/debug/handoff",async(e,t)=>{let n=Math.min(Number(e.query.limit)||80,500);t.json({verbose:au(),paths:s0,trace:await o0({limit:n})})});var Bb=Ie.resolve(bM,"..","..","client","dist");St.existsSync(Bb)&&(E.use(Jt.static(Bb)),E.get(/^(?!\/api\/).*/,(e,t)=>t.sendFile(Ie.join(Bb,"index.html"),Cl)));var OM=$G.createServer(E),HG=new MG({noServer:!0}),MM=(e,t,n)=>{if(!(bl(e)||ti.allowed(ti.clientIp(e))||Lb.allows(e))){t.write(`HTTP/1.1 403 Forbidden\r
|
|
774
776
|
\r
|
|
775
|
-
`),t.destroy();return}if(
|
|
777
|
+
`),t.destroy();return}if(NR(e,t,n))return;if(!OR(e,Fb,{enforce:xM})){t.write(`HTTP/1.1 401 Unauthorized\r
|
|
776
778
|
\r
|
|
777
|
-
`),t.destroy();return}let r=
|
|
779
|
+
`),t.destroy();return}let r=E0(e.url);if(!r.ok){t.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
778
780
|
\r
|
|
779
781
|
`),t.destroy();return}let o=r.sessionName;if(!De(o)){t.write(`HTTP/1.1 400 Bad Request\r
|
|
780
782
|
\r
|
|
781
|
-
`),t.destroy();return}let{cols:s,rows:a}=r;
|
|
783
|
+
`),t.destroy();return}let{cols:s,rows:a}=r;HG.handleUpgrade(e,t,n,i=>S0(i,o,{cols:s,rows:a}))};OM.on("upgrade",MM);var CM=process.env.BOSTRAT_CERT_DIR||Ie.join(process.env.HOME||Il.tmpdir(),".bostrat","certs"),wM=Number(process.env.BOSTRAT_TLS_PORT)||3443,gM=Ie.join(CM,"bostrat.globopi.key"),yM=Ie.join(CM,"bostrat.globopi.crt");if(St.existsSync(gM)&&St.existsSync(yM)){let e=RG.createServer({key:St.readFileSync(gM),cert:St.readFileSync(yM)},E);e.on("upgrade",MM),e.on("error",t=>{console.error(`[bostrat-agent] https listener failed (${t?.code||t?.message}) \u2014 continuing HTTP-only`)}),e.listen(wM,ff,()=>{console.log(`[bostrat-agent] https listening on ${ff||"0.0.0.0"}:${wM}`)})}OM.listen(pM,ff,()=>{console.log(`[bostrat-agent] listening on ${ff||"0.0.0.0"}:${pM}`);let e=HI(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: ${ti.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(Lb.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),ny().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),qg().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)),cy().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Zg().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(()=>Qg()).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)),Kg().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)),YG(),FI({listTracks:we}),Z_({trackOf:$g}),KG(),GG(),JG(),MS({listTracks:we,getTrack:le}).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)),H_()&&K_({listTracks:we}),i0().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)),CO(),BO(),F$({resolveTrack:le}),Y$({listTracks:we,createTrack:hp,archiveTrack:gs,listRepos:er,resolveTrack:le,sayToLead:qt,answerPrompt:up,startLeadHandoff:bp,interruptLead:pp,listSubtaskJobs:bs,cancelSubtask:Ap,readEvents:q,appendEvent:C,divertShip:Eb,answerPrExists:_b,knownAgents:Vc}),WG(),Ri().then(t=>{t||console.warn(`[boot] no host tmux server on ${Ft} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),XO(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(ni=ZI({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,apiToken:Fb,nodeId:process.env.BOSTRAT_NODE_ID||Il.hostname(),name:process.env.BOSTRAT_NODE_NAME||Il.hostname(),listTracks:we,detectAgents:cp,sampleMetrics:fR,deliverySample:()=>gE(Cg()),onHashKey:pE,onTracksTransferred:ay,onDegraded:({since:t,unacked:n,oldestAgeMs:r,mode:o})=>{let s=Math.round(r/6e4),a=`${process.env.BOSTRAT_NODE_NAME||Il.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(()=>{}),hM=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(()=>{}),hM=null},onConnected:()=>{MO()},onRemoved:({reason:t,via:n})=>{let r=`${process.env.BOSTRAT_NODE_NAME||Il.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{jb={latest:t.latest,current:t.current},await zn({...t,policy:It()}),It()==="auto"&&await SM({to:t.latest,from:t.current})}catch(n){console.error(`[plane-link] update notice failed: ${n?.message||n}`)}},onUpdatePolicy:t=>DG(t).catch(()=>{}),onHelloRefused:async({current:t,min:n,latest:r,installKind:o,fix:s})=>{try{let a=r||n;if(await zn({current:t,latest:a,min:n,installKind:o,required:!0,policy:It(),status:"available",reason:"refused"}),It()!=="off"&&a){let i=await Mp({to:a,kind:sn,reason:"hard-floor"});i.ok?(Ol.cancel(),await zn({current:t,latest:a,min:n,installKind:o,required:!0,policy:It(),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:It}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),Hs().catch(()=>{}),C_().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),U$().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),r_().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),k_().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),TA().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{ni?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function WG(){qG(),Ld().catch(()=>{}),setInterval(()=>Ld().catch(()=>{}),1e4)}function qG(){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=Ie.resolve(bM,"..","bin",e),n=Ie.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!St.existsSync(t))continue;St.mkdirSync(n,{recursive:!0});let r=Ie.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 KG(){let e=!1,t=async()=>{if(!e){e=!0;try{let n=await we(),r=await wR({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 GG(){let e=null,t=async()=>{try{let n=ra(),r=0,o=[],s=null,a=null;try{let u=await we(),d=await Zy(u);r=d.fleet.unpricedTurns||0,o=d.fleet.unpricedModels||[],s=await mR(u,{days:7}),a=(await eb(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(`
|
|
782
784
|
`);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(`
|
|
783
|
-
`).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
|
|
785
|
+
`).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 VG=Number(process.env.BOSTRAT_MEMORY_RECONCILE_MS)||5*6e4,zG=Number(process.env.BOSTRAT_MEMORY_RECONCILE_FIRST_MS)||5*6e4;function JG(){let e=async()=>{try{let n=await Fk();n.ran&&console.log(`[memory] one-time full backfill sweep: ${n.harvested} fact(s) harvested`);let o=(await we()).filter(a=>!je(a)&&a.kind!=="setup").flatMap(a=>[a.cwd,...(a.boxes||[]).map(i=>i.cwd)]).filter(Boolean),s=await Lk([...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,VG).unref()},zG).unref()}function YG(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{let t=await we();for(let n of t)je(n)||!n.boxes?.some(r=>r.state==="live")||(await lp(n.id).catch(()=>{}),await Ud(n).catch(()=>{}))}catch{}finally{e=!1}}},4e3)}
|