bostrat-node 1.11.3 → 1.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +157 -157
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
var RN=Object.defineProperty;var G=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var sm=(e,t)=>{for(var n in t)RN(e,n,{get:t[n],enumerable:!0})};import{createHash as ON,randomBytes as MN,timingSafeEqual as CN}from"node:crypto";function
|
|
1
|
+
var RN=Object.defineProperty;var G=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var sm=(e,t)=>{for(var n in t)RN(e,n,{get:t[n],enumerable:!0})};import{createHash as ON,randomBytes as MN,timingSafeEqual as CN}from"node:crypto";function Zk(){return MN(32).toString("hex")}function Ho(e){let t=typeof e=="string"?e.trim():"";return t?ON("sha256").update(t).digest("hex"):null}function Qk(e,t){return typeof e!="string"||typeof t!="string"||e.length!==t.length||!e?!1:CN(Buffer.from(e),Buffer.from(t))}function Ii(e,t={},{now:n=Date.now(),log:r=console.warn}={}){if(am.set(e,(am.get(e)||0)+1),im.has(e)&&n-im.get(e)<NN)return!1;im.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=${am.get(e)}${o?` ${o}`:""}`),!0}async function e0(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 dr,pr,am,im,NN,Ys=G(()=>{dr="BOSTRAT_HOOK_TOKEN",pr="x-bostrat-hook";am=new Map,im=new Map,NN=6e4});import Vt from"node:fs/promises";import t0 from"node:os";import Dt from"node:path";import{execFile as PN}from"node:child_process";import{promisify as DN}from"node:util";function cu(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function cm(){return process.env.CLAUDE_CONFIG_DIR?Dt.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Dt.join(t0.homedir(),".claude.json")}function BN(){return process.env.CLAUDE_CONFIG_DIR?Dt.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Dt.join(t0.homedir(),".claude","settings.json")}async function Wr(e){if(!e||typeof e!="string")return;let t=Dt.resolve(e),n=cm(),r=BN(),o={};try{o=JSON.parse(await Vt.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 Vt.mkdir(Dt.dirname(n),{recursive:!0}),await Vt.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await Vt.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 "${pr}: $${dr}" --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 Vt.mkdir(Dt.dirname(r),{recursive:!0}),await Vt.writeFile(r,JSON.stringify(i,null,2)),await LN(t)}async function LN(e){try{let t=Dt.resolve(e),n=Dt.join(t,".bostrat","memory","claude"),r=Dt.join(t,".claude","settings.local.json"),o={};try{o=JSON.parse(await Vt.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(Dt.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await Vt.mkdir(n,{recursive:!0}),await Vt.mkdir(Dt.dirname(r),{recursive:!0}),await Vt.writeFile(r,JSON.stringify(o,null,2)),await jN(t,["/.claude/settings.local.json"])}catch{}}async function jN(e,t){try{let{stdout:n}=await DN(PN)("git",["-C",e,"rev-parse","--git-path","info/exclude"]),r=Dt.resolve(e,n.trim()),o=await Vt.readFile(r,"utf8").catch(()=>""),s=t.filter(a=>!o.split(`
|
|
2
2
|
`).includes(a));if(!s.length)return;await Vt.mkdir(Dt.dirname(r),{recursive:!0}),await Vt.appendFile(r,(o&&!o.endsWith(`
|
|
3
3
|
`)?`
|
|
4
4
|
`:"")+s.join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`)}catch{}}var $i=G(()=>{Ys()});import lm from"node:fs/promises";import FN from"node:os";import
|
|
6
|
+
`)}catch{}}var $i=G(()=>{Ys()});import lm from"node:fs/promises";import FN from"node:os";import lu from"node:path";function UN(){let e=process.env.CODEX_HOME||lu.join(FN.homedir(),".codex");return lu.join(e,"config.toml")}function HN(e=process.env){return String(e.BOSTRAT_CODEX_UPDATE_CHECK||"").trim().toLowerCase()!=="codex"}function WN(e){let t=String(e||""),n=t.search(/^\s*\[/m),r=n===-1?t:t.slice(0,n);return new RegExp(`^\\s*${r0}\\s*=`,"m").test(r)}function qN(e,{env:t=process.env}={}){let n=String(e||"");return!HN(t)||WN(n)?n:n.trim()?`${n0}
|
|
7
7
|
|
|
8
|
-
${n.replace(/^\n+/,"")}`:`${
|
|
9
|
-
`}function
|
|
8
|
+
${n.replace(/^\n+/,"")}`:`${n0}
|
|
9
|
+
`}function o0(e){return`[projects."${e}"]`}function GN(e,t){let n=o0(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 Xs(e){if(!e||typeof e!="string")return;let t=lu.resolve(e),n=UN(),r="";try{r=await lm.readFile(n,"utf8")}catch{}let o=r;if(r=qN(r),!GN(r,t)){let s=o0(t);if(r.includes(s)){let i=r.indexOf(s)+s.length,l=r.slice(i).search(/^\s*\[/m),u=l===-1?r.length:i+l,d=r.slice(i,u),p=/trust_level\s*=/.test(d)?d.replace(/trust_level\s*=\s*"[^"]*"/,'trust_level = "trusted"'):`${d.replace(/\s*$/,"")}
|
|
10
10
|
trust_level = "trusted"
|
|
11
11
|
`;r=r.slice(0,i)+p+r.slice(u)}else{let a=`
|
|
12
12
|
${s}
|
|
13
13
|
trust_level = "trusted"
|
|
14
14
|
`;r=r.trimEnd()+a+(r.length&&!r.endsWith(`
|
|
15
15
|
`)?`
|
|
16
|
-
`:"")}}if(r!==o)try{await lm.mkdir(
|
|
16
|
+
`:"")}}if(r!==o)try{await lm.mkdir(lu.dirname(n),{recursive:!0}),await lm.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var r0,n0,uu=G(()=>{r0="check_for_update_on_startup",n0=`${r0} = false`});import Ri from"node:fs/promises";import KN from"node:os";import a0 from"node:path";function Wo(e){let t=String(e??"");for(let n of VN)t=t.replace(n,"\u2039redacted\u203A");return t}function du(){let e=process.env.BOSTRAT_HANDOFF_DEBUG;return e==="1"||e==="true"}async function zN(e){try{await Ri.mkdir(um,{recursive:!0}),await Ri.appendFile(Zs,`${JSON.stringify(e)}
|
|
17
17
|
`,"utf8");let n=(await Ri.readFile(Zs,"utf8")).split(`
|
|
18
|
-
`).filter(Boolean);n.length>
|
|
18
|
+
`).filter(Boolean);n.length>s0&&await Ri.writeFile(Zs,`${n.slice(-s0).join(`
|
|
19
19
|
`)}
|
|
20
|
-
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${Zs}: ${t?.message||t}`)}}async function ee(e={}){let t=e.paneTail===!0,n={at:new Date().toISOString(),ts:Date.now(),...e};t&&delete n.paneTail;let r=[n.flow||"handoff",n.phase||n.step||"?",n.trackId?`track=${n.trackId}`:"",n.sessionName?`box=${n.sessionName}`:"",n.error?`ERR=${n.error}`:""].filter(Boolean).join(" \xB7 ");if(console.log(`[handoff-debug] ${r}`),n.sessionName&&(t||
|
|
21
|
-
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Wo(o.paneTail)),o}catch{return{raw:Wo(r)}}})}catch{return[]}}var VN,um,Zs,
|
|
22
|
-
`,"utf8")}var hu=G(()=>{});import ym from"node:fs/promises";import qr from"node:path";import{execFile as hP}from"node:child_process";import{promisify as wP}from"node:util";async function vP(e){try{return(await ym.stat(qr.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function SP(e){let t=i=>ym.readFile(qr.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 fr(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await vP(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:qr.basename(o),branch:await SP(o)});return}if(s>=kP)return;let i;try{i=await ym.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||xP.has(c.name)||await t(qr.join(o,c.name),s+1)}for(let o of bP)await t(qr.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=await Me(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 TP(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function Me(e){if(!e)return null;if(gm.has(e))return gm.get(e);let t=null;try{let{stdout:n}=await gP("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=TP(n)}catch{t=null}return gm.set(e,t),t}async function ea(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await fr())if((await Me(n.path))?.toLowerCase()===t)return n.path;return null}var gP,g0,yP,bP,kP,xP,gm,Gr=G(()=>{gP=wP(hP),g0=process.env.HOME||"/home/globo",yP=qr.join(process.env.BOSTRAT_STATE_DIR||qr.join(g0,".bostrat"),"repos"),bP=[...(process.env.BOSTRAT_REPOS_ROOT||qr.join(g0,"Documents")).split(","),yP].map(e=>e.trim()).filter(Boolean),kP=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,xP=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);gm=new Map});function ta(e=Date.now()){if(Ko)return{tripped:!1,holdMs:1/0};if(Go>e)return{tripped:!1,holdMs:Go-e};if((!Pi||e-Pi>=wu.windowMs)&&(Pi=e,Ni=0),Ni++,Ni<wu.trip)return{tripped:!1,holdMs:0};gu++;let t=Math.min(wu.minMs*2**(gu-1),wu.maxMs);return Go=e+t,Ni=0,Pi=0,{tripped:!0,holdMs:t}}function y0(){Ni=0,Pi=0,gu=0,Go=0}function Di(e=Date.now()){return Ko?1/0:Go>e?Go-e:0}function na(e=Date.now()){return Di(e)>0}function bm(e={}){Ko={at:Date.now(),...e},Go=1/0}function yu(){return Ko}function b0(e=Date.now()){return{held:na(e),holdMs:Ko?null:Di(e),holds:gu,removed:Ko?{...Ko}:null}}var AP,wu,Ni,Pi,gu,Go,Ko,km=G(()=>{AP={trip:3,windowMs:3e5,minMs:3e5,maxMs:216e5},wu={...AP},Ni=0,Pi=0,gu=0,Go=0,Ko=null});import EP from"node:fs";import k0 from"node:fs/promises";import Bi from"node:path";import _P from"node:os";import{fileURLToPath as IP}from"node:url";import{execFile as $P}from"node:child_process";import{promisify as RP}from"node:util";function De(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 bu(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}function A0(e){return e?.status===404&&/not granted/i.test(String(e?.message||""))}async function ra(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!v0.test(n))throw Vo("repo must be owner/name",400);let r=xm.get(n);if(r&&r.expiresAt-Date.now()>CP)return r;if(!bu(t))throw Vo("node is not plane-linked \u2014 GitHub auth unavailable",503);if(na()){if(r&&r.expiresAt>Date.now())return r;throw Vo(yu()?"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 S0(`${De(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:T0(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw Vo(`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)&&ta(),o.status>=500&&r&&r.expiresAt>Date.now())return r;throw Vo(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 xm.set(n,a),a}async function Li({env:e=process.env,force:t=!1}={}){if(!bu(e))return{configured:!1,repos:[],username:null};if(!t&&Kr&&Date.now()-Kr.at<NP)return Kr.data;if(na())return Kr?.data??{configured:!0,repos:[],username:null,error:"plane refused this node's token"};try{let n=await S0(`${De(e)}/v1/github/repos`,{headers:T0(e)});if((n.status===401||n.status===403||n.status===410)&&ta(),!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Kr={at:Date.now(),data:r},r}catch(n){return Kr?Kr.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function E0(){xm.clear(),Kr=null}async function Bn({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await Me(e):null);if(!r)return{};let{token:o}=await ra(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function vm({env:e=process.env}={}){return(await Li({env:e})).username??null}function LP(e){let[t,n]=String(e).split("/");return Bi.join(MP,t,n)}async function Sm(e,{env:t=process.env}={}){let n=(...r)=>x0("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${DP()}" ${BP(t)}`),await n("config","credential.useHttpPath","true")}async function ku(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!v0.test(String(e||"")))throw Vo(`bad repo slug "${e}"`,400);let r=LP(e);if(EP.existsSync(Bi.join(r,".git")))return r;let{token:o}=await ra(e,{env:t});await k0.mkdir(Bi.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await x0("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:PP,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await k0.rm(r,{recursive:!0,force:!0}).catch(()=>{}),Vo(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Sm(r,{env:t}),r}var x0,OP,MP,v0,CP,NP,PP,S0,xm,Kr,T0,Vo,DP,BP,xt=G(()=>{Gr();km();x0=RP($P),OP=process.env.BOSTRAT_STATE_DIR||Bi.join(process.env.HOME||_P.tmpdir(),".bostrat"),MP=Bi.join(OP,"repos"),v0=/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?\/[A-Za-z0-9._-]{1,100}$/,CP=5*6e4,NP=6e4,PP=10*6e4,S0=fetch,xm=new Map,Kr=null;T0=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Vo=(e,t)=>Object.assign(new Error(e),{status:t});DP=()=>IP(new URL("../bin/bostrat-git-credential",import.meta.url)),BP=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function I0(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}async function R0(e,t,n,r=_0){let o=De(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await FP(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function O0(e,{env:t=process.env,force:n=!1,timeoutMs:r=_0}={}){if(!I0(t))return{facts:[],source:"unconfigured"};let o=Tm.get(e);if(!n&&o&&Date.now()-o.at<jP)return{facts:o.facts,source:"cache"};try{let s=await R0(`/v1/memory/${encodeURIComponent(e)}`,{headers:$0(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 Tm.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 xu(e,t,{env:n=process.env}={}){if(!I0(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await R0(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...$0(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 Tm.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 jP,_0,Tm,FP,$0,M0=G(()=>{xt();jP=6e4,_0=5e3,Tm=new Map,FP=(...e)=>fetch(...e);$0=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import Am from"node:fs/promises";import oa from"node:path";import UP from"node:os";import{EventEmitter as HP}from"node:events";import{randomUUID as WP}from"node:crypto";function sa(e){return e?.cwd?oa.join(e.cwd,".bostrat","events"):oa.join(Em,"events")}async function VP(e,t){if(!vu.has(t)){let o=(await K(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);vu.set(t,o)}let n=vu.get(t)+1;return vu.set(t,n),n}function ji(e,t){let n=Array.isArray(e?.epochs)?e.epochs:null;if(!(!n||n.length===0)){if(!Number.isFinite(t))return n.length-1;for(let r=n.length-1;r>=0;r--){let o=n[r].startedAt;if(o==null||o<=t)return r}return 0}}async function N(e,t){let n=typeof e=="string"?e:e?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!t||!qP.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!GP.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!KP.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?oa.join(Em,"events"):sa(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=ji(u,Number(t.payload?.ts??t.ts)||Date.now())}let s=await VP(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||WP(),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 N0(c)&&(c=P0(c),l=JSON.parse(c)),await Am.mkdir(r,{recursive:!0}),await Am.appendFile(C0(r,n),c+`
|
|
23
|
-
`),Ze.emit("event",l),Ze.emit(`track:${n}`,l),l}async function K(e,t){let n,r;typeof e=="string"&&t?(n=e,r=t):(r=typeof e=="string"?e:e?.id,n=typeof e=="string"?oa.join(Em,"events"):sa(e));let o;try{o=await Am.readFile(
|
|
24
|
-
`))if(a.trim())try{s.push(JSON.parse(
|
|
20
|
+
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${Zs}: ${t?.message||t}`)}}async function ee(e={}){let t=e.paneTail===!0,n={at:new Date().toISOString(),ts:Date.now(),...e};t&&delete n.paneTail;let r=[n.flow||"handoff",n.phase||n.step||"?",n.trackId?`track=${n.trackId}`:"",n.sessionName?`box=${n.sessionName}`:"",n.error?`ERR=${n.error}`:""].filter(Boolean).join(" \xB7 ");if(console.log(`[handoff-debug] ${r}`),n.sessionName&&(t||du()))try{n.paneTail=Wo((await Ne(n.sessionName,{lines:t?JN:35})).slice(-YN))}catch{n.paneTail=null}(t||du()||n.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|seed|failed|launch|submit-|gate-accept|blocked|timeout|dialog|exit|config-|waiting|quiet|quiescence-|await-)/.test(String(n.phase||n.step||"")))&&await zN(n)}async function i0({limit:e=80}={}){try{return(await Ri.readFile(Zs,"utf8")).split(`
|
|
21
|
+
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Wo(o.paneTail)),o}catch{return{raw:Wo(r)}}})}catch{return[]}}var VN,um,Zs,s0,JN,YN,c0,yn=G(()=>{dt();VN=[/[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];um=process.env.BOSTRAT_DEBUG_DIR||a0.join(KN.homedir(),".bostrat","debug"),Zs=a0.join(um,"handoff.jsonl"),s0=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;JN=40,YN=4e3;c0={DEBUG_DIR:um,LOG_FILE:Zs}});import XN from"node:fs/promises";import{execFile as ZN}from"node:child_process";import{promisify as QN}from"node:util";function mm(e){dm=typeof e=="function"?e:null}function f0({holdWhile:e=null}={}){pm=typeof e=="function"?e:null}function aP(e){return new Promise(t=>setTimeout(t,e))}function fu(e){let t=String(e||""),n=t.indexOf("/lib/node_modules/");return n>0?t.slice(0,n):null}function iP(e,t){let n=s=>String(s||"").split(/[-+]/)[0].split(".").map(Number),r=n(e),o=n(t);if(!r.length||!o.length||r.some(Number.isNaN)||o.some(Number.isNaN))return!1;for(let s=0;s<Math.max(r.length,o.length);s++){let a=(r[s]||0)-(o[s]||0);if(a)return a<0}return!1}async function m0(e){let t=(await pu("/bin/sh",["-c",`command -v ${e.bin}`])).stdout.trim();if(!t)throw new Error(`${e.bin} not on PATH`);let n=await XN.realpath(t).catch(()=>t),r=fu(n),o=r?{...process.env,PATH:`${r}/bin:${process.env.PATH||""}`}:process.env;return{bin:t,env:o}}async function h0(e,t){return(await pu(t,["--version"],{timeout:p0})).stdout.trim().replace(e.versionRe,"")||null}async function mu({timeoutMs:e=tP()}={}){if(!Oi)return{waited:!1,timedOut:!1};let t=Date.now(),n=null,r=await Promise.race([Oi.then(()=>!1,()=>!1),new Promise(o=>{n=setTimeout(()=>o(!0),e)})]);return clearTimeout(n),{waited:!0,waitedMs:Date.now()-t,timedOut:r}}function hu(e,{ttlMs:t=nP()}={}){let n=String(e||"");return n?(Mi.set(n,Date.now()+t),()=>hm(n)):()=>{}}function hm(e){Mi.delete(String(e||""))}function cP(){let e=Date.now();for(let[t,n]of Mi)n<=e&&Mi.delete(t);return Mi.size}async function lP({timeoutMs:e=rP()}={}){let t=Date.now()+e;for(;cP()>0||pm&&await pm().catch(()=>!1);){if(Date.now()>t)return!1;await aP(oP)}return!0}function w0({cli:e=fm.codex,sessionName:t=null,from:n=null,to:r,reason:o="sweep"}={}){let s=String(r||"").trim();if(!s||l0.has(s))return!1;l0.add(s);let a=u0.then(()=>uP({cli:e,sessionName:t,from:n,to:s,reason:o})).catch(()=>{});u0=a;let i=a.then(()=>{Oi===i&&(Oi=null)});return Oi=i,!0}function wm({sessionName:e,from:t,to:n}={}){let r=String(n||"").trim();r&&(ee({flow:"cli-currency",phase:"fallback",cli:"codex",sessionName:e,from:t,to:r}).catch(()=>{}),w0({cli:fm.codex,sessionName:e,from:t,to:r,reason:"chooser"}))}async function uP({cli:e,sessionName:t,from:n,to:r,reason:o}){await ee({flow:"codex-update",phase:"start",cli:e.name,sessionName:t,from:n,to:r,reason:o}).catch(()=>{});try{await lP()||await ee({flow:"codex-update",phase:"lease-wait-cap",cli:e.name,to:r}).catch(()=>{});let{bin:a,env:i}=await m0(e);await pu("npm",["install","-g",e.pkg],{env:i,timeout:eP});let c=await h0(e,a).catch(()=>null)||r;await ee({flow:"codex-update",phase:"done",cli:e.name,sessionName:t,from:n,to:r,version:c,reason:o}).catch(()=>{}),await d0({cli:e.name,sessionName:t,from:n,to:r,version:c,status:"updated",reason:o})}catch(s){let a=String(s?.message||s).slice(0,300);await ee({flow:"codex-update",phase:"failed",cli:e.name,sessionName:t,from:n,to:r,error:a,reason:o}).catch(()=>{}),await d0({cli:e.name,sessionName:t,from:n,to:r,status:"failed",error:a,reason:o})}}async function d0(e){if(dm)try{await dm(e)}catch{}}async function dP(e="codex",{reason:t="sweep"}={}){let n=fm[e],r={cli:e,installed:null,latest:null,drift:!1,scheduled:!1};if(!n)return r;try{let{bin:o,env:s}=await m0(n);r.installed=await h0(n,o);let a=await pu("npm",["view",n.pkg,"dist-tags.latest"],{env:s,timeout:p0});if(r.latest=String(a.stdout||"").trim().split(/\s+/).pop()||null,!/^\d+\.\d+\.\d+/.test(r.latest||""))throw new Error(`registry answered "${r.latest}"`);r.drift=!!(r.installed&&iP(r.installed,r.latest)),await ee({flow:"cli-currency",phase:"discover",cli:e,installed:r.installed,latest:r.latest,drift:r.drift,reason:t}).catch(()=>{}),r.drift&&(r.scheduled=w0({cli:n,from:r.installed,to:r.latest,reason:t}))}catch(o){await ee({flow:"cli-currency",phase:"discover-failed",cli:e,error:String(o?.message||o).slice(0,300),reason:t}).catch(()=>{})}return r}async function pP({reason:e="sweep"}={}){let t=[];for(let n of sP)t.push(await dP(n,{reason:e}));return t}function g0({env:e=process.env,initialDelayMs:t=9e4}={}){let n=e.BOSTRAT_CLI_CURRENCY_MS===void 0?216e5:Number(e.BOSTRAT_CLI_CURRENCY_MS);if(!(n>0))return null;let r=()=>pP({reason:"sweep"}).catch(()=>{}),o=setTimeout(r,t);o.unref?.();let s=setInterval(r,n);return s.unref?.(),{stop:()=>{clearTimeout(o),clearInterval(s)}}}var pu,eP,p0,tP,nP,rP,oP,fm,sP,l0,u0,Oi,Mi,dm,pm,qo=G(()=>{yn();pu=QN(ZN),eP=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,p0=Number(process.env.BOSTRAT_CLI_REGISTRY_TIMEOUT_MS)||15e3,tP=()=>Number(process.env.BOSTRAT_CLI_UPDATE_WAIT_MS)||45e3,nP=()=>Number(process.env.BOSTRAT_CLI_SPAWN_LEASE_MS)||9e4,rP=()=>Number(process.env.BOSTRAT_CLI_LEASE_WAIT_MS)||12e4,oP=250,fm={codex:{name:"codex",pkg:"@openai/codex",bin:"codex",versionRe:/^codex-cli\s*/}},sP=["codex"],l0=new Set,u0=Promise.resolve(),Oi=null,Mi=new Map,dm=null;pm=null});import y0 from"node:fs/promises";import fP from"node:os";import Ci from"node:path";function mP(e){let n=Ci.resolve(e).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||Ci.join(fP.homedir(),".cursor");return Ci.join(r,"projects",n)}async function Qs(e){if(!e||typeof e!="string")return;let t=Ci.resolve(e),n=mP(t),r=Ci.join(n,".workspace-trusted");await y0.mkdir(n,{recursive:!0}),await y0.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:t},null,2)}
|
|
22
|
+
`,"utf8")}var wu=G(()=>{});import ym from"node:fs/promises";import qr from"node:path";import{execFile as hP}from"node:child_process";import{promisify as wP}from"node:util";async function vP(e){try{return(await ym.stat(qr.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function SP(e){let t=i=>ym.readFile(qr.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 fr(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await vP(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:qr.basename(o),branch:await SP(o)});return}if(s>=kP)return;let i;try{i=await ym.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||xP.has(c.name)||await t(qr.join(o,c.name),s+1)}for(let o of bP)await t(qr.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=await Me(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 TP(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function Me(e){if(!e)return null;if(gm.has(e))return gm.get(e);let t=null;try{let{stdout:n}=await gP("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=TP(n)}catch{t=null}return gm.set(e,t),t}async function ea(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await fr())if((await Me(n.path))?.toLowerCase()===t)return n.path;return null}var gP,b0,yP,bP,kP,xP,gm,Gr=G(()=>{gP=wP(hP),b0=process.env.HOME||"/home/globo",yP=qr.join(process.env.BOSTRAT_STATE_DIR||qr.join(b0,".bostrat"),"repos"),bP=[...(process.env.BOSTRAT_REPOS_ROOT||qr.join(b0,"Documents")).split(","),yP].map(e=>e.trim()).filter(Boolean),kP=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,xP=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);gm=new Map});function ta(e=Date.now()){if(Ko)return{tripped:!1,holdMs:1/0};if(Go>e)return{tripped:!1,holdMs:Go-e};if((!Pi||e-Pi>=gu.windowMs)&&(Pi=e,Ni=0),Ni++,Ni<gu.trip)return{tripped:!1,holdMs:0};yu++;let t=Math.min(gu.minMs*2**(yu-1),gu.maxMs);return Go=e+t,Ni=0,Pi=0,{tripped:!0,holdMs:t}}function k0(){Ni=0,Pi=0,yu=0,Go=0}function Di(e=Date.now()){return Ko?1/0:Go>e?Go-e:0}function na(e=Date.now()){return Di(e)>0}function bm(e={}){Ko={at:Date.now(),...e},Go=1/0}function bu(){return Ko}function x0(e=Date.now()){return{held:na(e),holdMs:Ko?null:Di(e),holds:yu,removed:Ko?{...Ko}:null}}var AP,gu,Ni,Pi,yu,Go,Ko,km=G(()=>{AP={trip:3,windowMs:3e5,minMs:3e5,maxMs:216e5},gu={...AP},Ni=0,Pi=0,yu=0,Go=0,Ko=null});import EP from"node:fs";import v0 from"node:fs/promises";import Bi from"node:path";import _P from"node:os";import{fileURLToPath as IP}from"node:url";import{execFile as $P}from"node:child_process";import{promisify as RP}from"node:util";function De(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 ku(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}function _0(e){return e?.status===404&&/not granted/i.test(String(e?.message||""))}async function ra(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!T0.test(n))throw Vo("repo must be owner/name",400);let r=xm.get(n);if(r&&r.expiresAt-Date.now()>CP)return r;if(!ku(t))throw Vo("node is not plane-linked \u2014 GitHub auth unavailable",503);if(na()){if(r&&r.expiresAt>Date.now())return r;throw Vo(bu()?"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 A0(`${De(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:E0(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw Vo(`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)&&ta(),o.status>=500&&r&&r.expiresAt>Date.now())return r;throw Vo(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 xm.set(n,a),a}async function Li({env:e=process.env,force:t=!1}={}){if(!ku(e))return{configured:!1,repos:[],username:null};if(!t&&Kr&&Date.now()-Kr.at<NP)return Kr.data;if(na())return Kr?.data??{configured:!0,repos:[],username:null,error:"plane refused this node's token"};try{let n=await A0(`${De(e)}/v1/github/repos`,{headers:E0(e)});if((n.status===401||n.status===403||n.status===410)&&ta(),!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Kr={at:Date.now(),data:r},r}catch(n){return Kr?Kr.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function I0(){xm.clear(),Kr=null}async function Bn({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await Me(e):null);if(!r)return{};let{token:o}=await ra(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function vm({env:e=process.env}={}){return(await Li({env:e})).username??null}function LP(e){let[t,n]=String(e).split("/");return Bi.join(MP,t,n)}async function Sm(e,{env:t=process.env}={}){let n=(...r)=>S0("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${DP()}" ${BP(t)}`),await n("config","credential.useHttpPath","true")}async function xu(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!T0.test(String(e||"")))throw Vo(`bad repo slug "${e}"`,400);let r=LP(e);if(EP.existsSync(Bi.join(r,".git")))return r;let{token:o}=await ra(e,{env:t});await v0.mkdir(Bi.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await S0("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:PP,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await v0.rm(r,{recursive:!0,force:!0}).catch(()=>{}),Vo(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Sm(r,{env:t}),r}var S0,OP,MP,T0,CP,NP,PP,A0,xm,Kr,E0,Vo,DP,BP,vt=G(()=>{Gr();km();S0=RP($P),OP=process.env.BOSTRAT_STATE_DIR||Bi.join(process.env.HOME||_P.tmpdir(),".bostrat"),MP=Bi.join(OP,"repos"),T0=/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?\/[A-Za-z0-9._-]{1,100}$/,CP=5*6e4,NP=6e4,PP=10*6e4,A0=fetch,xm=new Map,Kr=null;E0=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Vo=(e,t)=>Object.assign(new Error(e),{status:t});DP=()=>IP(new URL("../bin/bostrat-git-credential",import.meta.url)),BP=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function R0(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}async function M0(e,t,n,r=$0){let o=De(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await FP(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function C0(e,{env:t=process.env,force:n=!1,timeoutMs:r=$0}={}){if(!R0(t))return{facts:[],source:"unconfigured"};let o=Tm.get(e);if(!n&&o&&Date.now()-o.at<jP)return{facts:o.facts,source:"cache"};try{let s=await M0(`/v1/memory/${encodeURIComponent(e)}`,{headers:O0(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 Tm.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 vu(e,t,{env:n=process.env}={}){if(!R0(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await M0(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...O0(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 Tm.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 jP,$0,Tm,FP,O0,N0=G(()=>{vt();jP=6e4,$0=5e3,Tm=new Map,FP=(...e)=>fetch(...e);O0=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import Am from"node:fs/promises";import oa from"node:path";import UP from"node:os";import{EventEmitter as HP}from"node:events";import{randomUUID as WP}from"node:crypto";function sa(e){return e?.cwd?oa.join(e.cwd,".bostrat","events"):oa.join(Em,"events")}async function VP(e,t){if(!Su.has(t)){let o=(await K(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);Su.set(t,o)}let n=Su.get(t)+1;return Su.set(t,n),n}function ji(e,t){let n=Array.isArray(e?.epochs)?e.epochs:null;if(!(!n||n.length===0)){if(!Number.isFinite(t))return n.length-1;for(let r=n.length-1;r>=0;r--){let o=n[r].startedAt;if(o==null||o<=t)return r}return 0}}async function N(e,t){let n=typeof e=="string"?e:e?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!t||!qP.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!GP.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!KP.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?oa.join(Em,"events"):sa(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=ji(u,Number(t.payload?.ts??t.ts)||Date.now())}let s=await VP(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||WP(),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 D0(c)&&(c=B0(c),l=JSON.parse(c)),await Am.mkdir(r,{recursive:!0}),await Am.appendFile(P0(r,n),c+`
|
|
23
|
+
`),Ze.emit("event",l),Ze.emit(`track:${n}`,l),l}async function K(e,t){let n,r;typeof e=="string"&&t?(n=e,r=t):(r=typeof e=="string"?e:e?.id,n=typeof e=="string"?oa.join(Em,"events"):sa(e));let o;try{o=await Am.readFile(P0(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
24
|
+
`))if(a.trim())try{s.push(JSON.parse(D0(a)?B0(a):a))}catch{}return s}function L0(e,t){let n=`track:${e}`;return Ze.on(n,t),()=>Ze.off(n,t)}var qP,GP,KP,Ze,Em,P0,Su,zP,D0,B0,be=G(()=>{qP=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","job.stopped","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.login.started","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"]),GP=new Set(["user","agent","orchestrator","system","peer"]),KP=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor","cli"]),Ze=new HP;Ze.setMaxListeners(0);Em=process.env.BOSTRAT_STATE_DIR||oa.join(process.env.HOME||UP.tmpdir(),".bostrat");P0=(e,t)=>oa.join(e,`${t}.ndjson`),Su=new Map;zP=/(?<!\\)((?:\\\\)*)\\u0000/g,D0=e=>e.includes("\\u0000"),B0=e=>e.replace(zP,"$1\\ufffd")});import ue from"node:fs/promises";import JP from"node:os";import Ae from"node:path";import{execFile as YP}from"node:child_process";import{promisify as XP}from"node:util";function Au(){return Ae.join(Eu,"memory","repos")}function zo(e){return e===mr?Ae.join(Eu,"memory",mr):Ae.join(Au(),e)}async function Ui(e){try{return JSON.parse(await ue.readFile(Ae.join(e,G0),"utf8"))}catch{return{}}}async function Im(e,t){let n=await Ui(e);for(let[r,o]of t)n[r]=Math.max(Number(n[r])||0,o);await ue.mkdir(e,{recursive:!0}),await ue.writeFile(Ae.join(e,G0),JSON.stringify(n))}function F0(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 U0(e){try{let{stdout:t}=await ZP("git",["-C",e,"config","--get","remote.origin.url"]);return t.trim()||null}catch{return null}}async function $m(e){if(!e)return[];let t=F0(await U0(e));if(t)return[t];let n=[],r=[];try{r=await ue.readdir(e,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=F0(await U0(Ae.join(e,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function _u(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 Vr(e,t){let n=new RegExp(`^\\s*${t}:\\s*(.+?)\\s*$`,"m").exec(e||"");return n?n[1].replace(/^"|"$/g,""):null}async function Ln(e,t=null){let n=t??await ue.readFile(e,"utf8").catch(()=>""),{fm:r}=_u(n),o=Vr(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await ue.stat(e)).mtimeMs}catch{return 0}}function K0(e,t){let n=Vr(e,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===mr){let o=Vr(e,"trust");if(!o||/^operator/i.test(o))return mr}return t}function Rm(e,t,n,r=new Map){let o=(e||"").split(`
|
|
25
25
|
`),s=new Map;for(let c of(t||"").split(`
|
|
26
26
|
`)){let l=ia(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=ia(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(`
|
|
27
27
|
`).replace(/\n{3,}/g,`
|
|
28
28
|
|
|
29
|
-
`)}function
|
|
29
|
+
`)}function V0(e,t){let n=Vr(t,"name")||e.replace(/\.md$/,""),r=Vr(t,"description")||"";return`- [${n}](${e})${r?` \u2014 ${r}`:""}`}async function Hi(e){let t=[];try{t=await ue.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 z0(e,t){let n=Ae.join(e,".bostrat","memory"),r=await Hi(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=H0.get(e)??0,s=Date.now(),a=new Map,i=new Map,c=0,l=new Map;for(let d of r){let p=Ae.join(n,d);if(o)try{if((await ue.stat(p)).mtimeMs<o-eD)continue}catch{continue}let f;try{f=await ue.readFile(p,"utf8")}catch{continue}let{fm:w}=_u(f),h=K0(w,t);if(!h)continue;let y=zo(h),k=Ae.join(y,d),g=await Ln(p,f);if(l.has(y)||l.set(y,await Ui(y)),(l.get(y)[d]||0)>=g)continue;let x=await Ln(k).catch(()=>0);if(await ue.access(k).then(()=>!0).catch(()=>!1)&&x>=g)continue;await ue.mkdir(y,{recursive:!0}),await ue.writeFile(k,f),c++;let _=a.get(y);_||a.set(y,_={won:new Set,generated:new Map}),_.won.add(d),_.generated.set(d,V0(d,w)),i.has(h)||i.set(h,new Set),i.get(h).add(d)}let u=await ue.readFile(Ae.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[d,p]of a){let f=Ae.join(d,"MEMORY.md"),w=await ue.readFile(f,"utf8").catch(()=>"");await ue.writeFile(f,Rm(w,u,p.won,p.generated))}return H0.set(e,s),{harvested:c,changedByKey:i}}async function J0(e){let t=0,n=new Map;for(let r of e||[]){if(!r||!await ue.access(Ae.join(r,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[s]=await $m(r),a=await z0(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 tD(){let e=Ae.join(Eu,"workspaces"),t=[];try{t=await ue.readdir(e,{withFileTypes:!0})}catch{return{harvested:0,changedByKey:new Map}}return J0(t.filter(n=>n.isDirectory()).map(n=>Ae.join(e,n.name)))}async function oD(e,t){let n=zo(e),r=await Ui(n),o=new Set,s=new Map,a=[],i=0;for(let c of t||[]){let l=String(c?.file||"");if(!nD.test(l)||l==="MEMORY.md")continue;let u=Ae.join(n,l),d=await ue.access(u).then(()=>!0).catch(()=>!1),p=d?await Ln(u):0,f=Number(c.modified)||0;if(c.deleted){f>p&&(d&&await ue.rm(u,{force:!0}),await Im(n,[[l,f]]),d&&(a.push(l),i++));continue}d&&p>=f||(r[l]||0)>=f||(await ue.mkdir(n,{recursive:!0}),await ue.writeFile(u,String(c.content??"")),o.add(l),typeof c.indexLine=="string"&&c.indexLine.trim()&&s.set(l,c.indexLine),i++)}if(o.size||a.length){let c=Ae.join(n,"MEMORY.md"),l=await ue.readFile(c,"utf8").catch(()=>""),u=Rm(l,[...s.values()].join(`
|
|
30
30
|
`),o);if(a.length){let d=new Set(a);u=u.split(`
|
|
31
31
|
`).filter(p=>!d.has(ia(p))).join(`
|
|
32
|
-
`)}await ue.mkdir(n,{recursive:!0}),await ue.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function
|
|
33
|
-
`);t.length>
|
|
32
|
+
`)}await ue.mkdir(n,{recursive:!0}),await ue.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function Iu(e,t=null){let n=zo(e),r=t?.length?t:await Hi(n);if(!r.length)return{pushed:0,source:"empty"};let o=rD(await ue.readFile(Ae.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=Ae.join(n,a),c;try{c=await ue.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await Ln(i,c),indexLine:o.get(a)??null})}return vu(e,s)}async function sD(e,{timeoutMs:t}={}){let{facts:n,source:r}=await C0(e,t?{timeoutMs:t}:{});n.length?await oD(e,n):r==="plane"&&await Iu(e)}function aD(e){let t=e.split(`
|
|
33
|
+
`);t.length>j0&&(t=t.slice(0,j0));let n=t.join(`
|
|
34
34
|
`);for(;Buffer.byteLength(n,"utf8")>QP&&t.length;)t.pop(),n=t.join(`
|
|
35
35
|
`);return n}async function cD(e,t){if(!e||!t?.length)return{materialized:!1,reason:"no-repo-key"};let n=Ae.join(e,".bostrat","memory"),r=0,o=0,s=[];for(let u of t){let d=zo(u),p=await Ui(d);for(let[y,k]of Object.entries(p)){let g=Ae.join(n,y);await ue.access(g).then(()=>!0).catch(()=>!1)&&await Ln(g)<=k&&(await ue.rm(g,{force:!0}),o++)}let f=await Hi(d);if(!f.length)continue;await ue.mkdir(n,{recursive:!0});for(let y of f){let k=Ae.join(d,y),g=Ae.join(n,y),x=await Ln(k),S=await Ln(g).catch(()=>0);await ue.access(g).then(()=>!0).catch(()=>!1)&&S>=x||(await ue.copyFile(k,g),r++)}let w=await ue.readFile(Ae.join(d,"MEMORY.md"),"utf8").catch(()=>""),h=u===mr?"## operator memory":`## repo: ${u}`;w.trim()&&s.push(t.length>1?`${h}
|
|
36
36
|
|
|
37
37
|
${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(`
|
|
38
38
|
|
|
39
39
|
`)+`
|
|
40
|
-
`,c=aD(i);await ue.mkdir(n,{recursive:!0}),await ue.writeFile(Ae.join(n,"MEMORY.md"),c);let l=Ae.join(n,"claude");return await ue.mkdir(l,{recursive:!0}),await ue.writeFile(Ae.join(l,"MEMORY.md"),iD,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function
|
|
40
|
+
`,c=aD(i);await ue.mkdir(n,{recursive:!0}),await ue.writeFile(Ae.join(n,"MEMORY.md"),c);let l=Ae.join(n,"claude");return await ue.mkdir(l,{recursive:!0}),await ue.writeFile(Ae.join(l,"MEMORY.md"),iD,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function Y0(e){try{let t=await $m(e);if(!t.length)return{materialized:!1,reason:"no-repo-key"};let n=[...t,mr];return await Promise.all(n.map(r=>sD(r,{timeoutMs:lD}))),await cD(e,n)}catch(t){return{materialized:!1,reason:"error",error:t?.message||String(t)}}}function X0(){return{...ct,pending:aa.size,queued:_m.size}}function Z0(e){if(Tu.has(e))return Tu.get(e);_m.add(e);let t=W0.then(async()=>{_m.delete(e),Tu.delete(e);let n=await Om(e);return ct.runs++,ct.lastRunAt=Date.now(),n.error?(ct.errors++,ct.lastError=n.error):ct.harvested+=n.harvested||0,n}).catch(n=>({harvested:0,error:n?.message||String(n)}));return Tu.set(e,t),W0=t.then(()=>{}),t}function $u(e,{delayMs:t=uD}={}){if(!e||typeof e!="string")return;ct.scheduled++;let n=aa.get(e);n&&clearTimeout(n);let r=setTimeout(()=>{aa.delete(e),Z0(e)},t);r.unref?.(),aa.set(e,r)}async function Ru(e,{timeoutMs:t=3e3}={}){if(!e)return{harvested:0,skipped:"no-cwd"};if(!await ue.access(Ae.join(e,".bostrat","memory")).then(()=>!0,()=>!1))return{harvested:0,skipped:"no-memory-dir"};let r=aa.get(e);r&&(clearTimeout(r),aa.delete(e));let o,s=new Promise(a=>{o=setTimeout(()=>a({harvested:0,timedOut:!0}),t),o.unref?.()});try{return await Promise.race([Z0(e),s])}finally{clearTimeout(o)}}async function Q0(e){return Fi||(Fi=(async()=>{try{q0||(q0=!0,await pD());let{harvested:t,changedByKey:n}=await J0(e);for(let[r,o]of n)await Iu(r,[...o]);return ct.reconciles++,ct.lastReconcileAt=Date.now(),ct.harvested+=t,{harvested:t,keys:[...n.keys()]}}catch(t){return ct.errors++,ct.lastError=t?.message||String(t),{harvested:0,error:ct.lastError}}finally{Fi=null}})(),Fi)}function dD(){return Ae.join(Eu,"memory",".full-sweep-done")}async function ex(){let e=dD();try{let t=JSON.parse(await ue.readFile(e,"utf8"));return ct.backfill={at:Number(t?.at)||0,harvested:Number(t?.harvested)||0,prior:!0},{ran:!1,...ct.backfill}}catch{}try{let{harvested:t,changedByKey:n}=await tD();for(let[o,s]of n)await Iu(o,[...s]);let r={at:Date.now(),harvested:t};return await ue.mkdir(Ae.dirname(e),{recursive:!0}),await ue.writeFile(e,JSON.stringify(r)),ct.backfill=r,{ran:!0,...r}}catch(t){return ct.errors++,ct.lastError=t?.message||String(t),{ran:!1,error:ct.lastError}}}async function pD(){let e=0,t=[];try{t=await ue.readdir(Au(),{withFileTypes:!0})}catch{return{moved:e}}for(let n of t){if(!n.isDirectory())continue;let r=Ae.join(Au(),n.name),o=zo(mr),s=new Set,a=new Map;for(let f of await Hi(r)){let w=Ae.join(r,f),h;try{h=await ue.readFile(w,"utf8")}catch{continue}let{fm:y}=_u(h);if(K0(y,n.name)!==mr)continue;let k=Ae.join(o,f),g=await Ln(k).catch(()=>0),x=await Ln(w,h);await ue.mkdir(o,{recursive:!0}),x>g&&await ue.writeFile(k,h),await ue.rm(w,{force:!0}),s.add(f),a.set(f,V0(f,y)),e++}if(!s.size)continue;let i=Date.now();await Im(r,[...s].map(f=>[f,i])),await vu(n.name,[...s].map(f=>({file:f,deleted:!0,modified:i})));let c=Ae.join(r,"MEMORY.md"),l=await ue.readFile(c,"utf8").catch(()=>""),u=l.split(`
|
|
41
41
|
`).filter(f=>!s.has(ia(f))).join(`
|
|
42
|
-
`);await ue.writeFile(c,u).catch(()=>{});let d=Ae.join(o,"MEMORY.md"),p=await ue.readFile(d,"utf8").catch(()=>"");await ue.writeFile(d,Rm(p,l,s,a))}return{moved:e}}async function
|
|
42
|
+
`);await ue.writeFile(c,u).catch(()=>{});let d=Ae.join(o,"MEMORY.md"),p=await ue.readFile(d,"utf8").catch(()=>"");await ue.writeFile(d,Rm(p,l,s,a))}return{moved:e}}async function tx(){let e=[],t=[mr];try{for(let n of await ue.readdir(Au(),{withFileTypes:!0}))n.isDirectory()&&t.push(n.name)}catch{}for(let n of t){let r=zo(n),o=await Hi(r),s=await Ui(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await ue.readFile(Ae.join(r,i),"utf8").catch(()=>""),{fm:l}=_u(c);a.push({file:i,name:Vr(l,"name")||i.replace(/\.md$/,""),description:Vr(l,"description")||"",scope:Vr(l,"scope")||null,modified:await Ln(Ae.join(r,i),c)})}e.push({key:n,facts:a,tombstones:Object.keys(s).length})}return e}async function nx(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=zo(e),r=Ae.join(n,t);if(!await ue.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${t} not found in ${e}`);let s=Date.now();await ue.rm(r,{force:!0}),await Im(n,[[t,s]]);let a=Ae.join(n,"MEMORY.md"),i=await ue.readFile(a,"utf8").catch(()=>"");await ue.writeFile(a,i.split(`
|
|
43
43
|
`).filter(l=>ia(l)!==t).join(`
|
|
44
|
-
`)).catch(()=>{});let c=await
|
|
44
|
+
`)).catch(()=>{});let c=await vu(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function Om(e){try{let t=await $m(e),{harvested:n,changedByKey:r}=await z0(e,t[0]||null);for(let[o,s]of r)await Iu(o,[...s]);return n>0&&await mD(e,r).catch(()=>{}),{harvested:n}}catch(t){return{harvested:0,error:t?.message||String(t)}}}async function mD(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,fD)}}await N({id:n,cwd:e},{type:"memory.harvested",actor:"orchestrator",payload:{harvested:o,keys:r,ts:Date.now()}})}var ZP,Eu,j0,QP,mr,G0,ia,H0,eD,nD,rD,iD,lD,uD,aa,_m,W0,ct,Tu,q0,Fi,fD,Wi=G(()=>{N0();be();ZP=XP(YP),Eu=process.env.BOSTRAT_STATE_DIR||Ae.join(process.env.HOME||JP.tmpdir(),".bostrat"),j0=200,QP=25*1024;mr="operator";G0=".tombstones.json";ia=e=>/\]\(([^)]+\.md)\)/.exec(e)?.[1]||null;H0=new Map,eD=2e3;nD=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,rD=e=>{let t=new Map;for(let n of(e||"").split(`
|
|
45
45
|
`)){let r=ia(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};iD=`# Track memory
|
|
46
46
|
|
|
47
47
|
Shared Track memory (all providers) lives one level up: read \`../MEMORY.md\` for the
|
|
48
48
|
index and the fact files beside it. Facts you save here stay Claude-local but ride the
|
|
49
49
|
same sync; prefer adding durable cross-provider facts to \`../\` following its format.
|
|
50
|
-
`;lD=Number(process.env.BOSTRAT_MEMORY_SPAWN_PULL_MS)||1500;uD=Number(process.env.BOSTRAT_MEMORY_HARVEST_DEBOUNCE_MS)||2e3,aa=new Map,_m=new Set,
|
|
51
|
-
`,n!=null?{mode:n}:void 0),await
|
|
52
|
-
`,r=Dm.join(e,t),o=null;try{o=await
|
|
50
|
+
`;lD=Number(process.env.BOSTRAT_MEMORY_SPAWN_PULL_MS)||1500;uD=Number(process.env.BOSTRAT_MEMORY_HARVEST_DEBOUNCE_MS)||2e3,aa=new Map,_m=new Set,W0=Promise.resolve(),ct={scheduled:0,runs:0,harvested:0,errors:0,lastRunAt:null,lastError:null,reconciles:0,lastReconcileAt:null,backfill:null};Tu=new Map;q0=!1,Fi=null;fD=20});import Ou from"node:fs/promises";import hD from"node:path";async function Bt(e,t,{mode:n}={}){await Ou.mkdir(hD.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${++wD}.tmp`;try{await Ou.writeFile(r,JSON.stringify(t,null,2)+`
|
|
51
|
+
`,n!=null?{mode:n}:void 0),await Ou.rename(r,e)}catch(o){throw await Ou.unlink(r).catch(()=>{}),o}}var wD,zr=G(()=>{wD=0});import ix from"node:fs/promises";import cx from"node:os";import lx from"node:path";function Ge(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Ki(e,t={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),gD);try{return await vD(`${De(n)}${e}`,{...t,signal:r.signal})}finally{clearTimeout(o)}}async function fx(){return Cu||(Mm||=ix.readFile(ux(),"utf8").then(e=>JSON.parse(e)).catch(()=>({ops:[]})).then(e=>Cu={ops:Array.isArray(e?.ops)?e.ops:[],parked:Array.isArray(e?.parked)?e.parked:[]}).finally(()=>Mm=null),Mm)}async function Nu(){await Bt(ux(),Cu).catch(()=>{})}function sx(e){return e.op==="blob"?`blob ${e.family} ${e.id} ${e.v} ${e.kind}`:`${e.op} ${e.family} ${e.id}`}async function Nm(e){let t=await fx(),n=sx(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=>sx(o)===n);r>=0?t.ops[r]=e:t.ops.push(e)}t.ops.length>rx&&(t.ops=t.ops.slice(-rx)),await Nu()}function ax(){Mu||(Mu=setTimeout(()=>{Mu=null,ca().catch(()=>{})},SD),Mu.unref?.())}function Cm(e){let t=new Error(`plane ${e}`);return t.refused=!0,t}async function AD(e,t=process.env){if(e.op==="record"){let n=await Ki(`/v1/content/${encodeURIComponent(e.family)}`,{method:"POST",headers:{...Gi(t),"content-type":"application/json"},body:JSON.stringify({records:[{id:e.id,trackId:e.trackId??null,data:e.data,updatedAt:e.updatedAt,originNode:dx()}]})},t);if(!n.ok)throw Cm(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 ix.readFile(e.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await Ki(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}/blob?v=${e.v}&kind=${encodeURIComponent(e.kind)}`,{method:"POST",headers:{...Gi(t),"content-type":"application/octet-stream"},body:n},t);if(!r.ok&&r.status!==400)throw Cm(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 Ki(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}?deletedAt=${e.deletedAt}`,{method:"DELETE",headers:Gi(t)},t);if(!n.ok)throw Cm(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function ca({env:e=process.env,log:t=console,now:n=Date.now()}={}){return Ge(e)?qi||(qi=(async()=>{let r=await fx();ED(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await AD(a,e)}catch(c){if(!c?.refused)return ax(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<bD)return ax(),await Nu(),{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>ox&&(r.parked=r.parked.slice(-ox)),s++,await Nu();continue}i.rejected&&t.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await Nu()}return{flushed:o,parked:s,pending:0}})().finally(()=>{qi=null}),qi):{flushed:0,source:"unconfigured"}}function ED(e,t){if(!e.parked.length||e.ops.length)return;let n=e.parked.filter(r=>t-(r.parkedAt||0)>=TD);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 It(e,t,{trackId:n=null,env:r=process.env}={}){return Ge(r)?(await Nm({op:"record",family:e,id:t.id,trackId:n??t.trackId??null,data:t,updatedAt:Number(t.updatedAt)||Date.now()}),ca({env:r})):{queued:!1,source:"unconfigured"}}async function Jr(e,t,n,r,o,{env:s=process.env}={}){return Ge(s)?(await Nm({op:"blob",family:e,id:t,v:Number(n),kind:r,path:o}),ca({env:s})):{queued:!1,source:"unconfigured"}}async function bn(e,t,{deletedAt:n=Date.now(),env:r=process.env}={}){return Ge(r)?(await Nm({op:"delete",family:e,id:t,deletedAt:n}),ca({env:r})):{queued:!1,source:"unconfigured"}}async function pt(e,{env:t=process.env}={}){if(!Ge(t))return{records:[],source:"unconfigured"};try{let n=await Ki(`/v1/content/${encodeURIComponent(e)}?tombstones=1`,{headers:Gi(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 Vi(e,t,n,r,{env:o=process.env}={}){if(!Ge(o))return null;try{let s=await Ki(`/v1/content/${encodeURIComponent(e)}/${encodeURIComponent(t)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Gi(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 gD,yD,rx,bD,ox,kD,xD,ux,dx,px,vD,SD,TD,Gi,Cu,Mm,Mu,qi,Yr=G(()=>{vt();zr();gD=1e4,yD=3e4,rx=2e3,bD=5,ox=100,kD=60*6e4,xD=()=>process.env.BOSTRAT_STATE_DIR||lx.join(process.env.HOME||cx.tmpdir(),".bostrat"),ux=()=>lx.join(xD(),"content-outbox.json"),dx=()=>process.env.BOSTRAT_NODE_ID||cx.hostname(),px=()=>dx(),vD=(...e)=>fetch(...e),SD=yD,TD=kD;Gi=(e=process.env)=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`});Cu=null,Mm=null,Mu=null,qi=null});import Pm from"node:fs/promises";import mx from"node:path";async function hx(e,t,{box:n=null,agent:r=null}={}){if(!e||!Ge())return{pushed:!1};try{let o=Date.now();return await It("checkpoints",{id:e,trackId:e,v:o,box:n,agent:r,updatedAt:o},{trackId:e}),await Jr("checkpoints",e,o,"md",t),zi=null,{pushed:!0,v:o}}catch{return{pushed:!1}}}async function OD(){if(zi&&Date.now()-zi.at<RD)return{records:zi.records,source:"plane"};let e=await pt("checkpoints");return e.source==="plane"&&(zi={at:Date.now(),records:e.records}),e}async function Pu(e){try{let t=$D(e);if(!t||!Ge())return{restored:!1};let n=mx.join(e,_D,ID);if(await Pm.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await OD();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 Vi("checkpoints",t,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await Pm.mkdir(mx.dirname(n),{recursive:!0}),await Pm.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var _D,ID,$D,RD,zi,Du=G(()=>{Yr();_D=".bostrat/handoff",ID="latest.md",$D=e=>/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1]??null;RD=6e4,zi=null});import Bu from"node:fs/promises";import Dm from"node:path";import MD from"node:os";async function gx(e=wx){await Bu.mkdir(e,{recursive:!0});for(let t of ND){let n=t==="prepare-commit-msg"?PD:t==="post-commit"?DD:`${Bm}exit 0
|
|
52
|
+
`,r=Dm.join(e,t),o=null;try{o=await Bu.readFile(r,"utf8")}catch{}o!==n&&await Bu.writeFile(r,n,{mode:493}),await Bu.chmod(r,493).catch(()=>{})}return e}function yx({nodeUrl:e,dir:t=wx,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",`${Ji}=${e}`]}function bx(e=process.env){return e.BOSTRAT_NODE_URL||`http://127.0.0.1:${e.PORT||3362}`}var Ji,CD,wx,ND,Bm,PD,DD,Lm=G(()=>{Ys();Ji="BOSTRAT_NODE_URL",CD=process.env.BOSTRAT_STATE_DIR||Dm.join(process.env.HOME||MD.tmpdir(),".bostrat"),wx=Dm.join(CD,"git-hooks"),ND=["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"],Bm=`#!/bin/sh
|
|
53
53
|
# bostrat git hook shim (agent/src/git-hooks.js) \u2014 chains to the repo's own hook,
|
|
54
54
|
# then stamps/reports for rework attribution when running inside a bostrat box.
|
|
55
55
|
hook=$(basename "$0")
|
|
@@ -85,57 +85,57 @@ cwd=$(git rev-parse --show-toplevel 2>/dev/null)
|
|
|
85
85
|
printf '{"sha":"%s","cwd":"%s"}' "$sha" "$cwd" | curl -sS -m 5 -X POST "$${Ji}/api/git/committed" \\
|
|
86
86
|
-H 'content-type: application/json' -H "${pr}: $${dr}" --data-binary @- >/dev/null 2>&1 || true
|
|
87
87
|
exit 0
|
|
88
|
-
`});import{execFile as
|
|
89
|
-
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,p,f,w]=r.split("|"),h=Number(i),y=Number(w);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(h)?n-h*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:d||null,agent:p||(l==="1"?"claude":null),alternateOn:f==="1",createdMs:Number.isFinite(y)&&y>0?y*1e3:null}}).filter(r=>!HD.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function Se(e){return(await kn()).find(n=>n.name===e)||null}function JD(e){if(typeof e!="string")return!1;let t=e.trim();return t.length>=1&&t.length<=zD&&VD.test(t)}function
|
|
90
|
-
`).map(n=>n.trim()).includes(e)}catch{return!1}}function ZD(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 QD(e,t){let n=Date.now()+ec(),r=ZD(t),o=!1,s=0,a=0;for(;;){if(await Jo(YD),s+=1,!await
|
|
91
|
-
`).map(n=>Number(n.trim())).filter(n=>Number.isFinite(n)&&n>0)}catch{return[]}}async function
|
|
92
|
-
`).filter(Boolean).length}catch{return 0}}async function Xo(e,{cols:t=n1,rows:n=r1}={}){if(!je(e))return!0;if(await
|
|
93
|
-
`);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 nc(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=xn(e,t),o=!0,!r)return null}else if(n){if(r=Km(e),o=!1,!r)return null}else return null;let s;try{s=h1(await jt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(
|
|
94
|
-
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function rc(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 oc(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 Fe(e,t=new Set){let n=[];for(let r=1;r<e.length;r++){let o=e[r];if(o==="<<")break;if(
|
|
95
|
-
`,n);i!==-1&&(t=t.slice(0,i))}let r=w1(t);for(let i=0;i<r.length-1;i++){let c=oc(rc(r[i])),l=oc(rc(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(Je(c[0]))&&Je(l[0])==="sed"){let u=
|
|
96
|
-
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function Qr(e,t=""){let n=t.trim();return n||Ym(e)||ha(e)}function Xm(e,t){if(e){if(Array.isArray(e)){for(let n of e)Xm(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&Zm.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))Xm(n,t)}}}async function
|
|
97
|
-
`)){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(Xm(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:Qr(d.command,d.description),ts:u})}return o.slice(-n)}function
|
|
98
|
-
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=t)break}return o}finally{await n.close()}}async function $1(e){let t=await I1(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 Zo(e,t){if(!e&&!t)return null;let n=_1();if(t){let o=n.find(s=>s.id===t);return o||null}let r=await
|
|
88
|
+
`});import{execFile as xx}from"node:child_process";import{promisify as BD}from"node:util";import ju from"node:fs";import{randomUUID as vx}from"node:crypto";function Jo(e){return new Promise(t=>setTimeout(t,e))}function Lu(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 Sx(e){return`/tmp/tmux-${e}/default`}function LD({explicit:e,legacyExists:t,runtimeDir:n,uid:r}){if(e)return e;let o=Sx(r);return t?o:n?`${n}/bostrat/tmux-default`:o}function Xi(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${Yi}=1`]}function UD(){return FD.flatMap(e=>["-e",`${e}=`])}function Zi(e,t,n){let r=Yo(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&&je(n)&&(s+=` --name ${n}`),s}function Yo(e){return/(^|\/|\s)claude(\s|$)/.test(String(e||""))}function Xr(e){return/(^|\/|\s)claude(\s|$)/.test(e)?"claude":/cursor/.test(e)?"cursor":/codex/.test(e)?"codex":/gemini/.test(e)?"gemini":"agent"}function WD(e){let t=String(e||"").trim();return!t||/fake-|\.sh(\s|$)/.test(t)?!1:Yo(t)||/(^|\/|\s)codex(\s|$)/.test(t)||/cursor-agent/.test(t)||/(^|\/|\s)gemini(\s|$)/.test(t)}async function Tx(e,{loginShell:t=!1,failOpen:n=!1}={}){let r=await Ax(e,{loginShell:t});return r===Fu?!0:r===la?!1:n}async function Ax(e,{loginShell:t=!1,timeoutMs:n=jm}={}){if(!e)return la;let r=t?process.env.SHELL||"/bin/sh":"sh",o=t?"-lic":"-c";try{return await Ce(r,[o,'command -v "$1" >/dev/null 2>&1',"_",e],{timeout:n}),Fu}catch(s){return s?.killed||s?.signal?ua:s?.code===1||s?.code===127?la:ua}}async function Uu(e,{retries:t=qD,sleep:n=GD,probe:r=Ax}={}){let o=String(e||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:jm});for(let a=0;s===ua&&a<t.length;a++)await n(t[a]),s=await r(o,{timeoutMs:jm*2});return s}function se(...e){return["-S",zt,...e]}async function Zr(){if(!ju.existsSync(zt))return!1;try{return await Ce(oe,se("list-sessions")),!0}catch(e){let t=e&&e.stderr?String(e.stderr):"";return!/no server running/i.test(t)}}async function Qi(){if(await Zr())return!0;try{ju.mkdirSync(zt.replace(/\/[^/]+$/,""),{recursive:!0}),await Ce(oe,se("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await Zr()}function je(e){return typeof e=="string"&&KD.test(e)}async function kn(){if(!ju.existsSync(zt))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 Ce(oe,se("list-sessions","-F",e)),n=Date.now();return t.trim().split(`
|
|
89
|
+
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,p,f,w]=r.split("|"),h=Number(i),y=Number(w);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(h)?n-h*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:d||null,agent:p||(l==="1"?"claude":null),alternateOn:f==="1",createdMs:Number.isFinite(y)&&y>0?y*1e3:null}}).filter(r=>!HD.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function Se(e){return(await kn()).find(n=>n.name===e)||null}function JD(e){if(typeof e!="string")return!1;let t=e.trim();return t.length>=1&&t.length<=zD&&VD.test(t)}function Hu(e){return e.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function Ex(e){try{let{stdout:t}=await Ce(oe,se("list-sessions","-F","#{session_name}"));return t.split(`
|
|
90
|
+
`).map(n=>n.trim()).includes(e)}catch{return!1}}function ZD(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 QD(e,t){let n=Date.now()+ec(),r=ZD(t),o=!1,s=0,a=0;for(;;){if(await Jo(YD),s+=1,!await Ex(e))return;let i=await Ne(e,{lines:15}),c=!o&&XD.test(i)&&!!r?.test(i),l=!o&&!!r?.test(i);if(!c&&!l&&s>1&&!await Fn(e)){if(a+=1,a>1)return}else c||(a=0);if(c){o=!0,await ee({flow:"spawn",phase:"launch-retry",sessionName:e,agent:Xr(t),promptHead:t.slice(0,120)}).catch(()=>{}),await Ce(oe,se("send-keys","-t",e,"C-u")),await Ce(oe,se("send-keys","-t",e,"-l",t)),await Ce(oe,se("send-keys","-t",e,"Enter"));continue}if(Date.now()>n){await ee({flow:"spawn",phase:"launch-failed",sessionName:e,agent:Xr(t),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function jn(e,t=jD,n={}){if(!JD(e))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=e.trim(),o=Hu(r);if(!je(o))throw new Error("resulting tmux session name is invalid");if(!await Qi())throw new Error(`host tmux server not running on ${zt} and auto-start failed \u2014 start one: ${oe} -S ${zt} new-session -d -s keeper "sleep 2147483647"`);if(await Ex(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||Fm,a=n.agent||Xr(s),i=!!n.resumeSessionId,c=n.resumeSessionId||vx(),l=i?s:Zi(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),d=a==="claude"&&Yo(s),p=n.firstRun===!0;if(a==="codex"){let y=await mu();y.waited&&await ee({flow:"cli-currency",phase:"spawn-gate",sessionName:o,...y}),hu(o)}p||(d&&await Wr(t),a==="codex"&&await Xs(t),a==="cursor"&&await Qs(t),await Y0(t),n.freshWorkspace!==!0&&await Pu(t));let f=d&&!p?cu():null,w=Zk(),h=["-e",`${dr}=${w}`];h.push(...yx({nodeUrl:bx()})),h.push(...UD()),f&&h.push("-e",`ANTHROPIC_API_KEY=${f}`),d&&h.push(...Xi());try{await Ce(oe,se("new-session","-d","-s",o,"-c",t,...h))}catch(y){throw Lu(f?Lu(y,f):y,w)}return await Xo(o),await Ce(oe,se("set-option","-t",o,"@bostrat-session","1")),await Ce(oe,se("set-option","-t",o,"@bostrat-title",r)),await Ce(oe,se("set-option","-t",o,"@bostrat-sid",c)),await Ce(oe,se("set-option","-t",o,"@bostrat-agent",a)),await Ce(oe,se("send-keys","-t",o,l,"Enter")),WD(l)&&QD(o,l).catch(()=>{}),{name:o,title:r,cwd:t,sessionId:c,agent:a,pinned:u,hookToken:w}}async function Lt(e){if(!je(e))throw new Error("invalid session name");await Ce(oe,se("kill-session","-t",e))}async function _x(e,t){if(je(e))try{let n=t?se("set-option","-t",e,"@bostrat-sid",t):se("set-option","-u","-t",e,"@bostrat-sid");await Ce(oe,n)}catch{}}async function Ne(e,{lines:t=40}={}){if(!je(e))return"";try{let n=se("capture-pane","-t",e,"-p");Number.isFinite(t)&&t>0&&n.push("-S",`-${Math.floor(t)}`);let{stdout:r}=await Ce(oe,n);return String(r||"")}catch{return""}}async function Ix(e){if(!je(e))return[];try{let{stdout:t}=await Ce(oe,se("list-panes","-s","-t",e,"-F","#{pane_pid}"));return String(t||"").trim().split(`
|
|
91
|
+
`).map(n=>Number(n.trim())).filter(n=>Number.isFinite(n)&&n>0)}catch{return[]}}async function $x(e){if(!je(e))return 0;try{let{stdout:t}=await Ce(oe,se("list-clients","-t",e,"-F","1"));return String(t||"").trim().split(`
|
|
92
|
+
`).filter(Boolean).length}catch{return 0}}async function Xo(e,{cols:t=n1,rows:n=r1}={}){if(!je(e))return!0;if(await $x(e))return!1;try{let r=await Rx(e);if(r.cols===t&&r.rows===n)return!0;await Ce(oe,se("resize-window","-t",e,"-x",String(t),"-y",String(n))),await Ce(oe,se("set-window-option","-t",e,"window-size","latest")),await Jo(80)}catch{}return!0}async function o1(e){if(!je(e))return 0;try{let{stdout:t}=await Ce(oe,se("display-message","-t",e,"-p","#{pane_pid}"));return Number(String(t||"").trim())||0}catch{return 0}}async function Fn(e){let t=await o1(e);if(!t)return!1;try{return await Ce("pgrep",["-P",String(t)]),!1}catch(n){return n?.code===1}}async function Rx(e){if(!je(e))return{cols:0,rows:0};try{let{stdout:t}=await Ce(oe,se("display-message","-t",e,"-p","#{window_width} #{window_height}")),[n,r]=String(t||"").trim().split(/\s+/).map(o=>Number(o)||0);return{cols:n,rows:r}}catch{return{cols:0,rows:0}}}async function Wu(e,{cols:t=e1,rows:n=t1}={}){if(je(e)&&!await $x(e))try{let r=await Rx(e),o=Math.max(r.cols,t),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await Ce(oe,se("resize-window","-t",e,"-x",String(o),"-y",String(s))),await Ce(oe,se("set-window-option","-t",e,"window-size","latest")),await Jo(80))}catch{}}async function Jt(e){return await Wu(e),Ne(e,{lines:ze})}async function Ke(e,t,{settleMs:n=40,kind:r}={}){if(!je(e))throw new Error("invalid session name");let o=(Array.isArray(t)?t:[t]).filter(s=>Ox.has(s));for(let s=0;s<o.length;s++)await Ce(oe,se("send-keys","-t",e,o[s])),n>0&&s<o.length-1&&await Jo(n);return ee({flow:"tmux",phase:"send-keys",sessionName:e,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function Wm(e,t,{settleMs:n=40,kind:r}={}){if(!je(e))throw new Error("invalid session name");let o=(Array.isArray(t)?t:[]).filter(s=>s&&(typeof s.key=="string"&&Ox.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 Ce(oe,se("send-keys","-t",e,"-l",a.text)):await Ce(oe,se("send-keys","-t",e,a.key)),n>0&&s<o.length-1&&await Jo(n)}return ee({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 da(e,t,{kind:n}={}){if(!je(e))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(t)||0));return r>0&&await Ce(oe,se("send-keys","-t",e,"-N",String(r),"BSpace")),ee({flow:"tmux",phase:"send-keys",sessionName:e,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function Gu(e,t,{perCharDelayMs:n=120,kind:r}={}){if(!je(e))throw new Error("invalid session name");let o=[...String(t)];for(let s of o)await Ce(oe,se("send-keys","-t",e,"-l",s)),n>0&&await Jo(n);ee({flow:"tmux",phase:"type",sessionName:e,kind:r||"type",textLen:o.length,textHead:String(t).slice(0,120)}).catch(()=>{})}function s1(e,t){return new Promise((n,r)=>{let o=xx(oe,se(...e),(s,a,i)=>{s?r(s):n({stdout:a,stderr:i})});o.stdin.on("error",()=>{}),o.stdin.end(t)})}async function a1(e,t){let n=`bostrat-${vx().slice(0,8)}`;await s1(["load-buffer","-b",n,"-"],t),await Ce(oe,se("paste-buffer","-p","-d","-b",n,"-t",e))}async function Qe(e,t,{enter:n=!0,submitDelayMs:r=0,kind:o,paste:s=!1}={}){if(!je(e))throw new Error("invalid session name");if(typeof t=="string"&&t.length>0&&(s?await a1(e,t):await Ce(oe,se("send-keys","-t",e,"-l",t))),n){let a=Number(r);Number.isFinite(a)&&a>0&&await Jo(a),await Ce(oe,se("send-keys","-t",e,"Enter"))}ee({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 Ce,oe,kx,zt,jD,Fm,Yi,FD,Um,HD,Fu,la,ua,jm,qD,GD,Hm,KD,VD,zD,ec,YD,XD,e1,t1,ze,n1,r1,Ox,qu,dt=G(()=>{$i();uu();qo();wu();Wi();Du();yn();Ys();Lm();Ce=BD(xx);oe=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";kx=typeof process.getuid=="function"?process.getuid():0,zt=LD({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return ju.existsSync(Sx(kx))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:kx}),jD=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",Fm=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",Yi="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";FD=Object.freeze(["BOSTRAT_PLANE_URL","BOSTRAT_PLANE_TOKEN"]);Um="bostrat-usage-probe",HD=new Set([Um]);Fu="present",la="absent",ua="unknown",jm=5e3,qD=[250,1e3,3e3],GD=e=>new Promise(t=>setTimeout(t,e));Hm={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"};KD=/^[A-Za-z0-9_.-]{1,64}$/;VD=/^[A-Za-z0-9 ._↻-]+$/,zD=64;ec=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,YD=1e3,XD=/not found|Unknown command/i;e1=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,t1=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,ze=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,n1=Number(process.env.BOSTRAT_BOX_COLS)||120,r1=Number(process.env.BOSTRAT_BOX_ROWS)||40;Ox=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);qu=1600});import tc from"node:fs";import p1 from"node:fs/promises";import pa from"node:path";import f1 from"node:os";function Gm(e){return e.replace(/[^A-Za-z0-9]/g,"-")}function Km(e){let t=pa.join(qm,Gm(e)),n;try{n=tc.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=pa.join(t,o);try{let{mtimeMs:a}=tc.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function xn(e,t){if(!t)return null;let n=pa.join(qm,Gm(e),`${t}.jsonl`);return tc.existsSync(n)?n:null}function Lx(e){if(!e)return[];let t=pa.join(qm,Gm(e)),n;try{n=tc.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=tc.statSync(pa.join(t,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function jt(e,t=131072){let n=await p1.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 h1(e){let t=e.split(`
|
|
93
|
+
`);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 nc(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=xn(e,t),o=!0,!r)return null}else if(n){if(r=Km(e),o=!1,!r)return null}else return null;let s;try{s=h1(await jt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(Bx,s.model),i=Bx[s.model]||m1;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 Bx,m1,qm,vn=G(()=>{Bx={"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},m1=2e5,qm=process.env.BOSTRAT_CLAUDE_PROJECTS||pa.join(f1.homedir(),".claude","projects")});function w1(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===`
|
|
94
|
+
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function rc(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 oc(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 Fe(e,t=new Set){let n=[];for(let r=1;r<e.length;r++){let o=e[r];if(o==="<<")break;if(Ux(o)){t.has(o)&&r++;continue}n.push(o)}return n}function Hx(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 b1(e){let t=Hx(e),n=St(Fe(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 Vm(e){if(e.includes("--files")){let s=St(Fe(e,jx)[0]);return s&&s!=="."?`List files in ${fa(s)}`:"List files"}let t=Fe(e,jx),n=t[0];if(!n)return null;let r=St(t.slice(1).find(s=>s!==".")),o=ma(n,40);return r?`Search for \u201C${o}\u201D in ${fa(r)}`:`Search for \u201C${o}\u201D`}function zm(e){let t=Fe(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 Jm(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=Fe(e)[0];return n?`Run ${Je(n)}`:null}function k1(e){let t=Fe(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 Fx(e){return e.find(t=>/^https?:\/\//.test(t))||null}function S1(e){let t=oc(rc(e));if(!t.length)return null;if(/^(ba|z)?sh$/.test(Je(t[0]))&&(t[1]==="-lc"||t[1]==="-c")&&t[2])return Ym(t[2]);let n=Je(t[0]);if(v1.has(n))return null;if(t.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=x1[n];return r?r(t.map((o,s)=>s===0?n:o)):null}function Ym(e){let t=String(e||"").trim();if(!t)return null;let n=t.indexOf("<<");if(n!==-1){let i=t.indexOf(`
|
|
95
|
+
`,n);i!==-1&&(t=t.slice(0,i))}let r=w1(t);for(let i=0;i<r.length-1;i++){let c=oc(rc(r[i])),l=oc(rc(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(Je(c[0]))&&Je(l[0])==="sed"){let u=Hx(l),d=Fe(c)[0];if(u&&d)return ma(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=S1(i);c&&o.push({d:c,head:Je(oc(rc(i))[0]||""),sub:Fe(oc(rc(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":ma(s?s.d:o[0].d,88)}var ma,St,Je,fa,Ux,jx,g1,y1,x1,v1,Wx=G(()=>{ma=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,St=e=>e&&!/[$`]/.test(e)?e:null,Je=e=>e.replace(/\/+$/,"").split("/").pop()||e,fa=e=>{let t=e.replace(/\/+$/,"").split("/").filter(Boolean);return t.length>3?t.slice(-2).join("/"):e},Ux=e=>e.startsWith("-"),jx=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);g1={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"},y1={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"};x1={git:e=>{let t=Fe(e,new Set(["-C","-c"])),n=g1[t[0]];return n?n(t,e):null},gh:e=>{let t=Fe(e),n=y1[t[0]];return n?n(t):null},sed:b1,rg:Vm,grep:Vm,egrep:Vm,nl:e=>{let t=St(Fe(e)[0]);return t?`Read ${Je(t)}`:null},cat:e=>{let t=St(Fe(e)[0]);return t?`Read ${Je(t)}`:null},head:e=>{let t=St(Fe(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the start of ${Je(t)}`:null},tail:e=>{let t=St(Fe(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the end of ${Je(t)}`:null},wc:e=>{let t=St(Fe(e)[0]);return t?`Count lines in ${Je(t)}`:"Count input lines"},ls:e=>{let t=St(Fe(e)[0]);return t&&t!=="."?`List files in ${fa(t)}`:"List files"},find:e=>St(e[1])&&!Ux(e[1])&&e[1]!=="."?`Find files in ${fa(e[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:e=>{let t=St(Fe(e)[0]);return t?`Create directory ${fa(t)}`:null},touch:e=>{let t=St(Fe(e)[0]);return t?`Create ${Je(t)}`:null},rm:e=>{let t=St(Fe(e)[0]);return t?`Delete ${fa(t)}`:null},mv:e=>{let t=St(Fe(e)[0]);return t?`Move ${Je(t)}`:null},cp:e=>{let t=St(Fe(e)[0]);return t?`Copy ${Je(t)}`:null},chmod:e=>{let t=St(Fe(e)[1]);return t?`Change permissions on ${Je(t)}`:null},diff:e=>{let t=Fe(e);return t.length>=2?`Compare ${Je(t[0])} and ${Je(t[1])}`:null},curl:e=>{let t=Fx(e);return t?`Fetch ${ma(t,56)}`:"Fetch a URL"},wget:e=>{let t=Fx(e);return t?`Fetch ${ma(t,56)}`:"Fetch a URL"},npm:zm,pnpm:zm,yarn:zm,npx:e=>{let t=Fe(e)[0];return t?`Run ${t}`:null},node:e=>Jm(e,"Node"),python:e=>Jm(e,"Python"),python3:e=>Jm(e,"Python"),make:e=>{let t=Fe(e)[0];return t?`Run make ${t}`:"Run make"},systemctl:k1,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=Fe(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=Fe(e)[0];return t?`Locate ${t}`:null},command:e=>e[1]==="-v"&&e[2]?`Locate ${e[2]}`:null,fly:e=>{let t=Fe(e)[0];return t==="deploy"?"Deploy to Fly":t?`Run fly ${t}`:null}},v1=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"])});function ha(e){let t=e.split(`
|
|
96
|
+
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function Qr(e,t=""){let n=t.trim();return n||Ym(e)||ha(e)}function Xm(e,t){if(e){if(Array.isArray(e)){for(let n of e)Xm(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&Zm.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))Xm(n,t)}}}async function qx(e,{maxBytes:t=1048576,limit:n=400}={}){let r;try{r=await jt(e,t)}catch{return[]}let o=[];for(let s of r.split(`
|
|
97
|
+
`)){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(Xm(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:Qr(d.command,d.description),ts:u})}return o.slice(-n)}function Gx(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(...T1(i,r.get(a)||[]));return s.sort((a,i)=>(a.ts||0)-(i.ts||0))}function T1(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,h)=>h>=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 Zm,wa=G(()=>{vn();Wx();Zm=new Set(["Bash"])});import Kx from"node:fs";import eh from"node:fs/promises";import Qm from"node:path";import A1 from"node:os";async function Vx(e){try{return await eh.realpath(e)}catch{return e}}function _1(e=E1){let t=[],n=r=>{let o;try{o=Kx.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=Qm.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:Kx.statSync(a).mtimeMs})}catch{}}}};return n(e),t.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function I1(e,t=1048576){let n;try{n=await eh.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(`
|
|
98
|
+
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=t)break}return o}finally{await n.close()}}async function $1(e){let t=await I1(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 Zo(e,t){if(!e&&!t)return null;let n=_1();if(t){let o=n.find(s=>s.id===t);return o||null}let r=await Vx(e);for(let o of n){let s=await $1(o.file);if(s&&(s===e||await Vx(s)===r))return o}return null}async function zx(e,t,{limit:n=80,maxBytes:r=524288}={}){let o=await Zo(e,t);if(!o)return[];let s;try{s=await jt(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,d=null;for(let p of s.split(`
|
|
99
99
|
`)){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 g=w.payload?.type;g==="task_started"?(l=!0,u=!1,d=w.payload?.turn_id||null):g==="task_complete"&&l&&(l=!1,!u&&w.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:Qo,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 h=w.payload;if(!h)continue;if((h.type==="function_call"||h.type==="custom_tool_call")&&h.name){u=!0,th.has(h.name)||c.push(h.name);continue}if(h.type!=="message"||h.role!=="assistant")continue;u=!0;let y=O1(h.content),k=c.length?c.slice():null;c=[],!(!y&&!k)&&a.push({role:"assistant",text:y,tools:k||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 M1(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 C1(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+=`
|
|
100
|
-
`;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 N1(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=C1(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function
|
|
101
|
-
`)){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"&&sc.has(u.name)){let f;try{f=JSON.parse(u.arguments||"{}")}catch{continue}let w=M1(f);w&&(d=[w])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=N1(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:Qr(f),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function
|
|
102
|
-
`).trim():"";sc=new Set(["exec_command","shell","exec"]),th=new Set(["wait","write_stdin"]),Qo="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 ac({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&
|
|
103
|
-
`).trim():""}function L1(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 j1(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&t.type==="tool_result")}function nh(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
|
|
104
|
-
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type==="attachment"){let k=l.attachment;k?.type==="queued_command"&&k.commandMode==="task-notification"&&typeof k.prompt=="string"&&k.prompt.trim()&&o.push({role:"user",text:k.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 k of u.content){let g=k?.type==="tool_use"?k.input?.file_path||k.input?.path:null;typeof g=="string"&&F1.test(g)&&s.set(k.id,g.split("/").pop()),k?.type==="tool_use"&&k.name==="Skill"&&k.id&&a.set(k.id,String(k.input?.skill||"").trim().replace(/^\//,"")||null)}if(j1(u.content)){let k=
|
|
105
|
-
|
|
106
|
-
`):""}var F1,U1,
|
|
107
|
-
`);if(!
|
|
108
|
-
`);for(let r=n.length-1;r>=0;r--)if(
|
|
100
|
+
`;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 N1(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=C1(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function Jx(e,t,{limit:n=400,maxBytes:r=524288}={}){let o=await Zo(e,t);if(!o)return[];let s;try{s=await jt(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
|
|
101
|
+
`)){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"&&sc.has(u.name)){let f;try{f=JSON.parse(u.arguments||"{}")}catch{continue}let w=M1(f);w&&(d=[w])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=N1(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:Qr(f),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function Yx(e,t){let n=await Zo(e,t);if(!n)return null;try{return Math.round((await eh.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var E1,R1,O1,sc,th,Qo,es=G(()=>{vn();wa();E1=process.env.BOSTRAT_CODEX_SESSIONS||Qm.join(process.env.CODEX_HOME||Qm.join(A1.homedir(),".codex"),"sessions");R1=new Set(["output_text","text"]),O1=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&typeof t.text=="string"&&R1.has(t.type)).map(t=>t.text).join(`
|
|
102
|
+
`).trim():"";sc=new Set(["exec_command","shell","exec"]),th=new Set(["wait","write_stdin"]),Qo="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 ac({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&Xx.has(r))return Xx.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 Zx(e){if(!e||e.role!=="assistant")return null;if(e.apiError){let t=e.apiError;return{cls:t.cls||ac({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===Qo?"other":ac({text:t}),code:null,status:null,requestId:null}}return null}function Qx(e,{codexFailedText:t=null}={}){let n=String(e?.text||"").trim();return n?e?.model==="<synthetic>"?/^No response requested\.$/.test(n)?null:{cls:ac({text:n}),code:null,status:P1(n),requestId:null,text:n}:t&&n===t?{cls:"other",code:null,status:null,requestId:null,text:n}:null:null}function P1(e){let t=/^API Error:\s*(\d{3})\b/.exec(e);return t?Number(t[1]):null}var aY,Xx,zu=G(()=>{es();aY=Object.freeze(["entitlement","auth","rate_limit","overloaded","server","other"]),Xx=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 D1(e,t){return!e||!t?null:xn(e,t)}function B1(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(`
|
|
103
|
+
`).trim():""}function L1(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 j1(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&t.type==="tool_result")}function nh(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 ev(e,t){if(!e.uuid)return null;let n=nh(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 rh(e,{maxBytes:t=524288,limit:n=80}={}){let r;try{r=await jt(e,t)}catch{return[]}let o=[],s=new Map,a=new Map;for(let i of r.split(`
|
|
104
|
+
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type==="attachment"){let k=l.attachment;k?.type==="queued_command"&&k.commandMode==="task-notification"&&typeof k.prompt=="string"&&k.prompt.trim()&&o.push({role:"user",text:k.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 k of u.content){let g=k?.type==="tool_use"?k.input?.file_path||k.input?.path:null;typeof g=="string"&&F1.test(g)&&s.set(k.id,g.split("/").pop()),k?.type==="tool_use"&&k.name==="Skill"&&k.id&&a.set(k.id,String(k.input?.skill||"").trim().replace(/^\//,"")||null)}if(j1(u.content)){let k=ev(l,s);k&&o.push({role:"assistant",text:"",images:k,ts:l.timestamp?Date.parse(l.timestamp):null,model:null});continue}let p=B1(u.content),f=d==="assistant"?L1(u.content):null,w=d==="user"?ev(l,s):null;if(!p&&!f&&!w)continue;let h=d==="user"&&l.isMeta&&typeof l.sourceToolUseID=="string"&&l.sourceToolUseID?{toolUseId:l.sourceToolUseID,skill:a.get(l.sourceToolUseID)??null}:null,y=d==="assistant"&&l.isApiErrorMessage?{code:typeof l.error=="string"?l.error:null,status:Number(l.apiErrorStatus)||null,requestId:l.requestId||null,cls:ac({error:l.error,status:l.apiErrorStatus,text:p})}:null;o.push({role:d,text:p,tools:f||void 0,images:w||void 0,injected:h||void 0,ts:l.timestamp?Date.parse(l.timestamp):null,model:u.model||null,apiError:y||void 0})}return o.slice(-n)}async function tv(e,t,{turns:n=16}={}){let r=D1(e,t);if(!r)return"";let o=await rh(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}:** ${U1(s.text||c,700)} ${i}`.trim()}).join(`
|
|
105
|
+
|
|
106
|
+
`):""}var F1,U1,Ju=G(()=>{zu();vn();F1=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;U1=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e});function W1(e,{provider:t,box:n}){let r=String(e).replace(/\r/g,"").split(`
|
|
107
|
+
`);if(!nv.test(e))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(H1);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"&&nv.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(`
|
|
108
|
+
`);for(let r=n.length-1;r>=0;r--)if(sv.test(n[r]))return n.slice(r).join(`
|
|
109
109
|
`);return t}function q1(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 G1(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 K1(e,t=""){let n=hr(e);if(ic.test(n)||!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n))return null;let r=n.split(`
|
|
110
|
-
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(
|
|
111
|
-
`).pop()||"";return z1.test(r)?{provider:"codex",box:t,multiSelect:!1,cursorIndex:0,answerMode:"keys",setup:"continue",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 X1(e,t=""){let n=hr(e);if(ic.test(n)||!
|
|
112
|
-
`),o=r.findIndex(i=>
|
|
113
|
-
`).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",setup:"login-start",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 nB(e,t=""){let n=String(e);if(!tB.test(n)||
|
|
114
|
-
`),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,setup:"link",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 rB(e,t=""){let n=String(e);return!/Scan this QR code to log in/.test(n)||
|
|
110
|
+
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(rv);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(rv.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&&!sv.test(r[l])&&q1(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",setup:"login-method",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=G1(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function J1(e,t=""){let n=hr(e);if(ic.test(n)||!V1.test(n))return null;let r=n.trimEnd().split(`
|
|
111
|
+
`).pop()||"";return z1.test(r)?{provider:"codex",box:t,multiSelect:!1,cursorIndex:0,answerMode:"keys",setup:"continue",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 X1(e,t=""){let n=hr(e);if(ic.test(n)||!ov.test(n))return null;let r=n.split(`
|
|
112
|
+
`),o=r.findIndex(i=>ov.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(Y1);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",setup:"device-code",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 Z1(e,t=""){let n=hr(e);return ic.test(n)||!/open the following link to authenticate/.test(n)?null:{provider:"codex",box:t,multiSelect:!1,cursorIndex:0,answerMode:"esc",setup:"browser",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 Q1(e,t=""){let n=hr(e);return ic.test(n)||!/Paste or type your API key/.test(n)?null:{provider:"codex",box:t,multiSelect:!1,cursorIndex:0,answerMode:"esc",setup:"apikey",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 eB(e,t=""){let n=String(e);if(!/Press any key to log in/.test(n)||Yu.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
113
|
+
`).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",setup:"login-start",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 nB(e,t=""){let n=String(e);if(!tB.test(n)||Yu.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
114
|
+
`),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,setup:"link",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 rB(e,t=""){let n=String(e);return!/Scan this QR code to log in/.test(n)||Yu.test(n)?null:{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,setup:"qr",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 oB(e,t=""){let n=String(e);return Yu.test(n)||!/Trust this workspace/.test(n)||!/\[a\]\s*Trust/i.test(n)?null:{provider:"cursor",box:t,setup:"trust",answerMode:"keys",cursorIndex:0,promptId:"cursor:setup:trust",multiSelect:!1,questions:[{header:"Workspace trust",question:"Trust this workspace?",multiSelect:!1,options:[{label:"Trust this workspace",description:"Allow Cursor to work in this node's workspace"}]}]}}function av(e,t,n=""){if(e!=="cursor"&&e!=="codex")return null;if(e==="codex"){let r=J1(t,n)||X1(t,n)||Z1(t,n)||Q1(t,n)||K1(t,n);if(r)return r}if(e==="cursor"){let r=oB(t,n)||nB(t,n)||rB(t,n)||eB(t,n);if(r)return r}return W1(t,{provider:e,box:n})}function iv(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 H1,nv,ic,sv,rv,V1,z1,ov,Y1,Yu,tB,Xu=G(()=>{H1=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,nv=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;ic=/(^|\n)\s*›/,sv=/^\s*Welcome to Codex\b|^\s*(?:│\s*)?>_ OpenAI Codex\b/;rv=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;V1=/Successfully logged in\./,z1=/^\s*Press Enter to continue\.\s*$/;ov=/Enter this one-time code/,Y1=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;Yu=/Run Everything|Add a follow-up|Plan, search, build anything/;tB=/click this link to log in|Signing in with the browser/});function cc(e,t){if(e!=="claude")return!1;let n=String(t||"");if(!lv.test(n))return!1;let r=n.replace(/\r/g,"").split(`
|
|
115
115
|
`).filter(o=>o.trim());return sB.test(r.slice(-6).join(`
|
|
116
|
-
`))}function
|
|
117
|
-
`);for(let n=t.length-1;n>=0;n--)if(t[n].trim())return t[n];return""}function
|
|
116
|
+
`))}function cv(e){let t=String(e||"").replace(/\r/g,"").split(`
|
|
117
|
+
`);for(let n=t.length-1;n>=0;n--)if(t[n].trim())return t[n];return""}function uv(e,t){let n=String(t||"");return e==="claude"?lv.test(n)?!1:aB.test(cv(n)):e==="codex"?lB.test(n)||!iB.test(n)?!1:cB.test(cv(n)):!1}function dv(e){let t=String(e||"").match(uB);if(!t)return null;let n=t[1].replace(/[.,;:]+$/,"");return n.length>1&&n.length<=120?n:null}function pv(e){return ga.get(e)||null}function Zu(e){let t=ga.get(e);return t||(t={issuedAt:0,issueCount:0,startedReason:null,enterClearedAt:0,account:null,connectedAt:0,reported:{started:!1,connected:!1}},ga.set(e,t)),t}function fv(e){let t=ga.get(e);return!!t&&!!t.startedReason&&!t.connectedAt}function Qu(e,{onDrive:t=!1,now:n=Date.now()}={}){let r=ga.get(e);return!r||!r.issuedAt?!0:t?n-r.issuedAt>=dB:!1}function ed(e,t=Date.now()){let n=Zu(e);return n.issuedAt=t,n.issueCount+=1,n.connectedAt&&(n.connectedAt=0,n.reported={started:!1,connected:!1},n.startedReason=null),n.startedReason||(n.startedReason="not-logged-in"),n}function mv(e,t=""){let n=Zu(e);n.startedReason||(n.startedReason="auth-screen"),n.connectedAt&&(n.connectedAt=0,n.reported={started:!1,connected:!1});let r=dv(t);return r&&(n.account=r),n}function td(e,t="",n=Date.now()){let r=Zu(e);r.startedReason||(r.startedReason="auth-screen"),r.enterClearedAt=n;let o=dv(t);return o&&(r.account=o),r}function hv(e,t=Date.now()){let n=Zu(e);return n.connectedAt=t,n}function wv(e){let t=ga.get(e);if(!t)return{started:!1,connected:!1};let n={started:!1,connected:!1};return t.startedReason&&!t.reported.started&&(t.reported.started=!0,n.started=!0),t.connectedAt&&!t.reported.connected&&(t.reported.connected=!0,n.connected=!0),n}var sB,lv,aB,iB,cB,lB,uB,ga,dB,oh=G(()=>{sB=/Not logged in · (?:Run|Please run) \/login/,lv=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;aB=/Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/,iB=/Successfully logged in\./,cB=/^\s*Press Enter to continue\.\s*$/,lB=/^\s*›/m;uB=/Logged in as\s+(\S+@\S+|\S+)/;ga=new Map;dB=3e4});function hB(e){let t=String(e||"").replace(/\r/g,"").split(`
|
|
118
118
|
`),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(fB.test(r)||mB.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(`
|
|
119
|
-
`).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 kB(e){let t=String(e||"");if(!rt.codex.block.test(t))return null;let n=hr(t);if(!rt.codex.block.test(n))return null;let r=n.match(bB);return{from:r?.[1]||null,to:r?.[2]||null}}function Ft(e){return new Promise(t=>setTimeout(t,e))}function eo(e,t){let n=Date.now(),r=xB();for(let[s,a]of
|
|
120
|
-
`),r=-1;for(let o=n.length-1;o>=0;o--)if(
|
|
119
|
+
`).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 kB(e){let t=String(e||"");if(!rt.codex.block.test(t))return null;let n=hr(t);if(!rt.codex.block.test(n))return null;let r=n.match(bB);return{from:r?.[1]||null,to:r?.[2]||null}}function Ft(e){return new Promise(t=>setTimeout(t,e))}function eo(e,t){let n=Date.now(),r=xB();for(let[s,a]of nd)n-a>=r&&nd.delete(s);let o=`${e}::${t}`;return nd.has(o)?!1:(nd.set(o,n),!0)}function kv(e,t){if(e?.busyScope!=="footer")return t;let n=String(t||"").split(`
|
|
120
|
+
`),r=-1;for(let o=n.length-1;o>=0;o--)if(bv.test(n[o])){r=o;break}return r<0?t:n.slice(r+1).join(`
|
|
121
121
|
`)}function SB(e){let t=0;for(let n of String(e).split(`
|
|
122
|
-
`))
|
|
122
|
+
`))bv.test(n)&&(t=Math.max(t,n.trimEnd().length));return t}function lc(e,t){let n=rt[e];if(!n?.busy)return{state:"idle",reason:null};if(ya(e,t))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(t))return{state:"idle",reason:null};if(n.busy.test(kv(n,t)))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(t))return{state:"busy",reason:null};let r=SB(t);return e==="claude"&&r>0&&r<vB?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function sh(e,t){return lc(e,t).state}function od(e,t){if(e!=="claude")return!1;let n=String(t||"").split(`
|
|
123
123
|
`).slice(-AB).join(`
|
|
124
|
-
`);return TB.test(n)}function ba(e,t){return sh(e,t)==="busy"}function uc(e,t){return sh(e,t)!=="idle"}function to(e,t){let n=rt[e];return n?.up?n.up.test(t):!0}function EB(e,t){let n=rt[e];return n?ya(e,t)?!1:n.up.test(t)&&sh(e,t)==="idle":!0}function ya(e,t){let n=rt[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
|
|
124
|
+
`);return TB.test(n)}function ba(e,t){return sh(e,t)==="busy"}function uc(e,t){return sh(e,t)!=="idle"}function to(e,t){let n=rt[e];return n?.up?n.up.test(t):!0}function EB(e,t){let n=rt[e];return n?ya(e,t)?!1:n.up.test(t)&&sh(e,t)==="idle":!0}function ya(e,t){let n=rt[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 rd(e,t){let n=String(t||"").trim();if(!n)return e;let r=String(e||"").split(`
|
|
125
125
|
`);for(let o=r.length-1;o>=0;o--)if(r[o].includes(n))return r.slice(o+1).join(`
|
|
126
|
-
`);return e}function
|
|
126
|
+
`);return e}function xv(e){let t=String(e||"").trim();return t?t.slice(0,32):null}function gv(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 yv(e,t,n,{bailOnDialog:r=!1,launchEcho:o=null,onReady:s}={}){let a=Date.now()+n,i=0,c=null;await Xo(e).catch(()=>{});let l=0;for(;;){let u=rd(await Ne(e),o);if(cc(t,u)){if(l<2&&Qu(e,{onDrive:!0})&&eo(e,"login-issue")){l+=1,ed(e),await ee({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"login-issue",attempt:l}).catch(()=>{}),await Qe(e,"/login",{enter:!0,submitDelayMs:600,kind:"login-issue"});let d=Date.now()+8e3,p=!1;for(;Date.now()<d;)if(await Ft(ts),ya(t,rd(await Ne(e),o))){p=!0;break}if(p&&r)return ee({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"login-issued"}).catch(()=>{}),"login-issued";continue}if(Date.now()>a){let d=`${t} isn't signed in on this box \u2014 answer the Sign-in card in chat (or run /login from the Terminal tab), then send again`;throw ee({flow:"agent-ready",phase:"blocked",reason:"not-logged-in",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:d}).catch(()=>{}),new Error(d)}await Ft(ts);continue}if((t==="claude"||t==="codex")&&i<3){let d=t==="codex"?hr(u):u;if(uv(t,d)){if(!eo(e,"login-enter")){await Ft(ts);continue}i+=1,td(e,d),await ee({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"login-enter",attempt:i}).catch(()=>{}),await Qe(e,"",{enter:!0,kind:"login-enter"}),await Ft(ns);continue}}if(EB(t,u))return await s?.(),await Ft(ns),"up";if(rt[t]?.up&&!rt[t].up.test(u))if(await Fn(e)){if(c??=Date.now(),Date.now()-c>=ec()){let d=gv(t);throw ee({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,idleMs:Date.now()-c,error:d.message}).catch(()=>{}),d}}else c=null;else c=null;if(r&&t==="claude"&&yB.test(u)&&!rt.claude.up.test(u))return ee({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"resume-confirm"}).catch(()=>{}),"dialog-bail";if(t==="codex"&&gB.test(u)&&!rt.codex.up.test(u)&&i<3){if(!eo(e,"codex-trust")){await Ft(ts);continue}i+=1,await ee({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"codex-trust",attempt:i}).catch(()=>{}),await Qe(e,"",{enter:!0,kind:"codex-trust-accept"}),await Ft(ns);continue}if(t==="cursor"&&wB.test(u)&&!rt.cursor.up.test(u)&&i<3){if(!eo(e,"cursor-trust")){await Ft(ts);continue}i+=1,await ee({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i}).catch(()=>{}),await Qe(e,"a",{enter:!1,kind:"cursor-trust-accept"}),await Ft(ns);continue}if(t==="codex"&&i<3){let d=kB(u);if(d){if(!eo(e,`codex-update:${d.from}->${d.to}`)){await Ft(ts);continue}i+=1,await ee({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i,update:`${d.from} -> ${d.to}`}).catch(()=>{}),await Qe(e,"3",{enter:!1,kind:"codex-update-skip"}),wm({sessionName:e,from:d.from,to:d.to}),await Ft(ns);continue}}if(Date.now()>a){if(ya(t,u)){let d=`${t} is blocked on an interactive prompt`;throw t==="codex"?d=rt.codex.auth.test(u)?"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"&&rt.cursor.auth.test(u)?d="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"&&rt.claude.auth.test(u)?d="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"&&rt.claude.block.test(u)&&(d=`claude is waiting on its "Press Enter to continue" screen (bostrat's Enter didn't clear it) \u2014 answer the Continue card in chat (or press Enter in the Terminal tab), then send again`),ee({flow:"agent-ready",phase:"blocked",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:d}).catch(()=>{}),new Error(d)}if(await Fn(e)){let d=gv(t);throw ee({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:d.message}).catch(()=>{}),d}return console.warn(`[agent-ready] ${t} readiness unconfirmed in ${e} after ${n}ms \u2014 driving the box anyway.`),ee({flow:"agent-ready",phase:"timeout",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,paneBlocked:!!rt[t]?.block?.test(u),paneUp:!!rt[t]?.up?.test(u),paneBusy:!!rt[t]?.busy?.test(kv(rt[t],u))}).catch(()=>{}),await Ft(ns),"timeout"}await Ft(ts)}}function dc({agent:e,agentCmd:t}={}){let n=t?Xr(t):Xr(e||"");return{label:n,realClaude:t?Yo(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 vv({agent:e,agentCmd:t}={}){let{realClaude:n,realCodex:r,realCursor:o}=dc({agent:e,agentCmd:t});return n||r||o||process.env.BOSTRAT_FAKE_POLL_READY==="1"}function rs({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o,onBlocked:s}={}){let{realCodex:a,realCursor:i}=dc({agent:n,agentCmd:r});return!t||!a&&!i?null:wr({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o}).then(()=>{},c=>{try{_B(c,{onBlocked:s,agent:n,sessionName:t})}catch{}})}function _B(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 wr({cwd:e,sessionId:t,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=pB(),bailOnDialog:a=!1,launchEcho:i=null,onReady:c}={}){let{label:l,realClaude:u,realCodex:d,realCursor:p}=dc({agent:r,agentCmd:o});if(u&&e&&await Wr(e),d&&e&&await Xs(e),p&&e&&await Qs(e),(u||d||p)&&n)try{return await yv(n,l,s,{bailOnDialog:a,launchEcho:i,onReady:c})}finally{d&&hm(n)}let f=rt[r]?r:rt[l]?l:null;return process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&f?await yv(n,f,s,{launchEcho:i}):(await Ft(ns),"settle")}var pB,ts,ns,fB,mB,rt,wB,gB,yB,bB,xB,nd,vB,bv,TB,AB,os=G(()=>{dt();$i();uu();wu();qo();Xu();yn();oh();pB=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,ts=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,ns=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,fB=/^\s{0,4}[^\s›•■◦╭╰│]\S*(?: \S+){0,3} · \S/,mB=/^\s*tab to queue message\b/;rt={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:hB},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/}},wB=/Trust this workspace|\[a\]\s*Trust/,gB=/Do you trust the contents of this directory\?[\s\S]*›\s*1\. Yes, continue[\s\S]*Press enter to continue/,yB=/Resume from summary/i,bB=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;xB=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,nd=new Map;vB=68,bv=/^\s*[─━]{10,}(?: [^─━\n]+ [─━]+)?\s*$/;TB=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,AB=15});function OB(e){return typeof e!="string"||!e?!1:e.includes(`
|
|
127
127
|
`)?!0:Buffer.byteLength(e,"utf8")>=RB}function Un(e,t){return e==="codex"?{enter:!0,submitDelayMs:IB,...OB(t)?{paste:!0}:{}}:e==="claude"?{enter:!0,submitDelayMs:$B}:{enter:!0}}var IB,$B,RB,pc=G(()=>{IB=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),$B=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),RB=(()=>{let e=Number(process.env.BOSTRAT_CODEX_PASTE_MIN_BYTES??512);return Number.isFinite(e)&&e>=0?e:512})()});function fc(e,t){if(e==="codex")return DB(t);if(e!=="claude")return null;let n=String(t).replace(/\r/g,"").split(`
|
|
128
|
-
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(
|
|
129
|
-
`).map(o=>o.replace(/\s+$/,"")),n=-1;for(let o=t.length-1;o>=0;o--)if(t[o]){(CB.test(t[o])||NB.test(t[o]))&&!ah.test(t[o])?n=o:ah.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(ah);if(a){r.unshift(a[1]);let i=r.map(c=>c.trim()).filter(Boolean);return i.length?i.join(" "):null}if(PB.test(s))return null;r.unshift(s)}return null}function ih(e,t){let n=String(t||"");if(!/\[Pasted Content \d+ chars\]/.test(n))return!1;let r=0,o=He(n.replace(BB,(i,c)=>(r+=Number(c)," "))),s=[...String(e||"")].length;return o&&!He(e).includes(o)?!1:r+o.length<=s+Math.max(4,Math.round(s*.05))}function He(e){return String(e||"").replace(/\s+/g," ").trim()}function
|
|
128
|
+
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(Sv.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(Sv.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(MB);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 DB(e){let t=String(e).replace(/\r/g,"").split(`
|
|
129
|
+
`).map(o=>o.replace(/\s+$/,"")),n=-1;for(let o=t.length-1;o>=0;o--)if(t[o]){(CB.test(t[o])||NB.test(t[o]))&&!ah.test(t[o])?n=o:ah.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(ah);if(a){r.unshift(a[1]);let i=r.map(c=>c.trim()).filter(Boolean);return i.length?i.join(" "):null}if(PB.test(s))return null;r.unshift(s)}return null}function ih(e,t){let n=String(t||"");if(!/\[Pasted Content \d+ chars\]/.test(n))return!1;let r=0,o=He(n.replace(BB,(i,c)=>(r+=Number(c)," "))),s=[...String(e||"")].length;return o&&!He(e).includes(o)?!1:r+o.length<=s+Math.max(4,Math.round(s*.05))}function He(e){return String(e||"").replace(/\s+/g," ").trim()}function Tv(e,t,{slackMs:n=2e4}={}){let r=He(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&&He(a.text).includes(r));return s>=0&&s<o.length-1}function Av(e,t,n,{busy:r=!1}={}){let o=fc(e,n);return o&&sd(t,o)||o&&e==="codex"&&ih(t,o)?"stuck":r&&e!=="codex"||o?"submitted":"unknown"}function sd(e,t){let n=He(e),r=He(t).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var Sv,MB,ah,CB,NB,PB,BB,mc=G(()=>{Sv=/^\s*─{8,}(?: [^─\n]+ ─+)?\s*$/,MB=/^❯[ ]?(.*)$/,ah=/^\s*›[ ]?(.*)$/,CB=/\S+ · \S/,NB=/^\s*(?:\? for shortcuts|tab to queue message)\b/,PB=/^\s*(?:[•■◦✶✻⚠]|╭|╰|│)/;BB=/\[Pasted Content (\d+) chars\](?: #\d+)?/g});async function wc({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=(P,j,A={})=>(a&&typeof a=="object"&&Object.assign(a,{verdict:P,retries:A.retries??0,ms:(i.now||Date.now)()-c,sawUp:!!A.sawUp,reason:A.reason??null}),j);if(!LB.has(t))return l("skipped",!0);let u=i.waitForUp??vv({agent:t,agentCmd:n}),d=i.capture||(P=>Ne(P,{lines:ze})),p=i.pressEnter||(P=>Ke(P,["Enter"],{kind:"submit-retry"})),f=i.sleep||HB,w=i.now||Date.now,h=i.log||(P=>ee(P).catch(()=>{})),y=i.maxMs??UB(),k=w()+y,g={flow:s,trackId:o,sessionName:e,agent:t,textHead:String(r).slice(0,120)},x=0,S=0,_=0,M=!1;for(await f(hc[0]);;){let P=await d(e).catch(()=>""),j=to(t,P),A=ba(t,P),$=Av(t,r,P,{busy:A});if($==="submitted")return l("submitted",!0,{retries:x,sawUp:!0});if(u&&(!j||$==="stuck"&&A)){if(w()>=k)return $==="stuck"?(h({...g,phase:"submit-failed",reason:"stuck-at-deadline",retries:x}),l("failed",!1,{retries:x,sawUp:M,reason:"stuck-at-deadline"})):(h({...g,phase:"submit-unverified",reason:M?"cli-down":"cli-never-up",retries:x,waitedMs:y}),l("unverified",!0,{retries:x,sawUp:M,reason:M?"cli-down":"cli-never-up"}));await f(jB);continue}if(M=!0,$==="stuck"){if(x>=FB)return h({...g,phase:"submit-failed",retries:x}),l("failed",!1,{retries:x,sawUp:!0,reason:"stuck"});x+=1,S=0,_=0,await p(e),h({...g,phase:"submit-retry",attempt:x}),await f(hc[0]);continue}if(S+=1,S>=hc.length||w()>=k)return l("assumed",!0,{retries:x,sawUp:!0});_+=1,await f(hc[Math.min(_,hc.length-1)])}}var LB,hc,jB,FB,UB,HB,ch=G(()=>{dt();os();mc();yn();LB=new Set(["claude","codex"]),hc=[250,450,700],jB=500,FB=2,UB=()=>{let e=Number(process.env.BOSTRAT_SUBMIT_VERIFY_MAX_MS);return Number.isFinite(e)&&e>0?e:3e4},HB=e=>new Promise(t=>setTimeout(t,e))});function Ev(e){return String(e||"").replace(/[\u2018\u2019\u201A\u02BC\u0060\u00B4]/g,"'").replace(/[\u201C\u201D\u201E]/g,'"')}function gr(e){let t=Ev(e).toLowerCase();if(!t.trim())return null;let n=null;for(let r of WB){let o=0;for(let s of qB[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function _v(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))GB.has(r)&&(t+=o||0),KB.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function gc(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 Iv(e){let t=Ev(e).trim();return t?zB.test(t)?!0:t.split(/\s+/).length<=VB:!1}var WB,qB,GB,KB,VB,zB,ad=G(()=>{WB=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],qB={research:[/\bresearch\b/,/\binvestigat/,/\bverif/,/\bcompar(e|ing|ison)/,/\bcompetit/,/\bdeep[ -]?dive\b/,/\banaly[sz]/,/\bassess/,/\bdiligence\b/,/\bmarket\b/,/\bstrateg/,/\broadmap\b/,/\bnorth star/,/\bwhat('| i)?s the (state|status)\b/,/\bstatus of\b/,/\bexplore\b/,/\bfeasib/,/\bevaluat/,/\bscope (it |this |that |them )?out\b/,/\btalk (me |us )?through\b/,/\b(give|show|tell) me\b/,/\bwhat about\b/,/\bwhy\b/,/\btake a look\b/,/\bwalk (me |us )?through\b/,/\bexplain\b/,/\bdo we (still |even )?need\b/,/\bis there (a|an|any)\b/,/\bhow often\b/,/\bdo we (currently|already)\b/,/\bwould it be (valuable|useful|worth)\b/,/\bis it possible\b/,/\bwhat are the odds\b/,/\bdetermine (what|which|whether|how|if)\b/,/\bwhat (does|do|would|could|should|will) .{1,60}\blook like\b/,/\bhow (is|are) (our|the|this|that|your|my) .{1,50}\b(going|doing|performing|trending)\b/,/\bhow (thorough|far along|complete|accurate|reliable|good) are we\b/],"bug-fix":[/\bfix(e[sd]|ing)?\b/,/\bbugs?\b/,/\bbroken\b/,/\bregression\b/,/\bcrash/,/\bincident\b/,/\bdebug/,/\bdoesn'?t (work|render|load|start)/,/\bnot working\b/,/\btroubleshoot/,/\bflaky?\b/,/\brepro(duce|duction)?\b/,/\brace condition\b/,/\bleak\b/,/\bkeeps? getting\b/,/\bdoesn'?t (seem|look) right\b/,/\bseems? (wrong|off|broken)\b/,/\b(too slow|slower than|much slower)\b/,/\bfail(ed|ing|s)\b/,/\b(another|one other|one more|next) (thing|issue|bug|problem) i (found|noticed|spotted|saw)\b/],"feature-build":[/\bimplement/,/\bbuild(ing)?\b/,/\badd(ing)?\b/,/\bcreate\b/,/\bwire (it |them )?(up|in)\b/,/\bship(ping)?\b/,/\bintegrat/,/\bsupport for\b/,/\bfeature\b/,/\bendpoint\b/,/\badapter\b/,/\bprototype\b/,/\bupdate (the |this |that |my |our )?[\w-]+( [\w-]+){0,3} to\b/,/\bset up\b/,/\b(can|could) we (also |maybe )?(tag|allow for|support|enable)\b/,/\bnow needs to be\b/,/\b(tie|hook|connect) (this|it|that|them) (in)?to\b/,/\bmake (a|an) (entirely |brand |whole |completely )?new\b/],design:[/\bdesign/,/\bui\b/,/\bux\b/,/\bstyl(e|es|ing)\b/,/\bcss\b/,/\blayout\b/,/\bmock-?up/,/\brender(ing|s)?\b/,/\bpolish/,/\btheme\b/,/\bfonts?\b/,/\bresponsive\b/,/\banimation/,/\bvisual/,/\bpopover\b/],orchestration:[/\bhand[ -]?(off|over)/,/\bhand (it|the lead|this) (over|off)\b/,/\bdelegat/,/\bsubtask/,/\bmerge[ -]?back\b/,/\borchestrat/,/\b(switch|change) (the )?model\b/,/\bmodel switch/,/\bpropose (the |a |these |those |new )?loops?\b/,/\bloop (to|that|which|for) (start |begin )?(check|watch|monitor|scan|sweep|poll|run)/],"docs-writing":[/\bdocs?\b/,/\bdocument/,/\bmemo\b/,/\breadme\b/,/\bwrite-?up\b/,/\bchangelog\b/,/\bclaude\.md\b/,/\bblog/,/\bproofread/,/\bcopy(edit|writ)/,/\b(linked[ -]?in|social( media)?) post\b/,/\b(framed|phrased|worded) as\b/,/\bwhat'?s a good (response|reply|answer)\b/,/\b(slides?|deck)\b/,/\btagline\b/,/\btitle (card|slide)\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/]};GB=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),KB=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);VB=12,zB=/^\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 Mv(e){let t=gc(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||JB.has(r)||n.push(r);return n}function YB(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(Ov(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(Ov(n,"assistant",o))}}return t}function Ov(e,t,n){let r=gr(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 yc(e){let t=YB(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=Mv(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 id(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(Mv(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,h]of d){let y=e.lengths[w],k=h+$v*(1-Rv+Rv*(y/(e.avgdl||1))),g=f*(h*($v+1)/k);c.set(w,(c.get(w)||0)+g)}}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 $v,Rv,JB,lh=G(()=>{ad();$v=1.5,Rv=.75,JB=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 cd from"node:fs/promises";import Cv from"node:path";async function XB(e){let t=Cv.join(e,".bostrat","events"),n;try{n=await cd.readdir(t)}catch{return yc([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await cd.readFile(Cv.join(t,o),"utf8");for(let a of s.split(`
|
|
130
130
|
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return yc(r)}function ZB(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}
|
|
131
|
-
${o}`.trim()||t.slice(0,2e3)}function QB(e,t,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=
|
|
132
|
-
`):""}async function
|
|
131
|
+
${o}`.trim()||t.slice(0,2e3)}function QB(e,t,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=id(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?["",`## ${Nv}, 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(`
|
|
132
|
+
`):""}async function Pv(e,t,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await cd.readFile(t,"utf8");if(a.includes(Nv))return{appended:!1,reason:"already-enriched"};let i=await XB(e);if(!i.N)return{appended:!1,reason:"empty-index"};let c=QB(i,ZB(a),{k:r,maxChars:o,exclude:s});return c?(await cd.appendFile(t,`
|
|
133
133
|
`+c),{appended:!0,chars:c.length,chunks:c.split(`
|
|
134
|
-
- `).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
|
|
135
|
-
`).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 cL(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return ka(t.split(/\n{2,}/).slice(0,2).join(" "))}function
|
|
134
|
+
- `).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 Nv,Dv=G(()=>{lh();Nv="Relevant prior discussion (verbatim, retrieved"});function sL(e){return eL.test(String(e||"").trimStart())}function jv(e){let t=String(e||"");if(!sL(t.trimStart()))return null;let n=t.replace(tL,"").trim(),r=aL(n),o=[];for(let u of rL){let d=u.from.map(p=>ka(r.get(p))).find(p=>p&&!Bv(p));d&&o.push({h:u.h,text:bc(d,nL)})}let s=ka(r.get("Current Work"))||ka(r.get("Primary Request and Intent"))||cL(n),a=ud(s),i=bc(ld(ph(a[0]||"")),oL)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(bc(ld(ph(l)),dh));for(let u of["Pending Tasks","Optional Next Step"]){let d=ud(ka(r.get(u)))[0]||"";if(!(!d||Bv(d))){c.push(bc(`Left to do: ${ld(ph(d))}`,dh));break}}if(!c.length){let u=ud(ka(r.get("Problem Solving"))||"")[0];u&&c.push(bc(ld(u),dh))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function aL(e){let t=new Map,n=[];uh.lastIndex=0;for(let r=uh.exec(e);r;r=uh.exec(e))n.push({name:iL(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 iL(e){return Lv.find(n=>n.toLowerCase()===e.toLowerCase())||e}function ka(e){return e?String(e).replace(/```[\s\S]*?(?:```|$)/g," ").split(`
|
|
135
|
+
`).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 cL(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return ka(t.split(/\n{2,}/).slice(0,2).join(" "))}function ud(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 ph(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 ld(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function Bv(e){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(e||"").trim())}function bc(e,t){let n=String(e||"").trim();if(n.length<=t)return n;let r=ud(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 eL,Lv,uh,tL,nL,rL,oL,dh,Fv=G(()=>{eL=/^This session is being continued from a previous conversation that ran out of context\./,Lv=["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"],uh=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${Lv.join("|")})[:*\t ]*$`,"gim"),tL=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,nL=420,rL=[{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"]}],oL=116,dh=168});function Wv(e){return fh.has(String(e||"").replace(/^\//,"").toLowerCase())}function kc(e){let t=String(e||"").trim();if(!t)return null;let n=jv(t);if(n)return n;let r=t.match(lL);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(uL);if(o)return{kind:"skill",name:o[1]};let s=t.match(dL);if(s)return{kind:"skill",name:s[1].toLowerCase()};let a=t.match(pL);if(a){let c=a[1].toLowerCase();return fh.has(c)?{kind:"suppress"}:{kind:"command",command:`/${c}`}}let i=t.match(vL);if(i&&fh.has(i[1].toLowerCase()))return{kind:"suppress"};if(fL.test(t))return{kind:"suppress"};if(hL.test(t))return{kind:"suppress"};if(mL.test(t))return{kind:"suppress"};if(wL.test(t)){let c=u=>EL(t.match(u)?.[1].trim()||"")||null,l=c(xL);return{kind:"task",summary:c(gL)||"",taskId:c(yL),toolUseId:c(bL),status:c(kL)?.toLowerCase()||null,resultHead:l?l.length>Uv?`${l.slice(0,Uv).trimEnd()}\u2026`:l:null}}return null}function qv(e){let t=kc(e?.text);return t||(e?.injected?{kind:"skill",name:e.injected.skill||"skill"}:null)}function Gv(e){let t=String(e||""),n=t.match(SL);if(n)return{kind:"shell-input",command:n[1].trim()};let r=t.match(TL);return r?{kind:"shell-output",stdout:Hv(r[1]),stderr:Hv(r[2])}:null}function EL(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function Kv(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 Vv(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var lL,uL,dL,pL,fL,mL,hL,wL,gL,yL,bL,kL,xL,Uv,fh,vL,SL,TL,AL,Hv,dd=G(()=>{Fv();lL=/^Base directory for this skill:\s*(\S+)/,uL=/^Run the "([^"]+)" workflow\./,dL=/^#\s*\/([a-z0-9:_-]+)\s*[—–:-]/i,pL=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,fL=/^<local-command-(?:stdout|caveat)>/i,mL=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,hL=/^Continue from where you left off\.$/,wL=/^<task-notification>/i,gL=/<summary>([\s\S]*?)<\/summary>/i,yL=/<task-id>([\s\S]*?)<\/task-id>/i,bL=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,kL=/<status>([\s\S]*?)<\/status>/i,xL=/<result>([\s\S]*?)(?:<\/result>|$)/i,Uv=600,fh=new Set(["model","clear","compact","login","exit","quit"]);vL=/^\/([a-z0-9:_-]+)$/i;SL=/^\s*<bash-input>([\s\S]*?)<\/bash-input>\s*$/i,TL=/^\s*<bash-stdout>([\s\S]*?)<\/bash-stdout>\s*(?:<bash-stderr>([\s\S]*?)<\/bash-stderr>)?\s*$/i,AL=/\x1b\[[0-9;?]*[ -/]*[@-~]/g,Hv=e=>String(e||"").replace(AL,"").replace(/^\n+/,"").replace(/\s+$/,"")});import _L from"node:crypto";function OL(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,Jv)){let o=xa(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,IL)){let c=xa(i?.label);if(!c)return null;let l=xa(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<$L)return null;n.push({question:o,header:xa(r?.header).slice(0,RL),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function mh(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;zv.lastIndex=0;let t=null;for(let o;o=zv.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=OL(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function hh(e,t,n){let r=_L.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${e}:ask:${t||"turn"}:${r}`}function wh(e,{leadBoxId:t,provider:n,isAnswered:r,maxQuestions:o=Jv}={}){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||mh(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=hh(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 Yv(e,t,n,r){let o=[];(e?.questions||[]).forEach((i,c)=>{let l=(t?.[c]||[]).map(d=>i.options?.[d]?.label).filter(Boolean),u=xa(Array.isArray(r)?r[c]:"");u&&l.push(u),l.length&&o.push({name:i.header||i.question,picks:l.join(", ")})});let s=xa(n),a;return o.length?o.length===1?a=`${o[0].name}: ${o[0].picks}`:a=`Answers:
|
|
136
136
|
${o.map(i=>`- ${i.name}: ${i.picks}`).join(`
|
|
137
137
|
`)}`:a=s,o.length&&s&&(a+=`
|
|
138
|
-
${s}`),a||""}var
|
|
138
|
+
${s}`),a||""}var Jv,IL,$L,RL,zv,xa,Xv,gh=G(()=>{Jv=4,IL=4,$L=2,RL=24,zv=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,xa=e=>typeof e=="string"?e.trim():"";Xv=`
|
|
139
139
|
When you are BLOCKED on a decision only the operator can make \u2014 an ambiguous
|
|
140
140
|
requirement, an irreversible choice, several valid approaches \u2014 do not bury the
|
|
141
141
|
question in prose. End your message with exactly ONE fenced code block tagged
|
|
@@ -156,91 +156,91 @@ your turn. The operator sees the questions as an interactive card in their chat
|
|
|
156
156
|
They may instead reply in free text \u2014 treat that as the answer. Never use the block for rhetorical questions, progress updates, or
|
|
157
157
|
anything you can decide yourself; keep asking in prose for questions without
|
|
158
158
|
enumerable options.
|
|
159
|
-
`});function ML(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 Xv(e,t,n){let r=yh(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 Hn(e,{now:t=Date.now()}={}){let n=[],r=new Set,o=new Set,s=new Map,a=new Map,i=new Set,c=new Map,l=new Map,u=new Map,d=(b,m)=>{m!=null&&(u.has(b)||u.set(b,[]),u.get(b).push(m))},p=(b,m)=>{let v=null;for(let D of[b,null])for(let J of u.get(D)||[])J>m&&(v==null||J<v)&&(v=J);return v},f=new Set,w=new Map,h=new Map,y=(b,m)=>{h.set(b,m),n.push(m)},k=b=>{let m=h.get(b);m&&(m.retired=!0)},g=/^Agent "([\s\S]+)" finished$/,x=new Map;for(let b of e||[]){if(b.type!=="tool.called"||b.source!=="transcript")continue;let m=String(b.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 D=v[0].split("/").pop().toLowerCase();x.has(D)||x.set(D,[]),x.get(D).push(b.payload?.ts??b.ts)}}let S=3*6e4,_=20*6e4,M=(b,m)=>!!(b?.name&&x.get(String(b.name).toLowerCase()))?.some(D=>D>=m-S&&D<=m+_);for(let b of e||[])if(b.type==="turn.retracted"){o.add(`${b.boxId}|${b.payload?.saidTs}`);let m=He(b.payload?.text);m&&(s.has(b.boxId)||s.set(b.boxId,[]),s.get(b.boxId).push(m))}else if(b.type==="turn.user"&&b.source==="orchestrator"){let m=He(b.payload?.text);m&&(a.has(b.boxId)||a.set(b.boxId,new Set),a.get(b.boxId).add(m))}else if(b.type==="job.stalled"&&b.payload?.id)c.set(b.payload.id,{ts:pe(b),quietMs:Number(b.payload.quietMs)||null});else if(b.type==="job.stopped"&&b.payload?.id)l.has(b.payload.id)||l.set(b.payload.id,{status:"killed",summary:null,resultHead:null,ts:pe(b)});else if(b.type==="session.ready"){let m=b.payload?.source;m!=="compact"&&m!=="clear"&&d(b.boxId??null,pe(b))}else if(b.type==="handoff.done")d(b.boxId??b.payload?.from??null,pe(b));else if(b.type==="box.state.changed"&&b.payload?.change==="archived")d(null,pe(b));else if(b.type==="job.started"&&b.payload?.id)i.add(b.payload.id),(b.payload.kind||"agent")==="agent"&&b.payload.name&&f.add(b.payload.name);else if(b.type==="turn.user"&&b.payload?.note?.kind==="task"){let m=b.payload.note,v={status:m.status||null,summary:m.summary||null,resultHead:m.resultHead??null,ts:pe(b)};m.toolUseId&&l.set(m.toolUseId,v);let D=String(m.summary||"").match(g);D&&w.set(D[1],v)}let P=new Map,j=new Map,A=null,$=new Map,L=new Map,H=new Map,R=new Map,T=new Map,C=new Map,W=new Map,B=new Map,Z=new Map,xe=new Map,Te=new Map,_e=new Map,Q=new Map,Xe=new Map,Pe=new Map,le=new Map,ge=(b,m)=>{b.arm.status=m;let v=b.line,D=v.arms.map(J=>J.status);v.status=D.includes("armed")?"armed":D.includes("offered")?"offered":D.includes("replaced")?"replaced":"cancelled"},de=[],te=new Set,we=new Map,Oe=(b,m)=>{we.delete(b);let v=n.indexOf(b);v>=0&&n.splice(v,1),m!=null&&(b.ts=m),n.push(b)},Ve=b=>{if(de.push(b),!(b.box?te.has(b.box):te.size>0))return;let v=n.indexOf(b);v>=0&&n.splice(v,1),we.set(b,"held")},Nt=b=>{if(!de.length)return;let m=pe(b);for(let v of de)v.status!=="open"||we.get(v)==="superseded"||v.box&&v.box!==b.boxId||(v.ts??0)>m||Oe(v,m)},O=b=>{if(!de.length)return;let m=pe(b);for(let v of de)we.get(v)!=="held"||v.status!=="open"||v.box&&b.boxId&&v.box!==b.boxId||Oe(v,Math.max(v.ts??0,m))},X=()=>{for(let[b,m]of[...we])m==="held"&&Oe(b,null);de.length=0,re.clear()},re=new Set,he=b=>{for(let m of re){if(m.status!=="open"||m.box&&b&&m.box!==b)continue;m.status="superseded";let v=n.indexOf(m);v>=0&&n.splice(v,1),we.set(m,"superseded"),$.delete(m.offerId)}re.clear()},Be=new Map,Pt=new Map,tt=new Map,wn=new Map,Kt=new Map,ht=new Map,wt=new Map,at=new Map,Ai=()=>{for(let b of at.values())b.questions.length&&n.push(b);at.clear()},gt=null,cr=new Map,Hr=6e4,lr=new Map,gn=(b,m)=>{let v=s.get(b);if(!v?.length)return!1;let D=He(m),J=a.get(b);return v.some(ie=>{if(!D.startsWith(ie))return!1;let ve=He(D.slice(ie.length));return!!ve&&!!J?.has(ve)})};for(let b of e||[]){let m=b.payload||{};switch(b.type){case"turn.user":{if(b.source==="hook"){b.boxId&&te.add(b.boxId);break}if(X(),!String(m.text||"").trim()&&!m.images?.length&&!m.attachments?.length)break;{let v=Wv(m.text);if(v){if(v.kind==="shell-input"){let D={id:b.id,kind:"shell",command:v.command,stdout:null,stderr:null,done:!1,ts:pe(b),box:b.boxId,agent:b.provider};n.push(D),cr.set(b.boxId,D)}else{let D=cr.get(b.boxId);D?(D.stdout=v.stdout||null,D.stderr=v.stderr||null,D.done=!0,cr.delete(b.boxId)):n.push({id:b.id,kind:"shell",command:null,stdout:v.stdout||null,stderr:v.stderr||null,done:!0,ts:pe(b),box:b.boxId,agent:b.provider})}break}}{let v=m.note||kc(m.text);if(v){if(v.kind==="task"){if(v.toolUseId&&i.has(v.toolUseId))break;let D=String(v.summary||"").match(g);if(!v.toolUseId&&D&&f.has(D[1]))break}if(v.kind==="command"&&Uv(v.command))break;if(v.kind==="resume-summary"){n.push({id:b.id,kind:"resume-summary",title:v.title||"Context resumed from summary",bullets:v.bullets||[],sections:v.sections||[],rawChars:v.rawChars??null,ts:pe(b),box:b.boxId,agent:b.provider});break}v.kind!=="suppress"&&n.push({id:b.id,kind:"event",type:Gv(v),name:v.name??null,command:v.command??null,summary:v.summary??null,status:v.status??null,ts:pe(b),box:b.boxId,agent:b.provider});break}}if(o.has(`${b.boxId}|${pe(b)}`)||b.source==="transcript"&&gn(b.boxId,m.text))break;if(m.kind==="merge-notify"||m.kind==="ship-notify"){n.push({id:b.id,kind:"event",type:m.kind==="ship-notify"?"ship-notified":"merge-notified",text:m.text,ts:pe(b),box:b.boxId,agent:b.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==="login-greet"){m.kind==="prompt-answer"&&(gt=null);break}for(let v of $.values())v.status="stale";$.clear();for(let v of H.values())v.status="stale";H.clear();for(let v of Q.values())v.rows.some(D=>D.rowStatus==="sent")||(v.status="stale",Q.delete(v.offerId));gt&>.box===b.boxId&&(n.push(ML(gt)),gt=null),n.push({id:b.id,kind:"message",role:"user",text:m.text,ts:pe(b),box:b.boxId,agent:b.provider,boxRole:m.boxRole,...b.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||{};n.push({id:b.id,kind:"event",type:"peer-message",text:m.text,from:v.trackName||v.boxId||v.name||null,fromTrackId:v.trackId||null,ts:pe(b),box:b.boxId,agent:b.provider});break}case"turn.agent":{if(m.model==="<synthetic>"&&/^No response requested\.$/.test(String(m.text||"").trim()))break;let v=Xx(m,{codexFailedText:Qo});if(v){n.push({id:b.id,kind:"event",type:"provider-error",cls:v.cls,status:v.status??void 0,text:v.text,provider:b.provider,legacy:!0,at:pe(b),ts:pe(b),box:b.boxId,agent:b.provider});break}Ai();let D=(m.images||[]).filter(yt=>!M(yt,pe(b)));if(b.source==="hook"&&!String(m.text||"").trim()&&(O(b),b.boxId&&te.delete(b.boxId)),!String(m.text||"").trim()&&!D.length)break;if(b.provider==="cursor"){let yt=`${b.boxId}|${m.text}`;if(r.has(yt))break;r.add(yt)}(!gt||gt.box===b.boxId)&&(gt=null);let J=b.provider==="codex"?(m.tools||[]).filter(yt=>!th.has(yt)&&!sc.has(yt)):m.tools||[],ie=m.text,ve=null,_t=mh(String(m.text||""));_t&&(ie=_t.text,ve={questions:_t.questions}),n.push({id:b.id,kind:"message",role:"assistant",text:ie,ts:pe(b),box:b.boxId,agent:b.provider,boxRole:m.boxRole,...ve?{ask:ve}:{},...J.length?{tools:J}:{},...m.model?{model:m.model}:{},...m.sourceId?{sourceId:m.sourceId}:{},...D.length?{images:D}:{}}),Nt(b),ve&&(gt={id:b.id,groupId:hh(b.provider,m.sourceId||b.id,ve.questions),provider:b.provider,box:b.boxId,questions:ve.questions,ts:pe(b)});break}case"job.started":{if(!m.id)break;let v=pe(b),D=l.get(m.id);if(!D&&(m.kind||"agent")==="agent"&&m.name){let rn=w.get(m.name);rn&&rn.ts>=v&&(D=rn)}let J=D?null:c.get(m.id)||null,ie=D||J?null:p(b.boxId??null,v),ve={jobId:m.id,jobKind:m.kind||"agent",name:m.name||null,detail:m.detail||null,status:D?D.status==="killed"?"stopped":D.status&&D.status!=="completed"?"failed":"done":J?"stalled":ie!=null?"lost":"running",startedTs:v,finishedTs:D?.ts??J?.ts??ie??null,quietMs:J?.quietMs??null,summary:D?.summary??null,resultHead:D?.resultHead??null},_t=lr.get(b.boxId),yt=_t?.jobs[_t.jobs.length-1]?.startedTs;if(_t&&yt!=null&&v-yt<=Hr)_t.jobs.push(ve);else{let rn={id:b.id,kind:"job-dock",dockId:m.id,ts:v,box:b.boxId,agent:b.provider,jobs:[ve]};lr.set(b.boxId,rn),n.push(rn)}break}case"handoff.done":n.push(bt(b,"lead-handoff"));break;case"subtask.delegated":n.push({...bt(b,"subtask-delegated"),to:b.boxId,toAgent:b.provider});break;case"subtask.merged":n.push(bt(b,"subtask-merged"));break;case"pr.state.changed":{if(!m.change)break;if(m.change==="opened"){for(let D of $.values())D.status="stale";$.clear();for(let D of H.values())D.status="stale";H.clear()}let v=bt(b,`pr-${m.change}`);if(v.box=b.boxId??null,Yv.has(m.change)&&m.number!=null){let D=Xv(P,j,m);if(P.size>=2){for(let ie of j.values())for(let ve of ie)ve.grouped=!0;v.groupPrs=[...P.values()];let J=[v];for(let ie of P.keys())j.set(ie,J)}else{let J=j.get(D)??[];J.push(v),j.set(D,J)}}else if(m.change==="merged"||m.change==="closed"||m.change==="lost"){let D=yh(m),J=A&&!A.complete||P.size>=2;if(P.has(D))P.delete(D),j.delete(D);else if(m.number!=null)for(let ie of[...P.keys()])ie.endsWith(`#${m.number}`)&&(P.delete(ie),j.delete(ie));if(J&&m.number!=null){(!A||A.complete)&&(A={id:b.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:pe(b),ts:pe(b),box:b.boxId,agent:b.provider},n.push(A)),A.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:pe(b)}),A.remaining=[...P.values()],A.complete=P.size===0;break}}n.push(v);break}case"pr.offered":{if(!m.offerId)break;he(b.boxId);for(let D of $.values())D.status="stale";$.clear();for(let D of H.values())D.status="stale";H.clear();let v={id:b.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:b.source==="cli"||m.via==="cli"?"cli":"transcript",ts:pe(b),box:b.boxId,agent:b.provider};$.set(m.offerId,v),L.set(m.offerId,v),n.push(v),re.add(v),v.source==="cli"&&Ve(v);break}case"pr.offer.answered":{let v=L.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}),$.delete(m.offerId));break}case"pr.exists.offered":{if(!m.offerId)break;for(let D of H.values())D.status="stale";H.clear();let v={id:b.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:pe(b),box:b.boxId,agent:b.provider};H.set(m.offerId,v),R.set(m.offerId,v),n.push(v);break}case"pr.exists.answered":{let v=R.get(m.offerId);v&&(v.status=m.choice||"dismiss",H.delete(m.offerId));break}case"box.state.changed":{let v=m.change==="model"?"model-change":m.change;if(!v||v==="cli-update"&&m.status!=="failed")break;let D=bt(b,v);if(v==="model-change"&&(D.to=m.model),v==="node-update"&&D.latest){let J=n.findIndex(ie=>ie.kind==="event"&&ie.type==="node-update"&&ie.latest===D.latest);if(J>=0){let ie=n[J];n[J]={...D,ts:ie.ts,at:ie.at,firstAt:ie.firstAt??ie.ts,updatedAt:D.ts};break}}n.push(D);break}case"track.transferred":case"track.recovered":n.push(bt(b,"track-moved"));break;case"view.state.changed":{if(m.change==="deleted"){k(`view:${m.viewId}`);break}if(m.change!=="created"||m.restarted)break;y(`view:${m.viewId}`,{id:b.id,kind:"view-card",viewId:m.viewId,name:m.name,slug:m.slug,port:m.port,origin:m.origin,ts:pe(b),box:b.boxId,agent:b.provider});break}case"view.spinup.failed":{n.push({id:b.id,kind:"view-spinup-failed",name:m.name||null,reason:m.reason||"no-view",ts:pe(b),box:b.boxId,agent:b.provider});break}case"mock.state.changed":{if(m.change==="picked"){n.push({id:b.id,kind:"mock-picked",mockId:m.mockId,name:m.name,group:m.group??null,version:m.version??null,siblings:m.siblings??[],ts:pe(b),box:b.boxId,agent:b.provider});break}if(m.change==="deleted"){k(`mock:${m.mockId}`);break}if(m.change!=="created")break;y(`mock:${m.mockId}`,{id:b.id,kind:"mock-card",mockId:m.mockId,name:m.name,group:m.group??null,ts:pe(b),box:b.boxId,agent:b.provider});break}case"flow.state.changed":{if(m.change==="deleted"){k(`flow:${m.flowId}`);break}if(m.change!=="created")break;y(`flow:${m.flowId}`,{id:b.id,kind:"flow-card",flowId:m.flowId,name:m.name,stepCount:m.stepCount??null,mockIds:m.mockIds??[],ts:pe(b),box:b.boxId,agent:b.provider});break}case"runbook.state.changed":{if(m.change==="deleted"){k(`runbook:${m.runbookId}`);break}if(m.change!=="created")break;y(`runbook:${m.runbookId}`,{id:b.id,kind:"runbook-card",runbookId:m.runbookId,name:m.name,ts:pe(b),box:b.boxId,agent:b.provider});break}case"ticket.linked":{n.push({id:b.id,kind:"ticket-card",provider:m.provider||"linear",key:m.key,url:m.url,title:m.title??null,linkSource:m.linkSource??null,ts:pe(b),box:b.boxId,agent:b.provider});break}case"box.fullscreen.detected":{if(!m.offerId)break;for(let D of W.values())D.box===(m.box||b.boxId)&&(D.status="stale",W.delete(D.offerId));let v={id:b.id,kind:"fullscreen-offer",offerId:m.offerId,status:"open",ts:pe(b),box:m.box||b.boxId,agent:m.agent||b.provider};W.set(m.offerId,v),B.set(m.offerId,v),n.push(v);break}case"box.fullscreen.answered":{let v=B.get(m.offerId);v&&(v.status=m.action==="restarted"?"restarted":m.action==="cleared"?"cleared":"dismiss",W.delete(m.offerId));break}case"ticket.move.offered":{if(!m.offerId)break;for(let D of T.values())D.key===m.key&&(D.status="stale",T.delete(D.offerId));let v={id:b.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:pe(b),box:b.boxId,agent:b.provider};T.set(m.offerId,v),C.set(m.offerId,v),n.push(v);break}case"ticket.move.answered":{let v=C.get(m.offerId);v&&(v.status=m.action==="moved"?"moved":"dismiss",T.delete(m.offerId));break}case"ticket.state.changed":{for(let v of T.values())v.key===m.key&&v.to===m.to&&(v.status="stale",T.delete(v.offerId));if(m.mode==="offer")break;n.push(bt(b,"ticket-state"));break}case"ticket.commented":{n.push({id:b.id,kind:"ticket-comment",key:m.key,url:m.url??null,excerpt:m.excerpt??null,ts:pe(b),box:b.boxId,agent:b.provider});break}case"ticket.flow.changed":n.push(bt(b,"ticket-flow"));break;case"ticket.create.offered":{if(!m.proposalId)break;for(let D of Z.values())D.status="stale";Z.clear();let v={id:b.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:pe(b),box:b.boxId,agent:b.provider};Z.set(m.proposalId,v),xe.set(m.proposalId,v),n.push(v),Ve(v);break}case"ticket.create.answered":{let v=xe.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",v.createdCount=m.createdCount??null,v.failedCount=m.failedCount??null,Z.delete(m.proposalId));break}case"loop.create.offered":{if(!m.proposalId)break;for(let D of Te.values())D.status="stale";Te.clear();let v={id:b.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:pe(b),box:b.boxId,agent:b.provider};Te.set(m.proposalId,v),_e.set(m.proposalId,v),n.push(v),Ve(v);break}case"loop.create.answered":{let v=_e.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",Te.delete(m.proposalId));break}case"followup.armed":{if(!m.armId)break;let v=m.replaces?Pe.get(m.replaces):null;v&&v.arm.status==="armed"&&ge(v,"replaced");let D={armId:m.armId,seq:Number.isInteger(m.seq)?m.seq:0,label:m.label??"",prompt:m.prompt??"",why:m.why??null,standing:m.standing==="never"?"never":"offerable",status:"armed"},J=m.groupId??null,ie=J?le.get(J):null;ie||(ie={id:b.id,kind:"followup-armed",groupId:J,armId:D.armId,trigger:m.trigger??"now",label:D.label,prompt:D.prompt,why:D.why,planWhy:m.planWhy??null,arms:[],status:"armed",ts:pe(b),box:b.boxId,agent:b.provider},J&&le.set(J,ie),ie.trigger!=="now"&&n.push(ie)),ie.arms.push(D),Pe.set(m.armId,{arm:D,line:ie});break}case"followup.armed.cancelled":{let v=Pe.get(m.armId);v&&v.arm.status==="armed"&&ge(v,"cancelled");break}case"followup.offered":{if(!m.offerId)break;for(let J of Q.values())J.trigger===m.trigger&&(J.status="stale",Q.delete(J.offerId));let v=Array.isArray(m.rows)?m.rows:[];for(let J of v){let ie=J.armId?Pe.get(J.armId):null;ie&&ie.arm.status==="armed"&&ge(ie,"offered")}let D={id:b.id,kind:"followup-offer",offerId:m.offerId,trigger:m.trigger??"now",repo:m.repo??null,context:m.context&&typeof m.context=="object"?m.context:{},groupId:m.groupId??null,why:m.why??null,rows:v.map(J=>({followupId:J.followupId??null,armId:J.armId??null,label:J.label??"",prompt:J.prompt??null,promptPreview:J.promptPreview??null,promptHash:J.promptHash??null,origin:J.origin??"operator",why:J.why??null,groupId:J.groupId??null,seq:Number.isInteger(J.seq)?J.seq:0,standing:J.standing==="never"?"never":"offerable",promotable:J.armId?J.promotable!==!1:!1,promotableReason:J.promotableReason??null,rowStatus:"open"})),status:"open",answer:null,answers:[],ts:pe(b),box:b.boxId,agent:b.provider};Q.set(m.offerId,D),Xe.set(m.offerId,D),n.push(D),D.trigger==="now"&&Ve(D);break}case"followup.answered":{let v=Xe.get(m.offerId);if(!v)break;let D=new Set;m.armId&&D.add(`arm:${m.armId}`),m.followupId&&D.add(`fu:${m.followupId}`);for(let ve of Array.isArray(m.armIds)?m.armIds:[])D.add(`arm:${ve}`);for(let ve of Array.isArray(m.followupIds)?m.followupIds:[])D.add(`fu:${ve}`);let J=m.action==="sent"?"sent":m.action==="removed"?"removed":"dismiss";for(let ve of v.rows){if(ve.rowStatus!=="open")continue;let _t=ve.armId?`arm:${ve.armId}`:`fu:${ve.followupId}`;D.size&&!D.has(_t)||(ve.rowStatus=J)}let ie={followupId:m.followupId??null,armId:m.armId??null,label:m.label??null,promoted:m.promoted??null,revived:m.revived===!0,action:J,batchId:m.batchId??null};v.answers.push(ie),v.answer=ie,v.rows.some(ve=>ve.rowStatus==="open")?v.status="open":(v.status=v.rows.some(ve=>ve.rowStatus==="sent")?"sent":v.rows.some(ve=>ve.rowStatus==="removed")?"removed":"dismiss",Q.delete(m.offerId));break}case"followup.create.offered":{if(!m.proposalId)break;for(let D of Be.values())D.status="stale";Be.clear();let v={id:b.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:pe(b),box:b.boxId,agent:b.provider};Be.set(m.proposalId,v),Pt.set(m.proposalId,v),n.push(v),Ve(v);break}case"followup.create.answered":{let v=Pt.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",Be.delete(m.proposalId));break}case"followup.created":{let v=m.proposalId?Pt.get(m.proposalId):null;v&&(v.created={followupId:m.followupId??null});break}case"loop.created":{let v=m.proposalId?_e.get(m.proposalId):null;v&&(v.created={loopId:m.loopId??null,nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),n.push(bt(b,"loop-created"));break}case"loop.update.offered":case"loop.delete.offered":{if(!m.proposalId||!m.loopId)break;for(let J of tt.values())J.loopId===m.loopId&&(J.status="stale",J.staleReason="superseded",tt.delete(J.proposalId));let v=b.type==="loop.update.offered",D={id:b.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:pe(b),box:b.boxId,agent:b.provider};tt.set(m.proposalId,D),wn.set(m.proposalId,D),n.push(D),Ve(D);break}case"loop.update.answered":{let v=wn.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="applied"?"applied":"dismiss",tt.delete(m.proposalId));break}case"loop.delete.answered":{let v=wn.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="delete"?"deleted":m.action==="pause"?"paused":"keep",tt.delete(m.proposalId));break}case"loop.updated":{let v=m.proposalId?wn.get(m.proposalId):null;v&&(v.applied={changed:Array.isArray(m.changed)?m.changed:[],nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),n.push(bt(b,"loop-updated"));break}case"loop.deleted":n.push(bt(b,"loop-deleted"));break;case"loop.paused":n.push(bt(b,"loop-paused"));break;case"planning.change.offered":{if(!m.proposalId||!Array.isArray(m.ops)||!m.ops.length)break;for(let D of Kt.values())D.status="stale",D.staleReason="superseded",Kt.delete(D.proposalId);let v={id:b.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:pe(b),box:b.boxId,agent:b.provider};Kt.set(m.proposalId,v),ht.set(m.proposalId,v),n.push(v),Ve(v);break}case"planning.change.answered":{let v=ht.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="applied"?"applied":"dismiss",Kt.delete(m.proposalId));break}case"planning.changed":{let v=m.proposalId?ht.get(m.proposalId):null;v&&(v.applied={results:Array.isArray(m.results)?m.results:[]}),n.push(bt(b,"planning-changed"));break}case"rework.cause.offered":{if(!m.sha||wt.has(m.sha))break;let v={id:b.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:pe(b),box:b.boxId,agent:b.provider};wt.set(m.sha,v),n.push(v);break}case"rework.cause.answered":{let v=m.sha?wt.get(m.sha):null;v&&(v.status="answered",v.cause=m.cause??null);break}case"rework.cause.retracted":{let v=m.sha?wt.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},D=m.proposalId?xe.get(m.proposalId):null;if(D){D.created=v;break}n.push({id:b.id,kind:"ticket-created",...v,ts:pe(b),box:b.boxId,agent:b.provider});break}case"error":{if(!m.kind)break;n.push(bt(b,m.kind));break}case"provider.error":n.push({...bt(b,"provider-error"),provider:b.provider,box:b.boxId,agent:b.provider});break;case"usage.cap.hit":n.push(bt(b,"usage-cap-hit"));break;case"usage.cap.warning":n.push(bt(b,"usage-cap-warning"));break;case"provider.connected":{n.push({id:b.id,kind:"event",type:"provider-connected",provider:m.provider||b.provider,account:m.account||null,reason:m.reason||null,ts:pe(b),at:pe(b),box:b.boxId,agent:b.provider});break}case"provider.login.started":break;case"prompt.answered":{if(gt=null,m.control||!m.question)break;let v=m.groupId||m.promptId;if(!v)break;let D=at.get(v);D||(D={id:b.id,kind:"prompt",groupId:v,provider:b.provider,box:b.boxId,agent:b.provider,questionTotal:m.questionTotal??1,questions:[],ts:pe(b)},at.set(v,D));let J=m.questionIndex??0,ie={index:J,header:m.header||"",question:m.question,multiSelect:!!m.multiSelect,chosen:Array.isArray(m.chosen)?m.chosen:[]},ve=D.questions.findIndex(yt=>yt.index===J&&He(yt.question)===He(ie.question));ve>=0?D.questions[ve]=ie:D.questions.push(ie),D.questions.sort((yt,nt)=>yt.index-nt.index),D.questionTotal=Math.max(D.questionTotal,m.questionTotal??1),D.questions.length>=D.questionTotal&&(n.push(D),at.delete(v));break}case"prompt.dismissed":{gt=null;let v=m.groupId||m.promptId;if(!v||!m.question)break;let J=at.get(v)||{id:b.id,kind:"prompt",groupId:v,provider:b.provider,box:b.boxId,agent:b.provider,questionTotal:m.questionTotal??1,questions:[],ts:pe(b)},ie=m.questionIndex??0;J.questions.some(ve=>ve.index===ie&&He(ve.question)===He(m.question))||(J.questions.push({index:ie,header:m.header||"",question:m.question,multiSelect:!!m.multiSelect,chosen:[]}),J.questions.sort((ve,_t)=>ve.index-_t.index)),J.answeredVia="chat",n.push(J),at.delete(v);break}default:break}}for(let[b,m]of[...we]){if(m==="superseded"){b.status!=="superseded"&&Oe(b,null);continue}(b.status!=="open"||t-(b.ts??0)>=CL)&&Oe(b,null)}let ur=h.size?n.filter(b=>!b.retired):n;return PL(ur.sort((b,m)=>(b.ts??0)-(m.ts??0)))}function PL(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<=NL){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 bh(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:DL(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??Hn(e)).filter(o=>o.kind==="message");return Wx(n,r)}function DL(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==ha(e.command)?t:Qr(e.command,e.description||"")}function bt(e,t){let{ts:n,change:r,kind:o,...s}=e.payload||{};return{id:e.id,kind:"event",type:t,...s,at:pe(e),ts:pe(e)}}function Zv(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)+BL,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&LL.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function Qv(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!FL.has(r.type)||!r.box)continue;let o=(r.ts??0)+jL,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 dd(e){return(e||[]).filter(t=>UL.has(t?.kind)&&t.status==="open")}function eS(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(Yv.has(o.change)&&o.number!=null)Xv(t,n,o);else if(o.change==="merged"||o.change==="closed"||o.change==="lost"){let s=yh(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 pe,Yv,yh,CL,NL,BL,LL,jL,FL,UL,pd=G(()=>{wa();es();Vu();mc();ud();gh();pe=e=>e.payload?.ts??e.ts;Yv=new Set(["opened","ci-fixed","reconciled","pushed"]),yh=e=>`${e.repo??""}#${e.number}`;CL=3e4;NL=2*6e4;BL=12e4,LL=new Set(["archived","unarchived","opened"]);jL=2500,FL=new Set(["pr-opened","pr-merged","pr-auto-armed"]);UL=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 KL(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 zL(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 JL(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<qL&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<GL&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function oS(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=KL(e),s=zL(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=Hn(e??[]).filter(p=>p?.kind==="message"&&typeof p.text=="string"&&p.text.trim()).slice(-HL);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(rS[r]??rS.failover),l.push(""),o.lastPush?.sha){let p=nS(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=nS(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=JL(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"}:** ${VL(p.text,WL)}`),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(`
|
|
160
|
-
`);return d.length<=
|
|
159
|
+
`});function ML(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 Qv(e,t,n){let r=yh(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 Hn(e,{now:t=Date.now()}={}){let n=[],r=new Set,o=new Set,s=new Map,a=new Map,i=new Set,c=new Map,l=new Map,u=new Map,d=(b,m)=>{m!=null&&(u.has(b)||u.set(b,[]),u.get(b).push(m))},p=(b,m)=>{let v=null;for(let D of[b,null])for(let J of u.get(D)||[])J>m&&(v==null||J<v)&&(v=J);return v},f=new Set,w=new Map,h=new Map,y=(b,m)=>{h.set(b,m),n.push(m)},k=b=>{let m=h.get(b);m&&(m.retired=!0)},g=/^Agent "([\s\S]+)" finished$/,x=new Map;for(let b of e||[]){if(b.type!=="tool.called"||b.source!=="transcript")continue;let m=String(b.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 D=v[0].split("/").pop().toLowerCase();x.has(D)||x.set(D,[]),x.get(D).push(b.payload?.ts??b.ts)}}let S=3*6e4,_=20*6e4,M=(b,m)=>!!(b?.name&&x.get(String(b.name).toLowerCase()))?.some(D=>D>=m-S&&D<=m+_);for(let b of e||[])if(b.type==="turn.retracted"){o.add(`${b.boxId}|${b.payload?.saidTs}`);let m=He(b.payload?.text);m&&(s.has(b.boxId)||s.set(b.boxId,[]),s.get(b.boxId).push(m))}else if(b.type==="turn.user"&&b.source==="orchestrator"){let m=He(b.payload?.text);m&&(a.has(b.boxId)||a.set(b.boxId,new Set),a.get(b.boxId).add(m))}else if(b.type==="job.stalled"&&b.payload?.id)c.set(b.payload.id,{ts:fe(b),quietMs:Number(b.payload.quietMs)||null});else if(b.type==="job.stopped"&&b.payload?.id)l.has(b.payload.id)||l.set(b.payload.id,{status:"killed",summary:null,resultHead:null,ts:fe(b)});else if(b.type==="session.ready"){let m=b.payload?.source;m!=="compact"&&m!=="clear"&&d(b.boxId??null,fe(b))}else if(b.type==="handoff.done")d(b.boxId??b.payload?.from??null,fe(b));else if(b.type==="box.state.changed"&&b.payload?.change==="archived")d(null,fe(b));else if(b.type==="job.started"&&b.payload?.id)i.add(b.payload.id),(b.payload.kind||"agent")==="agent"&&b.payload.name&&f.add(b.payload.name);else if(b.type==="turn.user"&&b.payload?.note?.kind==="task"){let m=b.payload.note,v={status:m.status||null,summary:m.summary||null,resultHead:m.resultHead??null,ts:fe(b)};m.toolUseId&&l.set(m.toolUseId,v);let D=String(m.summary||"").match(g);D&&w.set(D[1],v)}let P=new Map,j=new Map,A=null,$=new Map,L=new Map,H=new Map,R=new Map,T=new Map,C=new Map,W=new Map,B=new Map,Z=new Map,xe=new Map,Te=new Map,_e=new Map,Q=new Map,Xe=new Map,Pe=new Map,le=new Map,ge=(b,m)=>{b.arm.status=m;let v=b.line,D=v.arms.map(J=>J.status);v.status=D.includes("armed")?"armed":D.includes("offered")?"offered":D.includes("replaced")?"replaced":"cancelled"},de=[],te=new Set,we=new Map,Oe=(b,m)=>{we.delete(b);let v=n.indexOf(b);v>=0&&n.splice(v,1),m!=null&&(b.ts=m),n.push(b)},Ve=b=>{if(de.push(b),!(b.box?te.has(b.box):te.size>0))return;let v=n.indexOf(b);v>=0&&n.splice(v,1),we.set(b,"held")},Nt=b=>{if(!de.length)return;let m=fe(b);for(let v of de)v.status!=="open"||we.get(v)==="superseded"||v.box&&v.box!==b.boxId||(v.ts??0)>m||Oe(v,m)},O=b=>{if(!de.length)return;let m=fe(b);for(let v of de)we.get(v)!=="held"||v.status!=="open"||v.box&&b.boxId&&v.box!==b.boxId||Oe(v,Math.max(v.ts??0,m))},X=()=>{for(let[b,m]of[...we])m==="held"&&Oe(b,null);de.length=0,re.clear()},re=new Set,he=b=>{for(let m of re){if(m.status!=="open"||m.box&&b&&m.box!==b)continue;m.status="superseded";let v=n.indexOf(m);v>=0&&n.splice(v,1),we.set(m,"superseded"),$.delete(m.offerId)}re.clear()},Be=new Map,Pt=new Map,tt=new Map,wn=new Map,Kt=new Map,ht=new Map,wt=new Map,at=new Map,Ai=()=>{for(let b of at.values())b.questions.length&&n.push(b);at.clear()},gt=null,cr=new Map,Hr=6e4,lr=new Map,gn=(b,m)=>{let v=s.get(b);if(!v?.length)return!1;let D=He(m),J=a.get(b);return v.some(ie=>{if(!D.startsWith(ie))return!1;let ve=He(D.slice(ie.length));return!!ve&&!!J?.has(ve)})};for(let b of e||[]){let m=b.payload||{};switch(b.type){case"turn.user":{if(b.source==="hook"){b.boxId&&te.add(b.boxId);break}if(X(),!String(m.text||"").trim()&&!m.images?.length&&!m.attachments?.length)break;{let v=Gv(m.text);if(v){if(v.kind==="shell-input"){let D={id:b.id,kind:"shell",command:v.command,stdout:null,stderr:null,done:!1,ts:fe(b),box:b.boxId,agent:b.provider};n.push(D),cr.set(b.boxId,D)}else{let D=cr.get(b.boxId);D?(D.stdout=v.stdout||null,D.stderr=v.stderr||null,D.done=!0,cr.delete(b.boxId)):n.push({id:b.id,kind:"shell",command:null,stdout:v.stdout||null,stderr:v.stderr||null,done:!0,ts:fe(b),box:b.boxId,agent:b.provider})}break}}{let v=m.note||kc(m.text);if(v){if(v.kind==="task"){if(v.toolUseId&&i.has(v.toolUseId))break;let D=String(v.summary||"").match(g);if(!v.toolUseId&&D&&f.has(D[1]))break}if(v.kind==="command"&&Wv(v.command))break;if(v.kind==="resume-summary"){n.push({id:b.id,kind:"resume-summary",title:v.title||"Context resumed from summary",bullets:v.bullets||[],sections:v.sections||[],rawChars:v.rawChars??null,ts:fe(b),box:b.boxId,agent:b.provider});break}v.kind!=="suppress"&&n.push({id:b.id,kind:"event",type:Vv(v),name:v.name??null,command:v.command??null,summary:v.summary??null,status:v.status??null,ts:fe(b),box:b.boxId,agent:b.provider});break}}if(o.has(`${b.boxId}|${fe(b)}`)||b.source==="transcript"&&gn(b.boxId,m.text))break;if(m.kind==="merge-notify"||m.kind==="ship-notify"){n.push({id:b.id,kind:"event",type:m.kind==="ship-notify"?"ship-notified":"merge-notified",text:m.text,ts:fe(b),box:b.boxId,agent:b.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==="login-greet"){m.kind==="prompt-answer"&&(gt=null);break}for(let v of $.values())v.status="stale";$.clear();for(let v of H.values())v.status="stale";H.clear();for(let v of Q.values())v.rows.some(D=>D.rowStatus==="sent")||(v.status="stale",Q.delete(v.offerId));gt&>.box===b.boxId&&(n.push(ML(gt)),gt=null),n.push({id:b.id,kind:"message",role:"user",text:m.text,ts:fe(b),box:b.boxId,agent:b.provider,boxRole:m.boxRole,...b.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||{};n.push({id:b.id,kind:"event",type:"peer-message",text:m.text,from:v.trackName||v.boxId||v.name||null,fromTrackId:v.trackId||null,ts:fe(b),box:b.boxId,agent:b.provider});break}case"turn.agent":{if(m.model==="<synthetic>"&&/^No response requested\.$/.test(String(m.text||"").trim()))break;let v=Qx(m,{codexFailedText:Qo});if(v){n.push({id:b.id,kind:"event",type:"provider-error",cls:v.cls,status:v.status??void 0,text:v.text,provider:b.provider,legacy:!0,at:fe(b),ts:fe(b),box:b.boxId,agent:b.provider});break}Ai();let D=(m.images||[]).filter(yt=>!M(yt,fe(b)));if(b.source==="hook"&&!String(m.text||"").trim()&&(O(b),b.boxId&&te.delete(b.boxId)),!String(m.text||"").trim()&&!D.length)break;if(b.provider==="cursor"){let yt=`${b.boxId}|${m.text}`;if(r.has(yt))break;r.add(yt)}(!gt||gt.box===b.boxId)&&(gt=null);let J=b.provider==="codex"?(m.tools||[]).filter(yt=>!th.has(yt)&&!sc.has(yt)):m.tools||[],ie=m.text,ve=null,_t=mh(String(m.text||""));_t&&(ie=_t.text,ve={questions:_t.questions}),n.push({id:b.id,kind:"message",role:"assistant",text:ie,ts:fe(b),box:b.boxId,agent:b.provider,boxRole:m.boxRole,...ve?{ask:ve}:{},...J.length?{tools:J}:{},...m.model?{model:m.model}:{},...m.sourceId?{sourceId:m.sourceId}:{},...D.length?{images:D}:{}}),Nt(b),ve&&(gt={id:b.id,groupId:hh(b.provider,m.sourceId||b.id,ve.questions),provider:b.provider,box:b.boxId,questions:ve.questions,ts:fe(b)});break}case"job.started":{if(!m.id)break;let v=fe(b),D=l.get(m.id);if(!D&&(m.kind||"agent")==="agent"&&m.name){let rn=w.get(m.name);rn&&rn.ts>=v&&(D=rn)}let J=D?null:c.get(m.id)||null,ie=D||J?null:p(b.boxId??null,v),ve={jobId:m.id,jobKind:m.kind||"agent",name:m.name||null,detail:m.detail||null,status:D?D.status==="killed"?"stopped":D.status&&D.status!=="completed"?"failed":"done":J?"stalled":ie!=null?"lost":"running",startedTs:v,finishedTs:D?.ts??J?.ts??ie??null,quietMs:J?.quietMs??null,summary:D?.summary??null,resultHead:D?.resultHead??null},_t=lr.get(b.boxId),yt=_t?.jobs[_t.jobs.length-1]?.startedTs;if(_t&&yt!=null&&v-yt<=Hr)_t.jobs.push(ve);else{let rn={id:b.id,kind:"job-dock",dockId:m.id,ts:v,box:b.boxId,agent:b.provider,jobs:[ve]};lr.set(b.boxId,rn),n.push(rn)}break}case"handoff.done":n.push(bt(b,"lead-handoff"));break;case"subtask.delegated":n.push({...bt(b,"subtask-delegated"),to:b.boxId,toAgent:b.provider});break;case"subtask.merged":n.push(bt(b,"subtask-merged"));break;case"pr.state.changed":{if(!m.change)break;if(m.change==="opened"){for(let D of $.values())D.status="stale";$.clear();for(let D of H.values())D.status="stale";H.clear()}let v=bt(b,`pr-${m.change}`);if(v.box=b.boxId??null,Zv.has(m.change)&&m.number!=null){let D=Qv(P,j,m);if(P.size>=2){for(let ie of j.values())for(let ve of ie)ve.grouped=!0;v.groupPrs=[...P.values()];let J=[v];for(let ie of P.keys())j.set(ie,J)}else{let J=j.get(D)??[];J.push(v),j.set(D,J)}}else if(m.change==="merged"||m.change==="closed"||m.change==="lost"){let D=yh(m),J=A&&!A.complete||P.size>=2;if(P.has(D))P.delete(D),j.delete(D);else if(m.number!=null)for(let ie of[...P.keys()])ie.endsWith(`#${m.number}`)&&(P.delete(ie),j.delete(ie));if(J&&m.number!=null){(!A||A.complete)&&(A={id:b.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:fe(b),ts:fe(b),box:b.boxId,agent:b.provider},n.push(A)),A.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:fe(b)}),A.remaining=[...P.values()],A.complete=P.size===0;break}}n.push(v);break}case"pr.offered":{if(!m.offerId)break;he(b.boxId);for(let D of $.values())D.status="stale";$.clear();for(let D of H.values())D.status="stale";H.clear();let v={id:b.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:b.source==="cli"||m.via==="cli"?"cli":"transcript",ts:fe(b),box:b.boxId,agent:b.provider};$.set(m.offerId,v),L.set(m.offerId,v),n.push(v),re.add(v),v.source==="cli"&&Ve(v);break}case"pr.offer.answered":{let v=L.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}),$.delete(m.offerId));break}case"pr.exists.offered":{if(!m.offerId)break;for(let D of H.values())D.status="stale";H.clear();let v={id:b.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:fe(b),box:b.boxId,agent:b.provider};H.set(m.offerId,v),R.set(m.offerId,v),n.push(v);break}case"pr.exists.answered":{let v=R.get(m.offerId);v&&(v.status=m.choice||"dismiss",H.delete(m.offerId));break}case"box.state.changed":{let v=m.change==="model"?"model-change":m.change;if(!v||v==="cli-update"&&m.status!=="failed")break;let D=bt(b,v);if(v==="model-change"&&(D.to=m.model),v==="node-update"&&D.latest){let J=n.findIndex(ie=>ie.kind==="event"&&ie.type==="node-update"&&ie.latest===D.latest);if(J>=0){let ie=n[J];n[J]={...D,ts:ie.ts,at:ie.at,firstAt:ie.firstAt??ie.ts,updatedAt:D.ts};break}}n.push(D);break}case"track.transferred":case"track.recovered":n.push(bt(b,"track-moved"));break;case"view.state.changed":{if(m.change==="deleted"){k(`view:${m.viewId}`);break}if(m.change!=="created"||m.restarted)break;y(`view:${m.viewId}`,{id:b.id,kind:"view-card",viewId:m.viewId,name:m.name,slug:m.slug,port:m.port,origin:m.origin,ts:fe(b),box:b.boxId,agent:b.provider});break}case"view.spinup.failed":{n.push({id:b.id,kind:"view-spinup-failed",name:m.name||null,reason:m.reason||"no-view",ts:fe(b),box:b.boxId,agent:b.provider});break}case"mock.state.changed":{if(m.change==="picked"){n.push({id:b.id,kind:"mock-picked",mockId:m.mockId,name:m.name,group:m.group??null,version:m.version??null,siblings:m.siblings??[],ts:fe(b),box:b.boxId,agent:b.provider});break}if(m.change==="deleted"){k(`mock:${m.mockId}`);break}if(m.change!=="created")break;y(`mock:${m.mockId}`,{id:b.id,kind:"mock-card",mockId:m.mockId,name:m.name,group:m.group??null,ts:fe(b),box:b.boxId,agent:b.provider});break}case"flow.state.changed":{if(m.change==="deleted"){k(`flow:${m.flowId}`);break}if(m.change!=="created")break;y(`flow:${m.flowId}`,{id:b.id,kind:"flow-card",flowId:m.flowId,name:m.name,stepCount:m.stepCount??null,mockIds:m.mockIds??[],ts:fe(b),box:b.boxId,agent:b.provider});break}case"runbook.state.changed":{if(m.change==="deleted"){k(`runbook:${m.runbookId}`);break}if(m.change!=="created")break;y(`runbook:${m.runbookId}`,{id:b.id,kind:"runbook-card",runbookId:m.runbookId,name:m.name,ts:fe(b),box:b.boxId,agent:b.provider});break}case"ticket.linked":{n.push({id:b.id,kind:"ticket-card",provider:m.provider||"linear",key:m.key,url:m.url,title:m.title??null,linkSource:m.linkSource??null,ts:fe(b),box:b.boxId,agent:b.provider});break}case"box.fullscreen.detected":{if(!m.offerId)break;for(let D of W.values())D.box===(m.box||b.boxId)&&(D.status="stale",W.delete(D.offerId));let v={id:b.id,kind:"fullscreen-offer",offerId:m.offerId,status:"open",ts:fe(b),box:m.box||b.boxId,agent:m.agent||b.provider};W.set(m.offerId,v),B.set(m.offerId,v),n.push(v);break}case"box.fullscreen.answered":{let v=B.get(m.offerId);v&&(v.status=m.action==="restarted"?"restarted":m.action==="cleared"?"cleared":"dismiss",W.delete(m.offerId));break}case"ticket.move.offered":{if(!m.offerId)break;for(let D of T.values())D.key===m.key&&(D.status="stale",T.delete(D.offerId));let v={id:b.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:fe(b),box:b.boxId,agent:b.provider};T.set(m.offerId,v),C.set(m.offerId,v),n.push(v);break}case"ticket.move.answered":{let v=C.get(m.offerId);v&&(v.status=m.action==="moved"?"moved":"dismiss",T.delete(m.offerId));break}case"ticket.state.changed":{for(let v of T.values())v.key===m.key&&v.to===m.to&&(v.status="stale",T.delete(v.offerId));if(m.mode==="offer")break;n.push(bt(b,"ticket-state"));break}case"ticket.commented":{n.push({id:b.id,kind:"ticket-comment",key:m.key,url:m.url??null,excerpt:m.excerpt??null,ts:fe(b),box:b.boxId,agent:b.provider});break}case"ticket.flow.changed":n.push(bt(b,"ticket-flow"));break;case"ticket.create.offered":{if(!m.proposalId)break;for(let D of Z.values())D.status="stale";Z.clear();let v={id:b.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:fe(b),box:b.boxId,agent:b.provider};Z.set(m.proposalId,v),xe.set(m.proposalId,v),n.push(v),Ve(v);break}case"ticket.create.answered":{let v=xe.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",v.createdCount=m.createdCount??null,v.failedCount=m.failedCount??null,Z.delete(m.proposalId));break}case"loop.create.offered":{if(!m.proposalId)break;for(let D of Te.values())D.status="stale";Te.clear();let v={id:b.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:fe(b),box:b.boxId,agent:b.provider};Te.set(m.proposalId,v),_e.set(m.proposalId,v),n.push(v),Ve(v);break}case"loop.create.answered":{let v=_e.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",Te.delete(m.proposalId));break}case"followup.armed":{if(!m.armId)break;let v=m.replaces?Pe.get(m.replaces):null;v&&v.arm.status==="armed"&&ge(v,"replaced");let D={armId:m.armId,seq:Number.isInteger(m.seq)?m.seq:0,label:m.label??"",prompt:m.prompt??"",why:m.why??null,standing:m.standing==="never"?"never":"offerable",status:"armed"},J=m.groupId??null,ie=J?le.get(J):null;ie||(ie={id:b.id,kind:"followup-armed",groupId:J,armId:D.armId,trigger:m.trigger??"now",label:D.label,prompt:D.prompt,why:D.why,planWhy:m.planWhy??null,arms:[],status:"armed",ts:fe(b),box:b.boxId,agent:b.provider},J&&le.set(J,ie),ie.trigger!=="now"&&n.push(ie)),ie.arms.push(D),Pe.set(m.armId,{arm:D,line:ie});break}case"followup.armed.cancelled":{let v=Pe.get(m.armId);v&&v.arm.status==="armed"&&ge(v,"cancelled");break}case"followup.offered":{if(!m.offerId)break;for(let J of Q.values())J.trigger===m.trigger&&(J.status="stale",Q.delete(J.offerId));let v=Array.isArray(m.rows)?m.rows:[];for(let J of v){let ie=J.armId?Pe.get(J.armId):null;ie&&ie.arm.status==="armed"&&ge(ie,"offered")}let D={id:b.id,kind:"followup-offer",offerId:m.offerId,trigger:m.trigger??"now",repo:m.repo??null,context:m.context&&typeof m.context=="object"?m.context:{},groupId:m.groupId??null,why:m.why??null,rows:v.map(J=>({followupId:J.followupId??null,armId:J.armId??null,label:J.label??"",prompt:J.prompt??null,promptPreview:J.promptPreview??null,promptHash:J.promptHash??null,origin:J.origin??"operator",why:J.why??null,groupId:J.groupId??null,seq:Number.isInteger(J.seq)?J.seq:0,standing:J.standing==="never"?"never":"offerable",promotable:J.armId?J.promotable!==!1:!1,promotableReason:J.promotableReason??null,rowStatus:"open"})),status:"open",answer:null,answers:[],ts:fe(b),box:b.boxId,agent:b.provider};Q.set(m.offerId,D),Xe.set(m.offerId,D),n.push(D),D.trigger==="now"&&Ve(D);break}case"followup.answered":{let v=Xe.get(m.offerId);if(!v)break;let D=new Set;m.armId&&D.add(`arm:${m.armId}`),m.followupId&&D.add(`fu:${m.followupId}`);for(let ve of Array.isArray(m.armIds)?m.armIds:[])D.add(`arm:${ve}`);for(let ve of Array.isArray(m.followupIds)?m.followupIds:[])D.add(`fu:${ve}`);let J=m.action==="sent"?"sent":m.action==="removed"?"removed":"dismiss";for(let ve of v.rows){if(ve.rowStatus!=="open")continue;let _t=ve.armId?`arm:${ve.armId}`:`fu:${ve.followupId}`;D.size&&!D.has(_t)||(ve.rowStatus=J)}let ie={followupId:m.followupId??null,armId:m.armId??null,label:m.label??null,promoted:m.promoted??null,revived:m.revived===!0,action:J,batchId:m.batchId??null};v.answers.push(ie),v.answer=ie,v.rows.some(ve=>ve.rowStatus==="open")?v.status="open":(v.status=v.rows.some(ve=>ve.rowStatus==="sent")?"sent":v.rows.some(ve=>ve.rowStatus==="removed")?"removed":"dismiss",Q.delete(m.offerId));break}case"followup.create.offered":{if(!m.proposalId)break;for(let D of Be.values())D.status="stale";Be.clear();let v={id:b.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:fe(b),box:b.boxId,agent:b.provider};Be.set(m.proposalId,v),Pt.set(m.proposalId,v),n.push(v),Ve(v);break}case"followup.create.answered":{let v=Pt.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",Be.delete(m.proposalId));break}case"followup.created":{let v=m.proposalId?Pt.get(m.proposalId):null;v&&(v.created={followupId:m.followupId??null});break}case"loop.created":{let v=m.proposalId?_e.get(m.proposalId):null;v&&(v.created={loopId:m.loopId??null,nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),n.push(bt(b,"loop-created"));break}case"loop.update.offered":case"loop.delete.offered":{if(!m.proposalId||!m.loopId)break;for(let J of tt.values())J.loopId===m.loopId&&(J.status="stale",J.staleReason="superseded",tt.delete(J.proposalId));let v=b.type==="loop.update.offered",D={id:b.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:fe(b),box:b.boxId,agent:b.provider};tt.set(m.proposalId,D),wn.set(m.proposalId,D),n.push(D),Ve(D);break}case"loop.update.answered":{let v=wn.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="applied"?"applied":"dismiss",tt.delete(m.proposalId));break}case"loop.delete.answered":{let v=wn.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="delete"?"deleted":m.action==="pause"?"paused":"keep",tt.delete(m.proposalId));break}case"loop.updated":{let v=m.proposalId?wn.get(m.proposalId):null;v&&(v.applied={changed:Array.isArray(m.changed)?m.changed:[],nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),n.push(bt(b,"loop-updated"));break}case"loop.deleted":n.push(bt(b,"loop-deleted"));break;case"loop.paused":n.push(bt(b,"loop-paused"));break;case"planning.change.offered":{if(!m.proposalId||!Array.isArray(m.ops)||!m.ops.length)break;for(let D of Kt.values())D.status="stale",D.staleReason="superseded",Kt.delete(D.proposalId);let v={id:b.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:fe(b),box:b.boxId,agent:b.provider};Kt.set(m.proposalId,v),ht.set(m.proposalId,v),n.push(v),Ve(v);break}case"planning.change.answered":{let v=ht.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="applied"?"applied":"dismiss",Kt.delete(m.proposalId));break}case"planning.changed":{let v=m.proposalId?ht.get(m.proposalId):null;v&&(v.applied={results:Array.isArray(m.results)?m.results:[]}),n.push(bt(b,"planning-changed"));break}case"rework.cause.offered":{if(!m.sha||wt.has(m.sha))break;let v={id:b.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:fe(b),box:b.boxId,agent:b.provider};wt.set(m.sha,v),n.push(v);break}case"rework.cause.answered":{let v=m.sha?wt.get(m.sha):null;v&&(v.status="answered",v.cause=m.cause??null);break}case"rework.cause.retracted":{let v=m.sha?wt.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},D=m.proposalId?xe.get(m.proposalId):null;if(D){D.created=v;break}n.push({id:b.id,kind:"ticket-created",...v,ts:fe(b),box:b.boxId,agent:b.provider});break}case"error":{if(!m.kind)break;n.push(bt(b,m.kind));break}case"provider.error":n.push({...bt(b,"provider-error"),provider:b.provider,box:b.boxId,agent:b.provider});break;case"usage.cap.hit":n.push(bt(b,"usage-cap-hit"));break;case"usage.cap.warning":n.push(bt(b,"usage-cap-warning"));break;case"provider.connected":{n.push({id:b.id,kind:"event",type:"provider-connected",provider:m.provider||b.provider,account:m.account||null,reason:m.reason||null,ts:fe(b),at:fe(b),box:b.boxId,agent:b.provider});break}case"provider.login.started":break;case"prompt.answered":{if(gt=null,m.control||!m.question)break;let v=m.groupId||m.promptId;if(!v)break;let D=at.get(v);D||(D={id:b.id,kind:"prompt",groupId:v,provider:b.provider,box:b.boxId,agent:b.provider,questionTotal:m.questionTotal??1,questions:[],ts:fe(b)},at.set(v,D));let J=m.questionIndex??0,ie={index:J,header:m.header||"",question:m.question,multiSelect:!!m.multiSelect,chosen:Array.isArray(m.chosen)?m.chosen:[]},ve=D.questions.findIndex(yt=>yt.index===J&&He(yt.question)===He(ie.question));ve>=0?D.questions[ve]=ie:D.questions.push(ie),D.questions.sort((yt,nt)=>yt.index-nt.index),D.questionTotal=Math.max(D.questionTotal,m.questionTotal??1),D.questions.length>=D.questionTotal&&(n.push(D),at.delete(v));break}case"prompt.dismissed":{gt=null;let v=m.groupId||m.promptId;if(!v||!m.question)break;let J=at.get(v)||{id:b.id,kind:"prompt",groupId:v,provider:b.provider,box:b.boxId,agent:b.provider,questionTotal:m.questionTotal??1,questions:[],ts:fe(b)},ie=m.questionIndex??0;J.questions.some(ve=>ve.index===ie&&He(ve.question)===He(m.question))||(J.questions.push({index:ie,header:m.header||"",question:m.question,multiSelect:!!m.multiSelect,chosen:[]}),J.questions.sort((ve,_t)=>ve.index-_t.index)),J.answeredVia="chat",n.push(J),at.delete(v);break}default:break}}for(let[b,m]of[...we]){if(m==="superseded"){b.status!=="superseded"&&Oe(b,null);continue}(b.status!=="open"||t-(b.ts??0)>=CL)&&Oe(b,null)}let ur=h.size?n.filter(b=>!b.retired):n;return PL(ur.sort((b,m)=>(b.ts??0)-(m.ts??0)))}function PL(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<=NL){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 bh(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:DL(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??Hn(e)).filter(o=>o.kind==="message");return Gx(n,r)}function DL(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==ha(e.command)?t:Qr(e.command,e.description||"")}function bt(e,t){let{ts:n,change:r,kind:o,...s}=e.payload||{};return{id:e.id,kind:"event",type:t,...s,at:fe(e),ts:fe(e)}}function eS(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)+BL,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&LL.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function tS(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!FL.has(r.type)||!r.box)continue;let o=(r.ts??0)+jL,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 pd(e){return(e||[]).filter(t=>UL.has(t?.kind)&&t.status==="open")}function nS(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(Zv.has(o.change)&&o.number!=null)Qv(t,n,o);else if(o.change==="merged"||o.change==="closed"||o.change==="lost"){let s=yh(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 fe,Zv,yh,CL,NL,BL,LL,jL,FL,UL,fd=G(()=>{wa();es();zu();mc();dd();gh();fe=e=>e.payload?.ts??e.ts;Zv=new Set(["opened","ci-fixed","reconciled","pushed"]),yh=e=>`${e.repo??""}#${e.number}`;CL=3e4;NL=2*6e4;BL=12e4,LL=new Set(["archived","unarchived","opened"]);jL=2500,FL=new Set(["pr-opened","pr-merged","pr-auto-armed"]);UL=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 KL(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 zL(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 JL(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<qL&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<GL&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function aS(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=KL(e),s=zL(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=Hn(e??[]).filter(p=>p?.kind==="message"&&typeof p.text=="string"&&p.text.trim()).slice(-HL);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(sS[r]??sS.failover),l.push(""),o.lastPush?.sha){let p=oS(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=oS(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=JL(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"}:** ${VL(p.text,WL)}`),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(`
|
|
160
|
+
`);return d.length<=rS?d:`${d.slice(0,rS)}
|
|
161
161
|
|
|
162
|
-
[\u2026 brief truncated]`}var HL,WL,
|
|
162
|
+
[\u2026 brief truncated]`}var HL,WL,rS,qL,GL,VL,oS,sS,iS=G(()=>{fd();HL=12,WL=2e3,rS=48e3,qL=30,GL=10;VL=(e,t)=>{let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1)}\u2026`},oS=(e,t)=>e&&t?Math.max(1,Math.round((e-t)/6e4)):null;sS={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."}});function ro(e){let t=e.search(Sh);return(t>=0?e.slice(0,t):e).trim()}function YL(e){if(!e)return"";let t=e.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!t||/hidden|\d+\s+lines?/i.test(t)?"":t}function XL(e){let t=String(e).trim();return!!(!t||no.test(e)||Sh.test(t)||/hidden|\d+\s+lines?/i.test(t)||/notespress|press n to add/i.test(t)||/^\s*notes?\s*$/i.test(t))}function ZL(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=QL(n,t);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function QL(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 cS(e){return e.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function ej(e,t=""){let n=String(e);if(!dS.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
163
163
|
`),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(ss);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&&!no.test(r[u])&&a.push(p)}}if(s.length<2)return null;let i=s.map(u=>({label:ro(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 nj(e,t=""){let n=String(e).replace(/\r/g,"").split(`
|
|
164
|
-
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!
|
|
164
|
+
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!pS.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(ss);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&&!(tj.test(n[c])||no.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:ro(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 rj(e,t=""){let n=String(e);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
165
165
|
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(ss);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 sj(e,t=""){let n=String(e);if(!kh.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
166
|
-
`),o=-1;for(let u=r.length-1;u>=0;u--)if(oj.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(ss);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&&!no.test(r[u])&&a.push(p)}}if(s.length<2||!s.some(u=>kh.test(u.label)))return null;let i=s.map(u=>({label:ro(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 Th(e){let t=[];for(let r=0;r<e.length;r++){let o=e[r].match(ss);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 aj(e,t=""){let n=String(e);if(!
|
|
167
|
-
`),o=Th(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:ro(s.label),description:""}))}]}:null}function ij(e,t=""){let n=String(e);if(!
|
|
166
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(oj.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(ss);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&&!no.test(r[u])&&a.push(p)}}if(s.length<2||!s.some(u=>kh.test(u.label)))return null;let i=s.map(u=>({label:ro(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 Th(e){let t=[];for(let r=0;r<e.length;r++){let o=e[r].match(ss);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 aj(e,t=""){let n=String(e);if(!fS.test(n)||!mS.test(n)||as.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
167
|
+
`),o=Th(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:ro(s.label),description:""}))}]}:null}function ij(e,t=""){let n=String(e);if(!hS.test(n)||as.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
168
168
|
`),o=Th(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:ro(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 lj(e,t){let n=e[t].trim();for(let r=t+1;r<e.length;r++){let o=e[r].trim();if(!o||!cj.test(o)||Ah.test(e[r]))break;n+=o}return n}function uj(e,t=""){let n=String(e);if(!Ah.test(n)||as.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
169
169
|
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=lj(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 pj(e,t=""){let n=String(e);if(!xh.test(n)||!dj.test(n)||as.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
170
|
-
`)].reverse().find(a=>xh.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 fj(e,t=""){let n=String(e);if(!
|
|
170
|
+
`)].reverse().find(a=>xh.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 fj(e,t=""){let n=String(e);if(!wS.test(n)||as.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
171
171
|
`),o=r.flatMap(a=>{let i=a.match(/^\s*(❯\s*)?(Yes, I trust this folder|No, exit)\s*$/);return i?[{cursor:!!i[1],label:i[2]}]:[]}),s=o.length===2&&new Set(o.map(a=>a.label)).size===2&&o.filter(a=>a.cursor).length===1?o:Th(r);return s?{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,s.findIndex(a=>a.cursor)),setup:"trust",promptId:`claude:setup:trust:${s.map(a=>a.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:s.map(a=>({label:ro(a.label),description:""}))}]}:null}function mj(e,t=""){let n=String(e);if(as.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
172
|
-
`),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(
|
|
173
|
-
`),o=hj(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let L=i;L<c;L++){let H=r[L].match(ss);H&&(u<0&&(u=L),l.push({line:L,cursor:!!H[1],checked:H[3]!=null&&H[3]!==" ",hasCheckbox:H[3]!=null,label:H[4].trim(),desc:""}))}if(!l.length)return null;for(let L of l)for(let H=L.line+1;H<Math.min(r.length,L.line+4)&&!(ss.test(r[H])||no.test(r[H]));H++){let R=r[H].trim();if(!(!R||XL(r[H])||
|
|
174
|
-
`).pop()||"")?!0:as.test(n)?!1:
|
|
175
|
-
`).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)||e==="cursor"&&/Trust this workspace[\s\S]*\[a\]\s*Trust/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 Sn(e,t){return!e||!t?null:e.agent==="claude"?rj(t,e.name)||yj(t,e.name)||ej(t,e.name)||sj(t,e.name)||pj(t,e.name)||uj(t,e.name)||ij(t,e.name)||aj(t,e.name)||fj(t,e.name)||mj(t,e.name)||nj(t,e.name):e.agent==="cursor"||e.agent==="codex"?
|
|
176
|
-
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];kj=5e3,
|
|
177
|
-
`)){let f=p.trim();if(!f||f[0]!=="{")continue;let w;try{w=JSON.parse(f)}catch{continue}if(w.isSidechain)continue;let h=Oj(w);if(h){let g=h.match(Rj)?.[1]?.trim();g&&a.add(g);continue}let y=w.message?.content;if(!Array.isArray(y))continue;let k=w.timestamp?Date.parse(w.timestamp):null;for(let g of y)if(!(!g||typeof g!="object")){if(g.type==="tool_use"&&g.id){if(g.name==="Agent"){let x=g.input||{};o.set(g.id,{id:g.id,kind:"agent",name:String(x.description||"").trim()||null,detail:String(x.subagent_type||"").trim()||null,ts:k,proven:!1})}else if(g.name==="Bash"){let x=String(g.input?.command||"").trim(),S={id:g.id,kind:"command",name:String(g.input?.description||"").trim()||(x?ha(x):null),detail:x?ha(x):null,ts:k,proven:g.input?.run_in_background===!0};S.proven?o.set(g.id,S):c.set(g.id,S)}else if(_j.has(g.name)){let x=g.input?.task_id??g.input?.shell_id??g.input?.bash_id;x&&u.set(g.id,{taskId:String(x),ts:k})}}else if(g.type==="tool_result"&&g.tool_use_id){let x=Mj(g.content);if(vj.test(x)){s.add(g.tool_use_id);let P=x.match(Ej)?.[1];P&&l.set(P,g.tool_use_id)}let S=x.match(Aj)?.[1];S&&l.set(S,g.tool_use_id);let _=x.match(Sj);if(_){l.set(_[1].trim(),g.tool_use_id);let P=c.get(g.tool_use_id);P&&(c.delete(g.tool_use_id),P.proven=!0,P.outputFile=x.match(Tj)?.[1]?.replace(/\.$/,"")||null,o.set(P.id,P))}let M=u.get(g.tool_use_id);if(M){u.delete(g.tool_use_id);let{message:P,taskId:j}=$j(x);if(!g.is_error&&Ij.test(P)){let A=l.get(M.taskId)??(j?l.get(j):void 0);A&&(a.add(A),i.set(A,k??M.ts??null))}}}}}let d=[];for(let p of o.values()){if(!p.proven&&!s.has(p.id))continue;let{proven:f,...w}=p;w.stoppedTs=i.has(p.id)?i.get(p.id):null,d.push(w)}return{launches:d.slice(-n),done:a,stopped:i}}var vj,Sj,Tj,Aj,Ej,_j,Ij,
|
|
178
|
-
`).trim():"",qj=e=>Array.isArray(e)?e.filter(t=>t&&t.type==="tool-call"&&t.toolName).map(t=>t.toolName):[],
|
|
179
|
-
|
|
180
|
-
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function iF(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(zS.test(s)){if(r.length>=aT)continue;let a=aF(s.replace(zS,"").replace(/[.;,:]+$/,""),iT,oF);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>VS&&(o=`${o.slice(0,VS-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function cT(e,t,{timeoutMs:n=9e4}={}){let r=co.join(t,nF),o=co.join(r,rF);await io.mkdir(r,{recursive:!0});let s=await xd(o),a=await Se(e);if(!a)throw new Error(`session "${e}" not found`);return await vd(e,a,sF(o)),await hT(o,s,n),iF(await io.readFile(o,"utf8"))}function lT(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===nT||n===sT;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(tT)||n===Qj||r.startsWith(rT)||r.startsWith(oT)}function uT(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-cF;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 dT(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 pT(e,{windows:t=[]}={}){if(lT(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 Sd(e){return jh.get(e)||null}async function fT(e){let t=jh.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await Se(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:co.join(n.cwd,ZS,QS)};return jh.set(e,r),dF(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function dF(e,t){e.step="summarize",await mT(e.name,t,{quiesce:{reason:"console-checkpoint",onWait:r=>{e.step="await-turn",e.wait=r},onResume:()=>{e.step="summarize"}}}),e.step="clear";let n=await Se(e.name);await vd(e.name,n,"/clear"),await Uh(lF),e.step="resume",await vd(e.name,n,tF),e.step="done",e.finishedAt=Date.now()}async function JS(e,t,n,r){if(!r)return{outcome:"skipped"};let o=r.box||{name:t,agent:n?.agent??null,cwd:n?.cwd??null,sessionId:n?.sessionId??null},s=r.reason?`${r.reason}:${e}`:e,a=!1;try{let i=await qS({...o,name:t},{timeoutMs:r.timeoutMs,reason:s,onWait:async c=>{a=!0,r.onWait&&await r.onWait(c,e)}});return a&&r.onResume&&await r.onResume(e),{outcome:"ok",waitedMs:i.waitedMs,waited:i.waited}}catch(i){if(i?.code!=="quiescence-timeout"||(r.onTimeout||pF)===fF)throw i;return await ee({flow:"checkpoint",phase:"quiescence-timeout",sessionName:t,gate:e,waitedMs:i.waitedMs,detail:i.detail,still:Lh(i.detail),paneTail:!0}),r.onResume&&await r.onResume(e),{outcome:"timeout",waitedMs:i.waitedMs,waited:!0,detail:i.detail}}}async function mT(e,t,{timeoutMs:n=uF,quiesce:r=null}={}){let o=co.join(t,ZS),s=co.join(o,QS);await io.mkdir(o,{recursive:!0});let a=await Se(e),i=await JS("pre-drive",e,a,r),c=await xd(s);await ee({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:s,beforeMtime:c,quiescence:i.outcome,waitedMs:i.waitedMs});try{await vd(e,a,eF(s)),await hT(s,c,n)}catch(f){throw f&&typeof f=="object"&&(f.quiescence={pre:i,post:{outcome:"skipped"}}),f}let l=await JS("post-write",e,a,r);await ee({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:s,afterMtime:await xd(s),quiescence:l.outcome,waitedMs:l.waitedMs});let u=await Cv(t,s,{exclude:f=>lT(f.role,f.text)});await ee({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:s,...u});let d=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(d){await N({id:d,cwd:t},{type:"memory.seed",actor:"orchestrator",payload:{...u,tokensEst:u.chars?Math.round(u.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let f=await io.readFile(s,"utf8");await N({id:d,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...eT(f),ts:Date.now()}})}catch{}}await Om(t);let p=await mF(o,s);return d&&await fx(d,p||s,{box:e,agent:a?.agent??null}),{file:s,quiescence:{pre:i,post:l}}}async function Td(e,{timeoutMs:t,quiesce:n=null}={}){let r=await Se(e);if(!r)throw new Error(`session "${e}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${e}"`);return mT(e,r.cwd,{...t?{timeoutMs:t}:{},quiesce:n})}function Fh(e){let t=e?.quiescence;if(!t)return null;let n=[t.pre?.outcome,t.post?.outcome].filter(r=>r&&r!=="skipped");return n.length?n.includes("timeout")?"timeout":"ok":null}async function Ad(e,t,{trackName:n="this Track"}={}){let r=await K({id:e,cwd:t}),o=oS(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=co.join(t,YS);return await io.mkdir(co.dirname(s),{recursive:!0}),await io.writeFile(s,o,"utf8"),await N({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...eT(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),YS}async function xd(e){try{return(await io.stat(e)).mtimeMs}catch{return 0}}async function hT(e,t,n){let r=Date.now()+n;for(;;){if(await xd(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await Uh(2e3)}}async function mF(e,t){try{if(!Zj.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=co.join(e,`${n}.md`);return await io.copyFile(t,r),r}catch{return null}}function Uh(e){return new Promise(t=>setTimeout(t,e))}function XS(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 vd(e,t,n){if(t){let o=XS(t.agent);await wr({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await Se(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await Ke(e,["Escape"],{kind:"drive-wipe"}),await Uh(150),await da(e,Wu,{kind:"drive-wipe"}),await Qe(e,n,{kind:"orchestration-drive",...Un(r.agent,n)}),await wc({sessionName:e,agent:r.agent,agentCmd:XS(r.agent),text:n,flow:"orchestration-drive"})}var ZS,QS,KS,tT,nT,rT,Qj,eF,tF,oT,sT,nF,rF,VS,aT,iT,oF,sF,zS,aF,cF,lF,uF,jh,pF,fF,YS,Hh=G(()=>{dt();os();yn();pc();ch();Nv();Wi();be();Pu();sS();GS();ZS=".bostrat/handoff",QS="latest.md",KS=32*1024;tT="Checkpoint: write a work-in-progress handoff",nT="HANDOFF WRITTEN",rT="Resume from .bostrat/handoff/latest.md",Qj="/clear",eF=e=>`${tT} 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 ${nT} and take no further action.`,tF=`${rT}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,oT="Beat: write a",sT="BEAT WRITTEN",nF=".bostrat/arc",rF="beat.txt",VS=200,aT=4,iT=5,oF=36,sF=e=>`${oT} 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 ${aT} further lines, each starting with "- " and naming ONE piece in at most ${iT} 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 ${sT} and take no further action.`,zS=/^(?:[-–—*•·]|\d+[.)])\s+/,aF=(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};cF=6e5;lF=3500,uF=3e5,jh=new Map;pF="proceed",fF="reject";YS=".bostrat/handoff/status-latest.md"});function uo(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!Ed.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 yT(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:uo(o.from,"from",n),toMs:uo(o.to,"to",n)}))}return t}function qh(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 wF(e){if(!e)return null;let t=String(e).trim();if(lo[t])return t;if(wT[t])return wT[t];let n=null;for(let r of Object.keys(lo))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function gF(e){if(!e||e===lo)return hF;let t=gT.get(e);return t||(t=yT(e),gT.set(e,t)),t}function bT(e,t,n={}){let{table:r,speed:o}=n,s=gF(r),a=r&&r!==lo?s[e]?e:null:wF(e);if(!a)return null;let i=qh(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 Wn(e,t={},n,r){let o=bT(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 yF(e=lo){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:uo(a.from,"from",n),toMs:uo(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`),Ed.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 kF({now:e=Date.now(),table:t=lo,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...bF,...n},a=[],i=qh(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:uo(d.from,"from",c),toMs:uo(d.to,"to",c),verifiedMs:Ed.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*Wh;p&&i-d.verifiedMs>r*Wh&&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*Wh&&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=lo,...n}={}){let r=qh(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=uo(l.from,"from",i),d=uo(l.to,"to",i);r>=u&&r<=d&&Ed.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=[...yF(t),...kF({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var lo,wT,Ed,hF,gT,kT,bF,Wh,yr=G(()=>{lo={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"GA rates; verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-08-17",note:"$2/$10 is the standard price (intro pricing made permanent; the 2026-09-01 $3/$15 increase was cancelled) \u2014 verified 2026-08-17 against platform.claude.com/docs/en/about-claude/pricing."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-fable-5-1":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:.25,cacheReadX:.025,verifiedAt:"2026-09-07",note:"verified 2026-09-07 against platform.claude.com/docs/en/about-claude/pricing: $10/$50, 1h cache write $20 (2\xD7), cache hits $0.25 (0.025\xD7 \u2014 the 5.1-only multiplier; footnote 1 on the page)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-03, re-verified 2026-08-17 (developers.openai.com pricing); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:"2026-08-23",input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL). Superseded 2026-08-24 by the vendor's promotional cut (window below)."},{from:"2026-08-24",to:null,input:4,output:20,cacheWrite:5,cacheRead:.4,verifiedAt:"2026-08-24",note:"promotional pricing verified 2026-08-24 (developers.openai.com pricing), 'at least through November 21, 2026'; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:"2026-08-16",input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:2,output:12,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:"2026-08-16",input:1,output:6,cacheWrite:1.25,cacheRead:.1,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:.2,output:1.2,cacheWrite:.25,cacheRead:.02,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-6-astra":[{from:null,to:null,input:10,output:50,cacheWrite:12.5,cacheRead:1,fast:{input:20,output:100,cacheWrite:25,cacheRead:2},verifiedAt:"2026-09-07",note:"verified 2026-09-07 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input; fast tier $20/$100 recorded as the fast variant (codex reports no speed)."}]},wT={opus:"claude-opus-5",opusplan:"claude-opus-5",default:"claude-opus-5",sonnet:"claude-sonnet-5",haiku:"claude-haiku-4-5",fable:"claude-fable-5-1","gpt-5.6":"gpt-5.6-sol"},Ed=/^\d{4}-\d{2}-\d{2}$/;hF=yT(lo);gT=new WeakMap;kT=(e,t,n)=>bT(e,t,n)!==null;bF=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),Wh=864e5});function xF(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function vT(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function sn(e){let t=e.payload?.model||"unknown",n=xF(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=Wn(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&&!kT(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function po(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=xT();for(let p of n){let{usd:f,model:w,tokens:h,repriced:y,unpriced:k,asRecorded:g}=sn(p);i+=g;let x=p.provider||"unknown",S=p.boxId||"unattributed";if(y&&c++,k&&(l++,u.add(w)),a+=f,vT(d,h),r[w]||={usd:0,tokens:xT(),events:0},r[w].usd+=f,r[w].events+=1,vT(r[w].tokens,h),o[x]||={usd:0,events:0},o[x].usd+=f,o[x].events+=1,s[S]||={usd:0,events:0,provider:x,models:new Set,label:t[S]||S},s[S].usd+=f,s[S].events+=1,w!=="unknown"&&s[S].models.add(w),p.epoch!=null){let _=(s[S].epochs||={})[p.epoch]||={usd:0,events:0,provider:x,models:new Set};_.usd+=f,_.events+=1,_.provider=x,w!=="unknown"&&_.models.add(w)}}return{totalUsd:_a(a),asRecordedUsd:_a(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:_d(r,p=>({...p,usd:_a(p.usd)})),byProvider:_d(o,p=>({...p,usd:_a(p.usd)})),byBox:_d(s,p=>({usd:_a(p.usd),events:p.events,provider:p.provider,label:p.label,models:[...p.models],...p.epochs?{epochs:_d(p.epochs,f=>({usd:_a(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var xT,_a,_d,fo=G(()=>{yr();xT=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});_a=e=>Math.round(e*1e4)/1e4,_d=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function vF(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 ST(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(Gh(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Gh(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(`${Gh(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=po(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?vF(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Gh,TT=G(()=>{fo();Gh=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as SF}from"node:child_process";import{promisify as TF}from"node:util";import{randomUUID as AF}from"node:crypto";import AT from"node:fs/promises";async function Kh(e,t){let n=Date.now()+t;for(;;){if(await Fn(e))return!0;if(Date.now()>n)return!1;await us(RT)}}function OF(e){let t=String(e||"").split(`
|
|
172
|
+
`),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(gS);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(no.test(r[l])||Sh.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 hj(e){let t=-1;for(let r=e.length-1;r>=0;r--)if(vh.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 wj(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(md.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&&(no.test(o)||uS.test(o))?r:null}function gj(e,t){if(!e)return null;for(let n=e.line+1;n<Math.min(t.length,e.line+3);n++){let r=t[n],o=r.replace(/^\s*❯/,"").trim();if(o)return uS.test(o)?{cursor:/^\s*❯/.test(r)}:null}return null}function yj(e,t=""){let n=String(e);if(!lS.test(n)||!vh.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
173
|
+
`),o=hj(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let L=i;L<c;L++){let H=r[L].match(ss);H&&(u<0&&(u=L),l.push({line:L,cursor:!!H[1],checked:H[3]!=null&&H[3]!==" ",hasCheckbox:H[3]!=null,label:H[4].trim(),desc:""}))}if(!l.length)return null;for(let L of l)for(let H=L.line+1;H<Math.min(r.length,L.line+4)&&!(ss.test(r[H])||no.test(r[H]));H++){let R=r[H].trim();if(!(!R||XL(r[H])||md.test(R))){L.desc=YL(R);break}}let d=wj(l,r),p=l.filter(L=>L!==d&&!md.test(L.label));if(!p.length)return null;let f=p.some(L=>L.hasCheckbox),w=f?gj(d,r):null,h=w?.cursor?p.length+1:d?.cursor?p.length:Math.max(0,p.findIndex(L=>L.cursor)),y=d&&!md.test(d.label)?ro(d.label):"",k=s>=0?s+1:0,g=[];for(let L=k;L<u;L++){let H=r[L].trim();H&&!no.test(r[L])&&!/[☐☒]/.test(r[L])&&g.push(H)}let x=g.join(" ")||(s>=0?cS(r[s]):"")||"The agent is asking a question",S=s>=0?ZL(r[s],x):null,_=S?.currentTabLabel||(s>=0?cS(r[s]):""),M=p.map(L=>({label:ro(L.label),description:L.desc,...f?{checked:!!L.checked}:{}})),P=M.map(L=>L.label).join("|"),A=`claude:${S?`q${S.questionIndex+1}/${S.questionTotal}`:"q1/1"}:${_}:${x}:${P}`.slice(0,240),$=S?`claude:ask:${S.tabs.map(L=>L.label).join("|")}`.slice(0,240):A;return{provider:"claude",box:t,multiSelect:f,cursorIndex:h,promptId:A,groupId:$,...d?{custom:!0,customIndex:p.length,customText:y}:{},...w?{commitIndex:p.length+1}:{},dismissable:!0,...S?{questionIndex:S.questionIndex,questionTotal:S.questionTotal,currentTabLabel:S.currentTabLabel,tabs:S.tabs}:{},questions:[{question:x,header:_,multiSelect:f,options:M}]}}function oo(e,t){let n=String(t||"");return n?e==="claude"?lS.test(n)&&vh.test(n)||dS.test(n)||kh.test(n)||pS.test(n.trimEnd().split(`
|
|
174
|
+
`).pop()||"")?!0:as.test(n)?!1:fS.test(n)&&mS.test(n)||hS.test(n)||Ah.test(n)||xh.test(n)||wS.test(n)||gS.test(n.trimEnd().split(`
|
|
175
|
+
`).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)||e==="cursor"&&/Trust this workspace[\s\S]*\[a\]\s*Trust/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 Sn(e,t){return!e||!t?null:e.agent==="claude"?rj(t,e.name)||yj(t,e.name)||ej(t,e.name)||sj(t,e.name)||pj(t,e.name)||uj(t,e.name)||ij(t,e.name)||aj(t,e.name)||fj(t,e.name)||mj(t,e.name)||nj(t,e.name):e.agent==="cursor"||e.agent==="codex"?av(e.agent,t,e.name):null}async function yS(e){if(!e)return null;let t=await Jt(e.name).catch(()=>"");return Sn(e,t)}function Eh(e,t){if(e?.provider==="cursor"&&e.promptId==="cursor:setup:trust")return["a"];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 bS(e,n))s.push(...o(a,i),"Enter"),a=i;Number.isInteger(e.commitIndex)?s.push(...o(a,e.commitIndex),"Enter"):s.push("Tab","Enter")}else{let a=n.length?n[0]:0;s.push(...o(r,a),"Enter")}return s}function bS(e,t){let n=e?.questions?.[0]?.options||[],r=new Set(t),o=Math.max(n.length,t.length?Math.max(...t)+1:0),s=[];for(let a=0;a<o;a++)!!n[a]?.checked!==r.has(a)&&s.push(a);return s}function kS(e,t,n=""){let r=typeof n=="string"?n.trim():"";if(!r||!e?.custom)return{pre:Eh(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 bS(e,o))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 xS(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 lS,vh,ss,md,uS,no,Sh,dS,pS,tj,kh,oj,as,fS,mS,hS,Ah,cj,xh,dj,wS,gS,_h=G(()=>{dt();Xu();lS=/to navigate\b.*\bEsc to cancel|Enter to select\b/,vh=/Chat about this/i,ss=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,md=/^(type something|chat about this|submit|next)\b/i,uS=/^(submit|next)\b/i,no=/^[─—-]{6,}\s*$/,Sh=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;dS=/^\s*Switch model\?\s*$/m;pS=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,tj=/^\s*[─—▔-]{6,}\s*$/;kh=/Resume from summary/i,oj=/^\s*This session is\b/;as=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;fS=/Choose the text style that looks best with your terminal/,mS=/Let's get started\./;hS=/Select login method:/;Ah=/Paste code here if prompted/,cj=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;xh=/OAuth error:/,dj=/Press Enter to retry\./;wS=/Quick safety check:\s+Is this a project you created or one you trust/;gS=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function hd(e,t,n){if(!e||!t)return null;for(let r of bj)if(r.provider===e.agent)try{if(r.match(t,n))return r}catch{}return null}function xj(e){let t=Ih.get(e);return t||(t={sentAt:[],last:0,emitted:new Set},Ih.set(e,t)),t}function TS(e,t,n=Date.now()){let r=xj(e);if(r.sentAt=r.sentAt.filter(s=>n-s<SS),r.sentAt.length>=vS)return{action:"give-up"};if(n-r.last<kj)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 AS(e,t=Date.now()){let n=Ih.get(e);return n?n.sentAt.filter(r=>t-r<SS).length>=vS:!1}var bj,kj,vS,SS,Ih,$h=G(()=>{bj=[{provider:"claude",id:"login-continue",match(e){return/^claude:setup:continue:/.test(e?.promptId||"")&&e?.setup==="continue"},keys:["Enter"],accept:!0,gate:"login-enter"},{provider:"codex",id:"login-continue",match(e){return e?.promptId==="codex:signin-complete"&&e?.setup==="continue"},keys:["Enter"],accept:!0,gate:"login-enter"},{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||""}
|
|
176
|
+
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];kj=5e3,vS=5,SS=3600*1e3,Ih=new Map});function wd(e,t,n){let{state:r,reason:o}=lc(t,n);if(r!=="unknown"){if(e){let a=Date.now();for(let[i,c]of xc)a-c.at>=ES()&&xc.delete(i);xc.set(e,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!e)return!1;let s=xc.get(e);return s?Date.now()-s.at>=ES()?(xc.delete(e),!1):s.busy:!1}async function vc(e,t){let n={working:!1,prompted:!1};if(!e)return n;let r=await Ne(e,{lines:ze}).catch(()=>"");if(!wd(e,t,r))return od(t,r)?{working:!0,prompted:!1}:n;if(oo(t,r)){let o=await Jt(e).catch(()=>r),s=Sn({name:e,agent:t},o);if(s)return!hd({name:e,agent:t},s,o)||AS(e)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function is(e,t){return(await vc(e,t)).working}var ES,xc,va=G(()=>{dt();os();_h();$h();ES=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,xc=new Map});function $j(e){if(e.startsWith("{"))try{let t=JSON.parse(e);return{message:String(t?.message??""),taskId:t?.task_id?String(t.task_id):null}}catch{}return{message:e,taskId:null}}function Oj(e){if(e.type==="queue-operation"&&e.operation==="enqueue")return _S.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"&&_S.test(t)?t:null}async function IS(e,t={}){return(await RS(e,t)).launches}async function $S(e,t={}){let{launches:n,done:r}=await RS(e,t);return n.filter(o=>!r.has(o.id))}async function RS(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await jt(e,t)}catch{return{launches:[],done:new Set,stopped:new Map}}let o=new Map,s=new Set,a=new Set,i=new Map,c=new Map,l=new Map,u=new Map;for(let p of r.split(`
|
|
177
|
+
`)){let f=p.trim();if(!f||f[0]!=="{")continue;let w;try{w=JSON.parse(f)}catch{continue}if(w.isSidechain)continue;let h=Oj(w);if(h){let g=h.match(Rj)?.[1]?.trim();g&&a.add(g);continue}let y=w.message?.content;if(!Array.isArray(y))continue;let k=w.timestamp?Date.parse(w.timestamp):null;for(let g of y)if(!(!g||typeof g!="object")){if(g.type==="tool_use"&&g.id){if(g.name==="Agent"){let x=g.input||{};o.set(g.id,{id:g.id,kind:"agent",name:String(x.description||"").trim()||null,detail:String(x.subagent_type||"").trim()||null,ts:k,proven:!1})}else if(g.name==="Bash"){let x=String(g.input?.command||"").trim(),S={id:g.id,kind:"command",name:String(g.input?.description||"").trim()||(x?ha(x):null),detail:x?ha(x):null,ts:k,proven:g.input?.run_in_background===!0};S.proven?o.set(g.id,S):c.set(g.id,S)}else if(_j.has(g.name)){let x=g.input?.task_id??g.input?.shell_id??g.input?.bash_id;x&&u.set(g.id,{taskId:String(x),ts:k})}}else if(g.type==="tool_result"&&g.tool_use_id){let x=Mj(g.content);if(vj.test(x)){s.add(g.tool_use_id);let P=x.match(Ej)?.[1];P&&l.set(P,g.tool_use_id)}let S=x.match(Aj)?.[1];S&&l.set(S,g.tool_use_id);let _=x.match(Sj);if(_){l.set(_[1].trim(),g.tool_use_id);let P=c.get(g.tool_use_id);P&&(c.delete(g.tool_use_id),P.proven=!0,P.outputFile=x.match(Tj)?.[1]?.replace(/\.$/,"")||null,o.set(P.id,P))}let M=u.get(g.tool_use_id);if(M){u.delete(g.tool_use_id);let{message:P,taskId:j}=$j(x);if(!g.is_error&&Ij.test(P)){let A=l.get(M.taskId)??(j?l.get(j):void 0);A&&(a.add(A),i.set(A,k??M.ts??null))}}}}}let d=[];for(let p of o.values()){if(!p.proven&&!s.has(p.id))continue;let{proven:f,...w}=p;w.stoppedTs=i.has(p.id)?i.get(p.id):null,d.push(w)}return{launches:d.slice(-n),done:a,stopped:i}}var vj,Sj,Tj,Aj,Ej,_j,Ij,_S,Rj,Mj,OS=G(()=>{vn();wa();vj=/^Async (?:agent|task) launched/i,Sj=/^Command did not complete within its [^\n]*?moved to the background \(ID: ([^)]+)\)/i,Tj=/Output is being written to:\s*(\S+)/i,Aj=/^Command running in background with ID:\s*([^\s.]+)/i,Ej=/\bagentId:\s*([A-Za-z0-9_-]+)/,_j=new Set(["TaskStop","KillShell"]),Ij=/^Successfully (?:stopped|killed)\b/i;_S=/^\s*<task-notification>/i,Rj=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i;Mj=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 so(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 Cj(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 MS(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:Tn(`read ${so(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?so(t.path,n):null,s=t?.glob?` --glob ${Rh(t.glob)}`:"",a=Tn(String(r),40);return{command:Tn(`grep ${Rh(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?so(t.target_directory,n):null;return{command:Tn(`glob ${Rh(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${Tn(String(r),40)}\u201D`}}case"StrReplace":return t?.path?{command:Tn(`edit ${so(t.path,n)}`,140),description:`Edit ${Sa(t.path)}`}:null;case"Write":return t?.path?{command:Tn(`write ${so(t.path,n)}`,140),description:`Write ${Sa(t.path)}`}:null;case"Delete":return t?.path?{command:Tn(`delete ${so(t.path,n)}`,140),description:`Delete ${Sa(t.path)}`}:null;case"ReadLints":{let o=(Array.isArray(t?.paths)?t.paths:[]).map(s=>so(s,n)).filter(Boolean);return{command:Tn(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=Cj(t);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:Tn(`apply_patch ${so(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:Tn(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var gd,Tn,Sa,Rh,CS=G(()=>{gd=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),Tn=(e,t=88)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,Sa=e=>String(e).replace(/\/+$/,"").split("/").pop()||String(e);Rh=e=>`"${String(e).replace(/"/g,'\\"')}"`});import Sc from"node:fs";import Nj from"node:fs/promises";import cs from"node:path";import Pj from"node:os";import Dj from"node:crypto";import Bj from"node:zlib";function DS(e){let t=Dj.createHash("md5").update(String(e)).digest("hex");return cs.join(Lj,t)}function BS(e){try{let t=JSON.parse(Sc.readFileSync(cs.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 jj(e){let t=DS(e),n;try{n=Sc.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=cs.join(t,o.name),a=cs.join(s,"store.db");if(!Sc.existsSync(a))continue;let i=BS(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 yd(e,t){if(!e)return null;if(t){let n=cs.join(DS(e),t),r=cs.join(n,"store.db");if(Sc.existsSync(r))return{id:t,dir:n,store:r,...BS(n)||{createdAtMs:null,updatedAtMs:null}}}return jj(e)}function NS(e,t){let n=e.prepare("SELECT data FROM blobs WHERE id = ?").get(t);return n?Buffer.from(n.data):null}function Fj(e){if(!e)return null;let t=e;if(t.length>1&&t[0]===31&&t[1]===139)try{t=Bj.gunzipSync(t)}catch{}try{return JSON.parse(t.toString("utf8"))}catch{return null}}function Uj(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 Hj(e,t){if(!Array.isArray(e))return[];let n=[];for(let r of e){if(!r||r.type!=="tool-call"||!gd.has(r.toolName))continue;let o=MS(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 Oh(e,t){if(!PS)return Ta;let{DatabaseSync:n}=PS,r;try{r=new n(e,{readOnly:!0})}catch{return Ta}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 Ta;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return Ta}let a=s.lastUsedModel||null,i=s.latestRootBlobId?NS(r,s.latestRootBlobId):null;if(!i)return{...Ta,model:a};let c=[],l=[],u=[],d=null,p=[];for(let f of Uj(i)){let w=Fj(NS(r,f));if(!w)continue;let h=w.providerOptions?.cursor;if(h?.requestId&&p.push(h.requestId),h?.modelProviderMessageId&&p.push(h.modelProviderMessageId),w.role!=="assistant")continue;if(Array.isArray(w.content))for(let g of w.content){let x=g?.providerOptions?.cursor?.modelName;x&&(d=x)}l.push(...Hj(w.content,t).map((g,x)=>({...g,id:g.id||`${f}:${x}`}))),u.push(...qj(w.content));let y=Wj(w.content);if(!y)continue;let k=[...new Set(u)];u=[],c.push({id:f,text:y,tools:k.length?k:void 0,commands:l,model:d}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:p}}catch{return Ta}finally{try{r.close()}catch{}}}function LS(e,t){let n=e.updatedAtMs;if(!n)try{n=Math.round(Sc.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 jS(e,t,{limit:n=80}={}){let r=yd(e,t);if(!r)return[];let{turns:o,model:s}=await Oh(r.store,e);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=LS(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 FS(e,t){let n=yd(e,t);if(!n)return null;let{turns:r,requestIds:o,model:s}=await Oh(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 US(e,t,{limit:n=80}={}){let r=yd(e,t);if(!r)return[];let{turns:o,trailingCommands:s}=await Oh(r.store,e);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=LS(r,a.length||1),l=[];a.forEach((u,d)=>{for(let p of u.commands)l.push({...p,summary:Qr(p.command,p.description),ts:c(d)})});for(let u of s)l.push({...u,summary:Qr(u.command,u.description),ts:i+1});return l}async function HS(e,t){let n=yd(e,t);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await Nj.stat(n.store)).mtimeMs)}catch{return null}}var Lj,Wj,qj,PS,Ta,Mh=G(()=>{wa();CS();Lj=process.env.BOSTRAT_CURSOR_CHATS||cs.join(Pj.homedir(),".cursor","chats");Wj=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(`
|
|
178
|
+
`).trim():"",qj=e=>Array.isArray(e)?e.filter(t=>t&&t.type==="tool-call"&&t.toolName).map(t=>t.toolName):[],PS=process.getBuiltinModule?.("node:sqlite"),Ta={turns:[],trailingCommands:[],model:null,requestIds:[]}});import Gj from"node:fs/promises";function Dh(e){return WS.set(e.agent,e),e}function ao(e){return WS.get(e)||Kj}async function Bh(e,t={}){return(await ao(e.agent).readMessages(e,t)).map(bd(e))}async function qS(e,t={}){return(await ao(e.agent).readCommands(e,t)).map(bd(e))}async function GS(e,t={}){return(await ao(e.agent).readJobs(e,t)).map(bd(e))}async function kd(e,t={}){return(await ao(e.agent).readOpenJobs(e,t)).map(bd(e))}var Aa,Ch,Nh,Ph,Kj,WS,bd,xd=G(()=>{vn();Ju();wa();OS();Mh();es();Aa=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}},Ch=class extends Aa{transcript(t){return!t?.cwd||!t?.sessionId?null:xn(t.cwd,t.sessionId)}async readMessages(t,n){let r=this.transcript(t);return r?rh(r,n):[]}async readCommands(t,n){let r=this.transcript(t);return r?qx(r,n):[]}async readJobs(t,n){let r=this.transcript(t);return r?IS(r,n):[]}async readOpenJobs(t,n){let r=this.transcript(t);return r?$S(r,n):[]}async updatedAt(t){let n=this.transcript(t);if(!n)return null;try{return Math.round((await Gj.stat(n)).mtimeMs)}catch{return null}}},Nh=class extends Aa{async readMessages(t,n){return jS(t.cwd,t.sessionId,n)}async readCommands(t,n){return US(t.cwd,t.sessionId,n)}async updatedAt(t){return HS(t.cwd,t.sessionId)}},Ph=class extends Aa{sessionId(t){return t?.pinned?t.sessionId:null}async readMessages(t,n){return zx(t.cwd,this.sessionId(t),n)}async readCommands(t,n){return Jx(t.cwd,this.sessionId(t),n)}async updatedAt(t){return Yx(t.cwd,this.sessionId(t))}},Kj=new Aa("unknown"),WS=new Map;Dh(new Ch("claude",Zm));Dh(new Nh("cursor",gd));Dh(new Ph("codex",sc));bd=e=>t=>({...t,box:e.name,agent:e.agent,boxRole:e.role})});function Lh(e){if(!e)return"the current turn";let t=[];e.prompted?t.push("an unanswered prompt"):e.busy?t.push("the current turn"):e.unknown&&t.push("a pane too narrow to read"),e.awaitingBackground&&t.push("a background wait");let n=Array.isArray(e.jobs)?e.jobs:[];if(n.length){let r=n.slice(0,2).map(o=>o.name||o.id).filter(Boolean);t.push(`${n.length} background job${n.length===1?"":"s"}${r.length?` (${r.join(", ")}${n.length>r.length?", \u2026":""})`:""}`)}return t.length?t.join(" and "):"the current turn"}async function KS(e,{timeoutMs:t,reason:n=null,onWait:r=null,deps:o=null}={}){let s={...Xj,...o||{}},a=e?.name,i=e?.agent||null;if(!a)throw new Error("awaitQuiescence: box.name is required");let c=Number(t)||Vj(),l=zj(),u=s.now(),d=0,p=0,f=!1,w=null;for(;;){p++;let h=await s.capture(a),y=s.busyRead(i,h);if(y.reason==="blocked"){let g=new Error(`${i||"the agent"} in "${a}" is sitting at a login or trust gate \u2014 answer it in the terminal, then retry`);throw g.code="quiescence-blocked",g.detail={blocked:!0},g}if(w={busy:y.state==="busy",unknown:y.state==="unknown",awaitingBackground:s.awaitingBackground(i,h),prompted:!1,jobs:[]},w.busy){let g=await s.turnState(a,i).catch(()=>({prompted:!1}));w.prompted=g?.prompted===!0}if(!w.busy&&!w.unknown&&!w.awaitingBackground){let g=await s.openJobs(e).catch(()=>[]);w.jobs=(Array.isArray(g)?g:[]).map(x=>({id:x.id??null,name:x.name??null,kind:x.kind??null}))}let k=!w.busy&&!w.unknown&&!w.awaitingBackground&&!w.prompted&&w.jobs.length===0;if(d=k?d+1:0,d>=Jj){let g=s.now()-u;return f&&ee({flow:"quiescence",phase:"quiet",sessionName:a,agent:i,reason:n,waitedMs:g,reads:p}).catch(()=>{}),{waitedMs:g,reads:p,waited:f,detail:w}}if(!k&&!f&&(f=!0,ee({flow:"quiescence",phase:"waiting",sessionName:a,agent:i,reason:n,detail:w}).catch(()=>{}),r&&await r(w)),s.now()-u>=c){let g=new Error(`waited ${Math.round(c/1e3)}s for ${Lh(w)} in "${a}" to finish`);throw g.code="quiescence-timeout",g.detail=w,g.waitedMs=s.now()-u,ee({flow:"quiescence",phase:"timeout",sessionName:a,agent:i,reason:n,waitedMs:g.waitedMs,reads:p,detail:w,error:g.message}).catch(()=>{}),g}await s.sleep(l)}}var Vj,zj,Jj,Yj,Xj,VS=G(()=>{dt();os();va();xd();yn();Vj=()=>Number(process.env.BOSTRAT_QUIESCENCE_TIMEOUT_MS)||6e5,zj=()=>Number(process.env.BOSTRAT_QUIESCENCE_POLL_MS)||1500,Jj=2,Yj=e=>new Promise(t=>setTimeout(t,e)),Xj={capture:e=>Ne(e,{lines:ze}),busyRead:lc,awaitingBackground:od,turnState:vc,openJobs:kd,now:()=>Date.now(),sleep:Yj}});import Zj from"node:fs";import io from"node:fs/promises";import co from"node:path";function nT(e){let t=Wo(String(e??"")),n=Buffer.byteLength(t,"utf8");return n<=zS?{content:t,bytes:n,truncated:!1}:{content:`${Buffer.from(t,"utf8").subarray(0,zS).toString("utf8").replace(/�+$/,"")}
|
|
179
|
+
|
|
180
|
+
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function iF(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(YS.test(s)){if(r.length>=cT)continue;let a=aF(s.replace(YS,"").replace(/[.;,:]+$/,""),lT,oF);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>JS&&(o=`${o.slice(0,JS-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function uT(e,t,{timeoutMs:n=9e4}={}){let r=co.join(t,nF),o=co.join(r,rF);await io.mkdir(r,{recursive:!0});let s=await vd(o),a=await Se(e);if(!a)throw new Error(`session "${e}" not found`);return await Sd(e,a,sF(o)),await gT(o,s,n),iF(await io.readFile(o,"utf8"))}function dT(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===oT||n===iT;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(rT)||n===Qj||r.startsWith(sT)||r.startsWith(aT)}function pT(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-cF;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 fT(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 mT(e,{windows:t=[]}={}){if(dT(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 Td(e){return jh.get(e)||null}async function hT(e){let t=jh.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await Se(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:co.join(n.cwd,eT,tT)};return jh.set(e,r),dF(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function dF(e,t){e.step="summarize",await wT(e.name,t,{quiesce:{reason:"console-checkpoint",onWait:r=>{e.step="await-turn",e.wait=r},onResume:()=>{e.step="summarize"}}}),e.step="clear";let n=await Se(e.name);await Sd(e.name,n,"/clear"),await Uh(lF),e.step="resume",await Sd(e.name,n,tF),e.step="done",e.finishedAt=Date.now()}async function XS(e,t,n,r){if(!r)return{outcome:"skipped"};let o=r.box||{name:t,agent:n?.agent??null,cwd:n?.cwd??null,sessionId:n?.sessionId??null},s=r.reason?`${r.reason}:${e}`:e,a=!1;try{let i=await KS({...o,name:t},{timeoutMs:r.timeoutMs,reason:s,onWait:async c=>{a=!0,r.onWait&&await r.onWait(c,e)}});return a&&r.onResume&&await r.onResume(e),{outcome:"ok",waitedMs:i.waitedMs,waited:i.waited}}catch(i){if(i?.code!=="quiescence-timeout"||(r.onTimeout||pF)===fF)throw i;return await ee({flow:"checkpoint",phase:"quiescence-timeout",sessionName:t,gate:e,waitedMs:i.waitedMs,detail:i.detail,still:Lh(i.detail),paneTail:!0}),r.onResume&&await r.onResume(e),{outcome:"timeout",waitedMs:i.waitedMs,waited:!0,detail:i.detail}}}async function wT(e,t,{timeoutMs:n=uF,quiesce:r=null}={}){let o=co.join(t,eT),s=co.join(o,tT);await io.mkdir(o,{recursive:!0});let a=await Se(e),i=await XS("pre-drive",e,a,r),c=await vd(s);await ee({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:s,beforeMtime:c,quiescence:i.outcome,waitedMs:i.waitedMs});try{await Sd(e,a,eF(s)),await gT(s,c,n)}catch(f){throw f&&typeof f=="object"&&(f.quiescence={pre:i,post:{outcome:"skipped"}}),f}let l=await XS("post-write",e,a,r);await ee({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:s,afterMtime:await vd(s),quiescence:l.outcome,waitedMs:l.waitedMs});let u=await Pv(t,s,{exclude:f=>dT(f.role,f.text)});await ee({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:s,...u});let d=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(d){await N({id:d,cwd:t},{type:"memory.seed",actor:"orchestrator",payload:{...u,tokensEst:u.chars?Math.round(u.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let f=await io.readFile(s,"utf8");await N({id:d,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...nT(f),ts:Date.now()}})}catch{}}await Om(t);let p=await mF(o,s);return d&&await hx(d,p||s,{box:e,agent:a?.agent??null}),{file:s,quiescence:{pre:i,post:l}}}async function Ad(e,{timeoutMs:t,quiesce:n=null}={}){let r=await Se(e);if(!r)throw new Error(`session "${e}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${e}"`);return wT(e,r.cwd,{...t?{timeoutMs:t}:{},quiesce:n})}function Fh(e){let t=e?.quiescence;if(!t)return null;let n=[t.pre?.outcome,t.post?.outcome].filter(r=>r&&r!=="skipped");return n.length?n.includes("timeout")?"timeout":"ok":null}async function Ed(e,t,{trackName:n="this Track"}={}){let r=await K({id:e,cwd:t}),o=aS(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=co.join(t,ZS);return await io.mkdir(co.dirname(s),{recursive:!0}),await io.writeFile(s,o,"utf8"),await N({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...nT(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),ZS}async function vd(e){try{return(await io.stat(e)).mtimeMs}catch{return 0}}async function gT(e,t,n){let r=Date.now()+n;for(;;){if(await vd(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await Uh(2e3)}}async function mF(e,t){try{if(!Zj.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=co.join(e,`${n}.md`);return await io.copyFile(t,r),r}catch{return null}}function Uh(e){return new Promise(t=>setTimeout(t,e))}function QS(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 Sd(e,t,n){if(t){let o=QS(t.agent);await wr({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await Se(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await Ke(e,["Escape"],{kind:"drive-wipe"}),await Uh(150),await da(e,qu,{kind:"drive-wipe"}),await Qe(e,n,{kind:"orchestration-drive",...Un(r.agent,n)}),await wc({sessionName:e,agent:r.agent,agentCmd:QS(r.agent),text:n,flow:"orchestration-drive"})}var eT,tT,zS,rT,oT,sT,Qj,eF,tF,aT,iT,nF,rF,JS,cT,lT,oF,sF,YS,aF,cF,lF,uF,jh,pF,fF,ZS,Hh=G(()=>{dt();os();yn();pc();ch();Dv();Wi();be();Du();iS();VS();eT=".bostrat/handoff",tT="latest.md",zS=32*1024;rT="Checkpoint: write a work-in-progress handoff",oT="HANDOFF WRITTEN",sT="Resume from .bostrat/handoff/latest.md",Qj="/clear",eF=e=>`${rT} 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 ${oT} and take no further action.`,tF=`${sT}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,aT="Beat: write a",iT="BEAT WRITTEN",nF=".bostrat/arc",rF="beat.txt",JS=200,cT=4,lT=5,oF=36,sF=e=>`${aT} 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 ${cT} further lines, each starting with "- " and naming ONE piece in at most ${lT} 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 ${iT} and take no further action.`,YS=/^(?:[-–—*•·]|\d+[.)])\s+/,aF=(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};cF=6e5;lF=3500,uF=3e5,jh=new Map;pF="proceed",fF="reject";ZS=".bostrat/handoff/status-latest.md"});function uo(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!_d.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 kT(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:uo(o.from,"from",n),toMs:uo(o.to,"to",n)}))}return t}function qh(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 wF(e){if(!e)return null;let t=String(e).trim();if(lo[t])return t;if(yT[t])return yT[t];let n=null;for(let r of Object.keys(lo))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function gF(e){if(!e||e===lo)return hF;let t=bT.get(e);return t||(t=kT(e),bT.set(e,t)),t}function xT(e,t,n={}){let{table:r,speed:o}=n,s=gF(r),a=r&&r!==lo?s[e]?e:null:wF(e);if(!a)return null;let i=qh(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 Wn(e,t={},n,r){let o=xT(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 yF(e=lo){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:uo(a.from,"from",n),toMs:uo(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`),_d.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 kF({now:e=Date.now(),table:t=lo,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...bF,...n},a=[],i=qh(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:uo(d.from,"from",c),toMs:uo(d.to,"to",c),verifiedMs:_d.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*Wh;p&&i-d.verifiedMs>r*Wh&&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*Wh&&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=lo,...n}={}){let r=qh(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=uo(l.from,"from",i),d=uo(l.to,"to",i);r>=u&&r<=d&&_d.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=[...yF(t),...kF({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var lo,yT,_d,hF,bT,vT,bF,Wh,yr=G(()=>{lo={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"GA rates; verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-08-17",note:"$2/$10 is the standard price (intro pricing made permanent; the 2026-09-01 $3/$15 increase was cancelled) \u2014 verified 2026-08-17 against platform.claude.com/docs/en/about-claude/pricing."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-fable-5-1":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:.25,cacheReadX:.025,verifiedAt:"2026-09-07",note:"verified 2026-09-07 against platform.claude.com/docs/en/about-claude/pricing: $10/$50, 1h cache write $20 (2\xD7), cache hits $0.25 (0.025\xD7 \u2014 the 5.1-only multiplier; footnote 1 on the page)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-03, re-verified 2026-08-17 (developers.openai.com pricing); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:"2026-08-23",input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL). Superseded 2026-08-24 by the vendor's promotional cut (window below)."},{from:"2026-08-24",to:null,input:4,output:20,cacheWrite:5,cacheRead:.4,verifiedAt:"2026-08-24",note:"promotional pricing verified 2026-08-24 (developers.openai.com pricing), 'at least through November 21, 2026'; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:"2026-08-16",input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:2,output:12,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:"2026-08-16",input:1,output:6,cacheWrite:1.25,cacheRead:.1,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:.2,output:1.2,cacheWrite:.25,cacheRead:.02,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-6-astra":[{from:null,to:null,input:10,output:50,cacheWrite:12.5,cacheRead:1,fast:{input:20,output:100,cacheWrite:25,cacheRead:2},verifiedAt:"2026-09-07",note:"verified 2026-09-07 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input; fast tier $20/$100 recorded as the fast variant (codex reports no speed)."}]},yT={opus:"claude-opus-5",opusplan:"claude-opus-5",default:"claude-opus-5",sonnet:"claude-sonnet-5",haiku:"claude-haiku-4-5",fable:"claude-fable-5-1","gpt-5.6":"gpt-5.6-sol"},_d=/^\d{4}-\d{2}-\d{2}$/;hF=kT(lo);bT=new WeakMap;vT=(e,t,n)=>xT(e,t,n)!==null;bF=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),Wh=864e5});function xF(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function TT(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function sn(e){let t=e.payload?.model||"unknown",n=xF(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=Wn(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&&!vT(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function po(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=ST();for(let p of n){let{usd:f,model:w,tokens:h,repriced:y,unpriced:k,asRecorded:g}=sn(p);i+=g;let x=p.provider||"unknown",S=p.boxId||"unattributed";if(y&&c++,k&&(l++,u.add(w)),a+=f,TT(d,h),r[w]||={usd:0,tokens:ST(),events:0},r[w].usd+=f,r[w].events+=1,TT(r[w].tokens,h),o[x]||={usd:0,events:0},o[x].usd+=f,o[x].events+=1,s[S]||={usd:0,events:0,provider:x,models:new Set,label:t[S]||S},s[S].usd+=f,s[S].events+=1,w!=="unknown"&&s[S].models.add(w),p.epoch!=null){let _=(s[S].epochs||={})[p.epoch]||={usd:0,events:0,provider:x,models:new Set};_.usd+=f,_.events+=1,_.provider=x,w!=="unknown"&&_.models.add(w)}}return{totalUsd:_a(a),asRecordedUsd:_a(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Id(r,p=>({...p,usd:_a(p.usd)})),byProvider:Id(o,p=>({...p,usd:_a(p.usd)})),byBox:Id(s,p=>({usd:_a(p.usd),events:p.events,provider:p.provider,label:p.label,models:[...p.models],...p.epochs?{epochs:Id(p.epochs,f=>({usd:_a(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var ST,_a,Id,fo=G(()=>{yr();ST=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});_a=e=>Math.round(e*1e4)/1e4,Id=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function vF(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 AT(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(Gh(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Gh(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(`${Gh(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=po(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?vF(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Gh,ET=G(()=>{fo();Gh=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as SF}from"node:child_process";import{promisify as TF}from"node:util";import{randomUUID as AF}from"node:crypto";import _T from"node:fs/promises";async function Kh(e,t){let n=Date.now()+t;for(;;){if(await Fn(e))return!0;if(Date.now()>n)return!1;await us(MT)}}function OF(e){let t=String(e||"").split(`
|
|
181
181
|
`).map(n=>n.trimEnd()).filter(n=>n.trim());return!t.length||!IF.test(t[t.length-1])?!1:_F.test(t.slice(-$F).join(`
|
|
182
|
-
`))}async function MF(e,t){if(t!=="claude")return null;let n=Date.now()+RF;for(;;){if(await Fn(e))return null;let r=await Ne(e,{lines:30});if(OF(r))return await ee({flow:"swap",phase:"exit-chooser",sessionName:e,from:t,paneTail:!0}),await Ke(e,["Enter"],{kind:"swap-exit"}),"answered";if(Date.now()>n)return null;await us(
|
|
182
|
+
`))}async function MF(e,t){if(t!=="claude")return null;let n=Date.now()+RF;for(;;){if(await Fn(e))return null;let r=await Ne(e,{lines:30});if(OF(r))return await ee({flow:"swap",phase:"exit-chooser",sessionName:e,from:t,paneTail:!0}),await Ke(e,["Enter"],{kind:"swap-exit"}),"answered";if(Date.now()>n)return null;await us(MT)}}async function CF(e,t){let n=IT[t]||IT.default;n.escape&&(await Ke(e,["Escape"],{kind:"swap-exit"}),await us(150)),n.wipe&&await da(e,qu,{kind:"swap-exit"}),await Gu(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Un(t).submitDelayMs||0;return await us(Math.max(r,250)),await Ke(e,["Enter"],{kind:"swap-exit"}),{chooser:await MF(e,t)}}async function BF(e,{settleMs:t=NF,timeoutMs:n=PF,parse:r=!0}={}){let o=Date.now(),s=null,a=null;for(;;){let i=null,c=!0;try{let u=await _T.stat(e);i=`${u.mtimeMs}:${u.size}`}catch{c=!1}if(!c)return{settled:!0,waitedMs:Date.now()-o,parsed:null,missing:!0};let l=Date.now();if(i!==s)s=i,a=l;else if(l-a>=t){let u=null;if(r)try{JSON.parse(await _T.readFile(e,"utf8")),u=!0}catch{u=!1}if(u!==!1)return{settled:!0,waitedMs:l-o,parsed:u};a=l}if(l-o>=n)return{settled:!1,waitedMs:l-o,parsed:null};await us(DF)}}async function $T(e,t){try{let{stdout:n}=await ls(oe,se("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function CT(e){await ls(oe,se("respawn-pane","-k","-t",e))}async function Vh(e,{exitTimeoutMs:t=RT,fromAgent:n=null}={}){if(!je(e))throw new Error("invalid session name");let r=!1;if(await Fn(e))return{forced:r,chooser:null};let o=n||(await Se(e))?.agent||"agent",{chooser:s}=await CF(e,o),a=await Kh(e,t);if(await ee({flow:"swap",phase:"exit",sessionName:e,from:o,graceful:a,chooser:s,paneTail:!0}),!a&&(r=!0,await ee({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await CT(e),!await Kh(e,OT)))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);return{forced:r,chooser:s}}async function zh(e,t={}){if(!je(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await Se(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||Xr(n),a=Number(t.exitTimeoutMs)||RT;if(await ee({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd}),s==="codex"){let k=await mu();k.waited&&await ee({flow:"cli-currency",phase:"spawn-gate",sessionName:e,...k}),hu(e)}let{forced:i,chooser:c}=await Vh(e,{exitTimeoutMs:a,fromAgent:o});if(o==="claude"){let k=await BF(cm());await ee({flow:"swap",phase:"config-settled",sessionName:e,from:o,...k})}let l=s==="claude"&&Yo(n),u=l?cu():null,d=await $T(e,"ANTHROPIC_API_KEY"),p=!1;try{u?(await ls(oe,se("set-environment","-t",e,"ANTHROPIC_API_KEY",u)),p=!0):d&&(await ls(oe,se("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),p=!0)}catch(k){throw u?Lu(k,u):k}if(p&&!i&&(await CT(e),!await Kh(e,OT)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let f=!!t.resumeSessionId,w=t.resumeSessionId||AF(),h=f?n:Zi(n,w,e),y=f?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(h);return l&&await Wr(r.cwd),s==="codex"&&await Xs(r.cwd),s==="cursor"&&await Qs(r.cwd),await Xo(e),await ls(oe,se("set-option","-t",e,"@bostrat-sid",w)),await ls(oe,se("set-option","-t",e,"@bostrat-agent",s)),await us(EF),l&&Xi().length&&(await $T(e,Yi)||await ls(oe,se("set-environment","-t",e,Yi,"1")).catch(()=>{}),await Qe(e,`export ${Yi}=1`,{kind:"swap-env"}),await us(150)),await Qe(e,h,{kind:"swap-launch"}),await ee({flow:"swap",phase:"launched",sessionName:e,from:o,to:s,paneTail:!0}),await ee({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:w,forced:i,chooser:c,envRespawned:p}),{name:e,cwd:r.cwd,sessionId:w,agent:s,pinned:y,forced:i,chooser:c,envRespawned:p,launchCmd:h}}var ls,us,RT,OT,MT,EF,IT,_F,IF,$F,RF,NF,PF,DF,NT=G(()=>{dt();$i();uu();qo();wu();pc();yn();ls=TF(SF),us=e=>new Promise(t=>setTimeout(t,e)),RT=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,OT=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,MT=250,EF=400,IT={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"}};_F=/Background work is running/,IF=/Enter to confirm\s*·\s*Esc to cancel/,$F=12,RF=Number(process.env.BOSTRAT_SWAP_CHOOSER_WAIT_MS)||3e3;NF=Number(process.env.BOSTRAT_CLAUDE_CONFIG_SETTLE_MS)||500,PF=Number(process.env.BOSTRAT_CLAUDE_CONFIG_SETTLE_TIMEOUT_MS)||5e3,DF=100});function Jh(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Yh({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!Jh(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),PT);try{let a=await DT(`${De(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 Tc(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(!Jh(a))return{ok:!1,source:"unconfigured"};let c=$d.get(e.id)||0;if(i()-c<LF)return{ok:!1,source:"debounced"};$d.set(e.id,i());let l=await Ac({env:a}),u=l.loops.find(f=>f.trackId===e.id);if(!u)return $d.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 Yh({loopId:u.id,outcome:"errored",note:d.slice(0,500),env:a});return p.ok||$d.delete(e.id),{...p,loop:{id:u.id,name:u.name}}}async function Ac({env:e=process.env}={}){if(!Jh(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),PT);try{let r=await DT(`${De(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 PT,DT,LF,$d,Rd=G(()=>{vt();PT=1e4,DT=(...e)=>fetch(...e);LF=10*6e4,$d=new Map});import{execFile as jF}from"node:child_process";import Od from"node:fs/promises";import Xh from"node:path";import{promisify as FF}from"node:util";function VF(e){let n=String(e||"").trim().match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.+-]+)?/);return n?n[0]:null}async function zF(e,{env:t=process.env}={}){let n=String(e||"").trim();if(!n)return null;let r=n.includes("/")?[Xh.resolve(n)]:String(t.PATH||"").split(Xh.delimiter).filter(Boolean).map(o=>Xh.join(o,n));for(let o of r)try{await Od.access(o,Od.constants.X_OK);let s=await Od.realpath(o),a=await Od.stat(s);if(!a.isFile())continue;return{path:s,mtimeMs:a.mtimeMs}}catch{}return null}async function ds({agent:e=null,agentCmd:t=null,env:n=process.env}={}){let r=String(t||(e?n[qF[e]]||WF[e]:"")||"").trim();if(!r)return null;let{realClaude:o,realCodex:s,realCursor:a}=dc({agent:e,agentCmd:r});if(!(o||s||a))return null;let i=await zF(r.split(/\s+/)[0],{env:n});if(!i)return null;let c=`${i.path}@${i.mtimeMs}`;return Zh.has(c)||Zh.set(c,Promise.resolve().then(()=>KF(i.path,{agent:e})).then(l=>VF(l)).catch(()=>null)),Zh.get(c)}var UF,HF,WF,qF,Zh,GF,KF,BT=G(()=>{os();UF=FF(jF),HF=8e3,WF={claude:"claude",codex:"codex",cursor:"cursor-agent"},qF={claude:"BOSTRAT_AGENT_CMD",codex:"BOSTRAT_CODEX_CMD",cursor:"BOSTRAT_CURSOR_CMD"},Zh=new Map,GF=async e=>(await UF(e,["--version"],{timeout:HF})).stdout,KF=GF});function Ia(e){return typeof e=="string"?e.trim():""}var LT=G(()=>{});import{execFile as JF}from"node:child_process";import{promisify as YF}from"node:util";async function Ye(e,t,{input:n}={}){let r=ew("git",["-C",e,...t],{maxBuffer:jT});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function $a(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 FT(e){let{stdout:t}=await Ye(e,["rev-parse","--show-toplevel"]);return t.trim()}async function XF(e){try{return await Ye(e,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function Ra(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)=>ew("git",["-C",e,...a],{maxBuffer:jT,env:o,...i});try{let a=await XF(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 ew("rm",["-f",r])}catch{}}}async function tw(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 ps(e,{ignorePlumbing:t=!1}={}){let{stdout:n}=await Ye(e,["status","--porcelain"]),r=n.split(`
|
|
183
183
|
`).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 fs(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 ms(e,t,n,r){return await Ye(e,["worktree","add","--no-track","-b",n,t,r]),t}async function nw(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 hs(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 rw(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 $t(e,t){try{await Ye(e,["worktree","remove","--force",t])}catch{try{await Ye(e,["worktree","prune"])}catch{}}}function ZF(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 ow(e,t){return!t||!t.length?e:[...e,"--",".",...t.map(n=>`:(exclude)${n}`)]}async function sw(e,t,n,r){let{stdout:o}=await Ye(e,ow(["diff","--shortstat",t,n],r));return ZF(o)}async function aw(e,t,n,r){let{stdout:o}=await Ye(e,ow(["diff","--name-only",t,n],r));return o.split(`
|
|
184
184
|
`).map(s=>s.trim()).filter(Boolean)}async function iw(e,t,n,r){let{stdout:o}=await Ye(e,ow(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await Qh(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await Qh(e,o,["--3way","--whitespace=nowarn"]),a=await QF(e);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await Qh(e,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function Qh(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 QF(e){try{let{stdout:t}=await Ye(e,["diff","--name-only","--diff-filter=U"]);return t.split(`
|
|
185
|
-
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var ew,
|
|
186
|
-
`),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
|
|
185
|
+
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var ew,jT,Md=G(()=>{ew=YF(JF),jT=64*1024*1024});function Cd(e,{at:t=Date.now(),reason:n="node-transfer"}={}){let r=!1;for(let o of e?.boxes||[]){if(!o||typeof o!="object")continue;!eU.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 Ue,eU,an=G(()=>{Ue=e=>!!(e&&(e.archivedAt||e.transferredTo)),eU=new Set(["merged","conflict","canceled","failed","handed-off"])});import cw from"node:fs/promises";import HT from"node:path";import tU from"node:os";function KT(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function VT(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function zT(e){let t=new Date(KT(e));return t.setDate(t.getDate()+7),t.getTime()}function JT(e){let t=new Date(VT(e));return t.setMonth(t.getMonth()+1),t.getTime()}function Pd(e=Date.now()){for(let[t,n]of Object.entries(Oa)){let r=n.startOf(e);if(r!==br[t]){br[t]=r;for(let o of ws.values())o[t]=0;for(let o of Ma)o.includes(`:${t}:`)&&!o.endsWith(`:${r}`)&&Ma.delete(o)}}}function YT(e,t){let n=An?.caps?.[e]?.[Oa[t].capKey];return Number.isFinite(n)&&n>0?n:null}function mo(e,t=Date.now()){if(!e||!uw||!An?.caps)return null;Pd(t);let n=ws.get(e);for(let r of Object.keys(Oa)){let o=YT(e,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:e,window:r,limitUsd:o,spentUsd:Nd(s),resetsAt:Oa[r].nextStart(t)}}return null}function XT(e,t){let n=`hit:${e}:${t.provider}:${t.window}:${br[t.window]}`;return Ma.has(n)?!1:(Ma.add(n),!0)}function UT(e,t,n,r=Date.now()){if(!e||!t)return;Pd(r);let o=nU(e);for(let[s,a]of Object.entries(Oa))n>=br[s]&&n<a.nextStart(r)&&(o[s]+=t)}async function rU(e,t,n=Date.now()){if(!An?.caps||!lw)return;let r=ws.get(e);if(r)for(let[o,s]of Object.entries(Oa)){let a=YT(e,o);if(a==null)continue;let i=r[o]||0;if(i<a*GT||i>=a)continue;let c=`warn:${e}:${o}:${br[o]}`;if(!Ma.has(c)){Ma.add(c);try{let l=await lw(t);if(!l)continue;await N(l,{type:"usage.cap.warning",actor:"system",provider:e,payload:{provider:e,window:o,limitUsd:a,spentUsd:Nd(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function ZT(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 Dd(e,{source:t="local",updatedAt:n=Date.now()}={}){An={caps:ZT(e),updatedAt:n,source:t};try{await cw.mkdir(WT,{recursive:!0}),await cw.writeFile(qT(),JSON.stringify(An,null,2))}catch{}return _c()}function _c(e=Date.now()){Pd(e);let t=new Set([...ws.keys(),...Object.keys(An?.caps||{})]),n={};for(let r of t){let o=ws.get(r);n[r]={weeklyUsd:Nd(o?.weekly||0),monthlyUsd:Nd(o?.monthly||0)}}return{caps:An?.caps||{},configured:!!An,seeded:uw,updatedAt:An?.updatedAt??null,spent:n,windows:{weekly:{start:br.weekly,resetsAt:zT(e)},monthly:{start:br.monthly,resetsAt:JT(e)}},warnPct:GT}}async function QT({listTracks:e,getTrack:t}={}){lw=t||null,Pd();try{let o=JSON.parse(await cw.readFile(qT(),"utf8"));An={caps:ZT(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{An=null}let n=Date.now();Ze.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=sn(o);UT(o.provider||"unknown",s,o.ts??Date.now()),rU(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(br.weekly,br.monthly);for(let o of await e?.()||[]){let s;try{s=await K(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||UT(a.provider||"unknown",sn(a).usd,i,n)}}return uw=!0,_c()}var WT,qT,GT,Oa,An,uw,br,ws,Ma,lw,nU,Nd,Bd=G(()=>{be();fo();WT=process.env.BOSTRAT_STATE_DIR||HT.join(process.env.HOME||tU.tmpdir(),".bostrat"),qT=()=>HT.join(WT,"usage-caps.json"),GT=.8;Oa={weekly:{startOf:KT,nextStart:zT,capKey:"weeklyUsd"},monthly:{startOf:VT,nextStart:JT,capKey:"monthlyUsd"}},An=null,uw=!1,br={weekly:0,monthly:0},ws=new Map,Ma=new Set,lw=null,nU=e=>{let t=ws.get(e);return t||(t={weekly:0,monthly:0},ws.set(e,t)),t};Nd=e=>Math.round(e*1e4)/1e4});import oU from"node:fs/promises";import sU from"node:os";import eA from"node:path";function dw(e){return e==="codex"?lU:e==="cursor"?aA:oA}function tA(e){let t=String(e||"").split(`
|
|
186
|
+
`),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 nA(e){let t=String(e||"").split(`
|
|
187
187
|
`),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 uU(e){let t=String(e||"").split(`
|
|
188
|
-
`);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
|
|
189
|
-
`),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 sA(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&&!rA.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=oA.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!cU.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function Ic(e,t,{timeoutMs:n=aU}={}){let r=Date.now()+n;for(;;){let o=await Ne(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await Bd(iU)}}async function dU(e,t){return await Qe(e,`/model ${t.model}`,Un("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function pU(e,t){await Hu(e),await Qe(e,"/model",{enter:!1}),await Bd(250),await Ke(e,["Enter"],{kind:"model-switch"});let n=await Ic(e,QT);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await Ke(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${t.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=uU(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await Qe(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await Ic(e,i=>eA(i)||!QT(i)&&o(i)&&{done:o(i)});if(!s)throw await Ke(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?rA.get(t.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await Ke(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await Qe(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Ic(e,i=>!eA(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 fU(){let e=ZT.join(process.env.CURSOR_CONFIG_DIR||ZT.join(sU.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await oU.readFile(e,"utf8"))?.model||null}catch{return null}}async function mU(e,t){await Hu(e),await qu(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await Ic(e,tA),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 Ic(e,tA,{timeoutMs:1500});if(s=l?.rows.find(u=>u.active)||null,s&&o(s))break;if(!l||!l.rows.some(o)){s=null;break}await Ke(e,["Down"],{kind:"model-switch"}),await Bd(250)}if(!n||!s||!o(s)){await Ke(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await Ke(e,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${t.filter}"`:"cursor model picker didn't open")}await Ke(e,["Enter"],{kind:"model-switch"}),await Bd(800);let a=await fU();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 aA(e,t,n){return e==="codex"?pU(t,n):e==="cursor"?mU(t,n):dU(t,n)}var Bd,aU,iU,nA,cU,lU,rA,oA,iA=G(()=>{dt();pc();Bd=e=>new Promise(t=>setTimeout(t,e)),aU=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,iU=300,nA=[{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"}],cU=new Set([...nA.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"]),lU=[{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"}],rA=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),oA=[{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 Fd(e){let t=e||{},n=hU(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function hU(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:lt(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:lt(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:lt(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:lt(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,...e.standing?{standing:e.standing}:{}}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(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:lt(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:lt(e.agent),payload:{...qn(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:lt(e.agent||e.toAgent),payload:{kind:"subtask-failed",...qn(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{change:"subtask-resumed",...qn(e)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"model",model:e.to,from:e.from??null,verified:e.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:"codex",payload:{change:"cli-update",from:e.from??null,to:e.to??null,status:e.status,...e.error?{error:e.error}:{}}};case"node-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",payload:{change:"node-update",...qn(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...qn(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...qn(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(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:lt(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,...e.standing?{standing:e.standing}:{}}};case"transferred-in":return{type:e.mode==="recover"?"track.recovered":"track.transferred",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{...qn(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...qn(e)}};case"retag-failed":return{type:"error",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(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:lt(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};case"delivery-held":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{kind:"delivery-held",...qn(e)}};case"startup-gate":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{kind:"startup-gate",...qn(e)}};default:return null}}function uA(e,t){let n=Fd(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;Ud(()=>N(r,n))}async function dA(e){if(!(!e?.id||cA.has(e.id))){cA.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=Fd(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await N(e,o)}}catch{}}}function qn(e){let{type:t,at:n,...r}=e;return r}function pA(e,t,n){!n?.promptId||jd.get(e.id)===n.promptId||(jd.set(e.id,n.promptId),Ud(()=>N(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:lt(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 fA(e,t,n,r={}){jd.delete(e.id),Ud(()=>N(e,{type:"prompt.dismissed",actor:"user",source:"orchestrator",boxId:t?.name,provider:lt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}function pw(e,t,n,r={}){jd.delete(e.id),Ud(()=>N(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:lt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var lt,Ld,Ud,lA,cA,jd,fw=G(()=>{be();lt=e=>e||null;Ld=Promise.resolve(),Ud=e=>(Ld=Ld.then(e).catch(()=>{}),Ld),lA=()=>Ld;cA=new Set;jd=new Map});function kU(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function bA(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of kU(n).reverse()){if(!wU.test(r))continue;let o=r.endsWith("?")&&r.length<=bU&&yU.test(r);if(gU.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function xA(e){let t=String(e||""),n=[];xU.test(t)&&n.push("commit"),kA.test(t)&&n.push("rebase"),vU.test(t)&&n.push("push");let r=TU.test(t);!r&&SU.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 vA(e){return kA.test(String(e||""))}function Ca(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 SA(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 TA(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<=AU&&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+EU||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function EA(e){let t=(Array.isArray(e)?e:[]).filter(r=>mA[r]).map(r=>mA[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 hA,wA,gA,yA,wU,gU,yU,bU,ho,xU,kA,vU,SU,TU,AU,EU,mA,gs=G(()=>{hA=/(?<!\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,wA=/(?<!\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,gA=/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,yA=/(?<!\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,wU=new RegExp(`\\b(?:${hA}|${wA}|${gA}|${yA})\\b`,"i"),gU=/\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,yU=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,bU=90;ho=["commit","rebase","push","pr"],xU=new RegExp(`\\b(?:${hA})\\b`,"i"),kA=new RegExp(`\\b(?:${yA})\\b`,"i"),vU=new RegExp(`\\b(?:${wA})\\b`,"i"),SU=new RegExp(`\\b(?:${gA})\\b`,"i"),TU=/\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;AU=3e4;EU=2500;mA={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import _U from"node:crypto";import Hd from"node:fs/promises";import _A from"node:path";import IU from"node:os";function OA(e){RA=typeof e=="function"?e:async()=>new Set}function $U(e,t,n){if(e.originNode&&e.originNode===t)return!0;let r=e.data?.trackId;return!!r&&n.has(r)}async function We(){return kr||(mw??=Hd.readFile(ww,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(kr=e,!t&&Ge()){let{records:n,source:r}=await pt("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){let o=ux(),s=await RA().catch(()=>new Set),a=0;for(let i of n){if(i.deletedAt||!i.data?.repo||i.data?.number==null)continue;if(!$U(i,o,s)){a++;continue}let{id:c,updatedAt:l,...u}=i.data;kr[ce(u.repo,u.number)]=u}a&&console.log(`[pr-links] restore: ${Object.keys(kr).length} link(s) re-seeded, ${a} other-node record(s) left on the plane`),Object.keys(kr).length&&await gw().catch(()=>{})}}return kr}).finally(()=>mw=null),mw)}async function gw(){let e=JSON.stringify({links:kr},null,2),t=`${ww}.${process.pid}.tmp`;await Hd.mkdir(IA,{recursive:!0}),await Hd.writeFile(t,e),await Hd.rename(t,ww)}async function et(e){hw=hw.then(gw,gw),await hw.catch(()=>{});let t=kr?.[e];t&&It("prlinks",{id:$A(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function Yt({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 We(),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:RU.has(a),firstSeen:Date.now()},await et(ce(e,t)),{link:i[c],created:!0})}async function xr(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return!n||n.announced?null:(n.announced=!0,await et(ce(e,t)),n)}async function MA(e,t){if(!e||t==null)return!1;let n=(await We())[ce(e,t)];return n?.notifyOnMerge?(delete n.notifyOnMerge,await et(ce(e,t)),!0):!1}async function ft(e,t){return!e||t==null?null:(await We())[ce(e,t)]??null}async function wo(e,t,n){if(!e||t==null)return null;let r=(await We())[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 CA(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await We())[ce(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await et(ce(e,t)),o):null}async function NA(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await We())[ce(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await et(ce(e,t)),o):null}async function Wd(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await We())[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 qd(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await et(ce(e,t)),n)}async function Gd(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await We())[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 Kd(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await et(ce(e,t)),n)}async function PA(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return r?(r.baseFoldAt=n,await et(ce(e,t)),r):null}async function DA(e,t,{misses:n,until:r}){if(!e||t==null)return null;let o=(await We())[ce(e,t)];return o?(o.brokerRefused={misses:Number(n)||0,until:Number(r)||0},await et(ce(e,t)),o):null}async function BA(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return n?.brokerRefused?(delete n.brokerRefused,await et(ce(e,t)),n):null}async function LA(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await et(ce(e,t)),n)}async function jA(e,t,n){if(!e||t==null||!n)return null;let r=(await We())[ce(e,t)];return r?(r.lastHeadSha=n,await et(ce(e,t)),r):null}async function FA(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await We())[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 $c(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await We())[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 ys(e,t,n=!0){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await et(ce(e,t)),r):null}async function bs(e,t,n=!0){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await et(ce(e,t)),r):null}async function UA(e,t,n){if(!e||t==null)return null;let r=(await We())[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 HA(e,t,n){if(!e||t==null)return null;let r=(await We())[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 Rc(e,t){if(!e||t==null)return null;let n=(await We())[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,delete n.verdict,await et(ce(e,t)),n)}async function WA(){return Object.values(await We()).filter(e=>!OU.has(e.state))}async function En(){return Object.values(await We()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function qA(){if(!Ge())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await pt("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await We(),r=0;for(let[o,s]of Object.entries(n))await It("prlinks",{id:$A(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var IA,ww,ce,$A,kr,mw,hw,RA,RU,OU,Gn=G(()=>{Yr();IA=process.env.BOSTRAT_STATE_DIR||_A.join(process.env.HOME||IU.tmpdir(),".bostrat"),ww=_A.join(IA,"pr-links.json"),ce=(e,t)=>`${e}#${Number(t)}`,$A=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=_U.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},kr=null,mw=null,hw=Promise.resolve(),RA=async()=>new Set;RU=new Set(["api","observed","reconciled"]);OU=new Set(["merged","closed","lost"])});import MU from"node:fs";import yw from"node:fs/promises";import zA from"node:path";import CU from"node:os";function XA(e,t=!1){if(!t&&e-KA<NU)return;KA=e;let n=JSON.stringify({windowStart:Na,byCaller:Object.fromEntries(vr),lastHour:go});VA=VA.then(()=>yw.mkdir(JA,{recursive:!0})).then(()=>yw.writeFile(`${Vd}.tmp`,n)).then(()=>yw.rename(`${Vd}.tmp`,Vd)).catch(()=>{})}function ZA(e){e-Na<YA||(go={start:Na,total:zd(vr),byCaller:Object.fromEntries(vr)},go.total&&console.log(`[github] ${go.total} calls last hour`,go.byCaller),Na=e,vr=new Map,XA(e,!0))}function Sr(e="other",t=Date.now()){ZA(t),vr.set(e,(vr.get(e)||0)+1),XA(t)}function QA(e=Date.now()){return ZA(e),{windowStart:Na,total:zd(vr),byCaller:Object.fromEntries(vr),lastHour:go,backoffUntil:ks>e?ks:null}}function yo(e){return PU.test(String(e?.message||e||""))}function bo(e,t=Date.now()){return ks<=t&&console.warn(`[github] rate limited \u2014 backing off ${GA/6e4} min:`,String(e?.message||e||"").slice(0,200)),ks=Math.max(ks,t+GA),ks}function Jd(e=Date.now()){return Math.max(0,ks-e)}var JA,Vd,YA,GA,NU,Na,vr,go,ks,KA,zd,VA,PU,Oc=G(()=>{JA=process.env.BOSTRAT_STATE_DIR||zA.join(process.env.HOME||CU.tmpdir(),".bostrat"),Vd=zA.join(JA,"github-metrics.json"),YA=60*6e4,GA=5*6e4,NU=6e4,Na=Date.now(),vr=new Map,go=null,ks=0,KA=0,zd=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(MU.readFileSync(Vd,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<YA?(Na=e.windowStart,vr=new Map(Object.entries(e.byCaller||{})),go=e.lastHour||null):zd(new Map(Object.entries(e.byCaller||{})))&&(go={start:e.windowStart,total:zd(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}VA=Promise.resolve();PU=/rate limit|too quickly|abuse detection|HTTP 429/i});import eE from"node:fs/promises";import tE from"node:path";import DU from"node:os";function Xd({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};Yd.push(l),Yd.length>LU&&Yd.shift();let u=bw.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)),bw.set(e,u),jU(l),l}async function jU(e){try{await eE.mkdir(nE,{recursive:!0}),await eE.appendFile(BU,JSON.stringify(e)+`
|
|
190
|
-
`)}catch{}}function rE(){let e={};for(let[t,n]of bw)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:Yd.slice(-20)}}var nE,BU,LU,Yd,bw,Zd=G(()=>{nE=process.env.BOSTRAT_STATE_DIR||tE.join(process.env.HOME||DU.tmpdir(),".bostrat"),BU=tE.join(nE,"github-latency.ndjson"),LU=100,Yd=[],bw=new Map});import{execFile as FU}from"node:child_process";import{promisify as UU}from"node:util";async function HU(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Da("git",["-C",e,"rev-parse","HEAD"]),Da("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await N({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function cn(e,t,n={}){let{caller:r="prs",...o}=n,s=await Bn({cwd:e});Sr(r);try{let{stdout:a}=await Da("gh",t,{cwd:e,...WU,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw yo(a)&&bo(a),a}}async function vw(){if(Qd&&Date.now()-Qd.at<6e4)return Qd.data;let e;try{Sr("auth");let t=await Da("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 Li();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return Qd={at:Date.now(),data:e},e}async function qU(e){try{let{stdout:t}=await Da("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function sE(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 kw(e){let t=(e||[]).map(sE),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 Tw(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function iE(e,t){if(!t)return null;let n=await Me(e);if(!n)return null;try{let r=await cn(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 cE(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 lE(e,t){let n=await iE(e,t);return n===null?{...kw([]),unavailable:!0}:kw(cE(n))}function ep(e,t=kw(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 KU(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)<GU}for(let o of Pa.keys())o.startsWith(`${e}#`)&&!r.has(o)&&Pa.delete(o);return t}async function Ba(e,t){let n=await Tr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await cn(n.path,["pr","view",String(r),"--json",Sw]);return ep(JSON.parse(o))}catch(o){if(!Tw(o))throw o;let s=await cn(n.path,["pr","view",String(r),"--json",aE]),a=JSON.parse(s);return ep(a,await lE(n.path,a.headRefOid))}}async function Kn(e,{caller:t="prs"}={}){try{let n=await cn(e,["pr","list","--json",Sw,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>ep(r)).sort(oE)}catch(n){if(!Tw(n))throw n;let r=await cn(e,["pr","list","--json",aE,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>ep(o,await lE(e,o.headRefOid))))).sort(oE)}}function ln(){Mc.clear()}async function JU(e,t){if(!e)return t;for(let n of t){let r=await ft(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 Wd(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Gd(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function uE(){let e=await vw();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await fr())await qU(a.path)&&t.push(a);let n=Date.now(),r=Jd(n)>0,o=await Promise.all(t.map(async a=>{let i=Mc.get(a.path),c=i?.pending?zU:VU;if(i&&(r||n-i.at<c))return i.entry;try{let l=await Me(a.path).catch(()=>null),u=KU(a.path,await Kn(a.path,{caller:"tab"}),n);if(await JU(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&&Xd({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 Mc.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:Vn(l)}}})),s=new Set(t.map(a=>a.path));for(let a of Mc.keys())s.has(a)||Mc.delete(a);return{auth:e,repos:o}}async function Tr(e){let t=(await fr()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function xs(e){return ea(e)}async function La(e,t,{method:n="squash"}={}){let r=await Tr(e);if(!Cc.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 Bn({cwd:r.path})).GH_TOKEN){let a=await vm().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await cn(r.path,s)}catch(a){throw new Error(Vn(a))}finally{ln()}return{ok:!0,number:o}}async function Nc(e,t){let n=await Tr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await cn(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Vn(s))}finally{ln()}let o=await Me(n.path).catch(()=>null);return o&&await PA(o,r).catch(()=>{}),{ok:!0,number:r}}async function Aw(e,t){let n=await Tr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await cn(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Vn(o))}finally{ln()}return{ok:!0,number:r}}function Ew(e){return e.green&&!e.isDraft}function QU(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 dE(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:Ew(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function pE(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function Pc(e,t){let n=a=>a?.firstSeen!=null&&Number.isFinite(Number(a.firstSeen))?Number(a.firstSeen):Number.MAX_SAFE_INTEGER,r=n(e),o=n(t);if(r!==o)return r-o;let s=String(e?.repo||"").localeCompare(String(t?.repo||""));return s||Number(e?.number||0)-Number(t?.number||0)}function fE(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(Pc))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function mE(){let e={};for(let[t,n]of xw)e[t]={...n};return e}async function hE(e,{method:t="merge",onMerged:n=null}={}){let r=await Tr(e);if(!Cc.has(t))throw new Error(`invalid merge method: ${t}`);if(xw.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Kn(r.path)).filter(Ew).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 xw.set(r.path,s),t4(s,o,{listPrs:()=>Kn(r.path),merge:a=>La(r.path,a,{method:t}),sleep:ZU,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function e4(e,t){let n=Date.now()+(t.settleMaxMs??XU),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(YU)}}async function t4(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 e4(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: ${Vn(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(!Ew(a)){e.skipped.push({number:a.number,title:a.title,reason:QU(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:Vn(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),ln()}}async function _w(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Da("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Vn(l)}`)}await HU(e);let o=n||"";if((await Bn({cwd:e})).GH_TOKEN){let l=await vm().catch(()=>null);l&&(o=`${o?`${o}
|
|
188
|
+
`);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 rA(e){let t=String(e||"").split(`
|
|
189
|
+
`),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 iA(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&&!sA.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=aA.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!cU.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function Ic(e,t,{timeoutMs:n=aU}={}){let r=Date.now()+n;for(;;){let o=await Ne(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await Ld(iU)}}async function dU(e,t){return await Qe(e,`/model ${t.model}`,Un("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function pU(e,t){await Wu(e),await Qe(e,"/model",{enter:!1}),await Ld(250),await Ke(e,["Enter"],{kind:"model-switch"});let n=await Ic(e,tA);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await Ke(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${t.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=uU(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await Qe(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await Ic(e,i=>nA(i)||!tA(i)&&o(i)&&{done:o(i)});if(!s)throw await Ke(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?sA.get(t.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await Ke(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await Qe(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Ic(e,i=>!nA(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 fU(){let e=eA.join(process.env.CURSOR_CONFIG_DIR||eA.join(sU.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await oU.readFile(e,"utf8"))?.model||null}catch{return null}}async function mU(e,t){await Wu(e),await Gu(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await Ic(e,rA),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 Ic(e,rA,{timeoutMs:1500});if(s=l?.rows.find(u=>u.active)||null,s&&o(s))break;if(!l||!l.rows.some(o)){s=null;break}await Ke(e,["Down"],{kind:"model-switch"}),await Ld(250)}if(!n||!s||!o(s)){await Ke(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await Ke(e,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${t.filter}"`:"cursor model picker didn't open")}await Ke(e,["Enter"],{kind:"model-switch"}),await Ld(800);let a=await fU();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 cA(e,t,n){return e==="codex"?pU(t,n):e==="cursor"?mU(t,n):dU(t,n)}var Ld,aU,iU,oA,cU,lU,sA,aA,lA=G(()=>{dt();pc();Ld=e=>new Promise(t=>setTimeout(t,e)),aU=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,iU=300,oA=[{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"}],cU=new Set([...oA.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"]),lU=[{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"}],sA=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),aA=[{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 Ud(e){let t=e||{},n=hU(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function hU(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:lt(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:lt(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:lt(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:lt(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,...e.standing?{standing:e.standing}:{}}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(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:lt(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:lt(e.agent),payload:{...qn(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:lt(e.agent||e.toAgent),payload:{kind:"subtask-failed",...qn(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{change:"subtask-resumed",...qn(e)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"model",model:e.to,from:e.from??null,verified:e.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:"codex",payload:{change:"cli-update",from:e.from??null,to:e.to??null,status:e.status,...e.error?{error:e.error}:{}}};case"node-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",payload:{change:"node-update",...qn(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...qn(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...qn(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(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:lt(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,...e.standing?{standing:e.standing}:{}}};case"transferred-in":return{type:e.mode==="recover"?"track.recovered":"track.transferred",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{...qn(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...qn(e)}};case"retag-failed":return{type:"error",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(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:lt(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};case"delivery-held":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{kind:"delivery-held",...qn(e)}};case"startup-gate":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{kind:"startup-gate",...qn(e)}};default:return null}}function pA(e,t){let n=Ud(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;Hd(()=>N(r,n))}async function fA(e){if(!(!e?.id||uA.has(e.id))){uA.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=Ud(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await N(e,o)}}catch{}}}function qn(e){let{type:t,at:n,...r}=e;return r}function mA(e,t,n){!n?.promptId||Fd.get(e.id)===n.promptId||(Fd.set(e.id,n.promptId),Hd(()=>N(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:lt(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 hA(e,t,n,r={}){Fd.delete(e.id),Hd(()=>N(e,{type:"prompt.dismissed",actor:"user",source:"orchestrator",boxId:t?.name,provider:lt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}function pw(e,t,n,r={}){Fd.delete(e.id),Hd(()=>N(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:lt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var lt,jd,Hd,dA,uA,Fd,fw=G(()=>{be();lt=e=>e||null;jd=Promise.resolve(),Hd=e=>(jd=jd.then(e).catch(()=>{}),jd),dA=()=>jd;uA=new Set;Fd=new Map});function kU(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function xA(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of kU(n).reverse()){if(!wU.test(r))continue;let o=r.endsWith("?")&&r.length<=bU&&yU.test(r);if(gU.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function SA(e){let t=String(e||""),n=[];xU.test(t)&&n.push("commit"),vA.test(t)&&n.push("rebase"),vU.test(t)&&n.push("push");let r=TU.test(t);!r&&SU.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 TA(e){return vA.test(String(e||""))}function Ca(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 AA(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 EA(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<=AU&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function _A(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+EU||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function IA(e){let t=(Array.isArray(e)?e:[]).filter(r=>wA[r]).map(r=>wA[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 gA,yA,bA,kA,wU,gU,yU,bU,ho,xU,vA,vU,SU,TU,AU,EU,wA,gs=G(()=>{gA=/(?<!\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,yA=/(?<!\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,bA=/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,kA=/(?<!\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,wU=new RegExp(`\\b(?:${gA}|${yA}|${bA}|${kA})\\b`,"i"),gU=/\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,yU=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,bU=90;ho=["commit","rebase","push","pr"],xU=new RegExp(`\\b(?:${gA})\\b`,"i"),vA=new RegExp(`\\b(?:${kA})\\b`,"i"),vU=new RegExp(`\\b(?:${yA})\\b`,"i"),SU=new RegExp(`\\b(?:${bA})\\b`,"i"),TU=/\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;AU=3e4;EU=2500;wA={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import _U from"node:crypto";import Wd from"node:fs/promises";import $A from"node:path";import IU from"node:os";function CA(e){MA=typeof e=="function"?e:async()=>new Set}function $U(e,t,n){if(e.originNode&&e.originNode===t)return!0;let r=e.data?.trackId;return!!r&&n.has(r)}async function We(){return kr||(mw??=Wd.readFile(ww,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(kr=e,!t&&Ge()){let{records:n,source:r}=await pt("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){let o=px(),s=await MA().catch(()=>new Set),a=0;for(let i of n){if(i.deletedAt||!i.data?.repo||i.data?.number==null)continue;if(!$U(i,o,s)){a++;continue}let{id:c,updatedAt:l,...u}=i.data;kr[ce(u.repo,u.number)]=u}a&&console.log(`[pr-links] restore: ${Object.keys(kr).length} link(s) re-seeded, ${a} other-node record(s) left on the plane`),Object.keys(kr).length&&await gw().catch(()=>{})}}return kr}).finally(()=>mw=null),mw)}async function gw(){let e=JSON.stringify({links:kr},null,2),t=`${ww}.${process.pid}.tmp`;await Wd.mkdir(RA,{recursive:!0}),await Wd.writeFile(t,e),await Wd.rename(t,ww)}async function et(e){hw=hw.then(gw,gw),await hw.catch(()=>{});let t=kr?.[e];t&&It("prlinks",{id:OA(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function Yt({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 We(),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:RU.has(a),firstSeen:Date.now()},await et(ce(e,t)),{link:i[c],created:!0})}async function xr(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return!n||n.announced?null:(n.announced=!0,await et(ce(e,t)),n)}async function NA(e,t){if(!e||t==null)return!1;let n=(await We())[ce(e,t)];return n?.notifyOnMerge?(delete n.notifyOnMerge,await et(ce(e,t)),!0):!1}async function ft(e,t){return!e||t==null?null:(await We())[ce(e,t)]??null}async function wo(e,t,n){if(!e||t==null)return null;let r=(await We())[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 PA(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await We())[ce(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await et(ce(e,t)),o):null}async function DA(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await We())[ce(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await et(ce(e,t)),o):null}async function qd(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await We())[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 Gd(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await et(ce(e,t)),n)}async function Kd(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await We())[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 Vd(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await et(ce(e,t)),n)}async function BA(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return r?(r.baseFoldAt=n,await et(ce(e,t)),r):null}async function LA(e,t,{misses:n,until:r}){if(!e||t==null)return null;let o=(await We())[ce(e,t)];return o?(o.brokerRefused={misses:Number(n)||0,until:Number(r)||0},await et(ce(e,t)),o):null}async function jA(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return n?.brokerRefused?(delete n.brokerRefused,await et(ce(e,t)),n):null}async function FA(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await et(ce(e,t)),n)}async function UA(e,t,n){if(!e||t==null||!n)return null;let r=(await We())[ce(e,t)];return r?(r.lastHeadSha=n,await et(ce(e,t)),r):null}async function HA(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await We())[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 $c(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await We())[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 ys(e,t,n=!0){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await et(ce(e,t)),r):null}async function bs(e,t,n=!0){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await et(ce(e,t)),r):null}async function WA(e,t,n){if(!e||t==null)return null;let r=(await We())[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 qA(e,t,n){if(!e||t==null)return null;let r=(await We())[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 Rc(e,t){if(!e||t==null)return null;let n=(await We())[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,delete n.verdict,await et(ce(e,t)),n)}async function GA(){return Object.values(await We()).filter(e=>!OU.has(e.state))}async function En(){return Object.values(await We()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function KA(){if(!Ge())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await pt("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await We(),r=0;for(let[o,s]of Object.entries(n))await It("prlinks",{id:OA(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var RA,ww,ce,OA,kr,mw,hw,MA,RU,OU,Gn=G(()=>{Yr();RA=process.env.BOSTRAT_STATE_DIR||$A.join(process.env.HOME||IU.tmpdir(),".bostrat"),ww=$A.join(RA,"pr-links.json"),ce=(e,t)=>`${e}#${Number(t)}`,OA=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=_U.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},kr=null,mw=null,hw=Promise.resolve(),MA=async()=>new Set;RU=new Set(["api","observed","reconciled"]);OU=new Set(["merged","closed","lost"])});import MU from"node:fs";import yw from"node:fs/promises";import YA from"node:path";import CU from"node:os";function QA(e,t=!1){if(!t&&e-zA<NU)return;zA=e;let n=JSON.stringify({windowStart:Na,byCaller:Object.fromEntries(vr),lastHour:go});JA=JA.then(()=>yw.mkdir(XA,{recursive:!0})).then(()=>yw.writeFile(`${zd}.tmp`,n)).then(()=>yw.rename(`${zd}.tmp`,zd)).catch(()=>{})}function eE(e){e-Na<ZA||(go={start:Na,total:Jd(vr),byCaller:Object.fromEntries(vr)},go.total&&console.log(`[github] ${go.total} calls last hour`,go.byCaller),Na=e,vr=new Map,QA(e,!0))}function Sr(e="other",t=Date.now()){eE(t),vr.set(e,(vr.get(e)||0)+1),QA(t)}function tE(e=Date.now()){return eE(e),{windowStart:Na,total:Jd(vr),byCaller:Object.fromEntries(vr),lastHour:go,backoffUntil:ks>e?ks:null}}function yo(e){return PU.test(String(e?.message||e||""))}function bo(e,t=Date.now()){return ks<=t&&console.warn(`[github] rate limited \u2014 backing off ${VA/6e4} min:`,String(e?.message||e||"").slice(0,200)),ks=Math.max(ks,t+VA),ks}function Yd(e=Date.now()){return Math.max(0,ks-e)}var XA,zd,ZA,VA,NU,Na,vr,go,ks,zA,Jd,JA,PU,Oc=G(()=>{XA=process.env.BOSTRAT_STATE_DIR||YA.join(process.env.HOME||CU.tmpdir(),".bostrat"),zd=YA.join(XA,"github-metrics.json"),ZA=60*6e4,VA=5*6e4,NU=6e4,Na=Date.now(),vr=new Map,go=null,ks=0,zA=0,Jd=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(MU.readFileSync(zd,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<ZA?(Na=e.windowStart,vr=new Map(Object.entries(e.byCaller||{})),go=e.lastHour||null):Jd(new Map(Object.entries(e.byCaller||{})))&&(go={start:e.windowStart,total:Jd(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}JA=Promise.resolve();PU=/rate limit|too quickly|abuse detection|HTTP 429/i});import nE from"node:fs/promises";import rE from"node:path";import DU from"node:os";function Zd({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};Xd.push(l),Xd.length>LU&&Xd.shift();let u=bw.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)),bw.set(e,u),jU(l),l}async function jU(e){try{await nE.mkdir(oE,{recursive:!0}),await nE.appendFile(BU,JSON.stringify(e)+`
|
|
190
|
+
`)}catch{}}function sE(){let e={};for(let[t,n]of bw)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:Xd.slice(-20)}}var oE,BU,LU,Xd,bw,Qd=G(()=>{oE=process.env.BOSTRAT_STATE_DIR||rE.join(process.env.HOME||DU.tmpdir(),".bostrat"),BU=rE.join(oE,"github-latency.ndjson"),LU=100,Xd=[],bw=new Map});import{execFile as FU}from"node:child_process";import{promisify as UU}from"node:util";async function HU(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Da("git",["-C",e,"rev-parse","HEAD"]),Da("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await N({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function cn(e,t,n={}){let{caller:r="prs",...o}=n,s=await Bn({cwd:e});Sr(r);try{let{stdout:a}=await Da("gh",t,{cwd:e,...WU,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw yo(a)&&bo(a),a}}async function vw(){if(ep&&Date.now()-ep.at<6e4)return ep.data;let e;try{Sr("auth");let t=await Da("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 Li();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return ep={at:Date.now(),data:e},e}async function qU(e){try{let{stdout:t}=await Da("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function iE(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 kw(e){let t=(e||[]).map(iE),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 Tw(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function lE(e,t){if(!t)return null;let n=await Me(e);if(!n)return null;try{let r=await cn(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 uE(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 dE(e,t){let n=await lE(e,t);return n===null?{...kw([]),unavailable:!0}:kw(uE(n))}function tp(e,t=kw(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 KU(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)<GU}for(let o of Pa.keys())o.startsWith(`${e}#`)&&!r.has(o)&&Pa.delete(o);return t}async function Ba(e,t){let n=await Tr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await cn(n.path,["pr","view",String(r),"--json",Sw]);return tp(JSON.parse(o))}catch(o){if(!Tw(o))throw o;let s=await cn(n.path,["pr","view",String(r),"--json",cE]),a=JSON.parse(s);return tp(a,await dE(n.path,a.headRefOid))}}async function Kn(e,{caller:t="prs"}={}){try{let n=await cn(e,["pr","list","--json",Sw,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>tp(r)).sort(aE)}catch(n){if(!Tw(n))throw n;let r=await cn(e,["pr","list","--json",cE,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>tp(o,await dE(e,o.headRefOid))))).sort(aE)}}function ln(){Mc.clear()}async function JU(e,t){if(!e)return t;for(let n of t){let r=await ft(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 qd(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Kd(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function pE(){let e=await vw();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await fr())await qU(a.path)&&t.push(a);let n=Date.now(),r=Yd(n)>0,o=await Promise.all(t.map(async a=>{let i=Mc.get(a.path),c=i?.pending?zU:VU;if(i&&(r||n-i.at<c))return i.entry;try{let l=await Me(a.path).catch(()=>null),u=KU(a.path,await Kn(a.path,{caller:"tab"}),n);if(await JU(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&&Zd({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 Mc.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:Vn(l)}}})),s=new Set(t.map(a=>a.path));for(let a of Mc.keys())s.has(a)||Mc.delete(a);return{auth:e,repos:o}}async function Tr(e){let t=(await fr()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function xs(e){return ea(e)}async function La(e,t,{method:n="squash"}={}){let r=await Tr(e);if(!Cc.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 Bn({cwd:r.path})).GH_TOKEN){let a=await vm().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await cn(r.path,s)}catch(a){throw new Error(Vn(a))}finally{ln()}return{ok:!0,number:o}}async function Nc(e,t){let n=await Tr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await cn(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Vn(s))}finally{ln()}let o=await Me(n.path).catch(()=>null);return o&&await BA(o,r).catch(()=>{}),{ok:!0,number:r}}async function Aw(e,t){let n=await Tr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await cn(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Vn(o))}finally{ln()}return{ok:!0,number:r}}function Ew(e){return e.green&&!e.isDraft}function QU(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 fE(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:Ew(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function mE(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function Pc(e,t){let n=a=>a?.firstSeen!=null&&Number.isFinite(Number(a.firstSeen))?Number(a.firstSeen):Number.MAX_SAFE_INTEGER,r=n(e),o=n(t);if(r!==o)return r-o;let s=String(e?.repo||"").localeCompare(String(t?.repo||""));return s||Number(e?.number||0)-Number(t?.number||0)}function hE(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(Pc))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function wE(){let e={};for(let[t,n]of xw)e[t]={...n};return e}async function gE(e,{method:t="merge",onMerged:n=null}={}){let r=await Tr(e);if(!Cc.has(t))throw new Error(`invalid merge method: ${t}`);if(xw.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Kn(r.path)).filter(Ew).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 xw.set(r.path,s),t4(s,o,{listPrs:()=>Kn(r.path),merge:a=>La(r.path,a,{method:t}),sleep:ZU,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function e4(e,t){let n=Date.now()+(t.settleMaxMs??XU),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(YU)}}async function t4(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 e4(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: ${Vn(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(!Ew(a)){e.skipped.push({number:a.number,title:a.title,reason:QU(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:Vn(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),ln()}}async function _w(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Da("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Vn(l)}`)}await HU(e);let o=n||"";if((await Bn({cwd:e})).GH_TOKEN){let l=await vm().catch(()=>null);l&&(o=`${o?`${o}
|
|
191
191
|
|
|
192
|
-
`:""}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 cn(e,s)}catch(l){throw new Error(Vn(l))}finally{ln()}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
|
|
192
|
+
`:""}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 cn(e,s)}catch(l){throw new Error(Vn(l))}finally{ln()}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 yE(e,t){let n=await Tr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await cn(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(Vn(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!Tw(i))throw i;let c=await cn(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await lE(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(Vn(i));o=uE(l).map(iE).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=n4(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await cn(n.path,["run","view",c,"--log-failed"]).then(u=>r4(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function n4(e){return String(e||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function r4(e,{maxLines:t=400,maxBytes:n=64*1024}={}){let r=String(e||""),o=r.split(`
|
|
193
193
|
`);return o.length>t&&(r=`\u2026 (${o.length-t} earlier lines truncated)
|
|
194
194
|
`+o.slice(-t).join(`
|
|
195
195
|
`)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
|
|
196
196
|
`+r.slice(-n)),r}function Vn(e){return String(e?.stderr||e?.message||e||"").trim().split(`
|
|
197
|
-
`).find(n=>n.trim())||"gh failed"}var Da,WU,
|
|
198
|
-
`)){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===
|
|
199
|
-
`)){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 L4(e,t){let n=Mw(e),r=Mw(t);if(n===r)return 1;if(!n.length||!r.length)return 0;n.length>
|
|
200
|
-
`)){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
|
|
197
|
+
`).find(n=>n.trim())||"gh failed"}var Da,WU,ep,Sw,cE,GU,Pa,aE,VU,zU,Mc,Cc,YU,XU,ZU,xw,vs=G(()=>{Gr();vt();Gn();Oc();Qd();be();Da=UU(FU),WU={timeout:3e4,maxBuffer:8*1024*1024};ep=null;Sw="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt,state,mergedAt,closedAt",cE=Sw.replace("statusCheckRollup,","");GU=9e4,Pa=new Map;aE=(e,t)=>e.number-t.number;VU=3e4,zU=1500,Mc=new Map;Cc=new Set(["squash","merge","rebase"]);YU=2500,XU=9e4,ZU=e=>new Promise(t=>setTimeout(t,e));xw=new Map});import{execFile as o4}from"node:child_process";import{promisify as s4}from"node:util";async function i4(e,t){try{let{stdout:n}=await a4("git",["-C",e,"rev-list","--count",t]);return Number(n.trim())||0}catch{return null}}async function c4(e,t,n,{rebase:r=!1,listPrs:o=Kn}={}){let s=await ps(e.dir,{ignorePlumbing:!0}).catch(()=>!1),a=await fs(e.dir,n).catch(()=>0),i=t?await i4(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(h=>h.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:np.filter(w=>p.includes(w)),defaults:np.filter(w=>f.includes(w))}}async function rp(e,{branch:t,rebase:n=!1,listPrs:r=Kn}={}){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 $a(s[0].dir).catch(()=>!1))return null;let a=[];for(let p of s)a.push(await c4(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=np.filter(p=>c.some(f=>f.available.includes(p))),u=np.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 a4,np,Iw=G(()=>{Md();vs();a4=s4(o4),np=["commit","rebase","push","pr"]});import{execFile as l4}from"node:child_process";import{promisify as u4}from"node:util";async function ko(e){if(!e)return null;let t=bE.get(e);if(t&&Date.now()-t.at<p4)return t.branch;let n=null;try{let{stdout:r}=await d4("git",["-C",e,"symbolic-ref","--short","-q","HEAD"],{timeout:5e3});n=r.trim()||null}catch{n=null}return bE.set(e,{at:Date.now(),branch:n}),n}function kE(e){return String(e||"").match(f4)?.[1]??null}function xE(e,t){let n=kE(e);return!!n&&!!t&&n===t}function vE(e,t){let n=kE(e);return!!n&&n!==t}function m4(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 ja(e){let t=[],n=r=>{r&&!t.includes(r)&&t.push(r)};n(e?.workspace?.branch??null);for(let r of m4(e))n(await ko(r));return t}async function op(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=>xE(t,a.id));if(s)return s;for(let a of r)if(!Ue(a)&&(await ja(a)).includes(t))return a;return null}function h4(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 sp(e,{branch:t=null,repo:n=null,slug:r=null}={}){return xE(t,e?.id)?!0:h4(e,n,r)}var d4,p4,bE,f4,Ss=G(()=>{an();d4=u4(l4),p4=5e3,bE=new Map;f4=/^track\/.+-([0-9a-f]{8})$/});import{execFile as w4}from"node:child_process";import{promisify as g4}from"node:util";function Fa({trackId:e,boxId:t,epoch:n}){return`${e}/${t}/${Number.isFinite(Number(n))?Number(n):0}`}function ip(e){let t=/^([^/\s]+)\/([^/\s]+)\/(\d+)$/.exec(String(e||"").trim());return t?{trackId:t[1],boxId:t[2],epoch:Number(t[3])}:null}function lp({trackId:e,boxId:t,epoch:n=0,provider:r=null,model:o=null}){let s=[],a=r?S4[r]:null;return a&&s.push(a),s.push(`${_E}: ${Fa({trackId:e,boxId:t,epoch:n})}`),o&&s.push(`${IE}: ${String(o).trim()}`),s}function SE(e){let t={epoch:null,model:null,provider:null,cause:null};for(let n of String(e||"").split(`
|
|
198
|
+
`)){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===_E.toLowerCase()&&!t.epoch)t.epoch=ip(a);else if(s===IE.toLowerCase()&&!t.model)t.model=a;else if(s===$E.toLowerCase()&&!t.cause){let i=a.toLowerCase();Dc.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 T4(e,t=xo){return e<=t?"rework":"drift"}function A4(e,{window:t=xo,end:n=Cw}={}){return!Number.isFinite(e)||e<=t?1:e>=n?0:1-(e-t)/(n-t)}function up({subject:e="",trailerCause:t=null,wallReason:n=null,fixerCategory:r=null,ciFix:o=!1}={}){let s=String(e||"");return E4.test(s)?{cause:"defect",level:"A",basis:"revert"}:o?{cause:"defect",level:"A",basis:"ci-fix"}:t?{cause:t,level:"E",basis:`${$E} trailer`}:I4.test(s)&&!TE.test(s)?{cause:"style",level:"C",basis:"subject"}:TE.test(s)?{cause:"defect",level:"C",basis:"subject"}:_4.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 Pw({heuristic:e=null,subject:t="",ref:n=null,promptKind:r=null,fixerCategory:o=null,ticketKind:s=null}={}){let a=String(t||""),i=$4.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&&R4.has(c)?{intent:"fix",basis:`subject prefix: ${c}:`}:c&&O4.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"}:AE.test(String(n||""))||AE.test(a)?{intent:"planned",basis:"blueprint item"}:o&&M4.has(o)?{intent:"planned",basis:`prompt: ${o}`}:{intent:"unknown",basis:null}}function D4(e){let t=String(e||"");return C4.some(n=>n.test(t))?"excluded":N4.some(n=>n.test(t))?"docs":P4.some(n=>n.test(t))?"config":"code"}function B4(e){let t=[],n=null,r=null;for(let o of String(e||"").split(`
|
|
199
|
+
`)){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 L4(e,t){let n=Mw(e),r=Mw(t);if(n===r)return 1;if(!n.length||!r.length)return 0;n.length>ap&&(n=n.slice(0,ap)),r.length>ap&&(r=r.slice(0,ap));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 j4(e,t=RE){let n=new Set,r=e?.del||[],o=e?.add||[];for(let s=0;s<r.length;s++){let a=e.oldStart+s;if(!Mw(r[s]).length){n.add(a);continue}s<o.length&&L4(r[s],o[s])>=t&&n.add(a)}return n}function F4(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 U4(e){let t=new Map,n=null;for(let r of String(e||"").split(`
|
|
200
|
+
`)){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 EE(e,t){let{stdout:n}=await Ow(e,["show","-s","--format=%H%n%P%n%ct%n%s%n%B",t]),[r,o,s,a,...i]=n.split(`
|
|
201
201
|
`);return{sha:r.trim(),parents:o.trim()?o.trim().split(" "):[],ts:Number(s)*1e3,subject:a??"",message:i.join(`
|
|
202
|
-
`)}}async function $E(e,t,{fixKey:n=null,window:r=xo,resolveOrigin:o=null,context:s={}}={}){let a=await TE(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=xE(a.message),l=n?ap(n):c.epoch,u=l?Fa(l):null,{stdout:d}=await Ow(e,["diff","-U0","--no-color","-M","-C",i,t]),p=B4(d),f=0,w=0,h=0,y=0,k=0,g=0,x=0,S=new Map;for(let $ of p){if($.binary)continue;let L=$.oldPath||$.newPath||"",H=$.deleted.reduce((B,[,Z])=>B+Z,0),R=D4(L);if(R==="excluded"){h+=H;continue}if(R==="docs"){y+=H,k+=$.added;continue}if(R==="config"){g+=H;continue}f+=$.added;let T=new Set;for(let B of $.hunks||[])for(let Z of j4(B))T.add(Z);x+=T.size;let C=[];for(let[B,Z]of $.deleted)for(let xe=B;xe<B+Z;xe++)T.has(xe)||C.push(xe);if(w+=C.length,!$.oldPath||!C.length)continue;let W;try{let B=["blame","-w","-M","-C","--line-porcelain"];for(let[Z,xe]of F4(C))B.push("-L",`${Z},${Z+xe-1}`);B.push(i,"--",$.oldPath),W=U4((await Ow(e,B)).stdout)}catch{continue}for(let{sha:B,time:Z}of W.values()){let xe=S.get(B)??{lines:0,time:Z};xe.lines+=1,S.set(B,xe)}}let _=[],M=0,P=new Map;for(let[$,{lines:L,time:H}]of S){let R=P.get($);if(!R){try{R=await TE(e,$)}catch{R=null}P.set($,R)}let T=R?xE(R.message):{epoch:null},C=T.epoch,W=T.provider,B=T.model;if(!C&&o){let Q=await o($,R);Q?.trackId&&Q?.boxId!=null&&(C={trackId:Q.trackId,boxId:Q.boxId,epoch:Q.epoch??0},W=W??Q.provider??null,B=B??Q.model??null)}if(!C){M+=L;continue}let Z=Number.isFinite(H)?H:R?.ts??a.ts,xe=Math.max(0,(a.ts-Z)/k4),Te=Fa(C),_e=Te===u?"self":T4(xe,r);_.push({sha:$,key:Te,trackId:C.trackId,boxId:C.boxId,epoch:C.epoch,provider:W??null,model:B??null,lines:L,ageDays:Math.round(xe*100)/100,class:_e,decay:_e==="rework"?1:_e==="drift"?Math.round(A4(xe,{window:r})*1e3)/1e3:0})}_.sort(($,L)=>L.lines-$.lines);let j=lp({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0}),A=Pw({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:x,docsDeleted:y,docsAdded:k,configDeleted:g,codeDelta:f+w+x>0,origins:_,unattributedLines:M,ghost:w===0&&f>0,heuristic:j,intent:A}}function RE(e,{minLines:t=Nw}={}){if(!e)return!1;let n=e.intent?.intent??"unknown";return Ua.has(n)?Bc(e)>=t:!1}function Bc(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function Rw(e){let t=e.genUsd;return{...e,genUsd:_n(t),reworkChargedUsd:_n(e.reworkChargedUsd),reworkPendingUsd:_n(e.reworkPendingUsd),trueUsd:_n(t+e.reworkChargedUsd),multiplier:t>0?_n((t+e.reworkChargedUsd)/t):null}}function OE(e,{window:t=xo,boxMeta:n={}}={}){let r=e||[],o=r.filter(A=>A.type==="cost.recorded"),s=r.filter(A=>A.type==="code.committed"),a=r.filter(A=>A.type==="code.rework"),i=A=>Array.isArray(A?.shas)&&A.shas.length?A.shas:A?.sha?[A.sha]:[],c=new Map,l=new Set;for(let A of r)if(A.type==="rework.cause.offered")for(let $ of i(A.payload))l.add($);else if(A.type==="rework.cause.answered"&&A.payload?.cause)for(let $ of i(A.payload))c.set($,A.payload.cause);else if(A.type==="rework.cause.retracted")for(let $ of i(A.payload))c.delete($);let u={},d=(A,$,L)=>Fa({trackId:A,boxId:$,epoch:L??0}),p=(A,$={})=>{if(!u[A]){let H=ap(A)||{};u[A]={key:A,...H,provider:null,model:null,...$w()}}let L=u[A];return $.provider&&!L.provider&&(L.provider=$.provider),$.model&&!L.model&&(L.model=$.model),L};for(let A of o){if(!A.boxId)continue;let $=d(A.trackId,A.boxId,A.epoch),{usd:L,model:H}=sn(A),R=p($,{provider:A.provider,model:H!=="unknown"?H:null});R.genUsd+=L}let f=new Map;for(let A of s){let $=`${A.trackId}/${A.boxId}`;(f.get($)??f.set($,[]).get($)).push({...A,ts:A.payload?.ts??A.ts})}for(let A of f.values())A.sort(($,L)=>$.ts-L.ts);let w=(A,$,L,H)=>{let R=f.get(`${A}/${$}`)||[],T=-1/0;for(let B of R)B.ts<H&&(T=B.ts);let C=0,W=0;for(let B of o)B.trackId!==A||B.boxId!==$||(B.epoch??0)===(L??0)&&(B.ts<=T||B.ts>H||(C+=sn(B).usd,W++));return{usd:C,turns:W,from:Number.isFinite(T)?T:null}},h={charged:0,pending:0,self:0,decayed:0,unattributed:0,ghost:0,planned:0},y={A:0,B:0,C:0,E:0,none:0},k={fix:0,"likely-fix":0,planned:0,unknown:0},g=0,x=[],S=[],_={},M={},P=new Set;for(let A of a){let $=A.payload||{};if(!$.sha||P.has($.sha))continue;if(P.add($.sha),$.codeDelta===!1){g++;continue}let L=ap($.fixKey)||{trackId:A.trackId,boxId:A.boxId,epoch:A.epoch??0},{usd:H,turns:R}=w(L.trackId,L.boxId,L.epoch,$.ts??A.ts),T=v4.includes($.intent?.intent)?$.intent.intent:"unknown";k[T]+=1;let C=null;c.has($.sha)?C={cause:c.get($.sha),level:"B",basis:"operator"}:($.heuristic?.level&&$.heuristic.level!=="C"||$.heuristic)&&(C=$.heuristic);let W=C?.level??"none";y[W]=(y[W]||0)+1;let B=x4.has(W),Z=Number($.deleted)||0,xe=Array.isArray($.origins)?$.origins:[],Te=xe.filter(Q=>Q.class==="rework").reduce((Q,Xe)=>Q+Xe.lines,0),_e={sha:$.sha,subject:$.subject??"",ts:$.ts??A.ts,trackId:A.trackId,fixKey:$.fixKey??null,fixProvider:$.fixProvider??A.provider??null,fixUsd:_n(H),fixTurns:R,linesReworked:Te,unattributedLines:Number($.unattributedLines)||0,ghost:$.ghost===!0,intent:{intent:T,basis:$.intent?.basis??null},verdict:C?{cause:C.cause,level:W,basis:C.basis??null}:null,charged:_n(0),origins:[]};if(T==="planned"||C?.cause===ip){h.planned+=H;for(let Q of xe)Q.class==="rework"&&_e.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||Z===0)h.ghost+=H;else{let Q=0;for(let Pe of xe){let le=Pe.lines/Z,ge=H*le;if(Q+=ge,Pe.class==="self"){h.self+=ge;continue}let de=Pe.class==="rework"?1:Number(Pe.decay)||0,te=ge*de;if(h.decayed+=ge-te,te<=0)continue;let we=p(Pe.key,{provider:Pe.provider,model:Pe.model});we.fixes+=1,we.linesReworked+=Pe.class==="rework"?Pe.lines:0;let Oe=C?.cause&&Dc.includes(C.cause)?C.cause:"unclassified";we.causes[Oe]+=1,B?(we.reworkChargedUsd+=te,h.charged+=te,_e.charged=_n(_e.charged+te)):(we.reworkPendingUsd+=te,h.pending+=te),_e.origins.push({key:Pe.key,provider:we.provider,model:we.model,lines:Pe.lines,ageDays:Pe.ageDays,class:Pe.class,usd:_n(te)})}let Xe=Math.max(0,H-Q);h.unattributed+=Xe}x.push(_e),!c.has($.sha)&&l.has($.sha)&&S.push($.sha)}for(let A of Object.values(u)){let $=A.provider||"unknown",L=A.model||"unknown",H=R=>{R.genUsd+=A.genUsd,R.reworkChargedUsd+=A.reworkChargedUsd,R.reworkPendingUsd+=A.reworkPendingUsd,R.linesReworked+=A.linesReworked,R.fixes+=A.fixes;for(let T of Object.keys(R.causes))R.causes[T]+=A.causes[T]};H(_[$]||=$w()),H(M[L]||=$w())}x.sort((A,$)=>$.ts-A.ts);let j=Object.values(u).map(A=>{let $=n[`${A.trackId}/${A.boxId}`]||{};return Rw({...A,trackName:$.trackName??null,label:$.label??null})}).sort((A,$)=>$.reworkChargedUsd+$.reworkPendingUsd-(A.reworkChargedUsd+A.reworkPendingUsd)||$.genUsd-A.genUsd);return{window:t,basis:`rework = code lines a fix deletes/modifies that blame to another epoch's commit \u2264${t} d old (docs/config churn and in-place updates \u2265${IE} 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:k,noCodeDelta:g,byEpoch:Object.fromEntries(j.map(A=>[A.key,A])),epochs:j,byProvider:Object.fromEntries(Object.entries(_).map(([A,$])=>[A,Rw($)])),byModel:Object.fromEntries(Object.entries(M).map(([A,$])=>[A,Rw($)])),buckets:Object.fromEntries(Object.entries(h).map(([A,$])=>[A,_n($)])),fixUsdTotal:_n(Object.values(h).reduce((A,$)=>A+$,0)),fixes:x,verdicts:y,pendingTaps:S,commits:s.length}}var y4,b4,Ow,AE,EE,_E,k4,xo,Cw,Nw,Dc,ip,x4,v4,Ua,IE,S4,E4,vE,_4,I4,$4,R4,O4,SE,M4,C4,N4,P4,Mw,sp,_n,$w,up=G(()=>{fo();y4=g4(w4),b4=64*1024*1024,Ow=(e,t)=>y4("git",["-C",e,...t],{maxBuffer:b4,timeout:6e4}),AE="Bostrat-Epoch",EE="Bostrat-Model",_E="Bostrat-Fix-Cause",k4=24*36e5,xo=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,Cw=90,Nw=3,Dc=Object.freeze(["defect","design","requirements","style"]),ip="not-a-fix",x4=new Set(["A","B","E"]),v4=Object.freeze(["fix","likely-fix","planned","unknown"]),Ua=new Set(["fix","likely-fix"]),IE=.8;S4={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};E4=/^revert\b|^revert "/i,vE=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,_4=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,I4=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;$4=/^([a-z]+)(\([^)]*\))?!?:\s/i,R4=new Set(["fix","hotfix","bugfix","revert"]),O4=new Set(["feat","feature","docs","doc","test","tests","build","ci","chore"]),SE=/\bBP-[A-Z0-9]+(?:-[A-Z0-9]+)*\b/i,M4=new Set(["feature-build","docs-writing","research","design","ops-infra","orchestration"]);C4=[/(^|\/)\.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)\//],N4=[/\.(md|mdx|markdown|rst|txt|adoc|asciidoc)$/i,/(^|\/)(docs?|notes|business|design)\//i,/(^|\/)(README|CHANGELOG|CHANGES|LICENSE|CONTRIBUTING|AUTHORS|NOTICE)(\.[a-z]+)?$/i],P4=[/\.(json|jsonc|json5|ya?ml|toml|ini|cfg|conf|properties|env|example)$/i,/(^|\/)\.[^/]+$/,/(^|\/)\.github\//,/(^|\/)(Dockerfile|Makefile|Procfile|fly\.toml)$/];Mw=e=>String(e??"").replace(/\s+/g," ").trim(),sp=400;_n=e=>Math.round(e*1e4)/1e4,$w=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import{execFile as H4}from"node:child_process";import{promisify as W4}from"node:util";function ME(e,t=Date.now()){return ji(e,t)??0}function G4(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 CE(e,t,{now:n=Date.now()}={}){let r=ME(t,n),o=await K(e);return cp({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:G4(o,t.name,r)})}function K4(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 V4(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=K4(n,t.name),c=i?gr(i.text)?.category??null:null;return{wallReason:a,fixerCategory:c,promptKind:i?.kind??null}}async function z4(e,t){try{let{stdout:n}=await Dw(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 J4(e){try{let{stdout:t}=await Dw(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function NE(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(h=>h.type==="code.committed"&&h.payload?.sha===n))return{recorded:!1,reason:"duplicate"};let i=s,c=null;try{let{stdout:h}=await Dw(r,["show","-s","--format=%ct%n%s",n]),[y,...k]=h.split(`
|
|
202
|
+
`)}}async function OE(e,t,{fixKey:n=null,window:r=xo,resolveOrigin:o=null,context:s={}}={}){let a=await EE(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=SE(a.message),l=n?ip(n):c.epoch,u=l?Fa(l):null,{stdout:d}=await Ow(e,["diff","-U0","--no-color","-M","-C",i,t]),p=B4(d),f=0,w=0,h=0,y=0,k=0,g=0,x=0,S=new Map;for(let $ of p){if($.binary)continue;let L=$.oldPath||$.newPath||"",H=$.deleted.reduce((B,[,Z])=>B+Z,0),R=D4(L);if(R==="excluded"){h+=H;continue}if(R==="docs"){y+=H,k+=$.added;continue}if(R==="config"){g+=H;continue}f+=$.added;let T=new Set;for(let B of $.hunks||[])for(let Z of j4(B))T.add(Z);x+=T.size;let C=[];for(let[B,Z]of $.deleted)for(let xe=B;xe<B+Z;xe++)T.has(xe)||C.push(xe);if(w+=C.length,!$.oldPath||!C.length)continue;let W;try{let B=["blame","-w","-M","-C","--line-porcelain"];for(let[Z,xe]of F4(C))B.push("-L",`${Z},${Z+xe-1}`);B.push(i,"--",$.oldPath),W=U4((await Ow(e,B)).stdout)}catch{continue}for(let{sha:B,time:Z}of W.values()){let xe=S.get(B)??{lines:0,time:Z};xe.lines+=1,S.set(B,xe)}}let _=[],M=0,P=new Map;for(let[$,{lines:L,time:H}]of S){let R=P.get($);if(!R){try{R=await EE(e,$)}catch{R=null}P.set($,R)}let T=R?SE(R.message):{epoch:null},C=T.epoch,W=T.provider,B=T.model;if(!C&&o){let Q=await o($,R);Q?.trackId&&Q?.boxId!=null&&(C={trackId:Q.trackId,boxId:Q.boxId,epoch:Q.epoch??0},W=W??Q.provider??null,B=B??Q.model??null)}if(!C){M+=L;continue}let Z=Number.isFinite(H)?H:R?.ts??a.ts,xe=Math.max(0,(a.ts-Z)/k4),Te=Fa(C),_e=Te===u?"self":T4(xe,r);_.push({sha:$,key:Te,trackId:C.trackId,boxId:C.boxId,epoch:C.epoch,provider:W??null,model:B??null,lines:L,ageDays:Math.round(xe*100)/100,class:_e,decay:_e==="rework"?1:_e==="drift"?Math.round(A4(xe,{window:r})*1e3)/1e3:0})}_.sort(($,L)=>L.lines-$.lines);let j=up({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0}),A=Pw({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:x,docsDeleted:y,docsAdded:k,configDeleted:g,codeDelta:f+w+x>0,origins:_,unattributedLines:M,ghost:w===0&&f>0,heuristic:j,intent:A}}function ME(e,{minLines:t=Nw}={}){if(!e)return!1;let n=e.intent?.intent??"unknown";return Ua.has(n)?Bc(e)>=t:!1}function Bc(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function Rw(e){let t=e.genUsd;return{...e,genUsd:_n(t),reworkChargedUsd:_n(e.reworkChargedUsd),reworkPendingUsd:_n(e.reworkPendingUsd),trueUsd:_n(t+e.reworkChargedUsd),multiplier:t>0?_n((t+e.reworkChargedUsd)/t):null}}function CE(e,{window:t=xo,boxMeta:n={}}={}){let r=e||[],o=r.filter(A=>A.type==="cost.recorded"),s=r.filter(A=>A.type==="code.committed"),a=r.filter(A=>A.type==="code.rework"),i=A=>Array.isArray(A?.shas)&&A.shas.length?A.shas:A?.sha?[A.sha]:[],c=new Map,l=new Set;for(let A of r)if(A.type==="rework.cause.offered")for(let $ of i(A.payload))l.add($);else if(A.type==="rework.cause.answered"&&A.payload?.cause)for(let $ of i(A.payload))c.set($,A.payload.cause);else if(A.type==="rework.cause.retracted")for(let $ of i(A.payload))c.delete($);let u={},d=(A,$,L)=>Fa({trackId:A,boxId:$,epoch:L??0}),p=(A,$={})=>{if(!u[A]){let H=ip(A)||{};u[A]={key:A,...H,provider:null,model:null,...$w()}}let L=u[A];return $.provider&&!L.provider&&(L.provider=$.provider),$.model&&!L.model&&(L.model=$.model),L};for(let A of o){if(!A.boxId)continue;let $=d(A.trackId,A.boxId,A.epoch),{usd:L,model:H}=sn(A),R=p($,{provider:A.provider,model:H!=="unknown"?H:null});R.genUsd+=L}let f=new Map;for(let A of s){let $=`${A.trackId}/${A.boxId}`;(f.get($)??f.set($,[]).get($)).push({...A,ts:A.payload?.ts??A.ts})}for(let A of f.values())A.sort(($,L)=>$.ts-L.ts);let w=(A,$,L,H)=>{let R=f.get(`${A}/${$}`)||[],T=-1/0;for(let B of R)B.ts<H&&(T=B.ts);let C=0,W=0;for(let B of o)B.trackId!==A||B.boxId!==$||(B.epoch??0)===(L??0)&&(B.ts<=T||B.ts>H||(C+=sn(B).usd,W++));return{usd:C,turns:W,from:Number.isFinite(T)?T:null}},h={charged:0,pending:0,self:0,decayed:0,unattributed:0,ghost:0,planned:0},y={A:0,B:0,C:0,E:0,none:0},k={fix:0,"likely-fix":0,planned:0,unknown:0},g=0,x=[],S=[],_={},M={},P=new Set;for(let A of a){let $=A.payload||{};if(!$.sha||P.has($.sha))continue;if(P.add($.sha),$.codeDelta===!1){g++;continue}let L=ip($.fixKey)||{trackId:A.trackId,boxId:A.boxId,epoch:A.epoch??0},{usd:H,turns:R}=w(L.trackId,L.boxId,L.epoch,$.ts??A.ts),T=v4.includes($.intent?.intent)?$.intent.intent:"unknown";k[T]+=1;let C=null;c.has($.sha)?C={cause:c.get($.sha),level:"B",basis:"operator"}:($.heuristic?.level&&$.heuristic.level!=="C"||$.heuristic)&&(C=$.heuristic);let W=C?.level??"none";y[W]=(y[W]||0)+1;let B=x4.has(W),Z=Number($.deleted)||0,xe=Array.isArray($.origins)?$.origins:[],Te=xe.filter(Q=>Q.class==="rework").reduce((Q,Xe)=>Q+Xe.lines,0),_e={sha:$.sha,subject:$.subject??"",ts:$.ts??A.ts,trackId:A.trackId,fixKey:$.fixKey??null,fixProvider:$.fixProvider??A.provider??null,fixUsd:_n(H),fixTurns:R,linesReworked:Te,unattributedLines:Number($.unattributedLines)||0,ghost:$.ghost===!0,intent:{intent:T,basis:$.intent?.basis??null},verdict:C?{cause:C.cause,level:W,basis:C.basis??null}:null,charged:_n(0),origins:[]};if(T==="planned"||C?.cause===cp){h.planned+=H;for(let Q of xe)Q.class==="rework"&&_e.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||Z===0)h.ghost+=H;else{let Q=0;for(let Pe of xe){let le=Pe.lines/Z,ge=H*le;if(Q+=ge,Pe.class==="self"){h.self+=ge;continue}let de=Pe.class==="rework"?1:Number(Pe.decay)||0,te=ge*de;if(h.decayed+=ge-te,te<=0)continue;let we=p(Pe.key,{provider:Pe.provider,model:Pe.model});we.fixes+=1,we.linesReworked+=Pe.class==="rework"?Pe.lines:0;let Oe=C?.cause&&Dc.includes(C.cause)?C.cause:"unclassified";we.causes[Oe]+=1,B?(we.reworkChargedUsd+=te,h.charged+=te,_e.charged=_n(_e.charged+te)):(we.reworkPendingUsd+=te,h.pending+=te),_e.origins.push({key:Pe.key,provider:we.provider,model:we.model,lines:Pe.lines,ageDays:Pe.ageDays,class:Pe.class,usd:_n(te)})}let Xe=Math.max(0,H-Q);h.unattributed+=Xe}x.push(_e),!c.has($.sha)&&l.has($.sha)&&S.push($.sha)}for(let A of Object.values(u)){let $=A.provider||"unknown",L=A.model||"unknown",H=R=>{R.genUsd+=A.genUsd,R.reworkChargedUsd+=A.reworkChargedUsd,R.reworkPendingUsd+=A.reworkPendingUsd,R.linesReworked+=A.linesReworked,R.fixes+=A.fixes;for(let T of Object.keys(R.causes))R.causes[T]+=A.causes[T]};H(_[$]||=$w()),H(M[L]||=$w())}x.sort((A,$)=>$.ts-A.ts);let j=Object.values(u).map(A=>{let $=n[`${A.trackId}/${A.boxId}`]||{};return Rw({...A,trackName:$.trackName??null,label:$.label??null})}).sort((A,$)=>$.reworkChargedUsd+$.reworkPendingUsd-(A.reworkChargedUsd+A.reworkPendingUsd)||$.genUsd-A.genUsd);return{window:t,basis:`rework = code lines a fix deletes/modifies that blame to another epoch's commit \u2264${t} d old (docs/config churn and in-place updates \u2265${RE} 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:k,noCodeDelta:g,byEpoch:Object.fromEntries(j.map(A=>[A.key,A])),epochs:j,byProvider:Object.fromEntries(Object.entries(_).map(([A,$])=>[A,Rw($)])),byModel:Object.fromEntries(Object.entries(M).map(([A,$])=>[A,Rw($)])),buckets:Object.fromEntries(Object.entries(h).map(([A,$])=>[A,_n($)])),fixUsdTotal:_n(Object.values(h).reduce((A,$)=>A+$,0)),fixes:x,verdicts:y,pendingTaps:S,commits:s.length}}var y4,b4,Ow,_E,IE,$E,k4,xo,Cw,Nw,Dc,cp,x4,v4,Ua,RE,S4,E4,TE,_4,I4,$4,R4,O4,AE,M4,C4,N4,P4,Mw,ap,_n,$w,dp=G(()=>{fo();y4=g4(w4),b4=64*1024*1024,Ow=(e,t)=>y4("git",["-C",e,...t],{maxBuffer:b4,timeout:6e4}),_E="Bostrat-Epoch",IE="Bostrat-Model",$E="Bostrat-Fix-Cause",k4=24*36e5,xo=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,Cw=90,Nw=3,Dc=Object.freeze(["defect","design","requirements","style"]),cp="not-a-fix",x4=new Set(["A","B","E"]),v4=Object.freeze(["fix","likely-fix","planned","unknown"]),Ua=new Set(["fix","likely-fix"]),RE=.8;S4={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};E4=/^revert\b|^revert "/i,TE=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,_4=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,I4=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;$4=/^([a-z]+)(\([^)]*\))?!?:\s/i,R4=new Set(["fix","hotfix","bugfix","revert"]),O4=new Set(["feat","feature","docs","doc","test","tests","build","ci","chore"]),AE=/\bBP-[A-Z0-9]+(?:-[A-Z0-9]+)*\b/i,M4=new Set(["feature-build","docs-writing","research","design","ops-infra","orchestration"]);C4=[/(^|\/)\.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)\//],N4=[/\.(md|mdx|markdown|rst|txt|adoc|asciidoc)$/i,/(^|\/)(docs?|notes|business|design)\//i,/(^|\/)(README|CHANGELOG|CHANGES|LICENSE|CONTRIBUTING|AUTHORS|NOTICE)(\.[a-z]+)?$/i],P4=[/\.(json|jsonc|json5|ya?ml|toml|ini|cfg|conf|properties|env|example)$/i,/(^|\/)\.[^/]+$/,/(^|\/)\.github\//,/(^|\/)(Dockerfile|Makefile|Procfile|fly\.toml)$/];Mw=e=>String(e??"").replace(/\s+/g," ").trim(),ap=400;_n=e=>Math.round(e*1e4)/1e4,$w=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import{execFile as H4}from"node:child_process";import{promisify as W4}from"node:util";function NE(e,t=Date.now()){return ji(e,t)??0}function G4(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 PE(e,t,{now:n=Date.now()}={}){let r=NE(t,n),o=await K(e);return lp({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:G4(o,t.name,r)})}function K4(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 V4(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=K4(n,t.name),c=i?gr(i.text)?.category??null:null;return{wallReason:a,fixerCategory:c,promptKind:i?.kind??null}}async function z4(e,t){try{let{stdout:n}=await Dw(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 J4(e){try{let{stdout:t}=await Dw(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function DE(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(h=>h.type==="code.committed"&&h.payload?.sha===n))return{recorded:!1,reason:"duplicate"};let i=s,c=null;try{let{stdout:h}=await Dw(r,["show","-s","--format=%ct%n%s",n]),[y,...k]=h.split(`
|
|
203
203
|
`),g=Number(y.trim())*1e3;Number.isFinite(g)&&g>0&&(i=g),c=k.join(`
|
|
204
|
-
`).trim().slice(0,300)||null}catch{return{recorded:!1,reason:"unknown-sha"}}let l=
|
|
205
|
-
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+a2(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 Lw(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function GE(e){let t=WE(e),n=t.match(r2);if(!n)return null;let r=Lw(t,n.index);return s2.test(r)?null:{repo:r.match(qE)?.[1]??null,head:r.match(i2)?.[1]??null}}function KE(e){let t=WE(e),n=t.match(o2);if(!n)return null;let r=Lw(t,n.index),o=Lw(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("-")){l2.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(qE)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function d2(e,t,{exclude:n=()=>!1}={}){let r=async i=>{Sr("observer");let{stdout:c}=await jE("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",u2],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw yo(l)&&bo(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-e2&&!n(c)),s=await Bn({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 p2(e,t){if(t)return[t];let n=[];for(let r of VE(e)){let o=await Me(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await fr().catch(()=>[])){let o=await Me(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function VE(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 f2(e,t,n){let r=await ja(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)||c2(c.headRefName,e.id),i=[];for(let c of await p2(e,t?.repo??null)){let l;try{l=await d2(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await ft(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 zE(e){if(!e)return 0;let t=0;for(let n of Ar)n.startsWith(`${e}|`)&&t++;return t}function JE(){for(let e of[...jw])e()}function YE(e,t,{delays:n=FE,turnEnd:r}={}){let o=GE(t.command);if(!o)return null;let s=!!KE(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(Ar.has(a))return null;Ar.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(h=>{let y=()=>{clearTimeout(k),jw.delete(y),h()},k=setTimeout(y,l);jw.add(y)});let u=await f2(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:d,pr:p}=u,{link:f,created:w}=await Yt({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 xr(d,p.number))?null:(await Bw(t.box,t.agent,r),await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"opened",number:p.number,url:p.url,title:p.title,branch:p.headRefName,repo:d,...s?{selfMerge:!0}:{}}}),p)}return null}finally{Ar.delete(a)}})();return c.catch(()=>Ar.delete(a)),c}function XE(e,t,{delays:n=FE,turnEnd:r,openedWait:o}={}){let s=KE(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(Ar.has(a))return null;Ar.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=VE(e);if(w.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let h of w){let y=await Me(h).catch(()=>null);y&&!d.includes(y)&&d.push(y)}}if(l||=(await ja(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 y=await ft(c,w.number);return y&&y.trackId!==e.id?null:(y||await Yt({repo:c,number:w.number,trackId:e.id,url:w.url,headRef:w.headRefName,title:w.title,source:"merge-observed"}),await $c(c,w.number),h&&((await K(e).catch(()=>[])).some(x=>x.type==="pr.state.changed"&&(x.payload?.change==="auto-armed"||x.payload?.change==="merged")&&x.payload?.number===w.number)||(await Bw(t.box,t.agent,r),await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:w.number,url:w.url,title:w.title,branch:w.headRefName,repo:c}}))),w)},f=null;for(let w of n){await new Promise(M=>setTimeout(M,w));let h=[];for(let M of d)try{Sr("observer");let{stdout:P}=await jE("gh",["pr","view",String(l),"--repo",M,"--json",m2],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await Bn({slug:M})}}),j=JSON.parse(P||"null");j?.number&&h.push({slug:M,pr:j})}catch(P){yo(P)&&bo(P)}let y=h.find(M=>M.pr.state==="MERGED")??(h.length===1?h[0]:null);if(!y)continue;c=y.slug;let k=y.pr;if(!k?.number)continue;if(k.state==="CLOSED")return null;if(k.state!=="MERGED"){if(f=k,s.auto)return p(k,!0);continue}let g=await ft(c,k.number);if(g&&g.trackId!==e.id)return null;g||await Yt({repo:c,number:k.number,trackId:e.id,url:k.url,headRef:k.headRefName,title:k.title,source:"merge-observed"}),await Bw(t.box,t.agent,r);let x=await K(e).catch(()=>[]);if(x.some(M=>M.type==="pr.state.changed"&&M.payload?.change==="merged"&&M.payload?.number===k.number))return null;let S=M=>M.some(P=>P.type==="pr.state.changed"&&P.payload?.change==="opened"&&P.payload?.number===k.number),_=0;if(!S(x)){if(GE(t.command)&&await xr(c,k.number)){let M=Date.now();_=M+1,await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:M,change:"opened",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c,selfMerge:!0}})}else if((await ft(c,k.number))?.announced){let M=o?.capMs??t2,P=o?.pollMs??n2,j=Date.now()+M;for(;Date.now()<j&&(await new Promise(A=>setTimeout(A,P)),!S(await K(e).catch(()=>[]))););}}return await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Math.max(Date.now(),_),change:"merged",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c}}),await dp(e,{repo:c,number:k.number,title:k.title,branch:k.headRefName,url:k.url}).catch(()=>{}),await _r(c,k.number).catch(()=>{}),await Er(c,k.number).catch(()=>{}),await wo(c,k.number,"merged"),k}return f?p(f,!1):null}finally{Ar.delete(a)}})();return i.catch(()=>Ar.delete(a)),i}var jE,FE,Z4,Q4,e2,t2,n2,UE,HE,r2,o2,s2,LE,a2,qE,i2,c2,l2,u2,Ar,jw,m2,fp=G(()=>{be();Gr();xt();Oc();Gn();Ir();va();Ss();pp();jE=X4(Y4),FE=[4e3,15e3,45e3],Z4=2500,Q4=5*6e4;e2=15e3,t2=9e4,n2=2e3,UE=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,HE=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,r2=new RegExp(`${UE}${HE}create\\b`),o2=new RegExp(`${UE}${HE}merge\\b`),s2=/\s--web\b/,LE=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,a2=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");qE=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,i2=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,c2=kE;l2=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);u2="number,url,title,headRefName,createdAt";Ar=new Set;jw=new Set;m2="number,url,title,headRefName,state"});import h2 from"node:fs/promises";import Uw from"node:path";import w2 from"node:os";function Fw(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function mp(e){let t=g2.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=Fw(n,"from"),s=o?o.replace(/^uds:/,""):null,a=ZE(s);return{socket:s,pid:a,name:Fw(n,"from-name"),mode:Fw(n,"from-mode"),text:r.trim()}}function ZE(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function y2(){return process.env.CLAUDE_CONFIG_DIR?Uw.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):Uw.join(w2.homedir(),".claude","sessions")}async function b2(e){let t=ZE(e);if(!t)return null;try{let n=await h2.readFile(Uw.join(y2(),`${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 QE(e,t){let n=await b2(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 g2,Hw=G(()=>{g2=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function e_(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}async function t_(e,t,n=Ww){let r=new AbortController,o=setTimeout(()=>r.abort(),n);try{return await x2(`${De(t)}${e}`,{headers:v2(t),signal:r.signal})}finally{clearTimeout(o)}}async function Lc({env:e=process.env,force:t=!1,timeoutMs:n=Ww}={}){if(!e_(e))return{followups:[],source:"unconfigured"};if(!t&&Ts&&Date.now()-Ts.at<k2)return{followups:Ts.followups,source:"cache"};try{let r=await t_("/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 Ts={at:Date.now(),followups:s},{followups:s,source:"plane"}}catch(r){return Ts?{followups:Ts.followups,source:"stale",error:r?.message}:{followups:[],source:"unreachable",error:r?.message}}}async function n_(e,{env:t=process.env,timeoutMs:n=Ww}={}){if(!e)return{followup:null,source:"unconfigured"};if(!e_(t))return{followup:null,source:"unconfigured"};try{let r=await t_(`/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=Ts?.followups.find(s=>s.id===e)??null;return o?{followup:o,source:"stale",error:r?.message}:{followup:null,source:"unreachable",error:r?.message}}}var k2,Ww,Ts,x2,v2,r_=G(()=>{xt();k2=6e4,Ww=5e3,Ts=null,x2=(...e)=>fetch(...e);v2=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});async function vo(e,t){if(qw.has(e)){let n=new Error("offer already answered (answer in flight)");throw n.code="answer-in-flight",n}qw.add(e);try{return await t()}finally{qw.delete(e)}}var qw,jc=G(()=>{qw=new Set});import{randomUUID as As,createHash as S2}from"node:crypto";function hp(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function p_(e){for(let[t,n]of _2)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 i_(e,{at:t="",promptRequired:n=!0,standing:r=!1}={}){let o=e&&typeof e=="object"?e:{},s=Es(o.label).replace(/\s+/g," ");if(!s)throw new Error(`${t}label required (\u2264${Gw} chars, e.g. "Deploy to Pi")`);if(s.length>Gw)throw new Error(`${t}label must be \u2264${Gw} characters`);let a=Es(o.prompt);if(n&&!a)throw new Error(`${t}prompt required \u2014 the full instruction a fresh session could execute`);if(Buffer.byteLength(a,"utf8")>o_)throw new Error(`${t}prompt must be \u2264${o_} bytes`);if(r){let l=p_(a);if(l)throw new Error(`${t}${l}`)}let i=o.standing==null||o.standing===""?"offerable":String(o.standing).trim().toLowerCase();if(!E2.includes(i))throw new Error(`${t}standing must be "offerable" or "never", got "${o.standing}"`);let c=Es(o.why).slice(0,l_)||null;return{label:s,prompt:a,why:c,standing:i}}function f_(e,{standing:t=!1}={}){let n=e&&typeof e=="object"?e:{},r=Es(n.trigger)||(n.ship?"now":""),o=t?T2:u_.filter(p=>p!=="track.opened");if(!o.includes(r))throw new Error(`trigger must be one of ${o.join(" | ")}${t?" (a standing follow-up can't be `now` \u2014 it would fire every turn)":""}, got "${n.trigger??""}"`);let s=n.ship&&r==="now"?R2(n.ship):null,a=n.items!=null;if(a&&s)throw new Error("a ship offer has its own card \u2014 `items` cannot be combined with a `ship` block");let i,c=null;if(a){if(!Array.isArray(n.items)||!n.items.length)throw new Error("items must be a non-empty array of {label, prompt, why?, standing?}");if(n.items.length>a_)throw new Error(`items must be \u2264${a_} \u2014 one item per step the operator might run, save or skip separately`);if(t&&n.items.length>1)throw new Error("propose one standing follow-up at a time \u2014 a standing follow-up is one prompt");i=n.items.map((f,w)=>i_(f,{at:`items[${w}]: `,standing:t})),c=Es(n.why).slice(0,l_)||null;let p=new Set;for(let f of i){let w=hp(f.label);if(p.has(w))throw new Error(`items must have distinct labels ("${f.label}" appears twice)`);p.add(w)}}else i=[i_(n,{promptRequired:!s,standing:t})];let l=i[0],u=c??l.why,d=t?$2(n.repo):null;return{label:l.label,prompt:l.prompt,trigger:r,why:u,planWhy:c,items:i,...d?{repo:d}:{},...s?{ship:s}:{}}}function I2(e){return e.standing==="never"?{promotable:!1,promotableReason:"one-off"}:p_(e.prompt)?{promotable:!1,promotableReason:"specifics"}:{promotable:!0,promotableReason:null}}function $2(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 R2(e){let t=e&&typeof e=="object"?e:{},n=Array.isArray(t.actions)?t.actions.map(i=>String(i).trim().toLowerCase()):Kw.slice();for(let i of n)if(!Kw.includes(i))throw new Error(`ship.actions may only contain ${Kw.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=Es(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=>Es(i)).filter(Boolean),a.length||(a=null)}return{actions:[...new Set(n)],rebase:r,toPr:o,base:s,repos:a}}function wp(e,{trigger:t=null}={}){let n=new Map;for(let r of e){let o=r.payload||{};if(r.type==="followup.armed"&&o.armId)o.replaces&&n.delete(o.replaces),n.set(o.armId,{...o,ts:o.ts??r.ts,boxId:r.boxId??null,provider:r.provider??null});else if(r.type==="followup.armed.cancelled"&&o.armId)n.delete(o.armId);else if(r.type==="followup.offered")for(let s of o.rows||[])s.armId&&n.delete(s.armId)}return[...n.values()].filter(r=>!t||r.trigger===t)}function m_(e){return{armId:e.armId,label:e.label,prompt:e.prompt,promptHash:d_(e.prompt),origin:"agent",why:e.why??null,groupId:e.groupId??null,seq:Number.isInteger(e.seq)?e.seq:0,standing:e.standing==="never"?"never":"offerable",...I2(e)}}async function h_(e,t,{pull:n=Lc,ship:r={}}={}){if(zw(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=f_(t);if(o.ship)return{kind:"ship",...await P2(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},c=As(),l=(g,x,S={})=>({ts:Date.now(),armId:As(),trigger:o.trigger,label:g.label,prompt:g.prompt,why:g.why,standing:g.standing,groupId:c,seq:x,...o.planWhy?{planWhy:o.planWhy}:{},...S}),u={groupId:c,trigger:o.trigger,label:o.label,prompt:o.prompt,why:o.why,planWhy:o.planWhy};if(o.trigger==="now"){let g=As(),x=[];for(let[S,_]of o.items.entries()){let M=l(_,S);await N(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:M}),x.push(M)}return await N(s,{type:"followup.offered",actor:"system",source:"orchestrator",...i,payload:{ts:Date.now(),offerId:g,trigger:"now",repo:e.workspace?.repo||null,context:{},groupId:c,why:o.planWhy,rows:x.map(m_)}}),{kind:"now",offerId:g,armId:x[0].armId,items:x.map(S=>({armId:S.armId,label:S.label,standing:S.standing})),...u}}let d=e.workspace?.repo||null,{followups:p}=await n().catch(()=>({followups:[]})),f=await K(s),w=wp(f,{trigger:o.trigger}),h=[],y=0;for(let g of o.items){let x=hp(g.label),S=p.find(j=>j.status==="active"&&j.mode!=="off"&&j.trigger===o.trigger&&(j.repo==null||j.repo===d)&&j.dedupeKey===x);if(S){h.push({label:g.label,standing:g.standing,covered:{id:S.id,label:S.label}});continue}let _=w.filter(j=>hp(j.label)===x),M=_.length?_[_.length-1].armId:null,P=l(g,y++,M?{replaces:M}:{});await N(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:P}),h.push({armId:P.armId,label:g.label,standing:g.standing,replaces:M})}let k=h.filter(g=>g.armId);return k.length?{kind:"armed",armId:k[0].armId,replaces:k[0].replaces,items:h,...u}:{kind:"covered",covered:h[0].covered,items:h,...u}}async function w_(e,t){if(!t)throw new Error("armId required");let n={id:e.id,cwd:e.cwd},r=await K(n);if(!wp(r).some(o=>o.armId===t))throw new Error("unknown or already-settled arm");return await N(n,{type:"followup.armed.cancelled",actor:"user",source:"orchestrator",payload:{ts:Date.now(),armId:t}}),{armId:t}}async function g_(e,t,{pull:n=Lc}={}){if(zw(e))throw new Error("propose from a lead Track, not a loop's run Track");let r=f_(t,{standing:!0}),o=r.repo??e.workspace?.repo??null,s={id:e.id,cwd:e.cwd};if((await K(s)).reduce((y,k)=>k.type==="followup.create.offered"?k.payload?.proposalId??y:k.type==="followup.create.answered"&&k.payload?.proposalId===y?null:y,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=hp(r.label),d=c.filter(y=>y.dedupeKey===u&&y.trigger===r.trigger),p=d.find(y=>y.status==="deleted"),f=d.find(y=>y.status!=="deleted");if(f)throw new Error(`"${f.label}" (${Vw[f.trigger]||f.trigger}) already exists as a standing follow-up \u2014 not re-proposed`);if(p){let y=p.deletedAt?new Date(p.deletedAt).toISOString().slice(0,10):"earlier";throw new Error(`the operator deleted "${p.label}" on ${y} \u2014 do not re-propose it`)}let w=As(),h=(e.boxes||[]).find(y=>y.name===e.leadBox)||null;return await N(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 y_(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 N(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};if(!r)throw new Error("followupId required \u2014 the follow-up is created on the plane before the card is answered");let i=a.payload;return await N(o,{type:"followup.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,followupId:String(r),label:i.label,trigger:i.trigger,repo:i.repo??null}}),await N(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",followupId:String(r)}}),{proposalId:t,action:"created",followupId:String(r)}}async function Fc(e,t,n={},{pull:r=Lc}={}){if(!e||zw(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 k=S=>{let _=(S?.prs||[]).filter(M=>Number.isFinite(Number(M?.number))).map(M=>`${M?.repo??S?.repo??""}#${Number(M.number)}`);return Number.isFinite(Number(S?.prNumber))&&_.push(`${S?.repo??""}#${Number(S.prNumber)}`),_},g=new Set(k(n));if(s.some(S=>S.type==="followup.offered"&&S.payload?.trigger==="pr.merged"&&k(S.payload?.context).some(_=>g.has(_))))return null}let i=wp(s,{trigger:t}),c=i.length?i[i.length-1].groupId??null:null,l=new Map(i.map((k,g)=>[k.armId,g])),u=k=>k.groupId&&k.groupId===c?0:1,d=i.slice().sort((k,g)=>u(k)-u(g)||l.get(k.armId)-l.get(g.armId)).slice(0,s_).map(m_),p=c?i.find(k=>k.groupId===c&&k.planWhy)?.planWhy??null:null,f=[];if(t!=="now"){let{followups:k}=await r().catch(()=>({followups:[]}));f=k.filter(g=>g.status==="active"&&g.mode!=="off"&&g.trigger===t&&(g.repo==null||g.repo===a)).sort((g,x)=>{let S=_=>_.stats?.offered?(_.stats.accepted||0)/_.stats.offered:0;return S(x)-S(g)||(g.origin==="operator"?-1:0)-(x.origin==="operator"?-1:0)||(x.createdAt||0)-(g.createdAt||0)}).slice(0,A2).map(g=>({followupId:g.id,label:g.label,promptPreview:O2(g.prompt),promptHash:d_(g.prompt),origin:g.origin,repo:g.repo??null}))}let w=[...d,...f].slice(0,s_);if(!w.length)return null;let h=As(),y={ts:Date.now(),offerId:h,trigger:t,repo:a,context:{...n,repo:a},groupId:c,why:p,rows:w};return await N(o,{type:"followup.offered",actor:"system",source:"orchestrator",payload:y}),y}function M2(e,t){let n=t?.rows||[],r=a=>a.armId?`arm:${a.armId}`:`fu:${a.followupId}`,o=new Map(n.map(a=>[r(a),null])),s=(a,i)=>{o.has(a)&&o.get(a)==null&&o.set(a,i)};for(let a of e){if(a.type!=="followup.answered"||a.payload?.offerId!==t.offerId)continue;let i=a.payload,c=[];i.armId&&c.push(`arm:${i.armId}`),i.followupId&&c.push(`fu:${i.followupId}`);for(let l of Array.isArray(i.armIds)?i.armIds:[])c.push(`arm:${l}`);for(let l of Array.isArray(i.followupIds)?i.followupIds:[])c.push(`fu:${l}`);if(c.length)for(let l of c)s(l,i.action);else for(let l of o.keys())s(l,i.action)}return o}function c_(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 b_(e,{offerId:t,followupId:n=null,armId:r=null,promoted:o=null,all:s=!1}={},a={}){if(!t)throw new Error("offerId required");return vo(`fu:${e.id}:${t}`,()=>N2(e,{offerId:t,followupId:n,armId:r,promoted:o,all:s===!0},a))}async function N2(e,{offerId:t,followupId:n,armId:r,promoted:o,all:s},a){let{sayToLead:i,unarchiveTrack:c,getFollowup:l=n_}=a,u={id:e.id,cwd:e.cwd},d=await K(u),p=d.find(A=>A.type==="followup.offered"&&A.payload?.offerId===t);if(!p)throw new Error("unknown offer");let f=p.payload.rows||[],w=M2(d,p.payload),h=C2(w,f);if(!h.length)throw new Error("offer already answered");let y=A=>N(u,{type:"followup.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,...A}}),k=async()=>{if(!e.archivedAt)return!1;if(!c)throw new Error("this Track is archived");return await c(e.id,{freshSession:!0}),e={...e,archivedAt:null},!0},g=A=>{let L=d.find(H=>H.type==="followup.armed"&&H.payload?.armId===A.armId)?.payload?.prompt??A.prompt;if(!L)throw new Error("the arm's prompt is missing from the log");return L};if(s){let A=p.payload.groupId??null,$=h.filter(B=>B.armId&&(!A||B.groupId===A)).sort((B,Z)=>(B.seq??0)-(Z.seq??0));if(!$.length)throw new Error("nothing to run together \u2014 no open items from this plan remain on the card");let L=$.map(B=>({row:B,prompt:g(B)})),H=await k();if(!i)throw new Error("sayToLead unavailable");let R=`${c_(p.payload).replace(/:$/,"")} \u2014 ${L.length} steps, in order:`,T=L.map((B,Z)=>`${Z+1}. ${B.row.label}
|
|
204
|
+
`).trim().slice(0,300)||null}catch{return{recorded:!1,reason:"unknown-sha"}}let l=NE(t,i),u=await z4(r,n),d=await J4(r),p=await N(e,{type:"code.committed",actor:"agent",source:"hook",boxId:t.name,provider:t.agent??null,payload:{sha:n,ref:d,subject:c,...u,ts:i}}),f=null,w=!1;try{let h=await OE(r,n,{fixKey:Fa({trackId:e.id,boxId:t.name,epoch:l}),window:xo,context:{...V4(e,t,a,l),ref:d,provider:t.agent??null}});if(h&&(f=await N(e,{type:"code.rework",actor:"system",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:p.id,payload:h}),!(e?.workspace?.kind==="worktree"&&!!e?.workspace?.branch)&&ME(h))){let k=Bc(h),g=h.origins.filter(x=>x.class==="rework").slice(0,3);await N(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:f.id,payload:{sha:n,subject:h.subject,linesReworked:k,origins:g.map(x=>({key:x.key,provider:x.provider,model:x.model,lines:x.lines,ageDays:x.ageDays})),heuristic:h.heuristic,intent:h.intent,ts:i}}),w=!0}}catch(h){o.warn?.(`[rework] ${e.id}: analysis of ${String(n).slice(0,7)} failed: ${h?.message||h}`)}return{recorded:!0,event:p,rework:f,offered:w}}function BE(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 LE(e,t,n){if(!Dc.includes(n)&&n!==cp)throw new Error(`bad cause "${n}" (${Dc.join("|")}|${cp})`);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(BE(r,t))throw new Error("already answered");return N(e,{type:"rework.cause.answered",actor:"user",source:"orchestrator",boxId:o.boxId??null,causationId:o.id,payload:{sha:t,...Array.isArray(o.payload?.shas)?{shas:o.payload.shas}:{},cause:n,level:"B",ts:Date.now()}})}async function jE(e,t){let n=await K(e);if(!BE(n,t))throw new Error("no standing answer to retract");let r=[...n].reverse().find(o=>o.type==="rework.cause.answered"&&o.payload?.sha===t);return N(e,{type:"rework.cause.retracted",actor:"user",source:"orchestrator",boxId:r?.boxId??null,causationId:r?.id,payload:{sha:t,...Array.isArray(r?.payload?.shas)?{shas:r.payload.shas}:{},ts:Date.now()}})}async function pp(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(g=>g.type==="rework.cause.offered"&&g.payload?.pr?.number===n&&(t==null||g.payload.pr.repo===t)))return{offered:!1,reason:"duplicate"};let l=new Set;for(let g of c)if(g.type==="rework.cause.offered")for(let x of Array.isArray(g.payload?.shas)?g.payload.shas:[g.payload?.sha])x&&l.add(x);let u=new Map;for(let g of c)g.type==="code.committed"&&g.payload?.sha&&u.set(g.payload.sha,g.payload.ref??null);let d=Pw({heuristic:up({subject:r}),subject:r,ref:o}),p=[];for(let g of c){if(g.type!=="code.rework")continue;let x=g.payload||{};if(!x.sha||l.has(x.sha)||x.codeDelta===!1||o&&u.has(x.sha)&&u.get(x.sha)!==o)continue;let S=x.intent?.intent??"unknown";(Ua.has(S)||S==="unknown"&&Ua.has(d.intent))&&Bc(x)!==0&&p.push(x)}let f=p.reduce((g,x)=>g+Bc(x),0);if(f<Nw)return{offered:!1,reason:"below-threshold"};let w=new Map;for(let g of p)for(let x of g.origins??[]){if(x.class!=="rework")continue;let S=w.get(x.key)??{key:x.key,provider:x.provider??null,model:x.model??null,lines:0,ageDays:x.ageDays};S.lines+=x.lines,S.ageDays=Math.min(S.ageDays,x.ageDays),w.set(x.key,S)}let h=p.map(g=>g.sha),y=p.map(g=>g.heuristic).find(g=>g&&g.level!=="C")??up({subject:r})??p.map(g=>g.heuristic).find(Boolean)??null,k=Ua.has(d.intent)?d:p.find(g=>Ua.has(g.intent?.intent))?.intent??d;return await N(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((g,x)=>x.lines-g.lines).slice(0,3),heuristic:y,intent:k,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 q4,Dw,fp=G(()=>{be();ad();dp();q4=W4(H4),Dw=(e,t)=>q4("git",["-C",e,...t],{maxBuffer:16*1024*1024,timeout:3e4})});import{execFile as Y4}from"node:child_process";import{promisify as X4}from"node:util";async function Bw(e,t,{midTurn:n=is,pollMs:r=Z4,capMs:o=Q4}={}){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 GE(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){FE.lastIndex=n;let o=FE.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(`
|
|
205
|
+
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+a2(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 Lw(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function VE(e){let t=GE(e),n=t.match(r2);if(!n)return null;let r=Lw(t,n.index);return s2.test(r)?null:{repo:r.match(KE)?.[1]??null,head:r.match(i2)?.[1]??null}}function zE(e){let t=GE(e),n=t.match(o2);if(!n)return null;let r=Lw(t,n.index),o=Lw(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("-")){l2.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(KE)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function d2(e,t,{exclude:n=()=>!1}={}){let r=async i=>{Sr("observer");let{stdout:c}=await UE("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",u2],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw yo(l)&&bo(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-e2&&!n(c)),s=await Bn({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 p2(e,t){if(t)return[t];let n=[];for(let r of JE(e)){let o=await Me(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await fr().catch(()=>[])){let o=await Me(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function JE(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 f2(e,t,n){let r=await ja(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)||c2(c.headRefName,e.id),i=[];for(let c of await p2(e,t?.repo??null)){let l;try{l=await d2(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await ft(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 YE(e){if(!e)return 0;let t=0;for(let n of Ar)n.startsWith(`${e}|`)&&t++;return t}function XE(){for(let e of[...jw])e()}function ZE(e,t,{delays:n=HE,turnEnd:r}={}){let o=VE(t.command);if(!o)return null;let s=!!zE(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(Ar.has(a))return null;Ar.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(h=>{let y=()=>{clearTimeout(k),jw.delete(y),h()},k=setTimeout(y,l);jw.add(y)});let u=await f2(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:d,pr:p}=u,{link:f,created:w}=await Yt({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 xr(d,p.number))?null:(await Bw(t.box,t.agent,r),await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"opened",number:p.number,url:p.url,title:p.title,branch:p.headRefName,repo:d,...s?{selfMerge:!0}:{}}}),p)}return null}finally{Ar.delete(a)}})();return c.catch(()=>Ar.delete(a)),c}function QE(e,t,{delays:n=HE,turnEnd:r,openedWait:o}={}){let s=zE(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(Ar.has(a))return null;Ar.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=JE(e);if(w.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let h of w){let y=await Me(h).catch(()=>null);y&&!d.includes(y)&&d.push(y)}}if(l||=(await ja(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 y=await ft(c,w.number);return y&&y.trackId!==e.id?null:(y||await Yt({repo:c,number:w.number,trackId:e.id,url:w.url,headRef:w.headRefName,title:w.title,source:"merge-observed"}),await $c(c,w.number),h&&((await K(e).catch(()=>[])).some(x=>x.type==="pr.state.changed"&&(x.payload?.change==="auto-armed"||x.payload?.change==="merged")&&x.payload?.number===w.number)||(await Bw(t.box,t.agent,r),await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:w.number,url:w.url,title:w.title,branch:w.headRefName,repo:c}}))),w)},f=null;for(let w of n){await new Promise(M=>setTimeout(M,w));let h=[];for(let M of d)try{Sr("observer");let{stdout:P}=await UE("gh",["pr","view",String(l),"--repo",M,"--json",m2],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await Bn({slug:M})}}),j=JSON.parse(P||"null");j?.number&&h.push({slug:M,pr:j})}catch(P){yo(P)&&bo(P)}let y=h.find(M=>M.pr.state==="MERGED")??(h.length===1?h[0]:null);if(!y)continue;c=y.slug;let k=y.pr;if(!k?.number)continue;if(k.state==="CLOSED")return null;if(k.state!=="MERGED"){if(f=k,s.auto)return p(k,!0);continue}let g=await ft(c,k.number);if(g&&g.trackId!==e.id)return null;g||await Yt({repo:c,number:k.number,trackId:e.id,url:k.url,headRef:k.headRefName,title:k.title,source:"merge-observed"}),await Bw(t.box,t.agent,r);let x=await K(e).catch(()=>[]);if(x.some(M=>M.type==="pr.state.changed"&&M.payload?.change==="merged"&&M.payload?.number===k.number))return null;let S=M=>M.some(P=>P.type==="pr.state.changed"&&P.payload?.change==="opened"&&P.payload?.number===k.number),_=0;if(!S(x)){if(VE(t.command)&&await xr(c,k.number)){let M=Date.now();_=M+1,await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:M,change:"opened",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c,selfMerge:!0}})}else if((await ft(c,k.number))?.announced){let M=o?.capMs??t2,P=o?.pollMs??n2,j=Date.now()+M;for(;Date.now()<j&&(await new Promise(A=>setTimeout(A,P)),!S(await K(e).catch(()=>[]))););}}return await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Math.max(Date.now(),_),change:"merged",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c}}),await pp(e,{repo:c,number:k.number,title:k.title,branch:k.headRefName,url:k.url}).catch(()=>{}),await _r(c,k.number).catch(()=>{}),await Er(c,k.number).catch(()=>{}),await wo(c,k.number,"merged"),k}return f?p(f,!1):null}finally{Ar.delete(a)}})();return i.catch(()=>Ar.delete(a)),i}var UE,HE,Z4,Q4,e2,t2,n2,WE,qE,r2,o2,s2,FE,a2,KE,i2,c2,l2,u2,Ar,jw,m2,mp=G(()=>{be();Gr();vt();Oc();Gn();Ir();va();Ss();fp();UE=X4(Y4),HE=[4e3,15e3,45e3],Z4=2500,Q4=5*6e4;e2=15e3,t2=9e4,n2=2e3,WE=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,qE=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,r2=new RegExp(`${WE}${qE}create\\b`),o2=new RegExp(`${WE}${qE}merge\\b`),s2=/\s--web\b/,FE=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,a2=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");KE=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,i2=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,c2=vE;l2=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);u2="number,url,title,headRefName,createdAt";Ar=new Set;jw=new Set;m2="number,url,title,headRefName,state"});import h2 from"node:fs/promises";import Uw from"node:path";import w2 from"node:os";function Fw(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function hp(e){let t=g2.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=Fw(n,"from"),s=o?o.replace(/^uds:/,""):null,a=e_(s);return{socket:s,pid:a,name:Fw(n,"from-name"),mode:Fw(n,"from-mode"),text:r.trim()}}function e_(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function y2(){return process.env.CLAUDE_CONFIG_DIR?Uw.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):Uw.join(w2.homedir(),".claude","sessions")}async function b2(e){let t=e_(e);if(!t)return null;try{let n=await h2.readFile(Uw.join(y2(),`${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 t_(e,t){let n=await b2(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 g2,Hw=G(()=>{g2=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function n_(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}async function r_(e,t,n=Ww){let r=new AbortController,o=setTimeout(()=>r.abort(),n);try{return await x2(`${De(t)}${e}`,{headers:v2(t),signal:r.signal})}finally{clearTimeout(o)}}async function Lc({env:e=process.env,force:t=!1,timeoutMs:n=Ww}={}){if(!n_(e))return{followups:[],source:"unconfigured"};if(!t&&Ts&&Date.now()-Ts.at<k2)return{followups:Ts.followups,source:"cache"};try{let r=await r_("/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 Ts={at:Date.now(),followups:s},{followups:s,source:"plane"}}catch(r){return Ts?{followups:Ts.followups,source:"stale",error:r?.message}:{followups:[],source:"unreachable",error:r?.message}}}async function o_(e,{env:t=process.env,timeoutMs:n=Ww}={}){if(!e)return{followup:null,source:"unconfigured"};if(!n_(t))return{followup:null,source:"unconfigured"};try{let r=await r_(`/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=Ts?.followups.find(s=>s.id===e)??null;return o?{followup:o,source:"stale",error:r?.message}:{followup:null,source:"unreachable",error:r?.message}}}var k2,Ww,Ts,x2,v2,s_=G(()=>{vt();k2=6e4,Ww=5e3,Ts=null,x2=(...e)=>fetch(...e);v2=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});async function vo(e,t){if(qw.has(e)){let n=new Error("offer already answered (answer in flight)");throw n.code="answer-in-flight",n}qw.add(e);try{return await t()}finally{qw.delete(e)}}var qw,jc=G(()=>{qw=new Set});import{randomUUID as As,createHash as S2}from"node:crypto";function wp(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function m_(e){for(let[t,n]of _2)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 l_(e,{at:t="",promptRequired:n=!0,standing:r=!1}={}){let o=e&&typeof e=="object"?e:{},s=Es(o.label).replace(/\s+/g," ");if(!s)throw new Error(`${t}label required (\u2264${Gw} chars, e.g. "Deploy to Pi")`);if(s.length>Gw)throw new Error(`${t}label must be \u2264${Gw} characters`);let a=Es(o.prompt);if(n&&!a)throw new Error(`${t}prompt required \u2014 the full instruction a fresh session could execute`);if(Buffer.byteLength(a,"utf8")>a_)throw new Error(`${t}prompt must be \u2264${a_} bytes`);if(r){let l=m_(a);if(l)throw new Error(`${t}${l}`)}let i=o.standing==null||o.standing===""?"offerable":String(o.standing).trim().toLowerCase();if(!E2.includes(i))throw new Error(`${t}standing must be "offerable" or "never", got "${o.standing}"`);let c=Es(o.why).slice(0,d_)||null;return{label:s,prompt:a,why:c,standing:i}}function h_(e,{standing:t=!1}={}){let n=e&&typeof e=="object"?e:{},r=Es(n.trigger)||(n.ship?"now":""),o=t?T2:p_.filter(p=>p!=="track.opened");if(!o.includes(r))throw new Error(`trigger must be one of ${o.join(" | ")}${t?" (a standing follow-up can't be `now` \u2014 it would fire every turn)":""}, got "${n.trigger??""}"`);let s=n.ship&&r==="now"?R2(n.ship):null,a=n.items!=null;if(a&&s)throw new Error("a ship offer has its own card \u2014 `items` cannot be combined with a `ship` block");let i,c=null;if(a){if(!Array.isArray(n.items)||!n.items.length)throw new Error("items must be a non-empty array of {label, prompt, why?, standing?}");if(n.items.length>c_)throw new Error(`items must be \u2264${c_} \u2014 one item per step the operator might run, save or skip separately`);if(t&&n.items.length>1)throw new Error("propose one standing follow-up at a time \u2014 a standing follow-up is one prompt");i=n.items.map((f,w)=>l_(f,{at:`items[${w}]: `,standing:t})),c=Es(n.why).slice(0,d_)||null;let p=new Set;for(let f of i){let w=wp(f.label);if(p.has(w))throw new Error(`items must have distinct labels ("${f.label}" appears twice)`);p.add(w)}}else i=[l_(n,{promptRequired:!s,standing:t})];let l=i[0],u=c??l.why,d=t?$2(n.repo):null;return{label:l.label,prompt:l.prompt,trigger:r,why:u,planWhy:c,items:i,...d?{repo:d}:{},...s?{ship:s}:{}}}function I2(e){return e.standing==="never"?{promotable:!1,promotableReason:"one-off"}:m_(e.prompt)?{promotable:!1,promotableReason:"specifics"}:{promotable:!0,promotableReason:null}}function $2(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 R2(e){let t=e&&typeof e=="object"?e:{},n=Array.isArray(t.actions)?t.actions.map(i=>String(i).trim().toLowerCase()):Kw.slice();for(let i of n)if(!Kw.includes(i))throw new Error(`ship.actions may only contain ${Kw.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=Es(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=>Es(i)).filter(Boolean),a.length||(a=null)}return{actions:[...new Set(n)],rebase:r,toPr:o,base:s,repos:a}}function gp(e,{trigger:t=null}={}){let n=new Map;for(let r of e){let o=r.payload||{};if(r.type==="followup.armed"&&o.armId)o.replaces&&n.delete(o.replaces),n.set(o.armId,{...o,ts:o.ts??r.ts,boxId:r.boxId??null,provider:r.provider??null});else if(r.type==="followup.armed.cancelled"&&o.armId)n.delete(o.armId);else if(r.type==="followup.offered")for(let s of o.rows||[])s.armId&&n.delete(s.armId)}return[...n.values()].filter(r=>!t||r.trigger===t)}function w_(e){return{armId:e.armId,label:e.label,prompt:e.prompt,promptHash:f_(e.prompt),origin:"agent",why:e.why??null,groupId:e.groupId??null,seq:Number.isInteger(e.seq)?e.seq:0,standing:e.standing==="never"?"never":"offerable",...I2(e)}}async function g_(e,t,{pull:n=Lc,ship:r={}}={}){if(zw(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=h_(t);if(o.ship)return{kind:"ship",...await P2(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},c=As(),l=(g,x,S={})=>({ts:Date.now(),armId:As(),trigger:o.trigger,label:g.label,prompt:g.prompt,why:g.why,standing:g.standing,groupId:c,seq:x,...o.planWhy?{planWhy:o.planWhy}:{},...S}),u={groupId:c,trigger:o.trigger,label:o.label,prompt:o.prompt,why:o.why,planWhy:o.planWhy};if(o.trigger==="now"){let g=As(),x=[];for(let[S,_]of o.items.entries()){let M=l(_,S);await N(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:M}),x.push(M)}return await N(s,{type:"followup.offered",actor:"system",source:"orchestrator",...i,payload:{ts:Date.now(),offerId:g,trigger:"now",repo:e.workspace?.repo||null,context:{},groupId:c,why:o.planWhy,rows:x.map(w_)}}),{kind:"now",offerId:g,armId:x[0].armId,items:x.map(S=>({armId:S.armId,label:S.label,standing:S.standing})),...u}}let d=e.workspace?.repo||null,{followups:p}=await n().catch(()=>({followups:[]})),f=await K(s),w=gp(f,{trigger:o.trigger}),h=[],y=0;for(let g of o.items){let x=wp(g.label),S=p.find(j=>j.status==="active"&&j.mode!=="off"&&j.trigger===o.trigger&&(j.repo==null||j.repo===d)&&j.dedupeKey===x);if(S){h.push({label:g.label,standing:g.standing,covered:{id:S.id,label:S.label}});continue}let _=w.filter(j=>wp(j.label)===x),M=_.length?_[_.length-1].armId:null,P=l(g,y++,M?{replaces:M}:{});await N(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:P}),h.push({armId:P.armId,label:g.label,standing:g.standing,replaces:M})}let k=h.filter(g=>g.armId);return k.length?{kind:"armed",armId:k[0].armId,replaces:k[0].replaces,items:h,...u}:{kind:"covered",covered:h[0].covered,items:h,...u}}async function y_(e,t){if(!t)throw new Error("armId required");let n={id:e.id,cwd:e.cwd},r=await K(n);if(!gp(r).some(o=>o.armId===t))throw new Error("unknown or already-settled arm");return await N(n,{type:"followup.armed.cancelled",actor:"user",source:"orchestrator",payload:{ts:Date.now(),armId:t}}),{armId:t}}async function b_(e,t,{pull:n=Lc}={}){if(zw(e))throw new Error("propose from a lead Track, not a loop's run Track");let r=h_(t,{standing:!0}),o=r.repo??e.workspace?.repo??null,s={id:e.id,cwd:e.cwd};if((await K(s)).reduce((y,k)=>k.type==="followup.create.offered"?k.payload?.proposalId??y:k.type==="followup.create.answered"&&k.payload?.proposalId===y?null:y,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=wp(r.label),d=c.filter(y=>y.dedupeKey===u&&y.trigger===r.trigger),p=d.find(y=>y.status==="deleted"),f=d.find(y=>y.status!=="deleted");if(f)throw new Error(`"${f.label}" (${Vw[f.trigger]||f.trigger}) already exists as a standing follow-up \u2014 not re-proposed`);if(p){let y=p.deletedAt?new Date(p.deletedAt).toISOString().slice(0,10):"earlier";throw new Error(`the operator deleted "${p.label}" on ${y} \u2014 do not re-propose it`)}let w=As(),h=(e.boxes||[]).find(y=>y.name===e.leadBox)||null;return await N(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 k_(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 N(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};if(!r)throw new Error("followupId required \u2014 the follow-up is created on the plane before the card is answered");let i=a.payload;return await N(o,{type:"followup.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,followupId:String(r),label:i.label,trigger:i.trigger,repo:i.repo??null}}),await N(o,{type:"followup.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",followupId:String(r)}}),{proposalId:t,action:"created",followupId:String(r)}}async function Fc(e,t,n={},{pull:r=Lc}={}){if(!e||zw(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 k=S=>{let _=(S?.prs||[]).filter(M=>Number.isFinite(Number(M?.number))).map(M=>`${M?.repo??S?.repo??""}#${Number(M.number)}`);return Number.isFinite(Number(S?.prNumber))&&_.push(`${S?.repo??""}#${Number(S.prNumber)}`),_},g=new Set(k(n));if(s.some(S=>S.type==="followup.offered"&&S.payload?.trigger==="pr.merged"&&k(S.payload?.context).some(_=>g.has(_))))return null}let i=gp(s,{trigger:t}),c=i.length?i[i.length-1].groupId??null:null,l=new Map(i.map((k,g)=>[k.armId,g])),u=k=>k.groupId&&k.groupId===c?0:1,d=i.slice().sort((k,g)=>u(k)-u(g)||l.get(k.armId)-l.get(g.armId)).slice(0,i_).map(w_),p=c?i.find(k=>k.groupId===c&&k.planWhy)?.planWhy??null:null,f=[];if(t!=="now"){let{followups:k}=await r().catch(()=>({followups:[]}));f=k.filter(g=>g.status==="active"&&g.mode!=="off"&&g.trigger===t&&(g.repo==null||g.repo===a)).sort((g,x)=>{let S=_=>_.stats?.offered?(_.stats.accepted||0)/_.stats.offered:0;return S(x)-S(g)||(g.origin==="operator"?-1:0)-(x.origin==="operator"?-1:0)||(x.createdAt||0)-(g.createdAt||0)}).slice(0,A2).map(g=>({followupId:g.id,label:g.label,promptPreview:O2(g.prompt),promptHash:f_(g.prompt),origin:g.origin,repo:g.repo??null}))}let w=[...d,...f].slice(0,i_);if(!w.length)return null;let h=As(),y={ts:Date.now(),offerId:h,trigger:t,repo:a,context:{...n,repo:a},groupId:c,why:p,rows:w};return await N(o,{type:"followup.offered",actor:"system",source:"orchestrator",payload:y}),y}function M2(e,t){let n=t?.rows||[],r=a=>a.armId?`arm:${a.armId}`:`fu:${a.followupId}`,o=new Map(n.map(a=>[r(a),null])),s=(a,i)=>{o.has(a)&&o.get(a)==null&&o.set(a,i)};for(let a of e){if(a.type!=="followup.answered"||a.payload?.offerId!==t.offerId)continue;let i=a.payload,c=[];i.armId&&c.push(`arm:${i.armId}`),i.followupId&&c.push(`fu:${i.followupId}`);for(let l of Array.isArray(i.armIds)?i.armIds:[])c.push(`arm:${l}`);for(let l of Array.isArray(i.followupIds)?i.followupIds:[])c.push(`fu:${l}`);if(c.length)for(let l of c)s(l,i.action);else for(let l of o.keys())s(l,i.action)}return o}function u_(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 x_(e,{offerId:t,followupId:n=null,armId:r=null,promoted:o=null,all:s=!1}={},a={}){if(!t)throw new Error("offerId required");return vo(`fu:${e.id}:${t}`,()=>N2(e,{offerId:t,followupId:n,armId:r,promoted:o,all:s===!0},a))}async function N2(e,{offerId:t,followupId:n,armId:r,promoted:o,all:s},a){let{sayToLead:i,unarchiveTrack:c,getFollowup:l=o_}=a,u={id:e.id,cwd:e.cwd},d=await K(u),p=d.find(A=>A.type==="followup.offered"&&A.payload?.offerId===t);if(!p)throw new Error("unknown offer");let f=p.payload.rows||[],w=M2(d,p.payload),h=C2(w,f);if(!h.length)throw new Error("offer already answered");let y=A=>N(u,{type:"followup.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,...A}}),k=async()=>{if(!e.archivedAt)return!1;if(!c)throw new Error("this Track is archived");return await c(e.id,{freshSession:!0}),e={...e,archivedAt:null},!0},g=A=>{let L=d.find(H=>H.type==="followup.armed"&&H.payload?.armId===A.armId)?.payload?.prompt??A.prompt;if(!L)throw new Error("the arm's prompt is missing from the log");return L};if(s){let A=p.payload.groupId??null,$=h.filter(B=>B.armId&&(!A||B.groupId===A)).sort((B,Z)=>(B.seq??0)-(Z.seq??0));if(!$.length)throw new Error("nothing to run together \u2014 no open items from this plan remain on the card");let L=$.map(B=>({row:B,prompt:g(B)})),H=await k();if(!i)throw new Error("sayToLead unavailable");let R=`${u_(p.payload).replace(/:$/,"")} \u2014 ${L.length} steps, in order:`,T=L.map((B,Z)=>`${Z+1}. ${B.row.label}
|
|
206
206
|
${B.prompt}`).join(`
|
|
207
207
|
|
|
208
208
|
`),C=await i(e.id,`${R}
|
|
209
209
|
|
|
210
|
-
${T}`,{kind:"followup-go"}),W=As();for(let[B,Z]of L.entries())await y({action:"sent",armId:Z.row.armId,label:Z.row.label,batchId:W,...B===0&&H?{revived:!0}:{},source:`arm:${Z.row.armId}`});return{offerId:t,action:"sent",all:!0,batchId:W,armIds:L.map(B=>B.row.armId),revived:H,remaining:h.length-L.length,queued:!!C?.queued}}if(!n&&!r)return await y({action:"dismiss",followupIds:h.map(A=>A.followupId).filter(Boolean),armIds:h.map(A=>A.armId).filter(Boolean)}),{offerId:t,action:"dismiss",remaining:0};let x=null,S=null,_=n?`fu:${n}`:`arm:${r}`;if(n){if(!f.find(H=>H.followupId===n))throw new Error("that follow-up is not on this card");if(w.get(_)!=null)throw new Error("that row is already answered");let{followup:$,source:L}=await l(n);if(L==="unreachable")throw new Error("plane unreachable \u2014 the follow-up's current prompt can't be read; try again shortly");if(!$||$.status!=="active"){await y({action:"removed",followupId:n});let H=new Error("removed in Settings since it was offered");throw H.code="removed",H}x=$.prompt,S=$.label}else{let A=f.find($=>$.armId===r);if(!A)throw new Error("that arm is not on this card");if(w.get(_)!=null)throw new Error("that row is already answered");x=g(A),S=A.label}let M=await k();if(!i)throw new Error("sayToLead unavailable");let P=`${
|
|
210
|
+
${T}`,{kind:"followup-go"}),W=As();for(let[B,Z]of L.entries())await y({action:"sent",armId:Z.row.armId,label:Z.row.label,batchId:W,...B===0&&H?{revived:!0}:{},source:`arm:${Z.row.armId}`});return{offerId:t,action:"sent",all:!0,batchId:W,armIds:L.map(B=>B.row.armId),revived:H,remaining:h.length-L.length,queued:!!C?.queued}}if(!n&&!r)return await y({action:"dismiss",followupIds:h.map(A=>A.followupId).filter(Boolean),armIds:h.map(A=>A.armId).filter(Boolean)}),{offerId:t,action:"dismiss",remaining:0};let x=null,S=null,_=n?`fu:${n}`:`arm:${r}`;if(n){if(!f.find(H=>H.followupId===n))throw new Error("that follow-up is not on this card");if(w.get(_)!=null)throw new Error("that row is already answered");let{followup:$,source:L}=await l(n);if(L==="unreachable")throw new Error("plane unreachable \u2014 the follow-up's current prompt can't be read; try again shortly");if(!$||$.status!=="active"){await y({action:"removed",followupId:n});let H=new Error("removed in Settings since it was offered");throw H.code="removed",H}x=$.prompt,S=$.label}else{let A=f.find($=>$.armId===r);if(!A)throw new Error("that arm is not on this card");if(w.get(_)!=null)throw new Error("that row is already answered");x=g(A),S=A.label}let M=await k();if(!i)throw new Error("sayToLead unavailable");let P=`${u_(p.payload)}
|
|
211
211
|
|
|
212
|
-
${x}`,j=await i(e.id,P,{kind:"followup-go"});return await y({action:"sent",...n?{followupId:n}:{armId:r},label:S,...o?{promoted:String(o)}:{},...M?{revived:!0}:{},source:n?`followup:${n}`:`arm:${r}`}),{offerId:t,action:"sent",followupId:n,armId:r,promoted:o,revived:M,remaining:h.length-1,queued:!!j?.queued}}async function P2(e,t,{changedRepos:n=null,probe:r=
|
|
212
|
+
${x}`,j=await i(e.id,P,{kind:"followup-go"});return await y({action:"sent",...n?{followupId:n}:{armId:r},label:S,...o?{promoted:String(o)}:{},...M?{revived:!0}:{},source:n?`followup:${n}`:`arm:${r}`}),{offerId:t,action:"sent",followupId:n,armId:r,promoted:o,revived:M,remaining:h.length-1,queued:!!j?.queued}}async function P2(e,t,{changedRepos:n=null,probe:r=rp,branchOf:o=ko}={}){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(k=>k.name===e.leadBox)||null,c=e.workspace?.kind==="multi",l;if(c&&n){let k=await n(e).catch(()=>[]),g=s.repos?k.filter(x=>s.repos.includes(x.name)):k;l=g.length?g.map(x=>({name:x.name,slug:x.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(k=>ho.includes(k));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=As(),h=t.why?`${t.label} \u2014 ${t.why}`:t.label,y=Jw(await K(a).catch(()=>[]));return await N(a,{type:"pr.offered",actor:"agent",source:"cli",boxId:e.leadBox??null,provider:i?.agent??null,payload:{offerId:w,ts:Date.now(),excerpt:h,via:"cli",branch:u,base:s.base||e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:"lead",available:p?.available,defaults:p?p.defaults.filter(k=>f.includes(k)||k==="rebase"):void 0,prState:p?.prState,openPr:p?.openPr||void 0,repos:p?.repos||l,offered:p?f.filter(k=>p.available.includes(k)):f,toPr:s.toPr?{number:s.toPr}:void 0}}),{offerId:w,branch:u,offered:f,available:p?.available??null,prState:p?.prState??null,replaced:y}}function Jw(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 v_(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 S_(e,{pull:t=Lc}={}){let{followups:n,source:r,error:o}=await t().catch(a=>({followups:[],source:"unreachable",error:a?.message})),s=e?await K({id:e.id,cwd:e.cwd}).catch(()=>[]):[];return{followups:n,source:r,error:o,arms:e?gp(s):[]}}var Gw,a_,d_,p_,T2,Vw,A2,i_,c_,E2,Kw,f_,Es,_2,zw,O2,C2,yp=G(()=>{be();gs();Iw();Ss();s_();jc();Gw=60,a_=8*1024,d_=200,p_=["now","pr.merged","ship.done","pr.opened","checks.green","track.opened"],T2=p_.filter(e=>e!=="now"&&e!=="track.opened"),Vw={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"},A2=3,i_=6,c_=5,E2=["offerable","never"],Kw=["commit","push","pr"];f_=e=>S2("sha256").update(String(e)).digest("hex").slice(0,16),Es=e=>typeof e=="string"?e.trim():"",_2=[[/(^|[^\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"]];zw=e=>String(e?.name||"").startsWith("\u21BB");O2=e=>String(e||"").split(`
|
|
213
213
|
`).slice(0,3).join(`
|
|
214
|
-
`).slice(0,400);C2=(e,t)=>t.filter(n=>e.get(n.armId?`arm:${n.armId}`:`fu:${n.followupId}`)==null)});import{randomUUID as D2}from"node:crypto";function Uc(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function
|
|
215
|
-
`)){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 L2(e,t){let n=`${e.id}:${t.name}`;if(!Zw.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);Zw.set(n,o)}return Zw.get(n)}async function $_(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await Zo(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await L2(e,t),o=B2(await jt(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||t.model||"gpt-5.5";await N(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:Wn(c,i,a.ts),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await N(e,{type:"context.usage",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{occupancy:a.occupancy,first:null,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}var Zw,R_=G(()=>{be();es();vn();yr();Zw=new Map});import bp from"node:fs";import eg from"node:path";import j2 from"node:os";async function H2({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(U2,{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:O_})});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<O_)break}return r}function W2(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 C_(){try{return JSON.parse(bp.readFileSync(kp(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function G2(e){try{bp.mkdirSync(eg.dirname(kp()),{recursive:!0});let t=kp()+".tmp";bp.writeFileSync(t,JSON.stringify(e)),bp.renameSync(t,kp())}catch{}}async function V2({fetchImpl:e,now:t=Date.now()}={}){if(So.rows&&t-So.at<q2)return So.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=C_(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await H2({sinceMs:o,cookie:n,fetchImpl:e});So={at:t,rows:s.map(W2).filter(Boolean),error:null}}catch(s){So={at:t,rows:null,error:String(s?.message||s)}}return So.rows}async function z2(e,t){let n=`${e.id}:${t.name}`;if(!Qw.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);Qw.set(n,o)}return Qw.get(n)}async function N_(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!M_())return;let r=await V2({fetchImpl:n});if(!r||!r.length)return;let o=await LS(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await z2(e,t),a=C_(),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=Wn(u,d,l.ts??void 0)),!p&&l.chargedCents!=null&&(p=l.chargedCents/100),await N(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.name,provider:"cursor",payload:{usd:p,model:u,...d?{tokens:d}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}G2({sinceMs:c,seen:[...i].slice(-K2)})}catch{}}var F2,M_,U2,O_,q2,So,kp,K2,tg,Qw,ng=G(()=>{be();Mh();yr();F2=process.env.BOSTRAT_STATE_DIR||eg.join(process.env.HOME||j2.tmpdir(),".bostrat"),M_=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,U2="https://cursor.com/api/dashboard/get-filtered-usage-events",O_=100;q2=5*6e4,So={at:0,rows:null,error:null},kp=()=>eg.join(F2,"cursor-usage.json");K2=5e3;tg=()=>({enabled:M_(),lastPollAt:So.at||null,error:So.error}),Qw=new Map});import{randomUUID as J2,createHmac as Y2}from"node:crypto";function D_(e){og=typeof e=="string"&&e?e:null}function X2(e){if(!og)return null;let t=He(e);return t?Y2("sha256",Buffer.from(og,"base64url")).update(t,"utf8").digest("hex").slice(0,16):null}function j_(){let e={};for(let t of L_)e[String(t)]=0;return e.inf=0,{buckets:e,sumMs:0,count:0}}function P_(e,t){let n=Math.max(0,Number(t)||0);for(let r of L_)n<=r&&(e.buckets[String(r)]+=1);e.buckets.inf+=1,e.sumMs+=n,e.count+=1}function F_(){let e={};for(let t of B_)e[t]=0;return e}function Q2(){zn={total:new Map,holds:F_(),queueWaitMs:j_(),readyWaitMs:new Map,verify:new Map,retries:new Map}}function U_({queueDepth:e=0,queueOldestAgeMs:t=0}={}){let n=zn;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 Wc({kind:e=null,producer:t=null,orchestration:n=!1}={}){return n?`orchestration:${e||"orchestration"}`:e?`scripted:${e}`:Z2.has(t)?t:"composer"}function qc({text:e="",producer:t="composer",kind:n=null,provider:r=null,attachments:o=0,now:s=Date.now}={}){let a=s();return{id:J2(),producer:t,kind:n||null,provider:r||null,textBytes:Buffer.byteLength(String(e||""),"utf8"),textHmac16:X2(e),attachments:Number(o)||0,paste:null,t0:a,queuedAt:null,dequeuedAt:null,holds:F_(),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 N(e,{type:"delivery.queued",actor:"system",source:"orchestrator",boxId:e.leadBox??null,provider:t.provider,payload:{ts:t.queuedAt,deliveryId:t.id,producer:t.producer,...t.kind?{kind:t.kind}:{},reason:r,position:n,textBytes:t.textBytes,textHmac16:t.textHmac16,requeued:t.requeued}}).catch(()=>{}))}function H_(e,t){B_.includes(t)&&(e?.holds&&(e.holds[t]+=1),zn.holds[t]+=1)}function Kc(e,{ms:t,outcome:n}){e&&(e.readyWaitMs=Math.max(0,Number(t)||0),e.readyOutcome=n||null)}function eH(e){let t=e?.boxes?.find?.(n=>n.name===e.leadBox);return typeof t?.cliVersion=="string"&&t.cliVersion?t.cliVersion:null}async function In(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(rg(zn.total,`${s}|${t.producer}|${n}`),a&&P_(zn.queueWaitMs,i),t.readyWaitMs!=null){let u=`${s}|${t.readyOutcome||"unknown"}`;zn.readyWaitMs.has(u)||zn.readyWaitMs.set(u,j_()),P_(zn.readyWaitMs.get(u),t.readyWaitMs)}let c=eH(e);t.verify?.verdict&&(rg(zn.verify,`${s}|${t.verify.verdict}|${c||""}`),t.verify.retries&&rg(zn.retries,s,t.verify.retries));let l={ts:o,deliveryId:t.id,producer:t.producer,...t.kind?{kind:t.kind}:{},cliVersion:c,textBytes:t.textBytes,textHmac16:t.textHmac16,paste:t.paste,attachments:t.attachments,outcome:n,queued:a,queueWaitMs:i,queueHolds:{...t.holds},requeued:t.requeued,readyWaitMs:t.readyWaitMs,readyOutcome:t.readyOutcome,verify:t.verify?{...t.verify}:null,receiptMs:null,totalMs:Math.max(0,o-t.t0)};return e&&await N(e,{type:"delivery.settled",actor:"system",source:"orchestrator",boxId:e.leadBox??null,provider:t.provider,payload:l}).catch(()=>{}),l}function Ha(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 og,B_,L_,Z2,zn,rg,sg=G(()=>{be();mc();og=null;B_=["busy","prompt","handoff","noSession","cap","grace"],L_=[500,2e3,1e4,6e4,6e5],Z2=new Set(["composer","voice","loop"]);zn=null;Q2();rg=(e,t,n=1)=>e.set(t,(e.get(t)||0)+n)});import Jc from"node:fs/promises";import Ut from"node:path";import tH from"node:os";import nH from"node:net";import{randomUUID as rH}from"node:crypto";import{execFile as oH}from"node:child_process";import{promisify as sH}from"node:util";function pH(e){for(let t of pg(e))if(!z_.test(t))return t;return null}function J_(e){let t=pg(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function fH(e,t){let n=J_(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Ut.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return Ut.resolve(e,n[r+1])}return e}function hH(e){let t=!1,n=!1;for(let r of pg(e)){if(!t){if(z_.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&ag(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,ag(r))return`serves the filesystem root ("${r}")`;continue}if(ag(r))return`serves the filesystem root ("${r}")`;if(mH(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function gH(e,t){if(Ut.resolve(e)===Ut.resolve(t))return!0;let[n,r]=await Promise.all([xp(e),xp(t)]);return n===r}async function yH(e){let n=(await Jc.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 Y_(Ut.join(e,o.name,lg))?`./${o.name}`:null))).filter(Boolean)}async function bH(e,t){let[n,r]=await Promise.all([xp(e),xp(t)]),o=Ut.relative(n,r);if(o&&(o.startsWith("..")||Ut.isAbsolute(o)))throw Vc(`serve directory escapes the Track workspace: ${t}`)}async function kH(e,t){let n=hH(t);if(n)throw Vc(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await wH(e))throw Vc(`serve directory does not exist: ${e}`);let r=pH(t);if(!r)return;if(!r.includes("/")&&!await vx(r,{loginShell:!0,failOpen:!0}))throw Vc(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(lH.test(t)||uH.has(r)||!cH.has(r)||!J_(t).includes("run")||dH.test(t))return;let o=fH(e,t);if(await Y_(Ut.join(o,lg)))return;let s=await yH(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Vc(`no ${lg} in ${o}${a}`)}async function Or(){return Jn||(ig||=(async()=>{try{Jn=JSON.parse(await Jc.readFile(V_,"utf8"))}catch{Jn={views:[],adoptWatermarks:{}}}finally{ig=null}return Jn.views||=[],Jn.adoptWatermarks||={},Jn})(),ig)}async function Xn(){await Bt(V_,Jn)}function Rr(e,t,n={}){return N({id:e.trackId,cwd:e.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,payload:{ts:Date.now(),change:t,viewId:e.id,name:e.name,slug:e.slug,port:e.port,cmd:e.cmd||null,...n}}).catch(()=>{})}function mg(e){return zc.get(e)||null}function Z_(e,{name:t,box:n}={}){let r=zc.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 zc.set(e,o),o}function ug(e,t){let n=zc.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function vp(e,{error:t=null}={}){let n=zc.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function Q_(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 dg(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function xH(e,t){let n=dg(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!G_.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!G_.has(s))return s}}function Yc(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=nH.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 hg(e){if(process.platform!=="darwin"){await Yn("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await Yn("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 eI(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function vH(e,t){let n=new Set(e.views.map(o=>o.port)),r=eI();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=W_;o<=q_;o++)if(!(n.has(o)||r.has(o))&&!await Yc(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${W_}-${q_}`)}async function SH(e){try{let{stdout:t}=await Yn(oe,se("list-sessions","-F","#{session_name}"));return t.split(`
|
|
216
|
-
`).some(n=>n.trim()===Wa(e))}catch{return!1}}async function Xc(e){try{await Yn(oe,se("kill-session","-t",Wa(e)))}catch{}}async function wg(e){if(!await Qi())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=Wa(e.slug),n=e.serveCwd??e.cwd,r=Ut.dirname(process.execPath);await Yn(oe,se("new-session","-d","-s",t,"-c",n)),await Yn(oe,se("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await Yn(oe,se("send-keys","-t",t,"Enter")),await Yn(oe,se("send-keys","-t",t,"-l",e.cmd)),await Yn(oe,se("send-keys","-t",t,"Enter"))}async function
|
|
214
|
+
`).slice(0,400);C2=(e,t)=>t.filter(n=>e.get(n.armId?`arm:${n.armId}`:`fu:${n.followupId}`)==null)});import{randomUUID as D2}from"node:crypto";function Uc(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function T_(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function A_(e){return`job|${e.box}|id:${e.id}`}function E_(e){return`jobstop|${e.box}|id:${e.id}`}async function Xw(e){return Yw.has(e.id)?Yw.get(e.id):(bp.has(e.id)||bp.set(e.id,K(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(Uc({role:o,sourceId:r.payload.sourceId})),n.keys.add(Uc({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(Uc({role:"user",sourceId:r.payload.sourceId})),n.keys.add(Uc({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(T_({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(A_({id:r.payload.id,box:r.boxId})):r.type==="job.stopped"&&r.payload?.id&&n.keys.add(E_({id:r.payload.id,box:r.boxId}));return Yw.set(e.id,n),n}).finally(()=>bp.delete(e.id))),bp.get(e.id))}async function __(e,t){let n=new Set;try{let r=(await Xw(e)).keys;for(let o of t||[]){if(!o||o.ts==null||!String(o.text||"").trim()&&!o.images?.length)continue;let s=Uc(o);if(r.has(s))continue;r.add(s);let a=o.role==="user"?hp(o.text):null;if(a){await N(e,{type:"peer.message",actor:"peer",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{text:a.text,ts:o.ts,keyText:String(o.text||"").slice(0,40),from:await t_(a,Hc),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=Zx(o);if(i){await N(e,{type:"provider.error",actor:"system",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{cls:i.cls,code:i.code||void 0,status:i.status||void 0,requestId:i.requestId||void 0,text:o.text,ts:o.ts,model:o.model||null,boxRole:o.boxRole,sourceId:o.sourceId||void 0}}),Tc(e,{provider:o.agent||null,cls:i.cls,status:i.status,text:o.text}).catch(()=>{});continue}if(o.role!=="user"&&o.box&&n.add(o.box),await N(e,{type:o.role==="user"?"turn.user":"turn.agent",actor:o.role==="user"?"user":"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{text:o.text,ts:o.ts,model:o.model||null,tools:o.tools||void 0,boxRole:o.boxRole,sourceId:o.sourceId||void 0,note:o.note||void 0,images:o.images?.length?o.images:void 0}}),o.role==="assistant"&&o.box===e.leadBox&&e.workspace?.branch){let c=xA(o.text);if(c&&Jw(await K(e).catch(()=>[])))continue;if(c){let l=TA(c),u;if(e.workspace?.kind==="multi"){let w=await _s(e).catch(()=>[]);u=w.length?w.map(h=>({name:h.name,slug:h.repo??null})):void 0}let d=(e.workspace?.kind==="multi"?null:await ko(e.cwd).catch(()=>null))||e.workspace.branch,p=await rp(e,{branch:d,rebase:l}).catch(()=>null),f=p?null:SA(c);if(p&&p.available.length===0)continue;await N(e,{type:"pr.offered",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{offerId:D2(),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 I_(e,t){try{let n=await Xw(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=T_(o);if(n.keys.has(s))continue;n.keys.add(s);let a=(n.cmdN.get(o.box)??0)+1;n.cmdN.set(o.box,a),await N(e,{type:"tool.called",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id??null,tool:o.tool||null,command:o.command,description:o.description||"",summary:o.summary||o.command,ts:o.ts??null,boxRole:o.boxRole,n:a}}),ZE(e,o),QE(e,o)}}catch{}}async function $_(e,t){try{let n=await Xw(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=A_(o);if(n.keys.has(s)||(n.keys.add(s),await N(e,{type:"job.started",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,detail:o.detail||null,ts:o.ts??null,boxRole:o.boxRole}})),o.stoppedTs==null)continue;let a=E_(o);n.keys.has(a)||(n.keys.add(a),await N(e,{type:"job.stopped",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,ts:o.stoppedTs,boxRole:o.boxRole}}))}}catch{}}var Yw,bp,R_=G(()=>{be();gs();Iw();mp();Ss();Hw();Ir();zu();Rd();yp();Yw=new Map,bp=new Map});function B2(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
215
|
+
`)){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 L2(e,t){let n=`${e.id}:${t.name}`;if(!Zw.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);Zw.set(n,o)}return Zw.get(n)}async function O_(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await Zo(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await L2(e,t),o=B2(await jt(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||t.model||"gpt-5.5";await N(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:Wn(c,i,a.ts),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await N(e,{type:"context.usage",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{occupancy:a.occupancy,first:null,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}var Zw,M_=G(()=>{be();es();vn();yr();Zw=new Map});import kp from"node:fs";import eg from"node:path";import j2 from"node:os";async function H2({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(U2,{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:C_})});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<C_)break}return r}function W2(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 P_(){try{return JSON.parse(kp.readFileSync(xp(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function G2(e){try{kp.mkdirSync(eg.dirname(xp()),{recursive:!0});let t=xp()+".tmp";kp.writeFileSync(t,JSON.stringify(e)),kp.renameSync(t,xp())}catch{}}async function V2({fetchImpl:e,now:t=Date.now()}={}){if(So.rows&&t-So.at<q2)return So.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=P_(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await H2({sinceMs:o,cookie:n,fetchImpl:e});So={at:t,rows:s.map(W2).filter(Boolean),error:null}}catch(s){So={at:t,rows:null,error:String(s?.message||s)}}return So.rows}async function z2(e,t){let n=`${e.id}:${t.name}`;if(!Qw.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);Qw.set(n,o)}return Qw.get(n)}async function D_(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!N_())return;let r=await V2({fetchImpl:n});if(!r||!r.length)return;let o=await FS(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await z2(e,t),a=P_(),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=Wn(u,d,l.ts??void 0)),!p&&l.chargedCents!=null&&(p=l.chargedCents/100),await N(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.name,provider:"cursor",payload:{usd:p,model:u,...d?{tokens:d}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}G2({sinceMs:c,seen:[...i].slice(-K2)})}catch{}}var F2,N_,U2,C_,q2,So,xp,K2,tg,Qw,ng=G(()=>{be();Mh();yr();F2=process.env.BOSTRAT_STATE_DIR||eg.join(process.env.HOME||j2.tmpdir(),".bostrat"),N_=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,U2="https://cursor.com/api/dashboard/get-filtered-usage-events",C_=100;q2=5*6e4,So={at:0,rows:null,error:null},xp=()=>eg.join(F2,"cursor-usage.json");K2=5e3;tg=()=>({enabled:N_(),lastPollAt:So.at||null,error:So.error}),Qw=new Map});import{randomUUID as J2,createHmac as Y2}from"node:crypto";function L_(e){og=typeof e=="string"&&e?e:null}function X2(e){if(!og)return null;let t=He(e);return t?Y2("sha256",Buffer.from(og,"base64url")).update(t,"utf8").digest("hex").slice(0,16):null}function U_(){let e={};for(let t of F_)e[String(t)]=0;return e.inf=0,{buckets:e,sumMs:0,count:0}}function B_(e,t){let n=Math.max(0,Number(t)||0);for(let r of F_)n<=r&&(e.buckets[String(r)]+=1);e.buckets.inf+=1,e.sumMs+=n,e.count+=1}function H_(){let e={};for(let t of j_)e[t]=0;return e}function Q2(){zn={total:new Map,holds:H_(),queueWaitMs:U_(),readyWaitMs:new Map,verify:new Map,retries:new Map}}function W_({queueDepth:e=0,queueOldestAgeMs:t=0}={}){let n=zn;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 Wc({kind:e=null,producer:t=null,orchestration:n=!1}={}){return n?`orchestration:${e||"orchestration"}`:e?`scripted:${e}`:Z2.has(t)?t:"composer"}function qc({text:e="",producer:t="composer",kind:n=null,provider:r=null,attachments:o=0,now:s=Date.now}={}){let a=s();return{id:J2(),producer:t,kind:n||null,provider:r||null,textBytes:Buffer.byteLength(String(e||""),"utf8"),textHmac16:X2(e),attachments:Number(o)||0,paste:null,t0:a,queuedAt:null,dequeuedAt:null,holds:H_(),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 N(e,{type:"delivery.queued",actor:"system",source:"orchestrator",boxId:e.leadBox??null,provider:t.provider,payload:{ts:t.queuedAt,deliveryId:t.id,producer:t.producer,...t.kind?{kind:t.kind}:{},reason:r,position:n,textBytes:t.textBytes,textHmac16:t.textHmac16,requeued:t.requeued}}).catch(()=>{}))}function q_(e,t){j_.includes(t)&&(e?.holds&&(e.holds[t]+=1),zn.holds[t]+=1)}function Kc(e,{ms:t,outcome:n}){e&&(e.readyWaitMs=Math.max(0,Number(t)||0),e.readyOutcome=n||null)}function eH(e){let t=e?.boxes?.find?.(n=>n.name===e.leadBox);return typeof t?.cliVersion=="string"&&t.cliVersion?t.cliVersion:null}async function In(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(rg(zn.total,`${s}|${t.producer}|${n}`),a&&B_(zn.queueWaitMs,i),t.readyWaitMs!=null){let u=`${s}|${t.readyOutcome||"unknown"}`;zn.readyWaitMs.has(u)||zn.readyWaitMs.set(u,U_()),B_(zn.readyWaitMs.get(u),t.readyWaitMs)}let c=eH(e);t.verify?.verdict&&(rg(zn.verify,`${s}|${t.verify.verdict}|${c||""}`),t.verify.retries&&rg(zn.retries,s,t.verify.retries));let l={ts:o,deliveryId:t.id,producer:t.producer,...t.kind?{kind:t.kind}:{},cliVersion:c,textBytes:t.textBytes,textHmac16:t.textHmac16,paste:t.paste,attachments:t.attachments,outcome:n,queued:a,queueWaitMs:i,queueHolds:{...t.holds},requeued:t.requeued,readyWaitMs:t.readyWaitMs,readyOutcome:t.readyOutcome,verify:t.verify?{...t.verify}:null,receiptMs:null,totalMs:Math.max(0,o-t.t0)};return e&&await N(e,{type:"delivery.settled",actor:"system",source:"orchestrator",boxId:e.leadBox??null,provider:t.provider,payload:l}).catch(()=>{}),l}function Ha(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 og,j_,F_,Z2,zn,rg,sg=G(()=>{be();mc();og=null;j_=["busy","prompt","handoff","noSession","cap","grace"],F_=[500,2e3,1e4,6e4,6e5],Z2=new Set(["composer","voice","loop"]);zn=null;Q2();rg=(e,t,n=1)=>e.set(t,(e.get(t)||0)+n)});import Jc from"node:fs/promises";import Ut from"node:path";import tH from"node:os";import nH from"node:net";import{randomUUID as rH}from"node:crypto";import{execFile as oH}from"node:child_process";import{promisify as sH}from"node:util";function pH(e){for(let t of pg(e))if(!Y_.test(t))return t;return null}function X_(e){let t=pg(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function fH(e,t){let n=X_(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Ut.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return Ut.resolve(e,n[r+1])}return e}function hH(e){let t=!1,n=!1;for(let r of pg(e)){if(!t){if(Y_.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&ag(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,ag(r))return`serves the filesystem root ("${r}")`;continue}if(ag(r))return`serves the filesystem root ("${r}")`;if(mH(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function gH(e,t){if(Ut.resolve(e)===Ut.resolve(t))return!0;let[n,r]=await Promise.all([vp(e),vp(t)]);return n===r}async function yH(e){let n=(await Jc.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 Z_(Ut.join(e,o.name,lg))?`./${o.name}`:null))).filter(Boolean)}async function bH(e,t){let[n,r]=await Promise.all([vp(e),vp(t)]),o=Ut.relative(n,r);if(o&&(o.startsWith("..")||Ut.isAbsolute(o)))throw Vc(`serve directory escapes the Track workspace: ${t}`)}async function kH(e,t){let n=hH(t);if(n)throw Vc(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await wH(e))throw Vc(`serve directory does not exist: ${e}`);let r=pH(t);if(!r)return;if(!r.includes("/")&&!await Tx(r,{loginShell:!0,failOpen:!0}))throw Vc(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(lH.test(t)||uH.has(r)||!cH.has(r)||!X_(t).includes("run")||dH.test(t))return;let o=fH(e,t);if(await Z_(Ut.join(o,lg)))return;let s=await yH(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Vc(`no ${lg} in ${o}${a}`)}async function Or(){return Jn||(ig||=(async()=>{try{Jn=JSON.parse(await Jc.readFile(J_,"utf8"))}catch{Jn={views:[],adoptWatermarks:{}}}finally{ig=null}return Jn.views||=[],Jn.adoptWatermarks||={},Jn})(),ig)}async function Xn(){await Bt(J_,Jn)}function Rr(e,t,n={}){return N({id:e.trackId,cwd:e.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,payload:{ts:Date.now(),change:t,viewId:e.id,name:e.name,slug:e.slug,port:e.port,cmd:e.cmd||null,...n}}).catch(()=>{})}function mg(e){return zc.get(e)||null}function eI(e,{name:t,box:n}={}){let r=zc.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 zc.set(e,o),o}function ug(e,t){let n=zc.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function Sp(e,{error:t=null}={}){let n=zc.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function tI(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 dg(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function xH(e,t){let n=dg(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!V_.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!V_.has(s))return s}}function Yc(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=nH.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 hg(e){if(process.platform!=="darwin"){await Yn("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await Yn("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 nI(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function vH(e,t){let n=new Set(e.views.map(o=>o.port)),r=nI();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=G_;o<=K_;o++)if(!(n.has(o)||r.has(o))&&!await Yc(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${G_}-${K_}`)}async function SH(e){try{let{stdout:t}=await Yn(oe,se("list-sessions","-F","#{session_name}"));return t.split(`
|
|
216
|
+
`).some(n=>n.trim()===Wa(e))}catch{return!1}}async function Xc(e){try{await Yn(oe,se("kill-session","-t",Wa(e)))}catch{}}async function wg(e){if(!await Qi())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=Wa(e.slug),n=e.serveCwd??e.cwd,r=Ut.dirname(process.execPath);await Yn(oe,se("new-session","-d","-s",t,"-c",n)),await Yn(oe,se("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await Yn(oe,se("send-keys","-t",t,"Enter")),await Yn(oe,se("send-keys","-t",t,"-l",e.cmd)),await Yn(oe,se("send-keys","-t",t,"Enter"))}async function z_(e,t=12){try{let{stdout:n}=await Yn(oe,se("capture-pane","-t",Wa(e),"-p"));return String(n||"").trimEnd().split(`
|
|
217
217
|
`).filter(Boolean).slice(-t).join(`
|
|
218
|
-
`)}catch{return""}}async function AH(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await SH(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await K_(e.slug)||"server exited before listening"),$r.delete(e.id),await Rr(e,e.state,e.error?{error:e.error}:{}),!0}if(await Yc(e.port))return $r.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await Rr(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>TH?(e.state="error",e.error=await K_(e.slug)||"never started listening",await Rr(e,"error",{error:e.error}),!0):!1;let o=($r.get(e.id)||0)+1;return $r.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function Sp(){if(!cg){cg=!0;try{let e=await Or(),t=!1;for(let n of e.views)await AH(n)&&(t=!0);t&&await Xn()}finally{cg=!1}}}async function gg({trackId:e}={}){await Sp();let t=await Or();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function tI(e){let n=(await Or()).views.find(r=>r.id===e);return n?{...n}:null}function yg(e){return Jn&&Jn.views.find(t=>t.slug===e)||null}async function bg({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=Ut.resolve(r,o??"."),d=i==="detected"?null:async()=>{await bH(r,u),await kH(u,n)},p=await Or();if(i!=="detected"){let h=dg(t),y=String(n).trim(),k=p.views.find(g=>g.trackId===e&&(g.slug===h||dg(g.name)===h||g.cmd===y&&Ut.resolve(g.serveCwd??g.cwd)===u));if(k)return EH(p,k,{cmd:y,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>=To)throw new Error(`view cap reached (${To} live) \u2014 stop one first`);let w={id:rH().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:xH(p,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await vH(p,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await wg(w),p.views.push(w),await Xn(),await Rr(w,"created",{origin:i}),ug(e,w.id),i==="detected"&&await Yc(w.port)&&(w.state="live",w.lastHealthyAt=Date.now(),await Xn(),await Rr(w,"live")),{...w}}async function EH(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 gH(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Yc(t.port))return await Xn(),ug(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>=To)throw new Error(`view cap reached (${To} live) \u2014 stop one first`);return t.origin==="detected"?(await hg(t.port),t.origin="agent"):await Xc(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),$r.delete(t.id),await wg(t),await Xn(),await Rr(t,"created",{restarted:!0,reused:!0}),ug(t.trackId,t.id),{...t,reused:!0}}async function nI(e){let n=(await Or()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await hg(n.port):await Xc(n.slug),n.state!=="stopped"&&(n.state="stopped",$r.delete(n.id),await Xn(),await Rr(n,"stopped")),{...n}}async function rI(e){let t=await Or(),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>=To)throw new Error(`view cap reached (${To} live) \u2014 stop one first`);return await Xc(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),$r.delete(n.id),await wg(n),await Xn(),await Rr(n,"created",{restarted:!0}),{...n}}async function oI(e){let t=await Or(),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 hg(r.port):await Xc(r.slug),t.views.splice(n,1),$r.delete(r.id),await Xn(),await Rr(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function kg(e){let t=await Or(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Xc(r.slug),$r.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await Xn()),n.length}async function sI(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await Or(),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 Xn()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(_H)){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(!eI().has(c)&&!n.views.some(l=>l.port===c)&&await Yc(c))try{i.push(await bg({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 Yn,aH,V_,W_,q_,To,iH,Wa,G_,Vc,cH,lg,lH,uH,dH,pg,z_,ag,mH,wH,Y_,xp,Jn,ig,zc,fg,X_,$r,TH,cg,_H,Tp=G(()=>{dt();be();zr();Yn=sH(oH),aH=process.env.BOSTRAT_STATE_DIR||Ut.join(process.env.HOME||tH.tmpdir(),".bostrat"),V_=Ut.join(aH,"views.json"),[W_,q_]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),To=Number(process.env.BOSTRAT_VIEW_MAX)||3,iH="bostrat-view-",Wa=e=>`${iH}${e}`,G_=new Set(["bostrat","www","api","views","app"]),Vc=e=>Object.assign(new Error(e),{status:400}),cH=new Set(["npm","pnpm","yarn","bun"]),lg="package.json",lH=/[|;&`]|\$\(/,uH=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),dH=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,pg=e=>String(e).trim().split(/\s+/).filter(Boolean),z_=/^[A-Za-z_][A-Za-z0-9_]*=/;ag=e=>e==="/",mH=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");wH=e=>Jc.stat(e).then(t=>t.isDirectory(),()=>!1),Y_=e=>Jc.stat(e).then(t=>t.isFile(),()=>!1),xp=e=>Jc.realpath(e).catch(()=>Ut.resolve(e));Jn=null,ig=null;zc=new Map,fg=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,X_=12e3;$r=new Map,TH=12e4;cg=!1;_H=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});import{execFile as IH}from"node:child_process";import{promisify as $H}from"node:util";function MH(e){return iI(e?.toPr?.number??e?.openPr?.number)}function iI(e){if(e==null||e==="")return null;let t=Number(e);return Number.isFinite(t)&&t>0?t:null}function CH(e){return Array.isArray(e?.actions)?e.actions.includes("push")||e.actions.includes("pr"):e?.action==="push"||e?.action==="pr"}function qa(e,t=Date.now()){let n=null,r=new Map;for(let o of e||[]){let s=o.payload?.ts??o.ts;if(o.type==="pr.offered"){o.payload?.offerId&&r.set(o.payload.offerId,o.payload);continue}if(o.type==="pr.offer.answered"){let a=o.payload||{},i=r.get(a.offerId)||null,c=MH(i),l=Array.isArray(a.actions)&&a.actions.includes("rebase"),u=i?.base??null;a.action==="pr"?n={ts:s,offerId:a.offerId??null,box:o.boxId??null,offerType:"offer",toPr:c,base:u,rebase:l}:a.action!=="dismiss"&&a.action!=="pr-exists"&&c!=null&&CH(a)?n={ts:s,offerId:a.offerId??null,box:o.boxId??null,offerType:"offer",toPr:c,base:u,rebase:l}:n=null}else if(o.type==="pr.exists.answered"){let a=o.payload||{};n=a.choice==="push"?{ts:s,offerId:a.offerId??null,box:o.boxId??null,offerType:"exists",toPr:iI(a.number),base:null,rebase:Array.isArray(a.actions)&&a.actions.includes("rebase")}:null}else o.type==="pr.state.changed"&&(o.payload?.change==="opened"||o.payload?.change==="pushed")&&n!=null&&s>=n.ts&&(n=null)}return n!=null&&t-n.ts>xg?null:n}function NH(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function Ep(e,{bypassThrottle:t=!1}={}){let n=e?.workspace?.branch;if(!n)return;let r;try{r=await K(e)}catch{return}let o=qa(r);if(o==null)return;let s=(e.boxes||[]).find(d=>d.name===e.leadBox);if(await is(e.leadBox,s?.agent).catch(()=>!1))return;let a=Date.now();if(!t&&a<(Zc.get(e.id)||0))return;Zc.set(e.id,a+aI);let i;try{i=await Kn(e.cwd,{caller:"reconcile"})}catch{return}let c=await ja(e).catch(()=>[n]),l=o.toPr!=null&&(i||[]).find(d=>Number(d.number)===o.toPr&&c.includes(d.headRefName))||(i||[]).find(d=>c.includes(d.headRefName));if(!l)return;let u=await Me(e.cwd).catch(()=>null);if(u){let{link:d,created:p}=await Yt({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 xr(u,l.number))){if(!NH(d,o))return;let f=await PH(e);if(f&&l.headSha&&f!==l.headSha){let h=Math.min(OH,(Ap.get(e.id)||aI)*2);Ap.set(e.id,h),Zc.set(e.id,a+h);return}let w=await K(e).catch(()=>null);if(w&&qa(w)==null)return;await N(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:DH(o,l,{branch:n,repo:u})}),Zc.delete(e.id),Ap.delete(e.id);return}}await N(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:l.number,url:l.url,title:l.title,branch:n,...u?{repo:u}:{}}}).catch(()=>{}),Zc.delete(e.id),Ap.delete(e.id)}async function PH(e){try{let{stdout:t}=await RH("git",["-C",e.cwd,"rev-parse","HEAD"],{timeout:1e4}),n=String(t||"").trim();return/^[0-9a-f]{40}$/.test(n)?n:null}catch{return null}}function DH(e,t,{branch:n=null,repo:r=null,now:o=Date.now()}={}){return{ts:o,change:"pushed",number:t.number,url:t.url,title:t.title,branch:n,...r?{repo:r}:{},...t.headSha?{sha:t.headSha}:{},via:"ship",...e?.rebase?{rebased:!0}:{},...e?.base?{base:e.base}:{}}}var RH,aI,Zc,OH,Ap,xg,Qc=G(()=>{be();vs();Gr();Gn();va();Ss();RH=$H(IH),aI=6e3,Zc=new Map,OH=5*6e4,Ap=new Map,xg=30*6e4});import vg from"node:os";import{execFile as BH}from"node:child_process";import{promisify as LH}from"node:util";async function _p(){if(process.platform!=="darwin")return vg.freemem();try{let{stdout:e}=await jH("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:vg.freemem()}catch{return vg.freemem()}}var jH,Sg=G(()=>{jH=LH(BH)});import ot from"node:fs/promises";import st from"node:path";import dI from"node:os";import{randomUUID as FH}from"node:crypto";import{execFile as UH}from"node:child_process";import{promisify as HH}from"node:util";import{chromium as WH}from"playwright-core";async function er(){return Is||(Tg||=(async()=>{try{Is=JSON.parse(await ot.readFile(fI,"utf8"))}catch{Is={mocks:[]}}finally{Tg=null}return Is.mocks||=[],Is})(),Tg)}async function un(){await Bt(fI,Is)}function gI(e,t){Qn(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&Qn(e,t.v,"png")}function Rt(e,t,n={}){return N({id:e.trackId,cwd:e.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,mockId:e.id,name:e.name,...e.group?{group:e.group}:{},...e.kind==="video"?{kind:"video"}:{},...n}}).catch(()=>{})}function dn(e,t){let n=tl(e.cwd);return{html:st.join(n,`${e.id}.v${t}.html`),png:st.join(n,`${e.id}.v${t}.png`)}}function el(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?st.join(e.cwd,r):null}function $n(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 _g(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function Ip(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(YH)}function XH(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 ZH(e){let t=Buffer.alloc(24),n=await ot.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function QH({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 eW(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,y=0;y<o;y++,h+=4)Math.abs(c[h]-l[0])+Math.abs(c[h+1]-l[1])+Math.abs(c[h+2]-l[2])>24&&(y<u&&(u=y),y>p&&(p=y),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 tW(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 nW({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await WH.launch({executablePath:mI,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Xt("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:Ga})).newPage();a.setDefaultTimeout(Ka),await a.goto(`file://${e}`,{waitUntil:"load",timeout:Ka}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Ka});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await eW(a,i),u=l&&QH({...l,pad:KH*Ga,align:Ga});u&&(i=await tW(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<hI)throw new Xt(`capture produced a ${i.length}-byte image`);let c=_g(i);if(!c)throw new Xt("output is not a PNG");return await ot.writeFile(t,i),{width:Math.round(c.width/Ga),height:Math.round(c.height/Ga),dpr:Ga,renderMs:Date.now()-r}}catch(s){throw s instanceof Xt?s:new Xt(/timeout/i.test(s?.message||"")?`render timed out after ${Ka}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function uI({htmlPath:e,outPath:t,width:n}){let r=await ot.mkdtemp(st.join(dI.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Eg(cI,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Ka,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new Xt(`firefox not found (${cI})`,""):new Xt(c?.killed?`render timed out after ${Ka}ms`:"firefox exited non-zero",s)}let a=await ot.stat(t).catch(()=>null);if(!a||a.size<hI)throw new Xt(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=_g(await ZH(t));if(!i)throw new Xt("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await ot.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function rW(e){if(qH==="firefox")return uI(e);try{return await nW(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await uI(e)}catch(n){throw/not found/.test(n?.message||"")?new Xt(`no browser installed \u2014 chromium missing at ${mI} 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 yI(e){return Ag=Ag.then(e,e),Ag}async function bI(e,t){let n=await er(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await un(),await Rt(r,"rendering",{version:t});try{let o=await _p();if(o<VH)throw new Xt(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=dn(r,t),a=await oW({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await ot.rm(s.png,{force:!0}).catch(()=>{});return}Ig(r,{v:t,source:"html",htmlRel:st.relative(r.cwd,s.html),pngRel:st.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await un(),await Rt(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),Zn(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 un(),await Rt(r,"error",{version:t,error:r.error}),Zn(r)}}}function Ig(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 sW({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?GH:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function nl({trackId:e,group:t}={}){let n=await er(),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 er()).mocks.find(r=>r.id===e);return n?{...n}:null}function kI({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&&!Ip(t))throw new Error("image must be a PNG");if(n){let o=XH(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>lI)throw new Error(`video must be ${lI/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function xI({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=sW({trackId:e,name:t,cwd:s,width:a}),p=typeof n=="string"&&n.trim(),f=kI({html:n,image:r,video:o}),w=await er(),h={id:FH().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 ot.mkdir(tl(s),{recursive:!0}),w.mocks.push(h),p?(await ot.writeFile(dn(h,1).html,n),await un(),await Rt(h,"created"),Zn(h),Qn(h,1,"html"),yI(()=>bI(h.id,1))):o?(await SI(h,1,o,f),await un(),await Rt(h,"created"),await Rt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr,durationS:h.versions[0]?.durationS??null}),Zn(h),gI(h,h.versions[0])):(await TI(h,1,r),await un(),await Rt(h,"created"),await Rt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr}),Zn(h),Qn(h,1,"png")),{...h}}async function vI(e,{html:t,image:n,video:r,width:o}={}){let a=(await er()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=kI({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 ot.mkdir(tl(a.cwd),{recursive:!0}),i?(a.state="queued",await ot.writeFile(dn(a,l).html,t),await un(),await Rt(a,"updated",{version:l}),Zn(a),Qn(a,l,"html"),yI(()=>bI(a.id,l))):r?(await SI(a,l,r,c),await un(),await Rt(a,"updated",{version:l}),await Rt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:$n(a)?.durationS??null}),Zn(a),gI(a,a.versions.find(u=>u.v===l))):(await TI(a,l,n),await un(),await Rt(a,"updated",{version:l}),await Rt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Zn(a),Qn(a,l,"png")),{...a}}async function aW(e){try{let{stdout:t}=await Eg(zH,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:wI,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 iW(e,t){try{await Eg(JH,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:wI,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await ot.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function SI(e,t,n,r){let o=tl(e.cwd),s=st.join(o,`${e.id}.v${t}.${r.ext}`),a=st.join(o,`${e.id}.v${t}.poster.png`);await ot.writeFile(s,n);let i=await aW(s),c=await iW(s,a);Ig(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:st.relative(e.cwd,s),posterRel:c?st.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 TI(e,t,n){let r=_g(n);if(!r)throw new Error("image must be a PNG");let o=dn(e,t);await ot.writeFile(o.png,n),Ig(e,{v:t,source:"image",htmlRel:null,pngRel:st.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function AI(e){let t=await er(),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 Rt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function EI(e){let t=await er(),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=dn(r,o);await ot.rm(s.html,{force:!0}).catch(()=>{}),await ot.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await ot.rm(st.join(r.cwd,s),{force:!0}).catch(()=>{});return await un(),await Rt(r,"deleted"),bn("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function _I(e){let n=(await er()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=st.join(pI,"mock-archive",e.id),o=tl(e.cwd),s=st.join(r,".bostrat","mocks");await ot.mkdir(s,{recursive:!0});let a=await ot.readdir(o).catch(()=>[]);for(let i of a)await ot.copyFile(st.join(o,i),st.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 ot.stat(st.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(),Zn(i)}return await un(),n.length}async function II(e){let t=await er(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await un();for(let r of n)bn("mocks",r.id).catch(()=>{})}return n.length}async function $I(){if(!Ge())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await pt("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await er();for(let r of n.mocks){await Zn(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 Eg,pI,fI,qH,mI,cI,GH,Ga,KH,hI,Ka,VH,lI,zH,JH,wI,Xt,Is,Tg,Zn,Qn,tl,YH,oW,Ag,Va=G(()=>{be();zr();Sg();Yr();Eg=HH(UH),pI=process.env.BOSTRAT_STATE_DIR||st.join(process.env.HOME||dI.tmpdir(),".bostrat"),fI=st.join(pI,"mocks.json"),qH=process.env.BOSTRAT_MOCK_ENGINE||"chromium",mI=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",cI=process.env.BOSTRAT_FIREFOX_BIN||"firefox",GH=1300,Ga=2,KH=32,hI=1500,Ka=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,VH=700*1024*1024,lI=100*1024*1024,zH=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",JH=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",wI=2e4,Xt=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
218
|
+
`)}catch{return""}}async function AH(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await SH(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await z_(e.slug)||"server exited before listening"),$r.delete(e.id),await Rr(e,e.state,e.error?{error:e.error}:{}),!0}if(await Yc(e.port))return $r.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await Rr(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>TH?(e.state="error",e.error=await z_(e.slug)||"never started listening",await Rr(e,"error",{error:e.error}),!0):!1;let o=($r.get(e.id)||0)+1;return $r.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function Tp(){if(!cg){cg=!0;try{let e=await Or(),t=!1;for(let n of e.views)await AH(n)&&(t=!0);t&&await Xn()}finally{cg=!1}}}async function gg({trackId:e}={}){await Tp();let t=await Or();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function rI(e){let n=(await Or()).views.find(r=>r.id===e);return n?{...n}:null}function yg(e){return Jn&&Jn.views.find(t=>t.slug===e)||null}async function bg({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=Ut.resolve(r,o??"."),d=i==="detected"?null:async()=>{await bH(r,u),await kH(u,n)},p=await Or();if(i!=="detected"){let h=dg(t),y=String(n).trim(),k=p.views.find(g=>g.trackId===e&&(g.slug===h||dg(g.name)===h||g.cmd===y&&Ut.resolve(g.serveCwd??g.cwd)===u));if(k)return EH(p,k,{cmd:y,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>=To)throw new Error(`view cap reached (${To} live) \u2014 stop one first`);let w={id:rH().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:xH(p,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await vH(p,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await wg(w),p.views.push(w),await Xn(),await Rr(w,"created",{origin:i}),ug(e,w.id),i==="detected"&&await Yc(w.port)&&(w.state="live",w.lastHealthyAt=Date.now(),await Xn(),await Rr(w,"live")),{...w}}async function EH(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 gH(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Yc(t.port))return await Xn(),ug(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>=To)throw new Error(`view cap reached (${To} live) \u2014 stop one first`);return t.origin==="detected"?(await hg(t.port),t.origin="agent"):await Xc(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),$r.delete(t.id),await wg(t),await Xn(),await Rr(t,"created",{restarted:!0,reused:!0}),ug(t.trackId,t.id),{...t,reused:!0}}async function oI(e){let n=(await Or()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await hg(n.port):await Xc(n.slug),n.state!=="stopped"&&(n.state="stopped",$r.delete(n.id),await Xn(),await Rr(n,"stopped")),{...n}}async function sI(e){let t=await Or(),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>=To)throw new Error(`view cap reached (${To} live) \u2014 stop one first`);return await Xc(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),$r.delete(n.id),await wg(n),await Xn(),await Rr(n,"created",{restarted:!0}),{...n}}async function aI(e){let t=await Or(),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 hg(r.port):await Xc(r.slug),t.views.splice(n,1),$r.delete(r.id),await Xn(),await Rr(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function kg(e){let t=await Or(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Xc(r.slug),$r.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await Xn()),n.length}async function iI(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await Or(),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 Xn()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(_H)){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(!nI().has(c)&&!n.views.some(l=>l.port===c)&&await Yc(c))try{i.push(await bg({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 Yn,aH,J_,G_,K_,To,iH,Wa,V_,Vc,cH,lg,lH,uH,dH,pg,Y_,ag,mH,wH,Z_,vp,Jn,ig,zc,fg,Q_,$r,TH,cg,_H,Ap=G(()=>{dt();be();zr();Yn=sH(oH),aH=process.env.BOSTRAT_STATE_DIR||Ut.join(process.env.HOME||tH.tmpdir(),".bostrat"),J_=Ut.join(aH,"views.json"),[G_,K_]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),To=Number(process.env.BOSTRAT_VIEW_MAX)||3,iH="bostrat-view-",Wa=e=>`${iH}${e}`,V_=new Set(["bostrat","www","api","views","app"]),Vc=e=>Object.assign(new Error(e),{status:400}),cH=new Set(["npm","pnpm","yarn","bun"]),lg="package.json",lH=/[|;&`]|\$\(/,uH=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),dH=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,pg=e=>String(e).trim().split(/\s+/).filter(Boolean),Y_=/^[A-Za-z_][A-Za-z0-9_]*=/;ag=e=>e==="/",mH=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");wH=e=>Jc.stat(e).then(t=>t.isDirectory(),()=>!1),Z_=e=>Jc.stat(e).then(t=>t.isFile(),()=>!1),vp=e=>Jc.realpath(e).catch(()=>Ut.resolve(e));Jn=null,ig=null;zc=new Map,fg=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,Q_=12e3;$r=new Map,TH=12e4;cg=!1;_H=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});import{execFile as IH}from"node:child_process";import{promisify as $H}from"node:util";function MH(e){return lI(e?.toPr?.number??e?.openPr?.number)}function lI(e){if(e==null||e==="")return null;let t=Number(e);return Number.isFinite(t)&&t>0?t:null}function CH(e){return Array.isArray(e?.actions)?e.actions.includes("push")||e.actions.includes("pr"):e?.action==="push"||e?.action==="pr"}function qa(e,t=Date.now()){let n=null,r=new Map;for(let o of e||[]){let s=o.payload?.ts??o.ts;if(o.type==="pr.offered"){o.payload?.offerId&&r.set(o.payload.offerId,o.payload);continue}if(o.type==="pr.offer.answered"){let a=o.payload||{},i=r.get(a.offerId)||null,c=MH(i),l=Array.isArray(a.actions)&&a.actions.includes("rebase"),u=i?.base??null;a.action==="pr"?n={ts:s,offerId:a.offerId??null,box:o.boxId??null,offerType:"offer",toPr:c,base:u,rebase:l}:a.action!=="dismiss"&&a.action!=="pr-exists"&&c!=null&&CH(a)?n={ts:s,offerId:a.offerId??null,box:o.boxId??null,offerType:"offer",toPr:c,base:u,rebase:l}:n=null}else if(o.type==="pr.exists.answered"){let a=o.payload||{};n=a.choice==="push"?{ts:s,offerId:a.offerId??null,box:o.boxId??null,offerType:"exists",toPr:lI(a.number),base:null,rebase:Array.isArray(a.actions)&&a.actions.includes("rebase")}:null}else o.type==="pr.state.changed"&&(o.payload?.change==="opened"||o.payload?.change==="pushed")&&n!=null&&s>=n.ts&&(n=null)}return n!=null&&t-n.ts>xg?null:n}function NH(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function _p(e,{bypassThrottle:t=!1}={}){let n=e?.workspace?.branch;if(!n)return;let r;try{r=await K(e)}catch{return}let o=qa(r);if(o==null)return;let s=(e.boxes||[]).find(p=>p.name===e.leadBox);if(await is(e.leadBox,s?.agent).catch(()=>!1))return;let a=Date.now();if(!t&&a<(Zc.get(e.id)||0))return;Zc.set(e.id,a+cI);let i;try{i=await Kn(e.cwd,{caller:"reconcile"})}catch{return}let c=await ja(e).catch(()=>[n]),l=o.toPr!=null&&(i||[]).find(p=>Number(p.number)===o.toPr&&c.includes(p.headRefName))||(i||[]).find(p=>c.includes(p.headRefName));if(!l)return;let u=l.headRefName||n,d=await Me(e.cwd).catch(()=>null);if(d){let{link:p,created:f}=await Yt({repo:d,number:l.number,trackId:e.id,url:l.url,headRef:l.headRefName||n,title:l.title,source:"reconciled"});if(!f&&!(p?.trackId===e.id&&await xr(d,l.number))){if(!NH(p,o))return;let w=await DH(e);if(w&&l.headSha&&w!==l.headSha){let y=Math.min(OH,(Ep.get(e.id)||cI)*2);Ep.set(e.id,y),Zc.set(e.id,a+y);return}let h=await K(e).catch(()=>null);if(h&&qa(h)==null)return;await N(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:BH(o,l,{branch:u,repo:d})}),Zc.delete(e.id),Ep.delete(e.id);return}}await N(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:PH(l,{branch:u,repo:d})}).catch(()=>{}),Zc.delete(e.id),Ep.delete(e.id)}function PH(e,{branch:t=null,repo:n=null,now:r=Date.now()}={}){return{ts:r,change:"opened",number:e.number,url:e.url,title:e.title,branch:e.headRefName||t,...n?{repo:n}:{}}}async function DH(e){try{let{stdout:t}=await RH("git",["-C",e.cwd,"rev-parse","HEAD"],{timeout:1e4}),n=String(t||"").trim();return/^[0-9a-f]{40}$/.test(n)?n:null}catch{return null}}function BH(e,t,{branch:n=null,repo:r=null,now:o=Date.now()}={}){return{ts:o,change:"pushed",number:t.number,url:t.url,title:t.title,branch:t.headRefName||n,...r?{repo:r}:{},...t.headSha?{sha:t.headSha}:{},via:"ship",...e?.rebase?{rebased:!0}:{},...e?.base?{base:e.base}:{}}}var RH,cI,Zc,OH,Ep,xg,Qc=G(()=>{be();vs();Gr();Gn();va();Ss();RH=$H(IH),cI=6e3,Zc=new Map,OH=5*6e4,Ep=new Map,xg=30*6e4});import vg from"node:os";import{execFile as LH}from"node:child_process";import{promisify as jH}from"node:util";async function Ip(){if(process.platform!=="darwin")return vg.freemem();try{let{stdout:e}=await FH("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:vg.freemem()}catch{return vg.freemem()}}var FH,Sg=G(()=>{FH=jH(LH)});import ot from"node:fs/promises";import st from"node:path";import fI from"node:os";import{randomUUID as UH}from"node:crypto";import{execFile as HH}from"node:child_process";import{promisify as WH}from"node:util";import{chromium as qH}from"playwright-core";async function er(){return Is||(Tg||=(async()=>{try{Is=JSON.parse(await ot.readFile(hI,"utf8"))}catch{Is={mocks:[]}}finally{Tg=null}return Is.mocks||=[],Is})(),Tg)}async function un(){await Bt(hI,Is)}function bI(e,t){Qn(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&Qn(e,t.v,"png")}function Rt(e,t,n={}){return N({id:e.trackId,cwd:e.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,mockId:e.id,name:e.name,...e.group?{group:e.group}:{},...e.kind==="video"?{kind:"video"}:{},...n}}).catch(()=>{})}function dn(e,t){let n=tl(e.cwd);return{html:st.join(n,`${e.id}.v${t}.html`),png:st.join(n,`${e.id}.v${t}.png`)}}function el(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?st.join(e.cwd,r):null}function $n(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 _g(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function $p(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(XH)}function ZH(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 QH(e){let t=Buffer.alloc(24),n=await ot.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function eW({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 tW(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,y=0;y<o;y++,h+=4)Math.abs(c[h]-l[0])+Math.abs(c[h+1]-l[1])+Math.abs(c[h+2]-l[2])>24&&(y<u&&(u=y),y>p&&(p=y),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 nW(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 rW({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await qH.launch({executablePath:wI,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Xt("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:Ga})).newPage();a.setDefaultTimeout(Ka),await a.goto(`file://${e}`,{waitUntil:"load",timeout:Ka}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Ka});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await tW(a,i),u=l&&eW({...l,pad:VH*Ga,align:Ga});u&&(i=await nW(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<gI)throw new Xt(`capture produced a ${i.length}-byte image`);let c=_g(i);if(!c)throw new Xt("output is not a PNG");return await ot.writeFile(t,i),{width:Math.round(c.width/Ga),height:Math.round(c.height/Ga),dpr:Ga,renderMs:Date.now()-r}}catch(s){throw s instanceof Xt?s:new Xt(/timeout/i.test(s?.message||"")?`render timed out after ${Ka}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function pI({htmlPath:e,outPath:t,width:n}){let r=await ot.mkdtemp(st.join(fI.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Eg(uI,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Ka,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new Xt(`firefox not found (${uI})`,""):new Xt(c?.killed?`render timed out after ${Ka}ms`:"firefox exited non-zero",s)}let a=await ot.stat(t).catch(()=>null);if(!a||a.size<gI)throw new Xt(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=_g(await QH(t));if(!i)throw new Xt("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await ot.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function oW(e){if(GH==="firefox")return pI(e);try{return await rW(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await pI(e)}catch(n){throw/not found/.test(n?.message||"")?new Xt(`no browser installed \u2014 chromium missing at ${wI} 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 kI(e){return Ag=Ag.then(e,e),Ag}async function xI(e,t){let n=await er(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await un(),await Rt(r,"rendering",{version:t});try{let o=await Ip();if(o<zH)throw new Xt(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=dn(r,t),a=await sW({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await ot.rm(s.png,{force:!0}).catch(()=>{});return}Ig(r,{v:t,source:"html",htmlRel:st.relative(r.cwd,s.html),pngRel:st.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await un(),await Rt(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),Zn(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 un(),await Rt(r,"error",{version:t,error:r.error}),Zn(r)}}}function Ig(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 aW({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?KH:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function nl({trackId:e,group:t}={}){let n=await er(),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 er()).mocks.find(r=>r.id===e);return n?{...n}:null}function vI({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&&!$p(t))throw new Error("image must be a PNG");if(n){let o=ZH(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>dI)throw new Error(`video must be ${dI/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function SI({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=aW({trackId:e,name:t,cwd:s,width:a}),p=typeof n=="string"&&n.trim(),f=vI({html:n,image:r,video:o}),w=await er(),h={id:UH().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 ot.mkdir(tl(s),{recursive:!0}),w.mocks.push(h),p?(await ot.writeFile(dn(h,1).html,n),await un(),await Rt(h,"created"),Zn(h),Qn(h,1,"html"),kI(()=>xI(h.id,1))):o?(await AI(h,1,o,f),await un(),await Rt(h,"created"),await Rt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr,durationS:h.versions[0]?.durationS??null}),Zn(h),bI(h,h.versions[0])):(await EI(h,1,r),await un(),await Rt(h,"created"),await Rt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr}),Zn(h),Qn(h,1,"png")),{...h}}async function TI(e,{html:t,image:n,video:r,width:o}={}){let a=(await er()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=vI({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 ot.mkdir(tl(a.cwd),{recursive:!0}),i?(a.state="queued",await ot.writeFile(dn(a,l).html,t),await un(),await Rt(a,"updated",{version:l}),Zn(a),Qn(a,l,"html"),kI(()=>xI(a.id,l))):r?(await AI(a,l,r,c),await un(),await Rt(a,"updated",{version:l}),await Rt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:$n(a)?.durationS??null}),Zn(a),bI(a,a.versions.find(u=>u.v===l))):(await EI(a,l,n),await un(),await Rt(a,"updated",{version:l}),await Rt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Zn(a),Qn(a,l,"png")),{...a}}async function iW(e){try{let{stdout:t}=await Eg(JH,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:yI,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 cW(e,t){try{await Eg(YH,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:yI,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await ot.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function AI(e,t,n,r){let o=tl(e.cwd),s=st.join(o,`${e.id}.v${t}.${r.ext}`),a=st.join(o,`${e.id}.v${t}.poster.png`);await ot.writeFile(s,n);let i=await iW(s),c=await cW(s,a);Ig(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:st.relative(e.cwd,s),posterRel:c?st.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 EI(e,t,n){let r=_g(n);if(!r)throw new Error("image must be a PNG");let o=dn(e,t);await ot.writeFile(o.png,n),Ig(e,{v:t,source:"image",htmlRel:null,pngRel:st.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function _I(e){let t=await er(),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 Rt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function II(e){let t=await er(),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=dn(r,o);await ot.rm(s.html,{force:!0}).catch(()=>{}),await ot.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await ot.rm(st.join(r.cwd,s),{force:!0}).catch(()=>{});return await un(),await Rt(r,"deleted"),bn("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function $I(e){let n=(await er()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=st.join(mI,"mock-archive",e.id),o=tl(e.cwd),s=st.join(r,".bostrat","mocks");await ot.mkdir(s,{recursive:!0});let a=await ot.readdir(o).catch(()=>[]);for(let i of a)await ot.copyFile(st.join(o,i),st.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 ot.stat(st.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(),Zn(i)}return await un(),n.length}async function RI(e){let t=await er(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await un();for(let r of n)bn("mocks",r.id).catch(()=>{})}return n.length}async function OI(){if(!Ge())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await pt("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await er();for(let r of n.mocks){await Zn(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 Eg,mI,hI,GH,wI,uI,KH,Ga,VH,gI,Ka,zH,dI,JH,YH,yI,Xt,Is,Tg,Zn,Qn,tl,XH,sW,Ag,Va=G(()=>{be();zr();Sg();Yr();Eg=WH(HH),mI=process.env.BOSTRAT_STATE_DIR||st.join(process.env.HOME||fI.tmpdir(),".bostrat"),hI=st.join(mI,"mocks.json"),GH=process.env.BOSTRAT_MOCK_ENGINE||"chromium",wI=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",uI=process.env.BOSTRAT_FIREFOX_BIN||"firefox",KH=1300,Ga=2,VH=32,gI=1500,Ka=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,zH=700*1024*1024,dI=100*1024*1024,JH=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",YH=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",yI=2e4,Xt=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
219
219
|
${n.trim().split(`
|
|
220
220
|
`).slice(-6).join(`
|
|
221
|
-
`)}`:t),this.name="MockRenderError"}},Is=null,Tg=null;Zn=e=>It("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),Qn=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?el(e,r,"video"):r&&el(e,r,n)||(n==="html"?dn(e,t).html:null);return o?Jr("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};tl=e=>st.join(e,".bostrat","mocks");YH=Buffer.from([137,80,78,71,13,10,26,10]);oW=rW,Ag=Promise.resolve()});import cW from"node:fs/promises";import NI from"node:path";import lW from"node:os";import{randomUUID as PI}from"node:crypto";async function Nr(){return Cr||($g||=(async()=>{let e=!0;try{Cr=JSON.parse(await cW.readFile(DI,"utf8"))}catch{Cr={flows:[]},e=!1}finally{$g=null}if(Cr.flows||=[],!e&&Ge()){let{records:t,source:n}=await pt("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&Cr.flows.push(r.data);Cr.flows.length&&await za().catch(()=>{})}}return Cr})(),$g)}async function za(){await Bt(DI,Cr)}function Rg(e,t,n={}){return N({id:e.trackId,cwd:e.cwd},{type:"flow.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,flowId:e.id,name:e.name,stepCount:e.steps.length,mockIds:[...new Set(e.steps.map(r=>r.mockId))],...n}}).catch(()=>{})}async function BI({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>RI)throw new Error(`steps must be an array of 1-${RI}`);let r=await nl({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>MI)throw new Error(`${l}: caption over ${MI} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>CI)throw new Error(`${l}: title over ${CI} chars`);let p=String(c.mock??"").trim().replace(/^mock:/,"");if(!p)throw new Error(`${l}: mock (name or id) required`);let f=r.filter(g=>g.id===p);if(f.length||(f=r.filter(g=>g.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(g=>`mock:${g.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(g=>g.v===h)))throw new Error(`${l}: "${w.name}" has no captured v${c.v} to pin`);let y=o.findIndex(g=>g.mockId===w.id),k=y>=0?o.splice(y,1)[0].id:PI().slice(0,8);s.push({id:k,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 dW(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>OI)throw new Error(`name over ${OI} chars`);return e.trim()}async function LI({trackId:e}={}){let t=await Nr();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function pW(e){let n=(await Nr()).flows.find(r=>r.id===e);return n?{...n}:null}async function jI(e){let t=await pW(e);if(!t)return null;let n=new Map((await nl({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=$n(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 FI({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=dW(t),i=await Nr(),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 BI({trackId:e,steps:n}),d=Date.now(),p={id:PI().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 za(),await Rg(p,"created"),$p(p),{...p}}async function UI(e,{steps:t,note:n}={}){let o=(await Nr()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await BI({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 za(),await Rg(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),$p(o),{...o}}async function HI(e){let t=await Nr(),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 za(),await Rg(r,"deleted"),bn("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function WI(e){return(await Nr()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function qI(e){let n=(await Nr()).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(),$p(r);return n.length&&await za(),n.length}async function GI(e){let t=await Nr(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await za();for(let r of n)bn("flows",r.id).catch(()=>{})}return n.length}async function KI(){if(!Ge())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await pt("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Nr();for(let r of n.flows)await $p(r);return{pushed:n.flows.length,source:"plane"}}var uW,DI,RI,OI,MI,CI,Cr,$g,$p,Og=G(()=>{be();zr();Va();Yr();uW=process.env.BOSTRAT_STATE_DIR||NI.join(process.env.HOME||lW.tmpdir(),".bostrat"),DI=NI.join(uW,"flows.json"),RI=20,OI=80,MI=500,CI=80,Cr=null,$g=null;$p=e=>It("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import Br from"node:fs/promises";import Lr from"node:path";import fW from"node:os";import{randomUUID as mW}from"node:crypto";function rl(e){return hW.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function tr(){return Pr||(Mg||=(async()=>{let e=!0;try{Pr=JSON.parse(await Br.readFile(zI,"utf8"))}catch{Pr={runbooks:[]},e=!1}finally{Mg=null}return Pr.runbooks||=[],!e&&Ge()&&await wW().catch(()=>{}),Pr})(),Mg)}async function Rs(){await Bt(zI,Pr)}function Op(e){return ol(It("runbooks",{...e},{trackId:e.trackId}))}async function wW(){let{records:e,source:t}=await pt("runbooks");if(!(t!=="plane"||!e.length)){await Br.mkdir(Rp,{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 Vi("runbooks",r.id,o.v,"md");typeof s=="string"&&await Br.writeFile(Dr(r,o.v),s).catch(()=>{})}Pr.runbooks.push(r)}Pr.runbooks.length&&await Rs()}}function Ja(e,t,n={}){return N({id:e.trackId,cwd:e.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,runbookId:e.id,name:e.name,status:e.status,...n}}).catch(()=>{})}function Dr(e,t){return Lr.join(Rp,`${e.id}.v${t}.md`)}function JI(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 YI(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>VI)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${VI/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=rl(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 gW(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function yW({trackId:e,status:t}={}){let n=await tr(),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 XI({trackId:e,status:t}={}){let n=await tr(),r=await yW({trackId:e,status:t});if(!Ge())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await pt("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 Cg(e){let t=await bW(e);if(t)return t;if(!Ge())return null;let{records:n,source:r}=await pt("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 ZI(e,t){let n=await Vi("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function bW(e){let n=(await tr()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function QI({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=YI(r),c=await tr(),l={id:mW().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 Br.mkdir(Rp,{recursive:!0}),await Br.writeFile(Dr(l,1),r),l.versions.push({v:1,mdRel:Lr.relative($s,Dr(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Rs(),await Ja(l,"created",{version:1}),Op(l),ol(Jr("runbooks",l.id,1,"md",Dr(l,1))),{...l}}async function e$(e,{md:t,note:n}={}){let o=(await tr()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");gW(o,"update");let s=YI(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Br.mkdir(Rp,{recursive:!0}),await Br.writeFile(Dr(o,a),t),o.versions.push({v:a,mdRel:Lr.relative($s,Dr(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Rs(),await Ja(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Op(o),ol(Jr("runbooks",o.id,a,"md",Dr(o,a))),{...o}}async function t$(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await tr()).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 Rs(),await Ja(r,t==="done"?"done":"reopened"),Op(r),{...r})}async function n$(e){let t=await tr(),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 Br.rm(Lr.join($s,o.mdRel),{force:!0}).catch(()=>{});return await Rs(),r.trackDeletedAt||await Ja(r,"deleted"),ol(bn("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function r$(e){let n=(await tr()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Ja(r,"orphaned"),r.cwd=null,Op(r);return n.length&&await Rs(),n.length}async function o$(e){let t=await tr(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await Ja(r,"deleted");for(let o of r.versions||[])await Br.rm(Lr.join($s,o.mdRel),{force:!0}).catch(()=>{});ol(bn("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await Rs()),n.length}async function s$(){if(!Ge())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await pt("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await tr();for(let r of n.runbooks){await It("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await Jr("runbooks",r.id,o.v,"md",Lr.join($s,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var $s,zI,Rp,VI,hW,Pr,Mg,ol,Mp=G(()=>{zr();be();Yr();$s=process.env.BOSTRAT_STATE_DIR||Lr.join(process.env.HOME||fW.tmpdir(),".bostrat"),zI=Lr.join($s,"runbooks.json"),Rp=Lr.join($s,"runbooks"),VI=256*1024,hW=[{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/}];Pr=null,Mg=null;ol=e=>Promise.resolve(e).catch(()=>{})});import Ng from"node:fs/promises";import sl from"node:path";import{randomUUID as kW}from"node:crypto";function Dg(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return xW.has(t)?"image":vW.has(t)?"doc":null}function SW(e){let r=sl.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 c$(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>a$)throw new Error(`file too large \u2014 max ${Math.floor(a$/(1024*1024))} MB`);let r=SW(t),o=sl.extname(r).slice(1),s=Dg(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=kW().slice(0,8),i=Pg(e);await Ng.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Ng.writeFile(sl.join(i,c),n,{mode:384}),await Ng.chmod(sl.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${i$}/${c}`,kind:s,size:n.length}}function l$(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function Bg(e){return e.role==="user"&&TW.test((e.text||"").trim())}var i$,Pg,a$,xW,vW,TW,Lg=G(()=>{i$=".bostrat/uploads",Pg=e=>sl.join(e,i$),a$=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,xW=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),vW=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"]);TW=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function Os(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}function jg(){let e=Number(process.env.BOSTRAT_LOOP_QUEUE_HOLD_MS);return Number.isFinite(e)&&e>0?e:AW}function _W(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 u$(e,t,{capMs:n=jg(),now:r=Date.now()}={}){if(!e)return!1;let o=_W(e,t,r);return e.delivery?.producer!=="loop"||!EW.has(t)?!1:o>=n}var AW,EW,d$=G(()=>{AW=5*6e4,EW=new Set(["busy","noSession"])});import{execFile as IW}from"node:child_process";import{promisify as $W}from"node:util";function Fg(e){return`refs/bostrat/wip/${e}`}function MW(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function m$(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function CW(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:OW}function h$(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 NW(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 Ao(s,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Ao(s,["remote","get-url","origin"])}catch{return p$.has(s)||(p$.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 Ra(s,"bostrat: wip snapshot"),{stdout:c}=await Ao(s,["rev-parse",`${i}^{tree}`]),l=c.trim();if(!n&&Cp.get(s)===l)return{pushed:!1,reason:"unchanged"};let u=Fg(o);return await Ao(s,["push","--force","origin",`${i}:${u}`],f$),Cp.set(s,l),await N(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:u,sha:i,wip:!0,...w$(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(`
|
|
222
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function Ug(e,{log:t=console,force:n=!1}={}){let r=[],o=[];for(let s of
|
|
223
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function Hg(e,{log:t=console,ts:n=Date.now()}={}){let r=[],o=[];for(let s of
|
|
221
|
+
`)}`:t),this.name="MockRenderError"}},Is=null,Tg=null;Zn=e=>It("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),Qn=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?el(e,r,"video"):r&&el(e,r,n)||(n==="html"?dn(e,t).html:null);return o?Jr("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};tl=e=>st.join(e,".bostrat","mocks");XH=Buffer.from([137,80,78,71,13,10,26,10]);sW=oW,Ag=Promise.resolve()});import lW from"node:fs/promises";import DI from"node:path";import uW from"node:os";import{randomUUID as BI}from"node:crypto";async function Nr(){return Cr||($g||=(async()=>{let e=!0;try{Cr=JSON.parse(await lW.readFile(LI,"utf8"))}catch{Cr={flows:[]},e=!1}finally{$g=null}if(Cr.flows||=[],!e&&Ge()){let{records:t,source:n}=await pt("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&Cr.flows.push(r.data);Cr.flows.length&&await za().catch(()=>{})}}return Cr})(),$g)}async function za(){await Bt(LI,Cr)}function Rg(e,t,n={}){return N({id:e.trackId,cwd:e.cwd},{type:"flow.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,flowId:e.id,name:e.name,stepCount:e.steps.length,mockIds:[...new Set(e.steps.map(r=>r.mockId))],...n}}).catch(()=>{})}async function jI({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>MI)throw new Error(`steps must be an array of 1-${MI}`);let r=await nl({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>NI)throw new Error(`${l}: caption over ${NI} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>PI)throw new Error(`${l}: title over ${PI} chars`);let p=String(c.mock??"").trim().replace(/^mock:/,"");if(!p)throw new Error(`${l}: mock (name or id) required`);let f=r.filter(g=>g.id===p);if(f.length||(f=r.filter(g=>g.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(g=>`mock:${g.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(g=>g.v===h)))throw new Error(`${l}: "${w.name}" has no captured v${c.v} to pin`);let y=o.findIndex(g=>g.mockId===w.id),k=y>=0?o.splice(y,1)[0].id:BI().slice(0,8);s.push({id:k,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 pW(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>CI)throw new Error(`name over ${CI} chars`);return e.trim()}async function FI({trackId:e}={}){let t=await Nr();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function fW(e){let n=(await Nr()).flows.find(r=>r.id===e);return n?{...n}:null}async function UI(e){let t=await fW(e);if(!t)return null;let n=new Map((await nl({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=$n(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 HI({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=pW(t),i=await Nr(),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 jI({trackId:e,steps:n}),d=Date.now(),p={id:BI().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 za(),await Rg(p,"created"),Rp(p),{...p}}async function WI(e,{steps:t,note:n}={}){let o=(await Nr()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await jI({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 za(),await Rg(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Rp(o),{...o}}async function qI(e){let t=await Nr(),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 za(),await Rg(r,"deleted"),bn("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function GI(e){return(await Nr()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function KI(e){let n=(await Nr()).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(),Rp(r);return n.length&&await za(),n.length}async function VI(e){let t=await Nr(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await za();for(let r of n)bn("flows",r.id).catch(()=>{})}return n.length}async function zI(){if(!Ge())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await pt("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Nr();for(let r of n.flows)await Rp(r);return{pushed:n.flows.length,source:"plane"}}var dW,LI,MI,CI,NI,PI,Cr,$g,Rp,Og=G(()=>{be();zr();Va();Yr();dW=process.env.BOSTRAT_STATE_DIR||DI.join(process.env.HOME||uW.tmpdir(),".bostrat"),LI=DI.join(dW,"flows.json"),MI=20,CI=80,NI=500,PI=80,Cr=null,$g=null;Rp=e=>It("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import Br from"node:fs/promises";import Lr from"node:path";import mW from"node:os";import{randomUUID as hW}from"node:crypto";function rl(e){return wW.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function tr(){return Pr||(Mg||=(async()=>{let e=!0;try{Pr=JSON.parse(await Br.readFile(YI,"utf8"))}catch{Pr={runbooks:[]},e=!1}finally{Mg=null}return Pr.runbooks||=[],!e&&Ge()&&await gW().catch(()=>{}),Pr})(),Mg)}async function Rs(){await Bt(YI,Pr)}function Mp(e){return ol(It("runbooks",{...e},{trackId:e.trackId}))}async function gW(){let{records:e,source:t}=await pt("runbooks");if(!(t!=="plane"||!e.length)){await Br.mkdir(Op,{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 Vi("runbooks",r.id,o.v,"md");typeof s=="string"&&await Br.writeFile(Dr(r,o.v),s).catch(()=>{})}Pr.runbooks.push(r)}Pr.runbooks.length&&await Rs()}}function Ja(e,t,n={}){return N({id:e.trackId,cwd:e.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,runbookId:e.id,name:e.name,status:e.status,...n}}).catch(()=>{})}function Dr(e,t){return Lr.join(Op,`${e.id}.v${t}.md`)}function XI(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 ZI(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>JI)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${JI/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=rl(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 yW(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function bW({trackId:e,status:t}={}){let n=await tr(),r=e?n.runbooks.filter(o=>o.trackId===e):n.runbooks;return t&&(r=r.filter(o=>o.status===t)),r.map(o=>({...o}))}async function QI({trackId:e,status:t}={}){let n=await tr(),r=await bW({trackId:e,status:t});if(!Ge())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await pt("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 Cg(e){let t=await kW(e);if(t)return t;if(!Ge())return null;let{records:n,source:r}=await pt("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 Vi("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function kW(e){let n=(await tr()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function t$({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=ZI(r),c=await tr(),l={id:hW().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 Br.mkdir(Op,{recursive:!0}),await Br.writeFile(Dr(l,1),r),l.versions.push({v:1,mdRel:Lr.relative($s,Dr(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Rs(),await Ja(l,"created",{version:1}),Mp(l),ol(Jr("runbooks",l.id,1,"md",Dr(l,1))),{...l}}async function n$(e,{md:t,note:n}={}){let o=(await tr()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");yW(o,"update");let s=ZI(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Br.mkdir(Op,{recursive:!0}),await Br.writeFile(Dr(o,a),t),o.versions.push({v:a,mdRel:Lr.relative($s,Dr(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Rs(),await Ja(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Mp(o),ol(Jr("runbooks",o.id,a,"md",Dr(o,a))),{...o}}async function r$(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await tr()).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 Rs(),await Ja(r,t==="done"?"done":"reopened"),Mp(r),{...r})}async function o$(e){let t=await tr(),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 Br.rm(Lr.join($s,o.mdRel),{force:!0}).catch(()=>{});return await Rs(),r.trackDeletedAt||await Ja(r,"deleted"),ol(bn("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function s$(e){let n=(await tr()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Ja(r,"orphaned"),r.cwd=null,Mp(r);return n.length&&await Rs(),n.length}async function a$(e){let t=await tr(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await Ja(r,"deleted");for(let o of r.versions||[])await Br.rm(Lr.join($s,o.mdRel),{force:!0}).catch(()=>{});ol(bn("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await Rs()),n.length}async function i$(){if(!Ge())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await pt("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await tr();for(let r of n.runbooks){await It("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await Jr("runbooks",r.id,o.v,"md",Lr.join($s,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var $s,YI,Op,JI,wW,Pr,Mg,ol,Cp=G(()=>{zr();be();Yr();$s=process.env.BOSTRAT_STATE_DIR||Lr.join(process.env.HOME||mW.tmpdir(),".bostrat"),YI=Lr.join($s,"runbooks.json"),Op=Lr.join($s,"runbooks"),JI=256*1024,wW=[{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/}];Pr=null,Mg=null;ol=e=>Promise.resolve(e).catch(()=>{})});import Ng from"node:fs/promises";import sl from"node:path";import{randomUUID as xW}from"node:crypto";function Dg(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return vW.has(t)?"image":SW.has(t)?"doc":null}function TW(e){let r=sl.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 u$(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>c$)throw new Error(`file too large \u2014 max ${Math.floor(c$/(1024*1024))} MB`);let r=TW(t),o=sl.extname(r).slice(1),s=Dg(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=xW().slice(0,8),i=Pg(e);await Ng.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Ng.writeFile(sl.join(i,c),n,{mode:384}),await Ng.chmod(sl.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${l$}/${c}`,kind:s,size:n.length}}function d$(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function Bg(e){return e.role==="user"&&AW.test((e.text||"").trim())}var l$,Pg,c$,vW,SW,AW,Lg=G(()=>{l$=".bostrat/uploads",Pg=e=>sl.join(e,l$),c$=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,vW=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),SW=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"]);AW=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function Os(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}function jg(){let e=Number(process.env.BOSTRAT_LOOP_QUEUE_HOLD_MS);return Number.isFinite(e)&&e>0?e:EW}function IW(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 p$(e,t,{capMs:n=jg(),now:r=Date.now()}={}){if(!e)return!1;let o=IW(e,t,r);return e.delivery?.producer!=="loop"||!_W.has(t)?!1:o>=n}var EW,_W,f$=G(()=>{EW=5*6e4,_W=new Set(["busy","noSession"])});import{execFile as $W}from"node:child_process";import{promisify as RW}from"node:util";function Fg(e){return`refs/bostrat/wip/${e}`}function CW(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function w$(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function NW(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:MW}function g$(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 PW(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 Ao(s,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Ao(s,["remote","get-url","origin"])}catch{return m$.has(s)||(m$.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 Ra(s,"bostrat: wip snapshot"),{stdout:c}=await Ao(s,["rev-parse",`${i}^{tree}`]),l=c.trim();if(!n&&Np.get(s)===l)return{pushed:!1,reason:"unchanged"};let u=Fg(o);return await Ao(s,["push","--force","origin",`${i}:${u}`],h$),Np.set(s,l),await N(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:u,sha:i,wip:!0,...y$(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(`
|
|
222
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function Ug(e,{log:t=console,force:n=!1}={}){let r=[],o=[];for(let s of g$(e)){let a=await PW(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 DW(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 Ao(s,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Ao(s,["remote","get-url","origin"])}catch{return{pushed:!1,reason:"no-origin"}}try{let i=await Ra(s,"bostrat: transfer rescue snapshot"),{stdout:c}=await Ao(s,["rev-parse",`${i}^{tree}`]),l=c.trim();if(Np.get(s)===l)return{pushed:!1,reason:"unchanged"};let u=CW(o,r);return await Ao(s,["push","origin",`${i}:${u}`],h$),Np.set(s,l),await N(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:u,sha:i,rescue:!0,...y$(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(`
|
|
223
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function Hg(e,{log:t=console,ts:n=Date.now()}={}){let r=[],o=[];for(let s of g$(e)){let a=await DW(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 b$({listTracks:e,sweepMs:t=NW(),log:n=console}){let r=!1,o=async()=>{if(!r){r=!0;try{let a=await e()??[];for(let i of a)Ue(i)||i?.kind==="setup"||await Ug(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 OW,MW,h$,Ao,Np,m$,y$,Wg=G(()=>{Md();be();an();OW=RW($W),MW=12e4,h$=6e4;Ao=(e,t,n=15e3)=>OW("git",["-C",e,...t],{timeout:n,maxBuffer:4*1024*1024}),Np=new Map,m$=new Set,y$=e=>e?.name||e?.repo?{name:e.name??null,repo:e.repo??null}:{}});import BW from"node:fs";import qg from"node:fs/promises";import LW from"node:path";function HW(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}async function k$(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(!HW(t))return{restored:!1,reason:"plane not configured"};let s=sa(e),a=LW.join(s,`${o}.ndjson`);try{if(BW.statSync(a).size>0)return{restored:!1,reason:"log not empty"}}catch{}let i=De(t),c={authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`},l=[],u=0;for(;;){let p=new AbortController,f=setTimeout(()=>p.abort(),UW),w;try{w=await n(`${i}/v1/spine/${encodeURIComponent(o)}?after=${u}&limit=${jW}`,{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 h=await w.json();for(let y of h.events||[])l.push(JSON.stringify(y));if(l.length>FW)return{restored:!1,reason:"event count over safety cap"};if(h.next==null)break;u=h.next}if(!l.length)return{restored:!1,reason:"unknown to plane"};await qg.mkdir(s,{recursive:!0});let d=`${a}.pull-${process.pid}-${Date.now()}`;return await qg.writeFile(d,l.join(`
|
|
224
224
|
`)+`
|
|
225
|
-
`),await qg.rename(d,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var LW,jW,FW,b$=G(()=>{xt();be();LW=1e3,jW=5e5,FW=3e4});function k$(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 x$(e){let t=(e||[]).filter(n=>!HW.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 v$({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?pn("archived","lifecycle",e):n?pn("needs-you","pane",o||w,{count:r||1}):t?pn("archiving","lifecycle",e||w):i?.status==="working"?pn("working","operation",i.since,{summary:i.summary??null,count:i.count??null}):(l||n)&&c?.phase?pn("working","agent-phase",c.at,{summary:c.summary??null}):l?pn("working","pane",u||w):r>0||s?pn("needs-you",s?"loop":"obligations",o||w,{count:r||1}):a?.count?pn("needs-you","pr",a.since,{count:a.count,summary:a.summary??null}):i?.status==="waiting"?pn("waiting","operation",i.since,{summary:i.summary??null,count:i.count??null}):d?.count?pn("waiting",d.source||"pending",d.since,{summary:d.summary??null,count:d.count}):p&&f?pn("settled","obligations",p,{summary:"no open actions"}):pn("idle","fallback",p||w)}var pn,HW,S$=G(()=>{pn=(e,t,n,r={})=>({kind:e,source:t,since:n||0,...r});HW=new Set(["merged","closed","lost"])});function WW(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 qW(e,t,n){let r=WW(t,n);return r?N(e,{type:"track.obligations.changed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),open:r.open,since:r.since}}).catch(()=>null):null}async function Gg(e){if(Np.has(e.id))return Np.get(e.id);let t=await K(e).catch(()=>[]),n=[];try{n=Hn(t)}catch{}let r=dd(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 Np.set(e.id,p),!Ue(e)&&e.kind!=="setup"&&qW(e,t,p),p}function T$({trackOf:e,debounceMs:t=GW,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(al.get(s));let a=setTimeout(()=>{al.delete(s),Promise.resolve(e(s)).then(i=>i&&!Ue(i)&&i.kind!=="setup"?Gg(i):null).catch(i=>n.warn?.(`[obligations] fold ${s} failed: ${i?.message||i}`))},t);a.unref?.(),al.set(s,a)};return Ze.on("event",r),()=>{Ze.off("event",r);for(let o of al.values())clearTimeout(o);al.clear()}}var Np,GW,al,Kg=G(()=>{be();pd();an();Np=new Map;Ze.on("event",e=>{e?.trackId&&Np.delete(e.trackId)});GW=750,al=new Map});import Vg from"node:fs/promises";async function VW(e){let t;try{t=await Vg.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/KW*1e3,ioBytes:await zW(e)}}async function zW(e){let t;try{t=await Vg.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 A$(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 Vg.readdir("/proc")}catch{return null}let r=new Map;await Promise.all(n.map(async l=>{if(!/^\d+$/.test(l))return;let u=await VW(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 KW,E$=G(()=>{KW=100});import JW from"node:fs/promises";function Xg(e){let t=[];for(let n of e||[])if(!(!n||n.archivedAt&&!n.archiving||n.transferredTo))for(let r of n.boxes||[])r?.state==="live"&&r.name&&t.push({track:n,box:r});return t}async function Zg(e,t,n){let r=null;try{r=await n.updatedAt(e)}catch{r=null}let o=_$.get(e.name);if(o&&o.mtime===r&&t-o.at<eq)return o.jobs;let s=[];try{s=await n.readOpenJobs(e)}catch{s=[]}return _$.set(e.name,{at:t,mtime:r,jobs:s}),s}async function tq(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 nq(e,t){return e?.outputFile?await t.statFile(e.outputFile):null}function rq(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>=ZW||t.ioBytes!=null&&e.ioBytes!=null&&(t.ioBytes-e.ioBytes)/n>=QW}function I$(e,t,n,r){let o=[];for(let s of n){let a=Yg(e.id,t.name,s.id),i=Rn.get(a);i?i.job=s:Rn.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 $$(e,{now:t=Date.now(),deps:n=Jg}={}){let o=(await Promise.all(Xg([e]).map(({box:s})=>Zg(s,t,n)))).flat();return o.length?{source:"jobs",count:o.length,since:Math.min(...o.map(s=>s.ts||t)),summary:o.length===1?"a background job is still running":`${o.length} background jobs are still running`}:null}async function R$(e,{now:t=Date.now(),deps:n=Jg}={}){let r=new Map,o=new Set;for(let{track:s,box:a}of Xg(e)){let i=await Zg(a,t,n);for(let l of i)o.add(Yg(s.id,a.name,l.id));let c=I$(s,a,i,t).filter(l=>!Rn.get(l).stalled).map(l=>Rn.get(l).job);c.length&&r.set(s.id,[...r.get(s.id)??[],...c])}for(let s of[...Rn.keys()])o.has(s)||Rn.delete(s);return r}async function oq(e,{now:t=Date.now(),deps:n=Jg}={}){let r={checked:0,progressing:0,quiet:0,stalled:[]},o=Xg(e),s=new Set(o.map(({box:a})=>a.name));for(let[a,i]of[...Rn])s.has(i.boxName)||Rn.delete(a);for(let{track:a,box:i}of o){let c=await Zg(i,t,n),l=new Set(c.map(w=>Yg(a.id,i.name,w.id)));for(let[w,h]of[...Rn])h.boxName===i.name&&!l.has(w)&&Rn.delete(w);if(!c.length){zg.delete(i.name);continue}let u=I$(a,i,c,t),d=await tq(i,t,n),p=zg.get(i.name);zg.set(i.name,d);let f=rq(p,d);for(let w of u){let h=Rn.get(w);r.checked++;let y=await nq(h.job,n),k=!!y&&!!h.file&&(y.size>h.file.size||y.mtimeMs>h.file.mtimeMs),g=!!y&&!h.file;if(h.file=y??h.file,f||k||g){h.quiet=0,h.lastProgressAt=t,h.stalled=!1,r.progressing++;continue}if(h.stalled||(h.quiet++,r.quiet++,h.quiet<XW))continue;h.stalled=!0,h.stalledAt=t,r.stalled.push(w);let S=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:S,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(S/6e4)}m over ${h.quiet} check(s) \u2014 released; the node may sleep`)}}return r}function O$({listTracks:e,intervalMs:t=YW}={}){let n=!1,o=setInterval(async()=>{if(!n){n=!0;try{await oq(await e())}catch(s){console.error(`[job-watch] sweep: ${s?.message||s}`)}finally{n=!1}}},t);return o.unref?.(),o}var YW,XW,ZW,QW,eq,Jg,Rn,_$,zg,Yg,Qg=G(()=>{be();kd();dt();E$();YW=Number(process.env.BOSTRAT_JOB_CHECK_MS)||15*6e4,XW=Number(process.env.BOSTRAT_JOB_STALL_CHECKS)||3,ZW=Number(process.env.BOSTRAT_JOB_CPU_RATE_MIN)||25,QW=Number(process.env.BOSTRAT_JOB_IO_RATE_MIN)||64*1024,eq=5*6e4,Jg={readOpenJobs:bd,statFile:async e=>{try{let t=await JW.stat(e);return{size:t.size,mtimeMs:Math.round(t.mtimeMs)}}catch{return null}},updatedAt:e=>ao(e.agent).updatedAt(e),panePids:Ex,treeStats:A$,appendEvent:N},Rn=new Map,_$=new Map,zg=new Map,Yg=(e,t,n)=>`${e}|${t}|${n}`});import sq from"node:fs/promises";import aq from"node:os";import Pp from"node:path";async function ey(e){try{return(await sq.stat(e)).size}catch{return null}}async function Ya(e){let[t,n,r]=await Promise.all([ey(Pp.join(iq,"CLAUDE.md")),e?ey(Pp.join(e,"CLAUDE.md")):null,e?ey(Pp.join(e,".bostrat","memory","MEMORY.md")):null]);return{bostratClaudeMd:t,repoClaudeMd:n,memoryIndex:r}}var iq,ty=G(()=>{iq=process.env.BOSTRAT_STATE_DIR||Pp.join(process.env.HOME||aq.tmpdir(),".bostrat")});import{randomUUID as cq}from"node:crypto";async function Bp(e,t,n={}){let r={operationId:cq(),kind:t,startedAt:Date.now(),...n};return Dp.set(e.id,r),await N(e,{type:"track.operation.changed",actor:"orchestrator",source:"orchestrator",payload:{ts:r.startedAt,state:"started",...r}}).catch(()=>{}),r}async function ny(e,t,n="completed",r=null){Dp.get(e.id)?.operationId===t?.operationId&&Dp.delete(e.id),await N(e,{type:"track.operation.changed",actor:"orchestrator",source:"orchestrator",payload:{ts:Date.now(),state:"finished",operationId:t?.operationId,kind:t?.kind,outcome:n,...r?{error:String(r)}:{}}}).catch(()=>{})}var Dp,M$,ry=G(()=>{be();Dp=new Map;M$=e=>Dp.get(e)||null});var mR={};sm(mR,{HANDOFF_LATEST_REL:()=>Io,KNOWN_AGENTS:()=>yl,LOGIN_GREETING:()=>Y$,TRACK_NAME_MAX:()=>gy,addTrackRepo:()=>Qp,adoptTrack:()=>tb,agentAvailability:()=>zp,announceNodeUpdate:()=>or,answerFullscreenOffer:()=>zy,answerPrompt:()=>Yp,answeringFor:()=>Ty,archiveTrack:()=>Ds,archiveTrackForMergedPr:()=>Er,augmentFirstPromptAfterHandoff:()=>H$,buildAgentCmd:()=>yy,canResumeSubtask:()=>dR,cancelQueued:()=>kl,cancelQueuedToDraft:()=>_y,cancelStopJob:()=>oi,cancelSubtask:()=>df,changedRepos:()=>_s,commitTransferOut:()=>cf,createSetupTrack:()=>jy,createTrack:()=>tf,deleteTrack:()=>Uy,demoteTransferredTracks:()=>eb,ensureLeadEpochs:()=>_o,forceMergeSubtask:()=>ob,getHandoffJob:()=>ii,getReshapeJob:()=>xl,getSubtaskJob:()=>_5,getTrack:()=>me,getTransferOutJob:()=>Zy,healDeadLead:()=>wl,interruptLead:()=>Zp,isArchiving:()=>p5,isHandoffNoteEcho:()=>dy,isInterruptEcho:()=>py,isLeadStarting:()=>z$,isModelSwitchEcho:()=>fy,leadCliDead:()=>Wy,listSubtaskJobs:()=>Ls,listTracks:()=>ke,matchTranscriptsToBoxes:()=>K$,mergeNotifyMessage:()=>iR,navigatePrompt:()=>Ay,notifyTrackForMergedPr:()=>_r,notifyTrackForResolvedPr:()=>nf,offerFollowupsForMergedPr:()=>Bs,pendingOffers:()=>vy,queueGauges:()=>Ey,queueShipCompleteNotify:()=>$y,queuedMessages:()=>Cs,rawTrack:()=>xy,reapArchivedOrphans:()=>Vy,reconcileDemotedTracks:()=>Ky,reconcileHandoffOutcome:()=>cR,reconcileInterruptedHandoffs:()=>Yy,reconcileInterruptedReshapes:()=>By,reconcileInterruptedSubtasks:()=>nb,reconcileNodeUpdateNotices:()=>ky,reconcileSubtaskOutcome:()=>uR,refreshStandingInstructions:()=>Ly,refuseIfTransferred:()=>af,rehydrateLead:()=>ai,removeTrackRepo:()=>ef,renameTrack:()=>Gy,reposOf:()=>Gt,reshapeTrack:()=>Dy,resolveTrackBox:()=>Hc,resolveTrackBoxByHookToken:()=>bl,restartLead:()=>of,resumeAgentCmd:()=>Vp,reviveLeadForSession:()=>qy,sayToLead:()=>qt,sendOptionsForAgent:()=>Un,setTrackDraft:()=>My,setTrackModel:()=>Cy,startLeadHandoff:()=>sf,startSpinUpView:()=>Ry,startSubtask:()=>rb,startTransferOut:()=>Qy,stopJobActive:()=>Z$,subtaskMessages:()=>Sy,trackMessages:()=>Jp,unarchiveTrack:()=>rf});import Y from"node:fs/promises";import z from"node:path";import{randomUUID as qp,createHash as lq}from"node:crypto";import{execFile as uq}from"node:child_process";import{promisify as dq}from"node:util";function yy(e,{skipPermissions:t=!0,model:n=null}={}){let r=Kp[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=dw(e).find(i=>i.alias===n);if(!a)throw new Error(`unknown ${e} launch model "${n}"`);s+=` --model ${a.alias}`}return s}function Vp(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=Kp[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&&!xn(r,t)){let c=o&&je(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 Ps(e){let t=Kp[e];if(t)return process.env[t.envVar]||t.base}async function zp({maxAgeMs:e=6e4}={}){if(Eo.value&&Date.now()-Eo.at<e)return Eo.value;let t=Eo.value,n=await Promise.all(yl.map(async r=>{let o=await Fu(Ps(r),{retries:[]});return o===ua?[r,t?.[r]??!0]:[r,o===ju]}));return Eo.value=Object.fromEntries(n),Eo.at=Date.now(),Eo.value}async function ri(e,t=!0,n=null){let r=yy(e,{skipPermissions:t,model:n});if(await Fu(r)===la){Eo.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.":Hm[o]?`Install it on this node: ${Hm[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 H$(e,t=Io){return`${e.trim()}
|
|
225
|
+
`),await qg.rename(d,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var jW,FW,UW,x$=G(()=>{vt();be();jW=1e3,FW=5e5,UW=3e4});function v$(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 S$(e){let t=(e||[]).filter(n=>!WW.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 T$({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?pn("archived","lifecycle",e):n?pn("needs-you","pane",o||w,{count:r||1}):t?pn("archiving","lifecycle",e||w):i?.status==="working"?pn("working","operation",i.since,{summary:i.summary??null,count:i.count??null}):(l||n)&&c?.phase?pn("working","agent-phase",c.at,{summary:c.summary??null}):l?pn("working","pane",u||w):r>0||s?pn("needs-you",s?"loop":"obligations",o||w,{count:r||1}):a?.count?pn("needs-you","pr",a.since,{count:a.count,summary:a.summary??null}):i?.status==="waiting"?pn("waiting","operation",i.since,{summary:i.summary??null,count:i.count??null}):d?.count?pn("waiting",d.source||"pending",d.since,{summary:d.summary??null,count:d.count}):p&&f?pn("settled","obligations",p,{summary:"no open actions"}):pn("idle","fallback",p||w)}var pn,WW,A$=G(()=>{pn=(e,t,n,r={})=>({kind:e,source:t,since:n||0,...r});WW=new Set(["merged","closed","lost"])});function qW(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 GW(e,t,n){let r=qW(t,n);return r?N(e,{type:"track.obligations.changed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),open:r.open,since:r.since}}).catch(()=>null):null}async function Gg(e){if(Pp.has(e.id))return Pp.get(e.id);let t=await K(e).catch(()=>[]),n=[];try{n=Hn(t)}catch{}let r=pd(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 Pp.set(e.id,p),!Ue(e)&&e.kind!=="setup"&&GW(e,t,p),p}function E$({trackOf:e,debounceMs:t=KW,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(al.get(s));let a=setTimeout(()=>{al.delete(s),Promise.resolve(e(s)).then(i=>i&&!Ue(i)&&i.kind!=="setup"?Gg(i):null).catch(i=>n.warn?.(`[obligations] fold ${s} failed: ${i?.message||i}`))},t);a.unref?.(),al.set(s,a)};return Ze.on("event",r),()=>{Ze.off("event",r);for(let o of al.values())clearTimeout(o);al.clear()}}var Pp,KW,al,Kg=G(()=>{be();fd();an();Pp=new Map;Ze.on("event",e=>{e?.trackId&&Pp.delete(e.trackId)});KW=750,al=new Map});import Vg from"node:fs/promises";async function zW(e){let t;try{t=await Vg.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/VW*1e3,ioBytes:await JW(e)}}async function JW(e){let t;try{t=await Vg.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 _$(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 Vg.readdir("/proc")}catch{return null}let r=new Map;await Promise.all(n.map(async l=>{if(!/^\d+$/.test(l))return;let u=await zW(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 VW,I$=G(()=>{VW=100});import YW from"node:fs/promises";function Xg(e){let t=[];for(let n of e||[])if(!(!n||n.archivedAt&&!n.archiving||n.transferredTo))for(let r of n.boxes||[])r?.state==="live"&&r.name&&t.push({track:n,box:r});return t}async function Zg(e,t,n){let r=null;try{r=await n.updatedAt(e)}catch{r=null}let o=$$.get(e.name);if(o&&o.mtime===r&&t-o.at<tq)return o.jobs;let s=[];try{s=await n.readOpenJobs(e)}catch{s=[]}return $$.set(e.name,{at:t,mtime:r,jobs:s}),s}async function nq(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 rq(e,t){return e?.outputFile?await t.statFile(e.outputFile):null}function oq(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>=QW||t.ioBytes!=null&&e.ioBytes!=null&&(t.ioBytes-e.ioBytes)/n>=eq}function R$(e,t,n,r){let o=[];for(let s of n){let a=Yg(e.id,t.name,s.id),i=Rn.get(a);i?i.job=s:Rn.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 O$(e,{now:t=Date.now(),deps:n=Jg}={}){let o=(await Promise.all(Xg([e]).map(({box:s})=>Zg(s,t,n)))).flat();return o.length?{source:"jobs",count:o.length,since:Math.min(...o.map(s=>s.ts||t)),summary:o.length===1?"a background job is still running":`${o.length} background jobs are still running`}:null}async function M$(e,{now:t=Date.now(),deps:n=Jg}={}){let r=new Map,o=new Set;for(let{track:s,box:a}of Xg(e)){let i=await Zg(a,t,n);for(let l of i)o.add(Yg(s.id,a.name,l.id));let c=R$(s,a,i,t).filter(l=>!Rn.get(l).stalled).map(l=>Rn.get(l).job);c.length&&r.set(s.id,[...r.get(s.id)??[],...c])}for(let s of[...Rn.keys()])o.has(s)||Rn.delete(s);return r}async function sq(e,{now:t=Date.now(),deps:n=Jg}={}){let r={checked:0,progressing:0,quiet:0,stalled:[]},o=Xg(e),s=new Set(o.map(({box:a})=>a.name));for(let[a,i]of[...Rn])s.has(i.boxName)||Rn.delete(a);for(let{track:a,box:i}of o){let c=await Zg(i,t,n),l=new Set(c.map(w=>Yg(a.id,i.name,w.id)));for(let[w,h]of[...Rn])h.boxName===i.name&&!l.has(w)&&Rn.delete(w);if(!c.length){zg.delete(i.name);continue}let u=R$(a,i,c,t),d=await nq(i,t,n),p=zg.get(i.name);zg.set(i.name,d);let f=oq(p,d);for(let w of u){let h=Rn.get(w);r.checked++;let y=await rq(h.job,n),k=!!y&&!!h.file&&(y.size>h.file.size||y.mtimeMs>h.file.mtimeMs),g=!!y&&!h.file;if(h.file=y??h.file,f||k||g){h.quiet=0,h.lastProgressAt=t,h.stalled=!1,r.progressing++;continue}if(h.stalled||(h.quiet++,r.quiet++,h.quiet<ZW))continue;h.stalled=!0,h.stalledAt=t,r.stalled.push(w);let S=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:S,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(S/6e4)}m over ${h.quiet} check(s) \u2014 released; the node may sleep`)}}return r}function C$({listTracks:e,intervalMs:t=XW}={}){let n=!1,o=setInterval(async()=>{if(!n){n=!0;try{await sq(await e())}catch(s){console.error(`[job-watch] sweep: ${s?.message||s}`)}finally{n=!1}}},t);return o.unref?.(),o}var XW,ZW,QW,eq,tq,Jg,Rn,$$,zg,Yg,Qg=G(()=>{be();xd();dt();I$();XW=Number(process.env.BOSTRAT_JOB_CHECK_MS)||15*6e4,ZW=Number(process.env.BOSTRAT_JOB_STALL_CHECKS)||3,QW=Number(process.env.BOSTRAT_JOB_CPU_RATE_MIN)||25,eq=Number(process.env.BOSTRAT_JOB_IO_RATE_MIN)||64*1024,tq=5*6e4,Jg={readOpenJobs:kd,statFile:async e=>{try{let t=await YW.stat(e);return{size:t.size,mtimeMs:Math.round(t.mtimeMs)}}catch{return null}},updatedAt:e=>ao(e.agent).updatedAt(e),panePids:Ix,treeStats:_$,appendEvent:N},Rn=new Map,$$=new Map,zg=new Map,Yg=(e,t,n)=>`${e}|${t}|${n}`});import aq from"node:fs/promises";import iq from"node:os";import Dp from"node:path";async function ey(e){try{return(await aq.stat(e)).size}catch{return null}}async function Ya(e){let[t,n,r]=await Promise.all([ey(Dp.join(cq,"CLAUDE.md")),e?ey(Dp.join(e,"CLAUDE.md")):null,e?ey(Dp.join(e,".bostrat","memory","MEMORY.md")):null]);return{bostratClaudeMd:t,repoClaudeMd:n,memoryIndex:r}}var cq,ty=G(()=>{cq=process.env.BOSTRAT_STATE_DIR||Dp.join(process.env.HOME||iq.tmpdir(),".bostrat")});import{randomUUID as lq}from"node:crypto";async function Lp(e,t,n={}){let r={operationId:lq(),kind:t,startedAt:Date.now(),...n};return Bp.set(e.id,r),await N(e,{type:"track.operation.changed",actor:"orchestrator",source:"orchestrator",payload:{ts:r.startedAt,state:"started",...r}}).catch(()=>{}),r}async function ny(e,t,n="completed",r=null){Bp.get(e.id)?.operationId===t?.operationId&&Bp.delete(e.id),await N(e,{type:"track.operation.changed",actor:"orchestrator",source:"orchestrator",payload:{ts:Date.now(),state:"finished",operationId:t?.operationId,kind:t?.kind,outcome:n,...r?{error:String(r)}:{}}}).catch(()=>{})}var Bp,N$,ry=G(()=>{be();Bp=new Map;N$=e=>Bp.get(e)||null});var hR={};sm(hR,{HANDOFF_LATEST_REL:()=>Io,KNOWN_AGENTS:()=>yl,LOGIN_GREETING:()=>Z$,TRACK_NAME_MAX:()=>gy,addTrackRepo:()=>ef,adoptTrack:()=>nb,agentAvailability:()=>Jp,announceNodeUpdate:()=>or,answerFullscreenOffer:()=>Jy,answerPrompt:()=>Xp,answeringFor:()=>Ty,archiveTrack:()=>Ds,archiveTrackForMergedPr:()=>Er,augmentFirstPromptAfterHandoff:()=>q$,buildAgentCmd:()=>yy,canResumeSubtask:()=>pR,cancelQueued:()=>kl,cancelQueuedToDraft:()=>_y,cancelStopJob:()=>oi,cancelSubtask:()=>df,changedRepos:()=>_s,commitTransferOut:()=>cf,createSetupTrack:()=>Fy,createTrack:()=>nf,deleteTrack:()=>Hy,demoteTransferredTracks:()=>tb,ensureLeadEpochs:()=>_o,forceMergeSubtask:()=>sb,getHandoffJob:()=>ii,getReshapeJob:()=>xl,getSubtaskJob:()=>$5,getTrack:()=>me,getTransferOutJob:()=>Qy,healDeadLead:()=>wl,interruptLead:()=>Qp,isArchiving:()=>m5,isHandoffNoteEcho:()=>dy,isInterruptEcho:()=>py,isLeadStarting:()=>Y$,isModelSwitchEcho:()=>fy,leadCliDead:()=>qy,listSubtaskJobs:()=>Ls,listTracks:()=>ke,matchTranscriptsToBoxes:()=>z$,mergeNotifyMessage:()=>cR,navigatePrompt:()=>Ay,notifyTrackForMergedPr:()=>_r,notifyTrackForResolvedPr:()=>rf,offerFollowupsForMergedPr:()=>Bs,pendingOffers:()=>vy,queueGauges:()=>Ey,queueShipCompleteNotify:()=>Ry,queuedMessages:()=>Cs,rawTrack:()=>xy,reapArchivedOrphans:()=>zy,reconcileDemotedTracks:()=>Vy,reconcileHandoffOutcome:()=>lR,reconcileInterruptedHandoffs:()=>Xy,reconcileInterruptedReshapes:()=>Ly,reconcileInterruptedSubtasks:()=>rb,reconcileNodeUpdateNotices:()=>ky,reconcileSubtaskOutcome:()=>dR,refreshStandingInstructions:()=>jy,refuseIfTransferred:()=>af,rehydrateLead:()=>ai,removeTrackRepo:()=>tf,renameTrack:()=>Ky,reposOf:()=>Gt,reshapeTrack:()=>By,resolveTrackBox:()=>Hc,resolveTrackBoxByHookToken:()=>bl,restartLead:()=>of,resumeAgentCmd:()=>zp,reviveLeadForSession:()=>Gy,sayToLead:()=>qt,sendOptionsForAgent:()=>Un,setTrackDraft:()=>Cy,setTrackModel:()=>Ny,startLeadHandoff:()=>sf,startSpinUpView:()=>Oy,startSubtask:()=>ob,startTransferOut:()=>eb,stopJobActive:()=>Q$,subtaskMessages:()=>Sy,trackMessages:()=>Yp,unarchiveTrack:()=>vl});import Y from"node:fs/promises";import z from"node:path";import{randomUUID as Gp,createHash as uq}from"node:crypto";import{execFile as dq}from"node:child_process";import{promisify as pq}from"node:util";function yy(e,{skipPermissions:t=!0,model:n=null}={}){let r=Vp[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=dw(e).find(i=>i.alias===n);if(!a)throw new Error(`unknown ${e} launch model "${n}"`);s+=` --model ${a.alias}`}return s}function zp(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=Vp[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&&!xn(r,t)){let c=o&&je(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 Ps(e){let t=Vp[e];if(t)return process.env[t.envVar]||t.base}async function Jp({maxAgeMs:e=6e4}={}){if(Eo.value&&Date.now()-Eo.at<e)return Eo.value;let t=Eo.value,n=await Promise.all(yl.map(async r=>{let o=await Uu(Ps(r),{retries:[]});return o===ua?[r,t?.[r]??!0]:[r,o===Fu]}));return Eo.value=Object.fromEntries(n),Eo.at=Date.now(),Eo.value}async function ri(e,t=!0,n=null){let r=yy(e,{skipPermissions:t,model:n});if(await Uu(r)===la){Eo.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.":Hm[o]?`Install it on this node: ${Hm[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 q$(e,t=Io){return`${e.trim()}
|
|
226
226
|
|
|
227
|
-
(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 dy(e){return e.role==="user"&&
|
|
228
|
-
`;return sy=sy.then(()=>Y.mkdir(nr,{recursive:!0})).then(()=>Y.appendFile(pq,n)).catch(()=>{}),sy}async function W$(e,{box:t,agent:n,message:r}){let o=fe(await ne(),e);!o||o.archivedAt||(Ee(o,"startup-gate",{box:t,agent:n??null,message:r}),await ae(),Tc(o,{provider:n??null,cls:"startup-gate",note:r}).catch(()=>{}))}async function or({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 Y.readFile(ay,"utf8"))}catch{}if(d?.latest===t&&d?.current===e)return{announced:0,deduped:!0}}let l=await ne(),u=0;for(let d of l.tracks)Ue(d)||!d.leadBox||(Ee(d,"node-update",{status:s,current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},...a?{policy:a}:{},...i?{error:String(i).slice(0,400)}:{},...c?{reason:c}:{},required:o===!0}),u++);if(await ae().catch(()=>{}),s==="available")try{await Y.mkdir(z.dirname(ay),{recursive:!0}),await Y.writeFile(ay,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function ky({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await ne(),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)||yq(e,c)||(Ee(a,"node-update",{status:"done",current:l.current??null,latest:c,...t?{installKind:t}:{},...n?{policy:n}:{},reason:"version-reconciled",installed:e,required:!1}),o++,s.add(c))}return o&&await ae().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function yq(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 q$(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=>bq.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 G$(e,t,n,{kind:r}={}){let o=await Se(t);if(!o)throw new Error(`box "${t}" is not running`);let s=Ps(o.agent),a=qc({text:n,producer:Wc({kind:r,orchestration:!0}),kind:r||"orchestration",provider:o.agent}),i=fe(await ne(),e)||null,c=Date.now(),l=null;try{l=await wr({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s})}catch(y){throw Kc(a,{ms:Date.now()-c,outcome:y?.code==="cli-not-running"?"cli-not-running":"blocked"}),await In(i,a,Ha(y)),y}Kc(a,{ms:Date.now()-c,outcome:l||"up"});let u=Un(o.agent,n);a.paste=!!u.paste;try{await Qe(t,n,{...u,kind:r||"orchestration"})}catch(y){throw await In(i,a,Ha(y)),y}let d={},p=await wc({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration",stats:d});a.verify=d,await In(i,a,p?d.verdict==="unverified"?"unverified":"delivered":"unsubmitted"),await ee({flow:"orchestration",phase:"sent",submitted:p,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let f=await ne(),w=fe(f,e);if(!w)return;let h=w.boxes.find(y=>y.name===t);Ee(w,"orchestration-prompt",{box:t,agent:h?.agent||o.agent,boxRole:h?.role,prompt:n,kind:r||"orchestration",deliveryId:a.id}),await ae()}async function kq(e){let t=z.join(sa(e),`${e.id}.ndjson`),n;try{n=await Y.stat(t)}catch{return null}let r=C$.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 C$.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function xq(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:vc(n.name,n.agent)}async function Zt(e,t=null,n=null){let r=t??new Map((await kn()).map(S=>[S.name,S])),o=!!e.transferredTo,s=e.boxes.map(S=>{let _=o?null:r.get(S.name),M=jr.has(S.state)?S.state:_?S.role==="lead"?"live":S.state:"stopped";return{...S,state:M,cwd:_?.cwd||S.cwd,agent:_?.agent||S.agent,sessionId:_?.sessionId||S.sessionId}}),[a,{working:i,prompted:c},l,u,d]=await Promise.all([kq(e),xq(e,s),Gg(e),n??En().catch(()=>[]),$$({...e,boxes:s})]),p=hl.has(e.id),f=(u||[]).filter(S=>S.trackId===e.id&&!["merged","closed","lost"].includes(S.state)),w=M$(e.id),h=w&&{status:"waiting",since:w.startedAt,summary:w.summary??null,count:w.count??null}||k$(f),y=s.find(S=>S.name===e.leadBox)?.state==="live",k=y?Cs(e.id).filter(S=>S.kind!=="ship-notify"&&S.kind!=="prompt-answer"&&S.kind!=="login-greet"):[],g=d||(y&&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`}:k.length?{source:"queue",count:k.length,since:Math.min(...k.map(S=>S.ts||Date.now())),summary:"a queued message is about to send"}:null),x=v$({archivedAt:e.archivedAt,archiving:p,prompted:c,openActions:l.openActions,openActionSince:l.openActionSince,prDecisions:x$(f),operation:h,phase:l.phase,working:i,workingSince:l.currentTurnAt,pending:g,completedTurnAt:l.completedTurnAt||a,live:y});return{...e,boxes:s,lastResponseAt:a,working:i,prompted:c,archiving:p,status:x}}function K$(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-vq,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 V$(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=Dx(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=K$(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 Ax(p.box,p.sessionId),n=!0}}return n}async function ke(){let e=await ne();await V$(e)&&await ae();let t=new Map((await kn()).map(r=>[r.name,r])),n=await En().catch(()=>[]);return Promise.all(e.tracks.map(r=>Zt(r,t,n)))}async function Hc(e,t){let n=await ne();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 bl(e){let t=Ho(e);if(!t)return null;let n=await ne();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Xk(o.hookTokenHash,t))return{track:r,box:o};return null}async function xy(e){return fe(await ne(),e)??null}async function me(e){let t=await ne(),n=fe(t,e);return n?(await V$(t)&&await ae(),Zt(n)):null}async function vy(e){let t=await K(e).catch(()=>[]),n;try{n=Hn(t)}catch{return[]}let r=dd(n).map(s=>({kind:s.kind,id:s.offerId??s.proposalId??s.id,ts:s.ts??null})),o=[];try{o=eS(t)}catch{o=[]}return r.concat(o.map(s=>({kind:"pr-open",id:`${s.repo??""}#${s.number}`,ts:null})))}function py(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(Sq[e.agent]||[]).some(n=>n.test(t))}function fy(e){let t=(e.text||"").trim();return t?Tq.some(n=>n.test(t)):!1}function z$(e,t,n=Date.now()){return!e||e.readyAt||e.state!=="live"?!1:n-(t||0)<Aq}async function Jp(e,t={}){let n=await me(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=ii(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=mg(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...Q_(n),...l],p={windows:[...uT(n),...dT(n,Sd),...a,...u]},w=(await Promise.all(n.boxes.map(O=>Bh(O,t)))).flat(),h=w.map(O=>{let X={kind:"message",...O};if(X.role==="assistant"&&(X.text||"").trim()&&X.tools?.length){let re=ao(X.agent).cliTools,he=X.tools.filter(Be=>!re.has(Be));return{...X,tools:he.length?he:void 0}}return X}).map(O=>{if(O.role!=="user")return O;let X=Hv(O);return X?X.kind==="suppress"?null:{...O,note:X,text:qv(X)}:O}).filter(Boolean).filter(O=>!(O.role==="user"&&r.has((O.text||"").trim()))).filter(O=>!py(O)).filter(O=>!fy(O)).filter(O=>!dy(O)).filter(O=>!Bg(O)).filter(O=>O.role!=="assistant"||!!(O.text||"").trim()||!!O.images?.length).filter(O=>!pT(O,p)),y=h.filter(O=>O.failed&&O.text===Qo);if(y.length){let O=new Map;for(let X of y){if(!O.has(X.box)){let Be=[...(await Ne(X.box,{lines:ze}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Pt=>Pt[1]);O.set(X.box,Be.length?Be[Be.length-1]:null)}let re=O.get(X.box);re&&(X.text=`\u25A0 ${re}`)}}let k=await Promise.all(n.boxes.map(O=>WS(O,t)));await __(n,k.flat());let{agentTurnBoxes:g}=await A_(n,h);for(let O of n.boxes)g.has(O.name)&&O.cwd&&Iu(O.cwd);await dA(n),await sI(n,h).catch(()=>{});for(let O of n.boxes)O.agent==="codex"&&O.state==="live"&&await $_(n,O),O.agent==="cursor"&&O.state==="live"&&await N_(n,O);let x=await Promise.all(n.boxes.map(O=>HS(O,t)));await E_(n,x.flat().filter(O=>!iy(O.box,O.ts,u)));let S=await K(n),_=c&&i.viewId?i.viewId:null,M=TA(S),P=Hn(S).filter(O=>!(O.kind==="message"&&(py(O)||fy(O)||dy(O)||Bg(O)))&&!(O.kind==="view-card"&&_&&O.viewId===_)),j=AA(P,M),A=P.filter(O=>!(O.kind==="message"&&O.role==="assistant"&&!j.has(O)&&iy(O.box,O.ts,M))),$=bh(S,A).map(O=>({kind:"command-batch",id:O.id,ts:O.pending?O.ts:(O.anchorTs??O.ts)-1,anchorTs:O.anchorTs,anchorBox:O.anchorBox,agent:O.agent,boxRole:O.boxRole,commands:O.commands,pending:O.pending})).filter(O=>!iy(O.anchorBox,O.anchorTs??O.ts,u)),L=Zv(Qv([...A,...$].filter(O=>O.ts!=null).sort((O,X)=>O.ts-X.ts))),H=Eq(n),R=H.size?L.filter(O=>O.kind==="event"||!H.has(O.box??O.anchorBox)):L,T=n.boxes.find(O=>O.name===n.leadBox)||null,C=T?await nc(T.cwd,T.sessionId,{fallbackNewest:!1}):null;if(!C&&T){let O=[...S].reverse().find(X=>X.type==="context.usage"&&X.boxId===T.name);O?.payload?.window&&(C={pct:O.payload.pct??(O.payload.occupancy!=null?O.payload.occupancy/O.payload.window:null),occupancy:O.payload.occupancy??null,window:O.payload.window,windowKnown:!0,model:O.payload.model||null})}let W=[...w].reverse().find(O=>O.role==="assistant"&&O.model),B=W?.model||null,Z=[...n.events||[]].reverse().find(O=>O.type==="model-change"),xe=C?.model||B;Z&&(!W?.ts||Z.at>=W.ts)&&(xe=Z.to);let Te=T?await Ne(T.name,{lines:ze}).catch(()=>""):"",_e=T&&oo(T.agent,Te)?await Jt(T.name).catch(()=>Te):Te,Q=T?Sn(T,_e):null;if(T&&Ty(T.name,Q,_e)&&Q&&(Q={...Q,answering:!0}),Q&&T){let O=md(T,Q,_e);if(O){let X=vS(T.name,Q.promptId);X.action!=="give-up"&&(X.action==="send"&&(O.accept&&ed(T.name,_e),(!O.gate||eo(T.name,O.gate))&&Ke(T.name,O.keys,{kind:O.accept?"auto-accept":"auto-reject"}).catch(()=>{}),X.first&&N(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{ts:Date.now(),promptId:Q.promptId,rule:O.id}}).catch(()=>{})),Q=null)}}T&&(Q?.setup&&pv(T.name,_e),!Q&&cc(T.agent,Te)&&Zu(T.name)&&eo(T.name,"login-issue")&&(Qu(T.name),Qe(T.name,"/login",{enter:!0,submitDelayMs:600,kind:"login-issue"}).catch(()=>{}))),!Q&&T&&!ba(T.agent,Te)&&!Cs(n.id).length&&(Q=wh(R,{leadBoxId:T.name,provider:T.agent,isAnswered:O=>S.some(X=>X.type==="prompt.answered"&&X.payload?.promptId===O)})),Q&&pA(n,T,Q);let Xe=R.filter(O=>!(O.kind==="message"&&O.role==="assistant"&&O.ask&&!String(O.text||"").trim()&&!O.images?.length)),le=!!T&&hd(T.name,T.agent,Te)&&!Q;if(T&&!T.readyAt&&(le||Q||Te&&to(T.agent,Te))){T.readyAt=Date.now();let O=fe(await ne(),n.id)?.boxes?.find(X=>X.name===T.name);O&&!O.readyAt&&(O.readyAt=T.readyAt,await ae())}let ge=z$(T,n.createdAt),de=!!T&&!!Te&&to(T.agent,Te)&&!Q&&!cc(T.agent,Te)&&!ya(T.agent,Te),te=!1;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&T){if(de){let O=await ne(),X=fe(O,n.id);X?.setup&&!X.setup.connectedAt&&(X.setup.connectedAt=Date.now(),n.setup.connectedAt=X.setup.connectedAt,te=!0,await ae())}}else T&&de&&dv(T.name)&&(te=!0);if(te&&fv(T.name),T){let O=mv(T.name),X=uv(T.name);O.started&&N(n,{type:"provider.login.started",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{provider:T.agent,reason:X?.startedReason||"auth-screen",ts:Date.now()}}).catch(()=>{}),O.connected&&(await N(n,{type:"provider.connected",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{provider:T.agent,ts:X?.connectedAt||Date.now(),...X?.account?{account:X.account}:{},reason:X?.startedReason||"already-signed-in"}}).catch(()=>{}),n.kind!=="setup"&&X?.startedReason&&!Pq(n.id)&&qt(n.id,Y$,{kind:"login-greet"}).catch(re=>{console.warn(`[track ${n.id}] login greeting not delivered: ${re?.message||re}`)}))}let we=s?{running:!0,step:o.step,toAgent:o.toAgent||null,wait:o.wait||null}:null,Oe=xl(n.id),Ve=Oe&&!Oe.finishedAt?{running:!0,step:Oe.step||null,added:Oe.added||[],removed:Oe.removed||[]}:null,Nt=null;if(c){let O=Date.now()-i.startedAt;if(i.viewId&&!le){let X=vp(n.id);X&&(Ee(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:X.finishedAt}),await ae())}else if(Q||O>X_&&!le&&!i.viewId||O>fg){let X=Q?"needs-input":O>fg?"timeout":"no-view";vp(n.id,{error:X})&&await N(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:T?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:X}}).catch(()=>{})}else Nt={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:Xe,context:C?{pct:C.pct,occupancy:C.occupancy,window:C.window,windowKnown:C.windowKnown,model:C.model}:null,models:{current:xe,options:dw(T?.agent)},pendingPrompt:Q,working:le,stopping:Z$(n.id),leadStarting:ge,handoff:we,reshape:Ve,view:Nt,queued:Cs(n.id).filter(O=>O.kind!=="ship-notify"&&O.kind!=="prompt-answer"&&O.kind!=="login-greet"),draft:n.draft||null,draftRev:n.draftRev||0}}function Eq(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 Sy(e,t){let n=await me(e);if(!n)return null;let r=Ls(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=Hn(s).filter(d=>d.box===o&&!(d.kind==="message"&&d.role==="assistant"&&d.ask&&!String(d.text||"").trim()&&!d.images?.length)),i=bh(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 Ne(o,{lines:ze}).catch(()=>"");u=hd(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}function _q(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>N$)throw new Error(`custom answer is too long (max ${N$} characters)`);return r})}function Iq(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))}function Ty(e,t,n,r=Date.now()){let o=my.get(e);if(!o)return!1;let s=!!t&&t.promptId===o.promptId&&r-o.at<$q&&(t.setup?!0:J$(n)===o.paneHash);return s||my.delete(e),s}async function Oq(e,t,n){let r=n;for(let o of Rq){await rr(o);let s=await Jt(e).catch(()=>null);if(s!=null&&(r=s,s!==n))break}my.set(e,{promptId:t.promptId,paneHash:J$(r),setup:t.setup||null,at:Date.now()})}async function Yp(e,t={}){let n=il.get(e);n&&await n.catch(()=>{});let r=Mq(e,t);il.set(e,r);try{return await r}finally{il.get(e)===r&&il.delete(e)}}async function Mq(e,{promptId:t,answers:n,text:r,custom:o}={}){let s=fe(await ne(),e);if(!s)throw new Error(`track "${e}" not found`);oi(e,"prompt-answer");let a=s.boxes.find(g=>g.name===s.leadBox);if(!a)throw new Error(`track "${e}" has no lead box`);if(!await Se(s.leadBox))throw new Error(`lead box "${s.leadBox}" is not running`);let c=await Jt(a.name).catch(()=>""),l=Sn(a,c);if(l||(l=await Cq(s,a)),!l)throw new Error("no pending prompt to answer");if(t&&t!==l.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");if(l.answerMode!=="say"&&Ty(a.name,l,c))throw new Error("prompt already answered \u2014 waiting for the CLI to move on");let u=_q(o,l.questions?.length??1);if(u.some(Boolean)&&!l.custom)throw new Error("custom answer isn't available for this prompt \u2014 pick one of its options");let d=(n||[]).some(g=>Array.isArray(g)&&g.length>0);if(l.custom&&!d&&!u.some(Boolean)&&l.answerMode!=="text")throw new Error("no answer selected \u2014 pick an option or type your own");u.some(Boolean)&&(n=(l.questions||[]).map((g,x)=>u[x]&&!g.multiSelect?[]:Array.isArray(n?.[x])?n[x]:[]));let p;if(l.answerMode==="text"){let g=typeof r=="string"?r.trim():"";if(!g)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(g)||g.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await Qe(s.leadBox,g,{enter:!0,kind:"answer-prompt"}),p={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(l.answerMode==="esc")await Ke(s.leadBox,["Escape"],{kind:"answer-prompt"}),p={mode:"keys",value:"Escape",count:1};else if(l.answerMode==="say"){let g=zv(l,n||[],typeof r=="string"?r:"",u);if(!g)throw new Error("no answer selected \u2014 pick an option or reply in chat");await qt(s.id,g,{kind:"prompt-answer"}),p={mode:"say",value:g,count:1}}else if(u[0]&&l.provider==="claude"){let g=yS(l,n||[],u[0]);await Wm(s.leadBox,[...g.pre,{text:g.text}],{kind:"answer-prompt"});let x=!1;for(let _ of[150,300,600]){await rr(_);let M=await Jt(s.leadBox).catch(()=>""),P=Sn(a,M);if(P?.customText&&Iq(P.customText,g.text)){x=!0;break}}if(!x)throw new Error("custom answer didn't take \u2014 the widget moved; reload and answer again");await Wm(s.leadBox,g.post,{kind:"answer-prompt"});let S=_=>_.map(M=>M.key).join(" ");p={mode:"keys+text",value:`${S(g.pre)} \u2039text\u203A ${S(g.post)}`.trim(),count:g.pre.length+1+g.post.length}}else if(l.provider==="claude"||l.answerMode==="keys"){let g=Eh(l,n||[]);await Ke(s.leadBox,g,{kind:"answer-prompt"}),p={mode:"keys",value:g.join(" "),count:g.length}}else{let g=sv(l,n||[]);await Qe(s.leadBox,g,{enter:!0,kind:"answer-prompt"}),p={mode:"text",value:g,count:1}}if(l.answerMode!=="say"&&await Oq(a.name,l,c),l.answerMode==="say"){let g=l.questions||[];for(let x=0;x<g.length;x++){let S=g[x],_=new Set(Array.isArray(n?.[x])?n[x]:[]),M=[..._].map(j=>S.options?.[j]).filter(Boolean).map(j=>({label:j.label,description:j.description||""}));u[x]&&M.push({label:u[x],description:"",custom:!0});let P=(S.options||[]).map((j,A)=>_.has(A)?null:j?.label).filter(Boolean);pw(s,a,l,{...p,groupId:l.groupId||l.promptId,questionIndex:x,questionTotal:l.questionTotal??g.length,header:S.header||"",question:S.question||"",multiSelect:!!S.multiSelect,chosen:M,rejected:P})}return await ee({flow:"prompt",phase:"answered",sessionName:s.leadBox,agent:a.agent,provider:l.provider,promptId:l.promptId,...p}).catch(()=>{}),{ok:!0,promptId:l.promptId,sent:p.count}}let f=l.questions?.[0]||{},w=new Set(Array.isArray(n?.[0])?n[0]:[]),h=[...w].map(g=>f.options?.[g]).filter(Boolean).map(g=>({label:g.label,description:g.description||""}));u[0]&&h.push({label:u[0],description:"",custom:!0});let y=(f.options||[]).map((g,x)=>w.has(x)?null:g?.label).filter(Boolean),k=l.review?"review":/^claude:model-confirm:/.test(l.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(l.promptId||"")?"resume-confirm":l.setup==="continue"?"login-continue":void 0;return p={...p,groupId:l.groupId||l.promptId,questionIndex:l.questionIndex??0,questionTotal:l.questionTotal??1,header:f.header||"",question:f.question||"",multiSelect:!!f.multiSelect,chosen:h,rejected:y,...k?{control:k}:{}},await ee({flow:"prompt",phase:"answered",sessionName:s.leadBox,agent:a.agent,provider:l.provider,promptId:l.promptId,...p}).catch(()=>{}),pw(s,a,l,p),{ok:!0,promptId:l.promptId,sent:p.count}}async function Cq(e,t){if(!t)return null;let n=await K(e).catch(()=>[]);return wh(Hn(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function Ay(e,{promptId:t,questionIndex:n}={}){let r=fe(await ne(),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 Se(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await wS(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=bS(a,n);return c.length?(await Ke(r.leadBox,c,{kind:"navigate-prompt"}),await ee({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 Xp(e){let t=Ms.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Ms.set(e,t)),t}function Pq(e){let t=Ms.get(e);return!!t&&(t.items.length>0||!!t.flushing)}function Cs(e){return(Ms.get(e)?.items??[]).map(({delivery:t,...n})=>({...n}))}function Ey(e=Date.now()){let t=0,n=0;for(let r of Ms.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 Dq(e,t,n){let r=t.filter(o=>o.delivery&&!o.delivery.settled);r.length&&(async()=>{let o=fe(await ne(),e)||null;for(let s of r)await In(o,s.delivery,`dropped:${n}`)})().catch(()=>{})}function kl(e,t="cancel"){let n=Ms.get(e);if(!n)return[];let r=n.items.splice(0);return n.idleSince=null,n.timer&&(clearInterval(n.timer),n.timer=null),Dq(e,r,t),r}async function _y(e){let t=kl(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=fe(await ne(),e);if(o)return r=ti(o,n),await ae(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function Iy(e){let t=Xp(e);t.timer||(t.timer=setInterval(()=>Bq(e).catch(()=>{}),Nq))}async function Bq(e){let t=Ms.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=fe(await ne(),e);if(!n||n.archivedAt){kl(e,"archived");return}let r=async(l,u="")=>{let d=[];for(let y of t.items)H_(y.delivery,l),u$(y,l)&&d.push(y);if(!d.length)return;t.items=t.items.filter(y=>!d.includes(y));let p=Date.now()-Math.min(...d.map(y=>y.heldSince||Date.now())),f=Math.max(1,Math.round(jg()/6e4)),w=String(u||"").split(`
|
|
227
|
+
(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 dy(e){return e.role==="user"&&hq.test((e.text||"").trim())}async function ne(){return Ht||(oy||=(async()=>{try{Ht=JSON.parse(await Y.readFile(W$,"utf8"))}catch{Ht={tracks:[]}}finally{oy=null}return Ht})(),oy)}async function ae(){await Bt(W$,Ht)}function yq(e,t){let n=JSON.stringify({trackId:e,...t})+`
|
|
228
|
+
`;return sy=sy.then(()=>Y.mkdir(nr,{recursive:!0})).then(()=>Y.appendFile(fq,n)).catch(()=>{}),sy}async function G$(e,{box:t,agent:n,message:r}){let o=pe(await ne(),e);!o||o.archivedAt||(Ee(o,"startup-gate",{box:t,agent:n??null,message:r}),await ae(),Tc(o,{provider:n??null,cls:"startup-gate",note:r}).catch(()=>{}))}async function or({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 Y.readFile(ay,"utf8"))}catch{}if(d?.latest===t&&d?.current===e)return{announced:0,deduped:!0}}let l=await ne(),u=0;for(let d of l.tracks)Ue(d)||!d.leadBox||(Ee(d,"node-update",{status:s,current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},...a?{policy:a}:{},...i?{error:String(i).slice(0,400)}:{},...c?{reason:c}:{},required:o===!0}),u++);if(await ae().catch(()=>{}),s==="available")try{await Y.mkdir(z.dirname(ay),{recursive:!0}),await Y.writeFile(ay,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function ky({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await ne(),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)||bq(e,c)||(Ee(a,"node-update",{status:"done",current:l.current??null,latest:c,...t?{installKind:t}:{},...n?{policy:n}:{},reason:"version-reconciled",installed:e,required:!1}),o++,s.add(c))}return o&&await ae().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function bq(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 K$(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=>kq.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 V$(e,t,n,{kind:r}={}){let o=await Se(t);if(!o)throw new Error(`box "${t}" is not running`);let s=Ps(o.agent),a=qc({text:n,producer:Wc({kind:r,orchestration:!0}),kind:r||"orchestration",provider:o.agent}),i=pe(await ne(),e)||null,c=Date.now(),l=null;try{l=await wr({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s})}catch(y){throw Kc(a,{ms:Date.now()-c,outcome:y?.code==="cli-not-running"?"cli-not-running":"blocked"}),await In(i,a,Ha(y)),y}Kc(a,{ms:Date.now()-c,outcome:l||"up"});let u=Un(o.agent,n);a.paste=!!u.paste;try{await Qe(t,n,{...u,kind:r||"orchestration"})}catch(y){throw await In(i,a,Ha(y)),y}let d={},p=await wc({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration",stats:d});a.verify=d,await In(i,a,p?d.verdict==="unverified"?"unverified":"delivered":"unsubmitted"),await ee({flow:"orchestration",phase:"sent",submitted:p,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let f=await ne(),w=pe(f,e);if(!w)return;let h=w.boxes.find(y=>y.name===t);Ee(w,"orchestration-prompt",{box:t,agent:h?.agent||o.agent,boxRole:h?.role,prompt:n,kind:r||"orchestration",deliveryId:a.id}),await ae()}async function xq(e){let t=z.join(sa(e),`${e.id}.ndjson`),n;try{n=await Y.stat(t)}catch{return null}let r=P$.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 P$.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function vq(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:vc(n.name,n.agent)}async function Zt(e,t=null,n=null){let r=t??new Map((await kn()).map(S=>[S.name,S])),o=!!e.transferredTo,s=e.boxes.map(S=>{let _=o?null:r.get(S.name),M=jr.has(S.state)?S.state:_?S.role==="lead"?"live":S.state:"stopped";return{...S,state:M,cwd:_?.cwd||S.cwd,agent:_?.agent||S.agent,sessionId:_?.sessionId||S.sessionId}}),[a,{working:i,prompted:c},l,u,d]=await Promise.all([xq(e),vq(e,s),Gg(e),n??En().catch(()=>[]),O$({...e,boxes:s})]),p=hl.has(e.id),f=(u||[]).filter(S=>S.trackId===e.id&&!["merged","closed","lost"].includes(S.state)),w=N$(e.id),h=w&&{status:"waiting",since:w.startedAt,summary:w.summary??null,count:w.count??null}||v$(f),y=s.find(S=>S.name===e.leadBox)?.state==="live",k=y?Cs(e.id).filter(S=>S.kind!=="ship-notify"&&S.kind!=="prompt-answer"&&S.kind!=="login-greet"):[],g=d||(y&&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`}:k.length?{source:"queue",count:k.length,since:Math.min(...k.map(S=>S.ts||Date.now())),summary:"a queued message is about to send"}:null),x=T$({archivedAt:e.archivedAt,archiving:p,prompted:c,openActions:l.openActions,openActionSince:l.openActionSince,prDecisions:S$(f),operation:h,phase:l.phase,working:i,workingSince:l.currentTurnAt,pending:g,completedTurnAt:l.completedTurnAt||a,live:y});return{...e,boxes:s,lastResponseAt:a,working:i,prompted:c,archiving:p,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-Sq,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 J$(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=Lx(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 _x(p.box,p.sessionId),n=!0}}return n}async function ke(){let e=await ne();await J$(e)&&await ae();let t=new Map((await kn()).map(r=>[r.name,r])),n=await En().catch(()=>[]);return Promise.all(e.tracks.map(r=>Zt(r,t,n)))}async function Hc(e,t){let n=await ne();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 bl(e){let t=Ho(e);if(!t)return null;let n=await ne();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Qk(o.hookTokenHash,t))return{track:r,box:o};return null}async function xy(e){return pe(await ne(),e)??null}async function me(e){let t=await ne(),n=pe(t,e);return n?(await J$(t)&&await ae(),Zt(n)):null}async function vy(e){let t=await K(e).catch(()=>[]),n;try{n=Hn(t)}catch{return[]}let r=pd(n).map(s=>({kind:s.kind,id:s.offerId??s.proposalId??s.id,ts:s.ts??null})),o=[];try{o=nS(t)}catch{o=[]}return r.concat(o.map(s=>({kind:"pr-open",id:`${s.repo??""}#${s.number}`,ts:null})))}function py(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(Tq[e.agent]||[]).some(n=>n.test(t))}function fy(e){let t=(e.text||"").trim();return t?Aq.some(n=>n.test(t)):!1}function Y$(e,t,n=Date.now()){return!e||e.readyAt||e.state!=="live"?!1:n-(t||0)<Eq}async function Yp(e,t={}){let n=await me(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=ii(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=mg(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...tI(n),...l],p={windows:[...pT(n),...fT(n,Td),...a,...u]},w=(await Promise.all(n.boxes.map(O=>Bh(O,t)))).flat(),h=w.map(O=>{let X={kind:"message",...O};if(X.role==="assistant"&&(X.text||"").trim()&&X.tools?.length){let re=ao(X.agent).cliTools,he=X.tools.filter(Be=>!re.has(Be));return{...X,tools:he.length?he:void 0}}return X}).map(O=>{if(O.role!=="user")return O;let X=qv(O);return X?X.kind==="suppress"?null:{...O,note:X,text:Kv(X)}:O}).filter(Boolean).filter(O=>!(O.role==="user"&&r.has((O.text||"").trim()))).filter(O=>!py(O)).filter(O=>!fy(O)).filter(O=>!dy(O)).filter(O=>!Bg(O)).filter(O=>O.role!=="assistant"||!!(O.text||"").trim()||!!O.images?.length).filter(O=>!mT(O,p)),y=h.filter(O=>O.failed&&O.text===Qo);if(y.length){let O=new Map;for(let X of y){if(!O.has(X.box)){let Be=[...(await Ne(X.box,{lines:ze}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Pt=>Pt[1]);O.set(X.box,Be.length?Be[Be.length-1]:null)}let re=O.get(X.box);re&&(X.text=`\u25A0 ${re}`)}}let k=await Promise.all(n.boxes.map(O=>GS(O,t)));await $_(n,k.flat());let{agentTurnBoxes:g}=await __(n,h);for(let O of n.boxes)g.has(O.name)&&O.cwd&&$u(O.cwd);await fA(n),await iI(n,h).catch(()=>{});for(let O of n.boxes)O.agent==="codex"&&O.state==="live"&&await O_(n,O),O.agent==="cursor"&&O.state==="live"&&await D_(n,O);let x=await Promise.all(n.boxes.map(O=>qS(O,t)));await I_(n,x.flat().filter(O=>!iy(O.box,O.ts,u)));let S=await K(n),_=c&&i.viewId?i.viewId:null,M=EA(S),P=Hn(S).filter(O=>!(O.kind==="message"&&(py(O)||fy(O)||dy(O)||Bg(O)))&&!(O.kind==="view-card"&&_&&O.viewId===_)),j=_A(P,M),A=P.filter(O=>!(O.kind==="message"&&O.role==="assistant"&&!j.has(O)&&iy(O.box,O.ts,M))),$=bh(S,A).map(O=>({kind:"command-batch",id:O.id,ts:O.pending?O.ts:(O.anchorTs??O.ts)-1,anchorTs:O.anchorTs,anchorBox:O.anchorBox,agent:O.agent,boxRole:O.boxRole,commands:O.commands,pending:O.pending})).filter(O=>!iy(O.anchorBox,O.anchorTs??O.ts,u)),L=eS(tS([...A,...$].filter(O=>O.ts!=null).sort((O,X)=>O.ts-X.ts))),H=_q(n),R=H.size?L.filter(O=>O.kind==="event"||!H.has(O.box??O.anchorBox)):L,T=n.boxes.find(O=>O.name===n.leadBox)||null,C=T?await nc(T.cwd,T.sessionId,{fallbackNewest:!1}):null;if(!C&&T){let O=[...S].reverse().find(X=>X.type==="context.usage"&&X.boxId===T.name);O?.payload?.window&&(C={pct:O.payload.pct??(O.payload.occupancy!=null?O.payload.occupancy/O.payload.window:null),occupancy:O.payload.occupancy??null,window:O.payload.window,windowKnown:!0,model:O.payload.model||null})}let W=[...w].reverse().find(O=>O.role==="assistant"&&O.model),B=W?.model||null,Z=[...n.events||[]].reverse().find(O=>O.type==="model-change"),xe=C?.model||B;Z&&(!W?.ts||Z.at>=W.ts)&&(xe=Z.to);let Te=T?await Ne(T.name,{lines:ze}).catch(()=>""):"",_e=T&&oo(T.agent,Te)?await Jt(T.name).catch(()=>Te):Te,Q=T?Sn(T,_e):null;if(T&&Ty(T.name,Q,_e)&&Q&&(Q={...Q,answering:!0}),Q&&T){let O=hd(T,Q,_e);if(O){let X=TS(T.name,Q.promptId);X.action!=="give-up"&&(X.action==="send"&&(O.accept&&td(T.name,_e),(!O.gate||eo(T.name,O.gate))&&Ke(T.name,O.keys,{kind:O.accept?"auto-accept":"auto-reject"}).catch(()=>{}),X.first&&N(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{ts:Date.now(),promptId:Q.promptId,rule:O.id}}).catch(()=>{})),Q=null)}}T&&(Q?.setup&&mv(T.name,_e),!Q&&cc(T.agent,Te)&&Qu(T.name)&&eo(T.name,"login-issue")&&(ed(T.name),Qe(T.name,"/login",{enter:!0,submitDelayMs:600,kind:"login-issue"}).catch(()=>{}))),!Q&&T&&!ba(T.agent,Te)&&!Cs(n.id).length&&(Q=wh(R,{leadBoxId:T.name,provider:T.agent,isAnswered:O=>S.some(X=>X.type==="prompt.answered"&&X.payload?.promptId===O)})),Q&&mA(n,T,Q);let Xe=R.filter(O=>!(O.kind==="message"&&O.role==="assistant"&&O.ask&&!String(O.text||"").trim()&&!O.images?.length)),le=!!T&&wd(T.name,T.agent,Te)&&!Q;if(T&&!T.readyAt&&(le||Q||Te&&to(T.agent,Te))){T.readyAt=Date.now();let O=pe(await ne(),n.id)?.boxes?.find(X=>X.name===T.name);O&&!O.readyAt&&(O.readyAt=T.readyAt,await ae())}let ge=Y$(T,n.createdAt),de=!!T&&!!Te&&to(T.agent,Te)&&!Q&&!cc(T.agent,Te)&&!ya(T.agent,Te),te=!1;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&T){if(de){let O=await ne(),X=pe(O,n.id);X?.setup&&!X.setup.connectedAt&&(X.setup.connectedAt=Date.now(),n.setup.connectedAt=X.setup.connectedAt,te=!0,await ae())}}else T&&de&&fv(T.name)&&(te=!0);if(te&&hv(T.name),T){let O=wv(T.name),X=pv(T.name);O.started&&N(n,{type:"provider.login.started",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{provider:T.agent,reason:X?.startedReason||"auth-screen",ts:Date.now()}}).catch(()=>{}),O.connected&&(await N(n,{type:"provider.connected",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{provider:T.agent,ts:X?.connectedAt||Date.now(),...X?.account?{account:X.account}:{},reason:X?.startedReason||"already-signed-in"}}).catch(()=>{}),n.kind!=="setup"&&X?.startedReason&&!Dq(n.id)&&qt(n.id,Z$,{kind:"login-greet"}).catch(re=>{console.warn(`[track ${n.id}] login greeting not delivered: ${re?.message||re}`)}))}let we=s?{running:!0,step:o.step,toAgent:o.toAgent||null,wait:o.wait||null}:null,Oe=xl(n.id),Ve=Oe&&!Oe.finishedAt?{running:!0,step:Oe.step||null,added:Oe.added||[],removed:Oe.removed||[]}:null,Nt=null;if(c){let O=Date.now()-i.startedAt;if(i.viewId&&!le){let X=Sp(n.id);X&&(Ee(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:X.finishedAt}),await ae())}else if(Q||O>Q_&&!le&&!i.viewId||O>fg){let X=Q?"needs-input":O>fg?"timeout":"no-view";Sp(n.id,{error:X})&&await N(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:T?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:X}}).catch(()=>{})}else Nt={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:Xe,context:C?{pct:C.pct,occupancy:C.occupancy,window:C.window,windowKnown:C.windowKnown,model:C.model}:null,models:{current:xe,options:dw(T?.agent)},pendingPrompt:Q,working:le,stopping:Q$(n.id),leadStarting:ge,handoff:we,reshape:Ve,view:Nt,queued:Cs(n.id).filter(O=>O.kind!=="ship-notify"&&O.kind!=="prompt-answer"&&O.kind!=="login-greet"),draft:n.draft||null,draftRev:n.draftRev||0}}function _q(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 Sy(e,t){let n=await me(e);if(!n)return null;let r=Ls(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=Hn(s).filter(d=>d.box===o&&!(d.kind==="message"&&d.role==="assistant"&&d.ask&&!String(d.text||"").trim()&&!d.images?.length)),i=bh(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 Ne(o,{lines:ze}).catch(()=>"");u=wd(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}function Iq(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>D$)throw new Error(`custom answer is too long (max ${D$} characters)`);return r})}function $q(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))}function Ty(e,t,n,r=Date.now()){let o=my.get(e);if(!o)return!1;let s=!!t&&t.promptId===o.promptId&&r-o.at<Rq&&(t.setup?!0:X$(n)===o.paneHash);return s||my.delete(e),s}async function Mq(e,t,n){let r=n;for(let o of Oq){await rr(o);let s=await Jt(e).catch(()=>null);if(s!=null&&(r=s,s!==n))break}my.set(e,{promptId:t.promptId,paneHash:X$(r),setup:t.setup||null,at:Date.now()})}async function Xp(e,t={}){let n=il.get(e);n&&await n.catch(()=>{});let r=Cq(e,t);il.set(e,r);try{return await r}finally{il.get(e)===r&&il.delete(e)}}async function Cq(e,{promptId:t,answers:n,text:r,custom:o}={}){let s=pe(await ne(),e);if(!s)throw new Error(`track "${e}" not found`);oi(e,"prompt-answer");let a=s.boxes.find(g=>g.name===s.leadBox);if(!a)throw new Error(`track "${e}" has no lead box`);if(!await Se(s.leadBox))throw new Error(`lead box "${s.leadBox}" is not running`);let c=await Jt(a.name).catch(()=>""),l=Sn(a,c);if(l||(l=await Nq(s,a)),!l)throw new Error("no pending prompt to answer");if(t&&t!==l.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");if(l.answerMode!=="say"&&Ty(a.name,l,c))throw new Error("prompt already answered \u2014 waiting for the CLI to move on");let u=Iq(o,l.questions?.length??1);if(u.some(Boolean)&&!l.custom)throw new Error("custom answer isn't available for this prompt \u2014 pick one of its options");let d=(n||[]).some(g=>Array.isArray(g)&&g.length>0);if(l.custom&&!d&&!u.some(Boolean)&&l.answerMode!=="text")throw new Error("no answer selected \u2014 pick an option or type your own");u.some(Boolean)&&(n=(l.questions||[]).map((g,x)=>u[x]&&!g.multiSelect?[]:Array.isArray(n?.[x])?n[x]:[]));let p;if(l.answerMode==="text"){let g=typeof r=="string"?r.trim():"";if(!g)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(g)||g.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await Qe(s.leadBox,g,{enter:!0,kind:"answer-prompt"}),p={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(l.answerMode==="esc")await Ke(s.leadBox,["Escape"],{kind:"answer-prompt"}),p={mode:"keys",value:"Escape",count:1};else if(l.answerMode==="say"){let g=Yv(l,n||[],typeof r=="string"?r:"",u);if(!g)throw new Error("no answer selected \u2014 pick an option or reply in chat");await qt(s.id,g,{kind:"prompt-answer"}),p={mode:"say",value:g,count:1}}else if(u[0]&&l.provider==="claude"){let g=kS(l,n||[],u[0]);await Wm(s.leadBox,[...g.pre,{text:g.text}],{kind:"answer-prompt"});let x=!1;for(let _ of[150,300,600]){await rr(_);let M=await Jt(s.leadBox).catch(()=>""),P=Sn(a,M);if(P?.customText&&$q(P.customText,g.text)){x=!0;break}}if(!x)throw new Error("custom answer didn't take \u2014 the widget moved; reload and answer again");await Wm(s.leadBox,g.post,{kind:"answer-prompt"});let S=_=>_.map(M=>M.key).join(" ");p={mode:"keys+text",value:`${S(g.pre)} \u2039text\u203A ${S(g.post)}`.trim(),count:g.pre.length+1+g.post.length}}else if(l.provider==="claude"||l.answerMode==="keys"){let g=Eh(l,n||[]);await Ke(s.leadBox,g,{kind:"answer-prompt"}),p={mode:"keys",value:g.join(" "),count:g.length}}else{let g=iv(l,n||[]);await Qe(s.leadBox,g,{enter:!0,kind:"answer-prompt"}),p={mode:"text",value:g,count:1}}if(l.answerMode!=="say"&&await Mq(a.name,l,c),l.answerMode==="say"){let g=l.questions||[];for(let x=0;x<g.length;x++){let S=g[x],_=new Set(Array.isArray(n?.[x])?n[x]:[]),M=[..._].map(j=>S.options?.[j]).filter(Boolean).map(j=>({label:j.label,description:j.description||""}));u[x]&&M.push({label:u[x],description:"",custom:!0});let P=(S.options||[]).map((j,A)=>_.has(A)?null:j?.label).filter(Boolean);pw(s,a,l,{...p,groupId:l.groupId||l.promptId,questionIndex:x,questionTotal:l.questionTotal??g.length,header:S.header||"",question:S.question||"",multiSelect:!!S.multiSelect,chosen:M,rejected:P})}return await ee({flow:"prompt",phase:"answered",sessionName:s.leadBox,agent:a.agent,provider:l.provider,promptId:l.promptId,...p}).catch(()=>{}),{ok:!0,promptId:l.promptId,sent:p.count}}let f=l.questions?.[0]||{},w=new Set(Array.isArray(n?.[0])?n[0]:[]),h=[...w].map(g=>f.options?.[g]).filter(Boolean).map(g=>({label:g.label,description:g.description||""}));u[0]&&h.push({label:u[0],description:"",custom:!0});let y=(f.options||[]).map((g,x)=>w.has(x)?null:g?.label).filter(Boolean),k=l.review?"review":/^claude:model-confirm:/.test(l.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(l.promptId||"")?"resume-confirm":l.setup==="continue"?"login-continue":void 0;return p={...p,groupId:l.groupId||l.promptId,questionIndex:l.questionIndex??0,questionTotal:l.questionTotal??1,header:f.header||"",question:f.question||"",multiSelect:!!f.multiSelect,chosen:h,rejected:y,...k?{control:k}:{}},await ee({flow:"prompt",phase:"answered",sessionName:s.leadBox,agent:a.agent,provider:l.provider,promptId:l.promptId,...p}).catch(()=>{}),pw(s,a,l,p),{ok:!0,promptId:l.promptId,sent:p.count}}async function Nq(e,t){if(!t)return null;let n=await K(e).catch(()=>[]);return wh(Hn(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function Ay(e,{promptId:t,questionIndex:n}={}){let r=pe(await ne(),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 Se(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await yS(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=xS(a,n);return c.length?(await Ke(r.leadBox,c,{kind:"navigate-prompt"}),await ee({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 Zp(e){let t=Ms.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Ms.set(e,t)),t}function Dq(e){let t=Ms.get(e);return!!t&&(t.items.length>0||!!t.flushing)}function Cs(e){return(Ms.get(e)?.items??[]).map(({delivery:t,...n})=>({...n}))}function Ey(e=Date.now()){let t=0,n=0;for(let r of Ms.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 Bq(e,t,n){let r=t.filter(o=>o.delivery&&!o.delivery.settled);r.length&&(async()=>{let o=pe(await ne(),e)||null;for(let s of r)await In(o,s.delivery,`dropped:${n}`)})().catch(()=>{})}function kl(e,t="cancel"){let n=Ms.get(e);if(!n)return[];let r=n.items.splice(0);return n.idleSince=null,n.timer&&(clearInterval(n.timer),n.timer=null),Bq(e,r,t),r}async function _y(e){let t=kl(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=pe(await ne(),e);if(o)return r=ti(o,n),await ae(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function Iy(e){let t=Zp(e);t.timer||(t.timer=setInterval(()=>Lq(e).catch(()=>{}),Pq))}async function Lq(e){let t=Ms.get(e);if(!(!t||t.flushing)){if(!t.items.length){t.timer&&(clearInterval(t.timer),t.timer=null);return}t.flushing=!0;try{let n=pe(await ne(),e);if(!n||n.archivedAt){kl(e,"archived");return}let r=async(l,u="")=>{let d=[];for(let y of t.items)q_(y.delivery,l),p$(y,l)&&d.push(y);if(!d.length)return;t.items=t.items.filter(y=>!d.includes(y));let p=Date.now()-Math.min(...d.map(y=>y.heldSince||Date.now())),f=Math.max(1,Math.round(jg()/6e4)),w=String(u||"").split(`
|
|
229
229
|
`).map(y=>y.trimEnd()).filter(y=>y.trim()).slice(-3).join(`
|
|
230
|
-
`),h=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 y of d)y.delivery&&await In(n,y.delivery,l==="noSession"?"failed:not-running":"failed:blocked");Ee(n,"delivery-held",{box:n.leadBox,agent:o?.agent??null,reason:l,heldMs:p,producer:"loop",deliveryId:d[0]?.delivery?.id??null,message:h,...w?{detail:w}:{}}),await ae(),Tc(n,{provider:o?.agent??null,cls:"delivery-held",note:h}).catch(()=>{})},o=n.boxes.find(l=>l.name===n.leadBox),s=ii(e);if(s&&!s.finishedAt&&s.step!=="done"&&!s.error){Os(t,!1),await r("handoff");return}let a=await Se(n.leadBox);if(a&&await
|
|
230
|
+
`),h=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 y of d)y.delivery&&await In(n,y.delivery,l==="noSession"?"failed:not-running":"failed:blocked");Ee(n,"delivery-held",{box:n.leadBox,agent:o?.agent??null,reason:l,heldMs:p,producer:"loop",deliveryId:d[0]?.delivery?.id??null,message:h,...w?{detail:w}:{}}),await ae(),Tc(n,{provider:o?.agent??null,cls:"delivery-held",note:h}).catch(()=>{})},o=n.boxes.find(l=>l.name===n.leadBox),s=ii(e);if(s&&!s.finishedAt&&s.step!=="done"&&!s.error){Os(t,!1),await r("handoff");return}let a=await Se(n.leadBox);if(a&&await qy(a)&&(a=await wl(n,o,"queue")?await Se(n.leadBox):null,await ae()),!a){Os(t,!1),await r("noSession");return}if(mo(o?.agent)){Os(t,!1),await r("cap");return}let i=await Ne(n.leadBox,{lines:ze}).catch(()=>"");if(uc(o?.agent,i)){Os(t,!1),await r("busy",i);return}if(oo(o?.agent,i)){let l=await Jt(n.leadBox).catch(()=>i);if(Sn(o,l)){Os(t,!1),await r("prompt");return}}if(!Os(t,!0)){await r("grace");return}let c=t.items.shift();t.idleSince=null,c.delivery&&(c.delivery.dequeuedAt=Date.now());try{await hy(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 In(n,c.delivery,Ha(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 wl(n,o,"queue-gate")}await ae()}finally{t.flushing=!1}}}async function $y(e,t,n,r){let o=mo(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(ti(e,[n]),await ae()),XT(e.id,o))try{await N(e,{type:"usage.cap.hit",actor:"system",provider:t?.agent??null,boxId:e.leadBox,payload:{...o,blockedKind:r??null}})}catch{}let s=new Error(`${t?.agent??"the lead provider"} is paused \u2014 its ${o.window} spend limit is reached ($${o.spentUsd.toFixed(2)} of $${o.limitUsd.toFixed(2)})`);throw s.usageCap=o,s}async function jq(e,t){let n=await Ne(e.leadBox,{lines:ze}).catch(()=>"");if(!oo(t?.agent,n))return!1;let r=await Jt(e.leadBox).catch(()=>n);return!!Sn(t,r)}async function hy(e,t,n,r,o=null,s=null,a=null){a||(a=qc({text:r,producer:Wc({kind:o}),kind:o,provider:t?.agent??null,attachments:Array.isArray(s)?s.length:0})),a.provider??=t?.agent??null,await $y(e,t,r,o);let i=pl.get(e.id)||0,c=r,l=!1,u=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(c=q$(r,e.pendingHandoffHydrate.artifact||Io),delete e.pendingHandoffHydrate,l=!0);let d=Array.isArray(s)?s.filter(h=>h&&h.rel):[];if(d.length){let h=d$(d.map(y=>y.rel));c=c.trim()?`${c.trim()}
|
|
231
231
|
|
|
232
|
-
${h}`:h}let p=Ee(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,deliveryId:a.id,...o?{kind:o}:{},...d.length?{attachments:d}:{}});o||ei(e,null);try{let h=Date.now(),y=null;try{y=await wr({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:Ps(n.agent),bailOnDialog:!0})}catch(g){throw Kc(a,{ms:Date.now()-h,outcome:g?.code==="cli-not-running"?"cli-not-running":"blocked"}),g}if(Kc(a,{ms:Date.now()-h,outcome:y||"up"}),(pl.get(e.id)||0)!==i){!o&&r.trim()&&(ti(e,[r]),await ae());let g=new Error("delivery stopped \u2014 the message was handed back as the draft");throw g.interrupted=!0,g}if(await
|
|
232
|
+
${h}`:h}let p=Ee(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,deliveryId:a.id,...o?{kind:o}:{},...d.length?{attachments:d}:{}});o||ei(e,null);try{let h=Date.now(),y=null;try{y=await wr({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:Ps(n.agent),bailOnDialog:!0})}catch(g){throw Kc(a,{ms:Date.now()-h,outcome:g?.code==="cli-not-running"?"cli-not-running":"blocked"}),g}if(Kc(a,{ms:Date.now()-h,outcome:y||"up"}),(pl.get(e.id)||0)!==i){!o&&r.trim()&&(ti(e,[r]),await ae());let g=new Error("delivery stopped \u2014 the message was handed back as the draft");throw g.interrupted=!0,g}if(await jq(e,t)){let g=Zp(e.id);a.requeued+=1,a.dequeuedAt=null;let x={id:a.id,text:r,ts:Date.now(),...o?{kind:o}:{},...d.length?{attachments:d}:{},delivery:a};g.items.unshift(x),Iy(e.id),await Gc(e,a,{position:1,reason:"prompt"});let S=new Error("lead is blocked on a prompt \u2014 message queued");throw S.requeued={id:x.id,position:g.items.length},S}let k=Un(t?.agent,c);a.paste=!!k.paste,await Qe(e.leadBox,c,{...k,kind:l?"lead-handoff-first-prompt":"say"})}catch(h){if(u&&!e.pendingHandoffHydrate&&(e.pendingHandoffHydrate=u),Ee(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:p.at,text:r,...h?.interrupted?{reason:"interrupted"}:{},...h?.requeued?{reason:"requeued"}:{}}),h?.requeued)return{queued:!0,...h.requeued};throw h?.code!=="cli-not-running"&&await In(e,a,Ha(h)),h}let f={},w=await n5(e,t,c,f);a.verify=f,w?await In(e,a,f.verdict==="unverified"?"unverified":"delivered"):(await eR(e.leadBox,t?.agent,c.length+8,c).catch(()=>{}),Ee(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:p.at,text:r,reason:"unsubmitted"}),o||ti(e,[r]),await In(e,a,"unsubmitted")),l&&await ee({flow:"lead-handoff",phase:"first-prompt",trackId:e.id,sessionName:e.leadBox,agent:t?.agent,userTextLen:r.length})}function Ry(e,{actions:t}={}){let n=Zp(e),r=IA(t),o=qc({text:r,producer:Wc({kind:"ship-notify"}),kind:"ship-notify"});n.items.push({id:o.id,text:r,ts:Date.now(),kind:"ship-notify",delivery:o}),Iy(e),(async()=>{let s=pe(await ne(),e);s&&(o.provider=s.boxes.find(a=>a.name===s.leadBox)?.agent??null,await Gc(s,o,{position:n.items.length,reason:"queue"}))})().catch(()=>{})}async function Fq(e,t,n,r){await Ke(e.leadBox,["Escape"],{kind:"dismiss-prompt"});let o="",s=!1;for(let c of[250,400,600,900]){if(await rr(c),o=await Ne(e.leadBox,{lines:ze}).catch(()=>""),!oo(t?.agent,o)){s=!0;break}let l=await Jt(e.leadBox).catch(()=>o);if(!Sn(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 ee({flow:"prompt",phase:"dismissed",sessionName:e.leadBox,agent:t?.agent,provider:n.provider,promptId:n.promptId,...i}).catch(()=>{}),s&&hA(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(p=>p&&typeof p.rel=="string"):[];if((typeof t!="string"||!Ia(t))&&!a.length)throw new Error("text (string) or an attachment required");let i=pe(await ne(),e);if(!i)throw new Error(`track "${e}" not found`);af(i);let c=i.boxes.find(p=>p.name===i.leadBox),l=Ia(t),u=!1;if(i.archivedAt){if(!Uq(n))throw new Error(`track "${e}" is archived \u2014 restore it before driving it`);await $y(i,c,l,n),await vl(e,{reason:"composer-send"}),i=pe(await ne(),e),c=i.boxes.find(p=>p.name===i.leadBox),u=!0}let d=qc({text:l,producer:Wc({kind:n,producer:o}),kind:n,provider:c?.agent??null,attachments:a.length});try{let p=await Hq(e,i,c,l,{kind:n,atts:a,d,dismissPrompt:s});return u?{...p,revived:!0}:p}catch(p){throw await In(i,d,Ha(p)),p}}async function Hq(e,t,n,r,{kind:o,atts:s,d:a,dismissPrompt:i=!1}){await $y(t,n,r,o),oi(e,"say");let c=await Se(t.leadBox);if(c&&await qy(c)?await wl(t,n,"say")?(await ae(),c=await Se(t.leadBox)):c=null:!c&&await ai(t)&&(await ae(),c=await Se(t.leadBox)),!c)throw new Error(`lead box "${t.leadBox}" is not running`);let l=il.get(e);l&&await l.catch(()=>{});let u=Zp(e),d=await Ne(t.leadBox,{lines:ze}).catch(()=>""),p=u.items.length>0?"queue":uc(n?.agent,d)?"busy":null,f=p!=null,w=i&&!o;if((!f||p==="queue"&&w)&&oo(n?.agent,d)){let y=await Jt(t.leadBox).catch(()=>d),k=Sn(n,y);if(f||(f=!!k,f&&(p="prompt")),k&&w&&k.dismissable){let g=await Fq(t,n,k,a);g.gone&&p==="prompt"&&(d=g.pane,f=uc(n?.agent,d),p=f?"busy":null)}}if(f){let y={id:a.id,text:r,ts:Date.now(),...o?{kind:o}:{},...s.length?{attachments:s}:{},delivery:a};return u.items.push(y),Iy(e),o||ei(t,null),await ae(),await Gc(t,a,{position:u.items.length,reason:p}),{ok:!0,leadBox:t.leadBox,queued:!0,id:y.id,position:u.items.length,deliveryId:a.id}}let h;try{h=await hy(t,n,c,r,o,s,a)}catch(y){if(y?.code!=="cli-not-running"||!await wl(t,n,"say-gate")||(await ae(),c=await Se(t.leadBox),!c))throw y;h=await hy(t,n,c,r,o,s,a)}return await ae(),h?.queued?{ok:!0,leadBox:t.leadBox,queued:!0,id:h.id,position:h.position,deliveryId:a.id}:{ok:!0,leadBox:t.leadBox,deliveryId:a.id}}async function Oy(e,{hint:t}={}){let n=pe(await ne(),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=mg(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";eI(n.id,{name:s,box:n.leadBox});let a=String(t||"").trim()?`${B$}
|
|
233
233
|
|
|
234
|
-
What to preview: ${String(t).trim()}`:
|
|
234
|
+
What to preview: ${String(t).trim()}`:B$;try{await qt(e,a,{kind:"spin-up-view"})}catch(i){throw Sp(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Qp(e){let t=pe(await ne(),e);if(!t)throw new Error(`track "${e}" not found`);if(t.archivedAt)throw new Error(`track "${e}" is archived`);pl.set(e,(pl.get(e)||0)+1);let n=kl(e,"interrupt"),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=Qa.get(e);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=ti(t,r),await ae()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:t.draftRev||0}}let s=zq(e,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,rr(Wq)]);let a=pe(await ne(),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 Q$(e){let t=Qa.get(e);return!!t&&!t.canceled}function oi(e,t){let n=Qa.get(e);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=t||null)}function My(e){oi(e,"track-ended"),pl.delete(e),kl(e,"track-ended")}function zq(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}),Qa.set(e,r),(async()=>{let s=pe(await ne(),e),a=s?.boxes.find(y=>y.name===s.leadBox),i=s?await Se(s.leadBox):null,c=!1,l=!1;if(i){e:for(let y=0;y<qq&&!r.canceled;y++){await Ke(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let k of Gq){await rr(k);let g=await Ne(s.leadBox,{lines:ze}).catch(()=>"");if(!ba(a?.agent,g)){c=!0;break e}}}if(!c){l=!0;for(let y=0;y<Vq&&!r.canceled&&(await rr(Kq),!r.canceled);y++){let k=await Ne(s.leadBox,{lines:ze}).catch(()=>"");if(!ba(a?.agent,k)){c=!0;break}await Ke(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=pe(await ne(),e)||s,d=u?.boxes.find(y=>y.name===u.leadBox)||a,p=null,f=null;u&&d&&i&&c&&!r.canceled&&(p=await Zq(u,d).catch(()=>null),p||(f=await t5(u,d).catch(()=>null)));let w=u&&i&&!r.canceled?await Yq(u).catch(()=>null):null,h=[p||f,...r.queuedTexts].filter(Boolean);u&&h.length&&(r.restored=ti(u,h),await ae()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await Jq(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 ee({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"),Qa.get(e)===r&&Qa.delete(e),o()}),r}async function Jq(e,t,n){await N(e,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:e.leadBox,provider:t?.agent||null,payload:n}).catch(()=>{})}async function Yq(e){let t=qa(await K(e));if(!t?.offerId)return null;let n=t.offerType==="exists";return await N(e,{type:n?"pr.exists.answered":"pr.offer.answered",actor:"user",source:"orchestrator",boxId:t.box,payload:{ts:Date.now(),offerId:t.offerId,...n?{choice:"dismiss"}:{action:"dismiss",actions:null},reason:"interrupt"}}),t.offerId}async function Zq(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 Xq){await rr(r);let o=await Ne(e.leadBox,{lines:ze}).catch(()=>""),s=fc(t.agent,o);if(s&&sd(n.text,s))return await eR(e.leadBox,t.agent,n.text.length+8),Ee(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:n.at,text:n.text}),n.text}return null}async function t5(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>Qq||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!He(r.text))return null;for(let s of e5){await rr(s);let a;try{a=await Bh(t,{})}catch{return null}if(Tv(a,r))return null;let i=await Ne(e.leadBox,{lines:ze}).catch(()=>"");if(fc(t.agent,i))return null}return Ee(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}async function n5(e,t,n,r=null){return wc({sessionName:e.leadBox,agent:t?.agent,agentCmd:Ps(t?.agent),text:n,trackId:e.id,flow:"say",stats:r})}async function eR(e,t,n,r=null){for(let o=0;o<3;o++){await da(e,n,{kind:"unsend-wipe"}).catch(()=>{}),await rr(200);let s=await Ne(e,{lines:ze}).catch(()=>""),a=fc(t,s);if(!a||r&&!sd(r,a)&&!(t==="codex"&&ih(r,a)))return!0;n=a.length+8}return!1}function ei(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 ti(e,t){let n=[],r=o=>n.some(s=>He(s).includes(He(o)));for(let o of[e.draft?.text,...t])o&&o.trim()&&!r(o)&&n.push(o);return n.length?ei(e,n.join(`
|
|
235
235
|
|
|
236
|
-
`),{source:"restore"})?.text??null:null}function
|
|
236
|
+
`),{source:"restore"})?.text??null:null}function o5(e,t){let n=He(t);if(!n)return!1;if(Cs(e.id).some(o=>He(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>r5)break;if(!(s.type!=="said"||!s.text||He(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function Cy(e,t,{rev:n=null}={}){if(typeof t!="string")throw new Error("text (string) required");let r=pe(await ne(),e);if(!r)throw new Error(`track "${e}" not found`);let o=t,s=Ia(t);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&o5(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;He(o).includes(He(i))?ei(r,o):ei(r,`${i}
|
|
237
237
|
|
|
238
|
-
${o}`,{source:"restore"})}else ei(r,o);return await ae(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Cy(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=fe(await ne(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await Se(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=sA(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await nc(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await wr({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:Ps(o.agent)});let u=await aA(a,r.leadBox,i);return Ee(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await ae(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function eR(e){for(let t of e){if(!t||!await $a(t))continue;let n=await LT(t).catch(()=>null);if(!n)continue;if((await Y.stat(z.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function Gt(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 _s(e){let t=[];for(let n of Gt(e?.workspace)){if(!n?.dir)continue;let r=await ps(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await fs(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function Ny(e){let t=e;typeof t=="string"&&!z.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await ea(t)??await ku(t));let n=await eR([t]);if(!n){let r=typeof e=="string"&&z.isAbsolute(e)&&!await Y.stat(e).catch(()=>null);throw new Error(`tagged repo "${e}" is not a usable base (${r?"that path does not exist on this node \u2014 pick one of this node's repos or use owner/name":"need a primary checkout or a synced owner/name"})`)}return n}async function r5(e,t,n,r){let o=`track/${uf(t)}-${e}`,s=[];try{await Y.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await Ny(i);if(s.some(f=>f.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await Me(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 hs(c);await ms(c,d,o,p),s.push({name:u,dir:d,base:c,baseRef:p,repo:l})}return await si(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await $t(i.base,i.dir).catch(()=>{});throw await Y.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function Qp(e,t){let n=await ne(),r=fe(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 Ny(typeof t=="string"?t.trim():t);if((o.repos??[]).some(p=>p?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await Me(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 hs(s);await nw(s,o.branch)?await Ec(s,l,o.branch):await ms(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 si(o.dir,o.repos,o.branch),Ee(r,"retagged",{action:"add",repo:a??s,name:c,workspace:Py(o)}),await ae(),d}async function ef(e,t){let n=await ne(),r=fe(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 Wt.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 ps(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await fs(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 $t(s.base,s.dir).catch(()=>{}),await Y.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await si(o.dir,o.repos,o.branch),Ee(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:Py(o)}),await ae(),{removed:s.name}}async function Fp(e,t){let n=await ne(),r=fe(n,e);r&&(r.reshape={...r.reshape||{},...t},await ae())}async function o5(e){let t=await Y.readdir(e).catch(()=>[]);for(let n of t)if(!(n===".git"||n===".bostrat")){if(n==="AGENTS.md"){let r=await Y.readFile(z.join(e,n),"utf8").catch(()=>"");if(ll(r))continue}return!0}try{let{stdout:n}=await Gp("git",["-C",e,"rev-list","--count","HEAD"]);if(Number(n.trim())>1)return!0}catch{}return!1}function D$(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 tR(e,{added:t=[]}={}){let{shape:n,dir:r,staging:o,sub:s,base:a,scratch:i}=e;for(let l of t)await $t(l.base,l.dir).catch(()=>{});let c=async(l,u)=>{await On(l)&&!await On(u)&&await Y.rename(l,u)};if(n==="worktree"&&s){let l=z.join(r,s);await On(l)&&(await c(z.join(r,".bostrat"),z.join(l,".bostrat")),await Up(a,l,o))}if(n==="repo"&&i){let l=z.join(r,i);await On(l)&&(await On(o)&&await Y.rm(o,{recursive:!0,force:!0}),await Y.rename(l,o))}return await On(o)?(await On(r)&&(await c(z.join(r,".bostrat"),z.join(o,".bostrat")),await Y.rm(r,{recursive:!0,force:!0})),n==="worktree"?await Up(a,o,r):await Y.rename(o,r),!0):!1}async function Dy(e,t,{skipPermissions:n=!0}={}){let r=await ne(),o=fe(r,e);if(!o)throw St(`track "${e}" not found`,404);if(o.archivedAt)throw St("track is archived \u2014 unarchive it first");try{af(o)}catch(y){throw Object.assign(y,{status:409})}if(o.kind==="setup")throw St("setup boxes have no repos to reshape");if(String(o.name||"").startsWith("\u21BB"))throw St("this Track's repos are the loop's \u2014 propose-update the loop instead");let s=wy.get(e);if(s&&!s.finishedAt)throw St("a reshape is already running for this Track");let a=Ns.get(e);if(a&&!a.finishedAt)throw St("a lead handoff is running \u2014 retry when it settles");let i=Wp.get(e);if(i&&!i.finishedAt)throw St("a node transfer is running \u2014 retry when it settles");if(Lp.has(e))throw St("an adopt is running for this Track \u2014 retry when it settles");for(let y of Wt.values())if(y.trackId===e&&!y.finishedAt)throw St("a subtask is still running on this Track \u2014 wait for it to merge back before reshaping");let c=(Array.isArray(t)?t:[]).map(y=>typeof y=="string"?y.trim():"").filter(Boolean);if(!c.length)throw St("repos (non-empty list of paths or owner/name slugs) required",400);let l=[];for(let y of c){let k;try{k=await Ny(y)}catch(g){throw St(g?.message||String(g),400)}if(l.some(g=>g.base===k))throw St(`repo tagged twice: ${y}`,400);l.push({sel:y,base:k,slug:await Me(k).catch(()=>null)})}let u=o.workspace;if(u?.kind==="multi"){let y=(u.repos??[]).filter(Boolean),k=y.filter(S=>!l.some(_=>_.base===S.base)),g=l.filter(S=>!y.some(_=>_.base===S.base));if(!k.length&&!g.length)return{trackId:e,step:"done",inPlace:!0,added:[],removed:[],startedAt:Date.now(),finishedAt:Date.now(),error:null};for(let S of k){let _=await ps(S.dir,{ignorePlumbing:!0}).catch(()=>!1),M=await fs(S.dir,S.baseRef).catch(()=>0);if(_||M>0)throw St(`"${S.name}" has work \u2014 ship or discard it before dropping it`)}let x={trackId:e,step:"retag",inPlace:!0,added:[],removed:[],startedAt:Date.now(),finishedAt:null,error:null};for(let S of g){let _=await Qp(e,S.base);x.added.push(_.repo??_.base)}for(let S of k)await ef(e,S.name),x.removed.push(S.repo??S.base);return x.step="done",x.finishedAt=Date.now(),x}if(u?.kind!=="worktree"&&u?.kind!=="repo")throw St(`can't reshape a "${u?.kind??"unknown"}" workspace`);let d=u.kind==="worktree"&&l.some(y=>y.base===u.base);if(u.kind==="worktree"&&!d){let y=await ps(u.dir??o.cwd,{ignorePlumbing:!0}).catch(()=>!1),k=await fs(u.dir??o.cwd,u.baseRef).catch(()=>0);if(y||k>0)throw St(`"${z.basename(u.base)}" has work (${y?"uncommitted changes":`${k} commit${k===1?"":"s"} ahead of ${u.baseRef??"base"}`}) \u2014 ship or discard it before swapping it out`)}oi(e,"reshape");let p=o.boxes.find(y=>y.name===o.leadBox)||null;if(!p)throw St("this Track has no lead box");let f=p.agent||"claude",w=await ri(f,n),h={trackId:e,step:"checkpoint",inPlace:!1,startedAt:Date.now(),finishedAt:null,error:null,added:l.filter(y=>!(u.kind==="worktree"&&y.base===u.base)).map(y=>y.slug??y.base),removed:u.kind==="worktree"&&!d?[u.repo??u.base]:[],scratch:!1,views:0,checkpoint:null};return wy.set(e,h),await Fp(e,{step:h.step,startedAt:h.startedAt,finishedAt:null,error:null,added:h.added,removed:h.removed,layout:null}),s5(h,o,{desired:l,keep:d,lead:p,agent:f,agentCmd:w,skipPermissions:n}).catch(async y=>{h.error=y?.message||String(y),h.finishedAt=Date.now(),console.error(`[reshape ${e}] FAILED at step "${h.step}": ${h.error}`),await Fp(e,{step:h.step,error:h.error,finishedAt:h.finishedAt}).catch(()=>{})}),h}async function s5(e,t,{desired:n,keep:r,lead:o,agent:s,agentCmd:a,skipPermissions:i}){let c=e.trackId,l=t.workspace,u=t.cwd,d=async(T,C={})=>{e.step=T,console.log(`[reshape ${c}] step \u2192 ${T}`),await Fp(c,{step:T,...C})},p=await Se(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(mo(s))e.checkpoint="skipped-capped";else try{await Td(o.name,{timeoutMs:by,quiesce:{box:o,reason:"reshape",onWait:()=>d("await-turn"),onResume:()=>d("checkpoint")}})}catch{e.checkpoint="reconstructed"}let h=null;e.checkpoint!=="llm"&&(h=await Ad(c,u,{trackName:t.name||c}).catch(()=>null)),await d("quiesce"),e.views=await kg(c).catch(()=>0),await $u(u).catch(()=>{}),p&&(await Vh(o.name,{fromAgent:s}).catch(()=>{}),await Lt(o.name).catch(()=>{}));let y=`${u}.reshape-${Date.now()}`,k=new Set([".bostrat"]),g={shape:l.kind,dir:u,staging:y,base:l.kind==="worktree"?l.base:null,sub:l.kind==="worktree"&&r?D$(l.base,l.repo??null,k):null,scratch:null},x=l.branch||`track/${uf(t.name||c)}-${c}`,S=[],_=[],M=null;try{if(await d("move",{layout:g}),l.kind==="worktree")if(await Up(l.base,u,y),await Y.mkdir(u,{recursive:!0}),r){let C=z.join(u,g.sub);await Up(l.base,y,C),await On(z.join(C,".bostrat"))&&await Y.rename(z.join(C,".bostrat"),z.join(u,".bostrat"));let W=z.join(C,"AGENTS.md");ll(await Y.readFile(W,"utf8").catch(()=>null))&&(await Y.rm(W,{force:!0}).catch(()=>{}),await oR(C,"/AGENTS.md").catch(()=>{})),S.push({name:g.sub,dir:C,base:l.base,baseRef:l.baseRef??null,repo:l.repo??null})}else await On(z.join(y,".bostrat"))&&await Y.rename(z.join(y,".bostrat"),z.join(u,".bostrat"));else await Y.rename(u,y),await Y.mkdir(u,{recursive:!0}),await On(z.join(y,".bostrat"))&&await Y.rename(z.join(y,".bostrat"),z.join(u,".bostrat"));await d("materialize");for(let C of n){if(l.kind==="worktree"&&C.base===l.base)continue;let W=D$(C.base,C.slug,k),B=z.join(u,W),Z=await hs(C.base);await nw(C.base,x)?await Ec(C.base,B,x):await ms(C.base,B,x,Z);let xe={name:W,dir:B,base:C.base,baseRef:Z,repo:C.slug};S.push(xe),_.push(xe)}if(l.kind==="repo"&&await o5(y)){let C="scratch";for(let W=2;k.has(C);W++)C=`scratch-${W}`;k.add(C),g.scratch=C,await Fp(c,{layout:g}),await Y.rename(y,z.join(u,C)),e.scratch=!0}await si(u,S,x,{scratch:g.scratch}),await d("relaunch"),M=await jn(o.name,u,{agentCmd:a,agent:s}),await wr({cwd:u,sessionName:o.name,agent:s,agentCmd:Ps(s)}).catch(C=>{if(C?.code!=="cli-not-running")throw C});let T=await Ne(o.name,{lines:ze}).catch(()=>"");if(!to(s,T))throw new Error(`relaunched ${s} CLI never drew its composer in the hub`)}catch(T){await d("rollback",{error:T?.message||String(T)}),await Lt(o.name).catch(()=>{}),await tR(g,{added:_}).catch(Z=>{console.error(`[reshape ${c}] rollback move failed: ${Z?.message||Z}`)});let C=await ne(),W=fe(C,c),B=W?.boxes.find(Z=>Z.name===o.name)||null;try{let Z=Vp(s,f,{skipPermissions:i,cwd:u,peerName:o.name}),xe=await jn(o.name,u,{agentCmd:Z,agent:s,resumeSessionId:f,pinned:w});B&&(B.hookTokenHash=Ho(xe.hookToken),B.state="live"),rs({cwd:u,sessionName:o.name,agent:s,agentCmd:Z})}catch(Z){console.error(`[reshape ${c}] rollback relaunch failed: ${Z?.message||Z}`)}throw W&&(Ee(W,"retag-failed",{action:"reshape",box:o.name,agent:s,reason:T?.message||String(T),rolledBack:!0}),W.reshape={...W.reshape||{},step:"error",error:T?.message||String(T),finishedAt:Date.now(),recovered:"rolled-back"},await ae()),e.step="error",e.recovered="rolled-back",e.error=T?.message||String(T),e.finishedAt=Date.now(),T}await d("commit");let P=await ne(),j=fe(P,c);if(!j)throw new Error(`track "${c}" vanished mid-reshape`);let A=j.boxes.find(T=>T.name===o.name);if(!A)throw new Error(`lead box row "${o.name}" missing during reshape commit`);let $=Date.now(),L=_o(A),H=L[L.length-1];H.endedAt==null&&(H.endedAt=$),H.endReason==null&&(H.endReason="reshape");let R=await ds({agent:s,agentCmd:a});L.push({agent:s,sessionId:M.sessionId,pinned:M.pinned===!0,cliVersion:R,startedAt:$,endedAt:null}),A.cwd=u,A.agent=s,A.cliVersion=R,A.sessionId=M.sessionId,A.pinned=M.pinned===!0,A.hookTokenHash=Ho(M.hookToken),A.state="live",A.role="lead",delete A.readyAt,j.cwd=u,j.workspace={dir:u,kind:"multi",branch:x,repos:S},j.pendingHandoffHydrate={artifact:h||Io,at:$},Ee(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:M.sessionId,epoch:L.length-1,workspace:Py(j.workspace)}),j.reshape={...j.reshape||{},step:"done",finishedAt:$,error:null,checkpoint:e.checkpoint,scratch:e.scratch,views:e.views,layout:null},await ae(),rs({cwd:u,sessionName:o.name,agent:s,agentCmd:a,onReady:async()=>{A.readyAt||(A.readyAt=Date.now(),await ae())}}),await On(y)&&(l.kind==="worktree"&&await $t(l.base,y).catch(()=>{}),await Y.rm(y,{recursive:!0,force:!0}).catch(()=>{})),e.step="done",e.finishedAt=$}async function By(){let e=await ne(),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 Lt(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 Y.readdir(c).catch(()=>[])){if(d.has(f))continue;let w=z.join(c,f),h=await Y.readFile(z.join(w,".git"),"utf8").catch(()=>null),y=h&&/^gitdir:\s*(.+?)\/\.git\/worktrees\//m.exec(h);y&&p.push({base:y[1],dir:w})}s=await tR(r.layout,{added:p})}catch(c){a=c?.message||String(c),console.error(`[reshape ${n.id}] boot rollback failed: ${a}`)}o&&!jr.has(o.state)&&(o.state="stopped");let i=`interrupted by a node restart during "${r.step}"${s?" \u2014 moved back":""}${a?` (${a})`:""}`;Ee(n,"retag-failed",{action:"reshape",box:n.leadBox,agent:o?.agent??null,reason:i,rolledBack:!a,recovered:!0}),n.reshape={...r,step:"error",error:i,finishedAt:Date.now(),recovered:a?"rollback-failed":"rolled-back",layout:null}}return t&&await ae(),t}async function a5(e,t,{repo:n,repos:r,cwd:o}={}){await Y.mkdir(ul,{recursive:!0});let s=z.join(ul,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return r5(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!z.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await ea(n)??await ku(n));let i=await eR([n,o,fq]);if(i){let c=`track/${uf(t)}-${e}`;try{let l=await hs(i);await ms(i,s,c,l),await ml(s);let u=await Me(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await Y.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await Y.mkdir(s,{recursive:!0}),await rw(s),await ml(s),{dir:s,base:null,branch:null,kind:"repo"}}async function ml(e){try{await Y.writeFile(z.join(e,"AGENTS.md"),cl,{flag:"wx"})}catch{return}await u5(e,["/AGENTS.md"])}function l5(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(`
|
|
238
|
+
${o}`,{source:"restore"})}else ei(r,o);return await ae(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Ny(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=pe(await ne(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await Se(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=iA(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await nc(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await wr({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:Ps(o.agent)});let u=await cA(a,r.leadBox,i);return Ee(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await ae(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function tR(e){for(let t of e){if(!t||!await $a(t))continue;let n=await FT(t).catch(()=>null);if(!n)continue;if((await Y.stat(z.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function Gt(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 _s(e){let t=[];for(let n of Gt(e?.workspace)){if(!n?.dir)continue;let r=await ps(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await fs(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function Py(e){let t=e;typeof t=="string"&&!z.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await ea(t)??await xu(t));let n=await tR([t]);if(!n){let r=typeof e=="string"&&z.isAbsolute(e)&&!await Y.stat(e).catch(()=>null);throw new Error(`tagged repo "${e}" is not a usable base (${r?"that path does not exist on this node \u2014 pick one of this node's repos or use owner/name":"need a primary checkout or a synced owner/name"})`)}return n}async function s5(e,t,n,r){let o=`track/${uf(t)}-${e}`,s=[];try{await Y.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await Py(i);if(s.some(f=>f.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await Me(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 hs(c);await ms(c,d,o,p),s.push({name:u,dir:d,base:c,baseRef:p,repo:l})}return await si(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await $t(i.base,i.dir).catch(()=>{});throw await Y.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function ef(e,t){let n=await ne(),r=pe(n,e);if(!r)throw new Error(`track "${e}" not found`);if(r.archivedAt)throw new Error("track is archived \u2014 unarchive it first");let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track \u2014 a single-repo Track's workspace IS its repo checkout; create the Track with multiple repos tagged instead");let s=await Py(typeof t=="string"?t.trim():t);if((o.repos??[]).some(p=>p?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await Me(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 hs(s);await nw(s,o.branch)?await Ec(s,l,o.branch):await ms(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 si(o.dir,o.repos,o.branch),Ee(r,"retagged",{action:"add",repo:a??s,name:c,workspace:Dy(o)}),await ae(),d}async function tf(e,t){let n=await ne(),r=pe(n,e);if(!r)throw new Error(`track "${e}" not found`);let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track");let s=(o.repos??[]).find(c=>c&&(c.name===t||c.repo===t||c.base===t));if(!s)throw new Error(`repo "${t}" is not tagged on this Track`);if((o.repos??[]).filter(Boolean).length<=1)throw new Error("can't untag the last repo \u2014 archive or delete the Track instead");for(let c of Wt.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 ps(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await fs(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 $t(s.base,s.dir).catch(()=>{}),await Y.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await si(o.dir,o.repos,o.branch),Ee(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:Dy(o)}),await ae(),{removed:s.name}}async function Up(e,t){let n=await ne(),r=pe(n,e);r&&(r.reshape={...r.reshape||{},...t},await ae())}async function a5(e){let t=await Y.readdir(e).catch(()=>[]);for(let n of t)if(!(n===".git"||n===".bostrat")){if(n==="AGENTS.md"){let r=await Y.readFile(z.join(e,n),"utf8").catch(()=>"");if(ll(r))continue}return!0}try{let{stdout:n}=await Kp("git",["-C",e,"rev-list","--count","HEAD"]);if(Number(n.trim())>1)return!0}catch{}return!1}function L$(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 nR(e,{added:t=[]}={}){let{shape:n,dir:r,staging:o,sub:s,base:a,scratch:i}=e;for(let l of t)await $t(l.base,l.dir).catch(()=>{});let c=async(l,u)=>{await On(l)&&!await On(u)&&await Y.rename(l,u)};if(n==="worktree"&&s){let l=z.join(r,s);await On(l)&&(await c(z.join(r,".bostrat"),z.join(l,".bostrat")),await Hp(a,l,o))}if(n==="repo"&&i){let l=z.join(r,i);await On(l)&&(await On(o)&&await Y.rm(o,{recursive:!0,force:!0}),await Y.rename(l,o))}return await On(o)?(await On(r)&&(await c(z.join(r,".bostrat"),z.join(o,".bostrat")),await Y.rm(r,{recursive:!0,force:!0})),n==="worktree"?await Hp(a,o,r):await Y.rename(o,r),!0):!1}async function By(e,t,{skipPermissions:n=!0}={}){let r=await ne(),o=pe(r,e);if(!o)throw Tt(`track "${e}" not found`,404);if(o.archivedAt)throw Tt("track is archived \u2014 unarchive it first");try{af(o)}catch(y){throw Object.assign(y,{status:409})}if(o.kind==="setup")throw Tt("setup boxes have no repos to reshape");if(String(o.name||"").startsWith("\u21BB"))throw Tt("this Track's repos are the loop's \u2014 propose-update the loop instead");let s=wy.get(e);if(s&&!s.finishedAt)throw Tt("a reshape is already running for this Track");let a=Ns.get(e);if(a&&!a.finishedAt)throw Tt("a lead handoff is running \u2014 retry when it settles");let i=qp.get(e);if(i&&!i.finishedAt)throw Tt("a node transfer is running \u2014 retry when it settles");if(jp.has(e))throw Tt("an adopt is running for this Track \u2014 retry when it settles");for(let y of Wt.values())if(y.trackId===e&&!y.finishedAt)throw Tt("a subtask is still running on this Track \u2014 wait for it to merge back before reshaping");let c=(Array.isArray(t)?t:[]).map(y=>typeof y=="string"?y.trim():"").filter(Boolean);if(!c.length)throw Tt("repos (non-empty list of paths or owner/name slugs) required",400);let l=[];for(let y of c){let k;try{k=await Py(y)}catch(g){throw Tt(g?.message||String(g),400)}if(l.some(g=>g.base===k))throw Tt(`repo tagged twice: ${y}`,400);l.push({sel:y,base:k,slug:await Me(k).catch(()=>null)})}let u=o.workspace;if(u?.kind==="multi"){let y=(u.repos??[]).filter(Boolean),k=y.filter(S=>!l.some(_=>_.base===S.base)),g=l.filter(S=>!y.some(_=>_.base===S.base));if(!k.length&&!g.length)return{trackId:e,step:"done",inPlace:!0,added:[],removed:[],startedAt:Date.now(),finishedAt:Date.now(),error:null};for(let S of k){let _=await ps(S.dir,{ignorePlumbing:!0}).catch(()=>!1),M=await fs(S.dir,S.baseRef).catch(()=>0);if(_||M>0)throw Tt(`"${S.name}" has work \u2014 ship or discard it before dropping it`)}let x={trackId:e,step:"retag",inPlace:!0,added:[],removed:[],startedAt:Date.now(),finishedAt:null,error:null};for(let S of g){let _=await ef(e,S.base);x.added.push(_.repo??_.base)}for(let S of k)await tf(e,S.name),x.removed.push(S.repo??S.base);return x.step="done",x.finishedAt=Date.now(),x}if(u?.kind!=="worktree"&&u?.kind!=="repo")throw Tt(`can't reshape a "${u?.kind??"unknown"}" workspace`);let d=u.kind==="worktree"&&l.some(y=>y.base===u.base);if(u.kind==="worktree"&&!d){let y=await ps(u.dir??o.cwd,{ignorePlumbing:!0}).catch(()=>!1),k=await fs(u.dir??o.cwd,u.baseRef).catch(()=>0);if(y||k>0)throw Tt(`"${z.basename(u.base)}" has work (${y?"uncommitted changes":`${k} commit${k===1?"":"s"} ahead of ${u.baseRef??"base"}`}) \u2014 ship or discard it before swapping it out`)}oi(e,"reshape");let p=o.boxes.find(y=>y.name===o.leadBox)||null;if(!p)throw Tt("this Track has no lead box");let f=p.agent||"claude",w=await ri(f,n),h={trackId:e,step:"checkpoint",inPlace:!1,startedAt:Date.now(),finishedAt:null,error:null,added:l.filter(y=>!(u.kind==="worktree"&&y.base===u.base)).map(y=>y.slug??y.base),removed:u.kind==="worktree"&&!d?[u.repo??u.base]:[],scratch:!1,views:0,checkpoint:null};return wy.set(e,h),await Up(e,{step:h.step,startedAt:h.startedAt,finishedAt:null,error:null,added:h.added,removed:h.removed,layout:null}),i5(h,o,{desired:l,keep:d,lead:p,agent:f,agentCmd:w,skipPermissions:n}).catch(async y=>{h.error=y?.message||String(y),h.finishedAt=Date.now(),console.error(`[reshape ${e}] FAILED at step "${h.step}": ${h.error}`),await Up(e,{step:h.step,error:h.error,finishedAt:h.finishedAt}).catch(()=>{})}),h}async function i5(e,t,{desired:n,keep:r,lead:o,agent:s,agentCmd:a,skipPermissions:i}){let c=e.trackId,l=t.workspace,u=t.cwd,d=async(T,C={})=>{e.step=T,console.log(`[reshape ${c}] step \u2192 ${T}`),await Up(c,{step:T,...C})},p=await Se(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(mo(s))e.checkpoint="skipped-capped";else try{await Ad(o.name,{timeoutMs:by,quiesce:{box:o,reason:"reshape",onWait:()=>d("await-turn"),onResume:()=>d("checkpoint")}})}catch{e.checkpoint="reconstructed"}let h=null;e.checkpoint!=="llm"&&(h=await Ed(c,u,{trackName:t.name||c}).catch(()=>null)),await d("quiesce"),e.views=await kg(c).catch(()=>0),await Ru(u).catch(()=>{}),p&&(await Vh(o.name,{fromAgent:s}).catch(()=>{}),await Lt(o.name).catch(()=>{}));let y=`${u}.reshape-${Date.now()}`,k=new Set([".bostrat"]),g={shape:l.kind,dir:u,staging:y,base:l.kind==="worktree"?l.base:null,sub:l.kind==="worktree"&&r?L$(l.base,l.repo??null,k):null,scratch:null},x=l.branch||`track/${uf(t.name||c)}-${c}`,S=[],_=[],M=null;try{if(await d("move",{layout:g}),l.kind==="worktree")if(await Hp(l.base,u,y),await Y.mkdir(u,{recursive:!0}),r){let C=z.join(u,g.sub);await Hp(l.base,y,C),await On(z.join(C,".bostrat"))&&await Y.rename(z.join(C,".bostrat"),z.join(u,".bostrat"));let W=z.join(C,"AGENTS.md");ll(await Y.readFile(W,"utf8").catch(()=>null))&&(await Y.rm(W,{force:!0}).catch(()=>{}),await sR(C,"/AGENTS.md").catch(()=>{})),S.push({name:g.sub,dir:C,base:l.base,baseRef:l.baseRef??null,repo:l.repo??null})}else await On(z.join(y,".bostrat"))&&await Y.rename(z.join(y,".bostrat"),z.join(u,".bostrat"));else await Y.rename(u,y),await Y.mkdir(u,{recursive:!0}),await On(z.join(y,".bostrat"))&&await Y.rename(z.join(y,".bostrat"),z.join(u,".bostrat"));await d("materialize");for(let C of n){if(l.kind==="worktree"&&C.base===l.base)continue;let W=L$(C.base,C.slug,k),B=z.join(u,W),Z=await hs(C.base);await nw(C.base,x)?await Ec(C.base,B,x):await ms(C.base,B,x,Z);let xe={name:W,dir:B,base:C.base,baseRef:Z,repo:C.slug};S.push(xe),_.push(xe)}if(l.kind==="repo"&&await a5(y)){let C="scratch";for(let W=2;k.has(C);W++)C=`scratch-${W}`;k.add(C),g.scratch=C,await Up(c,{layout:g}),await Y.rename(y,z.join(u,C)),e.scratch=!0}await si(u,S,x,{scratch:g.scratch}),await d("relaunch"),M=await jn(o.name,u,{agentCmd:a,agent:s}),await wr({cwd:u,sessionName:o.name,agent:s,agentCmd:Ps(s)}).catch(C=>{if(C?.code!=="cli-not-running")throw C});let T=await Ne(o.name,{lines:ze}).catch(()=>"");if(!to(s,T))throw new Error(`relaunched ${s} CLI never drew its composer in the hub`)}catch(T){await d("rollback",{error:T?.message||String(T)}),await Lt(o.name).catch(()=>{}),await nR(g,{added:_}).catch(Z=>{console.error(`[reshape ${c}] rollback move failed: ${Z?.message||Z}`)});let C=await ne(),W=pe(C,c),B=W?.boxes.find(Z=>Z.name===o.name)||null;try{let Z=zp(s,f,{skipPermissions:i,cwd:u,peerName:o.name}),xe=await jn(o.name,u,{agentCmd:Z,agent:s,resumeSessionId:f,pinned:w});B&&(B.hookTokenHash=Ho(xe.hookToken),B.state="live"),rs({cwd:u,sessionName:o.name,agent:s,agentCmd:Z})}catch(Z){console.error(`[reshape ${c}] rollback relaunch failed: ${Z?.message||Z}`)}throw W&&(Ee(W,"retag-failed",{action:"reshape",box:o.name,agent:s,reason:T?.message||String(T),rolledBack:!0}),W.reshape={...W.reshape||{},step:"error",error:T?.message||String(T),finishedAt:Date.now(),recovered:"rolled-back"},await ae()),e.step="error",e.recovered="rolled-back",e.error=T?.message||String(T),e.finishedAt=Date.now(),T}await d("commit");let P=await ne(),j=pe(P,c);if(!j)throw new Error(`track "${c}" vanished mid-reshape`);let A=j.boxes.find(T=>T.name===o.name);if(!A)throw new Error(`lead box row "${o.name}" missing during reshape commit`);let $=Date.now(),L=_o(A),H=L[L.length-1];H.endedAt==null&&(H.endedAt=$),H.endReason==null&&(H.endReason="reshape");let R=await ds({agent:s,agentCmd:a});L.push({agent:s,sessionId:M.sessionId,pinned:M.pinned===!0,cliVersion:R,startedAt:$,endedAt:null}),A.cwd=u,A.agent=s,A.cliVersion=R,A.sessionId=M.sessionId,A.pinned=M.pinned===!0,A.hookTokenHash=Ho(M.hookToken),A.state="live",A.role="lead",delete A.readyAt,j.cwd=u,j.workspace={dir:u,kind:"multi",branch:x,repos:S},j.pendingHandoffHydrate={artifact:h||Io,at:$},Ee(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:M.sessionId,epoch:L.length-1,workspace:Dy(j.workspace)}),j.reshape={...j.reshape||{},step:"done",finishedAt:$,error:null,checkpoint:e.checkpoint,scratch:e.scratch,views:e.views,layout:null},await ae(),rs({cwd:u,sessionName:o.name,agent:s,agentCmd:a,onReady:async()=>{A.readyAt||(A.readyAt=Date.now(),await ae())}}),await On(y)&&(l.kind==="worktree"&&await $t(l.base,y).catch(()=>{}),await Y.rm(y,{recursive:!0,force:!0}).catch(()=>{})),e.step="done",e.finishedAt=$}async function Ly(){let e=await ne(),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 Lt(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 Y.readdir(c).catch(()=>[])){if(d.has(f))continue;let w=z.join(c,f),h=await Y.readFile(z.join(w,".git"),"utf8").catch(()=>null),y=h&&/^gitdir:\s*(.+?)\/\.git\/worktrees\//m.exec(h);y&&p.push({base:y[1],dir:w})}s=await nR(r.layout,{added:p})}catch(c){a=c?.message||String(c),console.error(`[reshape ${n.id}] boot rollback failed: ${a}`)}o&&!jr.has(o.state)&&(o.state="stopped");let i=`interrupted by a node restart during "${r.step}"${s?" \u2014 moved back":""}${a?` (${a})`:""}`;Ee(n,"retag-failed",{action:"reshape",box:n.leadBox,agent:o?.agent??null,reason:i,rolledBack:!a,recovered:!0}),n.reshape={...r,step:"error",error:i,finishedAt:Date.now(),recovered:a?"rollback-failed":"rolled-back",layout:null}}return t&&await ae(),t}async function c5(e,t,{repo:n,repos:r,cwd:o}={}){await Y.mkdir(ul,{recursive:!0});let s=z.join(ul,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return s5(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!z.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await ea(n)??await xu(n));let i=await tR([n,o,mq]);if(i){let c=`track/${uf(t)}-${e}`;try{let l=await hs(i);await ms(i,s,c,l),await ml(s);let u=await Me(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await Y.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await Y.mkdir(s,{recursive:!0}),await rw(s),await ml(s),{dir:s,base:null,branch:null,kind:"repo"}}async function ml(e){try{await Y.writeFile(z.join(e,"AGENTS.md"),cl,{flag:"wx"})}catch{return}await p5(e,["/AGENTS.md"])}function d5(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(`
|
|
239
239
|
`),o=n?`
|
|
240
240
|
\`${n}/\` is this Track's pre-repo workspace, carried over when the
|
|
241
241
|
Track was reshaped onto these repos. It is not part of any tagged repo and
|
|
242
242
|
cannot be shipped \u2014 move anything worth keeping into the repo it belongs to.
|
|
243
|
-
`:"";return`${
|
|
243
|
+
`:"";return`${u5}
|
|
244
244
|
|
|
245
245
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
246
246
|
git worktree per tagged repo, ALL on the same branch \`${t}\`:
|
|
@@ -252,20 +252,20 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
252
252
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
253
253
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
254
254
|
or preview an app, run it from that app's own repo directory.
|
|
255
|
-
${o}`}async function si(e,t,n,r={}){let o=
|
|
255
|
+
${o}`}async function si(e,t,n,r={}){let o=d5(t,n,r);for(let s of["CLAUDE.md","AGENTS.md"])await Y.writeFile(z.join(e,s),o).catch(()=>{})}async function oR(e){let{stdout:t}=await Kp("git",["-C",e,"rev-parse","--git-path","info/exclude"]);return z.resolve(e,t.trim())}async function p5(e,t){try{let n=await oR(e);await Y.mkdir(z.dirname(n),{recursive:!0});let r=await Y.readFile(n,"utf8").catch(()=>""),o=t.filter(s=>!r.split(`
|
|
256
256
|
`).includes(s));o.length&&await Y.appendFile(n,(r.endsWith(`
|
|
257
257
|
`)||!r?"":`
|
|
258
258
|
`)+o.join(`
|
|
259
259
|
`)+`
|
|
260
|
-
`)}catch{}}async function
|
|
260
|
+
`)}catch{}}async function sR(e,t){try{let n=await oR(e),r=await Y.readFile(n,"utf8"),o=r.split(`
|
|
261
261
|
`).filter(s=>s!==t);o.length!==r.split(`
|
|
262
262
|
`).length&&await Y.writeFile(n,o.join(`
|
|
263
|
-
`))}catch{}}async function
|
|
263
|
+
`))}catch{}}async function jy(){let e={refreshed:0,removed:0,codexGlobal:null};try{await Y.mkdir(nr,{recursive:!0}),await Y.writeFile(z.join(nr,"CLAUDE.md"),l5).catch(()=>{}),await Y.writeFile(z.join(nr,"AGENTS.md"),cl).catch(()=>{});let t=process.env.CODEX_HOME||z.join(process.env.HOME||"/tmp",".codex");if((await Y.stat(t).catch(()=>null))?.isDirectory()){let r=z.join(t,"AGENTS.md"),o=await Y.readFile(r,"utf8").catch(()=>null);o===null?(await Y.writeFile(r,cy,{flag:"wx"}).catch(()=>{}),e.codexGlobal="created"):ll(o)&&o!==cy&&(await Y.writeFile(r,cy).catch(()=>{}),e.codexGlobal="refreshed")}for(let r of[ul,ni]){let o=await Y.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=z.join(r,s.name),i=z.join(a,"AGENTS.md"),c=await Y.readFile(i,"utf8").catch(()=>null);c!==null&&ll(c)&&c!==cl&&(await Y.writeFile(i,cl).catch(()=>{}),e.refreshed++);let l=z.join(a,"CLAUDE.md"),u=await Y.readFile(l,"utf8").catch(()=>null);u!==null&&ll(u)&&(await Y.rm(l,{force:!0}).catch(()=>{}),await sR(a,"/CLAUDE.md"),e.removed++)}}}catch{}return e}async function aR(e){let t=e.workspace;if(!(!t||!t.dir))try{if(t.kind==="multi"){for(let n of Gt(t))n.base&&n.dir&&await $t(n.base,n.dir).catch(()=>{});await Y.rm(t.dir,{recursive:!0,force:!0})}else t.kind==="worktree"&&t.base?await $t(t.base,t.dir):await Y.rm(t.dir,{recursive:!0,force:!0})}catch{}}async function nf({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 ri(o,a,s),c=ds({agent:o,agentCmd:i}),l=await ne(),u=Gp().slice(0,8),d=await c5(u,e,{repo:n,repos:r,cwd:t}),p=new Set(l.tracks.flatMap(y=>(y.boxes||[]).map(k=>k.name))),f=`${e.trim()} lead`;p.has(Hu(f))&&(f=`${e.trim().slice(0,50).trim()} ${u} lead`);let w=await jn(f,d.dir,{agentCmd:i,agent:o,freshWorkspace:!0}),h={id:u,name:e.trim(),cwd:w.cwd,workspace:d,leadBox:w.name,boxes:[Yy(w,"lead","live")],events:[],createdAt:Date.now()};return h.boxes[0].cliVersion=await c,Ee(h,"opened",{agent:o,...s?{model:s}:{},box:w.name,cliVersion:h.boxes[0].cliVersion,skipPermissions:a,name:h.name,cwd:h.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:y,base:k,baseRef:g,repo:x})=>({name:y,base:k,baseRef:g,repo:x}))}:{}}}),l.tracks.push(h),await ae(),rs({cwd:w.cwd,sessionName:w.name,agent:o,agentCmd:i,onBlocked:({message:y})=>G$(h.id,{box:w.name,agent:o,message:y}).catch(()=>{}),onReady:async()=>{let y=h.boxes.find(k=>k.name===h.leadBox);!y||y.readyAt||(y.readyAt=Date.now(),await ae())}}),Zt(h)}async function Fy({provider:e="claude",fresh:t=!1}={}){if(!f5.has(e))throw new Error(`unknown setup provider "${e}" (claude | codex | cursor)`);let r=(await ne()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===e&&!u.archivedAt);if(r){if(!t){let u=await Zt(r);if(u.boxes.find(p=>p.name===u.leadBox)?.state==="live")return u}await Ds(r.id).catch(()=>{})}let o=(await ri(e,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=Gp().slice(0,8),a=z.join(nr,"setup",s);await Y.mkdir(a,{recursive:!0});let i=await jn(`Connect ${e} ${s}`,a,{agentCmd:o,agent:e,firstRun:!0});e==="codex"&&rs({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:[Yy(i,"lead","live")],events:[],createdAt:Date.now()};return Ee(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 ne()).tracks.push(c),await ae(),Zt(c)}async function Uy(e,t,n){await Ru(t.cwd).catch(()=>{}),await kg(e).catch(()=>{});let r=Ns.get(e);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),Ns.delete(e),t.handoff&&!t.handoff.finishedAt&&(t.handoff.step="error",t.handoff.error=n,t.handoff.finishedAt=Date.now());for(let a of Ls(e))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await ab(a).catch(()=>{}),Wt.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 ne(),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 Lt(a.name).catch(()=>{})}async function Hy(e,{cascadeMocks:t=!0,cascadeRunbooks:n=!1}={}){let r=await ne(),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 $I(s),My(e),await Uy(e,s,"track deleted"),n?await a$(e).catch(()=>{}):await s$(e).catch(()=>{}),await dA(),await aR(s),t&&await RI(e).catch(()=>{}),t?await VI(e).catch(()=>{}):await KI(s).catch(()=>{}),Ee(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await ae(),{ok:!0,id:e,name:s.name,boxes:s.boxes.length}}async function Ds(e){let t=await ne(),n=pe(t,e);if(!n)throw new Error(`track "${e}" not found`);if(n.archivedAt)throw new Error(`track "${e}" is already archived`);return n.archivedAt=Date.now(),My(e),Ee(n,"archived",{boxes:n.boxes.length}),await ae(),n.kind==="setup"?(await iR(e),Zt(n)):(hl.add(e),w5(e).catch(()=>{}).finally(()=>hl.delete(e)),{...await Zt(n),archiving:!0})}async function w5(e){try{await g5(e)}catch{}await iR(e).catch(()=>{})}async function iR(e){let t=await ne(),n=pe(t,e);if(!(!n||!n.archivedAt)){await Uy(e,n,"track archived");for(let r of n.boxes)jr.has(r.state)||(r.state="stopped");await ae()}}async function g5(e){let t=await ne(),n=pe(t,e);if(!n||!n.archivedAt)return;let r=null,o=[],s="derived",a=(n.events||[]).some(l=>l.type==="said"),i=n.leadBox?await Se(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await uT(n.leadBox,i.cwd,{timeoutMs:h5})),s="lead-cli"}catch{}if(r||(r=AT(n,await K(n).catch(()=>[]))),!r)return;let c={text:r,source:s,at:Date.now()};o.length&&(c.items=o),n.beat=c,await N(n,{type:"track.summary.recorded",actor:"orchestrator",source:"orchestrator",boxId:n.leadBox||null,provider:i?.agent||null,payload:o.length?{text:r,items:o,source:s}:{text:r,source:s}}).catch(()=>{}),await ae()}async function Er(e,t){let n=await ft(e,t).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await Wy(n);if(r.length){for(let o of r)await ys(o.repo,o.number,!0).catch(()=>{});return!1}try{return await Ds(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function Wy(e){return(await En().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 _r(e,t){return rf(e,t,"merged")}async function Bs(e,t){let n=await ft(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 Wy(n);if(o.length){let c=[...n.followupPending||[],r];for(let l of o)await qA(l.repo,l.number,c).catch(()=>{});return null}let s=[...n.followupPending||[],r],a=pe(await ne(),n.trackId);if(!a)return null;let i=s[0];return Fc(await Zt(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 rf(e,t,n="merged"){let r=await ft(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 Wy(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await WA(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];if(!await NA(r.repo,r.number).catch(()=>!0))return!1;try{return await qt(r.trackId,cR(a),{kind:"merge-notify"}),!0}catch{return await bs(r.repo,r.number,!0).catch(()=>{}),!1}}function cR(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 ${j$[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 ${j$[i.outcome]??i.outcome}`).join(`
|
|
264
264
|
`),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}
|
|
265
265
|
${s}
|
|
266
|
-
${a}`}async function Wy(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<ec()?!1:Fn(e.name)}async function wl(e,t,n){return!t||e.archivedAt||jr.has(t.state)?!1:(await ee({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 Lt(t.name).catch(()=>{}),t.state="stopped",ai(e))}async function ai(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=ly.get(r.name);if(o)return o;let s=g5(e,r,{freshSession:t,reason:n}).finally(()=>ly.delete(r.name));return ly.set(r.name,s),s}async function qy(e){let n=(await ne()).tracks.find(o=>o.leadBox===e);if(!n||n.archivedAt||n.transferredTo||Date.now()-(uy.get(e)??0)<w5)return!1;let r=await ai(n,{reason:"attach"}).catch(()=>!1);return r?(uy.delete(e),await ae()):uy.set(e,Date.now()),r}async function g5(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||jr.has(t.state)||await Se(t.name))return!1;let o=n===!0,s=o?qp():null,a=o?Zi(yy(t.agent),s,t.name):Vp(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await Fu(a);if(i===la)return Ee(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===ua&&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(_o(t)))}:null,l=Date.now();if(o){let u=_o(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=ds({agent:t.agent,agentCmd:a}),d=await jn(t.name,t.cwd,{agentCmd:a,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=Ho(d.hookToken),t.cliVersion=await u,_o(t)[t.epochs.length-1].cliVersion=t.cliVersion,rs({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a,onBlocked:({message:p})=>W$(e.id,{box:t.name,agent:t.agent,message:p}).catch(()=>{})}),t.state="live",o&&Ee(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,standing:await Ya(t.cwd)}),Ee(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),Ee(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function rf(e,{freshSession:t=!1}={}){let n=await ne(),r=fe(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,hl.delete(e),Ee(r,"unarchived",{}),await ai(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await ae(),Zt(r)}async function Gy(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>gy)throw new Error(`name too long (max ${gy} chars)`);let r=await ne(),o=fe(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 Zt(o);let s=o.name;return o.name=n,Ee(o,"renamed",{from:s,to:n}),await ae(),Zt(o)}async function Ky(){let e=await ne(),t=0;for(let n of e.tracks||[])n.transferredTo&&Md(n,{at:n.transferredAt??Date.now()})&&t++;return t&&await ae(),t}async function Vy(){let e=await ne(),t=new Set((await kn()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!Ue(s)).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(Ue(s))for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Lt(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):Ee(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!jr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await ae(),r}async function of(e,{freshSession:t=!1}={}){let n=await ne(),r=fe(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`);af(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(jr.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await Se(r.leadBox)){let i=await Ne(r.leadBox,{lines:ze}).catch(()=>"");if(uc(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Lt(r.leadBox),o.state="stopped"}let a=await ai(r,t?{freshSession:!0,reason:"loop-fire"}:{});if(await ae(),!a)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Zt(r)}async function zy(e,t,n){let r=await me(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 of(e),await N(r,{type:"box.fullscreen.answered",actor:"user",source:"orchestrator",boxId:r.leadBox,payload:{ts:Date.now(),offerId:t,action:n?"restarted":"dismiss"}}).catch(()=>{}),{ok:!0,action:n?"restarted":"dismiss"}}function L$(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return cp({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function _o(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 Hp(e,t){let n=await ne(),r=fe(n,e);r&&(r.handoff={...r.handoff||{},...t},await ae())}function cR(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 Yy(){let e=await ne(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=cR(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await Se(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=_o(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,y=await ds({agent:r.toAgent});d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:h,cliVersion:y,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.cliVersion=y,l.sessionId=c.sessionId||null,l.pinned=h,l.state="live",n.pendingHandoffHydrate={artifact:Io,at:u},Ee(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,standing:await Ya(l.cwd)}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await ee({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 ee({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Lt(i).catch(()=>{}),a&&await Lt(a).catch(()=>{})}return t&&await ae(),t}async function sf(e,t,n=!0,r=null){let o=Ns.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=fe(await ne(),e);if(!s)throw new Error(`track "${e}" not found`);oi(e,"handoff");let a=await ri(t,n),i=mo(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 Se(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:q$(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return Ns.set(e,l),await Hp(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),y5(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 Hp(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),ee({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),ee({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function y5(e,t,n,r,o){let s=async(P,j={})=>{e.step=P,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${P}`),await Hp(e.trackId,{step:P,toBox:e.toBox}),await ee({flow:"lead-handoff",phase:P,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 ne(),i=fe(a,e.trackId)?.boxes.find(P=>P.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",e.quiescence=null,mo(c))e.checkpoint="skipped-capped";else try{let P=await Td(e.fromBox,{timeoutMs:by,quiesce:{box:i?{...i,name:e.fromBox}:n,reason:e.reason?.code||"lead-handoff",onWait:async j=>{e.wait=j,await s("await-turn",{sessionName:e.fromBox,wait:j})},onResume:async()=>{e.wait=null,await s("checkpoint",{sessionName:e.fromBox})}}});e.quiescence=Fh(P),e.waitedMs=(P.quiescence?.pre?.waitedMs||0)+(P.quiescence?.post?.waitedMs||0)}catch(P){e.checkpoint="reconstructed",e.quiescence=Fh(P),e.waitedMs=P?.quiescence?.pre?.waitedMs||0,e.wait=null,await ee({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:P?.message||String(P)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await Ad(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=ds({agent:o,agentCmd:r}),f=await zh(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let w=bv(f.launchCmd);await wr({cwd:n.cwd,sessionId:f.sessionId,sessionName:e.fromBox,agent:o,agentCmd:Ps(o),launchEcho:w}).catch(P=>{if(P?.code!=="cli-not-running")throw P});let h=nd(await Ne(e.fromBox,{lines:ze}).catch(()=>""),w);if(await ee({flow:"lead-handoff",phase:"hydrate-ready",trackId:e.trackId,sessionName:e.fromBox,toAgent:o,up:to(o,h),paneTail:!0}).catch(()=>{}),!to(o,h)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let P=Vp(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await zh(e.fromBox,{agentCmd:P,agent:c,resumeSessionId:l,pinned:u});let j=await ne(),A=fe(j,e.trackId);if(A){let $=A.boxes.find(L=>L.name===e.fromBox);$&&($.state="live"),Ee(A,"lead-handoff-failed",{box:e.fromBox,fromAgent:c,toAgent:o,reason:e.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:e.startedAt}),await ae()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let y=await ne(),k=fe(y,e.trackId);if(!k)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let g=k.boxes.find(P=>P.name===e.fromBox);if(!g)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let x=Date.now(),S=_o(g),_=S[S.length-1];_.endedAt=x,e.reason&&(_.endReason=e.reason);let M=await p;S.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,cliVersion:M,startedAt:x,endedAt:null}),g.agent=o,g.sessionId=f.sessionId,g.pinned=f.pinned,g.cliVersion=M,g.state="live",g.role="lead",k.pendingHandoffHydrate={artifact:e.statusArtifact||Io,at:x},Ee(k,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:S.length-1,singleBox:!0,forced:f.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,cliVersion:M,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint,quiescence:e.quiescence||void 0,waitedMs:e.waitedMs||void 0,standing:await Ya(g.cwd)}),await ae(),await s("done",{sessionName:e.fromBox}),await Hp(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function Qy(e){let t=Wp.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await ne(),r=fe(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=Ns.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of Wt.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 Wp.set(e,s),b5(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 b5(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await Se(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&mo(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await Td(t.leadBox,{timeoutMs:by,quiesce:{box:n,reason:"transfer-out",onWait:()=>{e.step="await-turn"},onResume:()=>{e.step="checkpoint"}}})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await Ad(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 Gt(t.workspace)){if(!u?.dir)continue;let d={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await Mn(u.dir,["push","origin",o],Xy);let{stdout:p}=await Mn(u.dir,["rev-parse","HEAD"]);d.baseSha=p.trim(),d.pushed=!0}catch(p){d.error=String(p?.message||p).split(`
|
|
267
|
-
`)[0]}s.push(d)}e.step="wip";let a=await Ug(t,{force:!0}),i=await ne(),c=
|
|
268
|
-
`)[0],advanced:null}}}function S5(e){if(typeof e!="string"||!v5.test(e)||e.includes("..")||e.includes("@{")||e.endsWith(".lock"))throw new Error("branch fails the safe-ref check")}async function tb(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&&S5(e.branch),Lp.has(t))throw new Error("an adopt is already running for this Track");Lp.set(t,{startedAt:Date.now()});try{return await T5({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{Lp.delete(t)}}async function T5({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:y=null}){let k=await ri(n,r),g=await ne(),x=fe(g,e);if(x&&!x.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);x&&(await Hg(x).catch(()=>{}),await sR(x),g.tracks.splice(g.tracks.indexOf(x),1),await ae()),await Y.mkdir(ul,{recursive:!0});let S=z.join(ul,e);await Y.rm(S,{recursive:!0,force:!0}).catch(()=>{});let _=null,M=!1,P=[];try{let j=[];if(i)for(let le of Array.isArray(c)?c:[]){let ge=typeof le=="string"?le:le?.repo;if(!ge||!/^[^/\s]+\/[^/\s]+$/.test(ge))continue;let de=await ea(ge)??await ku(ge);await Mn(de,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],Xy),j.push({slug:ge,base:de,name:typeof le=="object"?le?.name??null:null})}if(i&&j.length===1){let{base:le,slug:ge}=j[0];await j$(le,S,i),P.push({base:le,dir:S}),await ml(S);let de=await hs(le);_={dir:S,base:le,branch:i,kind:"worktree",baseRef:de,repo:ge}}else if(i&&j.length>=2){await Y.mkdir(S,{recursive:!0});let le=new Set([".bostrat"]),ge=[];for(let de of j){let te=de.name||z.basename(de.base);le.has(te)&&(te=de.slug.replace("/","-"));for(let Ve=2;le.has(te);Ve++)te=`${z.basename(de.base)}-${Ve}`;le.add(te);let we=z.join(S,te);await j$(de.base,we,i),P.push({base:de.base,dir:we});let Oe=await hs(de.base);ge.push({name:te,dir:we,base:de.base,baseRef:Oe,repo:de.slug})}await si(S,ge,i),_={dir:S,kind:"multi",branch:i,repos:ge}}else M=!0,await Y.mkdir(S,{recursive:!0}),await rw(S),await ml(S),_={dir:S,base:null,branch:null,kind:"repo"};let A=[];for(let le of k5(_,{wip:l,wips:u})){let ge=await x5(le.dir,e);A.push({name:le.name,repo:le.repo,applied:ge.applied,skipped:ge.skipped,...ge.advanced?{advanced:ge.advanced}:{}})}let $=A.some(le=>le.applied),L=A.length===1?A[0].skipped:A.filter(le=>le.skipped).map(le=>`${le.name??le.repo}: ${le.skipped}`).join("; ")||null,H=A.length===1?A[0].advanced??null:null;await y$({id:e,cwd:S}).catch(()=>({restored:!1}));let R=z.join(S,Io),T=null;(await Nu(S)).restored?T="checkpoint":typeof d=="string"&&d.trim()&&(await Y.mkdir(z.dirname(R),{recursive:!0}),await Y.writeFile(R,d),T="brief");let W=new Set(g.tracks.flatMap(le=>(le.boxes||[]).map(ge=>ge.name))),B=null;w&&!W.has(w)&&!await Se(w).catch(()=>null)?B=w:(B=`${t.trim()} lead`,W.has(Uu(B))&&(B=`${t.trim().slice(0,50).trim()} ${e} lead`));let Z=await jn(B,S,{agentCmd:k,agent:n});rs({cwd:Z.cwd,sessionName:Z.name,agent:n,agentCmd:k});let xe=Date.now(),Te=Jy(Z,"lead","live"),_e=(Array.isArray(p)?p:[]).filter(le=>le&&typeof le=="object").map(le=>({...le})),Q=_e[_e.length-1];Q&&Q.endedAt==null&&(Q.endedAt=xe,Q.endReason==null&&(Q.endReason=o==="recover"?"node-offline":"node-transfer"));let Xe=await ds({agent:n,agentCmd:k});_e.push({agent:n,sessionId:Z.sessionId??null,pinned:Z.pinned===!0,cliVersion:Xe,startedAt:xe,endedAt:null}),Te.epochs=_e,Te.cliVersion=Xe;let Pe={id:e,name:t.trim(),cwd:Z.cwd,workspace:_,leadBox:Z.name,boxes:[Te],events:[],createdAt:Number.isFinite(f)?f:xe,...T?{pendingHandoffHydrate:{artifact:Io,at:xe}}:{}};return Ee(Pe,"transferred-in",{box:Z.name,agent:n,fromNode:s,toNode:a,mode:o,reason:h,...o==="recover"?{downSince:Number.isFinite(y)?y:null}:{},branch:_.branch??null,wip:$,...H?{wipAdvanced:H}:{},..._.kind==="multi"&&A.length?{wipRepos:A}:{},epoch:_e.length-1,name:Pe.name,cwd:Pe.cwd,workspace:{base:_.base??null,branch:_.branch??null,kind:_.kind,..._.baseRef?{baseRef:_.baseRef}:{},..._.repo?{repo:_.repo}:{},..._.repos?{repos:_.repos.map(({name:le,base:ge,baseRef:de,repo:te})=>({name:le,base:ge,baseRef:de,repo:te}))}:{}}}),g.tracks.push(Pe),await ae(),{...await Zt(Pe),adopt:{wipApplied:$,wipSkipped:L,...H?{wipAdvanced:H}:{},..._.kind==="multi"?{wipRepos:A}:{},codeless:M,artifactSource:T}}}catch(j){for(let A of P)await $t(A.base,A.dir).catch(()=>{});throw await Y.rm(S,{recursive:!0,force:!0}).catch(()=>{}),j}}async function lR(e,t,n){let r=await ne(),o=fe(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await ae())}async function Za(e,t){e.step=t,await vl(e)}function uR(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 dR(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function I5(e,t,n){if(!dR(n)||Wt.has(t)||!await Se(n.box).catch(()=>null))return!1;try{await Y.access(n.dir)}catch{return!1}let o=await Se(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return Wt.set(t,s),fR(s,o).catch(a=>pR(s,a)),!0}async function nb(){let e=await ne(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Wt.has(r))continue;if(await I5(n,r,o)){t=!0,Ee(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await ee({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=uR(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!jr.has(i.state)&&(i.state="canceled"),Ee(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 ee({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await F$(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||jr.has(r.state)||Wt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?z.join(ni,`${n.id}-${r.subtaskId}`):null;await ee({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await F$(n,r.name,o)}}return t=await $5(e)||t,t&&await ae(),t}async function F$(e,t,n){if(t&&await Lt(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of Gt(e.workspace)){let s=z.join(n,o.name);await $t(o.base,s).then(()=>!0,()=>!1)||await $t(o.dir,s).catch(()=>{})}await Y.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await Se(e.leadBox).catch(()=>null);r?.cwd&&await $t(r.cwd,n).catch(()=>{})}async function $5(e){let t;try{t=await Y.readdir(ni)}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 Wt.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(ni,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of Gt(a.workspace))await $t(i.base,z.join(s,i.name)).catch(()=>{});await Y.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await Se(a.leadBox).catch(()=>null):null;i?.cwd?await $t(i.cwd,s).catch(()=>{}):await Y.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await ee({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function rb(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Ia(n))throw new Error("task (string) required");let s=await ri(t,r),a=mo(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=fe(await ne(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await Se(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=Gt(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 $a(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 $a(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=qp().slice(0,8),p={id:d,trackId:e,trackName:i.name,agent:t,task:Ia(n),skipPermissions:r,reason:q$(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${uf(i.name)}-${d}`,base:null,repos:u,dir:z.join(ni,`${e}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Wt.set(d,p),await vl(p),R5(p,c,s).catch(f=>pR(p,f)),p}async function pR(e,t){e.failedStep=e.step,e.error=t instanceof gl?"canceled":t?.message||String(t),e.step=t instanceof gl?"canceled":"error",e.finishedAt=Date.now(),await sb(e).catch(()=>{}),await vl(e).catch(()=>{}),await N5(e).catch(()=>{})}function df(e){let t=Wt.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 ob(e){let t=Wt.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 R5(e,t,n){if(await Za(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await Ra(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await vl(e)}else e.base=await Ra(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await Za(e,"spawn"),await Y.mkdir(ni,{recursive:!0}),e.repos){await Y.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await ms(a.leadDir,z.join(e.dir,a.name),e.branch,a.base);let s=Gt((fe(await ne(),e.trackId)||{}).workspace);await si(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 ms(t.cwd,e.dir,e.branch,e.base),await ml(e.dir);await O5(e,t);let r=e.trackName.slice(0,38).trim(),o=await jn(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await M5(e,o),await G$(e.trackId,o.name,hq(e.task,!!e.repos),{kind:"subtask"}),await fR(e,t)}async function fR(e,t){await Za(e,"working");let n=z.join(e.dir,jp);if(await D5(n,e),e.canceled)throw new gl;if(await Za(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 tw(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:L$({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await sw(l.leadDir,l.base,e.branch,Xa),l.files=await aw(l.leadDir,l.base,e.branch,Xa);let d=await iw(l.leadDir,l.base,e.branch,Xa);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 tw(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:L$({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await sw(t.cwd,e.base,e.branch,Xa),e.files=await aw(t.cwd,e.base,e.branch,Xa);let s=await iw(t.cwd,e.base,e.branch,Xa);e.applied=s.applied,e.conflicts=s.conflicts}await vl(e);let r=await P5(e,t,n);await Za(e,"resume"),await G$(e.trackId,e.leadBox,wq(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await Za(e,"reap"),await sb(e),e.step="done";let o=Date.now();await lR(e.trackId,e.id,{...lf(e),finishedAt:o}),await C5(e,o),e.finishedAt=o}async function O5(e,t){let n=z.join(e.dir,z.dirname(dl));await Y.mkdir(n,{recursive:!0});let r="";try{r=await Qx(t.cwd,t.sessionId)}catch{}if(!r)try{r=await Y.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}
|
|
266
|
+
${a}`}async function qy(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<ec()?!1:Fn(e.name)}async function wl(e,t,n){return!t||e.archivedAt||jr.has(t.state)?!1:(await ee({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 Lt(t.name).catch(()=>{}),t.state="stopped",ai(e))}async function ai(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=ly.get(r.name);if(o)return o;let s=b5(e,r,{freshSession:t,reason:n}).finally(()=>ly.delete(r.name));return ly.set(r.name,s),s}async function Gy(e){let n=(await ne()).tracks.find(o=>o.leadBox===e);if(!n||n.archivedAt||n.transferredTo||Date.now()-(uy.get(e)??0)<y5)return!1;let r=await ai(n,{reason:"attach"}).catch(()=>!1);return r?(uy.delete(e),await ae()):uy.set(e,Date.now()),r}async function b5(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||jr.has(t.state)||await Se(t.name))return!1;let o=n===!0,s=o?Gp():null,a=o?Zi(yy(t.agent),s,t.name):zp(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await Uu(a);if(i===la)return Ee(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===ua&&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(_o(t)))}:null,l=Date.now();if(o){let u=_o(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=ds({agent:t.agent,agentCmd:a}),d=await jn(t.name,t.cwd,{agentCmd:a,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=Ho(d.hookToken),t.cliVersion=await u,_o(t)[t.epochs.length-1].cliVersion=t.cliVersion,rs({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a,onBlocked:({message:p})=>G$(e.id,{box:t.name,agent:t.agent,message:p}).catch(()=>{})}),t.state="live",o&&Ee(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,standing:await Ya(t.cwd)}),Ee(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),Ee(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function vl(e,{freshSession:t=!1,reason:n=null}={}){let r=await ne(),o=pe(r,e);if(!o)throw new Error(`track "${e}" not found`);if(!o.archivedAt)throw new Error(`track "${e}" is not archived`);return delete o.archivedAt,hl.delete(e),Ee(o,"unarchived",n?{reason:n}:{}),await ai(o,t?{freshSession:!0,reason:"loop-fire"}:{}),await ae(),Zt(o)}async function Ky(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>gy)throw new Error(`name too long (max ${gy} chars)`);let r=await ne(),o=pe(r,e);if(!o)throw new Error(`track "${e}" not found`);if(String(o.name||"").startsWith("\u21BB"))throw new Error("loop run Tracks are named by their loop \u2014 rename the loop on the Loops tab");if(n===o.name)return Zt(o);let s=o.name;return o.name=n,Ee(o,"renamed",{from:s,to:n}),await ae(),Zt(o)}async function Vy(){let e=await ne(),t=0;for(let n of e.tracks||[])n.transferredTo&&Cd(n,{at:n.transferredAt??Date.now()})&&t++;return t&&await ae(),t}async function zy(){let e=await ne(),t=new Set((await kn()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!Ue(s)).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(Ue(s))for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Lt(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):Ee(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!jr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await ae(),r}async function of(e,{freshSession:t=!1}={}){let n=await ne(),r=pe(n,e);if(!r)throw new Error(`track "${e}" not found`);if(r.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before restarting the lead`);af(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(jr.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await Se(r.leadBox)){let i=await Ne(r.leadBox,{lines:ze}).catch(()=>"");if(uc(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Lt(r.leadBox),o.state="stopped"}let a=await ai(r,t?{freshSession:!0,reason:"loop-fire"}:{});if(await ae(),!a)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Zt(r)}async function Jy(e,t,n){let r=await me(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 of(e),await N(r,{type:"box.fullscreen.answered",actor:"user",source:"orchestrator",boxId:r.leadBox,payload:{ts:Date.now(),offerId:t,action:n?"restarted":"dismiss"}}).catch(()=>{}),{ok:!0,action:n?"restarted":"dismiss"}}function F$(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return lp({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function _o(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 Wp(e,t){let n=await ne(),r=pe(n,e);r&&(r.handoff={...r.handoff||{},...t},await ae())}function lR(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 Xy(){let e=await ne(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=lR(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await Se(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=_o(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,y=await ds({agent:r.toAgent});d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:h,cliVersion:y,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.cliVersion=y,l.sessionId=c.sessionId||null,l.pinned=h,l.state="live",n.pendingHandoffHydrate={artifact:Io,at:u},Ee(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,standing:await Ya(l.cwd)}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await ee({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 ee({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Lt(i).catch(()=>{}),a&&await Lt(a).catch(()=>{})}return t&&await ae(),t}async function sf(e,t,n=!0,r=null){let o=Ns.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=pe(await ne(),e);if(!s)throw new Error(`track "${e}" not found`);oi(e,"handoff");let a=await ri(t,n),i=mo(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 Se(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:K$(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return Ns.set(e,l),await Wp(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),k5(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 Wp(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),ee({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),ee({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function k5(e,t,n,r,o){let s=async(P,j={})=>{e.step=P,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${P}`),await Wp(e.trackId,{step:P,toBox:e.toBox}),await ee({flow:"lead-handoff",phase:P,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 ne(),i=pe(a,e.trackId)?.boxes.find(P=>P.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",e.quiescence=null,mo(c))e.checkpoint="skipped-capped";else try{let P=await Ad(e.fromBox,{timeoutMs:by,quiesce:{box:i?{...i,name:e.fromBox}:n,reason:e.reason?.code||"lead-handoff",onWait:async j=>{e.wait=j,await s("await-turn",{sessionName:e.fromBox,wait:j})},onResume:async()=>{e.wait=null,await s("checkpoint",{sessionName:e.fromBox})}}});e.quiescence=Fh(P),e.waitedMs=(P.quiescence?.pre?.waitedMs||0)+(P.quiescence?.post?.waitedMs||0)}catch(P){e.checkpoint="reconstructed",e.quiescence=Fh(P),e.waitedMs=P?.quiescence?.pre?.waitedMs||0,e.wait=null,await ee({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:P?.message||String(P)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await Ed(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=ds({agent:o,agentCmd:r}),f=await zh(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let w=xv(f.launchCmd);await wr({cwd:n.cwd,sessionId:f.sessionId,sessionName:e.fromBox,agent:o,agentCmd:Ps(o),launchEcho:w}).catch(P=>{if(P?.code!=="cli-not-running")throw P});let h=rd(await Ne(e.fromBox,{lines:ze}).catch(()=>""),w);if(await ee({flow:"lead-handoff",phase:"hydrate-ready",trackId:e.trackId,sessionName:e.fromBox,toAgent:o,up:to(o,h),paneTail:!0}).catch(()=>{}),!to(o,h)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let P=zp(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await zh(e.fromBox,{agentCmd:P,agent:c,resumeSessionId:l,pinned:u});let j=await ne(),A=pe(j,e.trackId);if(A){let $=A.boxes.find(L=>L.name===e.fromBox);$&&($.state="live"),Ee(A,"lead-handoff-failed",{box:e.fromBox,fromAgent:c,toAgent:o,reason:e.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:e.startedAt}),await ae()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let y=await ne(),k=pe(y,e.trackId);if(!k)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let g=k.boxes.find(P=>P.name===e.fromBox);if(!g)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let x=Date.now(),S=_o(g),_=S[S.length-1];_.endedAt=x,e.reason&&(_.endReason=e.reason);let M=await p;S.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,cliVersion:M,startedAt:x,endedAt:null}),g.agent=o,g.sessionId=f.sessionId,g.pinned=f.pinned,g.cliVersion=M,g.state="live",g.role="lead",k.pendingHandoffHydrate={artifact:e.statusArtifact||Io,at:x},Ee(k,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:S.length-1,singleBox:!0,forced:f.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,cliVersion:M,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint,quiescence:e.quiescence||void 0,waitedMs:e.waitedMs||void 0,standing:await Ya(g.cwd)}),await ae(),await s("done",{sessionName:e.fromBox}),await Wp(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function eb(e){let t=qp.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await ne(),r=pe(n,e);if(!r)throw new Error(`track "${e}" not found`);if(r.archivedAt)throw new Error("track is archived \u2014 restore it before moving it");if(r.kind==="setup")throw new Error("setup boxes don't move");if(r.transferredTo)throw new Error(`track already transferred to ${r.transferredTo}`);let o=Ns.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of Wt.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 qp.set(e,s),x5(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 x5(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await Se(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&mo(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await Ad(t.leadBox,{timeoutMs:by,quiesce:{box:n,reason:"transfer-out",onWait:()=>{e.step="await-turn"},onResume:()=>{e.step="checkpoint"}}})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await Ed(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 Gt(t.workspace)){if(!u?.dir)continue;let d={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await Mn(u.dir,["push","origin",o],Zy);let{stdout:p}=await Mn(u.dir,["rev-parse","HEAD"]);d.baseSha=p.trim(),d.pushed=!0}catch(p){d.error=String(p?.message||p).split(`
|
|
267
|
+
`)[0]}s.push(d)}e.step="wip";let a=await Ug(t,{force:!0}),i=await ne(),c=pe(i,t.id),l=c?.boxes.find(u=>u.name===c.leadBox)||null;l&&_o(l),await ae(),e.result={name:c?.name??t.name,agent:l?.agent??null,leadBox:c?.leadBox??t.leadBox,createdAt:c?.createdAt??null,branch:o,repos:s.filter(u=>u.pushed).map(({name:u,repo:d,baseSha:p})=>({name:u,repo:d,baseSha:p})),unpushed:s.filter(u=>!u.pushed).map(({name:u,repo:d,error:p})=>({name:u,repo:d,error:p})),wip:a.pushed[0]?{ref:a.pushed[0].ref,sha:a.pushed[0].sha}:null,wips:a.pushed,wipSkipped:a.skipped,checkpoint:e.checkpoint,epochs:l?.epochs??[],codeless:!o||!s.some(u=>u.pushed)},e.step="ready",e.finishedAt=Date.now()}function af(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 cf(e,{toNode:t,mode:n="transfer"}={}){if(!t||typeof t!="string")throw new Error("toNode required");let r=await ne(),o=pe(r,e);if(!o)throw new Error(`track "${e}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};My(e),await Uy(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();Cd(o,{at:s,reason:n==="recover"?"node-offline":"node-transfer"});let a=await Hg(o,{ts:s}),i=a.pushed[0]??null;return o.transferredTo=t,o.transferredAt=s,Ee(o,"transferred-out",{toNode:t,mode:n,box:o.leadBox,...i?{rescueRef:i.ref,rescueSha:i.sha}:{},...a.pushed.length?{rescues:a.pushed}:{}}),await ae(),{ok:!0,toNode:t,rescue:i?{ref:i.ref,sha:i.sha}:null,rescues:a.pushed}}async function tb(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await cf(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 U$(e,t,n){try{await Ec(e,t,n)}catch{await Mn(e,["worktree","prune"]).catch(()=>{}),await Ec(e,t,n)}return t}function v5(e,{wip:t=null,wips:n=null}={}){let r=Gt(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 S5(e,t){try{await Mn(e,["fetch","origin",Fg(t)],Zy);let{stdout:n}=await Mn(e,["rev-parse","FETCH_HEAD^"]),{stdout:r}=await Mn(e,["rev-parse","HEAD"]),o=null,s=n.trim()===r.trim();return s||await Mn(e,["merge-base","--is-ancestor",r.trim(),n.trim()]).then(()=>!0,()=>!1)&&(await Mn(e,["reset","--hard",n.trim()]),o={from:r.trim(),to:n.trim()},s=!0),s?(await Mn(e,["read-tree","--reset","-u","FETCH_HEAD"]),await Mn(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(`
|
|
268
|
+
`)[0],advanced:null}}}function A5(e){if(typeof e!="string"||!T5.test(e)||e.includes("..")||e.includes("@{")||e.endsWith(".lock"))throw new Error("branch fails the safe-ref check")}async function nb(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&&A5(e.branch),jp.has(t))throw new Error("an adopt is already running for this Track");jp.set(t,{startedAt:Date.now()});try{return await E5({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{jp.delete(t)}}async function E5({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:y=null}){let k=await ri(n,r),g=await ne(),x=pe(g,e);if(x&&!x.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);x&&(await Hg(x).catch(()=>{}),await aR(x),g.tracks.splice(g.tracks.indexOf(x),1),await ae()),await Y.mkdir(ul,{recursive:!0});let S=z.join(ul,e);await Y.rm(S,{recursive:!0,force:!0}).catch(()=>{});let _=null,M=!1,P=[];try{let j=[];if(i)for(let le of Array.isArray(c)?c:[]){let ge=typeof le=="string"?le:le?.repo;if(!ge||!/^[^/\s]+\/[^/\s]+$/.test(ge))continue;let de=await ea(ge)??await xu(ge);await Mn(de,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],Zy),j.push({slug:ge,base:de,name:typeof le=="object"?le?.name??null:null})}if(i&&j.length===1){let{base:le,slug:ge}=j[0];await U$(le,S,i),P.push({base:le,dir:S}),await ml(S);let de=await hs(le);_={dir:S,base:le,branch:i,kind:"worktree",baseRef:de,repo:ge}}else if(i&&j.length>=2){await Y.mkdir(S,{recursive:!0});let le=new Set([".bostrat"]),ge=[];for(let de of j){let te=de.name||z.basename(de.base);le.has(te)&&(te=de.slug.replace("/","-"));for(let Ve=2;le.has(te);Ve++)te=`${z.basename(de.base)}-${Ve}`;le.add(te);let we=z.join(S,te);await U$(de.base,we,i),P.push({base:de.base,dir:we});let Oe=await hs(de.base);ge.push({name:te,dir:we,base:de.base,baseRef:Oe,repo:de.slug})}await si(S,ge,i),_={dir:S,kind:"multi",branch:i,repos:ge}}else M=!0,await Y.mkdir(S,{recursive:!0}),await rw(S),await ml(S),_={dir:S,base:null,branch:null,kind:"repo"};let A=[];for(let le of v5(_,{wip:l,wips:u})){let ge=await S5(le.dir,e);A.push({name:le.name,repo:le.repo,applied:ge.applied,skipped:ge.skipped,...ge.advanced?{advanced:ge.advanced}:{}})}let $=A.some(le=>le.applied),L=A.length===1?A[0].skipped:A.filter(le=>le.skipped).map(le=>`${le.name??le.repo}: ${le.skipped}`).join("; ")||null,H=A.length===1?A[0].advanced??null:null;await k$({id:e,cwd:S}).catch(()=>({restored:!1}));let R=z.join(S,Io),T=null;(await Pu(S)).restored?T="checkpoint":typeof d=="string"&&d.trim()&&(await Y.mkdir(z.dirname(R),{recursive:!0}),await Y.writeFile(R,d),T="brief");let W=new Set(g.tracks.flatMap(le=>(le.boxes||[]).map(ge=>ge.name))),B=null;w&&!W.has(w)&&!await Se(w).catch(()=>null)?B=w:(B=`${t.trim()} lead`,W.has(Hu(B))&&(B=`${t.trim().slice(0,50).trim()} ${e} lead`));let Z=await jn(B,S,{agentCmd:k,agent:n});rs({cwd:Z.cwd,sessionName:Z.name,agent:n,agentCmd:k});let xe=Date.now(),Te=Yy(Z,"lead","live"),_e=(Array.isArray(p)?p:[]).filter(le=>le&&typeof le=="object").map(le=>({...le})),Q=_e[_e.length-1];Q&&Q.endedAt==null&&(Q.endedAt=xe,Q.endReason==null&&(Q.endReason=o==="recover"?"node-offline":"node-transfer"));let Xe=await ds({agent:n,agentCmd:k});_e.push({agent:n,sessionId:Z.sessionId??null,pinned:Z.pinned===!0,cliVersion:Xe,startedAt:xe,endedAt:null}),Te.epochs=_e,Te.cliVersion=Xe;let Pe={id:e,name:t.trim(),cwd:Z.cwd,workspace:_,leadBox:Z.name,boxes:[Te],events:[],createdAt:Number.isFinite(f)?f:xe,...T?{pendingHandoffHydrate:{artifact:Io,at:xe}}:{}};return Ee(Pe,"transferred-in",{box:Z.name,agent:n,fromNode:s,toNode:a,mode:o,reason:h,...o==="recover"?{downSince:Number.isFinite(y)?y:null}:{},branch:_.branch??null,wip:$,...H?{wipAdvanced:H}:{},..._.kind==="multi"&&A.length?{wipRepos:A}:{},epoch:_e.length-1,name:Pe.name,cwd:Pe.cwd,workspace:{base:_.base??null,branch:_.branch??null,kind:_.kind,..._.baseRef?{baseRef:_.baseRef}:{},..._.repo?{repo:_.repo}:{},..._.repos?{repos:_.repos.map(({name:le,base:ge,baseRef:de,repo:te})=>({name:le,base:ge,baseRef:de,repo:te}))}:{}}}),g.tracks.push(Pe),await ae(),{...await Zt(Pe),adopt:{wipApplied:$,wipSkipped:L,...H?{wipAdvanced:H}:{},..._.kind==="multi"?{wipRepos:A}:{},codeless:M,artifactSource:T}}}catch(j){for(let A of P)await $t(A.base,A.dir).catch(()=>{});throw await Y.rm(S,{recursive:!0,force:!0}).catch(()=>{}),j}}async function uR(e,t,n){let r=await ne(),o=pe(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await ae())}async function Za(e,t){e.step=t,await Sl(e)}function dR(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 pR(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function R5(e,t,n){if(!pR(n)||Wt.has(t)||!await Se(n.box).catch(()=>null))return!1;try{await Y.access(n.dir)}catch{return!1}let o=await Se(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return Wt.set(t,s),mR(s,o).catch(a=>fR(s,a)),!0}async function rb(){let e=await ne(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Wt.has(r))continue;if(await R5(n,r,o)){t=!0,Ee(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await ee({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=dR(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!jr.has(i.state)&&(i.state="canceled"),Ee(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 ee({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await H$(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||jr.has(r.state)||Wt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?z.join(ni,`${n.id}-${r.subtaskId}`):null;await ee({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await H$(n,r.name,o)}}return t=await O5(e)||t,t&&await ae(),t}async function H$(e,t,n){if(t&&await Lt(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of Gt(e.workspace)){let s=z.join(n,o.name);await $t(o.base,s).then(()=>!0,()=>!1)||await $t(o.dir,s).catch(()=>{})}await Y.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await Se(e.leadBox).catch(()=>null);r?.cwd&&await $t(r.cwd,n).catch(()=>{})}async function O5(e){let t;try{t=await Y.readdir(ni)}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 Wt.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(ni,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of Gt(a.workspace))await $t(i.base,z.join(s,i.name)).catch(()=>{});await Y.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await Se(a.leadBox).catch(()=>null):null;i?.cwd?await $t(i.cwd,s).catch(()=>{}):await Y.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await ee({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function ob(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Ia(n))throw new Error("task (string) required");let s=await ri(t,r),a=mo(t);if(a){let f=new Error(`can't delegate to ${t} \u2014 its ${a.window} spend limit is reached ($${a.spentUsd.toFixed(2)} of $${a.limitUsd.toFixed(2)})`);throw f.usageCap=a,f}let i=pe(await ne(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await Se(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=Gt(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 $a(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 $a(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=Gp().slice(0,8),p={id:d,trackId:e,trackName:i.name,agent:t,task:Ia(n),skipPermissions:r,reason:K$(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${uf(i.name)}-${d}`,base:null,repos:u,dir:z.join(ni,`${e}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Wt.set(d,p),await Sl(p),M5(p,c,s).catch(f=>fR(p,f)),p}async function fR(e,t){e.failedStep=e.step,e.error=t instanceof gl?"canceled":t?.message||String(t),e.step=t instanceof gl?"canceled":"error",e.finishedAt=Date.now(),await ab(e).catch(()=>{}),await Sl(e).catch(()=>{}),await D5(e).catch(()=>{})}function df(e){let t=Wt.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 sb(e){let t=Wt.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 M5(e,t,n){if(await Za(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await Ra(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await Sl(e)}else e.base=await Ra(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await Za(e,"spawn"),await Y.mkdir(ni,{recursive:!0}),e.repos){await Y.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await ms(a.leadDir,z.join(e.dir,a.name),e.branch,a.base);let s=Gt((pe(await ne(),e.trackId)||{}).workspace);await si(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 ms(t.cwd,e.dir,e.branch,e.base),await ml(e.dir);await C5(e,t);let r=e.trackName.slice(0,38).trim(),o=await jn(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await N5(e,o),await V$(e.trackId,o.name,wq(e.task,!!e.repos),{kind:"subtask"}),await mR(e,t)}async function mR(e,t){await Za(e,"working");let n=z.join(e.dir,Fp);if(await L5(n,e),e.canceled)throw new gl;if(await Za(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 tw(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:F$({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await sw(l.leadDir,l.base,e.branch,Xa),l.files=await aw(l.leadDir,l.base,e.branch,Xa);let d=await iw(l.leadDir,l.base,e.branch,Xa);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 tw(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:F$({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await sw(t.cwd,e.base,e.branch,Xa),e.files=await aw(t.cwd,e.base,e.branch,Xa);let s=await iw(t.cwd,e.base,e.branch,Xa);e.applied=s.applied,e.conflicts=s.conflicts}await Sl(e);let r=await B5(e,t,n);await Za(e,"resume"),await V$(e.trackId,e.leadBox,gq(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await Za(e,"reap"),await ab(e),e.step="done";let o=Date.now();await uR(e.trackId,e.id,{...lf(e),finishedAt:o}),await P5(e,o),e.finishedAt=o}async function C5(e,t){let n=z.join(e.dir,z.dirname(dl));await Y.mkdir(n,{recursive:!0});let r="";try{r=await tv(t.cwd,t.sessionId)}catch{}if(!r)try{r=await Y.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}
|
|
269
269
|
|
|
270
270
|
**Delegated task:** ${e.task}
|
|
271
271
|
|
|
@@ -276,15 +276,15 @@ ${a}`}async function Wy(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<
|
|
|
276
276
|
## Conversation so far (lead \u2192 you)
|
|
277
277
|
|
|
278
278
|
${r}
|
|
279
|
-
`;await Y.writeFile(z.join(e.dir,dl),o)}async function
|
|
279
|
+
`;await Y.writeFile(z.join(e.dir,dl),o)}async function N5(e,t){let n=await ne(),r=pe(n,e.trackId);r&&(r.boxes.push({name:t.name,agent:t.agent,role:"subtask",state:"live",cwd:t.cwd,sessionId:t.sessionId,hookTokenHash:Ho(t.hookToken),createdAt:Date.now(),subtaskId:e.id,branch:e.branch,task:e.task}),Ee(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]||{},...lf(e)},await ae())}async function P5(e,t=e.finishedAt){let n=await ne(),r=pe(n,e.trackId);if(!r)return;let o=r.boxes.find(s=>s.subtaskId===e.id);o&&(o.state=e.conflicts.length?"conflict":"merged",o.stat=e.stat),Ee(r,"subtask-merged",{from:e.box,fromAgent:e.agent,task:e.task,subtaskId:e.id,stat:e.stat,conflicts:e.conflicts.length,files:e.files,durationMs:t?t-e.startedAt:null}),await ae()}async function D5(e){let t=await ne(),n=pe(t,e.trackId);if(!n)return;let r=e.canceled||e.error==="canceled",o=n.boxes.find(s=>s.subtaskId===e.id);o&&(o.state=r?"canceled":"failed"),Ee(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]||{},...lf(e)},await ae()}async function B5(e,t,n){let r=`.bostrat/handoff/subtask-${e.id}.md`;try{let o=z.join(t.cwd,r);await Y.mkdir(z.dirname(o),{recursive:!0}),await Y.copyFile(n,o)}catch{}return r}async function ab(e){if(e.dir&&await Ru(e.dir).catch(()=>{}),e.box&&await Lt(e.box).catch(()=>{}),e.repos&&e.dir){for(let t of e.repos)await $t(t.leadDir,z.join(e.dir,t.name)).catch(()=>{});await Y.rm(e.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(e.dir){let n=(await Se(e.leadBox).catch(()=>null))?.cwd;n&&await $t(n,e.dir).catch(()=>{})}}async function L5(e,t){let n=(t.startedAt||Date.now())+_5;for(;;){if(t.canceled||t.forceMerge)return;try{await Y.access(e);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await rr(I5)}}function rr(e){return new Promise(t=>setTimeout(t,e))}var Kp,nr,W$,fq,ul,mq,Vp,yl,Eo,Io,by,hq,Fp,dl,wq,gq,Ht,oy,pe,sy,Ee,ay,iy,kq,jr,P$,Sq,Tq,Aq,Eq,D$,il,Rq,Oq,my,X$,Pq,Ms,Z$,Uq,B$,Wq,qq,Gq,Kq,Vq,Qa,pl,Xq,Qq,e5,r5,Dy,wy,xl,Tt,On,Hp,fl,rR,l5,cl,ll,u5,cy,f5,hl,m5,h5,j$,ly,y5,uy,gy,Yy,Ns,ii,Zy,Mn,qp,Qy,T5,jp,_5,I5,ni,Xa,Wt,$5,Ls,lf,Sl,uf,gl,Ir=G(()=>{Wi();dt();Hh();ET();os();zr();qo();NT();vt();yn();Rd();BT();Ys();Ju();xd();es();_h();$h();oh();LT();mc();ch();Xu();gh();vn();Md();Gr();an();pc();Bd();lA();fw();R_();fd();dd();M_();ng();be();sg();dp();Ap();va();gs();Qc();Va();Og();Cp();Lg();Gn();yp();f$();Wg();x$();Du();A$();Kg();Qg();ty();ry();Kp=pq(dq),nr=process.env.BOSTRAT_STATE_DIR||z.join(process.env.HOME||"/tmp",".bostrat"),W$=z.join(nr,"tracks.json"),fq=z.join(nr,"events.jsonl"),ul=z.join(nr,"workspaces"),mq=process.env.BOSTRAT_DEFAULT_REPO||null,Vp={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"}},yl=Object.keys(Vp);Eo={at:0,value:null};Io=".bostrat/handoff/latest.md",by=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;hq=/\(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\.\)$/;Fp=".bostrat/handoff/subtask-result.md",dl=".bostrat/handoff/lead-context.md",wq=(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 ${dl} \u2014 read that first for context. Your scoped task:
|
|
280
280
|
|
|
281
281
|
${e}
|
|
282
282
|
|
|
283
|
-
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 ${
|
|
283
|
+
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 ${Fp} (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 ${dl} \u2014 read that first for context. Your scoped task:
|
|
284
284
|
|
|
285
285
|
${e}
|
|
286
286
|
|
|
287
|
-
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 ${
|
|
287
|
+
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 ${Fp} 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.`,gq=(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."),Ht=null,oy=null;pe=(e,t)=>e.tracks.find(n=>n.id===t)||null,sy=Promise.resolve();Ee=(e,t,n={})=>{let r={at:Date.now(),type:t,...n};return e.events.push(r),yq(e.id,r),pA(e,r),r};mm(async({sessionName:e,from:t,to:n,version:r,status:o,error:s})=>{let a=await ne(),i=e?a.tracks.find(l=>l.leadBox===e||(l.boxes||[]).some(u=>u.name===e)):null,c=i?[i]:o==="failed"?a.tracks.filter(l=>!l.archivedAt&&!l.transferredTo&&l.leadBox&&(l.boxes||[]).some(u=>u.name===l.leadBox&&u.agent==="codex")):[];if(c.length){for(let l of c)Ee(l,"codex-update",{box:e||l.leadBox,from:t,to:r||n,status:o,...s?{error:s}:{}});await ae().catch(()=>{})}});ay=z.join(nr,"plane-link","update-notice.json");iy=(e,t,n)=>t!=null&&n.some(r=>e===r.box&&t>=r.from&&t<=r.to),kq=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);jr=new Set(["merged","conflict","canceled","failed","handed-off"]),P$=new Map;Sq=5e3;Tq={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};Aq=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];Eq=10*6e4;D$=400;il=new Map,Rq=Math.max(1e3,Number(process.env.BOSTRAT_ANSWERING_TTL_MS)||15e3),Oq=[100,200,300],my=new Map,X$=e=>uq("sha1").update(String(e||"")).digest("hex");Pq=1500,Ms=new Map;Z$="Hello. I've just logged in.";Uq=e=>!e||e==="slack-reply";B$="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.";Wq=4e3,qq=3,Gq=[300,350,400],Kq=1e3,Vq=12,Qa=new Map,pl=new Map;Xq=[250,400,500,600];Qq=3e4,e5=[350,650,900];r5=12e4;Dy=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}))});wy=new Map,xl=e=>wy.get(e)||(Ht?pe(Ht,e)?.reshape:null)||null,Tt=(e,t=409)=>Object.assign(new Error(e),{status:t});On=e=>Y.access(e).then(()=>!0,()=>!1),Hp=(e,t,n)=>Kp("git",["-C",e,"worktree","move","--end-of-options",t,n],{timeout:6e4});fl="# bostrat workspace",rR=`${fl}
|
|
288
288
|
|
|
289
289
|
TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
|
|
290
290
|
before substantive work \u2014 it is the shared cross-provider memory for this repo
|
|
@@ -684,16 +684,16 @@ for the same follow-up at the same moment twice; run bostrat-followup ls first
|
|
|
684
684
|
specifics (no PR numbers, URLs, SHAs, tags \u2014 the card supplies them), and HOLD
|
|
685
685
|
HERE after proposing until the operator answers the card. Bostrat never runs a
|
|
686
686
|
follow-up on its own; the operator's tap is the gate.
|
|
687
|
-
`,
|
|
688
|
-
`)),
|
|
687
|
+
`,l5=rR,cl=rR+Xv;ll=e=>typeof e=="string"&&(e===fl||e.startsWith(`${fl}
|
|
688
|
+
`)),u5="# bostrat multi-repo workspace";cy=cl.replace(`${fl}
|
|
689
689
|
`,`${fl}
|
|
690
690
|
|
|
691
691
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
692
692
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
693
693
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
694
694
|
When working anywhere else, ignore everything below.
|
|
695
|
-
`);d5=new Set(["claude","codex","cursor"]);hl=new Set,p5=e=>hl.has(e);f5=9e4;B$={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};ly=new Map;w5=6e4,uy=new Map;gy=120;Jy=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:Ho(e.hookToken),createdAt:Date.now()});Ns=new Map,ii=e=>Ns.get(e)||(Ht?fe(Ht,e)?.handoff:null)||null;Xy=6e4,Mn=(e,t,n=15e3)=>Gp("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),Wp=new Map,Zy=e=>Wp.get(e)||null;v5=/^[A-Za-z0-9][A-Za-z0-9._/-]{0,200}$/;Lp=new Map;A5=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,E5=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,ni=z.join(nr,"worktrees"),Xa=[dl,jp],Wt=new Map,_5=e=>Wt.get(e)||(Ht?Ht.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,Ls=e=>{let t=new Map([...Wt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Ht?fe(Ht,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)},lf=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});vl=e=>lR(e.trackId,e.id,lf(e));uf=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",gl=class extends Error{}});dt();import Re from"node:path";import Et from"node:fs";import A3 from"node:readline";import{fileURLToPath as E3}from"node:url";import nn from"express";import rN from"node:http";import _3 from"node:https";import eu from"node:os";import{randomUUID as I3}from"node:crypto";import{WebSocketServer as $3}from"ws";dt();import i1 from"node-pty";async function Ox(e,t,n={}){if(!je(t)){Gu(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await Zr()){Gu(e,`host tmux server not running on ${zt}`),e.close(1011,"tmux server unavailable");return}if(!await Se(t)){let a=!1;if(typeof n.revive=="function"&&(Ku(e,{type:"notice",message:"Resuming the lead box\u2026"}),a=await n.revive().catch(()=>!1)),!a||!await Se(t)){Gu(e,`session "${t}" not found`),e.close(1008,"session not found");return}if(e.readyState!==e.OPEN)return}let r=Rx(n.cols,120,Mx),o=Rx(n.rows,32,Cx),s;try{s=i1.spawn(oe,["-S",zt,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){Gu(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&Ku(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(Ku(e,{type:"exit",code:a??null,signal:i??null}),e.close(1e3,"tmux exited"))}),e.on("message",a=>{let i;try{i=JSON.parse(a.toString())}catch{return}if(!(!i||typeof i!="object")){if(i.type==="data"&&typeof i.data=="string")s.write(i.data);else if(i.type==="resize"&&d1(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{}u1(t)})}var c1=6,l1=250;async function u1(e,t=c1,n=l1){for(let r=0;r<t;r++){if(await Xo(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var Mx=20,Cx=6;function d1(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=Mx&&Number(t)>=Cx}function Rx(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function Ku(e,t){try{e.send(JSON.stringify(t))}catch{}}function Gu(e,t){Ku(e,{type:"error",message:t})}function Nx(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}}vn();zu();Hh();Ir();import{createRequire as R3}from"node:module";var B5=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function ab({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(B5)&&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}}Qg();be();Qc();Gn();fp();an();var pf=60*6e4,L5=1440*6e4,j5=new Set(["merged","closed","lost"]),Sl=(e,t,n)=>Number.isFinite(Number(e))&&n-Number(e)<=t;function F5({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)<=xg&&s.push("ship"),r>0&&s.push("create-net");for(let a of n||[]){if(!a||j5.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&&Sl(a.ciFix.delegatedAt,pf,o)&&s.push(`ci-fix#${l}`),a.reconcile&&Sl(a.reconcile.delegatedAt,pf,o)&&s.push(`reconcile#${l}`),a.ciFixAwait&&Sl(a.ciFixAwait.since,pf,o)&&s.push(`ci-fix-await#${l}`),a.reconcileAwait&&Sl(a.reconcileAwait.since,pf,o)&&s.push(`reconcile-await#${l}`);let u=a.autoMerge?.armedAt??a.armedAt??null;a.trackId===e&&(a.autoMerge||a.armedAt)&&Sl(u,L5,o)&&s.push(`auto-merge#${l}`)}return s}var ff=new Map;Ze.on("event",e=>{e?.trackId&&ff.delete(e.trackId)});async function U5(e){if(ff.has(e.id))return ff.get(e.id);let t=await K(e).catch(()=>[]),n=qa(t,-1/0);return ff.set(e.id,n),n}async function hR(e,{now:t=Date.now()}={}){let n=new Map,r=await En().catch(()=>[]);for(let o of e||[]){if(!o||Ue(o))continue;let s=F5({trackId:o.id,shipPending:await U5(o),links:r,observers:zE(o.id),now:t});s.length&&n.set(o.id,s)}return n}Kg();import _R from"node:fs";import Ro from"node:fs/promises";import aG from"node:os";import kt from"node:path";import{spawn as iG}from"node:child_process";import{createRequire as cG}from"node:module";be();Dd();import ci from"node:fs";import Al from"node:fs/promises";import q5 from"node:http";import Ot from"node:path";import hf from"node:os";import{createRequire as G5}from"node:module";import cb from"ws";import H5 from"node:path";import W5 from"node:os";var Tl=process.env.BOSTRAT_STATE_DIR||H5.join(process.env.HOME||W5.tmpdir(),".bostrat");function ib(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 wR(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=ib(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function gR(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}km();var Cn=(()=>{try{return G5(import.meta.url)("../package.json").version||null}catch{return null}})(),ub=process.env.BOSTRAT_STATE_DIR||Ot.join(process.env.HOME||hf.tmpdir(),".bostrat"),TR=Ot.join(ub,"plane-link","removed.json");function yR(e){return typeof e!="string"||e.length<8?"\u2022\u2022\u2022\u2022":`${e.slice(0,6)}\u2026${e.slice(-4)}`}function K5(e=TR){try{let t=JSON.parse(ci.readFileSync(e,"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function js({env:e=process.env,pkgDir:t=Ot.resolve(Ot.dirname(new URL(import.meta.url).pathname),".."),exists:n=ci.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(Ot.join(t,"..",".git"))?"git-checkout":"dev"}function El(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 mf(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 V5({home:e=process.env.HOME||hf.homedir(),env:t=process.env}={}){let n=r=>{try{return ci.statSync(r),!0}catch{return!1}};return{claude:n(Ot.join(e,".claude",".credentials.json")),codex:n(Ot.join(t.CODEX_HOME||Ot.join(e,".codex"),"auth.json")),cursor:n(Ot.join(t.XDG_CONFIG_HOME||Ot.join(e,".config"),"cursor","auth.json"))}}var mt=1,bR=1e3,kR=3e4,z5=6e4,J5=1500,Y5=500,$o=100,X5=2e3,Z5=25e3,Q5=2.5,eG=6e4,AR=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},tG=AR(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),nG=3e4,xR=10*6e4,rG=400,vR=250,oG=AR(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),lb=4,sG=6e4,SR=6e4;function ER({url:e,token:t,nodeId:n=hf.hostname(),name:r=hf.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,deliverySample:c=null,onHashKey:l=null,onViewBase:u=null,metricsMs:d=sG,onUpdateAvailable:p=null,onUpdatePolicy:f=null,onHelloRefused:w=null,updatePolicy:h=null,installKind:y=js(),selfUrl:k=`http://127.0.0.1:${process.env.PORT||3362}`,apiToken:g=null,removedMarkerFile:x=TR,onRemoved:S=null,onDegraded:_=null,onRecovered:M=null,onConnected:P=null,ingestMode:j=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:A=tG,unackedRetryMs:$=eG,pingMs:L=Z5,deadLinkMs:H=Math.round(L*Q5),maxInflightEvents:R=rG,httpTimeoutMs:T=oG,stallBackoffMinMs:C=nG,fetchImpl:W=globalThis.fetch,log:B=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=gR(e);let Z=k.replace(/^http/,"ws"),xe=new Set,Te=new Map,_e=new Map,Q=Ot.join(ub,"plane-link","cursor.json"),Xe=Ot.join(ub,"plane-link","dead-letter.ndjson"),Pe=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),le={},ge={},de=new Map,te=null,we=null,Oe=0,Ve=0,Nt=0,O=Promise.resolve(),X="ws",re=0,he=!1,Be=0,Pt=!1,tt=$o,wn=new Set,Kt=[],ht=null,wt=null,at=!1,Ai=!1,gt=bR,cr=null,Hr=null,lr=null,gn=null,ur=0,b=0,m=null,v=!1,D=0,J=!1,ie=K5(x);if(ie)if(ie.tokenPreview===yR(t))J=!0,bm({reason:ie.reason??"removed",at:ie.at??null,nodeId:ie.nodeId??n}),B.error(`[plane-link] this node was REMOVED from the plane (${ie.reason??"removed"}${ie.at?` at ${new Date(ie.at).toISOString()}`:""}) \u2014 not dialing. Re-provision it with a new node token, or delete ${x} to force a dial.`);else try{ci.unlinkSync(x),B.log("[plane-link] stale removed-marker discarded \u2014 the node token changed")}catch{}function ve({reason:F="removed",at:q=Date.now(),via:U="ctl"}={}){if(!J){J=!0,bm({reason:F,at:q,nodeId:n,via:U});try{ci.mkdirSync(Ot.dirname(x),{recursive:!0}),ci.writeFileSync(x,JSON.stringify({at:q,reason:F,via:U,nodeId:n,tokenPreview:yR(t)},null,2))}catch(I){B.error(`[plane-link] could not write ${x}: ${I?.message||I}`)}B.error(`[plane-link] the plane REMOVED this node (${F}, via ${U}) \u2014 going dormant: no more dials, ingest or broker calls. Re-provision with a new node token to reconnect; the box and its data are untouched.`);try{S?.({reason:F,at:q,via:U})}catch{}zk({bye:!1})}}async function _t(){try{le=JSON.parse(await Al.readFile(Q,"utf8"))}catch{le={}}}function yt(){v=!0,!lr&&(lr=setTimeout(async()=>{if(lr=null,!!v){v=!1;try{await Al.mkdir(Ot.dirname(Q),{recursive:!0}),await Al.writeFile(Q,JSON.stringify(le))}catch(F){B.error("[plane-link] cursor flush failed:",F?.message||F)}}},X5),lr.unref?.())}function nt(F){return!wt||wt.readyState!==cb.OPEN?!1:(wt.send(JSON.stringify(F)),!0)}function rn(F){let q=Date.now();for(let{trackId:U,event:I}of F){let V=de.get(I.id);de.set(I.id,{trackId:U,seq:I.seq,event:I,sentAt:q,firstSentAt:V?.firstSentAt??q,retryAt:q+$,tries:(V?.tries??0)+1,nacks:V?.nacks??0})}return X==="http"?(vN(F),!0):nt({v:mt,ch:"events",type:"append",events:F})}function yN(F){let q=null;for(let U of de.values())U.trackId===F&&(q==null||U.seq<q)&&(q=U.seq);return q}function tm(F){let q=ge[F];if(q==null)return;let U=yN(F),I=U==null?q:Math.min(q,U-1);I>(le[F]??0)&&(le[F]=I,yt())}function Ei(){if(te=Date.now(),we!=null){let F=Date.now()-we;B.log(`[plane-link] spine sync recovered after ${Math.round(F/1e3)}s degraded (${Oe} stall redial(s))`);let q=we;we=null,Oe=0,Ve=0,he=!1,Be=0;try{M?.({since:q,downMs:F})}catch(U){B.error(`[plane-link] onRecovered failed: ${U?.message||U}`)}}}function nm(F){if(!Array.isArray(F)||!F.length)return;let q=new Set;for(let U of F){let I=de.get(U);I&&(de.delete(U),(ge[I.trackId]??0)<I.seq&&(ge[I.trackId]=I.seq),q.add(I.trackId))}for(let U of q)tm(U);Ei()}function Fk(F,q){Nt++;let U={at:Date.now(),trackId:F.trackId,seq:F.seq,id:F.event?.id??null,type:F.event?.type??null,reason:q,event:F.event??null};B.error(`[plane-link] DEAD-LETTER ${F.trackId} seq ${F.seq} (${U.type??"?"}): plane rejected it permanently (${q}) \u2014 kept in ${Xe}`),O=O.then(async()=>{await Al.mkdir(Ot.dirname(Xe),{recursive:!0}),await Al.appendFile(Xe,JSON.stringify(U)+`
|
|
696
|
-
`)}).catch(I=>B.error(`[plane-link] dead-letter write failed: ${I?.message||I}`))}function Uk(F,q,U,I=null){if(!Array.isArray(F)||!F.length)return;let V=new Set,Ie=Date.now();for(let ye of F){let Le=de.get(ye);Le&&(U?(de.delete(ye),Fk(Le,q),(ge[Le.trackId]??0)<Le.seq&&(ge[Le.trackId]=Le.seq),V.add(Le.trackId)):(Le.nacks++,Le.retryAt=Ie+Math.max(1e3,Number(I)||$)))}for(let ye of V)tm(ye);Ei()}function Hk(F=Date.now()){let q=1/0;for(let U of de.values())U.firstSentAt<q&&(q=U.firstSentAt);return q===1/0?0:F-q}function bN(F){if(!H||!F||F!==wt||!ur)return!1;let q=Date.now()-ur;if(q<=H)return!1;b++,B.error(`[plane-link] no frame from the plane for ${Math.round(q/1e3)}s \u2014 link is half-open; terminating and redialing`);try{F.terminate()}catch{}return!0}function Wk(){if(!at||!de.size)return;let F=Date.now(),q=Hk(F);if(q<A||te!=null&&F-te<A||X==="http"&&he||Ve>F)return;Oe++;let U=Math.min(C*2**(Oe-1),xR);if(we==null){we=F-q,B.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(q/1e3)}s with ${de.size} event(s) in flight (${X} door) \u2014 the plane is not answering appends`);try{_?.({since:we,unacked:de.size,oldestAgeMs:q,mode:X})}catch(I){B.error(`[plane-link] onDegraded failed: ${I?.message||I}`)}}if(X==="http"){he=!0,Be=F+U,B.error(`[plane-link] ingest circuit OPEN (stall #${Oe}); probing /v1/ingest/health in ${Math.round(U/1e3)}s`);return}Ve=F+U,B.error(`[plane-link] closing the link and redialing in ${Math.round(U/1e3)}s (stall redial #${Oe})`);try{wt?.terminate()}catch{}}function rm(){if(!at)return;let F=Date.now();if(X==="http"&&he)return;let q=[...de.values()].filter(I=>I.event&&I.retryAt<=F&&!(X==="http"&&wn.has(I.trackId)));if(!q.length)return;q.sort((I,V)=>I.trackId===V.trackId?I.seq-V.seq:I.trackId<V.trackId?-1:1),B.log(`[plane-link] re-sending ${q.length} unacked event(s) the plane never confirmed`);let U=X==="http"?tt:$o;if(X==="http"){let I=new Map;for(let V of q)(I.get(V.trackId)??I.set(V.trackId,[]).get(V.trackId)).push(V);for(let[,V]of I){if(re>=lb)return;rn(V.slice(0,U).map(Ie=>({trackId:Ie.trackId,event:Ie.event})))}return}for(let I=0;I<q.length;I+=U){let V=q.slice(I,I+U).map(Ie=>({trackId:Ie.trackId,event:Ie.event}));if(!rn(V))return}}async function kN(F=null){for(;;){if(!at)return!1;let q=de.size<R,U=X!=="http"||re<lb&&!he&&(!F||!wn.has(F));if(q&&U)return!0;await new Promise(I=>setTimeout(I,100))}}function om(){if(ht=null,!at||!Kt.length)return;let F=Kt;if(Kt=[],X!=="http"){for(let U=0;U<F.length;U+=$o)rn(F.slice(U,U+$o));return}let q=new Map;for(let U of F)(q.get(U.trackId)??q.set(U.trackId,[]).get(U.trackId)).push(U);for(let[U,I]of q){if(wn.has(U)||re>=lb||he){Kt.push(...I);continue}for(let V=0;V<I.length;V+=tt)rn(I.slice(V,V+tt))}Kt.length&&!ht&&(ht=setTimeout(om,vR),ht.unref?.())}let xN=F=>{let q=new Map;for(let{trackId:U,event:I}of F)(q.get(U)??q.set(U,[]).get(U)).push(I);return[...q].map(([U,I])=>({trackId:U,events:I}))},Uo=(F,q,U,{progress:I})=>{let V=Date.now();for(let{event:Ie}of F){let ye=de.get(Ie.id);ye&&(ye.nacks++,ye.retryAt=V+Math.max(1e3,U||$))}I&&Ei()};async function vN(F){let q=new Set(F.map(U=>U.trackId));for(let U of q)wn.add(U);re++;try{let U=await W(`${Pe}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:xN(F)}),signal:AbortSignal.timeout(T)}),I=(Number(U.headers.get("retry-after"))||0)*1e3;if(U.status===200){let V=await U.json();tt=$o;for(let Ie of V.results??[]){nm(Ie.acked??[]);let ye=(Le,ut)=>{let on=new Map;for(let it of Le??[])(on.get(it.reason)??on.set(it.reason,[]).get(it.reason)).push(it.id);for(let[it,Jk]of on)Uk(Jk,it,ut,V.retryAfterMs??null)};ye(Ie.rejected,!0),ye(Ie.deferred,!1)}nm(V.discarded??[]),Ei();return}if(U.status===429){Uo(F,"overloaded",I||5e3,{progress:!0});return}if(U.status===413&&F.length>1){tt=Math.max(1,Math.floor(F.length/2)),B.error(`[plane-link] ingest request too large (${F.length} events) \u2014 halving batches to ${tt}`),Uo(F,"too_large",1e3,{progress:!0});return}if(U.status===413){let V=de.get(F[0].event.id);V&&(de.delete(V.event.id),Fk(V,"too_large"),(ge[V.trackId]??0)<V.seq&&(ge[V.trackId]=V.seq),tm(V.trackId)),Ei();return}if(U.status===410){ve({reason:"removed",via:"410"}),Uo(F,"http_410",6e4,{progress:!1});return}if(U.status===401||U.status===403){let{tripped:V,holdMs:Ie}=ta();if(V&&B.error(`[plane-link] holding every plane call for ${Math.round(Ie/6e4)} min \u2014 the ingest door refused this node's token three times in a row`),Date.now()-D>=SR){D=Date.now();let ye="";try{ye=(await U.json())?.error??""}catch{}B.error(`[plane-link] plane refused ingest (HTTP ${U.status}${ye?`: ${ye}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}Uo(F,`http_${U.status}`,Math.max(6e4,Di()),{progress:!1});return}if(U.status===400){B.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${F.length} event(s) for retry; this is a bug, not data`),Uo(F,"http_400",6e4,{progress:!1});return}Uo(F,`http_${U.status}`,I||5e3,{progress:!1})}catch(U){let I=U?.name==="TimeoutError"||U?.name==="AbortError";I&&F.length>1&&(tt=Math.max(1,Math.floor(F.length/2)),B.error(`[plane-link] ingest request of ${F.length} event(s) outlived its ${T} ms deadline \u2014 halving batches to ${tt}`)),Uo(F,I?"timeout":"network",5e3,{progress:!1})}finally{re--;for(let U of q)wn.delete(U)}}async function SN(){if(X!=="http"||!he||!at||Pt)return;let F=Date.now();if(!(Be>F||na(F))){Pt=!0;try{let q=await W(`${Pe}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(T)});if(!q.ok)throw new Error(`HTTP ${q.status}`);B.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),he=!1,Be=0,Ve=Date.now()+T+$+1e3;for(let U of de.values())U.retryAt=Math.min(U.retryAt,Date.now());rm()}catch(q){Oe++;let U=Math.min(C*2**(Oe-1),xR);Be=Date.now()+U,B.error(`[plane-link] ingest door still not answering (${q?.message||q}); next probe in ${Math.round(U/1e3)}s`)}finally{Pt=!1}}}function qk(){let F=Date.now();return{mode:X,unacked:de.size,oldestUnackedAgeMs:Hk(F),lastProgressAt:te,degradedSince:we,stallRedials:Oe,deadLettered:Nt,http:X==="http"?{inFlight:re,circuitOpen:he,nextProbeInMs:he?Math.max(0,Be-F):0,batchCap:tt}:null}}async function Gk(){if(!i)return;let F;try{F=await i()}catch(U){B.error("[plane-link] metrics sample failed:",U?.message||U);return}let q=null;try{q=c?.()??null}catch{}F&&nt({v:mt,ch:"ctl",type:"metrics",metrics:{...F,planeLink:qk(),...q?{delivery:q}:{}}})}function TN(){i&&(m&&clearInterval(m),Gk(),m=setInterval(Gk,d),m.unref?.())}async function AN(){let F;try{F=await o()}catch(q){B.error("[plane-link] backfill listTracks failed:",q?.message||q);return}nt({v:mt,ch:"ctl",type:"tracks",ids:F.filter(q=>!q.transferredTo).map(q=>q.id).filter(q=>typeof q=="string")});for(let q of F){if(!at)return;let U;try{U=await K(q)}catch{continue}let I=le[q.id]??0,V=U.filter(ye=>ye.seq>I&&typeof ye.id=="string"),Ie=X==="http"?tt:$o;for(let ye=0;ye<V.length;ye+=Ie){if(!await kN(q.id))return;let Le=V.slice(ye,ye+Math.min(Ie,X==="http"?tt:$o)).map(ut=>({trackId:q.id,event:ut}));if(!rn(Le))return}}}function Kk(F){!at||!F?.trackId||typeof F.id!="string"||(Kt.push({trackId:F.trackId,event:F}),ht||(ht=setTimeout(om,vR),ht.unref?.()))}let _i=new Map;async function EN(F){let{reqId:q,method:U,path:I,headers:V={},body:Ie}=F,ye=Object.fromEntries(Object.entries(V).filter(([,ut])=>ut!=null)),Le=new AbortController;_i.set(q,Le);try{if(F.host){await _N(F,ye,Le);return}g&&(ye["x-bostrat-token"]=g);let ut=await fetch(k+I,{method:U,headers:ye,body:Ie?Buffer.from(Ie,"base64"):void 0,signal:Le.signal}),on=Object.fromEntries(ut.headers);for(let it of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete on[it];if(on["content-type"]||(on["content-type"]="application/octet-stream"),nt({v:mt,ch:"control",type:"http_head",reqId:q,status:ut.status,headers:on}),ut.body){for await(let it of ut.body)if(!nt({v:mt,ch:"control",type:"http_data",reqId:q,data:Buffer.from(it).toString("base64")}))break}nt({v:mt,ch:"control",type:"http_end",reqId:q,error:null})}finally{_i.delete(q)}}function _N(F,q,U){let{reqId:I,method:V,path:Ie,body:ye}=F,Le=new URL(k);return new Promise(ut=>{let on=q5.request({host:Le.hostname,port:Le.port,method:V,path:Ie,headers:{...q,host:F.host},signal:U.signal},it=>{let{connection:Jk,"keep-alive":J3,"transfer-encoding":Y3,upgrade:X3,...IN}=it.headers;nt({v:mt,ch:"control",type:"http_head",reqId:I,status:it.statusCode||502,headers:IN}),it.on("data",$N=>{nt({v:mt,ch:"control",type:"http_data",reqId:I,data:Buffer.from($N).toString("base64")})||it.destroy()}),it.on("end",()=>{nt({v:mt,ch:"control",type:"http_end",reqId:I,error:null}),ut()}),it.on("error",()=>{nt({v:mt,ch:"control",type:"http_end",reqId:I,error:"view upstream error"}),ut()})});on.on("error",it=>{nt({v:mt,ch:"control",type:"http_end",reqId:I,error:it?.message||"view request failed"}),ut()}),ye&&on.write(Buffer.from(ye,"base64")),on.end()})}function Vk(){if(Ai||J)return;wt=new cb(e,{headers:{authorization:`Bearer ${t}`}});let F=wt;ur=0,F.on("ping",()=>{ur=Date.now()}),F.on("pong",()=>{ur=Date.now()}),wt.on("open",async()=>{let U=null;try{U=await s?.()||null}catch{U=null}nt({v:mt,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:Cn,installKind:y,...typeof h=="function"?{updatePolicy:h()}:h?{updatePolicy:h}:{},providers:V5(),...U?{agents:U}:{}})}),wt.on("message",U=>{ur=Date.now();let I;try{I=JSON.parse(U)}catch{return}if(I.ch==="ctl"&&I.type==="hello_ok"){at=!0,gt=bR,cr=null,y0(),B.log(`[plane-link] connected to plane as ${n}`);try{P?.()}catch{}if(typeof I.hashKey=="string"&&I.hashKey)try{l?.(I.hashKey)}catch{}try{u?.(typeof I.viewBase=="string"&&I.viewBase?I.viewBase:null)}catch{}I.minNodeVersion&&Cn&&El(Cn,I.minNodeVersion)&&B.error(`[plane-link] this node is bostrat-node ${Cn}; the plane asks for \u2265${I.minNodeVersion} \u2014 update with: ${mf(y)}`);try{f?.(typeof I.updatePolicy=="string"?I.updatePolicy:null)}catch{}let V=typeof I.latestNodeVersion=="string"?I.latestNodeVersion:null;if(V&&Cn&&El(Cn,V)&&!xe.has(V)){xe.add(V),B.log(`[plane-link] bostrat-node ${V} is available (this node runs ${Cn}) \u2014 ${mf(y)}`);try{p?.({current:Cn,latest:V,min:I.minNodeVersion??null,installKind:y,required:!!(I.minNodeVersion&&El(Cn,I.minNodeVersion))})}catch(ye){B.error(`[plane-link] update notice failed: ${ye?.message||ye}`)}}let Ie=I.ingest==="http";X=j==="ws"?"ws":j==="http"||Ie?"http":"ws",j==="http"&&!Ie&&B.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),X==="http"&&B.log("[plane-link] spine rides the HTTPS ingest door"),he=!1,Be=0,tt=$o,gn&&clearInterval(gn),ur=Date.now(),gn=setInterval(()=>{bN(F)||(nt({v:mt,ch:"ctl",type:"ping"}),Wk(),SN().catch(()=>{}),rm(),Kt.length&&!ht&&om())},L),gn.unref?.(),TN(),AN().catch(ye=>B.error("[plane-link] backfill failed:",ye?.message||ye));return}if(I.ch==="ctl"&&I.type==="hello_refused"){let V=typeof I.minNodeVersion=="string"?I.minNodeVersion:null,Ie=typeof I.latestNodeVersion=="string"?I.latestNodeVersion:null;B.error(`[plane-link] plane REFUSED this node: bostrat-node ${Cn??"?"} is below its minimum ${V??"?"} \u2014 ${I.fix||mf(y)}`),gt=kR,cr=0;try{w?.({current:Cn,min:V,latest:Ie,fix:I.fix||mf(y),installKind:y})}catch(ye){B.error(`[plane-link] refusal handler failed: ${ye?.message||ye}`)}return}if(I.ch==="ctl"&&I.type==="removed"){nt({v:mt,ch:"ctl",type:"removed_ack",nodeId:n}),ve({reason:typeof I.reason=="string"?I.reason:"removed",at:Number(I.at)||Date.now(),via:"ctl"});return}if(I.ch==="ctl"&&I.type==="update_policy"){try{f?.(typeof I.mode=="string"?I.mode:null)}catch{}return}if(I.ch==="ctl"&&I.type==="usage_caps"){Pd(I.caps??{},{source:"plane",updatedAt:I.updatedAt??Date.now()}).then(V=>B.log(`[plane-link] spend caps ${Object.keys(V.caps).length?`updated (${Object.keys(V.caps).join(", ")})`:"cleared"} from plane`)).catch(V=>B.error("[plane-link] spend-caps push rejected:",V?.message||V));return}if(I.ch==="ctl"&&I.type==="transferred"&&Array.isArray(I.tracks)){Promise.resolve().then(()=>a?.(I.tracks)).catch(V=>B.error("[plane-link] transfer notice failed:",V?.message||V));return}if(I.ch==="events"&&I.type==="ack"&&Array.isArray(I.ids)){nm(I.ids);return}if(I.ch==="events"&&I.type==="nack"&&Array.isArray(I.ids)){Uk(I.ids,I.reason,I.permanent===!0,I.retryAfterMs??null);return}if(I.ch==="control"&&I.type==="http_open"){EN(I).catch(V=>nt({v:mt,ch:"control",type:"http_end",reqId:I.reqId,error:V?.message||"tunnel error"}));return}if(I.ch==="control"&&I.type==="http_cancel"){_i.get(I.reqId)?.abort();return}if(I.ch==="attach"&&I.type==="open"){let V=new cb(Z+I.path,Array.isArray(I.protocols)?I.protocols:[],I.host?{headers:{host:I.host}}:void 0);Te.set(I.streamId,V),_e.set(I.streamId,[]),V.on("open",()=>{let ye=_e.get(I.streamId)||[];_e.delete(I.streamId);for(let{buf:Le,bin:ut}of ye)V.send(Le,{binary:ut})}),V.on("message",(ye,Le)=>{let ut=Le?ye:Buffer.from(String(ye));nt({v:mt,ch:"attach",type:"data",streamId:I.streamId,data:ut.toString("base64"),...Le?{bin:!0}:{}})});let Ie=()=>{_e.delete(I.streamId),Te.delete(I.streamId)&&nt({v:mt,ch:"attach",type:"close",streamId:I.streamId})};V.on("close",Ie),V.on("error",Ie);return}if(I.ch==="attach"&&I.type==="data"){let V=Te.get(I.streamId);if(!V)return;let Ie=Buffer.from(I.data,"base64"),ye=I.bin===!0,Le=_e.get(I.streamId);Le?Le.push({buf:Ie,bin:ye}):V.send(Ie,{binary:ye});return}if(I.ch==="attach"&&I.type==="close"){let V=Te.get(I.streamId);Te.delete(I.streamId),_e.delete(I.streamId),V?.close();return}});let q=()=>{at&&B.log("[plane-link] disconnected from plane"),at=!1,gn&&(clearInterval(gn),gn=null),m&&(clearInterval(m),m=null),de.clear(),ge={},Kt=[],ht&&(clearTimeout(ht),ht=null),wn.clear();for(let V of Te.values())V.close();Te.clear(),_e.clear();for(let V of _i.values())V.abort();if(_i.clear(),Ai||Hr)return;cr==null&&(cr=Date.now());let U=Date.now()-cr<z5,I=Math.max(U?J5+Math.random()*Y5:gt,Ve-Date.now(),Di());Hr=setTimeout(()=>{Hr=null,Vk()},I),Hr.unref?.(),U||(gt=Math.min(gt*2,kR))};wt.on("close",q),wt.on("error",U=>{let I=/Unexpected server response: (\d+)/.exec(U?.message||"")?.[1];if(I==="410"){ve({reason:"removed",via:"410"});return}if(I==="401"||I==="403"){let{tripped:V,holdMs:Ie}=ta();V?B.error(`[plane-link] holding every plane call for ${Math.round(Ie/6e4)} min \u2014 three credential refusals in a row (HTTP ${I}) 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(Ie/6e4)} min.`):Date.now()-D>=SR&&(D=Date.now(),B.error(`[plane-link] plane refused this node's credentials (HTTP ${I}) \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)`))}q()})}Ze.on("event",Kk),_t().then(Vk);function zk({bye:F=!0}={}){Ai=!0,Ze.off("event",Kk),Hr&&clearTimeout(Hr),lr&&clearTimeout(lr),gn&&clearInterval(gn),m&&clearInterval(m),ht&&clearTimeout(ht),F&&nt({v:mt,ch:"ctl",type:"bye",reason:"shutdown"}),wt?.close()}return{stop:()=>zk(),status:()=>({connected:at,nodeId:n,cursor:{...le},inflight:de.size,...qk(),auth:b0(),dormant:J,removed:yu(),deadLinkMs:H,deadLinkRedials:b}),_tick:()=>{Wk(),rm()}}}qo();var wf=(()=>{try{return cG(import.meta.url)("../package.json").version||null}catch{return null}})(),lG=process.env.BOSTRAT_STATE_DIR||kt.join(process.env.HOME||aG.tmpdir(),".bostrat"),IR=kt.join(lG,"self-update"),gf=kt.join(IR,"status.json"),uG=1440*6e4,dG=6e4,_l=new Set(["npm-global","git-checkout"]);function $R({kind:e=js(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:_l.has(e)?e==="git-checkout"||pG(t)?"auto":"prompt":"off"}function pG(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var Qt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function fG({kind:e,to:t,from:n=wf,statusFile:r=gf,logFile:o=kt.join(IR,"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||kt.resolve(kt.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!_l.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=(y,k="")=>`write_status ${Qt(y)} ${k}`,p=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${Qt(i)}`,f=`
|
|
695
|
+
`);f5=new Set(["claude","codex","cursor"]);hl=new Set,m5=e=>hl.has(e);h5=9e4;j$={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};ly=new Map;y5=6e4,uy=new Map;gy=120;Yy=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:Ho(e.hookToken),createdAt:Date.now()});Ns=new Map,ii=e=>Ns.get(e)||(Ht?pe(Ht,e)?.handoff:null)||null;Zy=6e4,Mn=(e,t,n=15e3)=>Kp("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),qp=new Map,Qy=e=>qp.get(e)||null;T5=/^[A-Za-z0-9][A-Za-z0-9._/-]{0,200}$/;jp=new Map;_5=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,I5=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,ni=z.join(nr,"worktrees"),Xa=[dl,Fp],Wt=new Map,$5=e=>Wt.get(e)||(Ht?Ht.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,Ls=e=>{let t=new Map([...Wt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Ht?pe(Ht,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)},lf=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});Sl=e=>uR(e.trackId,e.id,lf(e));uf=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",gl=class extends Error{}});dt();import Re from"node:path";import xt from"node:fs";import _3 from"node:readline";import{fileURLToPath as I3}from"node:url";import nn from"express";import oN from"node:http";import $3 from"node:https";import tu from"node:os";import{randomUUID as R3}from"node:crypto";import{WebSocketServer as O3}from"ws";dt();import i1 from"node-pty";async function Cx(e,t,n={}){if(!je(t)){Ku(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await Zr()){Ku(e,`host tmux server not running on ${zt}`),e.close(1011,"tmux server unavailable");return}if(!await Se(t)){let a=!1;if(typeof n.revive=="function"&&(Vu(e,{type:"notice",message:"Resuming the lead box\u2026"}),a=await n.revive().catch(()=>!1)),!a||!await Se(t)){Ku(e,`session "${t}" not found`),e.close(1008,"session not found");return}if(e.readyState!==e.OPEN)return}let r=Mx(n.cols,120,Nx),o=Mx(n.rows,32,Px),s;try{s=i1.spawn(oe,["-S",zt,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){Ku(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&Vu(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(Vu(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"&&d1(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{}u1(t)})}var c1=6,l1=250;async function u1(e,t=c1,n=l1){for(let r=0;r<t;r++){if(await Xo(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var Nx=20,Px=6;function d1(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=Nx&&Number(t)>=Px}function Mx(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function Vu(e,t){try{e.send(JSON.stringify(t))}catch{}}function Ku(e,t){Vu(e,{type:"error",message:t})}function Dx(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}}vn();Ju();Hh();Ir();import{createRequire as M3}from"node:module";var j5=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function ib({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(j5)&&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}}Qg();be();Qc();Gn();mp();an();var pf=60*6e4,F5=1440*6e4,U5=new Set(["merged","closed","lost"]),Tl=(e,t,n)=>Number.isFinite(Number(e))&&n-Number(e)<=t;function H5({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)<=xg&&s.push("ship"),r>0&&s.push("create-net");for(let a of n||[]){if(!a||U5.has(a.state))continue;let i=[a.ciFix,a.reconcile,a.ciFixAwait,a.reconcileAwait];if(!(a.trackId===e||i.some(d=>d?.trackId===e)))continue;let l=a.number??"?";a.ciFix&&Tl(a.ciFix.delegatedAt,pf,o)&&s.push(`ci-fix#${l}`),a.reconcile&&Tl(a.reconcile.delegatedAt,pf,o)&&s.push(`reconcile#${l}`),a.ciFixAwait&&Tl(a.ciFixAwait.since,pf,o)&&s.push(`ci-fix-await#${l}`),a.reconcileAwait&&Tl(a.reconcileAwait.since,pf,o)&&s.push(`reconcile-await#${l}`);let u=a.autoMerge?.armedAt??a.armedAt??null;a.trackId===e&&(a.autoMerge||a.armedAt)&&Tl(u,F5,o)&&s.push(`auto-merge#${l}`)}return s}var ff=new Map;Ze.on("event",e=>{e?.trackId&&ff.delete(e.trackId)});async function W5(e){if(ff.has(e.id))return ff.get(e.id);let t=await K(e).catch(()=>[]),n=qa(t,-1/0);return ff.set(e.id,n),n}async function wR(e,{now:t=Date.now()}={}){let n=new Map,r=await En().catch(()=>[]);for(let o of e||[]){if(!o||Ue(o))continue;let s=H5({trackId:o.id,shipPending:await W5(o),links:r,observers:YE(o.id),now:t});s.length&&n.set(o.id,s)}return n}Kg();import IR from"node:fs";import Ro from"node:fs/promises";import cG from"node:os";import kt from"node:path";import{spawn as lG}from"node:child_process";import{createRequire as uG}from"node:module";be();Bd();import ci from"node:fs";import El from"node:fs/promises";import K5 from"node:http";import Ot from"node:path";import hf from"node:os";import{createRequire as V5}from"node:module";import lb from"ws";import q5 from"node:path";import G5 from"node:os";var Al=process.env.BOSTRAT_STATE_DIR||q5.join(process.env.HOME||G5.tmpdir(),".bostrat");function cb(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 gR(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=cb(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function yR(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}km();var Cn=(()=>{try{return V5(import.meta.url)("../package.json").version||null}catch{return null}})(),db=process.env.BOSTRAT_STATE_DIR||Ot.join(process.env.HOME||hf.tmpdir(),".bostrat"),AR=Ot.join(db,"plane-link","removed.json");function bR(e){return typeof e!="string"||e.length<8?"\u2022\u2022\u2022\u2022":`${e.slice(0,6)}\u2026${e.slice(-4)}`}function z5(e=AR){try{let t=JSON.parse(ci.readFileSync(e,"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function js({env:e=process.env,pkgDir:t=Ot.resolve(Ot.dirname(new URL(import.meta.url).pathname),".."),exists:n=ci.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(Ot.join(t,"..",".git"))?"git-checkout":"dev"}function _l(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 mf(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 J5({home:e=process.env.HOME||hf.homedir(),env:t=process.env}={}){let n=r=>{try{return ci.statSync(r),!0}catch{return!1}};return{claude:n(Ot.join(e,".claude",".credentials.json")),codex:n(Ot.join(t.CODEX_HOME||Ot.join(e,".codex"),"auth.json")),cursor:n(Ot.join(t.XDG_CONFIG_HOME||Ot.join(e,".config"),"cursor","auth.json"))}}var mt=1,kR=1e3,xR=3e4,Y5=6e4,X5=1500,Z5=500,$o=100,Q5=2e3,eG=25e3,tG=2.5,nG=6e4,ER=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},rG=ER(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),oG=3e4,vR=10*6e4,sG=400,SR=250,aG=ER(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),ub=4,iG=6e4,TR=6e4;function _R({url:e,token:t,nodeId:n=hf.hostname(),name:r=hf.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,deliverySample:c=null,onHashKey:l=null,onViewBase:u=null,metricsMs:d=iG,onUpdateAvailable:p=null,onUpdatePolicy:f=null,onHelloRefused:w=null,updatePolicy:h=null,installKind:y=js(),selfUrl:k=`http://127.0.0.1:${process.env.PORT||3362}`,apiToken:g=null,removedMarkerFile:x=AR,onRemoved:S=null,onDegraded:_=null,onRecovered:M=null,onConnected:P=null,ingestMode:j=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:A=rG,unackedRetryMs:$=nG,pingMs:L=eG,deadLinkMs:H=Math.round(L*tG),maxInflightEvents:R=sG,httpTimeoutMs:T=aG,stallBackoffMinMs:C=oG,fetchImpl:W=globalThis.fetch,log:B=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=yR(e);let Z=k.replace(/^http/,"ws"),xe=new Set,Te=new Map,_e=new Map,Q=Ot.join(db,"plane-link","cursor.json"),Xe=Ot.join(db,"plane-link","dead-letter.ndjson"),Pe=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),le={},ge={},de=new Map,te=null,we=null,Oe=0,Ve=0,Nt=0,O=Promise.resolve(),X="ws",re=0,he=!1,Be=0,Pt=!1,tt=$o,wn=new Set,Kt=[],ht=null,wt=null,at=!1,Ai=!1,gt=kR,cr=null,Hr=null,lr=null,gn=null,ur=0,b=0,m=null,v=!1,D=0,J=!1,ie=z5(x);if(ie)if(ie.tokenPreview===bR(t))J=!0,bm({reason:ie.reason??"removed",at:ie.at??null,nodeId:ie.nodeId??n}),B.error(`[plane-link] this node was REMOVED from the plane (${ie.reason??"removed"}${ie.at?` at ${new Date(ie.at).toISOString()}`:""}) \u2014 not dialing. Re-provision it with a new node token, or delete ${x} to force a dial.`);else try{ci.unlinkSync(x),B.log("[plane-link] stale removed-marker discarded \u2014 the node token changed")}catch{}function ve({reason:F="removed",at:q=Date.now(),via:U="ctl"}={}){if(!J){J=!0,bm({reason:F,at:q,nodeId:n,via:U});try{ci.mkdirSync(Ot.dirname(x),{recursive:!0}),ci.writeFileSync(x,JSON.stringify({at:q,reason:F,via:U,nodeId:n,tokenPreview:bR(t)},null,2))}catch(I){B.error(`[plane-link] could not write ${x}: ${I?.message||I}`)}B.error(`[plane-link] the plane REMOVED this node (${F}, via ${U}) \u2014 going dormant: no more dials, ingest or broker calls. Re-provision with a new node token to reconnect; the box and its data are untouched.`);try{S?.({reason:F,at:q,via:U})}catch{}Yk({bye:!1})}}async function _t(){try{le=JSON.parse(await El.readFile(Q,"utf8"))}catch{le={}}}function yt(){v=!0,!lr&&(lr=setTimeout(async()=>{if(lr=null,!!v){v=!1;try{await El.mkdir(Ot.dirname(Q),{recursive:!0}),await El.writeFile(Q,JSON.stringify(le))}catch(F){B.error("[plane-link] cursor flush failed:",F?.message||F)}}},Q5),lr.unref?.())}function nt(F){return!wt||wt.readyState!==lb.OPEN?!1:(wt.send(JSON.stringify(F)),!0)}function rn(F){let q=Date.now();for(let{trackId:U,event:I}of F){let V=de.get(I.id);de.set(I.id,{trackId:U,seq:I.seq,event:I,sentAt:q,firstSentAt:V?.firstSentAt??q,retryAt:q+$,tries:(V?.tries??0)+1,nacks:V?.nacks??0})}return X==="http"?(vN(F),!0):nt({v:mt,ch:"events",type:"append",events:F})}function yN(F){let q=null;for(let U of de.values())U.trackId===F&&(q==null||U.seq<q)&&(q=U.seq);return q}function tm(F){let q=ge[F];if(q==null)return;let U=yN(F),I=U==null?q:Math.min(q,U-1);I>(le[F]??0)&&(le[F]=I,yt())}function Ei(){if(te=Date.now(),we!=null){let F=Date.now()-we;B.log(`[plane-link] spine sync recovered after ${Math.round(F/1e3)}s degraded (${Oe} stall redial(s))`);let q=we;we=null,Oe=0,Ve=0,he=!1,Be=0;try{M?.({since:q,downMs:F})}catch(U){B.error(`[plane-link] onRecovered failed: ${U?.message||U}`)}}}function nm(F){if(!Array.isArray(F)||!F.length)return;let q=new Set;for(let U of F){let I=de.get(U);I&&(de.delete(U),(ge[I.trackId]??0)<I.seq&&(ge[I.trackId]=I.seq),q.add(I.trackId))}for(let U of q)tm(U);Ei()}function Hk(F,q){Nt++;let U={at:Date.now(),trackId:F.trackId,seq:F.seq,id:F.event?.id??null,type:F.event?.type??null,reason:q,event:F.event??null};B.error(`[plane-link] DEAD-LETTER ${F.trackId} seq ${F.seq} (${U.type??"?"}): plane rejected it permanently (${q}) \u2014 kept in ${Xe}`),O=O.then(async()=>{await El.mkdir(Ot.dirname(Xe),{recursive:!0}),await El.appendFile(Xe,JSON.stringify(U)+`
|
|
696
|
+
`)}).catch(I=>B.error(`[plane-link] dead-letter write failed: ${I?.message||I}`))}function Wk(F,q,U,I=null){if(!Array.isArray(F)||!F.length)return;let V=new Set,Ie=Date.now();for(let ye of F){let Le=de.get(ye);Le&&(U?(de.delete(ye),Hk(Le,q),(ge[Le.trackId]??0)<Le.seq&&(ge[Le.trackId]=Le.seq),V.add(Le.trackId)):(Le.nacks++,Le.retryAt=Ie+Math.max(1e3,Number(I)||$)))}for(let ye of V)tm(ye);Ei()}function qk(F=Date.now()){let q=1/0;for(let U of de.values())U.firstSentAt<q&&(q=U.firstSentAt);return q===1/0?0:F-q}function bN(F){if(!H||!F||F!==wt||!ur)return!1;let q=Date.now()-ur;if(q<=H)return!1;b++,B.error(`[plane-link] no frame from the plane for ${Math.round(q/1e3)}s \u2014 link is half-open; terminating and redialing`);try{F.terminate()}catch{}return!0}function Gk(){if(!at||!de.size)return;let F=Date.now(),q=qk(F);if(q<A||te!=null&&F-te<A||X==="http"&&he||Ve>F)return;Oe++;let U=Math.min(C*2**(Oe-1),vR);if(we==null){we=F-q,B.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(q/1e3)}s with ${de.size} event(s) in flight (${X} door) \u2014 the plane is not answering appends`);try{_?.({since:we,unacked:de.size,oldestAgeMs:q,mode:X})}catch(I){B.error(`[plane-link] onDegraded failed: ${I?.message||I}`)}}if(X==="http"){he=!0,Be=F+U,B.error(`[plane-link] ingest circuit OPEN (stall #${Oe}); probing /v1/ingest/health in ${Math.round(U/1e3)}s`);return}Ve=F+U,B.error(`[plane-link] closing the link and redialing in ${Math.round(U/1e3)}s (stall redial #${Oe})`);try{wt?.terminate()}catch{}}function rm(){if(!at)return;let F=Date.now();if(X==="http"&&he)return;let q=[...de.values()].filter(I=>I.event&&I.retryAt<=F&&!(X==="http"&&wn.has(I.trackId)));if(!q.length)return;q.sort((I,V)=>I.trackId===V.trackId?I.seq-V.seq:I.trackId<V.trackId?-1:1),B.log(`[plane-link] re-sending ${q.length} unacked event(s) the plane never confirmed`);let U=X==="http"?tt:$o;if(X==="http"){let I=new Map;for(let V of q)(I.get(V.trackId)??I.set(V.trackId,[]).get(V.trackId)).push(V);for(let[,V]of I){if(re>=ub)return;rn(V.slice(0,U).map(Ie=>({trackId:Ie.trackId,event:Ie.event})))}return}for(let I=0;I<q.length;I+=U){let V=q.slice(I,I+U).map(Ie=>({trackId:Ie.trackId,event:Ie.event}));if(!rn(V))return}}async function kN(F=null){for(;;){if(!at)return!1;let q=de.size<R,U=X!=="http"||re<ub&&!he&&(!F||!wn.has(F));if(q&&U)return!0;await new Promise(I=>setTimeout(I,100))}}function om(){if(ht=null,!at||!Kt.length)return;let F=Kt;if(Kt=[],X!=="http"){for(let U=0;U<F.length;U+=$o)rn(F.slice(U,U+$o));return}let q=new Map;for(let U of F)(q.get(U.trackId)??q.set(U.trackId,[]).get(U.trackId)).push(U);for(let[U,I]of q){if(wn.has(U)||re>=ub||he){Kt.push(...I);continue}for(let V=0;V<I.length;V+=tt)rn(I.slice(V,V+tt))}Kt.length&&!ht&&(ht=setTimeout(om,SR),ht.unref?.())}let xN=F=>{let q=new Map;for(let{trackId:U,event:I}of F)(q.get(U)??q.set(U,[]).get(U)).push(I);return[...q].map(([U,I])=>({trackId:U,events:I}))},Uo=(F,q,U,{progress:I})=>{let V=Date.now();for(let{event:Ie}of F){let ye=de.get(Ie.id);ye&&(ye.nacks++,ye.retryAt=V+Math.max(1e3,U||$))}I&&Ei()};async function vN(F){let q=new Set(F.map(U=>U.trackId));for(let U of q)wn.add(U);re++;try{let U=await W(`${Pe}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:xN(F)}),signal:AbortSignal.timeout(T)}),I=(Number(U.headers.get("retry-after"))||0)*1e3;if(U.status===200){let V=await U.json();tt=$o;for(let Ie of V.results??[]){nm(Ie.acked??[]);let ye=(Le,ut)=>{let on=new Map;for(let it of Le??[])(on.get(it.reason)??on.set(it.reason,[]).get(it.reason)).push(it.id);for(let[it,Xk]of on)Wk(Xk,it,ut,V.retryAfterMs??null)};ye(Ie.rejected,!0),ye(Ie.deferred,!1)}nm(V.discarded??[]),Ei();return}if(U.status===429){Uo(F,"overloaded",I||5e3,{progress:!0});return}if(U.status===413&&F.length>1){tt=Math.max(1,Math.floor(F.length/2)),B.error(`[plane-link] ingest request too large (${F.length} events) \u2014 halving batches to ${tt}`),Uo(F,"too_large",1e3,{progress:!0});return}if(U.status===413){let V=de.get(F[0].event.id);V&&(de.delete(V.event.id),Hk(V,"too_large"),(ge[V.trackId]??0)<V.seq&&(ge[V.trackId]=V.seq),tm(V.trackId)),Ei();return}if(U.status===410){ve({reason:"removed",via:"410"}),Uo(F,"http_410",6e4,{progress:!1});return}if(U.status===401||U.status===403){let{tripped:V,holdMs:Ie}=ta();if(V&&B.error(`[plane-link] holding every plane call for ${Math.round(Ie/6e4)} min \u2014 the ingest door refused this node's token three times in a row`),Date.now()-D>=TR){D=Date.now();let ye="";try{ye=(await U.json())?.error??""}catch{}B.error(`[plane-link] plane refused ingest (HTTP ${U.status}${ye?`: ${ye}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}Uo(F,`http_${U.status}`,Math.max(6e4,Di()),{progress:!1});return}if(U.status===400){B.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${F.length} event(s) for retry; this is a bug, not data`),Uo(F,"http_400",6e4,{progress:!1});return}Uo(F,`http_${U.status}`,I||5e3,{progress:!1})}catch(U){let I=U?.name==="TimeoutError"||U?.name==="AbortError";I&&F.length>1&&(tt=Math.max(1,Math.floor(F.length/2)),B.error(`[plane-link] ingest request of ${F.length} event(s) outlived its ${T} ms deadline \u2014 halving batches to ${tt}`)),Uo(F,I?"timeout":"network",5e3,{progress:!1})}finally{re--;for(let U of q)wn.delete(U)}}async function SN(){if(X!=="http"||!he||!at||Pt)return;let F=Date.now();if(!(Be>F||na(F))){Pt=!0;try{let q=await W(`${Pe}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(T)});if(!q.ok)throw new Error(`HTTP ${q.status}`);B.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),he=!1,Be=0,Ve=Date.now()+T+$+1e3;for(let U of de.values())U.retryAt=Math.min(U.retryAt,Date.now());rm()}catch(q){Oe++;let U=Math.min(C*2**(Oe-1),vR);Be=Date.now()+U,B.error(`[plane-link] ingest door still not answering (${q?.message||q}); next probe in ${Math.round(U/1e3)}s`)}finally{Pt=!1}}}function Kk(){let F=Date.now();return{mode:X,unacked:de.size,oldestUnackedAgeMs:qk(F),lastProgressAt:te,degradedSince:we,stallRedials:Oe,deadLettered:Nt,http:X==="http"?{inFlight:re,circuitOpen:he,nextProbeInMs:he?Math.max(0,Be-F):0,batchCap:tt}:null}}async function Vk(){if(!i)return;let F;try{F=await i()}catch(U){B.error("[plane-link] metrics sample failed:",U?.message||U);return}let q=null;try{q=c?.()??null}catch{}F&&nt({v:mt,ch:"ctl",type:"metrics",metrics:{...F,planeLink:Kk(),...q?{delivery:q}:{}}})}function TN(){i&&(m&&clearInterval(m),Vk(),m=setInterval(Vk,d),m.unref?.())}async function AN(){let F;try{F=await o()}catch(q){B.error("[plane-link] backfill listTracks failed:",q?.message||q);return}nt({v:mt,ch:"ctl",type:"tracks",ids:F.filter(q=>!q.transferredTo).map(q=>q.id).filter(q=>typeof q=="string")});for(let q of F){if(!at)return;let U;try{U=await K(q)}catch{continue}let I=le[q.id]??0,V=U.filter(ye=>ye.seq>I&&typeof ye.id=="string"),Ie=X==="http"?tt:$o;for(let ye=0;ye<V.length;ye+=Ie){if(!await kN(q.id))return;let Le=V.slice(ye,ye+Math.min(Ie,X==="http"?tt:$o)).map(ut=>({trackId:q.id,event:ut}));if(!rn(Le))return}}}function zk(F){!at||!F?.trackId||typeof F.id!="string"||(Kt.push({trackId:F.trackId,event:F}),ht||(ht=setTimeout(om,SR),ht.unref?.()))}let _i=new Map;async function EN(F){let{reqId:q,method:U,path:I,headers:V={},body:Ie}=F,ye=Object.fromEntries(Object.entries(V).filter(([,ut])=>ut!=null)),Le=new AbortController;_i.set(q,Le);try{if(F.host){await _N(F,ye,Le);return}g&&(ye["x-bostrat-token"]=g);let ut=await fetch(k+I,{method:U,headers:ye,body:Ie?Buffer.from(Ie,"base64"):void 0,signal:Le.signal}),on=Object.fromEntries(ut.headers);for(let it of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete on[it];if(on["content-type"]||(on["content-type"]="application/octet-stream"),nt({v:mt,ch:"control",type:"http_head",reqId:q,status:ut.status,headers:on}),ut.body){for await(let it of ut.body)if(!nt({v:mt,ch:"control",type:"http_data",reqId:q,data:Buffer.from(it).toString("base64")}))break}nt({v:mt,ch:"control",type:"http_end",reqId:q,error:null})}finally{_i.delete(q)}}function _N(F,q,U){let{reqId:I,method:V,path:Ie,body:ye}=F,Le=new URL(k);return new Promise(ut=>{let on=K5.request({host:Le.hostname,port:Le.port,method:V,path:Ie,headers:{...q,host:F.host},signal:U.signal},it=>{let{connection:Xk,"keep-alive":Z3,"transfer-encoding":Q3,upgrade:e8,...IN}=it.headers;nt({v:mt,ch:"control",type:"http_head",reqId:I,status:it.statusCode||502,headers:IN}),it.on("data",$N=>{nt({v:mt,ch:"control",type:"http_data",reqId:I,data:Buffer.from($N).toString("base64")})||it.destroy()}),it.on("end",()=>{nt({v:mt,ch:"control",type:"http_end",reqId:I,error:null}),ut()}),it.on("error",()=>{nt({v:mt,ch:"control",type:"http_end",reqId:I,error:"view upstream error"}),ut()})});on.on("error",it=>{nt({v:mt,ch:"control",type:"http_end",reqId:I,error:it?.message||"view request failed"}),ut()}),ye&&on.write(Buffer.from(ye,"base64")),on.end()})}function Jk(){if(Ai||J)return;wt=new lb(e,{headers:{authorization:`Bearer ${t}`}});let F=wt;ur=0,F.on("ping",()=>{ur=Date.now()}),F.on("pong",()=>{ur=Date.now()}),wt.on("open",async()=>{let U=null;try{U=await s?.()||null}catch{U=null}nt({v:mt,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:Cn,installKind:y,...typeof h=="function"?{updatePolicy:h()}:h?{updatePolicy:h}:{},providers:J5(),...U?{agents:U}:{}})}),wt.on("message",U=>{ur=Date.now();let I;try{I=JSON.parse(U)}catch{return}if(I.ch==="ctl"&&I.type==="hello_ok"){at=!0,gt=kR,cr=null,k0(),B.log(`[plane-link] connected to plane as ${n}`);try{P?.()}catch{}if(typeof I.hashKey=="string"&&I.hashKey)try{l?.(I.hashKey)}catch{}try{u?.(typeof I.viewBase=="string"&&I.viewBase?I.viewBase:null)}catch{}I.minNodeVersion&&Cn&&_l(Cn,I.minNodeVersion)&&B.error(`[plane-link] this node is bostrat-node ${Cn}; the plane asks for \u2265${I.minNodeVersion} \u2014 update with: ${mf(y)}`);try{f?.(typeof I.updatePolicy=="string"?I.updatePolicy:null)}catch{}let V=typeof I.latestNodeVersion=="string"?I.latestNodeVersion:null;if(V&&Cn&&_l(Cn,V)&&!xe.has(V)){xe.add(V),B.log(`[plane-link] bostrat-node ${V} is available (this node runs ${Cn}) \u2014 ${mf(y)}`);try{p?.({current:Cn,latest:V,min:I.minNodeVersion??null,installKind:y,required:!!(I.minNodeVersion&&_l(Cn,I.minNodeVersion))})}catch(ye){B.error(`[plane-link] update notice failed: ${ye?.message||ye}`)}}let Ie=I.ingest==="http";X=j==="ws"?"ws":j==="http"||Ie?"http":"ws",j==="http"&&!Ie&&B.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),X==="http"&&B.log("[plane-link] spine rides the HTTPS ingest door"),he=!1,Be=0,tt=$o,gn&&clearInterval(gn),ur=Date.now(),gn=setInterval(()=>{bN(F)||(nt({v:mt,ch:"ctl",type:"ping"}),Gk(),SN().catch(()=>{}),rm(),Kt.length&&!ht&&om())},L),gn.unref?.(),TN(),AN().catch(ye=>B.error("[plane-link] backfill failed:",ye?.message||ye));return}if(I.ch==="ctl"&&I.type==="hello_refused"){let V=typeof I.minNodeVersion=="string"?I.minNodeVersion:null,Ie=typeof I.latestNodeVersion=="string"?I.latestNodeVersion:null;B.error(`[plane-link] plane REFUSED this node: bostrat-node ${Cn??"?"} is below its minimum ${V??"?"} \u2014 ${I.fix||mf(y)}`),gt=xR,cr=0;try{w?.({current:Cn,min:V,latest:Ie,fix:I.fix||mf(y),installKind:y})}catch(ye){B.error(`[plane-link] refusal handler failed: ${ye?.message||ye}`)}return}if(I.ch==="ctl"&&I.type==="removed"){nt({v:mt,ch:"ctl",type:"removed_ack",nodeId:n}),ve({reason:typeof I.reason=="string"?I.reason:"removed",at:Number(I.at)||Date.now(),via:"ctl"});return}if(I.ch==="ctl"&&I.type==="update_policy"){try{f?.(typeof I.mode=="string"?I.mode:null)}catch{}return}if(I.ch==="ctl"&&I.type==="usage_caps"){Dd(I.caps??{},{source:"plane",updatedAt:I.updatedAt??Date.now()}).then(V=>B.log(`[plane-link] spend caps ${Object.keys(V.caps).length?`updated (${Object.keys(V.caps).join(", ")})`:"cleared"} from plane`)).catch(V=>B.error("[plane-link] spend-caps push rejected:",V?.message||V));return}if(I.ch==="ctl"&&I.type==="transferred"&&Array.isArray(I.tracks)){Promise.resolve().then(()=>a?.(I.tracks)).catch(V=>B.error("[plane-link] transfer notice failed:",V?.message||V));return}if(I.ch==="events"&&I.type==="ack"&&Array.isArray(I.ids)){nm(I.ids);return}if(I.ch==="events"&&I.type==="nack"&&Array.isArray(I.ids)){Wk(I.ids,I.reason,I.permanent===!0,I.retryAfterMs??null);return}if(I.ch==="control"&&I.type==="http_open"){EN(I).catch(V=>nt({v:mt,ch:"control",type:"http_end",reqId:I.reqId,error:V?.message||"tunnel error"}));return}if(I.ch==="control"&&I.type==="http_cancel"){_i.get(I.reqId)?.abort();return}if(I.ch==="attach"&&I.type==="open"){let V=new lb(Z+I.path,Array.isArray(I.protocols)?I.protocols:[],I.host?{headers:{host:I.host}}:void 0);Te.set(I.streamId,V),_e.set(I.streamId,[]),V.on("open",()=>{let ye=_e.get(I.streamId)||[];_e.delete(I.streamId);for(let{buf:Le,bin:ut}of ye)V.send(Le,{binary:ut})}),V.on("message",(ye,Le)=>{let ut=Le?ye:Buffer.from(String(ye));nt({v:mt,ch:"attach",type:"data",streamId:I.streamId,data:ut.toString("base64"),...Le?{bin:!0}:{}})});let Ie=()=>{_e.delete(I.streamId),Te.delete(I.streamId)&&nt({v:mt,ch:"attach",type:"close",streamId:I.streamId})};V.on("close",Ie),V.on("error",Ie);return}if(I.ch==="attach"&&I.type==="data"){let V=Te.get(I.streamId);if(!V)return;let Ie=Buffer.from(I.data,"base64"),ye=I.bin===!0,Le=_e.get(I.streamId);Le?Le.push({buf:Ie,bin:ye}):V.send(Ie,{binary:ye});return}if(I.ch==="attach"&&I.type==="close"){let V=Te.get(I.streamId);Te.delete(I.streamId),_e.delete(I.streamId),V?.close();return}});let q=()=>{at&&B.log("[plane-link] disconnected from plane"),at=!1,gn&&(clearInterval(gn),gn=null),m&&(clearInterval(m),m=null),de.clear(),ge={},Kt=[],ht&&(clearTimeout(ht),ht=null),wn.clear();for(let V of Te.values())V.close();Te.clear(),_e.clear();for(let V of _i.values())V.abort();if(_i.clear(),Ai||Hr)return;cr==null&&(cr=Date.now());let U=Date.now()-cr<Y5,I=Math.max(U?X5+Math.random()*Z5:gt,Ve-Date.now(),Di());Hr=setTimeout(()=>{Hr=null,Jk()},I),Hr.unref?.(),U||(gt=Math.min(gt*2,xR))};wt.on("close",q),wt.on("error",U=>{let I=/Unexpected server response: (\d+)/.exec(U?.message||"")?.[1];if(I==="410"){ve({reason:"removed",via:"410"});return}if(I==="401"||I==="403"){let{tripped:V,holdMs:Ie}=ta();V?B.error(`[plane-link] holding every plane call for ${Math.round(Ie/6e4)} min \u2014 three credential refusals in a row (HTTP ${I}) 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(Ie/6e4)} min.`):Date.now()-D>=TR&&(D=Date.now(),B.error(`[plane-link] plane refused this node's credentials (HTTP ${I}) \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)`))}q()})}Ze.on("event",zk),_t().then(Jk);function Yk({bye:F=!0}={}){Ai=!0,Ze.off("event",zk),Hr&&clearTimeout(Hr),lr&&clearTimeout(lr),gn&&clearInterval(gn),m&&clearInterval(m),ht&&clearTimeout(ht),F&&nt({v:mt,ch:"ctl",type:"bye",reason:"shutdown"}),wt?.close()}return{stop:()=>Yk(),status:()=>({connected:at,nodeId:n,cursor:{...le},inflight:de.size,...Kk(),auth:x0(),dormant:J,removed:bu(),deadLinkMs:H,deadLinkRedials:b}),_tick:()=>{Gk(),rm()}}}qo();var wf=(()=>{try{return uG(import.meta.url)("../package.json").version||null}catch{return null}})(),dG=process.env.BOSTRAT_STATE_DIR||kt.join(process.env.HOME||cG.tmpdir(),".bostrat"),$R=kt.join(dG,"self-update"),gf=kt.join($R,"status.json"),pG=1440*6e4,fG=6e4,Il=new Set(["npm-global","git-checkout"]);function RR({kind:e=js(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:Il.has(e)?e==="git-checkout"||mG(t)?"auto":"prompt":"off"}function mG(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var Qt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function hG({kind:e,to:t,from:n=wf,statusFile:r=gf,logFile:o=kt.join($R,"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||kt.resolve(kt.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!Il.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=(y,k="")=>`write_status ${Qt(y)} ${k}`,p=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${Qt(i)}`,f=`
|
|
697
697
|
health() {
|
|
698
698
|
for i in $(seq 1 45); do
|
|
699
699
|
sleep 1
|
|
@@ -758,68 +758,68 @@ if [ -n "$FROM" ] && ${h("$FROM")}; then
|
|
|
758
758
|
fi
|
|
759
759
|
${d("failed",'"deploy-pi.sh v$TO failed AND the rollback failed"')}
|
|
760
760
|
exit 1
|
|
761
|
-
`}async function li(e=gf){try{return JSON.parse(await Ro.readFile(e,"utf8"))}catch{return null}}var mG=new Set(["updating","restarting","rolling-back"]),Il=e=>!!(e&&mG.has(e.status));async function yf({to:e,from:t=wf,kind:n=js(),reason:r="manual",statusFile:o=gf,spawnImpl:s=iG,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!_l.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!El(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await li(o);if(Il(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 Ro.mkdir(kt.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?pu(await hG())||kt.resolve(kt.dirname(process.execPath),".."):null,d=fG({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),p=kt.join(kt.dirname(o),`update-${e}.sh`),f=kt.join(kt.dirname(o),"last.log");await Ro.writeFile(p,d,{mode:448}),await Ro.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let w=_R.existsSync("/run/systemd/system")&&await wG("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 Ro.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 hG(){try{return await Ro.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function wG(e){for(let t of String(process.env.PATH||"").split(kt.delimiter))try{return await Ro.access(kt.join(t,e),_R.constants.X_OK),!0}catch{}return!1}var gG=45*6e4;async function RR({statusFile:e=gf,current:t=wf,now:n=Date.now,staleMs:r=gG}={}){let o=await li(e);if(!o)return null;let s=a=>Ro.writeFile(e,JSON.stringify(a)).catch(()=>{});if(Il(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 OR({isBusy:e,start:t=yf,onStuck:n=null,onStarted:r=null,tickMs:o=dG,stuckMs:s=uG,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=wf,kind:h=js()}){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}}}qo();be();xt();var MR=(...e)=>fetch(...e);function CR(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}async function db(e=process.env){if(!CR(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 MR(`${De(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 pb(e=process.env){if(!CR(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 MR(`${De(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 ui(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await db(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 bf(e,t,{source:n="chat"}={}){return N(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}be();xt();var yG=(...e)=>fetch(...e);async function kf(e,t,n,r=process.env){if(!(De(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 yG(`${De(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 bG(e,t=process.env){return kf("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function NR({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await bG(t,o):await ui(e,o),a=await kf("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function fb(e,t=process.env){let n=await ui(e,t),r=await kf("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function mb(e,t=process.env){return(await kf("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function PR(e,t,n,r=process.env){let{blueprint:o,items:s}=await fb(t,r),a=new Map(s.map(c=>[c.itemId,c])),i=n.filter(c=>!a.get(c)?.bindable);if(i.length){let c=s.filter(l=>l.bindable).map(l=>l.itemId);throw new Error(`unknown/unbindable item(s): ${i.join(", ")} \u2014 bindable ids: ${c.join(", ")||"(none)"}`)}return await N(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}be();xt();var kG=(...e)=>fetch(...e);async function xG(e,t,n,r=process.env){if(!(De(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 kG(`${De(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 DR=72,hb=160;async function BR(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>DR)throw new Error(`the title is the choice as a LABEL \u2014 ${DR} 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 ${hb} chars) a reader can act on without opening the record`);if(c.length>hb)throw new Error(`the summary is one sentence \u2014 ${hb} chars max (yours is ${c.length}); the full story goes in the decision text`);if(!String(r??"").trim())throw new Error("the decision itself is required (what was decided)");return await N(e,{type:"decision.recorded",actor:"orchestrator",source:"orchestrator",payload:{title:i,context:String(n??"").trim(),decision:String(r).trim(),summary:c,alternatives:(Array.isArray(s)?s:[]).map(l=>typeof l=="string"?{label:l}:l).filter(l=>l?.label),...a?{supersedesId:a}:{}}}),{title:i}}function LR(e,t=process.env){return xG("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}Lg();be();import Pl from"node:fs/promises";import Rf from"node:path";import g6 from"node:os";zr();import vG from"node:fs/promises";import jR from"node:path";import SG from"node:os";var FR=()=>jR.join(process.env.BOSTRAT_STATE_DIR||jR.join(process.env.HOME||SG.tmpdir(),".bostrat"),"integrations.json");async function Fr(){try{return JSON.parse(await vG.readFile(FR(),"utf8"))||{}}catch{return{}}}var xf=["linked","prOpened","prMerged","create"],wb=["prompt","auto","off"],TG={create:["prompt","off"]},UR=e=>TG[e]??wb;function vf(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of xf){let s=UR(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 Fs(){let e=await Fr(),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:vf(e),defaultTeam:n}:null}async function Sf(){let e=await Fr(),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:vf(e)}}async function gb(){return vf(await Fr())}async function HR(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${xf.join(", ")}`);for(let[r,o]of t){if(!xf.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${xf.join(", ")}`);let s=UR(r);if(!s.includes(o))throw new Error(s.length<wb.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${wb.join(", ")}`)}let n=await Fr();return n.ticketFlow={...vf(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Bt(FR(),n,{mode:384}),n.ticketFlow}async function $l(){let e=(await Fr()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function AG(){let e=(await Fr()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var EG={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Tf(){let t=(await Fr()).voiceChat||{},n=t.provider||"openai",r=EG[n];if(!r)return null;let o=n==="openai"?await AG():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function WR(){let e=(await Fr()).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 Us(){let e=(await Fr()).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}}be();Yr();fo();Va();import Ol from"node:fs/promises";import iO from"node:path";import e6 from"node:os";import{randomUUID as cO}from"node:crypto";var bb={};sm(bb,{createComment:()=>PG,findIssueUrl:()=>RG,issueStates:()=>LG,mdToAdf:()=>KR,moveIssue:()=>jG,parseIssueRef:()=>$G,resolveIssue:()=>OG,uploadImage:()=>DG,upsertCard:()=>MG,whoami:()=>yb});var di=e=>String(e.baseUrl||"").replace(/\/$/,""),GR=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function Hs(e,t,n,r){let o=await fetch(`${di(e)}${n}`,{method:t,headers:{authorization:GR(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 _G=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,IG=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function $G(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(_G);return r?r[1].toUpperCase():null}let n=t.match(IG);return n?n[1]:null}function RG(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 OG(e,t){let n;try{n=await Hs(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:`${di(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function MG(e,t,{url:n,title:r,subtitle:o}){await Hs(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 CG=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function NG(e){let t=[],n=0;for(let r of e.matchAll(CG)){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 KR(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
762
|
-
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...
|
|
761
|
+
`}async function li(e=gf){try{return JSON.parse(await Ro.readFile(e,"utf8"))}catch{return null}}var wG=new Set(["updating","restarting","rolling-back"]),$l=e=>!!(e&&wG.has(e.status));async function yf({to:e,from:t=wf,kind:n=js(),reason:r="manual",statusFile:o=gf,spawnImpl:s=lG,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!Il.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!_l(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await li(o);if($l(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 Ro.mkdir(kt.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?fu(await gG())||kt.resolve(kt.dirname(process.execPath),".."):null,d=hG({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),p=kt.join(kt.dirname(o),`update-${e}.sh`),f=kt.join(kt.dirname(o),"last.log");await Ro.writeFile(p,d,{mode:448}),await Ro.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let w=IR.existsSync("/run/systemd/system")&&await yG("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 Ro.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 gG(){try{return await Ro.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function yG(e){for(let t of String(process.env.PATH||"").split(kt.delimiter))try{return await Ro.access(kt.join(t,e),IR.constants.X_OK),!0}catch{}return!1}var bG=45*6e4;async function OR({statusFile:e=gf,current:t=wf,now:n=Date.now,staleMs:r=bG}={}){let o=await li(e);if(!o)return null;let s=a=>Ro.writeFile(e,JSON.stringify(a)).catch(()=>{});if($l(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 MR({isBusy:e,start:t=yf,onStuck:n=null,onStarted:r=null,tickMs:o=fG,stuckMs:s=pG,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=wf,kind:h=js()}){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}}}qo();be();vt();var CR=(...e)=>fetch(...e);function NR(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}async function pb(e=process.env){if(!NR(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 CR(`${De(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 fb(e=process.env){if(!NR(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 CR(`${De(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 ui(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await pb(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 bf(e,t,{source:n="chat"}={}){return N(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}be();vt();var kG=(...e)=>fetch(...e);async function kf(e,t,n,r=process.env){if(!(De(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 kG(`${De(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 xG(e,t=process.env){return kf("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function PR({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await xG(t,o):await ui(e,o),a=await kf("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function mb(e,t=process.env){let n=await ui(e,t),r=await kf("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function hb(e,t=process.env){return(await kf("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function DR(e,t,n,r=process.env){let{blueprint:o,items:s}=await mb(t,r),a=new Map(s.map(c=>[c.itemId,c])),i=n.filter(c=>!a.get(c)?.bindable);if(i.length){let c=s.filter(l=>l.bindable).map(l=>l.itemId);throw new Error(`unknown/unbindable item(s): ${i.join(", ")} \u2014 bindable ids: ${c.join(", ")||"(none)"}`)}return await N(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}be();vt();var vG=(...e)=>fetch(...e);async function SG(e,t,n,r=process.env){if(!(De(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 vG(`${De(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 BR=72,wb=160;async function LR(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>BR)throw new Error(`the title is the choice as a LABEL \u2014 ${BR} 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 ${wb} chars) a reader can act on without opening the record`);if(c.length>wb)throw new Error(`the summary is one sentence \u2014 ${wb} chars max (yours is ${c.length}); the full story goes in the decision text`);if(!String(r??"").trim())throw new Error("the decision itself is required (what was decided)");return await N(e,{type:"decision.recorded",actor:"orchestrator",source:"orchestrator",payload:{title:i,context:String(n??"").trim(),decision:String(r).trim(),summary:c,alternatives:(Array.isArray(s)?s:[]).map(l=>typeof l=="string"?{label:l}:l).filter(l=>l?.label),...a?{supersedesId:a}:{}}}),{title:i}}function jR(e,t=process.env){return SG("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}Lg();be();import Dl from"node:fs/promises";import Rf from"node:path";import b6 from"node:os";zr();import TG from"node:fs/promises";import FR from"node:path";import AG from"node:os";var UR=()=>FR.join(process.env.BOSTRAT_STATE_DIR||FR.join(process.env.HOME||AG.tmpdir(),".bostrat"),"integrations.json");async function Fr(){try{return JSON.parse(await TG.readFile(UR(),"utf8"))||{}}catch{return{}}}var xf=["linked","prOpened","prMerged","create"],gb=["prompt","auto","off"],EG={create:["prompt","off"]},HR=e=>EG[e]??gb;function vf(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of xf){let s=HR(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 Fs(){let e=await Fr(),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:vf(e),defaultTeam:n}:null}async function Sf(){let e=await Fr(),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:vf(e)}}async function yb(){return vf(await Fr())}async function WR(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${xf.join(", ")}`);for(let[r,o]of t){if(!xf.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${xf.join(", ")}`);let s=HR(r);if(!s.includes(o))throw new Error(s.length<gb.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${gb.join(", ")}`)}let n=await Fr();return n.ticketFlow={...vf(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Bt(UR(),n,{mode:384}),n.ticketFlow}async function Rl(){let e=(await Fr()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function _G(){let e=(await Fr()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var IG={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Tf(){let t=(await Fr()).voiceChat||{},n=t.provider||"openai",r=IG[n];if(!r)return null;let o=n==="openai"?await _G():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function qR(){let e=(await Fr()).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 Us(){let e=(await Fr()).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}}be();Yr();fo();Va();import Ml from"node:fs/promises";import cO from"node:path";import n6 from"node:os";import{randomUUID as lO}from"node:crypto";var kb={};sm(kb,{createComment:()=>BG,findIssueUrl:()=>MG,issueStates:()=>FG,mdToAdf:()=>VR,moveIssue:()=>UG,parseIssueRef:()=>OG,resolveIssue:()=>CG,uploadImage:()=>LG,upsertCard:()=>NG,whoami:()=>bb});var di=e=>String(e.baseUrl||"").replace(/\/$/,""),KR=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function Hs(e,t,n,r){let o=await fetch(`${di(e)}${n}`,{method:t,headers:{authorization:KR(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 $G=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,RG=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function OG(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match($G);return r?r[1].toUpperCase():null}let n=t.match(RG);return n?n[1]:null}function MG(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 CG(e,t){let n;try{n=await Hs(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:`${di(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function NG(e,t,{url:n,title:r,subtitle:o}){await Hs(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 PG=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function DG(e){let t=[],n=0;for(let r of e.matchAll(PG)){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 VR(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
762
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...DG(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 BG(e,t,n){let r=await Hs(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/comment`,{body:VR(n)});return r?.id?`${di(e)}/browse/${t.key}?focusedCommentId=${r.id}`:null}async function bb(e){let t=await Hs(e,"GET","/rest/api/3/myself"),n=null;try{n=new URL(di(e)).host}catch{n=e.baseUrl??null}return{user:t?.displayName??t?.emailAddress??null,org:n}}async function LG(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(`${di(e)}/rest/api/3/issue/${encodeURIComponent(t)}/attachments`,{method:"POST",headers:{authorization:KR(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??`${di(e)}/rest/api/3/attachment/content/${c.id}`}var jG={new:"unstarted",indeterminate:"started",done:"completed"},GR=e=>jG[e?.key]??"started";async function FG(e,t){let n=encodeURIComponent(t.key),[r,o]=await Promise.all([Hs(e,"GET",`/rest/api/3/issue/${n}?fields=status`),Hs(e,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:GR(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:GR(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function UG(e,t,n){await Hs(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/transitions`,{transition:{id:String(n)}})}var Tb={};sm(Tb,{createComment:()=>JG,createIssue:()=>Sb,createProject:()=>vb,findIssueUrl:()=>KG,issueStates:()=>XG,moveIssue:()=>ZG,parseIssueRef:()=>GG,resolveIssue:()=>VG,teams:()=>pi,uploadImage:()=>YG,upsertCard:()=>zG,whoami:()=>xb});var HG=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function sr(e,t,n={}){let r=await fetch(HG(),{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 WG=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,qG=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function GG(e){let t=String(e||"").trim(),n=t.match(WG);if(n)return n[1].toUpperCase();let r=t.match(qG);return r?r[1]:null}function KG(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 VG(e,t){let r=(await sr(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 zG(e,t,{url:n,title:r,subtitle:o}){let s=await sr(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 JG(e,t,n){let r=await sr(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 xb(e){let t=await sr(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 YG(e,{filename:t,contentType:n,buffer:r}){let o=await sr(e,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
763
763
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
764
764
|
success
|
|
765
765
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
766
766
|
}
|
|
767
|
-
}`,{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
|
|
768
|
-
`)):Af.rmSync(r,{force:!0})}catch{}}function Ab(){return YR().viewBase}function Eb(e=process.env.BOSTRAT_PUBLIC_HOST){return XR(e,"https")}function QR(){let e=Ab();if(e)return{proto:"https",host:e,source:"learned"};let t=Eb();return t?{...t,source:"env"}:null}function Ef(e,t){return e?`${e.proto}://${t}.${e.host}/`:null}function QG(e=process.env.BOSTRAT_PLANE_URL){let t=String(e||"").trim();if(!t)return null;let n=/^(wss?|https?):\/\/([^/?#]+)/i.exec(t);return n?`${/^(wss|https)$/i.test(n[1])?"https":"http"}://${n[2]}`:null}function eO(){let e=String(process.env.BOSTRAT_PUBLIC_URL||"").trim().replace(/\/+$/,"");if(e)return{url:e,source:"env"};let t=QG();return t?{url:t,source:"plane"}:null}var tO="this node has no public app URL for Track links: link it to a plane (bostrat-node init) or set BOSTRAT_PUBLIC_URL in node.env";var lO={linear:{api:Sb,config:Fs},jira:{api:bb,config:Sf}};async function Cb(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(lO)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Ws(e){let t=lO[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function Nb(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 uO=()=>process.env.BOSTRAT_STATE_DIR||iO.join(process.env.HOME||e6.tmpdir(),".bostrat"),Rb=()=>iO.join(uO(),"tickets.json"),Cl=e=>{let t=eO();if(!t)throw new Error(tO);return`${t.url}/track/${e}`},t6=e=>`http://bostrat.globopi/track/${e}`,nO=e=>{let t=0;for(let[n,r]of Object.entries(e))r&&typeof r=="object"&&!r.cardUrl&&(r.cardUrl=t6(n),t++);return t},ar=null,_b=null,Ib=Promise.resolve();async function Nl(){return ar||(_b??=Ol.readFile(Rb(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{ar=e;let n=nO(ar)>0;if(!t&&Ge()){let{records:r,source:o}=await pt("tickets").catch(()=>({records:[],source:"error"}));if(o==="plane"){for(let s of r){if(s.deletedAt||!s.data?.id)continue;let{id:a,updatedAt:i,...c}=s.data;ar[a]=c}nO(ar),n=n||Object.keys(ar).length>0}}return n&&await Ob().catch(()=>{}),ar}).finally(()=>_b=null),_b)}async function Ob(){let e=JSON.stringify({tickets:ar},null,2),t=`${Rb()}.${process.pid}.tmp`;await Ol.mkdir(uO(),{recursive:!0}),await Ol.writeFile(t,e),await Ol.rename(t,Rb())}async function Oo(e=null){if(Ib=Ib.then(Ob,Ob),await Ib.catch(()=>{}),!e)return;let t=ar?.[e];t?It("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):bn("tickets",e).catch(()=>{})}async function ir(e){return(await Nl())[e]??null}async function dO(){return{...await Nl()}}async function pO(){return(await Cb()).length>0}async function mi(e,t,{source:n="api"}={}){let r=await Cb();if(!r.length)throw new Error("no ticketing integration configured \u2014 add a linear or jira entry to ~/.bostrat/integrations.json");let o=null,s=null,a=null;for(let u of r){let d=u.api.parseIssueRef(t);if(d)try{s=await u.api.resolveIssue(u.cfg,d),o=u;break}catch(p){a=p}}if(!o)throw a||new Error(`"${t}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(e.boxes||[]).find(u=>u.name===e.leadBox),c={provider:o.name,key:s.key,issueId:s.issueId,url:s.url,title:s.title,state:s.state,source:n,linkedAt:Date.now(),cardUrl:Cl(e.id),track:{name:e.name,branch:e.workspace?.branch??null,leadAgent:i?.agent??null,cwd:e.cwd??null,...e.workspace?.kind==="multi"?{repos:(e.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await Nl())[e.id]=c,await Oo(e.id),await N({id:e.id,cwd:e.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await Ml(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 Rl(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await hO(e.id,c,"linked"),c}async function Pb(e){let t=await Nl();return t[e]?(delete t[e],await Oo(e),!0):!1}var Mb=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function fO(e,t){if(!e.track?.cwd)return null;let n=po(await K({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function n6(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Mb(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${Mb(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function r6(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(Mb(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function Ml(e,t){let n=await Ws(t);if(!n)return;let r=await fO(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:t.cardUrl??Cl(e),title:`Bostrat Track: ${t.track.name}`,subtitle:r6(t,r)})}function o6(e){return Ef(QR(),e)}var _f=e=>o6(e)??"(no public URL on this node \u2014 link it to a plane)",s6={linked:"linked",opened:"prOpened",merged:"prMerged"};function a6(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 mO(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await Oo(e),await N({id:e,cwd:t.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:t.key,url:t.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function hO(e,t,n){let r=await Ws(t);if(!r)return;let o=r.cfg.ticketFlow?.[s6[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=Nb(s,a),c=a6(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 mO(e,t,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await N({id:e,cwd:t.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:cO(),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 wO(e){let t=await ir(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Ws(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=Nb(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await Oo(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 Db(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await ir(e);if(!r)throw new Error("no ticket linked to this Track");let o=await Ws(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Nb(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 Oo(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await mO(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function gO(e,t){let n=await ir(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Ws(n);if(!r)throw new Error("no ticketing integration configured");let o=String(t||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await N({id:e,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function yO(e,t,n){if(!t)throw new Error("offerId required");return vo(`tkt:${e.id}:${t}`,()=>i6(e,t,n))}async function i6(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 Db(e.id,o.payload.to?.id,{mode:"offer"})),await N({id:e.id,cwd:e.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:t,action:n?"moved":"dismiss",...s??{}}}var rO=50;async function bO(){let e=await Fs();if(!e)throw new Error("no ticketing integration configured");return pi(e)}function c6(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>rO)throw new Error(`too many issues (${n.length}); cap is ${rO} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function l6(e,t,n,r){let o=r??await pi(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 ir(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 kO(e,t){let n=await Fs();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=c6(t),o=await pi(n),s=await l6(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=cO();return await N({id:e.id,cwd:e.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function xO(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 N({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};let a=await Fs();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let w=(await pi(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 xb(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 vb(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 N({id:e.id,cwd:e.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,team:{key:c.key,name:c.name},items:l,failed:u}}),await N({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",createdCount:l.length,failedCount:u.length}});let p=l.filter(w=>w.kind==="issue"),f=null;return i.linkCurrentTrack&&p.length===1&&!await ir(e.id)&&(f=await mi(e,p[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:f?f.key:null}}async function Rl(e,t){let n=await Ws(e);n&&await n.api.createComment(n.cfg,e,t)}async function u6(e,t,n){let r=await Ws(e);if(!r)return null;let o=await Mr(t);if(!o)return null;let s=$n(o,n);if(!s)return null;let a=await Ol.readFile(dn(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 oO=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),$b=new Map;function d6(e,t){if(oO()<=0)return Ml(e,t);if($b.has(e))return;let n=setTimeout(()=>{$b.delete(e),ir(e).then(r=>r?Ml(e,r):null).catch(()=>{})},oO());n.unref?.(),$b.set(e,n)}var sO=new Map;async function aO(e,t,n){let r=sO.get(e)??0;Date.now()-r<600*1e3||(sO.set(e,Date.now()),await N({id:e,cwd:t},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function p6(e,{resolveTrack:t=null}={}){let n=await ir(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await Cb())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 mi(s,o,{source:"chat"})}catch(a){await aO(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 Oo(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Oo(e.trackId),await Ml(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 Oo(e.trackId),await Ml(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await fO(n,e.trackId).catch(()=>null):null;await Rl(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
767
|
+
}`,{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 XG(e,t){let n=await sr(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 ZG(e,t,n){if(!(await sr(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 pi(e){return((await sr(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var zR=255;async function vb(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>zR,a=s?`${o.slice(0,zR-1).trimEnd()}\u2026`:o,i=await sr(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 Sb(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await sr(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}}jc();import Af from"node:fs";import Ab from"node:path";import QG from"node:os";var e6=()=>process.env.BOSTRAT_STATE_DIR||Ab.join(process.env.HOME||QG.tmpdir(),".bostrat"),JR=()=>Ab.join(e6(),"plane-link","public-host.json"),YR="this node has no public host for Views: link it to a plane that sets PLANE_VIEW_BASE (bostrat-node init), or set BOSTRAT_PUBLIC_HOST in node.env for a plane-less node",fi=null;function XR(){if(fi)return fi;try{let e=JSON.parse(Af.readFileSync(JR(),"utf8"));fi={viewBase:typeof e?.viewBase=="string"&&e.viewBase?e.viewBase:null}}catch{fi={viewBase:null}}return fi}function ZR(e,t){let n=String(e||"").trim();if(!n)return null;let r=/^(https?):\/\/(.+?)\/?$/i.exec(n),o=r?r[1].toLowerCase():t,s=(r?r[2]:n).replace(/\/.*$/,"").replace(/:\d+$/,"").toLowerCase();return s?{proto:o,host:s}:null}function QR(e){let t=typeof e=="string"&&e.trim()?ZR(e,"https").host:null;if(XR().viewBase===t)return;fi={viewBase:t};let r=JR();try{t?(Af.mkdirSync(Ab.dirname(r),{recursive:!0}),Af.writeFileSync(r,JSON.stringify({viewBase:t,learnedAt:new Date().toISOString()})+`
|
|
768
|
+
`)):Af.rmSync(r,{force:!0})}catch{}}function Eb(){return XR().viewBase}function _b(e=process.env.BOSTRAT_PUBLIC_HOST){return ZR(e,"https")}function eO(){let e=Eb();if(e)return{proto:"https",host:e,source:"learned"};let t=_b();return t?{...t,source:"env"}:null}function Ef(e,t){return e?`${e.proto}://${t}.${e.host}/`:null}function t6(e=process.env.BOSTRAT_PLANE_URL){let t=String(e||"").trim();if(!t)return null;let n=/^(wss?|https?):\/\/([^/?#]+)/i.exec(t);return n?`${/^(wss|https)$/i.test(n[1])?"https":"http"}://${n[2]}`:null}function tO(){let e=String(process.env.BOSTRAT_PUBLIC_URL||"").trim().replace(/\/+$/,"");if(e)return{url:e,source:"env"};let t=t6();return t?{url:t,source:"plane"}:null}var nO="this node has no public app URL for Track links: link it to a plane (bostrat-node init) or set BOSTRAT_PUBLIC_URL in node.env";var uO={linear:{api:Tb,config:Fs},jira:{api:kb,config:Sf}};async function Nb(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(uO)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Ws(e){let t=uO[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function Pb(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 dO=()=>process.env.BOSTRAT_STATE_DIR||cO.join(process.env.HOME||n6.tmpdir(),".bostrat"),Ob=()=>cO.join(dO(),"tickets.json"),Nl=e=>{let t=tO();if(!t)throw new Error(nO);return`${t.url}/track/${e}`},r6=e=>`http://bostrat.globopi/track/${e}`,rO=e=>{let t=0;for(let[n,r]of Object.entries(e))r&&typeof r=="object"&&!r.cardUrl&&(r.cardUrl=r6(n),t++);return t},ar=null,Ib=null,$b=Promise.resolve();async function Pl(){return ar||(Ib??=Ml.readFile(Ob(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{ar=e;let n=rO(ar)>0;if(!t&&Ge()){let{records:r,source:o}=await pt("tickets").catch(()=>({records:[],source:"error"}));if(o==="plane"){for(let s of r){if(s.deletedAt||!s.data?.id)continue;let{id:a,updatedAt:i,...c}=s.data;ar[a]=c}rO(ar),n=n||Object.keys(ar).length>0}}return n&&await Mb().catch(()=>{}),ar}).finally(()=>Ib=null),Ib)}async function Mb(){let e=JSON.stringify({tickets:ar},null,2),t=`${Ob()}.${process.pid}.tmp`;await Ml.mkdir(dO(),{recursive:!0}),await Ml.writeFile(t,e),await Ml.rename(t,Ob())}async function Oo(e=null){if($b=$b.then(Mb,Mb),await $b.catch(()=>{}),!e)return;let t=ar?.[e];t?It("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):bn("tickets",e).catch(()=>{})}async function ir(e){return(await Pl())[e]??null}async function pO(){return{...await Pl()}}async function fO(){return(await Nb()).length>0}async function mi(e,t,{source:n="api"}={}){let r=await Nb();if(!r.length)throw new Error("no ticketing integration configured \u2014 add a linear or jira entry to ~/.bostrat/integrations.json");let o=null,s=null,a=null;for(let u of r){let d=u.api.parseIssueRef(t);if(d)try{s=await u.api.resolveIssue(u.cfg,d),o=u;break}catch(p){a=p}}if(!o)throw a||new Error(`"${t}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(e.boxes||[]).find(u=>u.name===e.leadBox),c={provider:o.name,key:s.key,issueId:s.issueId,url:s.url,title:s.title,state:s.state,source:n,linkedAt:Date.now(),cardUrl:Nl(e.id),track:{name:e.name,branch:e.workspace?.branch??null,leadAgent:i?.agent??null,cwd:e.cwd??null,...e.workspace?.kind==="multi"?{repos:(e.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await Pl())[e.id]=c,await Oo(e.id),await N({id:e.id,cwd:e.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await 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 Ol(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await wO(e.id,c,"linked"),c}async function Db(e){let t=await Pl();return t[e]?(delete t[e],await Oo(e),!0):!1}var Cb=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function mO(e,t){if(!e.track?.cwd)return null;let n=po(await K({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function o6(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Cb(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${Cb(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function s6(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(Cb(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function Cl(e,t){let n=await Ws(t);if(!n)return;let r=await mO(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:t.cardUrl??Nl(e),title:`Bostrat Track: ${t.track.name}`,subtitle:s6(t,r)})}function a6(e){return Ef(eO(),e)}var _f=e=>a6(e)??"(no public URL on this node \u2014 link it to a plane)",i6={linked:"linked",opened:"prOpened",merged:"prMerged"};function c6(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 hO(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await Oo(e),await N({id:e,cwd:t.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:t.key,url:t.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function wO(e,t,n){let r=await Ws(t);if(!r)return;let o=r.cfg.ticketFlow?.[i6[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=Pb(s,a),c=c6(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 hO(e,t,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await N({id:e,cwd:t.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:lO(),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 gO(e){let t=await ir(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Ws(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=Pb(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await Oo(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 Bb(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await ir(e);if(!r)throw new Error("no ticket linked to this Track");let o=await Ws(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Pb(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 Oo(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await hO(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function yO(e,t){let n=await ir(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Ws(n);if(!r)throw new Error("no ticketing integration configured");let o=String(t||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await N({id:e,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function bO(e,t,n){if(!t)throw new Error("offerId required");return vo(`tkt:${e.id}:${t}`,()=>l6(e,t,n))}async function l6(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 Bb(e.id,o.payload.to?.id,{mode:"offer"})),await N({id:e.id,cwd:e.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:t,action:n?"moved":"dismiss",...s??{}}}var oO=50;async function kO(){let e=await Fs();if(!e)throw new Error("no ticketing integration configured");return pi(e)}function u6(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>oO)throw new Error(`too many issues (${n.length}); cap is ${oO} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function d6(e,t,n,r){let o=r??await pi(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 ir(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 xO(e,t){let n=await Fs();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=u6(t),o=await pi(n),s=await d6(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=lO();return await N({id:e.id,cwd:e.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function vO(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 N({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};let a=await Fs();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let w=(await pi(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 vb(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 Sb(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 N({id:e.id,cwd:e.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,team:{key:c.key,name:c.name},items:l,failed:u}}),await N({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",createdCount:l.length,failedCount:u.length}});let p=l.filter(w=>w.kind==="issue"),f=null;return i.linkCurrentTrack&&p.length===1&&!await ir(e.id)&&(f=await mi(e,p[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:f?f.key:null}}async function Ol(e,t){let n=await Ws(e);n&&await n.api.createComment(n.cfg,e,t)}async function p6(e,t,n){let r=await Ws(e);if(!r)return null;let o=await Mr(t);if(!o)return null;let s=$n(o,n);if(!s)return null;let a=await Ml.readFile(dn(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 sO=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),Rb=new Map;function f6(e,t){if(sO()<=0)return Cl(e,t);if(Rb.has(e))return;let n=setTimeout(()=>{Rb.delete(e),ir(e).then(r=>r?Cl(e,r):null).catch(()=>{})},sO());n.unref?.(),Rb.set(e,n)}var aO=new Map;async function iO(e,t,n){let r=aO.get(e)??0;Date.now()-r<600*1e3||(aO.set(e,Date.now()),await N({id:e,cwd:t},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function m6(e,{resolveTrack:t=null}={}){let n=await ir(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await Nb())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 mi(s,o,{source:"chat"})}catch(a){await iO(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 Oo(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Oo(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 Oo(e.trackId),await Cl(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await mO(n,e.trackId).catch(()=>null):null;await Ol(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
769
769
|
|
|
770
|
-
Agent cost for this Track: ${
|
|
770
|
+
Agent cost for this Track: ${o6(o)}.`:"")),await wO(e.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await p6(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await Ol(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
771
771
|
|
|
772
|
-
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await
|
|
773
|
-
_(runs on the Track's branch; a Bostrat-fronted preview, not a public deployment)_`);break}case"cost.recorded":await
|
|
774
|
-
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${
|
|
772
|
+
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await Ol(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 Ol(n,`**Bostrat** \u2014 live preview **${r.name}**: ${_f(r.slug)}
|
|
773
|
+
_(runs on the Track's branch; a Bostrat-fronted preview, not a public deployment)_`);break}case"cost.recorded":await f6(e.trackId,n);break;default:break}}catch(o){let s=t?await t(e.trackId).catch(()=>null):null;await iO(e.trackId,s?.cwd??null,o)}}function SO({resolveTrack:e}){Ze.on("event",t=>{m6(t,{resolveTrack:e}).catch(()=>{})})}async function TO(){if(!Ge())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await pt("tickets");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Pl(),r=0;for(let[o,s]of Object.entries(n))await It("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}gs();Va();import h6 from"ws";var AO=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function If(e,t,n={}){let r=await fetch(`${AO()}/${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 Lb=(e,t)=>If("chat.postMessage",e,t),qs=(e,t)=>If("chat.update",e,t);async function Mt(e,t){await fetch(e,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(t)}).catch(()=>{})}async function w6(e,t,n={}){let r=await fetch(`${AO()}/${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 EO(e,{filename:t,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await w6("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 If("files.completeUploadExternal",e,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var g6=2200;function _O({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 If("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new h6(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=h=>{try{o.send(JSON.stringify(h?{envelope_id:d.envelope_id,payload:h}:{envelope_id:d.envelope_id}))}catch{}},f=!1,w=setTimeout(()=>{f=!0,p()},g6);Promise.resolve().then(()=>t(d)).then(h=>{clearTimeout(w),!f&&(f=!0,p(d.accepts_response_payload?h:void 0))},h=>{clearTimeout(w),f||(f=!0,p()),n(`envelope handler failed: ${h?.message||h}`)})}),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 RO=()=>process.env.BOSTRAT_STATE_DIR||Rf.join(process.env.HOME||b6.tmpdir(),".bostrat"),Wb=()=>Rf.join(RO(),"slack.json"),$f=null,jb=null,Fb=Promise.resolve();async function Gb(){return $f||(jb??=Dl.readFile(Wb(),"utf8").then(e=>JSON.parse(e)?.threads??{}).catch(()=>({})).then(e=>$f=e).finally(()=>jb=null),jb)}async function IO(){let e=JSON.stringify({threads:$f},null,2),t=`${Wb()}.${process.pid}.tmp`;await Dl.mkdir(RO(),{recursive:!0}),await Dl.writeFile(t,e),await Dl.rename(t,Wb())}async function Nn(){Fb=Fb.then(IO,IO),await Fb.catch(()=>{})}async function Kb(e){return(await Gb())[e]??null}async function k6(e,t){let n=await Gb();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 Bl(e,t){let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function x6(e,t=500){let n=String(e??"").trim().split(/\n\s*\n/)[0]??"";return Bl(n.replace(/\n+/g," "),t)}function OO(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(Bl(e.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${t}
|
|
774
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${Nl(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function qb(e,t){await qs(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:OO(t)})}async function Co(e,t,n,r=void 0){return Lb(e.botToken,{channel:t.channel,thread_ts:t.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var $O=new Set;function Mo(e){$O.add(e),e.catch(()=>{}).finally(()=>$O.delete(e))}var qe=e=>({response_type:"ephemeral",text:e}),v6="*/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._",S6=["claude","cursor","codex"];function Ub(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 Hb=(e,t)=>qe(`No live Track matches \u201C${$e(t)}\u201D. Live: ${e.map(n=>`\`${$e(n.name)}\``).join(" \xB7 ")||"none"}`);async function MO(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 Mt(o,{...qe(`At capacity \u2014 ${a.length} live Tracks (limit ${t.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!t.channel){await Mt(o,{...qe("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?Rf.basename(c.base):"fresh workspace"),lead:"claude"},u=await Lb(t.botToken,{channel:t.channel,text:`Track started: ${l.name}`,blocks:OO(l),unfurl_links:!1});l.ts=u.ts,(await Gb())[i.id]=l,await Nn(),await Mt(o,{...qe(`\u2713 *${$e(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await Mt(o,{...qe(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function T6(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.archiveTrack(n.id);let s=await Kb(n.id);s&&(s.archivedAt=Date.now(),await Nn(),await qb(t,s).catch(()=>{})),await Mt(r,{...qe(`\u2713 *${$e(n.name)}* archived.`),replace_original:!0})}catch(s){await Mt(r,{...qe(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function A6(e,t,{track:n,agent:r,responseUrl:o}){try{await e.deps.startLeadHandoff(n.id,r),await Mt(o,{...qe(`\u2713 Handing *${$e(n.name)}* to ${$e(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await Mt(o,{...qe(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function E6(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 Mt(r,{...qe(`\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 Mt(r,{...qe(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function _6(e,t){let n=[...t.slice(0,99).map(r=>({text:{type:"plain_text",text:Bl(`${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 I6(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?Rf.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(`
|
|
775
775
|
`)+(o?`
|
|
776
|
-
_${o} archived_`:"")}async function
|
|
777
|
-
\u2713 *${$e(a)}*`}}]}).catch(()=>{})}}catch(c){await Mt(n.response_url,qe(`Couldn't answer: ${c?.message||c}`))}}async function
|
|
778
|
-
\u2713 _answered_`}}]})}async function
|
|
779
|
-
_runs on the Track's branch; a Bostrat-fronted preview, not a public deployment_`}}]);return}case"mock.state.changed":return
|
|
780
|
-
`)){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
|
|
781
|
-
`)}be();fw();import iK from"node:fs/promises";import cK from"node:path";sd();ud();fo();var Mf=e=>e.payload?.ts??e.ts,tK=12e4,fn=e=>Math.round(e*1e4)/1e4,nK=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{},byTrack:new Map});function rK(e,{since:t}){let{track:n,events:r,brief:o}=e,s=x=>Mf(x)>=t,a=gr([gc(n?.name),gc(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let x of r)if(x.type==="subtask.delegated"&&x.boxId&&x.payload?.task){let S=gr(x.payload.task);S&&i.set(x.boxId,S)}let c=new Map,l=x=>{let S=x||"unattributed";return c.has(S)||c.set(S,{segments:[u(null)],lastTs:null,provider:null}),c.get(S)},u=(x,{shape:S="initial",continuation:_=!1,text:M=null,ts:P=null}={})=>({textCat:x,shape:S,continuation:_,text:M,ts:P,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((x,S)=>Mf(x)-Mf(S)),p=0,f=0,w=0,h={turns:0};for(let x of d){let S=l(x.boxId),_=S.segments[S.segments.length-1],M=Mf(x);switch(S.lastTs!=null&&M>S.lastTs&&s(x)&&(_.activeMs+=Math.min(M-S.lastTs,tK)),S.lastTs=M,x.provider&&(S.provider=x.provider),x.type){case"turn.user":{let P=x.payload?.text;if(x.source==="hook"||!String(P||"").trim()||x.payload?.note||kc(P))break;let j=gr(P),A=!!x.payload?.kind,$=A||Ev(P),L=u(j,{shape:A?"injected":$?"follow-up":"novel-ask",continuation:!j&&$,text:jO(P),ts:M});S.segments.push(L),s(x)&&(L.turns+=1);break}case"peer.message":{let P=x.payload?.text;if(!String(P||"").trim())break;let j=gr(P),A=u(j,{shape:"peer",continuation:!j,text:jO(P),ts:M});S.segments.push(A),s(x)&&(A.turns+=1);break}case"tool.called":{if(x.source==="transcript")break;let P=x.payload?.tool;P&&(_.tools[P]=(_.tools[P]||0)+1),s(x)&&(_.toolCalls+=1);break}case"cost.recorded":{if(!s(x))break;let{usd:P,tokens:j,model:A,unpriced:$}=sn(x);$&&(h.turns+=1),_.usd+=P,j&&(_.tokens.input+=j.input,_.tokens.output+=j.output,_.tokens.cacheRead+=j.cacheRead,_.tokens.cacheWrite+=j.cacheWrite),_.models[A]=(_.models[A]||0)+P,x.provider&&(_.provider=x.provider);break}case"handoff.done":s(x)&&(p+=1);break;case"subtask.merged":s(x)&&(f+=1);break;case"error":s(x)&&x.payload?.kind==="subtask-failed"&&(w+=1);break;default:break}}let y=new Map,k={usd:0,turns:0,byShape:{},samples:[]},g=!1;for(let[x,S]of c){let _=null;for(let M of S.segments){let P=null,j=null;if(M.textCat)P=M.textCat.category,j="text";else if(i.get(x))P=i.get(x).category,j="boxTask";else if(M.continuation&&_)P=_,j="inherited";else{let R=Av(M.tools);R?(P=R.category,j="toolMix"):a&&(P=a.category,j="hint")}if(P&&(_=P),!(M.usd>0||M.activeMs>0||M.turns>0||M.toolCalls>0))continue;let $=P??"uncategorized";y.has($)||y.set($,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{}});let L=y.get($);L.usd+=M.usd,L.activeMs+=M.activeMs,L.turns+=M.turns,L.toolCalls+=M.toolCalls,j&&(L.provenance[j]=(L.provenance[j]||0)+M.usd);for(let[R,T]of Object.entries(M.models))L.models[R]=(L.models[R]||0)+T;let H=M.provider||S.provider;H&&(L.providers[H]=(L.providers[H]||0)+M.usd),H==="cursor"&&M.usd===0&&(g=!0),P||(k.usd+=M.usd,k.turns+=M.turns,k.byShape[M.shape]=(k.byShape[M.shape]||0)+M.usd,M.usd>0&&M.text&&k.samples.push({trackId:n?.id,track:n?.name||n?.id,shape:M.shape,usd:fn(M.usd),ts:M.ts,text:M.text}))}}return{perCategory:y,uncategorized:k,handoffs:p,merged:f,failed:w,sawCursorActivity:g,unpricedTurns:h.turns}}function Vb(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 k of e||[]){let{track:g,events:x}=k;if(!x?.length){a+=1;continue}let{perCategory:S,uncategorized:_,handoffs:M,merged:P,failed:j,sawCursorActivity:A,unpricedTurns:$}=rK(k,{since:r});A&&(i=!0),c+=$,l.usd+=_.usd,l.turns+=_.turns;for(let[H,R]of Object.entries(_.byShape))l.byShape[H]=(l.byShape[H]||0)+R;l.samples.push(..._.samples);let L={trackId:g?.id,name:g?.name||g?.id,archived:!!g?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:M,subtasksMerged:P,subtasksFailed:j};for(let[H,R]of S){L.usd+=R.usd,L.activeMs+=R.activeMs,L.turns+=R.turns,L.categories[H]={usd:fn(R.usd),activeMs:R.activeMs};for(let[W,B]of Object.entries(R.models))L.models[W]=fn((L.models[W]||0)+B);for(let[W,B]of Object.entries(R.providers))L.providers[W]=fn((L.providers[W]||0)+B);o.has(H)||o.set(H,nK());let T=o.get(H);T.usd+=R.usd,T.activeMs+=R.activeMs,T.turns+=R.turns,T.toolCalls+=R.toolCalls;for(let[W,B]of Object.entries(R.models))T.models[W]=(T.models[W]||0)+B;for(let[W,B]of Object.entries(R.providers))T.providers[W]=(T.providers[W]||0)+B;for(let[W,B]of Object.entries(R.provenance))T.provenance[W]=(T.provenance[W]||0)+B;let C=T.byTrack.get(g?.id)||{usd:0,activeMs:0};C.usd+=R.usd,C.activeMs+=R.activeMs,T.byTrack.set(g?.id,C)}(L.usd>0||L.activeMs>0||L.turns>0)&&(L.usd=fn(L.usd),L.dominantCategory=aK(L.categories),s.push(L))}let u=[...o.values()].reduce((k,g)=>k+g.usd,0),d=[...o.values()].reduce((k,g)=>k+g.activeMs,0),p=u>0?"usd":"time",f=p==="usd"?u:d,w=new Map((e||[]).map(k=>[k.track?.id,k.track?.name||k.track?.id])),h=[...o.entries()].map(([k,g])=>({category:k,sharePct:f>0?Math.round((p==="usd"?g.usd:g.activeMs)/f*100):0,shareBasis:p,usd:fn(g.usd),activeMs:g.activeMs,turns:g.turns,toolCalls:g.toolCalls,models:Ll(g.models),providers:Ll(g.providers),provenance:Ll(g.provenance),topWorkstreams:[...g.byTrack.entries()].sort((x,S)=>S[1].usd-x[1].usd||S[1].activeMs-x[1].activeMs).slice(0,2).map(([x,S])=>({trackId:x,name:w.get(x),usd:fn(S.usd),activeMs:S.activeMs}))})).sort((k,g)=>g.usd-k.usd||g.activeMs-k.activeMs);s.sort((k,g)=>g.usd-k.usd||g.activeMs-k.activeMs);let y={};for(let k of o.values())for(let[g,x]of Object.entries(k.provenance))y[g]=(y[g]||0)+x;return{since:r,capturedAt:n,totalUsd:fn(u),activeMs:d,turns:[...o.values()].reduce((k,g)=>k+g.turns,0),bullets:h,workstreams:s,provenance:Ll(y),gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c,uncategorized:{usd:fn(l.usd),turns:l.turns,sharePct:u>0?Math.round(l.usd/u*100):0,byShape:Ll(l.byShape),top:l.samples.sort((k,g)=>g.usd-k.usd).slice(0,oK)}}}}var oK=25,LO=160,jO=e=>{let t=String(e||"").replace(/\s+/g," ").trim();return t.length>LO?`${t.slice(0,LO).trimEnd()}\u2026`:t},sK=10;function FO(e,{thresholdPct:t=sK}={}){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:fn(o),totalUsd:fn(r),turns:n.turns||0,inheritedPct:r>0?Math.round((e?.provenance?.inherited||0)/r*100):0,problems:i}}var aK=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,fn(n)]));var Ks={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function lK(e){let t=String(e||"7d");return t in Ks?{key:t,ms:Ks[t]}:{key:"7d",ms:Ks["7d"]}}function uK(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=Fd(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 dK(e){if(!e?.cwd)return null;try{let n=(await iK.readFile(cK.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 zb(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}:lK(t),d=[];for(let w of e||[]){let h=await K(w),y=uK(w,h);i&&c<o&&(y=y.filter(k=>(k.ts??0)<=c)),d.push({track:w,events:y,brief:await dK(w)})}let p=Vb(d,{windowMs:u,now:c}),f=l==="7d"&&c===o?p:Vb(d,{windowMs:Ks["7d"],now:o});return{window:l,...p,categorization:FO(f)}}var jl=e=>`$${Number(e||0).toFixed(2)}`,UO=e=>{let t=Math.round((e||0)/6e4);return t>=60?`${Math.floor(t/60)}h${String(t%60).padStart(2,"0")}`:`${t}m`},Gs=(e,t)=>String(e).padEnd(t);function HO(e){let t=[];t.push(`USAGE ROUNDUP \xB7 ${e.window} \xB7 ${jl(e.totalUsd)} \xB7 ${UO(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(` ${Gs(`${a.shareBasis==="time"?"~":""}${a.sharePct}%`,5)} ${Gs(a.category,15)} ${Gs(jl(a.usd),10)} ${Gs(UO(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 ${jl(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} ${jl(i)}`).join(" \xB7 ")||"none")),s.top?.length||t.push(" (nothing unmatched carried cost in this window)");for(let a of s.top||[])t.push(` ${Gs(jl(a.usd),8)} ${Gs(a.shape,10)} ${Gs(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(`
|
|
782
|
-
`)}yr();var Po=36e5,
|
|
783
|
-
`).slice(-3).join(" | ").slice(0,300);d!==
|
|
784
|
-
`).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
|
|
785
|
-
`).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
|
|
786
|
-
`),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
|
|
787
|
-
`,{mode:384}),jf.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function
|
|
776
|
+
_${o} archived_`:"")}async function $6(e,t){let n=await Us();if(!n)return qe("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 qe("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?(Mo(MO(e,n,{name:u,repo:l.path,responseUrl:t.response_url})),qe(`Starting *${$e(u)}* on ${$e(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:_6(u,i)}}case"archive":{if(!a.length)return qe("Usage: `/bostrat archive <track>`");let{match:i,live:c}=Ub(await r.listTracks(),a.join(" "));return i?(Mo(T6(e,n,{track:i,responseUrl:t.response_url})),qe(`Archiving *${$e(i.name)}*\u2026`)):Hb(c,a.join(" "))}case"handoff":{let i=r.knownAgents??S6,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return qe(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:d}=Ub(await r.listTracks(),l);return u?(Mo(A6(e,n,{track:u,agent:c,responseUrl:t.response_url})),qe(`Handing *${$e(u.name)}* to ${$e(c)}\u2026`)):Hb(d,l)}case"stop":{if(!a.length)return qe("Usage: `/bostrat stop <track>`");let{match:i,live:c}=Ub(await r.listTracks(),a.join(" "));return i?(Mo(E6(e,n,{track:i,responseUrl:t.response_url})),qe(`Stopping *${$e(i.name)}*\u2026`)):Hb(c,a.join(" "))}case"list":return qe(await I6(r));default:return qe(v6)}}async function R6(e,t,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Kb(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 Nn(),await qs(t.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${$e(l||"question")}~
|
|
777
|
+
\u2713 *${$e(a)}*`}}]}).catch(()=>{})}}catch(c){await Mt(n.response_url,qe(`Couldn't answer: ${c?.message||c}`))}}async function O6(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)?ho.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,Ca(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 Mt(n.response_url,qe(`Couldn't act on the offer: ${c?.message||c}`))}}async function M6(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 Mt(n.response_url,qe(`Couldn't act on the offer: ${i?.message||i}`))}}async function C6(e,t){let n=t.actions?.[0];if(!n?.action_id)return;let r=await Us();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 Mt(t.response_url,{...qe(`Starting *${$e(s)}*\u2026`),replace_original:!0}),Mo(MO(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"?Mo(R6(e,r,t,o)):n.action_id==="bostrat-ship"?Mo(O6(e,r,t,o)):n.action_id==="bostrat-pr-exists"&&Mo(M6(e,r,t,o))}async function N6(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 k6(t.channel,t.thread_ts);if(!n)return;let r=await Us();if(r)try{await e.deps.sayToLead(n.trackId,t.text.trim())}catch(o){await Co(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function P6(e){return async t=>{if(t.type==="slash_commands")return $6(e,t.payload);if(t.type==="interactive"){await C6(e,t.payload);return}if(t.type==="events_api"){await N6(e,t.payload?.event);return}}}var D6=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 B6(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:Bl(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 L6(e,t,n){let r=n.payload||{};if(!r.promptId||t.prompt?.promptId===r.promptId)return;t.prompt&&await qs(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 Co(e,t,r.question||"The lead needs an answer",B6(n.trackId,r));t.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await Nn()}async function j6(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 Nn(),await qs(e.botToken,{channel:t.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${$e(s||"question")}~
|
|
778
|
+
\u2713 _answered_`}}]})}async function F6(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 Co(e,t,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${$e(Bl(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await Nn()}async function U6(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await Nn();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 qs(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function H6(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 Co(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 Nn()}async function W6(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await Nn();let s=r.choice==="dismiss"?"dismissed":r.choice==="new"?"\u2713 opening a new PR":"\u2713 pushing to the open PR";await qs(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function q6(e,t,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??G6)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await EO(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 Co(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 G6(e,t){let n=await Mr(e);if(!n)return null;let r=$n(n,t);if(!r)return null;let o=await Dl.readFile(dn(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function K6(e,t){if(!D6.has(t.type))return;let n=await Kb(t.trackId);if(!n)return;let r=await Us();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 Nn(),await qb(r,n);return;case"turn.agent":{if(!o.text)return;let s=x6(o.text);if(!s)return;await Co(r,n,s,[{type:"section",text:{type:"mrkdwn",text:$e(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${Nl(t.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return L6(r,n,t);case"prompt.answered":return j6(r,n,t);case"pr.offered":return F6(r,n,t);case"pr.offer.answered":return U6(r,n,t);case"pr.exists.offered":return H6(r,n,t);case"pr.exists.answered":return W6(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 Co(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 Nn(),await qb(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await Co(r,n,`Live preview ${o.name}: ${_f(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${$e(o.name)}*: ${_f(o.slug)}
|
|
779
|
+
_runs on the Track's branch; a Bostrat-fronted preview, not a public deployment_`}}]);return}case"mock.state.changed":return q6(r,n,t,e.deps)}}function CO(e){let t={deps:e};Ze.on("event",s=>{K6(t,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await Us()||(r&&clearInterval(r),r=null,n=_O({getToken:async()=>(await Us())?.appToken,onEnvelope:P6(t),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}vn();be();ty();yn();Hw();yr();import V6 from"node:fs/promises";function z6(e){let t=new Map;for(let n of String(e||"").split(`
|
|
780
|
+
`)){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 J6(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 Y6=new Set(["Write","Edit","NotebookEdit"]),X6=220,Z6=160;function Q6(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(Y6.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,X6)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=Wo(o.command.trim().replace(/\s+/g," ")).slice(0,Z6));return r}function eK(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?xn(t.cwd,t.sessionId):t?.cwd?Km(t.cwd):null}async function PO(e,t){let n=await t(e?.session_id,e?.cwd);if(!n?.track)return[];let{track:r,box:o}=n,s={boxId:o?.name||null,provider:"claude",source:"hook"},a=[],i=async c=>{a.push(await N(r,{...s,...c}))};switch(e?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:e.session_id,source:e.source??null,standing:await Ya(o?.cwd)}});break;case"UserPromptSubmit":{let c=String(e.prompt??e.user_prompt??""),l=hp(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:Q6(e,o)});break;case"Stop":await tK(r,o,e,i);break;default:break}return a}async function tK(e,t,n,r){let o=[],s=eK(n,t);if(s)try{o=z6(await V6.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=await K(e),c=J6(i),l=o.filter(p=>!c.has(p.messageId));if(l.length){let p=new Map;for(let f of l){let w=`${f.model||"unknown"}\0${f.speed||""}`,h=p.get(w)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},cacheWriteTtl:null,messageIds:[]};for(let y of Object.keys(h.tokens))h.tokens[y]+=f.tokens[y];f.cacheWriteTtl&&(h.cacheWriteTtl??={m5:0,h1:0},h.cacheWriteTtl.m5+=f.cacheWriteTtl.m5,h.cacheWriteTtl.h1+=f.cacheWriteTtl.h1),h.messageIds.push(f.messageId),h.speed=f.speed||null,p.set(w,h)}for(let[f,w]of p){let h=f.split("\0")[0];await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:h,usd:Wn(h,w.tokens,void 0,w.speed?{speed:w.speed}:void 0),tokens:w.tokens,...w.speed?{speed:w.speed}:{},cacheWriteTtl:w.cacheWriteTtl,messageId:w.messageIds[w.messageIds.length-1],messageIds:w.messageIds,apiCalls:w.messageIds.length}})}}let u=NO(a),d=l[0]??null;await r({type:"context.usage",actor:"system",source:"transcript",payload:{model:a.model,occupancy:u,tokens:a.tokens,first:d?{occupancy:NO(d),tokens:d.tokens}:null,calls:l.length,turnIndex:nK(i,t)}})}var NO=e=>e.tokens.input+e.tokens.cacheWrite+e.tokens.cacheRead;function nK(e,t){if(!t?.name)return null;let n=ji(t,Date.now()),r=n!=null?t.epochs[n]?.startedAt??null:null,o=0;for(let s of e)s.type!=="turn.user"||s.source!=="hook"||s.boxId!==t.name||r!=null&&Number(s.ts)<r||o++;return o||null}Ys();Lm();fp();be();be();fo();be();fo();yr();function Ll(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 DO=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),BO=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},Of=e=>Math.round(e*1e4)/1e4;async function Vb(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=DO(),d=0,p=[];for(let f of e||[]){let w=await K(f),h=po(w,{boxLabels:Ll(f)});if(h.turns===0){f.archivedAt?d++:p.push(f.name||f.id);continue}o+=h.totalUsd,s+=h.turns,a+=h.repricedTurns||0,i+=h.asRecordedUsd||0,c+=h.unpricedTurns||0;for(let y of h.unpricedModels||[])l.add(y);BO(u,h.totalTokens);for(let[y,k]of Object.entries(h.byModel))n[y]||={usd:0,events:0,tokens:DO()},n[y].usd=Of(n[y].usd+k.usd),n[y].events+=k.events,BO(n[y].tokens,k.tokens);for(let[y,k]of Object.entries(h.byProvider))r[y]||={usd:0,events:0},r[y].usd=Of(r[y].usd+k.usd),r[y].events+=k.events;t.push({id:f.id,name:f.name,usd:h.totalUsd,rollup:h})}return t.sort((f,w)=>w.usd-f.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:Ea(),fleet:{totalUsd:Of(o),asRecordedUsd:Of(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 No=e=>`$${e.toFixed(4)}`,hi=(e,t)=>String(e).padEnd(t);function LO(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 ${No(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 ${No(t.asRecordedUsd)} \xB7 current-rates total ${No(t.totalUsd)} (${t.totalUsd>=t.asRecordedUsd?"+":""}${No(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(` ${hi(a,26)} ${hi(No(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(` ${hi(a.name||a.id,30)} ${No(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${hi(c.label,40)} ${hi(No(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))}${hi(No(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
781
|
+
`)}be();fw();import lK from"node:fs/promises";import uK from"node:path";ad();dd();fo();var Mf=e=>e.payload?.ts??e.ts,rK=12e4,fn=e=>Math.round(e*1e4)/1e4,oK=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{},byTrack:new Map});function sK(e,{since:t}){let{track:n,events:r,brief:o}=e,s=x=>Mf(x)>=t,a=gr([gc(n?.name),gc(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let x of r)if(x.type==="subtask.delegated"&&x.boxId&&x.payload?.task){let S=gr(x.payload.task);S&&i.set(x.boxId,S)}let c=new Map,l=x=>{let S=x||"unattributed";return c.has(S)||c.set(S,{segments:[u(null)],lastTs:null,provider:null}),c.get(S)},u=(x,{shape:S="initial",continuation:_=!1,text:M=null,ts:P=null}={})=>({textCat:x,shape:S,continuation:_,text:M,ts:P,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((x,S)=>Mf(x)-Mf(S)),p=0,f=0,w=0,h={turns:0};for(let x of d){let S=l(x.boxId),_=S.segments[S.segments.length-1],M=Mf(x);switch(S.lastTs!=null&&M>S.lastTs&&s(x)&&(_.activeMs+=Math.min(M-S.lastTs,rK)),S.lastTs=M,x.provider&&(S.provider=x.provider),x.type){case"turn.user":{let P=x.payload?.text;if(x.source==="hook"||!String(P||"").trim()||x.payload?.note||kc(P))break;let j=gr(P),A=!!x.payload?.kind,$=A||Iv(P),L=u(j,{shape:A?"injected":$?"follow-up":"novel-ask",continuation:!j&&$,text:FO(P),ts:M});S.segments.push(L),s(x)&&(L.turns+=1);break}case"peer.message":{let P=x.payload?.text;if(!String(P||"").trim())break;let j=gr(P),A=u(j,{shape:"peer",continuation:!j,text:FO(P),ts:M});S.segments.push(A),s(x)&&(A.turns+=1);break}case"tool.called":{if(x.source==="transcript")break;let P=x.payload?.tool;P&&(_.tools[P]=(_.tools[P]||0)+1),s(x)&&(_.toolCalls+=1);break}case"cost.recorded":{if(!s(x))break;let{usd:P,tokens:j,model:A,unpriced:$}=sn(x);$&&(h.turns+=1),_.usd+=P,j&&(_.tokens.input+=j.input,_.tokens.output+=j.output,_.tokens.cacheRead+=j.cacheRead,_.tokens.cacheWrite+=j.cacheWrite),_.models[A]=(_.models[A]||0)+P,x.provider&&(_.provider=x.provider);break}case"handoff.done":s(x)&&(p+=1);break;case"subtask.merged":s(x)&&(f+=1);break;case"error":s(x)&&x.payload?.kind==="subtask-failed"&&(w+=1);break;default:break}}let y=new Map,k={usd:0,turns:0,byShape:{},samples:[]},g=!1;for(let[x,S]of c){let _=null;for(let M of S.segments){let P=null,j=null;if(M.textCat)P=M.textCat.category,j="text";else if(i.get(x))P=i.get(x).category,j="boxTask";else if(M.continuation&&_)P=_,j="inherited";else{let R=_v(M.tools);R?(P=R.category,j="toolMix"):a&&(P=a.category,j="hint")}if(P&&(_=P),!(M.usd>0||M.activeMs>0||M.turns>0||M.toolCalls>0))continue;let $=P??"uncategorized";y.has($)||y.set($,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{}});let L=y.get($);L.usd+=M.usd,L.activeMs+=M.activeMs,L.turns+=M.turns,L.toolCalls+=M.toolCalls,j&&(L.provenance[j]=(L.provenance[j]||0)+M.usd);for(let[R,T]of Object.entries(M.models))L.models[R]=(L.models[R]||0)+T;let H=M.provider||S.provider;H&&(L.providers[H]=(L.providers[H]||0)+M.usd),H==="cursor"&&M.usd===0&&(g=!0),P||(k.usd+=M.usd,k.turns+=M.turns,k.byShape[M.shape]=(k.byShape[M.shape]||0)+M.usd,M.usd>0&&M.text&&k.samples.push({trackId:n?.id,track:n?.name||n?.id,shape:M.shape,usd:fn(M.usd),ts:M.ts,text:M.text}))}}return{perCategory:y,uncategorized:k,handoffs:p,merged:f,failed:w,sawCursorActivity:g,unpricedTurns:h.turns}}function zb(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 k of e||[]){let{track:g,events:x}=k;if(!x?.length){a+=1;continue}let{perCategory:S,uncategorized:_,handoffs:M,merged:P,failed:j,sawCursorActivity:A,unpricedTurns:$}=sK(k,{since:r});A&&(i=!0),c+=$,l.usd+=_.usd,l.turns+=_.turns;for(let[H,R]of Object.entries(_.byShape))l.byShape[H]=(l.byShape[H]||0)+R;l.samples.push(..._.samples);let L={trackId:g?.id,name:g?.name||g?.id,archived:!!g?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:M,subtasksMerged:P,subtasksFailed:j};for(let[H,R]of S){L.usd+=R.usd,L.activeMs+=R.activeMs,L.turns+=R.turns,L.categories[H]={usd:fn(R.usd),activeMs:R.activeMs};for(let[W,B]of Object.entries(R.models))L.models[W]=fn((L.models[W]||0)+B);for(let[W,B]of Object.entries(R.providers))L.providers[W]=fn((L.providers[W]||0)+B);o.has(H)||o.set(H,oK());let T=o.get(H);T.usd+=R.usd,T.activeMs+=R.activeMs,T.turns+=R.turns,T.toolCalls+=R.toolCalls;for(let[W,B]of Object.entries(R.models))T.models[W]=(T.models[W]||0)+B;for(let[W,B]of Object.entries(R.providers))T.providers[W]=(T.providers[W]||0)+B;for(let[W,B]of Object.entries(R.provenance))T.provenance[W]=(T.provenance[W]||0)+B;let C=T.byTrack.get(g?.id)||{usd:0,activeMs:0};C.usd+=R.usd,C.activeMs+=R.activeMs,T.byTrack.set(g?.id,C)}(L.usd>0||L.activeMs>0||L.turns>0)&&(L.usd=fn(L.usd),L.dominantCategory=cK(L.categories),s.push(L))}let u=[...o.values()].reduce((k,g)=>k+g.usd,0),d=[...o.values()].reduce((k,g)=>k+g.activeMs,0),p=u>0?"usd":"time",f=p==="usd"?u:d,w=new Map((e||[]).map(k=>[k.track?.id,k.track?.name||k.track?.id])),h=[...o.entries()].map(([k,g])=>({category:k,sharePct:f>0?Math.round((p==="usd"?g.usd:g.activeMs)/f*100):0,shareBasis:p,usd:fn(g.usd),activeMs:g.activeMs,turns:g.turns,toolCalls:g.toolCalls,models:jl(g.models),providers:jl(g.providers),provenance:jl(g.provenance),topWorkstreams:[...g.byTrack.entries()].sort((x,S)=>S[1].usd-x[1].usd||S[1].activeMs-x[1].activeMs).slice(0,2).map(([x,S])=>({trackId:x,name:w.get(x),usd:fn(S.usd),activeMs:S.activeMs}))})).sort((k,g)=>g.usd-k.usd||g.activeMs-k.activeMs);s.sort((k,g)=>g.usd-k.usd||g.activeMs-k.activeMs);let y={};for(let k of o.values())for(let[g,x]of Object.entries(k.provenance))y[g]=(y[g]||0)+x;return{since:r,capturedAt:n,totalUsd:fn(u),activeMs:d,turns:[...o.values()].reduce((k,g)=>k+g.turns,0),bullets:h,workstreams:s,provenance:jl(y),gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c,uncategorized:{usd:fn(l.usd),turns:l.turns,sharePct:u>0?Math.round(l.usd/u*100):0,byShape:jl(l.byShape),top:l.samples.sort((k,g)=>g.usd-k.usd).slice(0,aK)}}}}var aK=25,jO=160,FO=e=>{let t=String(e||"").replace(/\s+/g," ").trim();return t.length>jO?`${t.slice(0,jO).trimEnd()}\u2026`:t},iK=10;function UO(e,{thresholdPct:t=iK}={}){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:fn(o),totalUsd:fn(r),turns:n.turns||0,inheritedPct:r>0?Math.round((e?.provenance?.inherited||0)/r*100):0,problems:i}}var cK=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},jl=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,fn(n)]));var Ks={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function dK(e){let t=String(e||"7d");return t in Ks?{key:t,ms:Ks[t]}:{key:"7d",ms:Ks["7d"]}}function pK(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=Ud(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 fK(e){if(!e?.cwd)return null;try{let n=(await lK.readFile(uK.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 Jb(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}:dK(t),d=[];for(let w of e||[]){let h=await K(w),y=pK(w,h);i&&c<o&&(y=y.filter(k=>(k.ts??0)<=c)),d.push({track:w,events:y,brief:await fK(w)})}let p=zb(d,{windowMs:u,now:c}),f=l==="7d"&&c===o?p:zb(d,{windowMs:Ks["7d"],now:o});return{window:l,...p,categorization:UO(f)}}var Fl=e=>`$${Number(e||0).toFixed(2)}`,HO=e=>{let t=Math.round((e||0)/6e4);return t>=60?`${Math.floor(t/60)}h${String(t%60).padStart(2,"0")}`:`${t}m`},Gs=(e,t)=>String(e).padEnd(t);function WO(e){let t=[];t.push(`USAGE ROUNDUP \xB7 ${e.window} \xB7 ${Fl(e.totalUsd)} \xB7 ${HO(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(` ${Gs(`${a.shareBasis==="time"?"~":""}${a.sharePct}%`,5)} ${Gs(a.category,15)} ${Gs(Fl(a.usd),10)} ${Gs(HO(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 ${Fl(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} ${Fl(i)}`).join(" \xB7 ")||"none")),s.top?.length||t.push(" (nothing unmatched carried cost in this window)");for(let a of s.top||[])t.push(` ${Gs(Fl(a.usd),8)} ${Gs(a.shape,10)} ${Gs(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(`
|
|
782
|
+
`)}yr();var Po=36e5,VO=24*Po,mK=400,hK=12,Vs=e=>Math.round(e*1e4)/1e4;function Yb({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 Ks?String(e||"7d"):"7d",i=Ks[a];return{key:a,since:i==null?null:r-i,until:r}}var wK=(e,t)=>e!=null&&t-e<=48*Po?Po:VO;function qO(e,t){if(t===Po)return Math.floor(e/Po)*Po;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function gK(e,t){if(t===Po)return e+Po;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var GO=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),KO=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},yK=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Vs(n)]));async function zO(e,t={}){let n=t.now??Date.now(),r=Yb({...t,now:n}),o=r.since==null?VO:wK(r.since,r.until),s=A=>(r.since==null||A>=r.since)&&A<r.until,a=r.since!=null?r.until-r.since:null,i=A=>a!=null&&A>=r.since-a&&A<r.since,c={},l={},u=GO(),d=0,p=0,f=0,w=0,h=0,y=0,k=new Set,g=new Map,x=[],S=[],_=[],M=null;for(let A of e||[]){let L=(await K(A)).filter(T=>T.type==="cost.recorded");if(!L.length){A.archivedAt||_.push(A.name||A.id);continue}let H=[];for(let T of L){let C=T.ts??0;s(C)?H.push(T):i(C)&&(y+=sn(T).usd)}if(!H.length)continue;let R=po(H,{boxLabels:Ll(A)});d+=R.totalUsd,p+=R.turns,f+=R.repricedTurns||0,w+=R.asRecordedUsd||0,h+=R.unpricedTurns||0;for(let T of R.unpricedModels||[])k.add(T);KO(u,R.totalTokens);for(let[T,C]of Object.entries(R.byModel))c[T]||={usd:0,events:0,tokens:GO()},c[T].usd=Vs(c[T].usd+C.usd),c[T].events+=C.events,KO(c[T].tokens,C.tokens);for(let[T,C]of Object.entries(R.byProvider))l[T]||={usd:0,events:0},l[T].usd=Vs(l[T].usd+C.usd),l[T].events+=C.events;for(let T of H){let{usd:C}=sn(T);if(!C)continue;let W=qO(T.ts??0,o),B=g.get(W)??{t:W,total:0,byProvider:{}};B.total+=C;let Z=T.provider||"unknown";B.byProvider[Z]=(B.byProvider[Z]||0)+C,g.set(W,B),(M==null||W<M)&&(M=W)}x.push({id:A.id,name:A.name||A.id,archived:!!A.archivedAt,usd:R.totalUsd,turns:R.turns,providers:Object.keys(R.byProvider)});for(let[T,C]of Object.entries(R.byBox))S.push({trackId:A.id,trackName:A.name||A.id,boxId:T,...C})}let P=r.since!=null?qO(r.since,o):M,j=[];for(let A=P,$=0;A!=null&&A<r.until&&$<mK;A=gK(A,o),$++){let L=g.get(A);j.push(L?{t:A,total:Vs(L.total),byProvider:yK(L.byProvider)}:{t:A,total:0,byProvider:{}})}return x.sort((A,$)=>$.usd-A.usd),S.sort((A,$)=>$.usd-A.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:Ea(),window:r.key,since:r.since,until:r.until,bucketMs:o,series:j,fleet:{totalUsd:Vs(d),asRecordedUsd:Vs(w),turns:p,repricedTurns:f,unpricedTurns:h,unpricedModels:[...k],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:x.length,tracksSilentLive:_},prior:a!=null?{totalUsd:Vs(y),since:r.since-a,until:r.since}:null,tracks:x.slice(0,50),boxes:S.slice(0,hK)}}dp();var bK=new Set(["cost.recorded","code.committed","code.rework","rework.cause.offered","rework.cause.answered"]),kK=20,xK=40;async function JO(e,t={}){let n=t.now??Date.now(),r=Yb({...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 K(l),d=0,p=Ll(l);for(let f of u){if(!bK.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=CE(s,{boxMeta:a});return{generatedAt:new Date().toISOString(),window:r.key,since:r.since,until:r.until,reworkWindowDays:xo,decayEndDays:Cw,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,kK),fixes:c.fixes.slice(0,xK)}}import vK from"node:fs";import YO from"node:fs/promises";import Ul from"node:os";import SK from"node:path";Sg();dt();var TK=1024*1024,Cf=e=>Math.round(e/TK);async function AK(e){try{let t=await YO.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:Cf(r),diskTotalMb:Cf(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function EK(){try{return(await kn()).filter(t=>t.isBostrat).length}catch{return null}}async function _K(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await YO.readdir(SK.join(Al,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function XO({stateDir:e=Al,now:t=Date.now}={}){let n=Ul.totalmem(),[r,o,s,a]=await Promise.all([Ip().catch(()=>null),AK(vK.existsSync(e)?e:Ul.tmpdir()),EK(),_K()]),i=Ul.cpus()?.length||null,c=Ul.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?Cf(n-r):null,memTotalMb:Cf(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(Ul.uptime())}}sg();be();lh();Wi();yr();be();yr();var IK=864e5;function $K(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=Wn(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 ZO(e,{days:t=14,tolerance:n=.005,now:r=Date.now()}={}){let o=r-t*IK,s={},a=0,i=0,c=[];for(let u of e||[]){let d=await K(u);for(let p of d){if(!p.ts||p.ts<o)continue;let f=$K(p,{tolerance:n});if(!f)continue;a++;let w=f.speed?`${f.model} (${f.speed})`:f.model,h=s[w]||={rows:0,divergent:0,asRecordedUsd:0,expectedUsd:0};h.rows++,h.asRecordedUsd+=f.asRecorded,h.expectedUsd+=f.expected,f.divergent&&(i++,h.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}}ng();ry();Bd();vt();an();import wi from"node:fs/promises";import RK from"node:fs";import At from"node:path";import OK from"node:os";import{execFile as MK}from"node:child_process";import{promisify as CK}from"node:util";var NK=CK(MK),QO=process.env.BOSTRAT_STATE_DIR||At.join(process.env.HOME||OK.tmpdir(),".bostrat"),Nf=!1,Xb=null;async function Zb(e,t){let n=await wi.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await wi.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await wi.mkdir(At.dirname(t),{recursive:!0}),await wi.copyFile(e,t),!0)}var PK=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 Pf(e,t,{skip:n=null}={}){let r=0,o=await wi.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(PK.has(s.name)||n?.has(s.name))continue;let a=At.join(e,s.name);s.isDirectory()?r+=await Pf(a,At.join(t,s.name)):s.isFile()&&(r+=await Zb(a,At.join(t,s.name))?1:0)}return r}async function eM({stateDir:e=QO,dest:t=process.env.BOSTRAT_SYNC_DIR||`${QO}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await wi.mkdir(t,{recursive:!0});let o=0;o+=await Zb(At.join(e,"tracks.json"),At.join(t,"tracks.json"))?1:0,o+=await Zb(At.join(e,"events.jsonl"),At.join(t,"events.jsonl"))?1:0,o+=await Pf(At.join(e,"events"),At.join(t,"events")),o+=await Pf(At.join(e,"memory"),At.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await Pf(At.join(u.cwd,".bostrat"),At.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>NK("git",["-C",t,...u]);RK.existsSync(At.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 Sm(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||Nf))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,Nf&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),Nf=!1,Xb=null}catch(u){Nf=!0;let d=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
783
|
+
`).slice(-3).join(" | ").slice(0,300);d!==Xb&&(Xb=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)}}}Wg();vn();es();dt();$i();import{execFile as DK}from"node:child_process";import{promisify as BK}from"node:util";import{randomUUID as LK}from"node:crypto";import jK from"node:fs";import FK from"node:os";import UK from"node:path";var Bo=BK(DK),Df=e=>new Promise(t=>setTimeout(t,e)),Lo=Um,Qb=process.env.BOSTRAT_USAGE_PROBE_CWD||UK.join(FK.homedir(),".bostrat","usage-probe"),HK=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,WK=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,qK=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,tM=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Do=null,Hl=null,Wl=null;async function GK(){try{return await Bo(oe,se("has-session","-t",Lo)),!0}catch{return!1}}function KK(){Do&&clearTimeout(Do),Do=setTimeout(()=>VK().catch(()=>{}),HK),Do.unref&&Do.unref()}async function VK(){Do&&(clearTimeout(Do),Do=null);try{await Bo(oe,se("kill-session","-t",Lo))}catch{}}async function zK(){let e=Date.now()+qK;for(;;){let t=await ek().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(t)){await Df(500);return}if(Date.now()>e)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await Df(500)}}async function JK(){if(await GK())return;if(!await Zr())throw new Error("host tmux server not running \u2014 start globo-tmux.service");jK.mkdirSync(Qb,{recursive:!0}),await Wr(Qb);let e=LK();await Bo(oe,se("new-session","-d","-s",Lo,"-c",Qb,"-x","200","-y","60",...Xi()));let t=`${Fm} --session-id ${e}`;await Bo(oe,se("send-keys","-t",Lo,t,"Enter")),await zK()}async function ek(){let{stdout:e}=await Bo(oe,se("capture-pane","-t",Lo,"-p"));return e}async function YK(){await JK(),await Bo(oe,se("send-keys","-t",Lo,"-l","/usage")),await Bo(oe,se("send-keys","-t",Lo,"Enter"));let e=Date.now()+WK,t="";for(;await Df(400),t=await ek(),!(/Current\s+(session|week)/i.test(t)||/\d+%\s*used/i.test(t)||Date.now()>e););return await Df(900),t=await ek(),await Bo(oe,se("send-keys","-t",Lo,"Escape")).catch(()=>{}),KK(),t}function XK(e){let t=String(e||"").split(`
|
|
784
|
+
`).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 ZK(e){let t=String(e||"").split(`
|
|
785
|
+
`).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 nM({fresh:e=!1}={}){return!e&&Hl&&Date.now()-Hl.at<tM?Hl.data:Wl||(Wl=(async()=>{try{let t=await YK(),n=XK(t),r={...n,contributors:ZK(t),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Hl={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 Hl={at:Date.now()-(tM-3e3),data:n},n}finally{Wl=null}})(),Wl)}var QK=262144;function eV(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 tV=new Set([300]),nV=new Set([10080]);function rV(e){let t=Number(e);return!Number.isFinite(t)||t<=0?null:tV.has(t)?"session":nV.has(t)?"week":"other"}function oV(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 sV(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?eV((o-n)/1e3):null,resetsAt:o}}var ql=e=>({session:null,week:null,other:null,planType:null,credits:null,limitReached:null,sawTokenCount:e});function aV(e,t=Date.now()){let n=String(e||"").split(`
|
|
786
|
+
`),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 ql(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=ql(r);[c.primary,c.secondary].forEach((f,w)=>{let h=sV(f,l,t);if(!h)return;let y=rV(f.window_minutes);y===null&&(y=w===0?"session":"week"),y==="session"&&!u.session?u.session=h:y==="week"&&!u.week?u.week=h:(u.other??=[]).push({label:oV(f.window_minutes),...h})}),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{...ql(r),session:l(c.primary_used_percent),week:l(c.secondary_used_percent)}}return ql(r)}return ql(r)}async function tk(e){let t={provider:"codex",supported:!0,contributors:null};try{let n=await Zo(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 jt(n.file,QK),o=aV(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 rM(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 Bf({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 tk(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await tk(null))}return n.has("cursor")&&r.push(await Bf({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function Bf(e,{fresh:t=!1}={}){let n=e?.agent||"claude";return n==="codex"?tk(e):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await nM({fresh:t})}}Gr();vt();vt();function iV(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}var cV=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Lf=(e,t)=>Object.assign(new Error(e),{status:t});async function oM(e,t){if(!iV(t))throw Lf("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${De(t)}${e}`,{headers:cV(t)})}catch(o){throw Lf(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw Lf(r?.error||`plane replied ${n.status}`,n.status);return r}async function sM(e,{max:t=10,env:n=process.env}={}){let r=new URLSearchParams({q:e||""});return t&&r.set("max",String(t)),(await oM(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function aM(e,{env:t=process.env}={}){if(!e)throw Lf("message id required",400);return(await oM(`/v1/gmail/messages/${encodeURIComponent(e)}`,t)).message}Oc();Qd();function nk(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 lV(e){let[t,n]=e.split("/"),r=nk(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 iM(e){return(e||"").split(",").map(t=>t.trim()).filter(Boolean).map(lV).filter(Boolean)}function uV(e){let t=e.headers["fly-client-ip"];return typeof t=="string"&&t.trim()?t.trim():(e.socket&&e.socket.remoteAddress||"").replace(/^::ffff:/,"")}function cM(e){let t=iM(e),n=String(e||"").trim().toLowerCase()==="open";return{open:n,clientIp:uV,allowed(r){if(n)return!0;let o=nk((r||"").replace(/^::ffff:/,""));return o==null?!1:t.some(s=>s(o))}}}function lM(e,t){let n=iM(e);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=nk(o);if(s==null||!n.some(i=>i(s)))return!1;let a=t(r);return!!(a&&a.view)}}}import uM from"node:crypto";import jf from"node:fs";import dV from"node:path";function dM(e=process.env,{stateDir:t=Al,log:n=console}={}){let r=String(e.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=dV.join(t,"api-token");try{let a=jf.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=uM.randomBytes(32).toString("base64url");try{jf.mkdirSync(t,{recursive:!0}),jf.writeFileSync(o,s+`
|
|
787
|
+
`,{mode:384}),jf.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function Gl(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 pM(e,t){if(!e||!t)return!1;let n=Buffer.from(String(e)),r=Buffer.from(String(t));return n.length!==r.length?!1:uM.timingSafeEqual(n,r)}function fM(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 pV=[/^\/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/],fV=e=>pV.some(t=>t.test(e));function mM({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=fM(o);return pM(c,e)?a():fV(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||Gl(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 hM(e,t,{enforce:n=!0}={}){return!n||pM(fM(e),t)?!0:Gl(e)}yn();Ap();Ap();import mV from"node:http";import hV from"node:net";function Kl(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 Ff(e){let t=Kl(e.headers.host);return t?{slug:t,view:yg(t)}:null}var wV=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),gV=e=>{let t=wV(e.name);return`<!doctype html><meta charset="utf-8">
|
|
788
788
|
<meta http-equiv="refresh" content="3">
|
|
789
789
|
<title>${t} \u2014 starting</title>
|
|
790
790
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
791
791
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
792
|
-
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function
|
|
792
|
+
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function wM(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 gM(e,t,n){let r=Ff(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(gV(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=mV.request({host:"127.0.0.1",port:o.port,method:e.method,path:wM(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 yM(e,t,n){let r=Ff(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
|
|
793
793
|
\r
|
|
794
794
|
bostrat: no live view "${s}"\r
|
|
795
|
-
`),t.destroy(),!0;let a=
|
|
795
|
+
`),t.destroy(),!0;let a=hV.connect(o.port,"127.0.0.1",()=>{let c=[`${e.method} ${wM(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
|
|
796
796
|
`)+`\r
|
|
797
797
|
\r
|
|
798
|
-
`),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
|
|
798
|
+
`),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 rk(e){let t=typeof e=="string"?{host:e}:e||{},n=t["x-bostrat-view-base"];if(typeof n=="string"&&n)return{proto:t["x-forwarded-proto"]==="https"?"https":"http",host:n.replace(/:\d+$/,"").toLowerCase(),source:"header"};let r=Eb();if(r)return{proto:"https",host:r,source:"learned"};let o=_b();if(o)return{...o,source:"env"};let s=String(t.host||"").replace(/:\d+$/,"").toLowerCase();if(!s||!s.includes(".")||/^[\d.]+$/.test(s))return null;let a=s.split(".");return{proto:"http",host:Kl(s)&&yg(a[0])?a.slice(1).join("."):s,source:"request"}}function bM(e,t){return Ef(rk(t),e.slug)}function kM({app:e,onUpgrade:t,extractViewSlug:n,log:r=console}){let o=s=>{r.warn?.(`[bostrat-agent] view listener refused non-view Host ${JSON.stringify(String(s||""))}`)};return{request(s,a){if(!n(s.headers.host)){o(s.headers.host),a.statusCode=403,a.setHeader("content-type","text/plain"),a.end("bostrat: this listener serves views only");return}e(s,a)},upgrade(s,a,i){if(!n(s.headers.host)){o(s.headers.host),a.write(`HTTP/1.1 403 Forbidden\r
|
|
799
799
|
\r
|
|
800
|
-
`),a.destroy();return}t(s,a,i)}}}function kM(e,{addr:t,port:n,log:r=console,retryMs:o=15e3,maxRetries:s=1/0}){let a=0;e.on("error",c=>{let l=c?.code||c?.message;if(e.listening){r.error?.(`[bostrat-agent] view listener on ${t}:${n}: ${l}`);return}if(a>s){r.error?.(`[bostrat-agent] view listener on ${t}:${n} failed (${l}) \u2014 giving up`);return}r.error?.(`[bostrat-agent] view listener on ${t}:${n} failed (${l}) \u2014 retrying in ${Math.round(o/1e3)}s`),setTimeout(i,o).unref?.()});let i=()=>{a++,e.listen(n,t,()=>{r.log?.(`[bostrat-agent] view listener on ${t}:${n} (view Hosts only; front-door CIDRs decide the rest)`)})};return i(),e}Va();Og();Mp();$d();be();import xM from"node:os";import{randomUUID as ok}from"node:crypto";var wV=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||xM.hostname(),nodeName:e.BOSTRAT_NODE_NAME||xM.hostname()});var vM=32768,SM=120,TM=1e3,AM=new Set(["claude","codex","cursor"]),gV=new Set(["off","transition","nag"]),yV=[[0,59],[0,23],[1,31],[1,12],[0,6]];function bV(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=yV[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 Pn=e=>String(e??"").trim(),zs={name(e){let t=Pn(e);if(!t)throw new Error("a loop needs a name");if(t.length>SM)throw new Error(`name is too long (${t.length}); cap is ${SM}`);return t},schedule(e){let t=Pn(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!bV(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=Pn(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>vM)throw new Error(`prompt is too long (${t.length} chars); cap is ${vM}`);return t},expectation(e){let t=Pn(e)||null;if(t&&t.length>TM)throw new Error(`expectation is too long (${t.length}); cap is ${TM}`);return t},notify(e){let t=Pn(e)||"off";if(!gV.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=Pn(typeof t=="string"?t:t?.provider)||"claude";if(!AM.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...AM].join(", ")}`);let r=typeof t=="object"&&t&&Pn(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 kV(e,t){let n=e&&typeof e=="object"?e:{},r=zs.name(n.name),o=zs.schedule(n.schedule),s=zs.prompt(n.prompt),a=zs.expectation(n.expectation),i=zs.notify(n.notify),c=zs.assignment(n.assignment),l=Pn(t?.workspace?.repo)||Pn(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}=wV();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:d||null}}async function _M(e,t){let n=kV(t,e),r=ok();return await N({id:e.id,cwd:e.cwd},{type:"loop.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:r,...n}}),{proposalId:r,...n}}async function IM(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 N({id:e.id,cwd:e.cwd},{type:"loop.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};if(!r)throw new Error("loopId required \u2014 the loop is created on the plane before the card is answered");let i=a.payload;return await N({id:e.id,cwd:e.cwd},{type:"loop.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:String(r),name:i.name,schedule:i.schedule,provider:i.assignment?.provider??"claude",repo:i.repo??null,nodeId:i.nodeId??null,nextFireAt:Number.isFinite(Number(o))?Number(o):null}}),await N({id:e.id,cwd:e.cwd},{type:"loop.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",loopId:String(r)}}),{proposalId:t,action:"created",loopId:String(r)}}var rk=["name","schedule","prompt","expectation","notify","assignment","enabled"],xV=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function $M(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 RM(e,t){let n=Pn(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 vV(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=>!rk.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: ${rk.join(", ")}`)}let o={};for(let a of rk){if(!(a in n))continue;let i=zs[a](n[a]);(a==="assignment"?xV(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 OM(e,t,n){let r=$M(t),{patch:o,changed:s}=vV(n,r),a=ok(),i={ts:Date.now(),proposalId:a,loopId:String(t.id),name:r.name,base:r,patch:o,changed:s,selfEdit:!!(r.trackId&&r.trackId===e.id)};return await N({id:e.id,cwd:e.cwd},{type:"loop.update.offered",actor:"system",source:"orchestrator",payload:i}),i}var EM=600;async function MM(e,t,n){let r=Pn(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>EM)throw new Error(`reason is too long (${r.length}); cap is ${EM}`);let o=$M(t),s=ok(),a={ts:Date.now(),proposalId:s,loopId:String(t.id),name:o.name,base:o,reason:r,selfEdit:!!(o.trackId&&o.trackId===e.id)};return await N({id:e.id,cwd:e.cwd},{type:"loop.delete.offered",actor:"system",source:"orchestrator",payload:a}),a}var CM=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 NM(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 CM(e,"loop.update.offered",t)).payload,a={id:e.id,cwd:e.cwd};return n==="applied"&&await N(a,{type:"loop.updated",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:s.loopId,name:s.patch?.name??s.name,changed:s.changed??Object.keys(s.patch||{}),nextFireAt:Number.isFinite(Number(r))?Number(r):null}}),await N(a,{type:"loop.update.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:s.loopId}}),{proposalId:t,action:n,loopId:s.loopId}}async function PM(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 CM(e,"loop.delete.offered",t)).payload,s={id:e.id,cwd:e.cwd};return(n==="delete"||n==="pause")&&await N(s,{type:n==="delete"?"loop.deleted":"loop.paused",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:o.loopId,name:o.name,trackId:o.base?.trackId??null}}),await N(s,{type:"loop.delete.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:o.loopId}}),{proposalId:t,action:n,loopId:o.loopId}}be();import{randomUUID as SV}from"node:crypto";var ik=20,TV=300,sk=120,DM=120,BM=2e3,Uf=new Set(["build","ops","debt","spike"]),LM=new Set(["active","done","abandoned"]),ck=new Set(["circuit.create","circuit.update","circuit.delete"]),AV=new Set(["person.create","slot.create","slot.update","slot.delete","reservation.create","reservation.update","reservation.delete"]),Kl=new Set([...ck,...AV]),EV=["name","description","status","type"],_V=["person","startDate","endDate","estUsd"],IV=["person","label","startDate","endDate"],$V=/^\d{4}-\d{2}-\d{2}$/,en=e=>String(e??"").trim(),lk=e=>typeof e=="string"&&e.startsWith("@"),UM=864e5;function HM(e){let t=Date.parse(`${e}T00:00:00Z`),n=new Date(t).getUTCDay();return new Date(t-(n+6)%7*UM).toISOString().slice(0,10)}function RV(e){return new Date(Date.parse(`${HM(e)}T00:00:00Z`)+6*UM).toISOString().slice(0,10)}var jM=(e,t)=>{let n=en(e);if(!$V.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 OV(e,t){let n=jM(e,"startDate"),r=jM(t,"endDate");if(r<n)throw new Error(`endDate ${r} is before startDate ${n}`);let o=HM(n),s=RV(r);return{startDate:o,endDate:s,snapped:o!==n||s!==r}}var FM=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},jo=(e,t,n)=>{let r=en(e);if(r.length>t)throw new Error(`${n} is too long (${r.length}); cap is ${t}`);return r};function ak(e,t){let n=en(t);if(!n)throw new Error("a circuit name or id is required");if(lk(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 MV(e,t){let n=en(t);if(!n)throw new Error('a person is required \u2014 "me", a person id, or a row label');if(n==="me"||lk(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 CV(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>ik)throw new Error(`too many ops (${o.length}); cap is ${ik} \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=x=>{x.id&&(l.circuits[x.id]={name:x.name,status:x.status,type:x.type??"build",updatedAt:x.updatedAt??null},u[x.id]=x.name)},h=x=>{x.id&&(u[x.id]=x.label)},y=(x,S)=>{let _=en(x);if(!_)return null;if(d.has(_))throw new Error(`ref "${_}" is used by two ops \u2014 each ref names one thing`);return d.add(_),u[`@${_}`]=S,_},k=o.map((x,S)=>{let _=x&&typeof x=="object"?x:{},M=en(_.op),P=`op ${S+1}`;if(!Kl.has(M))throw new Error(`${P}: unknown op "${M}" \u2014 one of ${[...Kl].join(", ")}`);if(!n.has(M))throw new Error(`${P}: "${M}" isn't a circuit op \u2014 roadmap changes go through \`bostrat-roadmap propose\` (one card, either CLI)`);let j=R=>{throw new Error(`${P} (${M}): ${R}`)},A=R=>{let T=_.set&&typeof _.set=="object"&&!Array.isArray(_.set)?_.set:null;T||j("needs a `set` object naming the fields to change");let C=Object.keys(T).filter(W=>!R.includes(W));return C.length&&j(`unknown field(s) ${C.join(", ")} \u2014 settable: ${R.join(", ")}`),Object.keys(T).length||j("`set` names no fields"),T},$=R=>{let{startDate:T,endDate:C,snapped:W}=OV(R.startDate,R.endDate);return W&&(p=!0),{startDate:T,endDate:C,snapped:W}},L=R=>{let T=MV(a,R);return en(R)==="me"&&(f=!0),h(T),{key:T.id??en(R),label:T.label}},H=(R,T,C,W,B)=>{let Z=en(R);Z||j(`needs a ${W} id`);let xe=T.find(Te=>Te[C]===Z);return xe||j(`no ${W} "${Z}" \u2014 see \`bostrat-roadmap ls\``),B[Z]={...xe},xe};switch(M){case"circuit.create":{let R=jo(_.name,DM,"name");R||j("a circuit needs a name");let T=en(_.type)||"build";Uf.has(T)||j(`bad type "${T}" (${[...Uf].join(" | ")})`);let C=s.find(B=>String(B.name).toLowerCase()===R.toLowerCase());C&&j(`a circuit named "${C.name}" already exists (${C.id}) \u2014 update it instead of making a twin`);let W=y(_.ref,R);return{op:M,...W?{ref:W}:{},name:R,description:jo(_.description,BM,"description"),type:T}}case"circuit.update":{let R=ak(s,_.circuit);R.id||j("can't update a circuit created in the same plan \u2014 set its fields on the create op"),w(R);let T=A(EV),C={};if(T.name!==void 0){let W=jo(T.name,DM,"name");W||j("name can't be blank"),W!==R.name&&(C.name=W)}return T.description!==void 0&&(C.description=jo(T.description,BM,"description")),T.status!==void 0&&(LM.has(T.status)||j(`bad status "${T.status}" (${[...LM].join(" | ")})`),T.status!==R.status&&(C.status=T.status)),T.type!==void 0&&(Uf.has(T.type)||j(`bad type "${T.type}" (${[...Uf].join(" | ")})`),T.type!==(R.type??"build")&&(C.type=T.type)),Object.keys(C).length||j("nothing would change \u2014 the set equals the circuit's current values"),{op:M,circuit:R.id,name:R.name,set:C}}case"circuit.delete":{let R=ak(s,_.circuit);R.id||j("can't delete a circuit created in the same plan"),w(R);let T=o.some((C,W)=>W<S&&en(C?.op)==="circuit.update"&&C?.set?.status&&C.set.status!=="active"&&en(C?.circuit)===en(_.circuit));return R.status==="active"&&!T&&j(`\u201C${R.name}\u201D is active \u2014 an active circuit can't be deleted; propose a circuit.update to done/abandoned first (both can ride one card)`),{op:M,circuit:R.id,name:R.name,status:R.status}}case"person.create":{let R=jo(_.label,sk,"label");R||j("a person row needs a label");let T=a.find(W=>String(W.label).toLowerCase()===R.toLowerCase());T&&j(`a roadmap row labelled "${T.label}" already exists (${T.personId})`);let C=y(_.ref,R);return{op:M,...C?{ref:C}:{},label:R}}case"slot.create":{let R=ak(s,_.circuit);w(R);let T=L(_.person),C=$(_);return{op:M,circuit:R.id??R.ref,circuitName:R.name,person:T.key,personLabel:T.label,...C,estUsd:FM(_.estUsd)}}case"slot.update":{let R=H(_.slot,i,"slotId","slot",l.slots),T=A(_V),C={};if(T.person!==void 0){let B=L(T.person);C.person=B.key,C.personLabel=B.label}if(T.startDate!==void 0||T.endDate!==void 0){let B=$({startDate:T.startDate??R.startDate,endDate:T.endDate??R.endDate});B.startDate!==R.startDate&&(C.startDate=B.startDate),B.endDate!==R.endDate&&(C.endDate=B.endDate)}T.estUsd!==void 0&&(C.estUsd=FM(T.estUsd)),Object.keys(C).length||j("nothing would change");let W=s.find(B=>B.id===R.circuitId);return{op:M,slot:R.slotId,circuitName:W?.name??null,set:C}}case"slot.delete":{let R=H(_.slot,i,"slotId","slot",l.slots),T=s.find(C=>C.id===R.circuitId);return{op:M,slot:R.slotId,circuitName:T?.name??null,startDate:R.startDate,endDate:R.endDate}}case"reservation.create":{let R=L(_.person),T=jo(_.label,sk,"label");return T||j("a reservation needs a label (Leave, Support rotation, \u2026)"),{op:M,person:R.key,personLabel:R.label,label:T,...$(_)}}case"reservation.update":{let R=H(_.reservation,c,"reservationId","reservation",l.reservations),T=A(IV),C={};if(T.person!==void 0){let W=L(T.person);C.person=W.key,C.personLabel=W.label}if(T.label!==void 0){let W=jo(T.label,sk,"label");W||j("label can't be blank"),W!==R.label&&(C.label=W)}if(T.startDate!==void 0||T.endDate!==void 0){let W=$({startDate:T.startDate??R.startDate,endDate:T.endDate??R.endDate});W.startDate!==R.startDate&&(C.startDate=W.startDate),W.endDate!==R.endDate&&(C.endDate=W.endDate)}return Object.keys(C).length||j("nothing would change"),{op:M,reservation:R.reservationId,label:R.label,set:C}}case"reservation.delete":{let R=H(_.reservation,c,"reservationId","reservation",l.reservations);return{op:M,reservation:R.reservationId,label:R.label,startDate:R.startDate,endDate:R.endDate}}default:return j("unhandled op")}});k.forEach((x,S)=>{for(let _ of[x.circuit,x.person,x.set?.person])if(lk(_)&&!d.has(String(_).slice(1)))throw new Error(`op ${S+1} (${x.op}): unknown ref "${_}" \u2014 no earlier op in this plan creates it`)});let g=a.some(x=>x.kind==="user"&&x.userId);return{why:jo(r.why,TV,"why"),ops:k,base:l,resolved:u,snapped:p,createsMeRow:f&&!g}}async function WM(e,t,n,r={}){let o=CV(t,n,r),s=SV(),a={ts:Date.now(),proposalId:s,...o};return await N({id:e.id,cwd:e.cwd},{type:"planning.change.offered",actor:"system",source:"orchestrator",payload:a}),a}var NV=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 qM(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 NV(e,t),s={id:e.id,cwd:e.cwd},a=o.payload;return n==="applied"&&await N(s,{type:"planning.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,why:a.why??"",results:Array.isArray(r)?r.slice(0,ik+1):[]}}),await N(s,{type:"planning.change.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,ops:a.ops?.length??0}}),{proposalId:t,action:n}}gp();be();Yr();be();Gn();Mp();var GM=10,KM=200,PV=120,DV=24,zM=/^\d{4}-\d{2}-\d{2}$/;function uk(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var BV=(e=Date.now())=>new Date(e).toISOString().slice(0,10),At=(e,t="i")=>new RegExp(e,t),LV=[{re:At("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:At("\\bSPC\\b",""),reason:"business (SPC)"},{re:At("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:At("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:At("\\bReplicas\\b",""),reason:"competitor research"},{re:At("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:At("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:At("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],jV=[At("\\bSPC\\b",""),At("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),At("competitor|cloudcli|\\bkiro\\b"),At("\\bReplicas\\b",""),At("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),At("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),At("(^|[^\\w&])#\\d+\\b"),At("\\btrack/[\\w.-]+")];function FV({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of LV)if(o.test(n)||o.test(r))return s;return null}function VM(e){let t=String(e||"");for(let n of jV){let r=n.exec(t);if(r)return r[0].trim()}return null}function JM(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 UV=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function HV(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=rl(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(`
|
|
801
|
-
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,PV)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${DV}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=UV.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=VM(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>KM)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${KM}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=VM(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>GM)throw new Error(`${r.length} bullets \u2014 over the ${GM}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function WV(e,{repos:t=uk()}={}){let n=new Set(t);return(await En()).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:FV({title:r.title,headRef:r.headRef})}))}async function YM(e,t){let n=await WV(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function XM(e,{md:t,day:n=null}){let r=HV(t),o=n==null||n===""?BV():String(n);if(!zM.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await N({id:e.id,cwd:e.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:e.leadBox||null,payload:{ts:Date.now(),day:o,title:r.title,bullets:r.bullets}});return{day:o,title:r.title,bullets:r.bullets,eventId:s.id}}async function ZM(e,{day:t}){let n=String(t||"");if(!zM.test(n)||Number.isNaN(Date.parse(n)))throw new Error(`invalid day "${t}" \u2014 expected YYYY-MM-DD`);let r=await N({id:e.id,cwd:e.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:e.leadBox||null,payload:{ts:Date.now(),day:n,title:null,bullets:[],retract:!0}});return{day:n,retracted:!0,eventId:r.id}}vs();Gn();Ss();be();vs();xt();Ir();Gn();Oc();pp();Zd();va();an();import{execFile as tz}from"node:child_process";import{promisify as nz}from"node:util";var qV=new Set(["pull_request","check_suite","workflow_run","push"]),GV=/^[^/\s]+\/[^/\s]+$/,KV=500,dk=new Set,pk=[],Hf=new Map,mn={hourStart:0,byEvent:{},duplicates:0,rejected:0};function fk(e,t,n){let r=Math.floor(e/36e5)*36e5;r!==mn.hourStart&&(mn.hourStart=r,mn.byEvent={},mn.duplicates=0,mn.rejected=0),t==="accepted"?mn.byEvent[n]=(mn.byEvent[n]||0)+1:mn[t]+=1}function VV(e){if(!e||typeof e!="object"||e.forge!=="github"||typeof e.event!="string"||!qV.has(e.event)||typeof e.repo!="string"||!GV.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 QM(e,{now:t=Date.now(),onAccept:n=null,log:r=console}={}){let o=VV(e);if(!o)return fk(t,"rejected"),{ok:!1,error:"malformed github event envelope"};if(o.deliveryId){if(dk.has(o.deliveryId))return fk(t,"duplicates"),{ok:!0,duplicate:!0,envelope:o};for(dk.add(o.deliveryId),pk.push(o.deliveryId);pk.length>KV;)dk.delete(pk.shift())}let s=o.repo.toLowerCase(),a=Hf.get(s)||{lastDeliveryAt:0,count:0};a.lastDeliveryAt=t,a.count+=1,Hf.set(s,a),fk(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 zV(e,t=Date.now()){let n=Hf.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 eC=1440*6e4;function mk(e,t=Date.now()){let n=zV(e,t);return n.covered&&n.ageMs<eC}function tC(e=Date.now()){let t={};for(let[n,r]of Hf)t[n]={lastDeliveryAt:r.lastDeliveryAt,count:r.count,covered:e-r.lastDeliveryAt<eC};return{hourStart:mn.hourStart,byEvent:{...mn.byEvent},duplicates:mn.duplicates,rejected:mn.rejected,repos:t}}be();Gn();Gr();Ir();an();function nC(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function JV(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var YV=new Set(["merged","closed","lost"]);function XV(e,t){if(!e?.blocked)return null;let n=nC(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 ZV(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function QV(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 ez(e,t,n){await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:t,...n}})}async function Wf(e,t={}){let{now:n=Date.now(),slugOf:r=Me,lookup:o=ft,note:s=FA,tracks:a=async()=>(await ke()).filter(d=>!Ue(d)),asked:i=QV,emitChange:c=ez}=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(!JV(f))continue;let w=await o(p,f.number).catch(()=>null);if(!w?.trackId||YV.has(w.state))continue;let{prev:h}=await s(p,f.number,{blocked:nC(f),sha:f.headSha??null,at:n});if(!h)continue;let y=XV(h,f);if(!y||ZV(w,y.change))continue;u??=new Map((await a()).map(S=>[S.id,S]));let k=u.get(w.trackId);if(!k)continue;let g=f.headSha??null;if(await i(k,y.change,f.number,g,h.at||0))continue;let x={number:Number(f.number),url:f.url||w.url,title:f.title||w.title,branch:f.headRefName||w.headRef,repo:p,sha:g,delegated:!1,reason:y.reason};y.change==="ci-fixed"?x.passed=f.checks.passed:x.base=f.baseRefName||null,await c(k,y.change,x),l.push({change:y.change,number:f.number,reason:y.reason})}catch{}}return l}var gk=nz(tz),dC=3e4,rz=30*6e4,oz=dC,sz=10*6e4,az=10080*6e4,iz=60*6e4,rC=8,pC=1440*6e4,cz=3,fC="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",wk=60*6e4,lz=15*6e4,uz=9e4,dz=1440*6e4,Vl=new Map,zl=new Map,pz=360*6e4,fz=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}),mz=/could not resolve|not found|no pull requests?/i,hz=e=>mz.test(String(e?.message||e||""));function wz(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<wk)}var oC={fast:oz,slow:sz,decay:iz};function sC(e,t,n=mk){return mC(e,t,{grace:0})||!n(e.repo,t)&&(e.armedAt&&t-e.armedAt<rz||wz(e,t))?"fast":t-(e.firstSeen||0)>az?"decay":"slow"}function Yl(e,t,n,r){let o=e.lanes?.get(ce(t.repo,t.number))||{};return Xd({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function gz(e,t){Sr("watch");try{let{stdout:n}=await gk("gh",["pr","view",String(t),"--repo",e,"--json",fC],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Bn({slug:e})}});return JSON.parse(n||"null")}catch(n){throw yo(n)&&bo(n),n}}async function yz(e){Sr("watch");try{let{stdout:t}=await gk("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",fC],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await Bn({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw yo(t)&&bo(t),t}}async function bz(){let e=new Map;for(let t of await ke().catch(()=>[]))e.set(t.id,t);return e}async function kz(e,t){await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...t}}),await dp(e,t).catch(()=>{})}async function xz(e,t){let n=await xs(e);return n?Ba(n,t):null}async function vz(e,t,n){let r=await xs(e);if(!r)throw new Error(`no local checkout for ${e}`);return La(r,t,{method:n})}async function Sz(e,t){let n=await xs(e);if(!n)throw new Error(`no local checkout for ${e}`);return Nc(n,t)}async function Tz(e,t){await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...t}})}async function gi(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 Az=10*6e4;async function Ez(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>pC){await gi(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 gi(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await gi(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<Az)return;await gi(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 gi(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 gi(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await hC(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function yi(e,t,n){await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:t,...n}})}async function _z(e,t){try{return await gk("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Jl(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 bi=(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 mC(e,t,{grace:n=uz}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=dz}async function Iz(e,t,n,r){let o=r.now??Date.now();if(!mC(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||Ue(s))return!1;let a=(s.boxes||[]).find(d=>d.name===s.leadBox);return await(r.midTurn??is)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await K(s).catch(()=>[])).some(d=>d.type==="pr.state.changed"&&d.payload?.number===Number(e.number))?(await(r.claim??xr)(e.repo,e.number).catch(()=>null),!1):await(r.claim??xr)(e.repo,e.number).catch(()=>null)?(await yi(s,"opened",{...bi(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 $z(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 Wd(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await Gd(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<lz;await jA(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await LA(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Jl(a,"pushed",e.number,d=>d.payload?.sha===s)&&await yi(a,"pushed",{...bi(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>wk)await qd(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 qd(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 Jl(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await yi(a,"ci-fixed",{...bi(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await qd(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>wk)await Kd(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Kd(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Yl(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await Jl(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await yi(a,"reconciled",{...bi(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await Kd(e.repo,e.number).catch(()=>{})}}}}async function Rz(e,t,n,r){let o=Yl(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await Jl(s,"closed",e.number)&&await yi(s,"closed",{...bi(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 aC(e,t,n){Yl(n,e,"lost",null);let r=t.get(e.trackId);r&&!await Jl(r,"lost",e.number)&&await yi(r,"lost",bi(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function hC(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 iC(e={}){let{now:t=Date.now(),auth:n=vw,view:r=gz,list:o=yz,links:s=WA,trackMap:a=bz,emitMerged:i=kz,mark:c=wo,archiveIfArmed:l=Er,notifyIfArmed:u=_r,notifyIfResolved:d=nf,followupsIfMerged:p=Bs,batchThreshold:f=cz,detail:w=xz,merge:h=vz,updateBranch:y=Sz,lookup:k=ft,disarm:g=Rc,emitDisarmed:x=Tz,localHasCommit:S=_z,claim:_=xr,midTurn:M=is,only:P=null,wantDetail:j=null,covered:A=mk,verdicts:$=null,tokenFor:L=te=>ra(te),noteRefusal:H=DA,clearRefusal:R=BA}=e;if(!(await n().catch(()=>null))?.authenticated||Jd()>0)return 0;let C=await s().catch(()=>[]),W=fE(C),B=te=>{try{return!!j?.(te)}catch{return!1}},Z=P?C.filter(te=>{try{return P(te)}catch{return!1}}):C.filter(te=>t-(Vl.get(ce(te.repo,te.number))||0)>=oC[sC(te,t,A)]);if(!Z.length)return 0;let xe=Date.now(),Te=new Map,_e=new Map;for(let te of[...Z].sort(Pc)){let we=P?"event":sC(te,t,A);Te.set(ce(te.repo,te.number),{lane:we,intervalMs:oC[we]??null}),Vl.set(ce(te.repo,te.number),t),_e.has(te.repo)||_e.set(te.repo,[]),_e.get(te.repo).push(te)}let Q=await a().catch(()=>new Map),Xe=new Map,Pe=(te,we)=>{let Oe=ce(te,we);if(!Xe.has(Oe)){let Ve=Promise.resolve().then(()=>w(te,we));Ve.catch(()=>{}),Xe.set(Oe,Ve)}return Xe.get(Oe)},le=$??((te,we)=>Wf([{path:te,prs:[we]}],{now:t,slugOf:async()=>te,tracks:async()=>[...Q.values()].filter(Oe=>!Ue(Oe))}).catch(()=>{})),ge={now:t,lanes:Te,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,followupsIfMerged:p,detail:Pe,merge:h,updateBranch:y,lookup:k,disarm:g,emitDisarmed:x,localHasCommit:S,claim:_,midTurn:M};for(let[te,we]of _e){let Oe=fz(we);if(Oe.until>t)continue;let Ve=!1;try{if(await L(te),Oe.misses)for(let re of we)await R(re.repo,re.number).catch(()=>{})}catch(re){Ve=A0(re)}if(Ve){let re=Oe.misses+1;if(re>=rC){console.log(`[pr-watch] ${te}: not granted ${re}\xD7 \u2014 retiring ${we.length} link(s) as lost`);for(let he of we){await aC(he,Q,ge);let Be=ce(he.repo,he.number);zl.delete(Be),Vl.delete(Be)}}else for(let he of we)await H(he.repo,he.number,{misses:re,until:t+pz}).catch(()=>{});continue}let Nt,O=new Set,X=re=>re.ciFixAwait||re.reconcileAwait||B(re)?!0:!re.autoMerge||re.trackId&&W.get(re.trackId)!==ce(re.repo,re.number)?!1:t-(re.autoMerge.armedAt||0)<=pC;if(we.length>f)Nt=await o(te).catch(()=>null);else{Nt=new Map;for(let re of we){if(X(re)){let he=await Pe(te,re.number).catch(()=>null);if(he?.state){Nt.set(Number(re.number),{number:Number(re.number),url:he.url,title:he.title,headRefName:he.headRefName,headRefOid:he.headSha??null,state:he.state,mergedAt:he.mergedAt??null,closedAt:he.closedAt??null});continue}}try{let he=await r(te,re.number);he?.number!=null&&Nt.set(Number(he.number),he)}catch(he){hz(he)&&O.add(ce(re.repo,re.number))}}}if(Nt)for(let re of we){let he=ce(re.repo,re.number),Be=Nt.get(Number(re.number));if(!Be){if(O.has(he)){let Pt=(zl.get(he)||0)+1;zl.set(he,Pt),Pt>=rC&&(await aC(re,Q,ge),zl.delete(he),Vl.delete(he))}continue}if(zl.delete(he),Be.state==="MERGED"||Be.state==="CLOSED")Be.state==="MERGED"?await hC(re,Be,Q,ge):await Rz(re,Be,Q,ge),Vl.delete(he);else{await Iz(re,Be,Q,ge).catch(()=>{}),await $z(re,Be,Q,ge).catch(()=>{});let Pt=!re.trackId||W.get(re.trackId)===he;re.autoMerge&&Pt&&await Ez(re,Q,ge).catch(()=>{});try{if(B(re)&&!Xe.has(he)&&Pe(re.repo,re.number),Xe.has(he)){let tt=await Xe.get(he).catch(()=>null);tt?.checks&&await le(re.repo,tt)}}catch{}}}}let de={};for(let{lane:te}of Te.values())de[te]=(de[te]||0)+1;return console.log(`[pr-watch] probed ${Z.length} link(s) (${Object.entries(de).map(([te,we])=>`${te} ${we}`).join(", ")}) in ${Date.now()-xe}ms`),Z.length}function cC(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 hk=!1,qf=!1,Gf=[],lC=Promise.resolve();async function Oz(){for(;qf||Gf.length;){if(qf){qf=!1;try{await iC()}catch{}continue}let e=Gf;Gf=[];let t=e.map(r=>r.spec),n=Object.assign({},...e.map(r=>r.deps||{}));try{await iC({...n,only:r=>t.some(o=>cC(o,r)),wantDetail:r=>t.some(o=>o.detail&&cC(o,r))})}catch{}}}function Kf(e,t){return e===null?qf=!0:Gf.push({spec:e,deps:t}),hk||(hk=!0,lC=Oz().finally(()=>{hk=!1})),lC}function wC(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 Kf({repo:n,numbers:r,branch:e.branch||null,event:e.event||null,detail:e.detail===!0},t)}var Mz=6e4,uC=0;function gC(e=void 0){let t=e?.now??Date.now();return t-uC<Mz?Promise.resolve():(uC=t,Kf({all:!0},e))}function yC(){Kf(null),setInterval(()=>Kf(null),dC)}vs();fp();Qc();Ir();an();var Cz=new Set(["opened","reopened","closed","synchronize","ready_for_review","converted_to_draft","edited"]);async function Nz({tracks:e=ke,reconcile:t=Ep}={}){for(let n of(await e().catch(()=>[])).filter(r=>!Ue(r)))await t(n,{bypassThrottle:!0}).catch(()=>{})}function bC(e,t={}){let{invalidate:n=ln,probe:r=wC,poke:o=JE,reconcileOpened:s=Nz}=t;try{n()}catch{}let a={probe:null,poked:!1,reconciled:!1};if(!e?.repo)return a;if(e.event==="pull_request"&&Cz.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}dt();be();Ir();an();import{randomUUID as Pz}from"node:crypto";var Dz=2e4,kC=3,ki=new Map,yk=new Set;async function xC(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 Bz(e={}){let{sessions:t=null,tracks:n=null,append:r=N,read:o=K,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await kn(),i=n??await ke()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(Ue(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=(ki.get(d.name)||0)+1;ki.set(d.name,f),f===kC&&(yk.add(d.name),await xC(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:Pz(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let f=(ki.get(d.name)||0)>=kC;if(ki.delete(d.name),f||!yk.has(d.name)){yk.add(d.name);let w=await xC(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[...ki.keys()])l.has(u)||ki.delete(u)}var Xl=null,bk=!1;function vC(){return Xl?()=>{}:(Xl=setInterval(()=>{bk||(bk=!0,Bz().catch(()=>{}).finally(()=>{bk=!1}))},Dz),Xl.unref?.(),()=>{clearInterval(Xl),Xl=null})}gs();Qc();be();vs();Ss();gs();jc();import{randomUUID as Lz}from"node:crypto";async function jz(){let e=await Promise.resolve().then(()=>(Ir(),mR));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var kk={listRepoPrs:Kn,readEvents:K,appendEvent:N,sayDeps:jz,currentBranch:ko};async function Fz(e,t=kk,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??ko)(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 xk(e,t,{actions:n,notify:r=!1}={},o=kk){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 Fz(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=Lz();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 vk(e,{offerId:t,choice:n}={},r=kk){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');return vo(`prx:${e.id}:${t}`,()=>Uz(e,{offerId:t,choice:n},r))}async function Uz(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=ho.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(e.id,Ca(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,SA(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==="push"&&Number.isFinite(Number(a.pr?.number))?{number:Number(a.pr.number)}:{},...l?{actions:l}:{}}}),n!=="dismiss"&&a.notify===!0){let{queueShipCompleteNotify:u}=await r.sayDeps();u(e.id,{actions:l})}return{say:c,choice:n}}jc();import Ur from"node:fs/promises";import xi from"node:path";import $C from"node:os";import SC from"node:fs/promises";import Vf from"node:path";import{execFile as Hz}from"node:child_process";import{promisify as Wz}from"node:util";import{fileURLToPath as qz}from"node:url";var Gz=Wz(Hz),AC=Vf.dirname(qz(import.meta.url)),Kz=Vf.resolve(AC,"..","..","client","dist"),Vz="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",zz=new Map([...Vz].map((e,t)=>[e,t]));function Jz(e){let t=[],n=0,r=0;for(let o of e){let s=zz.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 Yz(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=Jz(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 Xz(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 TC=new Map;async function Zz(e){let t;try{t=await SC.stat(e)}catch{return null}let n=TC.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await SC.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:Yz(r.mappings||"")};return TC.set(e,o),o}catch{return null}}var Sk=null;function Qz(){return Sk||(Sk=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):Gz("git",["rev-parse","--short","HEAD"],{cwd:Vf.resolve(AC,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),Sk}var e3=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function t3(e,{distDir:t=Kz}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(e3)){let[a,i,c,l]=s,u=await Zz(Vf.join(t,"assets",`${i}.map`)),d=u?Xz(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 EC(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await Qz();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await t3(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var n3=()=>process.env.BOSTRAT_STATE_DIR||xi.join(process.env.HOME||$C.tmpdir(),".bostrat"),Fo=()=>xi.join(n3(),"telemetry"),RC=()=>xi.join(Fo(),".relay-offsets.json"),r3=50,o3=14,s3=3e4,_C=500,tn=(e,t)=>typeof e=="string"?e.slice(0,t):"";function a3(e,t){if(!e||typeof e!="object")return null;let n=tn(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:tn(e.anonId,64),sessionId:tn(e.sessionId,64),userId:tn(e.userId,64),release:tn(e.release,64),appVersion:tn(e.appVersion,32),route:tn(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:tn(e.error.kind,256),message:tn(e.error.message,8192),stack:tn(e.error.stack,32768),fingerprint:tn(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:tn(s?.n,64),r:tn(s?.r,128)}))),o}var OC=e=>new Date(e).toISOString().slice(0,10),IC=0;async function MC(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,r3):[]).map(s=>a3(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await EC(s);await Ur.mkdir(Fo(),{recursive:!0});let o=xi.join(Fo(),`${OC(t)}.ndjson`);return await Ur.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
802
|
-
`).join(""),"utf8"),t
|
|
803
|
-
`).filter(Boolean),d=[];for(let p of u)try{d.push(JSON.parse(p))}catch{}for(let p=0;p<d.length;p+=_C){let f=d.slice(p,p+_C).map(h=>c3(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 PC(r)}return{sent:o,disabled:!1}}var Tk=null,Ak=!1;function DC(){Tk||(Tk=setInterval(async()=>{if(!Ak){Ak=!0;try{await l3()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{Ak=!1}}},s3),Tk.unref?.())}import Yf from"node:fs/promises";import UC from"node:path";import u3 from"node:os";var HC=()=>process.env.BOSTRAT_STATE_DIR||UC.join(process.env.HOME||u3.tmpdir(),".bostrat"),WC=()=>UC.join(HC(),"dictation-glossary.json"),d3=100,p3=500,f3=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Xf=null,Ek=null,_k=Promise.resolve();function BC(){let e={},t=Date.now();for(let n of f3)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function qC(){return Xf||(Ek??=Yf.readFile(WC(),"utf8").then(e=>JSON.parse(e)?.terms??BC()).catch(()=>BC()).then(e=>Xf=e).finally(()=>Ek=null),Ek)}async function LC(){let e=JSON.stringify({terms:Xf},null,2),t=WC(),n=`${t}.${process.pid}.tmp`;await Yf.mkdir(HC(),{recursive:!0}),await Yf.writeFile(n,e),await Yf.rename(n,t)}function m3(){return _k=_k.then(LC,LC),_k.catch(()=>{})}async function Ik(){return Object.values(await qC()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var h3=e=>Math.max(1,Math.ceil(e.length/4));async function w3(){let e=[],t=0;for(let{term:n}of await Ik()){let r=h3(n);if(!(t+r>p3)&&(e.push(n),t+=r,e.length>=d3))break}return e}async function g3(e,t){let n=await qC(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var jC=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Jf=e=>e.toLowerCase();function FC(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function GC(e,t){let n=jC(e),r=jC(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]=Jf(n[u])===Jf(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;)Jf(n[c])===Jf(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(FC(r[l])&&i.add(r[l]),l++);for(;l<s;l++)FC(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await g3(u,"learned");return await m3(),[...i]}var Zl=null;function y3(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 KC(){if(!Zl)return null;let e=await $l();if(!e||e.apiKey.slice(-6)!==Zl.keyTail)return null;let{keyTail:t,...n}=Zl;return n}async function VC(e,t){let n=await $l();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 w3())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=y3(o.status,i);c.persistent&&(Zl={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 Zl=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}be();var $k={"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 b3(e){if(!e)return null;let t=String(e).trim();if($k[t])return t;let n=null;for(let r of Object.keys($k))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function k3(e,t={}){let n=$k[b3(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 x3(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 Ql=null;async function v3(){if(!Ql)return null;let e=await Tf();if(!e||e.apiKey.slice(-6)!==Ql.keyTail)return null;let{keyTail:t,...n}=Ql;return n}async function S3(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=x3(t.status,o);s.persistent&&(Ql={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}Ql=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 T3={openai:S3};async function zC(){let e=await Tf();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await v3()}:{configured:!1}}async function JC(){let e=await Tf();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 T3[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function YC(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=k3(t,s),i=await N(e,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:t||"unknown",usd:a,tokens:{input:s.textIn+s.audioIn,output:s.textOut+s.audioOut,cacheRead:s.textInCached+s.audioInCached,cacheWrite:0},voice:s,seconds:Math.max(0,Math.round(Number(r)||0)),source:"voice-chat"}});return{usd:a,seq:i.seq}}an();OA(async()=>new Set((await ke()).map(e=>e.id)));var oN=Re.dirname(E3(import.meta.url)),Nk=Number(process.env.PORT)||3362,Zf=ib(process.env),sN=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,vi=iM(process.env.BOSTRAT_ALLOW_IPS),Qf=cM(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Ff),Dk=uM(),aN=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",E=nn();E.disable("x-powered-by");E.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),Gl(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(ql(e)||vi.allowed(vi.clientIp(e))||Qf.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});E.use(wM);E.use(fM({token:Dk,enforce:aN}));E.use("/api/mocks",nn.json({limit:"1mb"}));E.use("/api/mocks",nn.raw({type:"image/png",limit:"24mb"}));E.use("/api/mocks",nn.raw({type:["video/mp4","video/webm"],limit:"105mb"}));E.use("/api/runbooks",nn.json({limit:"1mb"}));E.use("/api/tracks/adopt",nn.json({limit:"1mb"}));E.use("/api/telemetry",nn.json({limit:"256kb"}));E.use("/api/hook",nn.json({limit:"2mb"}));E.use("/api/dictation",nn.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));E.use("/api/tracks/:id/upload",nn.raw({type:()=>!0,limit:"25mb"}));E.use(nn.json({limit:"64kb"}));E.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:zt,hostTmuxAlive:await Zr(),autoTriggerPct:sN,plane:Si?Si.status():null})});E.get("/api/sessions",async(e,t)=>{t.json({sessions:await kn()})});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 jn(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(!je(n))return t.status(400).json({error:"invalid session name"});if(!await Se(n))return t.status(404).json({error:"session not found"});try{await Lt(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(!je(n))return t.status(400).json({error:"invalid session name"});let r=await Se(n);if(!r)return t.status(404).json({error:"session not found"});let o=await nc(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(!je(n))return t.status(400).json({error:"invalid session name"});try{let r=await fT(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=Sd(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 XC=10;async function iN(){let e=await ke(),[t,n]=await Promise.all([R$(e),hR(e)]);return{tracks:e,views:await gg(),queuedOf:r=>Cs(r),jobsOf:r=>t.get(r)??[],prPendingOf:r=>n.get(r)??[]}}E.get("/api/idle-guard",async(e,t)=>{try{t.json(ab(await iN()))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var hn=js(),O3=$R({kind:hn}),tu=null,Ct=()=>tu&&["auto","prompt","off"].includes(tu)?tu:O3,Pk=null,M3=_l.has(hn),cN=async({to:e,from:t})=>!M3||Il(await li())?!1:(ru.arm({to:e,from:t,kind:hn}),!0);async function C3(e){let t=Ct();tu=e??null;let n=Ct();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){ru.cancel();return}Pk&&await cN(Pk)}}var lN=(()=>{try{return R3(import.meta.url)("../package.json").version||null}catch{return null}})(),ru=OR({isBusy:async()=>ab(await iN()).busy,onStuck:({to:e,from:t})=>or({current:t,latest:e,installKind:hn,policy:Ct(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?or({current:t,latest:e,installKind:hn,policy:Ct(),status:"updating",reason:"auto"}):or({current:t,latest:e,installKind:hn,policy:Ct(),status:"failed",error:n?.reason})).catch(()=>{})});E.get("/api/node/self-update",async(e,t)=>{t.json({installKind:hn,policy:Ct(),policySource:tu?"plane":"node",current:lN,status:await li(),armed:ru.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 yf({to:n,kind:hn,reason:"manual"});if(r.ok)return ru.cancel(),or({current:r.from,latest:n,installKind:hn,policy:Ct(),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 uN(){try{let e=await RR();e&&await or({current:e.from,latest:e.to,installKind:e.kind||hn,policy:Ct(),status:e.status,error:e.error,reason:e.reason}),await ky({current:lN,installKind:hn,policy:Ct()})}catch{}}uN();setInterval(uN,3e4).unref();E.get("/api/tracks",async(e,t)=>{let n=(await ke()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>XC){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,XC).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:yl,agentAvailability:await zp(),tickets:await dO()})});E.get("/api/cost",async(e,t)=>{try{let n=await ke();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await Kb(n);if(r.cursorUsage=tg(),e.query.format==="text")return t.type("text/plain").send(BO(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 zb(await ke(),{window:e.query.window,from:e.query.from,to:e.query.to});if(e.query.format==="text")return t.type("text/plain").send(HO(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 VO(await ke(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=tg(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=_c(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/usage/caps",(e,t)=>{t.json(_c())});E.post("/api/usage/caps",async(e,t)=>{try{t.json(await Pd(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 nM(await ke(),{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 Zk(e.get(pr),{byToken:bl,legacy:()=>Hc(e.body?.session_id,e.body?.cwd)});if(n==="refused")return Ii("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return Ii("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&&Ii("mismatch",{track:r.track.id,box:r.box.name}),Ii(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 NO(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&Iu(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 bl(e.get(pr));if(!n?.track||!n?.box)return t.status(204).end();t.send((await CE(n.track,n.box)).join(`
|
|
800
|
+
`),a.destroy();return}t(s,a,i)}}}function xM(e,{addr:t,port:n,log:r=console,retryMs:o=15e3,maxRetries:s=1/0}){let a=0;e.on("error",c=>{let l=c?.code||c?.message;if(e.listening){r.error?.(`[bostrat-agent] view listener on ${t}:${n}: ${l}`);return}if(a>s){r.error?.(`[bostrat-agent] view listener on ${t}:${n} failed (${l}) \u2014 giving up`);return}r.error?.(`[bostrat-agent] view listener on ${t}:${n} failed (${l}) \u2014 retrying in ${Math.round(o/1e3)}s`),setTimeout(i,o).unref?.()});let i=()=>{a++,e.listen(n,t,()=>{r.log?.(`[bostrat-agent] view listener on ${t}:${n} (view Hosts only; front-door CIDRs decide the rest)`)})};return i(),e}Va();Og();Cp();Rd();be();import vM from"node:os";import{randomUUID as sk}from"node:crypto";var yV=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||vM.hostname(),nodeName:e.BOSTRAT_NODE_NAME||vM.hostname()});var SM=32768,TM=120,AM=1e3,EM=new Set(["claude","codex","cursor"]),bV=new Set(["off","transition","nag"]),kV=[[0,59],[0,23],[1,31],[1,12],[0,6]];function xV(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=kV[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 Pn=e=>String(e??"").trim(),zs={name(e){let t=Pn(e);if(!t)throw new Error("a loop needs a name");if(t.length>TM)throw new Error(`name is too long (${t.length}); cap is ${TM}`);return t},schedule(e){let t=Pn(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!xV(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=Pn(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>SM)throw new Error(`prompt is too long (${t.length} chars); cap is ${SM}`);return t},expectation(e){let t=Pn(e)||null;if(t&&t.length>AM)throw new Error(`expectation is too long (${t.length}); cap is ${AM}`);return t},notify(e){let t=Pn(e)||"off";if(!bV.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=Pn(typeof t=="string"?t:t?.provider)||"claude";if(!EM.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...EM].join(", ")}`);let r=typeof t=="object"&&t&&Pn(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 vV(e,t){let n=e&&typeof e=="object"?e:{},r=zs.name(n.name),o=zs.schedule(n.schedule),s=zs.prompt(n.prompt),a=zs.expectation(n.expectation),i=zs.notify(n.notify),c=zs.assignment(n.assignment),l=Pn(t?.workspace?.repo)||Pn(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}=yV();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:d||null}}async function IM(e,t){let n=vV(t,e),r=sk();return await N({id:e.id,cwd:e.cwd},{type:"loop.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:r,...n}}),{proposalId:r,...n}}async function $M(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 N({id:e.id,cwd:e.cwd},{type:"loop.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};if(!r)throw new Error("loopId required \u2014 the loop is created on the plane before the card is answered");let i=a.payload;return await N({id:e.id,cwd:e.cwd},{type:"loop.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:String(r),name:i.name,schedule:i.schedule,provider:i.assignment?.provider??"claude",repo:i.repo??null,nodeId:i.nodeId??null,nextFireAt:Number.isFinite(Number(o))?Number(o):null}}),await N({id:e.id,cwd:e.cwd},{type:"loop.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",loopId:String(r)}}),{proposalId:t,action:"created",loopId:String(r)}}var ok=["name","schedule","prompt","expectation","notify","assignment","enabled"],SV=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function RM(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 OM(e,t){let n=Pn(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 TV(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=>!ok.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: ${ok.join(", ")}`)}let o={};for(let a of ok){if(!(a in n))continue;let i=zs[a](n[a]);(a==="assignment"?SV(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 MM(e,t,n){let r=RM(t),{patch:o,changed:s}=TV(n,r),a=sk(),i={ts:Date.now(),proposalId:a,loopId:String(t.id),name:r.name,base:r,patch:o,changed:s,selfEdit:!!(r.trackId&&r.trackId===e.id)};return await N({id:e.id,cwd:e.cwd},{type:"loop.update.offered",actor:"system",source:"orchestrator",payload:i}),i}var _M=600;async function CM(e,t,n){let r=Pn(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>_M)throw new Error(`reason is too long (${r.length}); cap is ${_M}`);let o=RM(t),s=sk(),a={ts:Date.now(),proposalId:s,loopId:String(t.id),name:o.name,base:o,reason:r,selfEdit:!!(o.trackId&&o.trackId===e.id)};return await N({id:e.id,cwd:e.cwd},{type:"loop.delete.offered",actor:"system",source:"orchestrator",payload:a}),a}var NM=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 PM(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 NM(e,"loop.update.offered",t)).payload,a={id:e.id,cwd:e.cwd};return n==="applied"&&await N(a,{type:"loop.updated",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:s.loopId,name:s.patch?.name??s.name,changed:s.changed??Object.keys(s.patch||{}),nextFireAt:Number.isFinite(Number(r))?Number(r):null}}),await N(a,{type:"loop.update.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:s.loopId}}),{proposalId:t,action:n,loopId:s.loopId}}async function DM(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 NM(e,"loop.delete.offered",t)).payload,s={id:e.id,cwd:e.cwd};return(n==="delete"||n==="pause")&&await N(s,{type:n==="delete"?"loop.deleted":"loop.paused",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:o.loopId,name:o.name,trackId:o.base?.trackId??null}}),await N(s,{type:"loop.delete.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:o.loopId}}),{proposalId:t,action:n,loopId:o.loopId}}be();import{randomUUID as AV}from"node:crypto";var ck=20,EV=300,ak=120,BM=120,LM=2e3,Uf=new Set(["build","ops","debt","spike"]),jM=new Set(["active","done","abandoned"]),lk=new Set(["circuit.create","circuit.update","circuit.delete"]),_V=new Set(["person.create","slot.create","slot.update","slot.delete","reservation.create","reservation.update","reservation.delete"]),Vl=new Set([...lk,..._V]),IV=["name","description","status","type"],$V=["person","startDate","endDate","estUsd"],RV=["person","label","startDate","endDate"],OV=/^\d{4}-\d{2}-\d{2}$/,en=e=>String(e??"").trim(),uk=e=>typeof e=="string"&&e.startsWith("@"),HM=864e5;function WM(e){let t=Date.parse(`${e}T00:00:00Z`),n=new Date(t).getUTCDay();return new Date(t-(n+6)%7*HM).toISOString().slice(0,10)}function MV(e){return new Date(Date.parse(`${WM(e)}T00:00:00Z`)+6*HM).toISOString().slice(0,10)}var FM=(e,t)=>{let n=en(e);if(!OV.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 CV(e,t){let n=FM(e,"startDate"),r=FM(t,"endDate");if(r<n)throw new Error(`endDate ${r} is before startDate ${n}`);let o=WM(n),s=MV(r);return{startDate:o,endDate:s,snapped:o!==n||s!==r}}var UM=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},jo=(e,t,n)=>{let r=en(e);if(r.length>t)throw new Error(`${n} is too long (${r.length}); cap is ${t}`);return r};function ik(e,t){let n=en(t);if(!n)throw new Error("a circuit name or id is required");if(uk(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 NV(e,t){let n=en(t);if(!n)throw new Error('a person is required \u2014 "me", a person id, or a row label');if(n==="me"||uk(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 PV(e,t,{allow:n=Vl}={}){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>ck)throw new Error(`too many ops (${o.length}); cap is ${ck} \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=x=>{x.id&&(l.circuits[x.id]={name:x.name,status:x.status,type:x.type??"build",updatedAt:x.updatedAt??null},u[x.id]=x.name)},h=x=>{x.id&&(u[x.id]=x.label)},y=(x,S)=>{let _=en(x);if(!_)return null;if(d.has(_))throw new Error(`ref "${_}" is used by two ops \u2014 each ref names one thing`);return d.add(_),u[`@${_}`]=S,_},k=o.map((x,S)=>{let _=x&&typeof x=="object"?x:{},M=en(_.op),P=`op ${S+1}`;if(!Vl.has(M))throw new Error(`${P}: unknown op "${M}" \u2014 one of ${[...Vl].join(", ")}`);if(!n.has(M))throw new Error(`${P}: "${M}" isn't a circuit op \u2014 roadmap changes go through \`bostrat-roadmap propose\` (one card, either CLI)`);let j=R=>{throw new Error(`${P} (${M}): ${R}`)},A=R=>{let T=_.set&&typeof _.set=="object"&&!Array.isArray(_.set)?_.set:null;T||j("needs a `set` object naming the fields to change");let C=Object.keys(T).filter(W=>!R.includes(W));return C.length&&j(`unknown field(s) ${C.join(", ")} \u2014 settable: ${R.join(", ")}`),Object.keys(T).length||j("`set` names no fields"),T},$=R=>{let{startDate:T,endDate:C,snapped:W}=CV(R.startDate,R.endDate);return W&&(p=!0),{startDate:T,endDate:C,snapped:W}},L=R=>{let T=NV(a,R);return en(R)==="me"&&(f=!0),h(T),{key:T.id??en(R),label:T.label}},H=(R,T,C,W,B)=>{let Z=en(R);Z||j(`needs a ${W} id`);let xe=T.find(Te=>Te[C]===Z);return xe||j(`no ${W} "${Z}" \u2014 see \`bostrat-roadmap ls\``),B[Z]={...xe},xe};switch(M){case"circuit.create":{let R=jo(_.name,BM,"name");R||j("a circuit needs a name");let T=en(_.type)||"build";Uf.has(T)||j(`bad type "${T}" (${[...Uf].join(" | ")})`);let C=s.find(B=>String(B.name).toLowerCase()===R.toLowerCase());C&&j(`a circuit named "${C.name}" already exists (${C.id}) \u2014 update it instead of making a twin`);let W=y(_.ref,R);return{op:M,...W?{ref:W}:{},name:R,description:jo(_.description,LM,"description"),type:T}}case"circuit.update":{let R=ik(s,_.circuit);R.id||j("can't update a circuit created in the same plan \u2014 set its fields on the create op"),w(R);let T=A(IV),C={};if(T.name!==void 0){let W=jo(T.name,BM,"name");W||j("name can't be blank"),W!==R.name&&(C.name=W)}return T.description!==void 0&&(C.description=jo(T.description,LM,"description")),T.status!==void 0&&(jM.has(T.status)||j(`bad status "${T.status}" (${[...jM].join(" | ")})`),T.status!==R.status&&(C.status=T.status)),T.type!==void 0&&(Uf.has(T.type)||j(`bad type "${T.type}" (${[...Uf].join(" | ")})`),T.type!==(R.type??"build")&&(C.type=T.type)),Object.keys(C).length||j("nothing would change \u2014 the set equals the circuit's current values"),{op:M,circuit:R.id,name:R.name,set:C}}case"circuit.delete":{let R=ik(s,_.circuit);R.id||j("can't delete a circuit created in the same plan"),w(R);let T=o.some((C,W)=>W<S&&en(C?.op)==="circuit.update"&&C?.set?.status&&C.set.status!=="active"&&en(C?.circuit)===en(_.circuit));return R.status==="active"&&!T&&j(`\u201C${R.name}\u201D is active \u2014 an active circuit can't be deleted; propose a circuit.update to done/abandoned first (both can ride one card)`),{op:M,circuit:R.id,name:R.name,status:R.status}}case"person.create":{let R=jo(_.label,ak,"label");R||j("a person row needs a label");let T=a.find(W=>String(W.label).toLowerCase()===R.toLowerCase());T&&j(`a roadmap row labelled "${T.label}" already exists (${T.personId})`);let C=y(_.ref,R);return{op:M,...C?{ref:C}:{},label:R}}case"slot.create":{let R=ik(s,_.circuit);w(R);let T=L(_.person),C=$(_);return{op:M,circuit:R.id??R.ref,circuitName:R.name,person:T.key,personLabel:T.label,...C,estUsd:UM(_.estUsd)}}case"slot.update":{let R=H(_.slot,i,"slotId","slot",l.slots),T=A($V),C={};if(T.person!==void 0){let B=L(T.person);C.person=B.key,C.personLabel=B.label}if(T.startDate!==void 0||T.endDate!==void 0){let B=$({startDate:T.startDate??R.startDate,endDate:T.endDate??R.endDate});B.startDate!==R.startDate&&(C.startDate=B.startDate),B.endDate!==R.endDate&&(C.endDate=B.endDate)}T.estUsd!==void 0&&(C.estUsd=UM(T.estUsd)),Object.keys(C).length||j("nothing would change");let W=s.find(B=>B.id===R.circuitId);return{op:M,slot:R.slotId,circuitName:W?.name??null,set:C}}case"slot.delete":{let R=H(_.slot,i,"slotId","slot",l.slots),T=s.find(C=>C.id===R.circuitId);return{op:M,slot:R.slotId,circuitName:T?.name??null,startDate:R.startDate,endDate:R.endDate}}case"reservation.create":{let R=L(_.person),T=jo(_.label,ak,"label");return T||j("a reservation needs a label (Leave, Support rotation, \u2026)"),{op:M,person:R.key,personLabel:R.label,label:T,...$(_)}}case"reservation.update":{let R=H(_.reservation,c,"reservationId","reservation",l.reservations),T=A(RV),C={};if(T.person!==void 0){let W=L(T.person);C.person=W.key,C.personLabel=W.label}if(T.label!==void 0){let W=jo(T.label,ak,"label");W||j("label can't be blank"),W!==R.label&&(C.label=W)}if(T.startDate!==void 0||T.endDate!==void 0){let W=$({startDate:T.startDate??R.startDate,endDate:T.endDate??R.endDate});W.startDate!==R.startDate&&(C.startDate=W.startDate),W.endDate!==R.endDate&&(C.endDate=W.endDate)}return Object.keys(C).length||j("nothing would change"),{op:M,reservation:R.reservationId,label:R.label,set:C}}case"reservation.delete":{let R=H(_.reservation,c,"reservationId","reservation",l.reservations);return{op:M,reservation:R.reservationId,label:R.label,startDate:R.startDate,endDate:R.endDate}}default:return j("unhandled op")}});k.forEach((x,S)=>{for(let _ of[x.circuit,x.person,x.set?.person])if(uk(_)&&!d.has(String(_).slice(1)))throw new Error(`op ${S+1} (${x.op}): unknown ref "${_}" \u2014 no earlier op in this plan creates it`)});let g=a.some(x=>x.kind==="user"&&x.userId);return{why:jo(r.why,EV,"why"),ops:k,base:l,resolved:u,snapped:p,createsMeRow:f&&!g}}async function qM(e,t,n,r={}){let o=PV(t,n,r),s=AV(),a={ts:Date.now(),proposalId:s,...o};return await N({id:e.id,cwd:e.cwd},{type:"planning.change.offered",actor:"system",source:"orchestrator",payload:a}),a}var DV=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 GM(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 DV(e,t),s={id:e.id,cwd:e.cwd},a=o.payload;return n==="applied"&&await N(s,{type:"planning.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,why:a.why??"",results:Array.isArray(r)?r.slice(0,ck+1):[]}}),await N(s,{type:"planning.change.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,ops:a.ops?.length??0}}),{proposalId:t,action:n}}yp();be();Yr();be();Gn();Cp();var KM=10,VM=200,BV=120,LV=24,JM=/^\d{4}-\d{2}-\d{2}$/;function dk(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var jV=(e=Date.now())=>new Date(e).toISOString().slice(0,10),Et=(e,t="i")=>new RegExp(e,t),FV=[{re:Et("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:Et("\\bSPC\\b",""),reason:"business (SPC)"},{re:Et("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:Et("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:Et("\\bReplicas\\b",""),reason:"competitor research"},{re:Et("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:Et("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:Et("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],UV=[Et("\\bSPC\\b",""),Et("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),Et("competitor|cloudcli|\\bkiro\\b"),Et("\\bReplicas\\b",""),Et("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),Et("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),Et("(^|[^\\w&])#\\d+\\b"),Et("\\btrack/[\\w.-]+")];function HV({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of FV)if(o.test(n)||o.test(r))return s;return null}function zM(e){let t=String(e||"");for(let n of UV){let r=n.exec(t);if(r)return r[0].trim()}return null}function YM(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 WV=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function qV(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=rl(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(`
|
|
801
|
+
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,BV)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${LV}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=WV.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=zM(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>VM)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${VM}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=zM(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>KM)throw new Error(`${r.length} bullets \u2014 over the ${KM}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function GV(e,{repos:t=dk()}={}){let n=new Set(t);return(await En()).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:HV({title:r.title,headRef:r.headRef})}))}async function XM(e,t){let n=await GV(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function ZM(e,{md:t,day:n=null}){let r=qV(t),o=n==null||n===""?jV():String(n);if(!JM.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await N({id:e.id,cwd:e.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:e.leadBox||null,payload:{ts:Date.now(),day:o,title:r.title,bullets:r.bullets}});return{day:o,title:r.title,bullets:r.bullets,eventId:s.id}}async function QM(e,{day:t}){let n=String(t||"");if(!JM.test(n)||Number.isNaN(Date.parse(n)))throw new Error(`invalid day "${t}" \u2014 expected YYYY-MM-DD`);let r=await N({id:e.id,cwd:e.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:e.leadBox||null,payload:{ts:Date.now(),day:n,title:null,bullets:[],retract:!0}});return{day:n,retracted:!0,eventId:r.id}}vs();Gn();Ss();be();vs();vt();Ir();Gn();Oc();fp();Qd();va();an();import{execFile as rz}from"node:child_process";import{promisify as oz}from"node:util";var KV=new Set(["pull_request","check_suite","workflow_run","push"]),VV=/^[^/\s]+\/[^/\s]+$/,zV=500,pk=new Set,fk=[],Hf=new Map,mn={hourStart:0,byEvent:{},duplicates:0,rejected:0};function mk(e,t,n){let r=Math.floor(e/36e5)*36e5;r!==mn.hourStart&&(mn.hourStart=r,mn.byEvent={},mn.duplicates=0,mn.rejected=0),t==="accepted"?mn.byEvent[n]=(mn.byEvent[n]||0)+1:mn[t]+=1}function JV(e){if(!e||typeof e!="object"||e.forge!=="github"||typeof e.event!="string"||!KV.has(e.event)||typeof e.repo!="string"||!VV.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 eC(e,{now:t=Date.now(),onAccept:n=null,log:r=console}={}){let o=JV(e);if(!o)return mk(t,"rejected"),{ok:!1,error:"malformed github event envelope"};if(o.deliveryId){if(pk.has(o.deliveryId))return mk(t,"duplicates"),{ok:!0,duplicate:!0,envelope:o};for(pk.add(o.deliveryId),fk.push(o.deliveryId);fk.length>zV;)pk.delete(fk.shift())}let s=o.repo.toLowerCase(),a=Hf.get(s)||{lastDeliveryAt:0,count:0};a.lastDeliveryAt=t,a.count+=1,Hf.set(s,a),mk(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 YV(e,t=Date.now()){let n=Hf.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 tC=1440*6e4;function hk(e,t=Date.now()){let n=YV(e,t);return n.covered&&n.ageMs<tC}function nC(e=Date.now()){let t={};for(let[n,r]of Hf)t[n]={lastDeliveryAt:r.lastDeliveryAt,count:r.count,covered:e-r.lastDeliveryAt<tC};return{hourStart:mn.hourStart,byEvent:{...mn.byEvent},duplicates:mn.duplicates,rejected:mn.rejected,repos:t}}be();Gn();Gr();Ir();an();function rC(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function XV(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var ZV=new Set(["merged","closed","lost"]);function QV(e,t){if(!e?.blocked)return null;let n=rC(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 ez(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function tz(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 nz(e,t,n){await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:t,...n}})}async function Wf(e,t={}){let{now:n=Date.now(),slugOf:r=Me,lookup:o=ft,note:s=HA,tracks:a=async()=>(await ke()).filter(d=>!Ue(d)),asked:i=tz,emitChange:c=nz}=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(!XV(f))continue;let w=await o(p,f.number).catch(()=>null);if(!w?.trackId||ZV.has(w.state))continue;let{prev:h}=await s(p,f.number,{blocked:rC(f),sha:f.headSha??null,at:n});if(!h)continue;let y=QV(h,f);if(!y||ez(w,y.change))continue;u??=new Map((await a()).map(S=>[S.id,S]));let k=u.get(w.trackId);if(!k)continue;let g=f.headSha??null;if(await i(k,y.change,f.number,g,h.at||0))continue;let x={number:Number(f.number),url:f.url||w.url,title:f.title||w.title,branch:f.headRefName||w.headRef,repo:p,sha:g,delegated:!1,reason:y.reason};y.change==="ci-fixed"?x.passed=f.checks.passed:x.base=f.baseRefName||null,await c(k,y.change,x),l.push({change:y.change,number:f.number,reason:y.reason})}catch{}}return l}var yk=oz(rz),pC=3e4,sz=30*6e4,az=pC,iz=10*6e4,cz=10080*6e4,lz=60*6e4,oC=8,fC=1440*6e4,uz=3,mC="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",gk=60*6e4,dz=15*6e4,pz=9e4,fz=1440*6e4,zl=new Map,Jl=new Map,mz=360*6e4,hz=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}),wz=/could not resolve|not found|no pull requests?/i,gz=e=>wz.test(String(e?.message||e||""));function yz(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<gk)}var sC={fast:az,slow:iz,decay:lz};function aC(e,t,n=hk){return hC(e,t,{grace:0})||!n(e.repo,t)&&(e.armedAt&&t-e.armedAt<sz||yz(e,t))?"fast":t-(e.firstSeen||0)>cz?"decay":"slow"}function Xl(e,t,n,r){let o=e.lanes?.get(ce(t.repo,t.number))||{};return Zd({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function bz(e,t){Sr("watch");try{let{stdout:n}=await yk("gh",["pr","view",String(t),"--repo",e,"--json",mC],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Bn({slug:e})}});return JSON.parse(n||"null")}catch(n){throw yo(n)&&bo(n),n}}async function kz(e){Sr("watch");try{let{stdout:t}=await yk("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",mC],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await Bn({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw yo(t)&&bo(t),t}}async function xz(){let e=new Map;for(let t of await ke().catch(()=>[]))e.set(t.id,t);return e}async function vz(e,t){await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...t}}),await pp(e,t).catch(()=>{})}async function Sz(e,t){let n=await xs(e);return n?Ba(n,t):null}async function Tz(e,t,n){let r=await xs(e);if(!r)throw new Error(`no local checkout for ${e}`);return La(r,t,{method:n})}async function Az(e,t){let n=await xs(e);if(!n)throw new Error(`no local checkout for ${e}`);return Nc(n,t)}async function Ez(e,t){await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...t}})}async function gi(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 _z=10*6e4;async function Iz(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>fC){await gi(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 gi(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await gi(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<_z)return;await gi(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 gi(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 gi(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await wC(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function yi(e,t,n){await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:t,...n}})}async function $z(e,t){try{return await yk("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Yl(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 bi=(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 hC(e,t,{grace:n=pz}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=fz}async function Rz(e,t,n,r){let o=r.now??Date.now();if(!hC(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||Ue(s))return!1;let a=(s.boxes||[]).find(d=>d.name===s.leadBox);return await(r.midTurn??is)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await K(s).catch(()=>[])).some(d=>d.type==="pr.state.changed"&&d.payload?.number===Number(e.number))?(await(r.claim??xr)(e.repo,e.number).catch(()=>null),!1):await(r.claim??xr)(e.repo,e.number).catch(()=>null)?(await yi(s,"opened",{...bi(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 Oz(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 qd(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await Kd(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<dz;await UA(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await FA(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Yl(a,"pushed",e.number,d=>d.payload?.sha===s)&&await yi(a,"pushed",{...bi(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>gk)await Gd(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 Gd(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Xl(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await Yl(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await yi(a,"ci-fixed",{...bi(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await Gd(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>gk)await Vd(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Vd(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Xl(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await Yl(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await yi(a,"reconciled",{...bi(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await Vd(e.repo,e.number).catch(()=>{})}}}}async function Mz(e,t,n,r){let o=Xl(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await Yl(s,"closed",e.number)&&await yi(s,"closed",{...bi(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 iC(e,t,n){Xl(n,e,"lost",null);let r=t.get(e.trackId);r&&!await Yl(r,"lost",e.number)&&await yi(r,"lost",bi(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function wC(e,t,n,r,o={}){let s=Xl(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 cC(e={}){let{now:t=Date.now(),auth:n=vw,view:r=bz,list:o=kz,links:s=GA,trackMap:a=xz,emitMerged:i=vz,mark:c=wo,archiveIfArmed:l=Er,notifyIfArmed:u=_r,notifyIfResolved:d=rf,followupsIfMerged:p=Bs,batchThreshold:f=uz,detail:w=Sz,merge:h=Tz,updateBranch:y=Az,lookup:k=ft,disarm:g=Rc,emitDisarmed:x=Ez,localHasCommit:S=$z,claim:_=xr,midTurn:M=is,only:P=null,wantDetail:j=null,covered:A=hk,verdicts:$=null,tokenFor:L=te=>ra(te),noteRefusal:H=LA,clearRefusal:R=jA}=e;if(!(await n().catch(()=>null))?.authenticated||Yd()>0)return 0;let C=await s().catch(()=>[]),W=hE(C),B=te=>{try{return!!j?.(te)}catch{return!1}},Z=P?C.filter(te=>{try{return P(te)}catch{return!1}}):C.filter(te=>t-(zl.get(ce(te.repo,te.number))||0)>=sC[aC(te,t,A)]);if(!Z.length)return 0;let xe=Date.now(),Te=new Map,_e=new Map;for(let te of[...Z].sort(Pc)){let we=P?"event":aC(te,t,A);Te.set(ce(te.repo,te.number),{lane:we,intervalMs:sC[we]??null}),zl.set(ce(te.repo,te.number),t),_e.has(te.repo)||_e.set(te.repo,[]),_e.get(te.repo).push(te)}let Q=await a().catch(()=>new Map),Xe=new Map,Pe=(te,we)=>{let Oe=ce(te,we);if(!Xe.has(Oe)){let Ve=Promise.resolve().then(()=>w(te,we));Ve.catch(()=>{}),Xe.set(Oe,Ve)}return Xe.get(Oe)},le=$??((te,we)=>Wf([{path:te,prs:[we]}],{now:t,slugOf:async()=>te,tracks:async()=>[...Q.values()].filter(Oe=>!Ue(Oe))}).catch(()=>{})),ge={now:t,lanes:Te,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,followupsIfMerged:p,detail:Pe,merge:h,updateBranch:y,lookup:k,disarm:g,emitDisarmed:x,localHasCommit:S,claim:_,midTurn:M};for(let[te,we]of _e){let Oe=hz(we);if(Oe.until>t)continue;let Ve=!1;try{if(await L(te),Oe.misses)for(let re of we)await R(re.repo,re.number).catch(()=>{})}catch(re){Ve=_0(re)}if(Ve){let re=Oe.misses+1;if(re>=oC){console.log(`[pr-watch] ${te}: not granted ${re}\xD7 \u2014 retiring ${we.length} link(s) as lost`);for(let he of we){await iC(he,Q,ge);let Be=ce(he.repo,he.number);Jl.delete(Be),zl.delete(Be)}}else for(let he of we)await H(he.repo,he.number,{misses:re,until:t+mz}).catch(()=>{});continue}let Nt,O=new Set,X=re=>re.ciFixAwait||re.reconcileAwait||B(re)?!0:!re.autoMerge||re.trackId&&W.get(re.trackId)!==ce(re.repo,re.number)?!1:t-(re.autoMerge.armedAt||0)<=fC;if(we.length>f)Nt=await o(te).catch(()=>null);else{Nt=new Map;for(let re of we){if(X(re)){let he=await Pe(te,re.number).catch(()=>null);if(he?.state){Nt.set(Number(re.number),{number:Number(re.number),url:he.url,title:he.title,headRefName:he.headRefName,headRefOid:he.headSha??null,state:he.state,mergedAt:he.mergedAt??null,closedAt:he.closedAt??null});continue}}try{let he=await r(te,re.number);he?.number!=null&&Nt.set(Number(he.number),he)}catch(he){gz(he)&&O.add(ce(re.repo,re.number))}}}if(Nt)for(let re of we){let he=ce(re.repo,re.number),Be=Nt.get(Number(re.number));if(!Be){if(O.has(he)){let Pt=(Jl.get(he)||0)+1;Jl.set(he,Pt),Pt>=oC&&(await iC(re,Q,ge),Jl.delete(he),zl.delete(he))}continue}if(Jl.delete(he),Be.state==="MERGED"||Be.state==="CLOSED")Be.state==="MERGED"?await wC(re,Be,Q,ge):await Mz(re,Be,Q,ge),zl.delete(he);else{await Rz(re,Be,Q,ge).catch(()=>{}),await Oz(re,Be,Q,ge).catch(()=>{});let Pt=!re.trackId||W.get(re.trackId)===he;re.autoMerge&&Pt&&await Iz(re,Q,ge).catch(()=>{});try{if(B(re)&&!Xe.has(he)&&Pe(re.repo,re.number),Xe.has(he)){let tt=await Xe.get(he).catch(()=>null);tt?.checks&&await le(re.repo,tt)}}catch{}}}}let de={};for(let{lane:te}of Te.values())de[te]=(de[te]||0)+1;return console.log(`[pr-watch] probed ${Z.length} link(s) (${Object.entries(de).map(([te,we])=>`${te} ${we}`).join(", ")}) in ${Date.now()-xe}ms`),Z.length}function lC(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 wk=!1,qf=!1,Gf=[],uC=Promise.resolve();async function Cz(){for(;qf||Gf.length;){if(qf){qf=!1;try{await cC()}catch{}continue}let e=Gf;Gf=[];let t=e.map(r=>r.spec),n=Object.assign({},...e.map(r=>r.deps||{}));try{await cC({...n,only:r=>t.some(o=>lC(o,r)),wantDetail:r=>t.some(o=>o.detail&&lC(o,r))})}catch{}}}function Kf(e,t){return e===null?qf=!0:Gf.push({spec:e,deps:t}),wk||(wk=!0,uC=Cz().finally(()=>{wk=!1})),uC}function gC(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 Kf({repo:n,numbers:r,branch:e.branch||null,event:e.event||null,detail:e.detail===!0},t)}var Nz=6e4,dC=0;function yC(e=void 0){let t=e?.now??Date.now();return t-dC<Nz?Promise.resolve():(dC=t,Kf({all:!0},e))}function bC(){Kf(null),setInterval(()=>Kf(null),pC)}vs();mp();Qc();Ir();an();var Pz=new Set(["opened","reopened","closed","synchronize","ready_for_review","converted_to_draft","edited"]);async function Dz({tracks:e=ke,reconcile:t=_p}={}){for(let n of(await e().catch(()=>[])).filter(r=>!Ue(r)))await t(n,{bypassThrottle:!0}).catch(()=>{})}function kC(e,t={}){let{invalidate:n=ln,probe:r=gC,poke:o=XE,reconcileOpened:s=Dz}=t;try{n()}catch{}let a={probe:null,poked:!1,reconciled:!1};if(!e?.repo)return a;if(e.event==="pull_request"&&Pz.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}dt();be();Ir();an();import{randomUUID as Bz}from"node:crypto";var Lz=2e4,xC=3,ki=new Map,bk=new Set;async function vC(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 jz(e={}){let{sessions:t=null,tracks:n=null,append:r=N,read:o=K,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await kn(),i=n??await ke()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(Ue(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=(ki.get(d.name)||0)+1;ki.set(d.name,f),f===xC&&(bk.add(d.name),await vC(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:Bz(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let f=(ki.get(d.name)||0)>=xC;if(ki.delete(d.name),f||!bk.has(d.name)){bk.add(d.name);let w=await vC(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[...ki.keys()])l.has(u)||ki.delete(u)}var Zl=null,kk=!1;function SC(){return Zl?()=>{}:(Zl=setInterval(()=>{kk||(kk=!0,jz().catch(()=>{}).finally(()=>{kk=!1}))},Lz),Zl.unref?.(),()=>{clearInterval(Zl),Zl=null})}gs();Qc();be();vs();Ss();gs();jc();import{randomUUID as Fz}from"node:crypto";async function Uz(){let e=await Promise.resolve().then(()=>(Ir(),hR));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var xk={listRepoPrs:Kn,readEvents:K,appendEvent:N,sayDeps:Uz,currentBranch:ko};async function Hz(e,t=xk,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??ko)(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 vk(e,t,{actions:n,notify:r=!1}={},o=xk){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 Hz(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=Fz();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 Sk(e,{offerId:t,choice:n}={},r=xk){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');return vo(`prx:${e.id}:${t}`,()=>Wz(e,{offerId:t,choice:n},r))}async function Wz(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=ho.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(e.id,Ca(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,AA(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==="push"&&Number.isFinite(Number(a.pr?.number))?{number:Number(a.pr.number)}:{},...l?{actions:l}:{}}}),n!=="dismiss"&&a.notify===!0){let{queueShipCompleteNotify:u}=await r.sayDeps();u(e.id,{actions:l})}return{say:c,choice:n}}jc();import Ur from"node:fs/promises";import xi from"node:path";import RC from"node:os";import TC from"node:fs/promises";import Vf from"node:path";import{execFile as qz}from"node:child_process";import{promisify as Gz}from"node:util";import{fileURLToPath as Kz}from"node:url";var Vz=Gz(qz),EC=Vf.dirname(Kz(import.meta.url)),zz=Vf.resolve(EC,"..","..","client","dist"),Jz="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",Yz=new Map([...Jz].map((e,t)=>[e,t]));function Xz(e){let t=[],n=0,r=0;for(let o of e){let s=Yz.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 Zz(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=Xz(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 Qz(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 AC=new Map;async function e3(e){let t;try{t=await TC.stat(e)}catch{return null}let n=AC.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await TC.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:Zz(r.mappings||"")};return AC.set(e,o),o}catch{return null}}var Tk=null;function t3(){return Tk||(Tk=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):Vz("git",["rev-parse","--short","HEAD"],{cwd:Vf.resolve(EC,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),Tk}var n3=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function r3(e,{distDir:t=zz}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(n3)){let[a,i,c,l]=s,u=await e3(Vf.join(t,"assets",`${i}.map`)),d=u?Qz(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 _C(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await t3();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await r3(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var o3=()=>process.env.BOSTRAT_STATE_DIR||xi.join(process.env.HOME||RC.tmpdir(),".bostrat"),Fo=()=>xi.join(o3(),"telemetry"),OC=()=>xi.join(Fo(),".relay-offsets.json"),s3=50,a3=14,i3=3e4,IC=500,tn=(e,t)=>typeof e=="string"?e.slice(0,t):"";function c3(e,t){if(!e||typeof e!="object")return null;let n=tn(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:tn(e.anonId,64),sessionId:tn(e.sessionId,64),userId:tn(e.userId,64),release:tn(e.release,64),appVersion:tn(e.appVersion,32),route:tn(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:tn(e.error.kind,256),message:tn(e.error.message,8192),stack:tn(e.error.stack,32768),fingerprint:tn(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:tn(s?.n,64),r:tn(s?.r,128)}))),o}var MC=e=>new Date(e).toISOString().slice(0,10),$C=0;async function CC(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,s3):[]).map(s=>c3(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await _C(s);await Ur.mkdir(Fo(),{recursive:!0});let o=xi.join(Fo(),`${MC(t)}.ndjson`);return await Ur.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
802
|
+
`).join(""),"utf8"),t-$C>6*3600*1e3&&($C=t,l3(t).catch(()=>{})),r.length}async function l3(e=Date.now()){let t=MC(e-a3*24*3600*1e3),n=[];try{n=await Ur.readdir(Fo())}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 Ur.rm(xi.join(Fo(),s),{force:!0});let o=await PC();for(let s of r)delete o[s];await DC(o)}var zf=new Map;function NC(e,t=Date.now()){let n=e||"unknown",r=zf.get(n);return r||(zf.size>512&&zf.clear(),r={tokens:40,at:t},zf.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 PC(){try{return JSON.parse(await Ur.readFile(OC(),"utf8"))||{}}catch{return{}}}async function DC(e){await Ur.mkdir(Fo(),{recursive:!0}),await Ur.writeFile(OC(),JSON.stringify(e),"utf8")}function u3(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||RC.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 d3({fetchImpl:e=fetch}={}){let t=await qR();if(!t)return{sent:0,disabled:!0};let n;try{n=(await Ur.readdir(Fo())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await PC(),o=0;for(let s of n){let a=xi.join(Fo(),s),i=r[s]||0,c;try{let p=await Ur.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(`
|
|
803
|
+
`).filter(Boolean),d=[];for(let p of u)try{d.push(JSON.parse(p))}catch{}for(let p=0;p<d.length;p+=IC){let f=d.slice(p,p+IC).map(h=>u3(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 DC(r)}return{sent:o,disabled:!1}}var Ak=null,Ek=!1;function BC(){Ak||(Ak=setInterval(async()=>{if(!Ek){Ek=!0;try{await d3()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{Ek=!1}}},i3),Ak.unref?.())}import Yf from"node:fs/promises";import HC from"node:path";import p3 from"node:os";var WC=()=>process.env.BOSTRAT_STATE_DIR||HC.join(process.env.HOME||p3.tmpdir(),".bostrat"),qC=()=>HC.join(WC(),"dictation-glossary.json"),f3=100,m3=500,h3=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Xf=null,_k=null,Ik=Promise.resolve();function LC(){let e={},t=Date.now();for(let n of h3)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function GC(){return Xf||(_k??=Yf.readFile(qC(),"utf8").then(e=>JSON.parse(e)?.terms??LC()).catch(()=>LC()).then(e=>Xf=e).finally(()=>_k=null),_k)}async function jC(){let e=JSON.stringify({terms:Xf},null,2),t=qC(),n=`${t}.${process.pid}.tmp`;await Yf.mkdir(WC(),{recursive:!0}),await Yf.writeFile(n,e),await Yf.rename(n,t)}function w3(){return Ik=Ik.then(jC,jC),Ik.catch(()=>{})}async function $k(){return Object.values(await GC()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var g3=e=>Math.max(1,Math.ceil(e.length/4));async function y3(){let e=[],t=0;for(let{term:n}of await $k()){let r=g3(n);if(!(t+r>m3)&&(e.push(n),t+=r,e.length>=f3))break}return e}async function b3(e,t){let n=await GC(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var FC=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Jf=e=>e.toLowerCase();function UC(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function KC(e,t){let n=FC(e),r=FC(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]=Jf(n[u])===Jf(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;)Jf(n[c])===Jf(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(UC(r[l])&&i.add(r[l]),l++);for(;l<s;l++)UC(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await b3(u,"learned");return await w3(),[...i]}var Ql=null;function k3(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 VC(){if(!Ql)return null;let e=await Rl();if(!e||e.apiKey.slice(-6)!==Ql.keyTail)return null;let{keyTail:t,...n}=Ql;return n}async function zC(e,t){let n=await Rl();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 y3())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=k3(o.status,i);c.persistent&&(Ql={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 Ql=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}be();var Rk={"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 x3(e){if(!e)return null;let t=String(e).trim();if(Rk[t])return t;let n=null;for(let r of Object.keys(Rk))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function v3(e,t={}){let n=Rk[x3(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 S3(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 eu=null;async function T3(){if(!eu)return null;let e=await Tf();if(!e||e.apiKey.slice(-6)!==eu.keyTail)return null;let{keyTail:t,...n}=eu;return n}async function A3(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=S3(t.status,o);s.persistent&&(eu={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}eu=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 E3={openai:A3};async function JC(){let e=await Tf();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await T3()}:{configured:!1}}async function YC(){let e=await Tf();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 E3[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function XC(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=v3(t,s),i=await N(e,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:t||"unknown",usd:a,tokens:{input:s.textIn+s.audioIn,output:s.textOut+s.audioOut,cacheRead:s.textInCached+s.audioInCached,cacheWrite:0},voice:s,seconds:Math.max(0,Math.round(Number(r)||0)),source:"voice-chat"}});return{usd:a,seq:i.seq}}an();CA(async()=>new Set((await ke()).map(e=>e.id)));var Bk=Re.dirname(I3(import.meta.url)),Pk=Number(process.env.PORT)||3362,Zf=cb(process.env),sN=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,vi=cM(process.env.BOSTRAT_ALLOW_IPS),Qf=lM(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Ff),Lk=dM(),aN=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",E=nn();E.disable("x-powered-by");E.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),Kl(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(Gl(e)||vi.allowed(vi.clientIp(e))||Qf.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});E.use(gM);E.use(mM({token:Lk,enforce:aN}));E.use("/api/mocks",nn.json({limit:"1mb"}));E.use("/api/mocks",nn.raw({type:"image/png",limit:"24mb"}));E.use("/api/mocks",nn.raw({type:["video/mp4","video/webm"],limit:"105mb"}));E.use("/api/runbooks",nn.json({limit:"1mb"}));E.use("/api/tracks/adopt",nn.json({limit:"1mb"}));E.use("/api/telemetry",nn.json({limit:"256kb"}));E.use("/api/hook",nn.json({limit:"2mb"}));E.use("/api/dictation",nn.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));E.use("/api/tracks/:id/upload",nn.raw({type:()=>!0,limit:"25mb"}));E.use(nn.json({limit:"64kb"}));var C3=(()=>{try{let e=JSON.parse(xt.readFileSync(Re.resolve(Bk,"..","..","client","dist","build.json"),"utf8")),t=typeof e?.sha=="string"?e.sha.trim():"";return!t||t==="unknown"?null:{sha:t,version:typeof e.version=="string"&&e.version.trim()?e.version.trim():null}}catch{return null}})();E.get("/api/health",async(e,t)=>{t.json({ok:!0,build:C3,tmuxSocket:zt,hostTmuxAlive:await Zr(),autoTriggerPct:sN,plane:Si?Si.status():null})});E.get("/api/sessions",async(e,t)=>{t.json({sessions:await kn()})});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 jn(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(!je(n))return t.status(400).json({error:"invalid session name"});if(!await Se(n))return t.status(404).json({error:"session not found"});try{await Lt(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(!je(n))return t.status(400).json({error:"invalid session name"});let r=await Se(n);if(!r)return t.status(404).json({error:"session not found"});let o=await nc(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(!je(n))return t.status(400).json({error:"invalid session name"});try{let r=await hT(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=Td(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 ZC=10;async function iN(){let e=await ke(),[t,n]=await Promise.all([M$(e),wR(e)]);return{tracks:e,views:await gg(),queuedOf:r=>Cs(r),jobsOf:r=>t.get(r)??[],prPendingOf:r=>n.get(r)??[]}}E.get("/api/idle-guard",async(e,t)=>{try{t.json(ib(await iN()))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var hn=js(),N3=RR({kind:hn}),nu=null,Ct=()=>nu&&["auto","prompt","off"].includes(nu)?nu:N3,Dk=null,P3=Il.has(hn),cN=async({to:e,from:t})=>!P3||$l(await li())?!1:(ou.arm({to:e,from:t,kind:hn}),!0);async function D3(e){let t=Ct();nu=e??null;let n=Ct();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){ou.cancel();return}Dk&&await cN(Dk)}}var lN=(()=>{try{return M3(import.meta.url)("../package.json").version||null}catch{return null}})(),ou=MR({isBusy:async()=>ib(await iN()).busy,onStuck:({to:e,from:t})=>or({current:t,latest:e,installKind:hn,policy:Ct(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?or({current:t,latest:e,installKind:hn,policy:Ct(),status:"updating",reason:"auto"}):or({current:t,latest:e,installKind:hn,policy:Ct(),status:"failed",error:n?.reason})).catch(()=>{})});E.get("/api/node/self-update",async(e,t)=>{t.json({installKind:hn,policy:Ct(),policySource:nu?"plane":"node",current:lN,status:await li(),armed:ou.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 yf({to:n,kind:hn,reason:"manual"});if(r.ok)return ou.cancel(),or({current:r.from,latest:n,installKind:hn,policy:Ct(),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 uN(){try{let e=await OR();e&&await or({current:e.from,latest:e.to,installKind:e.kind||hn,policy:Ct(),status:e.status,error:e.error,reason:e.reason}),await ky({current:lN,installKind:hn,policy:Ct()})}catch{}}uN();setInterval(uN,3e4).unref();E.get("/api/tracks",async(e,t)=>{let n=(await ke()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>ZC){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,ZC).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:yl,agentAvailability:await Jp(),tickets:await pO()})});E.get("/api/cost",async(e,t)=>{try{let n=await ke();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await Vb(n);if(r.cursorUsage=tg(),e.query.format==="text")return t.type("text/plain").send(LO(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 Jb(await ke(),{window:e.query.window,from:e.query.from,to:e.query.to});if(e.query.format==="text")return t.type("text/plain").send(WO(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 zO(await ke(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=tg(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=_c(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/usage/caps",(e,t)=>{t.json(_c())});E.post("/api/usage/caps",async(e,t)=>{try{t.json(await Dd(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 rM(await ke(),{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 e0(e.get(pr),{byToken:bl,legacy:()=>Hc(e.body?.session_id,e.body?.cwd)});if(n==="refused")return Ii("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return Ii("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&&Ii("mismatch",{track:r.track.id,box:r.box.name}),Ii(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 PO(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&$u(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 bl(e.get(pr));if(!n?.track||!n?.box)return t.status(204).end();t.send((await PE(n.track,n.box)).join(`
|
|
804
804
|
`)+`
|
|
805
|
-
`)}catch{t.status(204).end()}});E.post("/api/git/committed",async(e,t)=>{try{let n=await bl(e.get(pr));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?Re.resolve(n.box.cwd):null,s=typeof e.body?.cwd=="string"&&e.body.cwd?Re.resolve(e.body.cwd):null,a=o&&s&&(s===o||s.startsWith(o+Re.sep))?s:o;NE(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 me(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 DE(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 me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,event:await BE(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 zO(await ke(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.post("/api/telemetry",async(e,t)=>{try{if(!CC(vi.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await MC(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 fr(),r=new Set((await Promise.all(n.map(a=>Me(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await Li()).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 ra(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:bu(),calls:QA(),latency:rE(),webhook:tC()})});E.post("/api/github/event",nn.json({limit:"64kb"}),(e,t)=>{let n=QM(e.body,{onAccept:r=>bC(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)=>{E0(),t.json({ok:!0})});E.get("/api/gmail/search",async(e,t)=>{try{let n=await oM(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 sM(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});async function nu({repo:e=null,number:t=null,branch:n=null}){let r=e?await Me(e):null;if(r&&t!=null){let s=await ft(r,t),a=s&&await me(s.trackId);if(a)return a}if(!n)return null;let o=await ke();return rp(o,n,s=>op(s,{branch:n,repo:e,slug:r}))}async function dN({repo:e=null,number:t=null,branch:n=null}){let r=await nu({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!Ue(r))return r;if(!n)return null;let o=e?await Me(e).catch(()=>null):null,s=await ke();return rp(s,n,a=>!Ue(a)&&op(a,{branch:n,repo:e,slug:o}))}async function Dn(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await nu({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await Me(s).catch(()=>null):null);await N({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:e,number:t,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function N3(e){let t=(await ke()).filter(n=>!Ue(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await Me(n.path);if(r)for(let o of n.prs){if(await ft(r,o.number))continue;let s=await rp(t,o.headRefName,a=>op(a,{branch:o.headRefName,repo:n.path,slug:r}));s&&await Yt({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 uE();await N3(n.repos).catch(()=>{}),await Wf(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 g=o||"merge";if(!Cc.has(g))throw new Error(`invalid merge method: ${g}`);let x=Number(r);if(!Number.isInteger(x)||x<=0)throw new Error("invalid PR number");let S=await Me((await Tr(n)).path);if(!S)throw new Error("repo has no GitHub remote");let _=await nu({repo:n,number:x,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 Bp(_,"merge",{count:1,summary:`arming auto-merge on PR #${x}`}),a&&await Nc(n,x),await Yt({repo:S,number:x,trackId:_.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await $c(S,x,{merge:{method:g}}))throw new Error("couldn't record the auto-merge arm");return await ys(S,x,i===!0),await bs(S,x,c===!0&&i!==!0),ln(),await Dn("auto-armed",{number:x,url:d,title:u,branch:l,repo:n,track:_,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:x,auto:!0})}p=await nu({repo:n,number:r,branch:l}).catch(()=>null),p&&(f=await Bp(p,"merge",{count:1,summary:`merging PR #${r}`}));let h=await La(n,r,{method:o||"squash"});await Dn("merged",{number:h.number,url:d,title:u,branch:l,repo:n});let y=await Me(n).catch(()=>null),k=!1;if(y&&(i===!0&&await ys(y,h.number,!0).catch(()=>{}),c===!0&&i!==!0&&await bs(y,h.number,!0).catch(()=>{}),await _r(y,h.number).catch(()=>{}),await Bs(y,h.number).catch(()=>{}),k=await Er(y,h.number).catch(()=>!1),await wo(y,h.number,"merged").catch(()=>{})),i===!0&&!k){let g=await nu({repo:n,number:h.number,branch:l}).catch(()=>null);g&&!g.archivedAt&&await Ds(g.id).catch(()=>{})}t.json(h)}catch(h){w=h;let y=h?.message||String(h),k=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(y)?400:502;t.status(k).json({error:y})}finally{p&&f&&await ny(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 Me((await Tr(n)).path).catch(()=>null),l=c?await Rc(c,i).catch(()=>null):null;try{await Aw(n,i)}catch(u){if(!l)throw u}ln(),await Dn("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 Me(n).catch(()=>null),s=await hE(n,{method:r||"merge",onMerged:a=>{Dn("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&_r(o,a.number).catch(()=>{}).then(()=>Bs(o,a.number)).catch(()=>{}).then(()=>Er(o,a.number)).catch(()=>{}).then(()=>wo(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:mE()})});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(!Cc.has(u))throw new Error(`invalid merge method: ${u}`);if(!n)return t.status(400).json({error:"trackId required"});let d=await me(n);if(!d)return t.status(404).json({error:`Track ${n} isn't on this node`});i=d;let p=(await En()).filter(h=>h.trackId===d.id&&h.state!=="merged"&&h.state!=="closed").sort(Pc);if(!p.length)return t.status(409).json({error:"no open PRs on this Track"});c=await Bp(d,"merge",{count:p.length,summary:p.length===1?"merging 1 PR":`merging ${p.length} PRs`});for(let h of p)await ys(h.repo,h.number,o===!0).catch(()=>{}),await bs(h.repo,h.number,s===!0&&o!==!0).catch(()=>{});let f=[],w=[];for(let h of p){let y={repo:h.repo,number:h.number,ok:!0,action:"armed"};f.push(y);try{let k=await xs(h.repo);if(!k)throw new Error(`no local checkout for ${h.repo}`);let g=await Ba(k,h.number);w.push({r:y,l:h,repoPath:k,pr:g,plan:dE(g,{update:r!==!1})})}catch(k){y.ok=!1,y.action="failed",y.reason=k?.message||String(k)}}for(let{r:h,l:y,repoPath:k,pr:g,plan:x}of pE(w))try{if(x.action==="skip")h.action="skipped",h.reason=x.reason;else if(x.action==="merge")await La(k,y.number,{method:u}),await Dn("merged",{number:y.number,url:g.url,title:g.title,branch:g.headRefName,repo:k,track:d}),await _r(y.repo,y.number).catch(()=>{}),await Bs(y.repo,y.number).catch(()=>{}),await Er(y.repo,y.number).catch(()=>{}),await wo(y.repo,y.number,"merged").catch(()=>{}),h.action="merged";else{if(x.action==="update-arm"&&(await Nc(k,y.number).catch(()=>{}),h.action="updated-armed"),!await $c(y.repo,y.number,{merge:{method:u}}))throw new Error("couldn't record the auto-merge arm");await Dn("auto-armed",{number:y.number,url:g.url,title:g.title,branch:g.headRefName,repo:k,track:d,...h.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(S){h.ok=!1,h.action="failed",h.reason=S?.message||String(S)}if(ln(),!f.some(h=>h.ok)){if(o===!0)for(let h of p)await ys(h.repo,h.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let h of p)await bs(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 ny(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 me(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await En()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await Rc(a.repo,a.number).catch(()=>null),c=await xs(a.repo).catch(()=>null);c&&await Aw(c,a.number).catch(()=>{}),i&&(await Dn("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}))}ln(),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 me(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=Gt(i.workspace),d,p=Array.isArray(a)?a.filter(Boolean):[];if(p.length)d=p.map(h=>{let y=u.find(k=>k.name===h||k.repo===h||k.base===h||k.dir===h);if(!y)throw new Error(`invalid repo "${h}" \u2014 not tagged on this Track`);return y});else if(d=await _s(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 y=await _w(h.dir,{title:r,body:o,base:s}),k=h.repo||await Me(h.dir).catch(()=>null);k&&y.number&&await Yt({repo:k,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Dn("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:k??h.name}}),f.push({repo:h.name,slug:k??null,ok:!0,url:y.url,number:y.number})}catch(y){f.push({repo:h.name,slug:h.repo??null,ok:!1,error:y?.message||String(y)})}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 _w(i.cwd,{title:r,body:o,base:s}),l=await Me(i.cwd).catch(()=>null);l&&c.number&&await Yt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Dn("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 me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await _s(n),o=new Map(r.map(a=>[a.base,a])),s=Gt(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 Qp(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 ef(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 Dy(e.params.id,n,{skipPermissions:r!==!1});t.status(o.finishedAt?200:202).json({ok:!0,...Bk(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(Bk(xl(e.params.id))??{running:!1})});function Bk(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 Ba(n,r),s=await Me(n).catch(()=>null),a=await dN({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=Gt(a.workspace).find(k=>k.base===n||s&&k.repo===s)??null,c=a.workspace?.kind==="multi",l=await wE(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?Re.join("ci",i.name):"ci",d=Re.join(a.cwd,".bostrat",u),p=Re.join(".bostrat",u,`pr-${o.number}-fail.log`);await Et.promises.mkdir(d,{recursive:!0}),await Et.promises.writeFile(Re.join(a.cwd,p),l.map(k=>`\u2550\u2550\u2550\u2550 ${k.name}${k.link?` \xB7 ${k.link}`:""} \u2550\u2550\u2550\u2550
|
|
805
|
+
`)}catch{t.status(204).end()}});E.post("/api/git/committed",async(e,t)=>{try{let n=await bl(e.get(pr));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?Re.resolve(n.box.cwd):null,s=typeof e.body?.cwd=="string"&&e.body.cwd?Re.resolve(e.body.cwd):null,a=o&&s&&(s===o||s.startsWith(o+Re.sep))?s:o;DE(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 me(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 LE(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 me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,event:await jE(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 JO(await ke(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.post("/api/telemetry",async(e,t)=>{try{if(!NC(vi.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await CC(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 fr(),r=new Set((await Promise.all(n.map(a=>Me(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await Li()).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 ra(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:ku(),calls:tE(),latency:sE(),webhook:nC()})});E.post("/api/github/event",nn.json({limit:"64kb"}),(e,t)=>{let n=eC(e.body,{onAccept:r=>kC(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)=>{I0(),t.json({ok:!0})});E.get("/api/gmail/search",async(e,t)=>{try{let n=await sM(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 aM(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});async function ru({repo:e=null,number:t=null,branch:n=null}){let r=e?await Me(e):null;if(r&&t!=null){let s=await ft(r,t),a=s&&await me(s.trackId);if(a)return a}if(!n)return null;let o=await ke();return op(o,n,s=>sp(s,{branch:n,repo:e,slug:r}))}async function dN({repo:e=null,number:t=null,branch:n=null}){let r=await ru({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!Ue(r))return r;if(!n)return null;let o=e?await Me(e).catch(()=>null):null,s=await ke();return op(s,n,a=>!Ue(a)&&sp(a,{branch:n,repo:e,slug:o}))}async function Dn(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await ru({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await Me(s).catch(()=>null):null);await N({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:e,number:t,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function B3(e){let t=(await ke()).filter(n=>!Ue(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await Me(n.path);if(r)for(let o of n.prs){if(await ft(r,o.number))continue;let s=await op(t,o.headRefName,a=>sp(a,{branch:o.headRefName,repo:n.path,slug:r}));s&&await Yt({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 pE();await B3(n.repos).catch(()=>{}),await Wf(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 g=o||"merge";if(!Cc.has(g))throw new Error(`invalid merge method: ${g}`);let x=Number(r);if(!Number.isInteger(x)||x<=0)throw new Error("invalid PR number");let S=await Me((await Tr(n)).path);if(!S)throw new Error("repo has no GitHub remote");let _=await ru({repo:n,number:x,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 Lp(_,"merge",{count:1,summary:`arming auto-merge on PR #${x}`}),a&&await Nc(n,x),await Yt({repo:S,number:x,trackId:_.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await $c(S,x,{merge:{method:g}}))throw new Error("couldn't record the auto-merge arm");return await ys(S,x,i===!0),await bs(S,x,c===!0&&i!==!0),ln(),await Dn("auto-armed",{number:x,url:d,title:u,branch:l,repo:n,track:_,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:x,auto:!0})}p=await ru({repo:n,number:r,branch:l}).catch(()=>null),p&&(f=await Lp(p,"merge",{count:1,summary:`merging PR #${r}`}));let h=await La(n,r,{method:o||"squash"});await Dn("merged",{number:h.number,url:d,title:u,branch:l,repo:n});let y=await Me(n).catch(()=>null),k=!1;if(y&&(i===!0&&await ys(y,h.number,!0).catch(()=>{}),c===!0&&i!==!0&&await bs(y,h.number,!0).catch(()=>{}),await _r(y,h.number).catch(()=>{}),await Bs(y,h.number).catch(()=>{}),k=await Er(y,h.number).catch(()=>!1),await wo(y,h.number,"merged").catch(()=>{})),i===!0&&!k){let g=await ru({repo:n,number:h.number,branch:l}).catch(()=>null);g&&!g.archivedAt&&await Ds(g.id).catch(()=>{})}t.json(h)}catch(h){w=h;let y=h?.message||String(h),k=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(y)?400:502;t.status(k).json({error:y})}finally{p&&f&&await ny(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 Me((await Tr(n)).path).catch(()=>null),l=c?await Rc(c,i).catch(()=>null):null;try{await Aw(n,i)}catch(u){if(!l)throw u}ln(),await Dn("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 Me(n).catch(()=>null),s=await gE(n,{method:r||"merge",onMerged:a=>{Dn("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&_r(o,a.number).catch(()=>{}).then(()=>Bs(o,a.number)).catch(()=>{}).then(()=>Er(o,a.number)).catch(()=>{}).then(()=>wo(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:wE()})});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(!Cc.has(u))throw new Error(`invalid merge method: ${u}`);if(!n)return t.status(400).json({error:"trackId required"});let d=await me(n);if(!d)return t.status(404).json({error:`Track ${n} isn't on this node`});i=d;let p=(await En()).filter(h=>h.trackId===d.id&&h.state!=="merged"&&h.state!=="closed").sort(Pc);if(!p.length)return t.status(409).json({error:"no open PRs on this Track"});c=await Lp(d,"merge",{count:p.length,summary:p.length===1?"merging 1 PR":`merging ${p.length} PRs`});for(let h of p)await ys(h.repo,h.number,o===!0).catch(()=>{}),await bs(h.repo,h.number,s===!0&&o!==!0).catch(()=>{});let f=[],w=[];for(let h of p){let y={repo:h.repo,number:h.number,ok:!0,action:"armed"};f.push(y);try{let k=await xs(h.repo);if(!k)throw new Error(`no local checkout for ${h.repo}`);let g=await Ba(k,h.number);w.push({r:y,l:h,repoPath:k,pr:g,plan:fE(g,{update:r!==!1})})}catch(k){y.ok=!1,y.action="failed",y.reason=k?.message||String(k)}}for(let{r:h,l:y,repoPath:k,pr:g,plan:x}of mE(w))try{if(x.action==="skip")h.action="skipped",h.reason=x.reason;else if(x.action==="merge")await La(k,y.number,{method:u}),await Dn("merged",{number:y.number,url:g.url,title:g.title,branch:g.headRefName,repo:k,track:d}),await _r(y.repo,y.number).catch(()=>{}),await Bs(y.repo,y.number).catch(()=>{}),await Er(y.repo,y.number).catch(()=>{}),await wo(y.repo,y.number,"merged").catch(()=>{}),h.action="merged";else{if(x.action==="update-arm"&&(await Nc(k,y.number).catch(()=>{}),h.action="updated-armed"),!await $c(y.repo,y.number,{merge:{method:u}}))throw new Error("couldn't record the auto-merge arm");await Dn("auto-armed",{number:y.number,url:g.url,title:g.title,branch:g.headRefName,repo:k,track:d,...h.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(S){h.ok=!1,h.action="failed",h.reason=S?.message||String(S)}if(ln(),!f.some(h=>h.ok)){if(o===!0)for(let h of p)await ys(h.repo,h.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let h of p)await bs(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 ny(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 me(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await En()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await Rc(a.repo,a.number).catch(()=>null),c=await xs(a.repo).catch(()=>null);c&&await Aw(c,a.number).catch(()=>{}),i&&(await Dn("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}))}ln(),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 me(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=Gt(i.workspace),d,p=Array.isArray(a)?a.filter(Boolean):[];if(p.length)d=p.map(h=>{let y=u.find(k=>k.name===h||k.repo===h||k.base===h||k.dir===h);if(!y)throw new Error(`invalid repo "${h}" \u2014 not tagged on this Track`);return y});else if(d=await _s(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 y=await _w(h.dir,{title:r,body:o,base:s}),k=h.repo||await Me(h.dir).catch(()=>null);k&&y.number&&await Yt({repo:k,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Dn("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:k??h.name}}),f.push({repo:h.name,slug:k??null,ok:!0,url:y.url,number:y.number})}catch(y){f.push({repo:h.name,slug:h.repo??null,ok:!1,error:y?.message||String(y)})}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 _w(i.cwd,{title:r,body:o,base:s}),l=await Me(i.cwd).catch(()=>null);l&&c.number&&await Yt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Dn("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 me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await _s(n),o=new Map(r.map(a=>[a.base,a])),s=Gt(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 ef(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 tf(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 By(e.params.id,n,{skipPermissions:r!==!1});t.status(o.finishedAt?200:202).json({ok:!0,...jk(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(jk(xl(e.params.id))??{running:!1})});function jk(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 Ba(n,r),s=await Me(n).catch(()=>null),a=await dN({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=Gt(a.workspace).find(k=>k.base===n||s&&k.repo===s)??null,c=a.workspace?.kind==="multi",l=await yE(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?Re.join("ci",i.name):"ci",d=Re.join(a.cwd,".bostrat",u),p=Re.join(".bostrat",u,`pr-${o.number}-fail.log`);await xt.promises.mkdir(d,{recursive:!0}),await xt.promises.writeFile(Re.join(a.cwd,p),l.map(k=>`\u2550\u2550\u2550\u2550 ${k.name}${k.link?` \xB7 ${k.link}`:""} \u2550\u2550\u2550\u2550
|
|
806
806
|
${k.log??"(no fetchable log \u2014 see link)"}
|
|
807
807
|
`).join(`
|
|
808
|
-
`));let f=l.map(k=>k.name).join(", "),w=c&&i?` in the ${i.name}/ checkout`:"",h=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"}),y=s;y&&(await Yt({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await CA(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Dn("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(k=>k.name)}}),t.json({ok:!0,leadBox:h.leadBox,queued:!!h.queued,checks:l.map(k=>k.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 Ba(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await Me(n).catch(()=>null),a=await dN({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=Gt(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 Yt({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await NA(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Dn("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=ho.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 vo(`pr:${e.params.id}:${n}`,async()=>{let a=await me(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 xk(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,Ca(o,{branch:c.payload.branch,base:c.payload.base,toPr:c.payload.toPr||null,repos:c.payload.repos||null,offeredRebase:Array.isArray(c.payload.offered)&&c.payload.offered.includes("rebase")}),{kind:o.includes("pr")?"pr-go":"push-go"}));let u=o?o.includes("pr")?"pr":o.includes("push")?"push":o.includes("commit")?"commit":"rebase":"dismiss";await N({id:a.id,cwd:a.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:c.boxId,payload:{ts:Date.now(),offerId:n,action:u,actions:o,...s?{notify:!0}:{}}}),s&&$y(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 me(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await vk(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 ou=e=>t=>({...t,url:yM(t,e.headers)});E.get("/api/views",async(e,t)=>{try{let n=await gg({trackId:e.query.track||void 0});t.json({views:n.map(ou(e)),maxLive:To})}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/views/:id",async(e,t)=>{let n=await tI(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:ou(e)(n),session:Wa(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 me(n):null;if(!i&&s&&(i=(await ke()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Re.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!nk(e.headers))return t.status(412).json({error:JR});let c=await bg({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:ou(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:ou(e)(await nI(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:ou(e)(await rI(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 oI(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var P3=[["linear",Fs,kb],["jira",Sf,yb]],Rk=new Map;E.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await gb(),r={};for(let[o,s,a]of P3){let i=await s();if(!i){Rk.delete(o),r[o]={configured:!1};continue}let c=Rk.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},Rk.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 gb()})}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 HR(o),i=n?await me(n):null;!i&&r&&(i=(await ke()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Re.sep)))||null),i&&await N({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),t.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);t.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});var Js=async(e,t)=>{let n=e?await me(e):null;return!n&&t&&(n=(await ke()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Re.sep)))||null),n};E.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await db()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});E.get("/api/roadmap",async(e,t)=>{try{t.json(await pb())}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 Js(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 ui(o);await bf(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 Js(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await bf(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var pN=async(e,t,n)=>{if(e)return{ref:e,track:await Js(t,n)};let r=await Js(t,n),o=r?await mb(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 pN(o,n,r),l=await NR({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 pN(e.query.circuit,e.query.track,e.query.cwd);t.json(await fb(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 Js(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=await BR(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 Js(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await LR(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 Js(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await mb(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 PR(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 me(n):null;if(!s&&r&&(s=(await ke()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+Re.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 mi(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 me(r):null;if(!o&&n&&(o=(await ke()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+Re.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 pO(),ticket:await ir(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 ir(e.params.id)})});E.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await mi(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 Pb(e.params.id)})});var em=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 wO(e.params.id))}catch(n){let r=n?.message||String(n);t.status(em(r)).json({error:r})}});E.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await Db(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(em(r)).json({error:r})}});E.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await gO(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(em(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 me(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await yO(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(em(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 zy(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 me(n):null;if(!s&&r&&(s=(await ke()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Re.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 kO(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 bO()})}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 me(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await xO(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 nl({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 su={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=$n(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=el(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,su,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=$n(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=el(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,su,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=$n(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(dn(n,r.v).html,su,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&&Ip(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 me(a):null;if(!d&&c&&(d=(await ke()).find(h=>h.cwd&&(c===h.cwd||c.startsWith(h.cwd+Re.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 xI({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&&Ip(n)?n:null;try{let o=await vI(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 AI(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 WI(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await EI(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 LI({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 jI(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 me(n):null;if(!a&&s&&(a=(await ke()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Re.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 FI({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 UI(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 HI(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 ke(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+Re.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await XI({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 Cg(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 Cg(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=JI(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 ZI(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(Dr(n,r.v),su,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});E.get("/api/loops",async(e,t)=>{t.json(await Ac())});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 ke()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+Re.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await Ac();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 Yh({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 me(n):null;if(!s&&r&&(s=(await ke()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Re.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 _M(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var Ti=async(e,t)=>{let n=e?await me(e):null;return!n&&t&&(n=(await ke()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Re.sep)))||null),n};E.post("/api/followups/offer",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await Ti(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 h_(s,o,{ship:{changedRepos:_s}})})}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 Ti(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 g_(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 Ti(null,n).catch(()=>null);t.json({ok:!0,trackId:r?.id??null,triggers:Vw,...await x_(r)})});var ZC=new Set(["reconcile","fixing-ci","rebase","merge"]);E.get("/api/status",async(e,t)=>{let n=await Ti(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 Ti(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&&!ZC.has(l))throw new Error(`phase must be one of ${[...ZC].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=I3();await N(c,{type:"track.phase.changed",actor:"agent",source:"cli",boxId:c.leadBox,provider:c.boxes?.find(p=>p.name===c.leadBox)?.agent??null,payload:{ts:Date.now(),phase:l||null,phaseId:d,summary:u,scope:a&&typeof a=="object"?a:null,...l?{}:{reason:i||"completed"}}}),t.json({ok:!0,trackId:c.id,phase:l||null,phaseId:d})}catch(n){t.status(400).json({error:n?.message||String(n)})}});E.post("/api/tracks/:id/followup-offer",async(e,t)=>{let{offerId:n,followupId:r,armId:o,promoted:s,all:a}=e.body||{};try{let i=await me(e.params.id);if(!i)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await b_(i,{offerId:n,followupId:r||null,armId:o||null,promoted:s||null,all:a===!0},{sayToLead:qt,unarchiveTrack:rf})})}catch(i){let c=i?.message||String(i),l=i?.code==="removed"?410:/unknown offer/i.test(c)?404:/already answered/i.test(c)?409:/required|not on this card|nothing to run together/i.test(c)?400:/unreachable/i.test(c)?503:502;t.status(l).json({error:c})}});E.post("/api/tracks/:id/followup-arm-cancel",async(e,t)=>{try{let n=await me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await w_(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 me(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await y_(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})}});Ze.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"||me(e.trackId).then(async s=>{if(s&&(n&&await Fc(s,n,o),t==="opened"||t==="pushed")){let a=k_(await K(s).catch(()=>[]));a&&await Fc(s,"ship.done",{...o,shipOfferId:a})}}).catch(()=>{})});var fN=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await me(n):null;if(!s&&r&&(s=(await ke()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Re.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"}),null;let a=await Ac();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=RM(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 fN(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await OM(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 fN(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await MM(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 Ti(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i;try{i=await pb()}catch(c){return t.status(503).json({error:c?.message||String(c)})}try{let c=s==="circuit"?ck:Kl;t.json({ok:!0,trackId:a.id,proposal:await WM(a,o,i,{allow:c})})}catch(c){t.status(400).json({error:c?.message||String(c)})}});var Lk=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 me(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await qM(s,n,r,{results:o||[]})})}catch(s){let a=s?.message||String(s);t.status(Lk(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 me(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await NM(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(Lk(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 me(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await PM(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(Lk(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 me(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await IM(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 me(n):null;if(!a&&s&&(a=(await ke()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Re.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 QI({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 e$(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 t$(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 n$(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=JM(e.query.since||null);t.json({since:n,repos:uk(),...await YM(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var mN=async({trackId:e,cwd:t})=>{let n=e?await me(e):null;return!n&&t&&(n=(await ke()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+Re.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 mN({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 XM(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 mN({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 ZM(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var D3=120,Ok=400,B3=/^[a-z0-9][a-z0-9_-]{0,23}$/,Si=null,QC=null;E.post("/api/notify",async(e,t)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a,channel:i}=e.body||{};try{let c=typeof r=="string"?r.trim():"";if(!c)return t.status(400).json({error:"body (the message) required"});let l=i==null||i===""?null:String(i).trim().toLowerCase();if(l!==null&&!B3.test(l))return t.status(400).json({error:`channel "${String(i).slice(0,40)}" is not a valid name \u2014 lowercase letters, digits, - or _, up to 24 chars`});if(c.length>Ok)return t.status(400).json({error:`message is ${c.length} chars \u2014 over the ${Ok} cap; a notification is a nudge, put the detail in chat`});let u=rl(`${n||""}
|
|
809
|
-
${c}`);if(u.length)return t.status(400).json({error:`notification appears to contain a secret VALUE (${u.join(", ")}) \u2014 notifications ship off-box; never put a credential in one`});let d=a?await me(a):null;if(!d&&s&&(d=(await ke()).find(y=>y.cwd&&(s===y.cwd||s.startsWith(y.cwd+Re.sep)))||null),!d)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let p=(d.boxes||[]).find(h=>h.name===d.leadBox),f=(h,y)=>String(h).replace(/\s+/g," ").trim().slice(0,y),w={title:n?f(n,
|
|
808
|
+
`));let f=l.map(k=>k.name).join(", "),w=c&&i?` in the ${i.name}/ checkout`:"",h=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"}),y=s;y&&(await Yt({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await PA(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Dn("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(k=>k.name)}}),t.json({ok:!0,leadBox:h.leadBox,queued:!!h.queued,checks:l.map(k=>k.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 Ba(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await Me(n).catch(()=>null),a=await dN({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=Gt(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 Yt({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await DA(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Dn("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=ho.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 vo(`pr:${e.params.id}:${n}`,async()=>{let a=await me(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 vk(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,Ca(o,{branch:c.payload.branch,base:c.payload.base,toPr:c.payload.toPr||null,repos:c.payload.repos||null,offeredRebase:Array.isArray(c.payload.offered)&&c.payload.offered.includes("rebase")}),{kind:o.includes("pr")?"pr-go":"push-go"}));let u=o?o.includes("pr")?"pr":o.includes("push")?"push":o.includes("commit")?"commit":"rebase":"dismiss";await N({id:a.id,cwd:a.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:c.boxId,payload:{ts:Date.now(),offerId:n,action:u,actions:o,...s?{notify:!0}:{}}}),s&&Ry(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 me(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await Sk(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 su=e=>t=>({...t,url:bM(t,e.headers)});E.get("/api/views",async(e,t)=>{try{let n=await gg({trackId:e.query.track||void 0});t.json({views:n.map(su(e)),maxLive:To})}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/views/:id",async(e,t)=>{let n=await rI(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:su(e)(n),session:Wa(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 me(n):null;if(!i&&s&&(i=(await ke()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Re.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!rk(e.headers))return t.status(412).json({error:YR});let c=await bg({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:su(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:su(e)(await oI(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:su(e)(await sI(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 aI(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var L3=[["linear",Fs,xb],["jira",Sf,bb]],Ok=new Map;E.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await yb(),r={};for(let[o,s,a]of L3){let i=await s();if(!i){Ok.delete(o),r[o]={configured:!1};continue}let c=Ok.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},Ok.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 yb()})}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 WR(o),i=n?await me(n):null;!i&&r&&(i=(await ke()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Re.sep)))||null),i&&await N({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),t.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);t.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});var Js=async(e,t)=>{let n=e?await me(e):null;return!n&&t&&(n=(await ke()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Re.sep)))||null),n};E.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await pb()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});E.get("/api/roadmap",async(e,t)=>{try{t.json(await fb())}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 Js(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 ui(o);await bf(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 Js(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await bf(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var pN=async(e,t,n)=>{if(e)return{ref:e,track:await Js(t,n)};let r=await Js(t,n),o=r?await hb(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 pN(o,n,r),l=await PR({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 pN(e.query.circuit,e.query.track,e.query.cwd);t.json(await mb(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 Js(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=await LR(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 Js(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await jR(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 Js(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await hb(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 DR(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 me(n):null;if(!s&&r&&(s=(await ke()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+Re.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 mi(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 me(r):null;if(!o&&n&&(o=(await ke()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+Re.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 fO(),ticket:await ir(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 ir(e.params.id)})});E.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await mi(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 Db(e.params.id)})});var em=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 gO(e.params.id))}catch(n){let r=n?.message||String(n);t.status(em(r)).json({error:r})}});E.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await Bb(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(em(r)).json({error:r})}});E.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await yO(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(em(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 me(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await bO(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(em(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 Jy(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 me(n):null;if(!s&&r&&(s=(await ke()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Re.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 xO(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 kO()})}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 me(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await vO(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 nl({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 au={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=$n(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=el(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,au,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=$n(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=el(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,au,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=$n(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(dn(n,r.v).html,au,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&&$p(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 me(a):null;if(!d&&c&&(d=(await ke()).find(h=>h.cwd&&(c===h.cwd||c.startsWith(h.cwd+Re.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 SI({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&&$p(n)?n:null;try{let o=await TI(e.params.id,{html:n?null:(e.body||{}).html,image:r,video:n&&!r?n:null,width:n?e.query.width:(e.body||{}).width});t.json({ok:!0,mock:o})}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required|invalid|must be/i.test(s)?400:500;t.status(a).json({error:s})}});E.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await _I(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await qt(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),t.json({ok:!0,mock:n,siblings:r})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/nothing to pick/i.test(r)?409:500;t.status(o).json({error:r})}});E.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await GI(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await II(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 FI({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 UI(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 me(n):null;if(!a&&s&&(a=(await ke()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Re.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 HI({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 WI(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 qI(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 ke(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+Re.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await QI({trackId:n,status:e.query.status||void 0});t.json({runbooks:r,...o?{fleetDegraded:!0}:{}})}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/runbooks/:id",async(e,t)=>{let n=await Cg(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 Cg(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=XI(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(Dr(n,r.v),au,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});E.get("/api/loops",async(e,t)=>{t.json(await Ac())});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 ke()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+Re.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await Ac();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 Yh({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 me(n):null;if(!s&&r&&(s=(await ke()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Re.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 IM(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var Ti=async(e,t)=>{let n=e?await me(e):null;return!n&&t&&(n=(await ke()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Re.sep)))||null),n};E.post("/api/followups/offer",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await Ti(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 g_(s,o,{ship:{changedRepos:_s}})})}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 Ti(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 b_(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 Ti(null,n).catch(()=>null);t.json({ok:!0,trackId:r?.id??null,triggers:Vw,...await S_(r)})});var QC=new Set(["reconcile","fixing-ci","rebase","merge"]);E.get("/api/status",async(e,t)=>{let n=await Ti(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 Ti(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&&!QC.has(l))throw new Error(`phase must be one of ${[...QC].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=R3();await N(c,{type:"track.phase.changed",actor:"agent",source:"cli",boxId:c.leadBox,provider:c.boxes?.find(p=>p.name===c.leadBox)?.agent??null,payload:{ts:Date.now(),phase:l||null,phaseId:d,summary:u,scope:a&&typeof a=="object"?a:null,...l?{}:{reason:i||"completed"}}}),t.json({ok:!0,trackId:c.id,phase:l||null,phaseId:d})}catch(n){t.status(400).json({error:n?.message||String(n)})}});E.post("/api/tracks/:id/followup-offer",async(e,t)=>{let{offerId:n,followupId:r,armId:o,promoted:s,all:a}=e.body||{};try{let i=await me(e.params.id);if(!i)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await x_(i,{offerId:n,followupId:r||null,armId:o||null,promoted:s||null,all:a===!0},{sayToLead:qt,unarchiveTrack:vl})})}catch(i){let c=i?.message||String(i),l=i?.code==="removed"?410:/unknown offer/i.test(c)?404:/already answered/i.test(c)?409:/required|not on this card|nothing to run together/i.test(c)?400:/unreachable/i.test(c)?503:502;t.status(l).json({error:c})}});E.post("/api/tracks/:id/followup-arm-cancel",async(e,t)=>{try{let n=await me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await y_(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 me(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await k_(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})}});Ze.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"||me(e.trackId).then(async s=>{if(s&&(n&&await Fc(s,n,o),t==="opened"||t==="pushed")){let a=v_(await K(s).catch(()=>[]));a&&await Fc(s,"ship.done",{...o,shipOfferId:a})}}).catch(()=>{})});var fN=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await me(n):null;if(!s&&r&&(s=(await ke()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Re.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"}),null;let a=await Ac();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=OM(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 fN(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await MM(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 fN(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await CM(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 Ti(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i;try{i=await fb()}catch(c){return t.status(503).json({error:c?.message||String(c)})}try{let c=s==="circuit"?lk:Vl;t.json({ok:!0,trackId:a.id,proposal:await qM(a,o,i,{allow:c})})}catch(c){t.status(400).json({error:c?.message||String(c)})}});var Fk=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 me(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await GM(s,n,r,{results:o||[]})})}catch(s){let a=s?.message||String(s);t.status(Fk(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 me(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await PM(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(Fk(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 me(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await DM(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(Fk(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 me(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await $M(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 me(n):null;if(!a&&s&&(a=(await ke()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Re.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,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 n$(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 r$(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 o$(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=YM(e.query.since||null);t.json({since:n,repos:dk(),...await XM(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var mN=async({trackId:e,cwd:t})=>{let n=e?await me(e):null;return!n&&t&&(n=(await ke()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+Re.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 mN({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 ZM(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 mN({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 QM(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var j3=120,Mk=400,F3=/^[a-z0-9][a-z0-9_-]{0,23}$/,Si=null,eN=null;E.post("/api/notify",async(e,t)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a,channel:i}=e.body||{};try{let c=typeof r=="string"?r.trim():"";if(!c)return t.status(400).json({error:"body (the message) required"});let l=i==null||i===""?null:String(i).trim().toLowerCase();if(l!==null&&!F3.test(l))return t.status(400).json({error:`channel "${String(i).slice(0,40)}" is not a valid name \u2014 lowercase letters, digits, - or _, up to 24 chars`});if(c.length>Mk)return t.status(400).json({error:`message is ${c.length} chars \u2014 over the ${Mk} cap; a notification is a nudge, put the detail in chat`});let u=rl(`${n||""}
|
|
809
|
+
${c}`);if(u.length)return t.status(400).json({error:`notification appears to contain a secret VALUE (${u.join(", ")}) \u2014 notifications ship off-box; never put a credential in one`});let d=a?await me(a):null;if(!d&&s&&(d=(await ke()).find(y=>y.cwd&&(s===y.cwd||s.startsWith(y.cwd+Re.sep)))||null),!d)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let p=(d.boxes||[]).find(h=>h.name===d.leadBox),f=(h,y)=>String(h).replace(/\s+/g," ").trim().slice(0,y),w={title:n?f(n,j3):d.name,body:f(c,Mk),urgency:o==="high"?"high":"normal",...l?{channel:l}:{},trackName:d.name,ts:Date.now()};await N(d,{type:"notify.raised",actor:"agent",boxId:d.leadBox??null,provider:p?.agent||null,payload:w}),t.json({ok:!0,notification:w,delivered:Si?Si.status().connected:null})}catch(c){t.status(500).json({error:c?.message||String(c)})}});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 zC(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 Rl(),terms:await $k(),problem:await VC()})});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 KC(n,r)})});E.get("/api/tracks/:id/voice-chat",async(e,t)=>{if(!await me(e.params.id))return t.status(404).json({error:"track not found"});t.json(await JC())});E.post("/api/tracks/:id/voice-chat/session",async(e,t)=>{try{let n=await me(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 YC())}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 me(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 XC(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 nf({name:n,cwd:r,repo:o,repos:s,agent:a,model:i,skipPermissions:c!==!1}),p=null;if(l)try{await mi(d,l,{source:"create"})}catch(w){p=w?.message||String(w)}let f=null;if(u)try{await bf(d,await ui(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 me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({track:n,handoff:hN(ii(e.params.id)),reshape:jk(xl(e.params.id)),subtasks:Ls(e.params.id).map(iu),pendingOffers:await vy(n)})});E.delete("/api/tracks/:id",async(e,t)=>{try{let n=await Hy(e.params.id,{cascadeMocks:e.query.mocks!=="0",cascadeRunbooks:e.query.runbooks==="1"});await Db(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 Ds(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 Ky(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 vl(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 Yp(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 me(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}
|
|
810
810
|
data: ${JSON.stringify(i)}
|
|
811
811
|
|
|
812
|
-
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await K(n))i.seq>o&&r(i);let s=
|
|
812
|
+
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await K(n))i.seq>o&&r(i);let s=L0(n.id,r),a=setInterval(()=>t.write(`: hb
|
|
813
813
|
|
|
814
|
-
`),25e3);e.on("close",()=>{clearInterval(a),s()})});E.get("/api/tracks/:id/search",async(e,t)=>{let n=await me(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=yc(await K(n)),a=
|
|
815
|
-
`);E.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await qt(e.params.id,
|
|
814
|
+
`),25e3);e.on("close",()=>{clearInterval(a),s()})});E.get("/api/tracks/:id/search",async(e,t)=>{let n=await me(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=yc(await K(n)),a=id(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 tx(),harvest:X0()})});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 nx(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 U3=["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(`
|
|
815
|
+
`);E.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await qt(e.params.id,U3)})}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 of(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 me(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 u$(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 me(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=Re.basename(String(e.params.file||""));if(!r||!Dg(Re.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=Pg(n.cwd),s,a;try{s=await xt.promises.realpath(o),a=await xt.promises.realpath(Re.join(o,r))}catch{return t.status(404).json({error:"upload not found"})}if(!a.startsWith(s+Re.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 H3=/^[0-9a-fA-F-]{8,64}$/;E.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(e,t)=>{try{let n=await me(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(!H3.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=xn(a.cwd,a.sessionId);if(!i)return t.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=_3.createInterface({input:xt.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?nh(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 Qp(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 _y(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 Xp(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|already answered|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 Fy({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 Ay(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 Cy(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 me(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 Bf(r,{fresh:o}))});E.post("/api/tracks/:id/model",async(e,t)=>{let{model:n}=e.body||{};try{t.json(await Ny(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 ob(e.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});t.json({ok:!0,subtask:iu(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:Ls(e.params.id).map(iu)})});E.get("/api/tracks/:id/subtask/:sid/messages",async(e,t)=>{let n=await Sy(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:iu(df(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:iu(sb(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});function iu(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 sf(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 Oy(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(hN(ii(e.params.id))??{running:!1})});E.post("/api/tracks/:id/transfer-out",async(e,t)=>{try{let n=await eb(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=Qy(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 cf(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 nb({...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 hN(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:du(),paths:c0,trace:await i0({limit:n})})});var Ck=Re.resolve(Bk,"..","..","client","dist");xt.existsSync(Ck)&&(E.use(nn.static(Ck)),E.get(/^(?!\/api\/).*/,(e,t)=>t.sendFile(Re.join(Ck,"index.html"),au)));var wN=oN.createServer(E),W3=new O3({noServer:!0}),Uk=(e,t,n)=>{if(!(Gl(e)||vi.allowed(vi.clientIp(e))||Qf.allows(e))){t.write(`HTTP/1.1 403 Forbidden\r
|
|
816
816
|
\r
|
|
817
|
-
`),t.destroy();return}if(
|
|
817
|
+
`),t.destroy();return}if(yM(e,t,n))return;if(!hM(e,Lk,{enforce:aN})){t.write(`HTTP/1.1 401 Unauthorized\r
|
|
818
818
|
\r
|
|
819
|
-
`),t.destroy();return}let r=
|
|
819
|
+
`),t.destroy();return}let r=Dx(e.url);if(!r.ok){t.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
820
820
|
\r
|
|
821
821
|
`),t.destroy();return}let o=r.sessionName;if(!je(o)){t.write(`HTTP/1.1 400 Bad Request\r
|
|
822
822
|
\r
|
|
823
|
-
`),t.destroy();return}let{cols:s,rows:a}=r;
|
|
823
|
+
`),t.destroy();return}let{cols:s,rows:a}=r;W3.handleUpgrade(e,t,n,i=>Cx(i,o,{cols:s,rows:a,revive:()=>Gy(o)}))};wN.on("upgrade",Uk);var gN=process.env.BOSTRAT_CERT_DIR||Re.join(process.env.HOME||tu.tmpdir(),".bostrat","certs"),tN=Number(process.env.BOSTRAT_TLS_PORT)||3443,nN=Re.join(gN,"bostrat.globopi.key"),rN=Re.join(gN,"bostrat.globopi.crt");if(xt.existsSync(nN)&&xt.existsSync(rN)){let e=$3.createServer({key:xt.readFileSync(nN),cert:xt.readFileSync(rN)},E);e.on("upgrade",Uk),e.on("error",t=>{console.error(`[bostrat-agent] https listener failed (${t?.code||t?.message}) \u2014 continuing HTTP-only`)}),e.listen(tN,Zf,()=>{console.log(`[bostrat-agent] https listening on ${Zf||"0.0.0.0"}:${tN}`)})}var Nk=String(process.env.BOSTRAT_VIEW_BIND||"").trim();if(Nk){Qf.active||console.warn(`[bostrat-agent] \u26A0 BOSTRAT_VIEW_BIND=${Nk} is set but BOSTRAT_VIEW_FRONT_CIDRS is not \u2014 no peer can pass the front gate on that listener, so LAN views will 403. Set both.`);let e=kM({app:E,onUpgrade:Uk,extractViewSlug:Kl,log:console}),t=oN.createServer(e.request);t.on("upgrade",e.upgrade),xM(t,{addr:Nk,port:Pk,log:console})}wN.listen(Pk,Zf,()=>{console.log(`[bostrat-agent] listening on ${Zf||"0.0.0.0"}:${Pk}`);let e=gR(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: ${zt}`),console.log(`[bostrat-agent] auto-trigger default: ${sN}%`),console.log(`[bostrat-agent] ip allowlist: ${vi.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(Qf.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),Xy().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),Ly().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)),rb().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Vy().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(()=>zy()).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)),jy().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)),X3(),C$({listTracks:ke}),E$({trackOf:xy}),K3(),V3(),Y3(),f0({holdWhile:async()=>$l(await li())}),g0()&&console.log("[cli-currency] codex release sweep armed"),QT({listTracks:ke,getTrack:me}).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)),w$()&&b$({listTracks:ke}),gx().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)),bC(),SC(),SO({resolveTrack:me}),CO({listTracks:ke,createTrack:nf,archiveTrack:Ds,listRepos:fr,resolveTrack:me,sayToLead:qt,answerPrompt:Xp,startLeadHandoff:sf,interruptLead:Qp,listSubtaskJobs:Ls,cancelSubtask:df,readEvents:K,appendEvent:N,divertShip:vk,answerPrExists:Sk,knownAgents:yl}),q3(),Qi().then(t=>{t||console.warn(`[boot] no host tmux server on ${zt} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),BC(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(Si=_R({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,apiToken:Lk,nodeId:process.env.BOSTRAT_NODE_ID||tu.hostname(),name:process.env.BOSTRAT_NODE_NAME||tu.hostname(),listTracks:ke,detectAgents:Jp,sampleMetrics:XO,deliverySample:()=>W_(Ey()),onHashKey:L_,onViewBase:QR,onTracksTransferred:tb,onDegraded:({since:t,unacked:n,oldestAgeMs:r,mode:o})=>{let s=Math.round(r/6e4),a=`${process.env.BOSTRAT_NODE_NAME||tu.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(()=>{}),eN=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(()=>{}),eN=null},onConnected:()=>{yC()},onRemoved:({reason:t,via:n})=>{let r=`${process.env.BOSTRAT_NODE_NAME||tu.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{Dk={latest:t.latest,current:t.current},await or({...t,policy:Ct()}),Ct()==="auto"&&await cN({to:t.latest,from:t.current})}catch(n){console.error(`[plane-link] update notice failed: ${n?.message||n}`)}},onUpdatePolicy:t=>D3(t).catch(()=>{}),onHelloRefused:async({current:t,min:n,latest:r,installKind:o,fix:s})=>{try{let a=r||n;if(await or({current:t,latest:a,min:n,installKind:o,required:!0,policy:Ct(),status:"available",reason:"refused"}),Ct()!=="off"&&a){let i=await yf({to:a,kind:hn,reason:"hard-floor"});i.ok?(ou.cancel(),await or({current:t,latest:a,min:n,installKind:o,required:!0,policy:Ct(),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:Ct}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),ca().catch(()=>{}),i$().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),TO().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),OI().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),zI().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),KA().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{Si?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function q3(){G3(),Tp().catch(()=>{}),setInterval(()=>Tp().catch(()=>{}),1e4)}function G3(){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=Re.resolve(Bk,"..","bin",e),n=Re.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!xt.existsSync(t))continue;xt.mkdirSync(n,{recursive:!0});let r=Re.join(n,e);(xt.existsSync(r)?xt.readlinkSync(r):null)!==t&&(xt.rmSync(r,{force:!0}),xt.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 K3(){let e=!1,t=async()=>{if(!e){e=!0;try{let n=await ke(),r=await eM({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 V3(){let e=null,t=async()=>{try{let n=Ea(),r=0,o=[],s=null,a=null;try{let u=await ke(),d=await Vb(u);r=d.fleet.unpricedTurns||0,o=d.fleet.unpricedModels||[],s=await ZO(u,{days:7}),a=(await Jb(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(`
|
|
824
824
|
`);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(`
|
|
825
|
-
`).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
|
|
825
|
+
`).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 z3=Number(process.env.BOSTRAT_MEMORY_RECONCILE_MS)||5*6e4,J3=Number(process.env.BOSTRAT_MEMORY_RECONCILE_FIRST_MS)||5*6e4;function Y3(){let e=async()=>{try{let n=await ex();n.ran&&console.log(`[memory] one-time full backfill sweep: ${n.harvested} fact(s) harvested`);let o=(await ke()).filter(a=>!Ue(a)&&a.kind!=="setup").flatMap(a=>[a.cwd,...(a.boxes||[]).map(i=>i.cwd)]).filter(Boolean),s=await Q0([...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,z3).unref()},J3).unref()}function X3(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{let t=await ke();for(let n of t)Ue(n)||!n.boxes?.some(r=>r.state==="live")||(await Yp(n.id).catch(()=>{}),await _p(n).catch(()=>{}))}catch{}finally{e=!1}}},4e3)}
|