bostrat-node 1.10.4 → 1.10.5
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 +189 -189
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
var
|
|
1
|
+
var KC=Object.defineProperty;var q=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var Uf=(e,t)=>{for(var n in t)KC(e,n,{get:t[n],enumerable:!0})};import{createHash as VC,randomBytes as zC,timingSafeEqual as JC}from"node:crypto";function Mk(){return zC(32).toString("hex")}function Fo(e){let t=typeof e=="string"?e.trim():"";return t?VC("sha256").update(t).digest("hex"):null}function Ck(e,t){return typeof e!="string"||typeof t!="string"||e.length!==t.length||!e?!1:JC(Buffer.from(e),Buffer.from(t))}function xi(e,t={},{now:n=Date.now(),log:r=console.warn}={}){if(Hf.set(e,(Hf.get(e)||0)+1),Wf.has(e)&&n-Wf.get(e)<YC)return!1;Wf.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=${Hf.get(e)}${o?` ${o}`:""}`),!0}async function Nk(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 lr,ur,Hf,Wf,YC,Gs=q(()=>{lr="BOSTRAT_HOOK_TOKEN",ur="x-bostrat-hook";Hf=new Map,Wf=new Map,YC=6e4});import Wt from"node:fs/promises";import Pk from"node:os";import Nt from"node:path";import{execFile as XC}from"node:child_process";import{promisify as ZC}from"node:util";function Xl(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function qf(){return process.env.CLAUDE_CONFIG_DIR?Nt.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Nt.join(Pk.homedir(),".claude.json")}function QC(){return process.env.CLAUDE_CONFIG_DIR?Nt.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Nt.join(Pk.homedir(),".claude","settings.json")}async function Fr(e){if(!e||typeof e!="string")return;let t=Nt.resolve(e),n=qf(),r=QC(),o={};try{o=JSON.parse(await Wt.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 Wt.mkdir(Nt.dirname(n),{recursive:!0}),await Wt.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await Wt.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 "${ur}: $${lr}" --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 Wt.mkdir(Nt.dirname(r),{recursive:!0}),await Wt.writeFile(r,JSON.stringify(i,null,2)),await eN(t)}async function eN(e){try{let t=Nt.resolve(e),n=Nt.join(t,".bostrat","memory","claude"),r=Nt.join(t,".claude","settings.local.json"),o={};try{o=JSON.parse(await Wt.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(Nt.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await Wt.mkdir(n,{recursive:!0}),await Wt.mkdir(Nt.dirname(r),{recursive:!0}),await Wt.writeFile(r,JSON.stringify(o,null,2)),await tN(t,["/.claude/settings.local.json"])}catch{}}async function tN(e,t){try{let{stdout:n}=await ZC(XC)("git",["-C",e,"rev-parse","--git-path","info/exclude"]),r=Nt.resolve(e,n.trim()),o=await Wt.readFile(r,"utf8").catch(()=>""),s=t.filter(a=>!o.split(`
|
|
2
2
|
`).includes(a));if(!s.length)return;await Wt.mkdir(Nt.dirname(r),{recursive:!0}),await Wt.appendFile(r,(o&&!o.endsWith(`
|
|
3
3
|
`)?`
|
|
4
4
|
`:"")+s.join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`)}catch{}}var vi=q(()=>{Gs()});import Gf from"node:fs/promises";import
|
|
6
|
+
`)}catch{}}var vi=q(()=>{Gs()});import Gf from"node:fs/promises";import nN from"node:os";import Zl from"node:path";function rN(){let e=process.env.CODEX_HOME||Zl.join(nN.homedir(),".codex");return Zl.join(e,"config.toml")}function Dk(e){return`[projects."${e}"]`}function oN(e,t){let n=Dk(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 Ks(e){if(!e||typeof e!="string")return;let t=Zl.resolve(e),n=rN(),r="";try{r=await Gf.readFile(n,"utf8")}catch{}if(oN(r,t))return;let o=Dk(t);if(r.includes(o)){let a=r.indexOf(o)+o.length,c=r.slice(a).search(/^\s*\[/m),l=c===-1?r.length:a+c,u=r.slice(a,l),d=/trust_level\s*=/.test(u)?u.replace(/trust_level\s*=\s*"[^"]*"/,'trust_level = "trusted"'):`${u.replace(/\s*$/,"")}
|
|
7
7
|
trust_level = "trusted"
|
|
8
8
|
`;r=r.slice(0,a)+d+r.slice(l)}else{let s=`
|
|
9
9
|
${o}
|
|
10
10
|
trust_level = "trusted"
|
|
11
11
|
`;r=r.trimEnd()+s+(r.length&&!r.endsWith(`
|
|
12
12
|
`)?`
|
|
13
|
-
`:"")}try{await Gf.mkdir(Zl.dirname(n),{recursive:!0}),await Gf.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var Ql=q(()=>{});import
|
|
14
|
-
`,"utf8")}var eu=q(()=>{});import Vf from"node:fs/promises";import Ur from"node:path";import{execFile as oN}from"node:child_process";import{promisify as sN}from"node:util";async function dN(e){try{return(await Vf.stat(Ur.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function pN(e){let t=i=>Vf.readFile(Ur.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 ur(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await dN(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:Ur.basename(o),branch:await pN(o)});return}if(s>=lN)return;let i;try{i=await Vf.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||uN.has(c.name)||await t(Ur.join(o,c.name),s+1)}for(let o of cN)await t(Ur.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=await Oe(o.path),a=s?.toLowerCase();if(a){if(n.has(a))continue;n.add(a),o.slug=s}r.push(o)}return r.sort((o,s)=>o.name.localeCompare(s.name))}function fN(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function Oe(e){if(!e)return null;if(Kf.has(e))return Kf.get(e);let t=null;try{let{stdout:n}=await aN("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=fN(n)}catch{t=null}return Kf.set(e,t),t}async function zs(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await ur())if((await Oe(n.path))?.toLowerCase()===t)return n.path;return null}var aN,Bk,iN,cN,lN,uN,Kf,Hr=q(()=>{aN=sN(oN),Bk=process.env.HOME||"/home/globo",iN=Ur.join(process.env.BOSTRAT_STATE_DIR||Ur.join(Bk,".bostrat"),"repos"),cN=[...(process.env.BOSTRAT_REPOS_ROOT||Ur.join(Bk,"Documents")).split(","),iN].map(e=>e.trim()).filter(Boolean),lN=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,uN=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);Kf=new Map});function Js(e=Date.now()){if(Ho)return{tripped:!1,holdMs:1/0};if(Uo>e)return{tripped:!1,holdMs:Uo-e};if((!Ti||e-Ti>=tu.windowMs)&&(Ti=e,Ai=0),Ai++,Ai<tu.trip)return{tripped:!1,holdMs:0};nu++;let t=Math.min(tu.minMs*2**(nu-1),tu.maxMs);return Uo=e+t,Ai=0,Ti=0,{tripped:!0,holdMs:t}}function jk(){Ai=0,Ti=0,nu=0,Uo=0}function Ei(e=Date.now()){return Ho?1/0:Uo>e?Uo-e:0}function Ys(e=Date.now()){return Ei(e)>0}function zf(e={}){Ho={at:Date.now(),...e},Uo=1/0}function ru(){return Ho}function Lk(e=Date.now()){return{held:Ys(e),holdMs:Ho?null:Ei(e),holds:nu,removed:Ho?{...Ho}:null}}var mN,tu,Ai,Ti,nu,Uo,Ho,Jf=q(()=>{mN={trip:3,windowMs:3e5,minMs:3e5,maxMs:216e5},tu={...mN},Ai=0,Ti=0,nu=0,Uo=0,Ho=null});import hN from"node:fs";import Fk from"node:fs/promises";import _i from"node:path";import wN from"node:os";import{fileURLToPath as gN}from"node:url";import{execFile as yN}from"node:child_process";import{promisify as bN}from"node:util";function Ce(e=process.env){let t=e.BOSTRAT_PLANE_URL||"";if(!t)return null;try{let n=new URL(t);return n.protocol=n.protocol==="ws:"?"http:":"https:",n.origin}catch{return null}}function ou(e=process.env){return!!(Ce(e)&&e.BOSTRAT_PLANE_TOKEN)}function Gk(e){return e?.status===404&&/not granted/i.test(String(e?.message||""))}async function Xs(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!Hk.test(n))throw Wo("repo must be owner/name",400);let r=Yf.get(n);if(r&&r.expiresAt-Date.now()>vN)return r;if(!ou(t))throw Wo("node is not plane-linked \u2014 GitHub auth unavailable",503);if(Ys()){if(r&&r.expiresAt>Date.now())return r;throw Wo(ru()?"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 Wk(`${Ce(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:qk(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw Wo(`plane unreachable \u2014 GitHub auth unavailable (${i?.message||i})`,503)}let s=await o.json().catch(()=>({}));if(!o.ok){if((o.status===401||o.status===403||o.status===410)&&Js(),o.status>=500&&r&&r.expiresAt>Date.now())return r;throw Wo(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 Yf.set(n,a),a}async function Ii({env:e=process.env,force:t=!1}={}){if(!ou(e))return{configured:!1,repos:[],username:null};if(!t&&Wr&&Date.now()-Wr.at<SN)return Wr.data;if(Ys())return Wr?.data??{configured:!0,repos:[],username:null,error:"plane refused this node's token"};try{let n=await Wk(`${Ce(e)}/v1/github/repos`,{headers:qk(e)});if((n.status===401||n.status===403||n.status===410)&&Js(),!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Wr={at:Date.now(),data:r},r}catch(n){return Wr?Wr.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Kk(){Yf.clear(),Wr=null}async function Dn({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await Oe(e):null);if(!r)return{};let{token:o}=await Xs(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Xf({env:e=process.env}={}){return(await Ii({env:e})).username??null}function _N(e){let[t,n]=String(e).split("/");return _i.join(xN,t,n)}async function Zf(e,{env:t=process.env}={}){let n=(...r)=>Uk("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${TN()}" ${EN(t)}`),await n("config","credential.useHttpPath","true")}async function su(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!Hk.test(String(e||"")))throw Wo(`bad repo slug "${e}"`,400);let r=_N(e);if(hN.existsSync(_i.join(r,".git")))return r;let{token:o}=await Xs(e,{env:t});await Fk.mkdir(_i.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Uk("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:AN,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Fk.rm(r,{recursive:!0,force:!0}).catch(()=>{}),Wo(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Zf(r,{env:t}),r}var Uk,kN,xN,Hk,vN,SN,AN,Wk,Yf,Wr,qk,Wo,TN,EN,bt=q(()=>{Hr();Jf();Uk=bN(yN),kN=process.env.BOSTRAT_STATE_DIR||_i.join(process.env.HOME||wN.tmpdir(),".bostrat"),xN=_i.join(kN,"repos"),Hk=/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?\/[A-Za-z0-9._-]{1,100}$/,vN=5*6e4,SN=6e4,AN=10*6e4,Wk=fetch,Yf=new Map,Wr=null;qk=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Wo=(e,t)=>Object.assign(new Error(e),{status:t});TN=()=>gN(new URL("../bin/bostrat-git-credential",import.meta.url)),EN=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function zk(e=process.env){return!!(Ce(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Yk(e,t,n,r=Vk){let o=Ce(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await $N(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function Xk(e,{env:t=process.env,force:n=!1,timeoutMs:r=Vk}={}){if(!zk(t))return{facts:[],source:"unconfigured"};let o=Qf.get(e);if(!n&&o&&Date.now()-o.at<IN)return{facts:o.facts,source:"cache"};try{let s=await Yk(`/v1/memory/${encodeURIComponent(e)}`,{headers:Jk(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 Qf.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 au(e,t,{env:n=process.env}={}){if(!zk(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await Yk(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...Jk(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 Qf.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 IN,Vk,Qf,$N,Jk,Zk=q(()=>{bt();IN=6e4,Vk=5e3,Qf=new Map,$N=(...e)=>fetch(...e);Jk=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import em from"node:fs/promises";import Zs from"node:path";import RN from"node:os";import{EventEmitter as ON}from"node:events";import{randomUUID as MN}from"node:crypto";function Qs(e){return e?.cwd?Zs.join(e.cwd,".bostrat","events"):Zs.join(tm,"events")}async function DN(e,t){if(!iu.has(t)){let o=(await K(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);iu.set(t,o)}let n=iu.get(t)+1;return iu.set(t,n),n}function $i(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||!CN.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!NN.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!PN.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?Zs.join(tm,"events"):Qs(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=$i(u,Number(t.payload?.ts??t.ts)||Date.now())}let s=await DN(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||MN(),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 e0(c)&&(c=t0(c),l=JSON.parse(c)),await em.mkdir(r,{recursive:!0}),await em.appendFile(Qk(r,n),c+`
|
|
15
|
-
`),Ye.emit("event",l),Ye.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"?Zs.join(tm,"events"):Qs(e));let o;try{o=await em.readFile(
|
|
16
|
-
`))if(a.trim())try{s.push(JSON.parse(
|
|
13
|
+
`:"")}try{await Gf.mkdir(Zl.dirname(n),{recursive:!0}),await Gf.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var Ql=q(()=>{});import Bk from"node:fs/promises";import sN from"node:os";import Si from"node:path";function aN(e){let n=Si.resolve(e).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||Si.join(sN.homedir(),".cursor");return Si.join(r,"projects",n)}async function Vs(e){if(!e||typeof e!="string")return;let t=Si.resolve(e),n=aN(t),r=Si.join(n,".workspace-trusted");await Bk.mkdir(n,{recursive:!0}),await Bk.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:t},null,2)}
|
|
14
|
+
`,"utf8")}var eu=q(()=>{});import Vf from"node:fs/promises";import Ur from"node:path";import{execFile as iN}from"node:child_process";import{promisify as cN}from"node:util";async function mN(e){try{return(await Vf.stat(Ur.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function hN(e){let t=i=>Vf.readFile(Ur.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 dr(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await mN(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:Ur.basename(o),branch:await hN(o)});return}if(s>=pN)return;let i;try{i=await Vf.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||fN.has(c.name)||await t(Ur.join(o,c.name),s+1)}for(let o of dN)await t(Ur.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=await Oe(o.path),a=s?.toLowerCase();if(a){if(n.has(a))continue;n.add(a),o.slug=s}r.push(o)}return r.sort((o,s)=>o.name.localeCompare(s.name))}function wN(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function Oe(e){if(!e)return null;if(Kf.has(e))return Kf.get(e);let t=null;try{let{stdout:n}=await lN("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=wN(n)}catch{t=null}return Kf.set(e,t),t}async function zs(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await dr())if((await Oe(n.path))?.toLowerCase()===t)return n.path;return null}var lN,jk,uN,dN,pN,fN,Kf,Hr=q(()=>{lN=cN(iN),jk=process.env.HOME||"/home/globo",uN=Ur.join(process.env.BOSTRAT_STATE_DIR||Ur.join(jk,".bostrat"),"repos"),dN=[...(process.env.BOSTRAT_REPOS_ROOT||Ur.join(jk,"Documents")).split(","),uN].map(e=>e.trim()).filter(Boolean),pN=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,fN=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);Kf=new Map});function Js(e=Date.now()){if(Ho)return{tripped:!1,holdMs:1/0};if(Uo>e)return{tripped:!1,holdMs:Uo-e};if((!Ti||e-Ti>=tu.windowMs)&&(Ti=e,Ai=0),Ai++,Ai<tu.trip)return{tripped:!1,holdMs:0};nu++;let t=Math.min(tu.minMs*2**(nu-1),tu.maxMs);return Uo=e+t,Ai=0,Ti=0,{tripped:!0,holdMs:t}}function Lk(){Ai=0,Ti=0,nu=0,Uo=0}function Ei(e=Date.now()){return Ho?1/0:Uo>e?Uo-e:0}function Ys(e=Date.now()){return Ei(e)>0}function zf(e={}){Ho={at:Date.now(),...e},Uo=1/0}function ru(){return Ho}function Fk(e=Date.now()){return{held:Ys(e),holdMs:Ho?null:Ei(e),holds:nu,removed:Ho?{...Ho}:null}}var gN,tu,Ai,Ti,nu,Uo,Ho,Jf=q(()=>{gN={trip:3,windowMs:3e5,minMs:3e5,maxMs:216e5},tu={...gN},Ai=0,Ti=0,nu=0,Uo=0,Ho=null});import yN from"node:fs";import Uk from"node:fs/promises";import _i from"node:path";import bN from"node:os";import{fileURLToPath as kN}from"node:url";import{execFile as xN}from"node:child_process";import{promisify as vN}from"node:util";function Ne(e=process.env){let t=e.BOSTRAT_PLANE_URL||"";if(!t)return null;try{let n=new URL(t);return n.protocol=n.protocol==="ws:"?"http:":"https:",n.origin}catch{return null}}function ou(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}function Kk(e){return e?.status===404&&/not granted/i.test(String(e?.message||""))}async function Xs(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!Wk.test(n))throw Wo("repo must be owner/name",400);let r=Yf.get(n);if(r&&r.expiresAt-Date.now()>TN)return r;if(!ou(t))throw Wo("node is not plane-linked \u2014 GitHub auth unavailable",503);if(Ys()){if(r&&r.expiresAt>Date.now())return r;throw Wo(ru()?"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 qk(`${Ne(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:Gk(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw Wo(`plane unreachable \u2014 GitHub auth unavailable (${i?.message||i})`,503)}let s=await o.json().catch(()=>({}));if(!o.ok){if((o.status===401||o.status===403||o.status===410)&&Js(),o.status>=500&&r&&r.expiresAt>Date.now())return r;throw Wo(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 Yf.set(n,a),a}async function Ii({env:e=process.env,force:t=!1}={}){if(!ou(e))return{configured:!1,repos:[],username:null};if(!t&&Wr&&Date.now()-Wr.at<EN)return Wr.data;if(Ys())return Wr?.data??{configured:!0,repos:[],username:null,error:"plane refused this node's token"};try{let n=await qk(`${Ne(e)}/v1/github/repos`,{headers:Gk(e)});if((n.status===401||n.status===403||n.status===410)&&Js(),!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Wr={at:Date.now(),data:r},r}catch(n){return Wr?Wr.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Vk(){Yf.clear(),Wr=null}async function Bn({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await Oe(e):null);if(!r)return{};let{token:o}=await Xs(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Xf({env:e=process.env}={}){return(await Ii({env:e})).username??null}function RN(e){let[t,n]=String(e).split("/");return _i.join(AN,t,n)}async function Zf(e,{env:t=process.env}={}){let n=(...r)=>Hk("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${IN()}" ${$N(t)}`),await n("config","credential.useHttpPath","true")}async function su(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!Wk.test(String(e||"")))throw Wo(`bad repo slug "${e}"`,400);let r=RN(e);if(yN.existsSync(_i.join(r,".git")))return r;let{token:o}=await Xs(e,{env:t});await Uk.mkdir(_i.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Hk("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:_N,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Uk.rm(r,{recursive:!0,force:!0}).catch(()=>{}),Wo(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Zf(r,{env:t}),r}var Hk,SN,AN,Wk,TN,EN,_N,qk,Yf,Wr,Gk,Wo,IN,$N,bt=q(()=>{Hr();Jf();Hk=vN(xN),SN=process.env.BOSTRAT_STATE_DIR||_i.join(process.env.HOME||bN.tmpdir(),".bostrat"),AN=_i.join(SN,"repos"),Wk=/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?\/[A-Za-z0-9._-]{1,100}$/,TN=5*6e4,EN=6e4,_N=10*6e4,qk=fetch,Yf=new Map,Wr=null;Gk=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Wo=(e,t)=>Object.assign(new Error(e),{status:t});IN=()=>kN(new URL("../bin/bostrat-git-credential",import.meta.url)),$N=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function Jk(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Xk(e,t,n,r=zk){let o=Ne(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await MN(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function Zk(e,{env:t=process.env,force:n=!1,timeoutMs:r=zk}={}){if(!Jk(t))return{facts:[],source:"unconfigured"};let o=Qf.get(e);if(!n&&o&&Date.now()-o.at<ON)return{facts:o.facts,source:"cache"};try{let s=await Xk(`/v1/memory/${encodeURIComponent(e)}`,{headers:Yk(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 Qf.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 au(e,t,{env:n=process.env}={}){if(!Jk(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await Xk(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...Yk(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 Qf.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 ON,zk,Qf,MN,Yk,Qk=q(()=>{bt();ON=6e4,zk=5e3,Qf=new Map,MN=(...e)=>fetch(...e);Yk=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import em from"node:fs/promises";import Zs from"node:path";import CN from"node:os";import{EventEmitter as NN}from"node:events";import{randomUUID as PN}from"node:crypto";function Qs(e){return e?.cwd?Zs.join(e.cwd,".bostrat","events"):Zs.join(tm,"events")}async function LN(e,t){if(!iu.has(t)){let o=(await K(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);iu.set(t,o)}let n=iu.get(t)+1;return iu.set(t,n),n}function $i(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||!DN.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!BN.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!jN.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?Zs.join(tm,"events"):Qs(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=$i(u,Number(t.payload?.ts??t.ts)||Date.now())}let s=await LN(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||PN(),trackId:n,boxId:t.boxId??null,...o!=null?{epoch:o}:{},...a?{userId:a}:{},seq:s,ts:t.ts??Date.now(),type:t.type,actor:t.actor??"system",provider:t.provider??null,source:t.source??"orchestrator",payload:t.payload??{},...t.causationId?{causationId:t.causationId}:{},...t.correlationId?{correlationId:t.correlationId}:{}},c=JSON.stringify(i),l=i;return t0(c)&&(c=n0(c),l=JSON.parse(c)),await em.mkdir(r,{recursive:!0}),await em.appendFile(e0(r,n),c+`
|
|
15
|
+
`),Ye.emit("event",l),Ye.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"?Zs.join(tm,"events"):Qs(e));let o;try{o=await em.readFile(e0(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
16
|
+
`))if(a.trim())try{s.push(JSON.parse(t0(a)?n0(a):a))}catch{}return s}function r0(e,t){let n=`track:${e}`;return Ye.on(n,t),()=>Ye.off(n,t)}var DN,BN,jN,Ye,tm,e0,iu,FN,t0,n0,ye=q(()=>{DN=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.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"]),BN=new Set(["user","agent","orchestrator","system","peer"]),jN=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor","cli"]),Ye=new NN;Ye.setMaxListeners(0);tm=process.env.BOSTRAT_STATE_DIR||Zs.join(process.env.HOME||CN.tmpdir(),".bostrat");e0=(e,t)=>Zs.join(e,`${t}.ndjson`),iu=new Map;FN=/(?<!\\)((?:\\\\)*)\\u0000/g,t0=e=>e.includes("\\u0000"),n0=e=>e.replace(FN,"$1\\ufffd")});import de from"node:fs/promises";import UN from"node:os";import Se from"node:path";import{execFile as HN}from"node:child_process";import{promisify as WN}from"node:util";function lu(){return Se.join(uu,"memory","repos")}function qo(e){return e===pr?Se.join(uu,"memory",pr):Se.join(lu(),e)}async function Oi(e){try{return JSON.parse(await de.readFile(Se.join(e,u0),"utf8"))}catch{return{}}}async function rm(e,t){let n=await Oi(e);for(let[r,o]of t)n[r]=Math.max(Number(n[r])||0,o);await de.mkdir(e,{recursive:!0}),await de.writeFile(Se.join(e,u0),JSON.stringify(n))}function s0(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 a0(e){try{let{stdout:t}=await qN("git",["-C",e,"config","--get","remote.origin.url"]);return t.trim()||null}catch{return null}}async function om(e){if(!e)return[];let t=s0(await a0(e));if(t)return[t];let n=[],r=[];try{r=await de.readdir(e,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=s0(await a0(Se.join(e,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function du(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 qr(e,t){let n=new RegExp(`^\\s*${t}:\\s*(.+?)\\s*$`,"m").exec(e||"");return n?n[1].replace(/^"|"$/g,""):null}async function jn(e,t=null){let n=t??await de.readFile(e,"utf8").catch(()=>""),{fm:r}=du(n),o=qr(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await de.stat(e)).mtimeMs}catch{return 0}}function d0(e,t){let n=qr(e,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===pr){let o=qr(e,"trust");if(!o||/^operator/i.test(o))return pr}return t}function sm(e,t,n,r=new Map){let o=(e||"").split(`
|
|
17
17
|
`),s=new Map;for(let c of(t||"").split(`
|
|
18
18
|
`)){let l=ta(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=ta(c);return l?(a.add(l),n.has(l)&&s.has(l)?s.get(l):c):c});for(let[c,l]of s)a.has(c)||!n.has(c)||(a.add(c),i.push(l));for(let c of n){if(a.has(c))continue;let l=r.get(c);l&&i.push(l)}return i.join(`
|
|
19
19
|
`).replace(/\n{3,}/g,`
|
|
20
20
|
|
|
21
|
-
`)}function
|
|
21
|
+
`)}function p0(e,t){let n=qr(t,"name")||e.replace(/\.md$/,""),r=qr(t,"description")||"";return`- [${n}](${e})${r?` \u2014 ${r}`:""}`}async function Mi(e){let t=[];try{t=await de.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 f0(e,t){let n=Se.join(e,".bostrat","memory"),r=await Mi(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=i0.get(e)??0,s=Date.now(),a=new Map,i=new Map,c=0,l=new Map;for(let d of r){let p=Se.join(n,d);if(o)try{if((await de.stat(p)).mtimeMs<o-KN)continue}catch{continue}let f;try{f=await de.readFile(p,"utf8")}catch{continue}let{fm:w}=du(f),m=d0(w,t);if(!m)continue;let y=qo(m),k=Se.join(y,d),g=await jn(p,f);if(l.has(y)||l.set(y,await Oi(y)),(l.get(y)[d]||0)>=g)continue;let x=await jn(k).catch(()=>0);if(await de.access(k).then(()=>!0).catch(()=>!1)&&x>=g)continue;await de.mkdir(y,{recursive:!0}),await de.writeFile(k,f),c++;let $=a.get(y);$||a.set(y,$={won:new Set,generated:new Map}),$.won.add(d),$.generated.set(d,p0(d,w)),i.has(m)||i.set(m,new Set),i.get(m).add(d)}let u=await de.readFile(Se.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[d,p]of a){let f=Se.join(d,"MEMORY.md"),w=await de.readFile(f,"utf8").catch(()=>"");await de.writeFile(f,sm(w,u,p.won,p.generated))}return i0.set(e,s),{harvested:c,changedByKey:i}}async function m0(e){let t=0,n=new Map;for(let r of e||[]){if(!r||!await de.access(Se.join(r,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[s]=await om(r),a=await f0(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 VN(){let e=Se.join(uu,"workspaces"),t=[];try{t=await de.readdir(e,{withFileTypes:!0})}catch{return{harvested:0,changedByKey:new Map}}return m0(t.filter(n=>n.isDirectory()).map(n=>Se.join(e,n.name)))}async function YN(e,t){let n=qo(e),r=await Oi(n),o=new Set,s=new Map,a=[],i=0;for(let c of t||[]){let l=String(c?.file||"");if(!zN.test(l)||l==="MEMORY.md")continue;let u=Se.join(n,l),d=await de.access(u).then(()=>!0).catch(()=>!1),p=d?await jn(u):0,f=Number(c.modified)||0;if(c.deleted){f>p&&(d&&await de.rm(u,{force:!0}),await rm(n,[[l,f]]),d&&(a.push(l),i++));continue}d&&p>=f||(r[l]||0)>=f||(await de.mkdir(n,{recursive:!0}),await de.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=Se.join(n,"MEMORY.md"),l=await de.readFile(c,"utf8").catch(()=>""),u=sm(l,[...s.values()].join(`
|
|
22
22
|
`),o);if(a.length){let d=new Set(a);u=u.split(`
|
|
23
23
|
`).filter(p=>!d.has(ta(p))).join(`
|
|
24
|
-
`)}await de.mkdir(n,{recursive:!0}),await de.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function pu(e,t=null){let n=qo(e),r=t?.length?t:await Mi(n);if(!r.length)return{pushed:0,source:"empty"};let o=
|
|
25
|
-
`);t.length>
|
|
26
|
-
`);for(;Buffer.byteLength(n,"utf8")>
|
|
27
|
-
`);return n}async function
|
|
24
|
+
`)}await de.mkdir(n,{recursive:!0}),await de.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function pu(e,t=null){let n=qo(e),r=t?.length?t:await Mi(n);if(!r.length)return{pushed:0,source:"empty"};let o=JN(await de.readFile(Se.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=Se.join(n,a),c;try{c=await de.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await jn(i,c),indexLine:o.get(a)??null})}return au(e,s)}async function XN(e,{timeoutMs:t}={}){let{facts:n,source:r}=await Zk(e,t?{timeoutMs:t}:{});n.length?await YN(e,n):r==="plane"&&await pu(e)}function ZN(e){let t=e.split(`
|
|
25
|
+
`);t.length>o0&&(t=t.slice(0,o0));let n=t.join(`
|
|
26
|
+
`);for(;Buffer.byteLength(n,"utf8")>GN&&t.length;)t.pop(),n=t.join(`
|
|
27
|
+
`);return n}async function eP(e,t){if(!e||!t?.length)return{materialized:!1,reason:"no-repo-key"};let n=Se.join(e,".bostrat","memory"),r=0,o=0,s=[];for(let u of t){let d=qo(u),p=await Oi(d);for(let[y,k]of Object.entries(p)){let g=Se.join(n,y);await de.access(g).then(()=>!0).catch(()=>!1)&&await jn(g)<=k&&(await de.rm(g,{force:!0}),o++)}let f=await Mi(d);if(!f.length)continue;await de.mkdir(n,{recursive:!0});for(let y of f){let k=Se.join(d,y),g=Se.join(n,y),x=await jn(k),S=await jn(g).catch(()=>0);await de.access(g).then(()=>!0).catch(()=>!1)&&S>=x||(await de.copyFile(k,g),r++)}let w=await de.readFile(Se.join(d,"MEMORY.md"),"utf8").catch(()=>""),m=u===pr?"## operator memory":`## repo: ${u}`;w.trim()&&s.push(t.length>1?`${m}
|
|
28
28
|
|
|
29
29
|
${w.trim()}`:w.trim())}if(!s.length)return o?{materialized:!1,reason:"empty-store",cleaned:o}:{materialized:!1,reason:"empty-store"};let i="_Shared Track memory (point-in-time facts). Verify file/line citations against the current code before acting on them; correct a wrong fact in place (bump `modified`, use `supersedes`) rather than working around it._\n\n"+s.join(`
|
|
30
30
|
|
|
31
31
|
`)+`
|
|
32
|
-
`,c=
|
|
32
|
+
`,c=ZN(i);await de.mkdir(n,{recursive:!0}),await de.writeFile(Se.join(n,"MEMORY.md"),c);let l=Se.join(n,"claude");return await de.mkdir(l,{recursive:!0}),await de.writeFile(Se.join(l,"MEMORY.md"),QN,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function h0(e){try{let t=await om(e);if(!t.length)return{materialized:!1,reason:"no-repo-key"};let n=[...t,pr];return await Promise.all(n.map(r=>XN(r,{timeoutMs:tP}))),await eP(e,n)}catch(t){return{materialized:!1,reason:"error",error:t?.message||String(t)}}}function w0(){return{...at,pending:ea.size,queued:nm.size}}function g0(e){if(cu.has(e))return cu.get(e);nm.add(e);let t=c0.then(async()=>{nm.delete(e),cu.delete(e);let n=await am(e);return at.runs++,at.lastRunAt=Date.now(),n.error?(at.errors++,at.lastError=n.error):at.harvested+=n.harvested||0,n}).catch(n=>({harvested:0,error:n?.message||String(n)}));return cu.set(e,t),c0=t.then(()=>{}),t}function fu(e,{delayMs:t=nP}={}){if(!e||typeof e!="string")return;at.scheduled++;let n=ea.get(e);n&&clearTimeout(n);let r=setTimeout(()=>{ea.delete(e),g0(e)},t);r.unref?.(),ea.set(e,r)}async function mu(e,{timeoutMs:t=3e3}={}){if(!e)return{harvested:0,skipped:"no-cwd"};if(!await de.access(Se.join(e,".bostrat","memory")).then(()=>!0,()=>!1))return{harvested:0,skipped:"no-memory-dir"};let r=ea.get(e);r&&(clearTimeout(r),ea.delete(e));let o,s=new Promise(a=>{o=setTimeout(()=>a({harvested:0,timedOut:!0}),t),o.unref?.()});try{return await Promise.race([g0(e),s])}finally{clearTimeout(o)}}async function y0(e){return Ri||(Ri=(async()=>{try{l0||(l0=!0,await oP());let{harvested:t,changedByKey:n}=await m0(e);for(let[r,o]of n)await pu(r,[...o]);return at.reconciles++,at.lastReconcileAt=Date.now(),at.harvested+=t,{harvested:t,keys:[...n.keys()]}}catch(t){return at.errors++,at.lastError=t?.message||String(t),{harvested:0,error:at.lastError}}finally{Ri=null}})(),Ri)}function rP(){return Se.join(uu,"memory",".full-sweep-done")}async function b0(){let e=rP();try{let t=JSON.parse(await de.readFile(e,"utf8"));return at.backfill={at:Number(t?.at)||0,harvested:Number(t?.harvested)||0,prior:!0},{ran:!1,...at.backfill}}catch{}try{let{harvested:t,changedByKey:n}=await VN();for(let[o,s]of n)await pu(o,[...s]);let r={at:Date.now(),harvested:t};return await de.mkdir(Se.dirname(e),{recursive:!0}),await de.writeFile(e,JSON.stringify(r)),at.backfill=r,{ran:!0,...r}}catch(t){return at.errors++,at.lastError=t?.message||String(t),{ran:!1,error:at.lastError}}}async function oP(){let e=0,t=[];try{t=await de.readdir(lu(),{withFileTypes:!0})}catch{return{moved:e}}for(let n of t){if(!n.isDirectory())continue;let r=Se.join(lu(),n.name),o=qo(pr),s=new Set,a=new Map;for(let f of await Mi(r)){let w=Se.join(r,f),m;try{m=await de.readFile(w,"utf8")}catch{continue}let{fm:y}=du(m);if(d0(y,n.name)!==pr)continue;let k=Se.join(o,f),g=await jn(k).catch(()=>0),x=await jn(w,m);await de.mkdir(o,{recursive:!0}),x>g&&await de.writeFile(k,m),await de.rm(w,{force:!0}),s.add(f),a.set(f,p0(f,y)),e++}if(!s.size)continue;let i=Date.now();await rm(r,[...s].map(f=>[f,i])),await au(n.name,[...s].map(f=>({file:f,deleted:!0,modified:i})));let c=Se.join(r,"MEMORY.md"),l=await de.readFile(c,"utf8").catch(()=>""),u=l.split(`
|
|
33
33
|
`).filter(f=>!s.has(ta(f))).join(`
|
|
34
|
-
`);await de.writeFile(c,u).catch(()=>{});let d=Se.join(o,"MEMORY.md"),p=await de.readFile(d,"utf8").catch(()=>"");await de.writeFile(d,sm(p,l,s,a))}return{moved:e}}async function
|
|
34
|
+
`);await de.writeFile(c,u).catch(()=>{});let d=Se.join(o,"MEMORY.md"),p=await de.readFile(d,"utf8").catch(()=>"");await de.writeFile(d,sm(p,l,s,a))}return{moved:e}}async function k0(){let e=[],t=[pr];try{for(let n of await de.readdir(lu(),{withFileTypes:!0}))n.isDirectory()&&t.push(n.name)}catch{}for(let n of t){let r=qo(n),o=await Mi(r),s=await Oi(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await de.readFile(Se.join(r,i),"utf8").catch(()=>""),{fm:l}=du(c);a.push({file:i,name:qr(l,"name")||i.replace(/\.md$/,""),description:qr(l,"description")||"",scope:qr(l,"scope")||null,modified:await jn(Se.join(r,i),c)})}e.push({key:n,facts:a,tombstones:Object.keys(s).length})}return e}async function x0(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=qo(e),r=Se.join(n,t);if(!await de.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${t} not found in ${e}`);let s=Date.now();await de.rm(r,{force:!0}),await rm(n,[[t,s]]);let a=Se.join(n,"MEMORY.md"),i=await de.readFile(a,"utf8").catch(()=>"");await de.writeFile(a,i.split(`
|
|
35
35
|
`).filter(l=>ta(l)!==t).join(`
|
|
36
|
-
`)).catch(()=>{});let c=await au(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function am(e){try{let t=await om(e),{harvested:n,changedByKey:r}=await
|
|
37
|
-
`)){let r=ta(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};
|
|
36
|
+
`)).catch(()=>{});let c=await au(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function am(e){try{let t=await om(e),{harvested:n,changedByKey:r}=await f0(e,t[0]||null);for(let[o,s]of r)await pu(o,[...s]);return n>0&&await aP(e,r).catch(()=>{}),{harvested:n}}catch(t){return{harvested:0,error:t?.message||String(t)}}}async function aP(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,sP)}}await N({id:n,cwd:e},{type:"memory.harvested",actor:"orchestrator",payload:{harvested:o,keys:r,ts:Date.now()}})}var qN,uu,o0,GN,pr,u0,ta,i0,KN,zN,JN,QN,tP,nP,ea,nm,c0,at,cu,l0,Ri,sP,Ci=q(()=>{Qk();ye();qN=WN(HN),uu=process.env.BOSTRAT_STATE_DIR||Se.join(process.env.HOME||UN.tmpdir(),".bostrat"),o0=200,GN=25*1024;pr="operator";u0=".tombstones.json";ta=e=>/\]\(([^)]+\.md)\)/.exec(e)?.[1]||null;i0=new Map,KN=2e3;zN=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,JN=e=>{let t=new Map;for(let n of(e||"").split(`
|
|
37
|
+
`)){let r=ta(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};QN=`# Track memory
|
|
38
38
|
|
|
39
39
|
Shared Track memory (all providers) lives one level up: read \`../MEMORY.md\` for the
|
|
40
40
|
index and the fact files beside it. Facts you save here stay Claude-local but ride the
|
|
41
41
|
same sync; prefer adding durable cross-provider facts to \`../\` following its format.
|
|
42
|
-
`;
|
|
43
|
-
`,n!=null?{mode:n}:void 0),await hu.rename(r,e)}catch(o){throw await hu.unlink(r).catch(()=>{}),o}}var
|
|
42
|
+
`;tP=Number(process.env.BOSTRAT_MEMORY_SPAWN_PULL_MS)||1500;nP=Number(process.env.BOSTRAT_MEMORY_HARVEST_DEBOUNCE_MS)||2e3,ea=new Map,nm=new Set,c0=Promise.resolve(),at={scheduled:0,runs:0,harvested:0,errors:0,lastRunAt:null,lastError:null,reconciles:0,lastReconcileAt:null,backfill:null};cu=new Map;l0=!1,Ri=null;sP=20});import hu from"node:fs/promises";import iP from"node:path";async function Pt(e,t,{mode:n}={}){await hu.mkdir(iP.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${++cP}.tmp`;try{await hu.writeFile(r,JSON.stringify(t,null,2)+`
|
|
43
|
+
`,n!=null?{mode:n}:void 0),await hu.rename(r,e)}catch(o){throw await hu.unlink(r).catch(()=>{}),o}}var cP,Gr=q(()=>{cP=0});import E0 from"node:fs/promises";import _0 from"node:os";import I0 from"node:path";function qe(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Di(e,t={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),lP);try{return await mP(`${Ne(n)}${e}`,{...t,signal:r.signal})}finally{clearTimeout(o)}}async function M0(){return gu||(im||=E0.readFile($0(),"utf8").then(e=>JSON.parse(e)).catch(()=>({ops:[]})).then(e=>gu={ops:Array.isArray(e?.ops)?e.ops:[],parked:Array.isArray(e?.parked)?e.parked:[]}).finally(()=>im=null),im)}async function yu(){await Pt($0(),gu).catch(()=>{})}function A0(e){return e.op==="blob"?`blob ${e.family} ${e.id} ${e.v} ${e.kind}`:`${e.op} ${e.family} ${e.id}`}async function lm(e){let t=await M0(),n=A0(e);if(e.op==="delete")t.ops=t.ops.filter(r=>!(r.family===e.family&&r.id===e.id)),t.ops.push(e);else{t.ops=t.ops.filter(o=>!(o.op==="delete"&&o.family===e.family&&o.id===e.id));let r=t.ops.findIndex(o=>A0(o)===n);r>=0?t.ops[r]=e:t.ops.push(e)}t.ops.length>v0&&(t.ops=t.ops.slice(-v0)),await yu()}function T0(){wu||(wu=setTimeout(()=>{wu=null,na().catch(()=>{})},hP),wu.unref?.())}function cm(e){let t=new Error(`plane ${e}`);return t.refused=!0,t}async function gP(e,t=process.env){if(e.op==="record"){let n=await Di(`/v1/content/${encodeURIComponent(e.family)}`,{method:"POST",headers:{...Pi(t),"content-type":"application/json"},body:JSON.stringify({records:[{id:e.id,trackId:e.trackId??null,data:e.data,updatedAt:e.updatedAt,originNode:R0()}]})},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 E0.readFile(e.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await Di(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}/blob?v=${e.v}&kind=${encodeURIComponent(e.kind)}`,{method:"POST",headers:{...Pi(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 Di(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}?deletedAt=${e.deletedAt}`,{method:"DELETE",headers:Pi(t)},t);if(!n.ok)throw cm(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function na({env:e=process.env,log:t=console,now:n=Date.now()}={}){return qe(e)?Ni||(Ni=(async()=>{let r=await M0();yP(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await gP(a,e)}catch(c){if(!c?.refused)return T0(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<dP)return T0(),await yu(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};t.error?.(`[content-plane] ALARM parking ${a.op} ${a.family}/${a.id} after ${a.refusals} refusals (${c.message}) \u2014 draining the ${r.ops.length-1} ops behind it`),r.ops.shift(),r.parked.push({...a,parkedAt:n,error:c.message}),r.parked.length>S0&&(r.parked=r.parked.slice(-S0)),s++,await yu();continue}i.rejected&&t.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await yu()}return{flushed:o,parked:s,pending:0}})().finally(()=>{Ni=null}),Ni):{flushed:0,source:"unconfigured"}}function yP(e,t){if(!e.parked.length||e.ops.length)return;let n=e.parked.filter(r=>t-(r.parkedAt||0)>=wP);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 Et(e,t,{trackId:n=null,env:r=process.env}={}){return qe(r)?(await lm({op:"record",family:e,id:t.id,trackId:n??t.trackId??null,data:t,updatedAt:Number(t.updatedAt)||Date.now()}),na({env:r})):{queued:!1,source:"unconfigured"}}async function Kr(e,t,n,r,o,{env:s=process.env}={}){return qe(s)?(await lm({op:"blob",family:e,id:t,v:Number(n),kind:r,path:o}),na({env:s})):{queued:!1,source:"unconfigured"}}async function gn(e,t,{deletedAt:n=Date.now(),env:r=process.env}={}){return qe(r)?(await lm({op:"delete",family:e,id:t,deletedAt:n}),na({env:r})):{queued:!1,source:"unconfigured"}}async function ut(e,{env:t=process.env}={}){if(!qe(t))return{records:[],source:"unconfigured"};try{let n=await Di(`/v1/content/${encodeURIComponent(e)}?tombstones=1`,{headers:Pi(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 Bi(e,t,n,r,{env:o=process.env}={}){if(!qe(o))return null;try{let s=await Di(`/v1/content/${encodeURIComponent(e)}/${encodeURIComponent(t)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Pi(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 lP,uP,v0,dP,S0,pP,fP,$0,R0,O0,mP,hP,wP,Pi,gu,im,wu,Ni,Vr=q(()=>{bt();Gr();lP=1e4,uP=3e4,v0=2e3,dP=5,S0=100,pP=60*6e4,fP=()=>process.env.BOSTRAT_STATE_DIR||I0.join(process.env.HOME||_0.tmpdir(),".bostrat"),$0=()=>I0.join(fP(),"content-outbox.json"),R0=()=>process.env.BOSTRAT_NODE_ID||_0.hostname(),O0=()=>R0(),mP=(...e)=>fetch(...e),hP=uP,wP=pP;Pi=(e=process.env)=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`});gu=null,im=null,wu=null,Ni=null});import um from"node:fs/promises";import C0 from"node:path";async function N0(e,t,{box:n=null,agent:r=null}={}){if(!e||!qe())return{pushed:!1};try{let o=Date.now();return await Et("checkpoints",{id:e,trackId:e,v:o,box:n,agent:r,updatedAt:o},{trackId:e}),await Kr("checkpoints",e,o,"md",t),ji=null,{pushed:!0,v:o}}catch{return{pushed:!1}}}async function SP(){if(ji&&Date.now()-ji.at<vP)return{records:ji.records,source:"plane"};let e=await ut("checkpoints");return e.source==="plane"&&(ji={at:Date.now(),records:e.records}),e}async function bu(e){try{let t=xP(e);if(!t||!qe())return{restored:!1};let n=C0.join(e,bP,kP);if(await um.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await SP();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 Bi("checkpoints",t,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await um.mkdir(C0.dirname(n),{recursive:!0}),await um.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var bP,kP,xP,vP,ji,ku=q(()=>{Vr();bP=".bostrat/handoff",kP="latest.md",xP=e=>/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1]??null;vP=6e4,ji=null});import Li from"node:fs/promises";import AP from"node:os";import D0 from"node:path";function Go(e){let t=String(e??"");for(let n of TP)t=t.replace(n,"\u2039redacted\u203A");return t}function xu(){let e=process.env.BOSTRAT_HANDOFF_DEBUG;return e==="1"||e==="true"}async function EP(e){try{await Li.mkdir(dm,{recursive:!0}),await Li.appendFile(ra,`${JSON.stringify(e)}
|
|
44
44
|
`,"utf8");let n=(await Li.readFile(ra,"utf8")).split(`
|
|
45
|
-
`).filter(Boolean);n.length>
|
|
45
|
+
`).filter(Boolean);n.length>P0&&await Li.writeFile(ra,`${n.slice(-P0).join(`
|
|
46
46
|
`)}
|
|
47
|
-
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${ra}: ${t?.message||t}`)}}async function oe(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||xu()))try{n.paneTail=Go((await
|
|
48
|
-
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Go(o.paneTail)),o}catch{return{raw:Go(r)}}})}catch{return[]}}var
|
|
49
|
-
`,r=pm.join(e,t),o=null;try{o=await vu.readFile(r,"utf8")}catch{}o!==n&&await vu.writeFile(r,n,{mode:493}),await vu.chmod(r,493).catch(()=>{})}return e}function
|
|
47
|
+
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${ra}: ${t?.message||t}`)}}async function oe(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||xu()))try{n.paneTail=Go((await Pe(n.sessionName,{lines:t?_P:35})).slice(-IP))}catch{n.paneTail=null}(t||xu()||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 EP(n)}async function B0({limit:e=80}={}){try{return(await Li.readFile(ra,"utf8")).split(`
|
|
48
|
+
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Go(o.paneTail)),o}catch{return{raw:Go(r)}}})}catch{return[]}}var TP,dm,ra,P0,_P,IP,j0,yn=q(()=>{dt();TP=[/[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];dm=process.env.BOSTRAT_DEBUG_DIR||D0.join(AP.homedir(),".bostrat","debug"),ra=D0.join(dm,"handoff.jsonl"),P0=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;_P=40,IP=4e3;j0={DEBUG_DIR:dm,LOG_FILE:ra}});import vu from"node:fs/promises";import pm from"node:path";import $P from"node:os";async function F0(e=L0){await vu.mkdir(e,{recursive:!0});for(let t of OP){let n=t==="prepare-commit-msg"?MP:t==="post-commit"?CP:`${fm}exit 0
|
|
49
|
+
`,r=pm.join(e,t),o=null;try{o=await vu.readFile(r,"utf8")}catch{}o!==n&&await vu.writeFile(r,n,{mode:493}),await vu.chmod(r,493).catch(()=>{})}return e}function U0({nodeUrl:e,dir:t=L0,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",`${Fi}=${e}`]}function H0(e=process.env){return e.BOSTRAT_NODE_URL||`http://127.0.0.1:${e.PORT||3362}`}var Fi,RP,L0,OP,fm,MP,CP,mm=q(()=>{Gs();Fi="BOSTRAT_NODE_URL",RP=process.env.BOSTRAT_STATE_DIR||pm.join(process.env.HOME||$P.tmpdir(),".bostrat"),L0=pm.join(RP,"git-hooks"),OP=["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"],fm=`#!/bin/sh
|
|
50
50
|
# bostrat git hook shim (agent/src/git-hooks.js) \u2014 chains to the repo's own hook,
|
|
51
51
|
# then stamps/reports for rework attribution when running inside a bostrat box.
|
|
52
52
|
hook=$(basename "$0")
|
|
@@ -61,75 +61,75 @@ esac
|
|
|
61
61
|
if [ -x "$orig/$hook" ] && [ "$orig/$hook" != "$0" ]; then
|
|
62
62
|
"$orig/$hook" "$@" || exit $?
|
|
63
63
|
fi
|
|
64
|
-
|
|
64
|
+
`,MP=`${fm}
|
|
65
65
|
# \u2500\u2500 bostrat: append provenance trailers (Layer 0). Skipped for merge commits
|
|
66
66
|
# (nobody "wrote" a merge) and outside a box (no token / no node url).
|
|
67
|
-
[ -z "$${
|
|
67
|
+
[ -z "$${lr}" ] && exit 0
|
|
68
68
|
[ -z "$${Fi}" ] && exit 0
|
|
69
69
|
[ "$2" = "merge" ] && exit 0
|
|
70
|
-
trailers=$(curl -sS -m 3 -H "${
|
|
70
|
+
trailers=$(curl -sS -m 3 -H "${ur}: $${lr}" "$${Fi}/api/git/trailers" 2>/dev/null) || exit 0
|
|
71
71
|
[ -z "$trailers" ] && exit 0
|
|
72
72
|
printf '%s\\n' "$trailers" | while IFS= read -r line; do
|
|
73
73
|
[ -n "$line" ] && git interpret-trailers --in-place --if-exists addIfDifferent --trailer "$line" "$1" >/dev/null 2>&1 || true
|
|
74
74
|
done
|
|
75
75
|
exit 0
|
|
76
|
-
`,
|
|
76
|
+
`,CP=`${fm}
|
|
77
77
|
# \u2500\u2500 bostrat: report the commit (code.committed + rework analysis, node-side).
|
|
78
|
-
[ -z "$${
|
|
78
|
+
[ -z "$${lr}" ] && exit 0
|
|
79
79
|
[ -z "$${Fi}" ] && exit 0
|
|
80
80
|
sha=$(git rev-parse HEAD 2>/dev/null) || exit 0
|
|
81
81
|
cwd=$(git rev-parse --show-toplevel 2>/dev/null)
|
|
82
82
|
printf '{"sha":"%s","cwd":"%s"}' "$sha" "$cwd" | curl -sS -m 5 -X POST "$${Fi}/api/git/committed" \\
|
|
83
|
-
-H 'content-type: application/json' -H "${
|
|
83
|
+
-H 'content-type: application/json' -H "${ur}: $${lr}" --data-binary @- >/dev/null 2>&1 || true
|
|
84
84
|
exit 0
|
|
85
|
-
`});import{execFile as
|
|
86
|
-
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,p,f,w]=r.split("|"),m=Number(i),
|
|
87
|
-
`).map(n=>n.trim()).includes(e)}catch{return!1}}function
|
|
88
|
-
`).map(n=>Number(n.trim())).filter(n=>Number.isFinite(n)&&n>0)}catch{return[]}}async function
|
|
89
|
-
`).filter(Boolean).length}catch{return 0}}async function zo(e,{cols:t=
|
|
90
|
-
`);for(let n=t.length-1;n>=0;n--){let r=t[n].trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a=(s.input_tokens||0)+(s.cache_creation_input_tokens||0)+(s.cache_read_input_tokens||0);if(a>0)return{occupancy:a,model:o.message.model||o.model||null}}return null}async function Vi(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=kn(e,t),o=!0,!r)return null}else if(n){if(r=vm(e),o=!1,!r)return null}else return null;let s;try{s=
|
|
91
|
-
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function zi(e){let t=[],n="",r=null,o=!1,s=()=>{if(o){if(/^\d*[<>]/.test(n)||n.startsWith(">")){n="",o=!1;return}t.push(n),n="",o=!1}};for(let a=0;a<e.length;a++){let i=e[a];if(r){i===r&&e[a-1]!=="\\"?r=null:n+=i,o=!0;continue}if(i==="'"||i==='"'){r=i,o=!0;continue}if(i==="<"&&e[a+1]==="<")return s(),t.push("<<"),t;if(/\s/.test(i)){s();continue}n+=i,o=!0}return s(),t}function Ji(e){let t=e;for(;t.length&&/^[A-Za-z_][A-Za-z0-9_]*=/.test(t[0]);)t=t.slice(1);for(;t.length&&(t[0]==="sudo"||t[0]==="time"||t[0]==="env");)t=t.slice(1);return t}function Le(e,t=new Set){let n=[];for(let r=1;r<e.length;r++){let o=e[r];if(o==="<<")break;if(
|
|
92
|
-
`,n);i!==-1&&(t=t.slice(0,i))}let r=
|
|
93
|
-
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function Yr(e,t=""){let n=t.trim();return n||Em(e)||ua(e)}function _m(e,t){if(e){if(Array.isArray(e)){for(let n of e)_m(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&Im.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))_m(n,t)}}}async function
|
|
94
|
-
`)){let a=s.trim();if(!a||a[0]!=="{")continue;let i;try{i=JSON.parse(a)}catch{continue}let c=i.message;if(!c)continue;let l=[];if(_m(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:Yr(d.command,d.description),ts:u})}return o.slice(-n)}function
|
|
95
|
-
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=t)break}return o}finally{await n.close()}}async function
|
|
96
|
-
`)){let f=p.trim();if(!f||f[0]!=="{")continue;let w;try{w=JSON.parse(f)}catch{continue}if((w.type==="turn_context"&&w.payload?.model||w.type==="session_meta"&&w.payload?.model)&&(i=w.payload.model),w.type==="event_msg"){let
|
|
97
|
-
`;else if(s==="t")n+=" ";else if(s==="r")n+="\r";else if(s==="u")if(e[r+1]==="{"){let a=e.indexOf("}",r+2);if(a===-1)return null;n+=String.fromCodePoint(parseInt(e.slice(r+2,a),16)),r=a}else n+=String.fromCharCode(parseInt(e.slice(r+1,r+5),16)),r+=4;else n+=s}else{if(o===t)return n;if(t==="`"&&o==="$"&&e[r+1]==="{")return null;n+=o}}return null}function
|
|
98
|
-
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type!=="response_item")continue;let u=l.payload;if(!u)continue;let d=[];if(u.type==="function_call"&&Yi.has(u.name)){let f;try{f=JSON.parse(u.arguments||"{}")}catch{continue}let w
|
|
99
|
-
`).trim():"";Yi=new Set(["exec_command","shell","exec"]),Om=new Set(["wait","write_stdin"]),Yo="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 Xi({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&
|
|
100
|
-
`).trim():""}function
|
|
101
|
-
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type==="attachment"){let
|
|
102
|
-
|
|
103
|
-
`):""}var
|
|
104
|
-
`);if(
|
|
105
|
-
`);for(let r=n.length-1;r>=0;r--)if(
|
|
106
|
-
`);return t}function
|
|
107
|
-
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(
|
|
108
|
-
`).pop()||"";return
|
|
109
|
-
`),o=r.findIndex(i=>
|
|
110
|
-
`).map(a=>a.trim()),o=r.findIndex(a=>/Press any key to log in/.test(a)),s=r.slice(0,o<0?r.length:o).filter(a=>a&&!/^Cursor Agent$/i.test(a)&&!/^v?\d{4}\.\d{2}\.\d{2}/.test(a)).join(" ");return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,answerMode:"keys",promptId:"cursor:signin-start",questions:[{question:(s?`${s} `:"")+"Cursor isn't signed in on this box \u2014 start the sign-in to get a login link you can open from any device.",header:"Sign in",multiSelect:!1,options:[{label:"Start sign-in",description:"Shows a login link (and a QR option) \u2014 your login goes to cursor.com, never through bostrat"}]}]}}function
|
|
111
|
-
`),o="",s=r.findIndex(a=>/https:\/\//.test(a));if(s>=0){o=r[s].slice(r[s].indexOf("https://")).trim();for(let a=s+1;a<r.length;a++){let i=r[a].trim();if(!i||!/^[A-Za-z0-9~%&=?/_.-]+$/.test(i))break;o+=i}}return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,promptId:`cursor:signin-link:${(o.match(/[?&]challenge=([\w-]+)/)||[])[1]?.slice(0,16)||o.slice(-24)||"pending"}`,questions:[{question:o?`Cursor is waiting for you to finish signing in: open ${o} on any device and log in. Your login goes straight to cursor.com \u2014 bostrat never sees it. This card clears once sign-in completes.`:"Cursor is waiting for you to finish signing in \u2014 open the login link shown in the Terminal tab. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function
|
|
112
|
-
`),n=-1;for(let c=t.length-1;c>=0;c--)if(t[c].trim()){n=c;break}if(n<0)return!1;let r=t[n];if(
|
|
113
|
-
`).filter(c=>/^\s*›/.test(c)),i=a.some(c=>/^\s*›\s+(?:Ask Codex to do anything|Implement \{feature\})\s*$/.test(c));return a.length>0&&!i&&/esc to interrupt|\bWorking \(/.test(s)}function
|
|
114
|
-
`),r=-1;for(let o=n.length-1;o>=0;o--)if(
|
|
115
|
-
`)}function
|
|
116
|
-
`))
|
|
117
|
-
`).slice(-
|
|
118
|
-
`);return
|
|
85
|
+
`});import{execFile as q0}from"node:child_process";import{promisify as NP}from"node:util";import Au from"node:fs";import{randomUUID as G0}from"node:crypto";function Ko(e){return new Promise(t=>setTimeout(t,e))}function Su(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 K0(e){return`/tmp/tmux-${e}/default`}function PP({explicit:e,legacyExists:t,runtimeDir:n,uid:r}){if(e)return e;let o=K0(r);return t?o:n?`${n}/bostrat/tmux-default`:o}function Hi(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${Ui}=1`]}function jP(){return BP.flatMap(e=>["-e",`${e}=`])}function Wi(e,t,n){let r=Vo(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 Vo(e){return/(^|\/|\s)claude(\s|$)/.test(String(e||""))}function zr(e){return/(^|\/|\s)claude(\s|$)/.test(e)?"claude":/cursor/.test(e)?"cursor":/codex/.test(e)?"codex":/gemini/.test(e)?"gemini":"agent"}function FP(e){let t=String(e||"").trim();return!t||/fake-|\.sh(\s|$)/.test(t)?!1:Vo(t)||/(^|\/|\s)codex(\s|$)/.test(t)||/cursor-agent/.test(t)||/(^|\/|\s)gemini(\s|$)/.test(t)}async function V0(e,{loginShell:t=!1,failOpen:n=!1}={}){let r=await z0(e,{loginShell:t});return r===Tu?!0:r===oa?!1:n}async function z0(e,{loginShell:t=!1,timeoutMs:n=hm}={}){if(!e)return oa;let r=t?process.env.SHELL||"/bin/sh":"sh",o=t?"-lic":"-c";try{return await Me(r,[o,'command -v "$1" >/dev/null 2>&1',"_",e],{timeout:n}),Tu}catch(s){return s?.killed||s?.signal?sa:s?.code===1||s?.code===127?oa:sa}}async function Eu(e,{retries:t=UP,sleep:n=HP,probe:r=z0}={}){let o=String(e||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:hm});for(let a=0;s===sa&&a<t.length;a++)await n(t[a]),s=await r(o,{timeoutMs:hm*2});return s}function se(...e){return["-S",qt,...e]}async function Jr(){if(!Au.existsSync(qt))return!1;try{return await Me(ne,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 Jr())return!0;try{Au.mkdirSync(qt.replace(/\/[^/]+$/,""),{recursive:!0}),await Me(ne,se("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await Jr()}function je(e){return typeof e=="string"&&WP.test(e)}async function bn(){if(!Au.existsSync(qt))return[];try{let e="#{session_name}|#{session_windows}|#{session_attached}|#{session_activity}|#{pane_current_path}|#{@bostrat-session}|#{@bostrat-title}|#{@bostrat-sid}|#{@bostrat-agent}|#{alternate_on}|#{session_created}",{stdout:t}=await Me(ne,se("list-sessions","-F",e)),n=Date.now();return t.trim().split(`
|
|
86
|
+
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,p,f,w]=r.split("|"),m=Number(i),y=Number(w);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(m)?n-m*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:d||null,agent:p||(l==="1"?"claude":null),alternateOn:f==="1",createdMs:Number.isFinite(y)&&y>0?y*1e3:null}}).filter(r=>!LP.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function xe(e){return(await bn()).find(n=>n.name===e)||null}function KP(e){if(typeof e!="string")return!1;let t=e.trim();return t.length>=1&&t.length<=GP&&qP.test(t)}function _u(e){return e.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function J0(e){try{let{stdout:t}=await Me(ne,se("list-sessions","-F","#{session_name}"));return t.split(`
|
|
87
|
+
`).map(n=>n.trim()).includes(e)}catch{return!1}}function JP(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 YP(e,t){let n=Date.now()+Gi(),r=JP(t),o=!1,s=0,a=0;for(;;){if(await Ko(VP),s+=1,!await J0(e))return;let i=await Pe(e,{lines:15}),c=!o&&zP.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 oe({flow:"spawn",phase:"launch-retry",sessionName:e,agent:zr(t),promptHead:t.slice(0,120)}).catch(()=>{}),await Me(ne,se("send-keys","-t",e,"C-u")),await Me(ne,se("send-keys","-t",e,"-l",t)),await Me(ne,se("send-keys","-t",e,"Enter"));continue}if(Date.now()>n){await oe({flow:"spawn",phase:"launch-failed",sessionName:e,agent:zr(t),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function Ln(e,t=DP,n={}){if(!KP(e))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=e.trim(),o=_u(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 ${qt} and auto-start failed \u2014 start one: ${ne} -S ${qt} new-session -d -s keeper "sleep 2147483647"`);if(await J0(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||wm,a=n.agent||zr(s),i=!!n.resumeSessionId,c=n.resumeSessionId||G0(),l=i?s:Wi(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),d=a==="claude"&&Vo(s),p=n.firstRun===!0;p||(d&&await Fr(t),a==="codex"&&await Ks(t),a==="cursor"&&await Vs(t),await h0(t),n.freshWorkspace!==!0&&await bu(t));let f=d&&!p?Xl():null,w=Mk(),m=["-e",`${lr}=${w}`];m.push(...U0({nodeUrl:H0()})),m.push(...jP()),f&&m.push("-e",`ANTHROPIC_API_KEY=${f}`),d&&m.push(...Hi());try{await Me(ne,se("new-session","-d","-s",o,"-c",t,...m))}catch(y){throw Su(f?Su(y,f):y,w)}return await zo(o),await Me(ne,se("set-option","-t",o,"@bostrat-session","1")),await Me(ne,se("set-option","-t",o,"@bostrat-title",r)),await Me(ne,se("set-option","-t",o,"@bostrat-sid",c)),await Me(ne,se("set-option","-t",o,"@bostrat-agent",a)),await Me(ne,se("send-keys","-t",o,l,"Enter")),FP(l)&&YP(o,l).catch(()=>{}),{name:o,title:r,cwd:t,sessionId:c,agent:a,pinned:u,hookToken:w}}async function Dt(e){if(!je(e))throw new Error("invalid session name");await Me(ne,se("kill-session","-t",e))}async function Y0(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 Me(ne,n)}catch{}}async function Pe(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 Me(ne,n);return String(r||"")}catch{return""}}async function X0(e){if(!je(e))return[];try{let{stdout:t}=await Me(ne,se("list-panes","-s","-t",e,"-F","#{pane_pid}"));return String(t||"").trim().split(`
|
|
88
|
+
`).map(n=>Number(n.trim())).filter(n=>Number.isFinite(n)&&n>0)}catch{return[]}}async function Z0(e){if(!je(e))return 0;try{let{stdout:t}=await Me(ne,se("list-clients","-t",e,"-F","1"));return String(t||"").trim().split(`
|
|
89
|
+
`).filter(Boolean).length}catch{return 0}}async function zo(e,{cols:t=QP,rows:n=eD}={}){if(!je(e))return!0;if(await Z0(e))return!1;try{let r=await Q0(e);if(r.cols===t&&r.rows===n)return!0;await Me(ne,se("resize-window","-t",e,"-x",String(t),"-y",String(n))),await Me(ne,se("set-window-option","-t",e,"window-size","latest")),await Ko(80)}catch{}return!0}async function tD(e){if(!je(e))return 0;try{let{stdout:t}=await Me(ne,se("display-message","-t",e,"-p","#{pane_pid}"));return Number(String(t||"").trim())||0}catch{return 0}}async function Fn(e){let t=await tD(e);if(!t)return!1;try{return await Me("pgrep",["-P",String(t)]),!1}catch(n){return n?.code===1}}async function Q0(e){if(!je(e))return{cols:0,rows:0};try{let{stdout:t}=await Me(ne,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 Iu(e,{cols:t=XP,rows:n=ZP}={}){if(je(e)&&!await Z0(e))try{let r=await Q0(e),o=Math.max(r.cols,t),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await Me(ne,se("resize-window","-t",e,"-x",String(o),"-y",String(s))),await Me(ne,se("set-window-option","-t",e,"window-size","latest")),await Ko(80))}catch{}}async function Gt(e){return await Iu(e),Pe(e,{lines:Ve})}async function Ge(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=>ex.has(s));for(let s=0;s<o.length;s++)await Me(ne,se("send-keys","-t",e,o[s])),n>0&&s<o.length-1&&await Ko(n);return oe({flow:"tmux",phase:"send-keys",sessionName:e,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function bm(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"&&ex.has(s.key)||typeof s.text=="string"&&s.text.length>0));for(let s=0;s<o.length;s++){let a=o[s];a.text!=null?await Me(ne,se("send-keys","-t",e,"-l",a.text)):await Me(ne,se("send-keys","-t",e,a.key)),n>0&&s<o.length-1&&await Ko(n)}return oe({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 aa(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 Me(ne,se("send-keys","-t",e,"-N",String(r),"BSpace")),oe({flow:"tmux",phase:"send-keys",sessionName:e,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function Ru(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 Me(ne,se("send-keys","-t",e,"-l",s)),n>0&&await Ko(n);oe({flow:"tmux",phase:"type",sessionName:e,kind:r||"type",textLen:o.length,textHead:String(t).slice(0,120)}).catch(()=>{})}function nD(e,t){return new Promise((n,r)=>{let o=q0(ne,se(...e),(s,a,i)=>{s?r(s):n({stdout:a,stderr:i})});o.stdin.on("error",()=>{}),o.stdin.end(t)})}async function rD(e,t){let n=`bostrat-${G0().slice(0,8)}`;await nD(["load-buffer","-b",n,"-"],t),await Me(ne,se("paste-buffer","-p","-d","-b",n,"-t",e))}async function it(e,t,{enter:n=!0,submitDelayMs:r=0,kind:o,paste:s=!1}={}){if(!je(e))throw new Error("invalid session name");if(typeof t=="string"&&t.length>0&&(s?await rD(e,t):await Me(ne,se("send-keys","-t",e,"-l",t))),n){let a=Number(r);Number.isFinite(a)&&a>0&&await Ko(a),await Me(ne,se("send-keys","-t",e,"Enter"))}oe({flow:"tmux",phase:"send",sessionName:e,kind:o||"text",textLen:typeof t=="string"?t.length:0,textHead:typeof t=="string"?t.slice(0,120):"",enter:n,submitDelayMs:r,...s?{paste:!0}:{}}).catch(()=>{})}var Me,ne,W0,qt,DP,wm,Ui,BP,gm,LP,Tu,oa,sa,hm,UP,HP,ym,WP,qP,GP,Gi,VP,zP,XP,ZP,Ve,QP,eD,ex,$u,dt=q(()=>{vi();Ql();eu();Ci();ku();yn();Gs();mm();Me=NP(q0);ne=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";W0=typeof process.getuid=="function"?process.getuid():0,qt=PP({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return Au.existsSync(K0(W0))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:W0}),DP=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",wm=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",Ui="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";BP=Object.freeze(["BOSTRAT_PLANE_URL","BOSTRAT_PLANE_TOKEN"]);gm="bostrat-usage-probe",LP=new Set([gm]);Tu="present",oa="absent",sa="unknown",hm=5e3,UP=[250,1e3,3e3],HP=e=>new Promise(t=>setTimeout(t,e));ym={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"};WP=/^[A-Za-z0-9_.-]{1,64}$/;qP=/^[A-Za-z0-9 ._↻-]+$/,GP=64;Gi=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,VP=1e3,zP=/not found|Unknown command/i;XP=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,ZP=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,Ve=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,QP=Number(process.env.BOSTRAT_BOX_COLS)||120,eD=Number(process.env.BOSTRAT_BOX_ROWS)||40;ex=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);$u=1600});import Ki from"node:fs";import lD from"node:fs/promises";import ia from"node:path";import uD from"node:os";function xm(e){return e.replace(/[^A-Za-z0-9]/g,"-")}function vm(e){let t=ia.join(km,xm(e)),n;try{n=Ki.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=ia.join(t,o);try{let{mtimeMs:a}=Ki.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function kn(e,t){if(!t)return null;let n=ia.join(km,xm(e),`${t}.jsonl`);return Ki.existsSync(n)?n:null}function ix(e){if(!e)return[];let t=ia.join(km,xm(e)),n;try{n=Ki.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=Ki.statSync(ia.join(t,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function Bt(e,t=131072){let n=await lD.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 pD(e){let t=e.split(`
|
|
90
|
+
`);for(let n=t.length-1;n>=0;n--){let r=t[n].trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a=(s.input_tokens||0)+(s.cache_creation_input_tokens||0)+(s.cache_read_input_tokens||0);if(a>0)return{occupancy:a,model:o.message.model||o.model||null}}return null}async function Vi(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=kn(e,t),o=!0,!r)return null}else if(n){if(r=vm(e),o=!1,!r)return null}else return null;let s;try{s=pD(await Bt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(ax,s.model),i=ax[s.model]||dD;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 ax,dD,km,xn=q(()=>{ax={"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},dD=2e5,km=process.env.BOSTRAT_CLAUDE_PROJECTS||ia.join(uD.homedir(),".claude","projects")});function fD(e){let t=[],n="",r=null;for(let o=0;o<e.length;o++){let s=e[o];if(r){n+=s,s===r&&e[o-1]!=="\\"&&(r=null);continue}if(s==="'"||s==='"'||s==="`"){r=s,n+=s;continue}if(s==="&"&&e[o+1]==="&"){t.push(n),n="",o++;continue}if(s==="|"&&e[o+1]==="|"){t.push(n),n="",o++;continue}if(s==="|"||s===";"||s===`
|
|
91
|
+
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function zi(e){let t=[],n="",r=null,o=!1,s=()=>{if(o){if(/^\d*[<>]/.test(n)||n.startsWith(">")){n="",o=!1;return}t.push(n),n="",o=!1}};for(let a=0;a<e.length;a++){let i=e[a];if(r){i===r&&e[a-1]!=="\\"?r=null:n+=i,o=!0;continue}if(i==="'"||i==='"'){r=i,o=!0;continue}if(i==="<"&&e[a+1]==="<")return s(),t.push("<<"),t;if(/\s/.test(i)){s();continue}n+=i,o=!0}return s(),t}function Ji(e){let t=e;for(;t.length&&/^[A-Za-z_][A-Za-z0-9_]*=/.test(t[0]);)t=t.slice(1);for(;t.length&&(t[0]==="sudo"||t[0]==="time"||t[0]==="env");)t=t.slice(1);return t}function Le(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 dx(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 wD(e){let t=dx(e),n=kt(Le(e).find(r=>!/^\d+(,\d+)?p$/.test(r)));return t&&n?t.from===t.to?`Read line ${t.from} of ${ze(n)}`:`Read lines ${t.from}\u2013${t.to} of ${ze(n)}`:e.includes("-i")&&n?`Edit ${ze(n)}`:null}function Sm(e){if(e.includes("--files")){let s=kt(Le(e,cx)[0]);return s&&s!=="."?`List files in ${ca(s)}`:"List files"}let t=Le(e,cx),n=t[0];if(!n)return null;let r=kt(t.slice(1).find(s=>s!==".")),o=la(n,40);return r?`Search for \u201C${o}\u201D in ${ca(r)}`:`Search for \u201C${o}\u201D`}function Am(e){let t=Le(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 Tm(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=Le(e)[0];return n?`Run ${ze(n)}`:null}function gD(e){let t=Le(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 lx(e){return e.find(t=>/^https?:\/\//.test(t))||null}function kD(e){let t=Ji(zi(e));if(!t.length)return null;if(/^(ba|z)?sh$/.test(ze(t[0]))&&(t[1]==="-lc"||t[1]==="-c")&&t[2])return Em(t[2]);let n=ze(t[0]);if(bD.has(n))return null;if(t.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=yD[n];return r?r(t.map((o,s)=>s===0?n:o)):null}function Em(e){let t=String(e||"").trim();if(!t)return null;let n=t.indexOf("<<");if(n!==-1){let i=t.indexOf(`
|
|
92
|
+
`,n);i!==-1&&(t=t.slice(0,i))}let r=fD(t);for(let i=0;i<r.length-1;i++){let c=Ji(zi(r[i])),l=Ji(zi(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(ze(c[0]))&&ze(l[0])==="sed"){let u=dx(l),d=Le(c)[0];if(u&&d)return la(u.from===u.to?`Read line ${u.from} of ${ze(d)}`:`Read lines ${u.from}\u2013${u.to} of ${ze(d)}`,88)}}let o=[];for(let i of r){let c=kD(i);c&&o.push({d:c,head:ze(Ji(zi(i))[0]||""),sub:Le(Ji(zi(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":la(s?s.d:o[0].d,88)}var la,kt,ze,ca,ux,cx,mD,hD,yD,bD,px=q(()=>{la=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,kt=e=>e&&!/[$`]/.test(e)?e:null,ze=e=>e.replace(/\/+$/,"").split("/").pop()||e,ca=e=>{let t=e.replace(/\/+$/,"").split("/").filter(Boolean);return t.length>3?t.slice(-2).join("/"):e},ux=e=>e.startsWith("-"),cx=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);mD={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 ${ze(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"},hD={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"};yD={git:e=>{let t=Le(e,new Set(["-C","-c"])),n=mD[t[0]];return n?n(t,e):null},gh:e=>{let t=Le(e),n=hD[t[0]];return n?n(t):null},sed:wD,rg:Sm,grep:Sm,egrep:Sm,nl:e=>{let t=kt(Le(e)[0]);return t?`Read ${ze(t)}`:null},cat:e=>{let t=kt(Le(e)[0]);return t?`Read ${ze(t)}`:null},head:e=>{let t=kt(Le(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the start of ${ze(t)}`:null},tail:e=>{let t=kt(Le(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the end of ${ze(t)}`:null},wc:e=>{let t=kt(Le(e)[0]);return t?`Count lines in ${ze(t)}`:"Count input lines"},ls:e=>{let t=kt(Le(e)[0]);return t&&t!=="."?`List files in ${ca(t)}`:"List files"},find:e=>kt(e[1])&&!ux(e[1])&&e[1]!=="."?`Find files in ${ca(e[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:e=>{let t=kt(Le(e)[0]);return t?`Create directory ${ca(t)}`:null},touch:e=>{let t=kt(Le(e)[0]);return t?`Create ${ze(t)}`:null},rm:e=>{let t=kt(Le(e)[0]);return t?`Delete ${ca(t)}`:null},mv:e=>{let t=kt(Le(e)[0]);return t?`Move ${ze(t)}`:null},cp:e=>{let t=kt(Le(e)[0]);return t?`Copy ${ze(t)}`:null},chmod:e=>{let t=kt(Le(e)[1]);return t?`Change permissions on ${ze(t)}`:null},diff:e=>{let t=Le(e);return t.length>=2?`Compare ${ze(t[0])} and ${ze(t[1])}`:null},curl:e=>{let t=lx(e);return t?`Fetch ${la(t,56)}`:"Fetch a URL"},wget:e=>{let t=lx(e);return t?`Fetch ${la(t,56)}`:"Fetch a URL"},npm:Am,pnpm:Am,yarn:Am,npx:e=>{let t=Le(e)[0];return t?`Run ${t}`:null},node:e=>Tm(e,"Node"),python:e=>Tm(e,"Python"),python3:e=>Tm(e,"Python"),make:e=>{let t=Le(e)[0];return t?`Run make ${t}`:"Run make"},systemctl:gD,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=Le(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=Le(e)[0];return t?`Locate ${t}`:null},command:e=>e[1]==="-v"&&e[2]?`Locate ${e[2]}`:null,fly:e=>{let t=Le(e)[0];return t==="deploy"?"Deploy to Fly":t?`Run fly ${t}`:null}},bD=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"])});function ua(e){let t=e.split(`
|
|
93
|
+
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function Yr(e,t=""){let n=t.trim();return n||Em(e)||ua(e)}function _m(e,t){if(e){if(Array.isArray(e)){for(let n of e)_m(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&Im.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))_m(n,t)}}}async function fx(e,{maxBytes:t=1048576,limit:n=400}={}){let r;try{r=await Bt(e,t)}catch{return[]}let o=[];for(let s of r.split(`
|
|
94
|
+
`)){let a=s.trim();if(!a||a[0]!=="{")continue;let i;try{i=JSON.parse(a)}catch{continue}let c=i.message;if(!c)continue;let l=[];if(_m(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:Yr(d.command,d.description),ts:u})}return o.slice(-n)}function mx(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(...xD(i,r.get(a)||[]));return s.sort((a,i)=>(a.ts||0)-(i.ts||0))}function xD(e,t){let n=[...t].sort((l,u)=>(l.ts||0)-(u.ts||0)),r=[...e].sort((l,u)=>(l.ts||0)-(u.ts||0)),o=[],s=0,a=0,i=l=>{let u=l.n??a+1;return u>a&&(a=u),u};for(let l of n){let u=r.findIndex((w,m)=>m>=s&&(w.ts||0)>(l.ts||0)),d=u===-1?r.length:u,p=r.slice(s,d);if(s=d,!p.length)continue;let f=p.map(w=>({...w,n:i(w)}));o.push({id:`batch-${l.ts}-${l.box}`,anchorTs:l.ts,anchorBox:l.box,agent:l.agent,boxRole:l.boxRole,commands:f,ts:Math.max(...f.map(w=>w.ts||0)),pending:!1})}let c=r.slice(s);if(c.length){let l=c.map(u=>({...u,n:i(u)}));o.push({id:`batch-pending-${l[0]?.box||"lead"}`,anchorTs:null,anchorBox:l[l.length-1]?.box??null,agent:l[l.length-1]?.agent??"claude",boxRole:l[l.length-1]?.boxRole??"lead",commands:l,ts:Math.max(...l.map(u=>u.ts||0)),pending:!0})}return o}var Im,da=q(()=>{xn();px();Im=new Set(["Bash"])});import hx from"node:fs";import Rm from"node:fs/promises";import $m from"node:path";import vD from"node:os";async function wx(e){try{return await Rm.realpath(e)}catch{return e}}function AD(e=SD){let t=[],n=r=>{let o;try{o=hx.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=$m.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:hx.statSync(a).mtimeMs})}catch{}}}};return n(e),t.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function TD(e,t=1048576){let n;try{n=await Rm.open(e,"r")}catch{return""}try{let r=Buffer.alloc(Math.min(t,65536)),o="",s=0;for(;;){let{bytesRead:a}=await n.read(r,0,r.length,s);if(!a)break;o+=r.toString("utf8",0,a);let i=o.indexOf(`
|
|
95
|
+
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=t)break}return o}finally{await n.close()}}async function ED(e){let t=await TD(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 Jo(e,t){if(!e&&!t)return null;let n=AD();if(t){let o=n.find(s=>s.id===t);return o||null}let r=await wx(e);for(let o of n){let s=await ED(o.file);if(s&&(s===e||await wx(s)===r))return o}return null}async function gx(e,t,{limit:n=80,maxBytes:r=524288}={}){let o=await Jo(e,t);if(!o)return[];let s;try{s=await Bt(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,d=null;for(let p of s.split(`
|
|
96
|
+
`)){let f=p.trim();if(!f||f[0]!=="{")continue;let w;try{w=JSON.parse(f)}catch{continue}if((w.type==="turn_context"&&w.payload?.model||w.type==="session_meta"&&w.payload?.model)&&(i=w.payload.model),w.type==="event_msg"){let 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:Yo,sourceId:w.payload?.turn_id||d||void 0,ts:w.timestamp?Date.parse(w.timestamp):null,model:i}));continue}if(w.type!=="response_item")continue;let m=w.payload;if(!m)continue;if((m.type==="function_call"||m.type==="custom_tool_call")&&m.name){u=!0,Om.has(m.name)||c.push(m.name);continue}if(m.type!=="message"||m.role!=="assistant")continue;u=!0;let y=ID(m.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 $D(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 RD(e){let t=e[0];if(t!=='"'&&t!=="'"&&t!=="`")return null;let n="";for(let r=1;r<e.length;r++){let o=e[r];if(o==="\\"){let s=e[++r];if(s===void 0)return null;if(s==="n")n+=`
|
|
97
|
+
`;else if(s==="t")n+=" ";else if(s==="r")n+="\r";else if(s==="u")if(e[r+1]==="{"){let a=e.indexOf("}",r+2);if(a===-1)return null;n+=String.fromCodePoint(parseInt(e.slice(r+2,a),16)),r=a}else n+=String.fromCharCode(parseInt(e.slice(r+1,r+5),16)),r+=4;else n+=s}else{if(o===t)return n;if(t==="`"&&o==="$"&&e[r+1]==="{")return null;n+=o}}return null}function OD(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=RD(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function yx(e,t,{limit:n=400,maxBytes:r=524288}={}){let o=await Jo(e,t);if(!o)return[];let s;try{s=await Bt(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
|
|
98
|
+
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type!=="response_item")continue;let u=l.payload;if(!u)continue;let d=[];if(u.type==="function_call"&&Yi.has(u.name)){let f;try{f=JSON.parse(u.arguments||"{}")}catch{continue}let w=$D(f);w&&(d=[w])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=OD(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:Yr(f),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function bx(e,t){let n=await Jo(e,t);if(!n)return null;try{return Math.round((await Rm.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var SD,_D,ID,Yi,Om,Yo,Xo=q(()=>{xn();da();SD=process.env.BOSTRAT_CODEX_SESSIONS||$m.join(process.env.CODEX_HOME||$m.join(vD.homedir(),".codex"),"sessions");_D=new Set(["output_text","text"]),ID=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&typeof t.text=="string"&&_D.has(t.type)).map(t=>t.text).join(`
|
|
99
|
+
`).trim():"";Yi=new Set(["exec_command","shell","exec"]),Om=new Set(["wait","write_stdin"]),Yo="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 Xi({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&kx.has(r))return kx.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 xx(e){if(!e||e.role!=="assistant")return null;if(e.apiError){let t=e.apiError;return{cls:t.cls||Xi({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===Yo?"other":Xi({text:t}),code:null,status:null,requestId:null}}return null}function vx(e,{codexFailedText:t=null}={}){let n=String(e?.text||"").trim();return n?e?.model==="<synthetic>"?/^No response requested\.$/.test(n)?null:{cls:Xi({text:n}),code:null,status:MD(n),requestId:null,text:n}:t&&n===t?{cls:"other",code:null,status:null,requestId:null,text:n}:null:null}function MD(e){let t=/^API Error:\s*(\d{3})\b/.exec(e);return t?Number(t[1]):null}var H8,kx,Cu=q(()=>{Xo();H8=Object.freeze(["entitlement","auth","rate_limit","overloaded","server","other"]),kx=new Map([["oauth_org_not_allowed","entitlement"],["permission_error","entitlement"],["billing_error","entitlement"],["authentication_error","auth"],["oauth_token_expired","auth"],["invalid_api_key","auth"],["rate_limit_error","rate_limit"],["overloaded_error","overloaded"],["server_error","server"],["api_error","server"],["internal_server_error","server"]])});function CD(e,t){return!e||!t?null:kn(e,t)}function ND(e){return typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&t.type==="text"&&typeof t.text=="string").map(t=>t.text).join(`
|
|
100
|
+
`).trim():""}function PD(e){if(!Array.isArray(e))return null;let t=e.filter(n=>n&&n.type==="tool_use").map(n=>n.name).filter(Boolean);return t.length?t:null}function DD(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&t.type==="tool_result")}function Mm(e){let t=[],n=e?.message?.content;if(!Array.isArray(n))return t;for(let r of n)if(r){if(r.type==="image"&&r.source?.type==="base64"&&r.source.data)t.push({mediaType:r.source.media_type||"image/png",data:r.source.data,toolUseId:null});else if(r.type==="tool_result"&&Array.isArray(r.content))for(let o of r.content)o?.type==="image"&&o.source?.type==="base64"&&o.source.data&&t.push({mediaType:o.source.media_type||"image/png",data:o.source.data,toolUseId:r.tool_use_id||null})}return t}function Sx(e,t){if(!e.uuid)return null;let n=Mm(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 Cm(e,{maxBytes:t=524288,limit:n=80}={}){let r;try{r=await Bt(e,t)}catch{return[]}let o=[],s=new Map,a=new Map;for(let i of r.split(`
|
|
101
|
+
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type==="attachment"){let 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"&&BD.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(DD(u.content)){let k=Sx(l,s);k&&o.push({role:"assistant",text:"",images:k,ts:l.timestamp?Date.parse(l.timestamp):null,model:null});continue}let p=ND(u.content),f=d==="assistant"?PD(u.content):null,w=d==="user"?Sx(l,s):null;if(!p&&!f&&!w)continue;let m=d==="user"&&l.isMeta&&typeof l.sourceToolUseID=="string"&&l.sourceToolUseID?{toolUseId:l.sourceToolUseID,skill:a.get(l.sourceToolUseID)??null}:null,y=d==="assistant"&&l.isApiErrorMessage?{code:typeof l.error=="string"?l.error:null,status:Number(l.apiErrorStatus)||null,requestId:l.requestId||null,cls:Xi({error:l.error,status:l.apiErrorStatus,text:p})}:null;o.push({role:d,text:p,tools:f||void 0,images:w||void 0,injected:m||void 0,ts:l.timestamp?Date.parse(l.timestamp):null,model:u.model||null,apiError:y||void 0})}return o.slice(-n)}async function Ax(e,t,{turns:n=16}={}){let r=CD(e,t);if(!r)return"";let o=await Cm(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}:** ${jD(s.text||c,700)} ${i}`.trim()}).join(`
|
|
102
|
+
|
|
103
|
+
`):""}var BD,jD,Nu=q(()=>{Cu();xn();BD=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;jD=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e});import LD from"node:fs/promises";import{execFile as FD}from"node:child_process";import{promisify as UD}from"node:util";function Ix(e){Pm=typeof e=="function"?e:null}function Dm(e){let t=String(e||""),n=t.indexOf("/lib/node_modules/");return n>0?t.slice(0,n):null}function $x({sessionName:e,from:t,to:n}={}){let r=String(n||"").trim();!r||Tx.has(r)||(Tx.add(r),Ex=Ex.then(()=>WD({sessionName:e,from:t,to:r})).catch(()=>{}))}async function WD({sessionName:e,from:t,to:n}){await oe({flow:"codex-update",phase:"start",sessionName:e,from:t,to:n}).catch(()=>{});try{let r=(await Nm("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await LD.realpath(r),s=Dm(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await Nm("npm",["install","-g","@openai/codex"],{env:a,timeout:HD});let i=(await Nm(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await oe({flow:"codex-update",phase:"done",sessionName:e,from:t,to:n,version:i}).catch(()=>{}),await _x({sessionName:e,from:t,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await oe({flow:"codex-update",phase:"failed",sessionName:e,from:t,to:n,error:o}).catch(()=>{}),await _x({sessionName:e,from:t,to:n,status:"failed",error:o})}}async function _x(e){if(Pm)try{await Pm(e)}catch{}}var Nm,HD,Tx,Ex,Pm,Pu=q(()=>{yn();Nm=UD(FD),HD=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,Tx=new Set,Ex=Promise.resolve(),Pm=null});function GD(e,{provider:t,box:n}){let r=String(e).replace(/\r/g,"").split(`
|
|
104
|
+
`);if(!Rx.test(e))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(qD);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"&&Rx.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 Xr(e){let t=String(e??"").replace(/\r/g,""),n=t.split(`
|
|
105
|
+
`);for(let r=n.length-1;r>=0;r--)if(Cx.test(n[r]))return n.slice(r).join(`
|
|
106
|
+
`);return t}function KD(e){let t=String(e).trim();if(!t)return!1;let n=(t.match(/[A-Za-z]/g)||[]).length;return n>=3&&n/t.length>=.5}function VD(e){return/device code/i.test(e)?"works from the device you're reading this on":/sign in with chatgpt/i.test(e)?"opens a browser link on the node itself \u2014 can't finish from another device":""}function zD(e,t=""){let n=Xr(e);if(Zi.test(n)||!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n))return null;let r=n.split(`
|
|
107
|
+
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(Ox);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(Ox.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&&!Cx.test(r[l])&&KD(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=VD(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function XD(e,t=""){let n=Xr(e);if(Zi.test(n)||!JD.test(n))return null;let r=n.trimEnd().split(`
|
|
108
|
+
`).pop()||"";return YD.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 QD(e,t=""){let n=Xr(e);if(Zi.test(n)||!Mx.test(n))return null;let r=n.split(`
|
|
109
|
+
`),o=r.findIndex(i=>Mx.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(ZD);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 e1(e,t=""){let n=Xr(e);return Zi.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 t1(e,t=""){let n=Xr(e);return Zi.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 n1(e,t=""){let n=String(e);if(!/Press any key to log in/.test(n)||Bm.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
110
|
+
`).map(a=>a.trim()),o=r.findIndex(a=>/Press any key to log in/.test(a)),s=r.slice(0,o<0?r.length:o).filter(a=>a&&!/^Cursor Agent$/i.test(a)&&!/^v?\d{4}\.\d{2}\.\d{2}/.test(a)).join(" ");return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,answerMode:"keys",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 o1(e,t=""){let n=String(e);if(!r1.test(n)||Bm.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
111
|
+
`),o="",s=r.findIndex(a=>/https:\/\//.test(a));if(s>=0){o=r[s].slice(r[s].indexOf("https://")).trim();for(let a=s+1;a<r.length;a++){let i=r[a].trim();if(!i||!/^[A-Za-z0-9~%&=?/_.-]+$/.test(i))break;o+=i}}return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,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 s1(e,t=""){let n=String(e);return!/Scan this QR code to log in/.test(n)||Bm.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 Nx(e,t,n=""){if(e!=="cursor"&&e!=="codex")return null;if(e==="codex"){let r=XD(t,n)||QD(t,n)||e1(t,n)||t1(t,n)||zD(t,n);if(r)return r}if(e==="cursor"){let r=o1(t,n)||s1(t,n)||n1(t,n);if(r)return r}return GD(t,{provider:e,box:n})}function Px(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 qD,Rx,Zi,Cx,Ox,JD,YD,Mx,ZD,Bm,r1,Du=q(()=>{qD=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,Rx=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;Zi=/(^|\n)\s*›/,Cx=/^\s*Welcome to Codex\b|^\s*(?:│\s*)?>_ OpenAI Codex\b/;Ox=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;JD=/Successfully logged in\./,YD=/^\s*Press Enter to continue\.\s*$/;Mx=/Enter this one-time code/,ZD=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;Bm=/Run Everything|Add a follow-up|Plan, search, build anything/;r1=/click this link to log in|Signing in with the browser/});function l1(e){let t=String(e||"").replace(/\r/g,"").split(`
|
|
112
|
+
`),n=-1;for(let c=t.length-1;c>=0;c--)if(t[c].trim()){n=c;break}if(n<0)return!1;let r=t[n];if(i1.test(r)||c1.test(r)){for(let c=n-1;c>=0&&n-c<=64;c--)if(/^\s*›/.test(t[c]))return!0}let s=String(e||""),a=s.split(`
|
|
113
|
+
`).filter(c=>/^\s*›/.test(c)),i=a.some(c=>/^\s*›\s+(?:Ask Codex to do anything|Implement \{feature\})\s*$/.test(c));return a.length>0&&!i&&/esc to interrupt|\bWorking \(/.test(s)}function m1(e){let t=String(e||"");if(!et.codex.block.test(t))return null;let n=Xr(t);if(!et.codex.block.test(n))return null;let r=n.match(f1);return{from:r?.[1]||null,to:r?.[2]||null}}function Un(e){return new Promise(t=>setTimeout(t,e))}function jm(e,t){let n=Date.now(),r=h1();for(let[s,a]of ju)n-a>=r&&ju.delete(s);let o=`${e}::${t}`;return ju.has(o)?!1:(ju.set(o,n),!0)}function Lx(e,t){if(e?.busyScope!=="footer")return t;let n=String(t||"").split(`
|
|
114
|
+
`),r=-1;for(let o=n.length-1;o>=0;o--)if(jx.test(n[o])){r=o;break}return r<0?t:n.slice(r+1).join(`
|
|
115
|
+
`)}function g1(e){let t=0;for(let n of String(e).split(`
|
|
116
|
+
`))jx.test(n)&&(t=Math.max(t,n.trimEnd().length));return t}function Qi(e,t){let n=et[e];if(!n?.busy)return{state:"idle",reason:null};if(Fm(e,t))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(t))return{state:"idle",reason:null};if(n.busy.test(Lx(n,t)))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(t))return{state:"busy",reason:null};let r=g1(t);return e==="claude"&&r>0&&r<w1?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function Lm(e,t){return Qi(e,t).state}function Lu(e,t){if(e!=="claude")return!1;let n=String(t||"").split(`
|
|
117
|
+
`).slice(-b1).join(`
|
|
118
|
+
`);return y1.test(n)}function fa(e,t){return Lm(e,t)==="busy"}function ec(e,t){return Lm(e,t)!=="idle"}function Zr(e,t){let n=et[e];return n?.up?n.up.test(t):!0}function k1(e,t){let n=et[e];return n?Fm(e,t)?!1:n.up.test(t)&&Lm(e,t)==="idle":!0}function Fm(e,t){let n=et[e];if(!n)return!1;let r=e==="codex"?Xr(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 Um(e,t){let n=String(t||"").trim();if(!n)return e;let r=String(e||"").split(`
|
|
119
119
|
`);for(let o=r.length-1;o>=0;o--)if(r[o].includes(n))return r.slice(o+1).join(`
|
|
120
|
-
`);return e}function
|
|
121
|
-
`)?!0:Buffer.byteLength(e,"utf8")>=
|
|
122
|
-
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(
|
|
123
|
-
`).map(o=>o.replace(/\s+$/,"")),n=-1;for(let o=t.length-1;o>=0;o--)if(t[o]){(
|
|
124
|
-
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return cc(r)}function
|
|
125
|
-
${o}`.trim()||t.slice(0,2e3)}function
|
|
126
|
-
`):""}async function
|
|
120
|
+
`);return e}function Fx(e){let t=String(e||"").trim();return t?t.slice(0,32):null}function Dx(e){let t=new Error(`${e} isn't running in this box \u2014 its pane is sitting at a bare shell (the launch command was likely swallowed at shell startup, or the CLI exited). Relaunch it from the Terminal tab, or stop and reopen the box, then send again.`);return t.code="cli-not-running",t}async function Bx(e,t,n,{bailOnDialog:r=!1,launchEcho:o=null,onReady:s}={}){let a=Date.now()+n,i=0,c=null;for(await zo(e).catch(()=>{});;){let l=Um(await Pe(e),o);if(k1(t,l))return await s?.(),await Un(pa),"up";if(et[t]?.up&&!et[t].up.test(l))if(await Fn(e)){if(c??=Date.now(),Date.now()-c>=Gi()){let u=Dx(t);throw oe({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,idleMs:Date.now()-c,error:u.message}).catch(()=>{}),u}}else c=null;else c=null;if(r&&t==="claude"&&p1.test(l)&&!et.claude.up.test(l))return oe({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"resume-confirm"}).catch(()=>{}),"dialog-bail";if(t==="codex"&&d1.test(l)&&!et.codex.up.test(l)&&i<3){if(!jm(e,"codex-trust")){await Un(Bu);continue}i+=1,await oe({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"codex-trust",attempt:i}).catch(()=>{}),await it(e,"",{enter:!0,kind:"codex-trust-accept"}),await Un(pa);continue}if(t==="cursor"&&u1.test(l)&&!et.cursor.up.test(l)&&i<3){if(!jm(e,"cursor-trust")){await Un(Bu);continue}i+=1,await oe({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i}).catch(()=>{}),await it(e,"a",{enter:!1,kind:"cursor-trust-accept"}),await Un(pa);continue}if(t==="codex"&&i<3){let u=m1(l);if(u){if(!jm(e,`codex-update:${u.from}->${u.to}`)){await Un(Bu);continue}i+=1,await oe({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i,update:`${u.from} -> ${u.to}`}).catch(()=>{}),await it(e,"3",{enter:!1,kind:"codex-update-skip"}),$x({sessionName:e,from:u.from,to:u.to}),await Un(pa);continue}}if(Date.now()>a){if(Fm(t,l)){let u=`${t} is blocked on an interactive prompt`;throw t==="codex"?u=et.codex.auth.test(l)?"codex isn't signed in on this box \u2014 answer the Sign in card in chat (Device Code works from any device) or finish signing in from the Terminal tab, then send again":"codex is stuck on its update prompt (auto-skip didn't clear it) \u2014 open the terminal and choose Skip, then send again":t==="cursor"&&et.cursor.auth.test(l)?u="cursor isn't signed in on this box \u2014 answer the Sign in card in chat (the login link works from any device) or finish signing in from the Terminal tab, then send again":t==="claude"&&et.claude.auth.test(l)?u="claude isn't signed in on this box \u2014 answer the setup cards in chat (or finish signing in from the Terminal tab), then send again":t==="claude"&&et.claude.block.test(l)&&(u='claude is waiting on its "Press Enter to continue" screen \u2014 answer the Continue card in chat (or press Enter in the Terminal tab), then send again'),oe({flow:"agent-ready",phase:"blocked",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:u}).catch(()=>{}),new Error(u)}if(await Fn(e)){let u=Dx(t);throw oe({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:u.message}).catch(()=>{}),u}return console.warn(`[agent-ready] ${t} readiness unconfirmed in ${e} after ${n}ms \u2014 driving the box anyway.`),oe({flow:"agent-ready",phase:"timeout",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,paneBlocked:!!et[t]?.block?.test(l),paneUp:!!et[t]?.up?.test(l),paneBusy:!!et[t]?.busy?.test(Lx(et[t],l))}).catch(()=>{}),await Un(pa),"timeout"}await Un(Bu)}}function tc({agent:e,agentCmd:t}={}){let n=t?zr(t):zr(e||"");return{label:n,realClaude:t?Vo(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 Ux({agent:e,agentCmd:t}={}){let{realClaude:n,realCodex:r,realCursor:o}=tc({agent:e,agentCmd:t});return n||r||o||process.env.BOSTRAT_FAKE_POLL_READY==="1"}function Zo({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o,onBlocked:s}={}){let{realCodex:a,realCursor:i}=tc({agent:n,agentCmd:r});return!t||!a&&!i?null:fr({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o}).then(()=>{},c=>{try{x1(c,{onBlocked:s,agent:n,sessionName:t})}catch{}})}function x1(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 fr({cwd:e,sessionId:t,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=a1(),bailOnDialog:a=!1,launchEcho:i=null,onReady:c}={}){let{label:l,realClaude:u,realCodex:d,realCursor:p}=tc({agent:r,agentCmd:o});if(u&&e&&await Fr(e),d&&e&&await Ks(e),p&&e&&await Vs(e),(u||d||p)&&n)return await Bx(n,l,s,{bailOnDialog:a,launchEcho:i,onReady:c});let f=et[r]?r:et[l]?l:null;return process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&f?await Bx(n,f,s,{launchEcho:i}):(await Un(pa),"settle")}var a1,Bu,pa,i1,c1,et,u1,d1,p1,f1,h1,ju,w1,jx,y1,b1,Qo=q(()=>{dt();vi();Ql();eu();Pu();Du();yn();a1=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Bu=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,pa=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,i1=/^\s{0,4}[^\s›•■◦╭╰│]\S*(?: \S+){0,3} · \S/,c1=/^\s*tab to queue message\b/;et={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,busyScope:"footer",busyAlt:/^[·✢✳✶✻✽✺✹✸✷]\s+[A-Z][\w '’-]{1,40}…/m,auth:/Select login method:|Paste code here if prompted|OAuth error:|Choose the text style that looks best|Quick safety check: Is this a project you created/,block:/Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/m},codex:{up:{test:l1},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/}},u1=/Trust this workspace|\[a\]\s*Trust/,d1=/Do you trust the contents of this directory\?[\s\S]*›\s*1\. Yes, continue[\s\S]*Press enter to continue/,p1=/Resume from summary/i,f1=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;h1=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,ju=new Map;w1=68,jx=/^\s*[─━]{10,}(?: [^─━\n]+ [─━]+)?\s*$/;y1=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,b1=15});function T1(e){return typeof e!="string"||!e?!1:e.includes(`
|
|
121
|
+
`)?!0:Buffer.byteLength(e,"utf8")>=A1}function Hn(e,t){return e==="codex"?{enter:!0,submitDelayMs:v1,...T1(t)?{paste:!0}:{}}:e==="claude"?{enter:!0,submitDelayMs:S1}:{enter:!0}}var v1,S1,A1,nc=q(()=>{v1=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),S1=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),A1=(()=>{let e=Number(process.env.BOSTRAT_CODEX_PASTE_MIN_BYTES??512);return Number.isFinite(e)&&e>=0?e:512})()});function rc(e,t){if(e==="codex")return R1(t);if(e!=="claude")return null;let n=String(t).replace(/\r/g,"").split(`
|
|
122
|
+
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(Hx.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(Hx.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(E1);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 R1(e){let t=String(e).replace(/\r/g,"").split(`
|
|
123
|
+
`).map(o=>o.replace(/\s+$/,"")),n=-1;for(let o=t.length-1;o>=0;o--)if(t[o]){(_1.test(t[o])||I1.test(t[o]))&&!Hm.test(t[o])?n=o:Hm.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(Hm);if(a){r.unshift(a[1]);let i=r.map(c=>c.trim()).filter(Boolean);return i.length?i.join(" "):null}if($1.test(s))return null;r.unshift(s)}return null}function Wm(e,t){let n=String(t||"");if(!/\[Pasted Content \d+ chars\]/.test(n))return!1;let r=0,o=Ue(n.replace(O1,(i,c)=>(r+=Number(c)," "))),s=[...String(e||"")].length;return o&&!Ue(e).includes(o)?!1:r+o.length<=s+Math.max(4,Math.round(s*.05))}function Ue(e){return String(e||"").replace(/\s+/g," ").trim()}function Wx(e,t,{slackMs:n=2e4}={}){let r=Ue(t?.text);if(!r||typeof t?.at!="number")return!1;let o=Array.isArray(e)?e:[],s=o.findIndex(a=>a?.role==="user"&&typeof a.ts=="number"&&a.ts>=t.at-n&&Ue(a.text).includes(r));return s>=0&&s<o.length-1}function qx(e,t,n,{busy:r=!1}={}){let o=rc(e,n);return o&&Fu(t,o)||o&&e==="codex"&&Wm(t,o)?"stuck":r&&e!=="codex"||o?"submitted":"unknown"}function Fu(e,t){let n=Ue(e),r=Ue(t).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var Hx,E1,Hm,_1,I1,$1,O1,oc=q(()=>{Hx=/^\s*─{8,}(?: [^─\n]+ ─+)?\s*$/,E1=/^❯[ ]?(.*)$/,Hm=/^\s*›[ ]?(.*)$/,_1=/\S+ · \S/,I1=/^\s*(?:\? for shortcuts|tab to queue message)\b/,$1=/^\s*(?:[•■◦✶✻⚠]|╭|╰|│)/;O1=/\[Pasted Content (\d+) chars\](?: #\d+)?/g});async function ac({sessionName:e,agent:t,agentCmd:n=null,text:r,trackId:o=null,flow:s="say",stats:a=null},i={}){let c=(i.now||Date.now)(),l=(D,j,A={})=>(a&&typeof a=="object"&&Object.assign(a,{verdict:D,retries:A.retries??0,ms:(i.now||Date.now)()-c,sawUp:!!A.sawUp,reason:A.reason??null}),j);if(!M1.has(t))return l("skipped",!0);let u=i.waitForUp??Ux({agent:t,agentCmd:n}),d=i.capture||(D=>Pe(D,{lines:Ve})),p=i.pressEnter||(D=>Ge(D,["Enter"],{kind:"submit-retry"})),f=i.sleep||D1,w=i.now||Date.now,m=i.log||(D=>oe(D).catch(()=>{})),y=i.maxMs??P1(),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(sc[0]);;){let D=await d(e).catch(()=>""),j=Zr(t,D),A=fa(t,D),_=qx(t,r,D,{busy:A});if(_==="submitted")return l("submitted",!0,{retries:x,sawUp:!0});if(u&&(!j||_==="stuck"&&A)){if(w()>=k)return _==="stuck"?(m({...g,phase:"submit-failed",reason:"stuck-at-deadline",retries:x}),l("failed",!1,{retries:x,sawUp:M,reason:"stuck-at-deadline"})):(m({...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(C1);continue}if(M=!0,_==="stuck"){if(x>=N1)return m({...g,phase:"submit-failed",retries:x}),l("failed",!1,{retries:x,sawUp:!0,reason:"stuck"});x+=1,S=0,$=0,await p(e),m({...g,phase:"submit-retry",attempt:x}),await f(sc[0]);continue}if(S+=1,S>=sc.length||w()>=k)return l("assumed",!0,{retries:x,sawUp:!0});$+=1,await f(sc[Math.min($,sc.length-1)])}}var M1,sc,C1,N1,P1,D1,qm=q(()=>{dt();Qo();oc();yn();M1=new Set(["claude","codex"]),sc=[250,450,700],C1=500,N1=2,P1=()=>{let e=Number(process.env.BOSTRAT_SUBMIT_VERIFY_MAX_MS);return Number.isFinite(e)&&e>0?e:3e4},D1=e=>new Promise(t=>setTimeout(t,e))});function Gx(e){return String(e||"").replace(/[\u2018\u2019\u201A\u02BC\u0060\u00B4]/g,"'").replace(/[\u201C\u201D\u201E]/g,'"')}function mr(e){let t=Gx(e).toLowerCase();if(!t.trim())return null;let n=null;for(let r of B1){let o=0;for(let s of j1[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function Kx(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))L1.has(r)&&(t+=o||0),F1.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function ic(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 Vx(e){let t=Gx(e).trim();return t?H1.test(t)?!0:t.split(/\s+/).length<=U1:!1}var B1,j1,L1,F1,U1,H1,Uu=q(()=>{B1=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],j1={research:[/\bresearch\b/,/\binvestigat/,/\bverif/,/\bcompar(e|ing|ison)/,/\bcompetit/,/\bdeep[ -]?dive\b/,/\banaly[sz]/,/\bassess/,/\bdiligence\b/,/\bmarket\b/,/\bstrateg/,/\broadmap\b/,/\bnorth star/,/\bwhat('| i)?s the (state|status)\b/,/\bstatus of\b/,/\bexplore\b/,/\bfeasib/,/\bevaluat/,/\bscope (it |this |that |them )?out\b/,/\btalk (me |us )?through\b/,/\b(give|show|tell) me\b/,/\bwhat about\b/,/\bwhy\b/,/\btake a look\b/,/\bwalk (me |us )?through\b/,/\bexplain\b/,/\bdo we (still |even )?need\b/,/\bis there (a|an|any)\b/,/\bhow often\b/,/\bdo we (currently|already)\b/,/\bwould it be (valuable|useful|worth)\b/,/\bis it possible\b/,/\bwhat are the odds\b/,/\bdetermine (what|which|whether|how|if)\b/,/\bwhat (does|do|would|could|should|will) .{1,60}\blook like\b/,/\bhow (is|are) (our|the|this|that|your|my) .{1,50}\b(going|doing|performing|trending)\b/,/\bhow (thorough|far along|complete|accurate|reliable|good) are we\b/],"bug-fix":[/\bfix(e[sd]|ing)?\b/,/\bbugs?\b/,/\bbroken\b/,/\bregression\b/,/\bcrash/,/\bincident\b/,/\bdebug/,/\bdoesn'?t (work|render|load|start)/,/\bnot working\b/,/\btroubleshoot/,/\bflaky?\b/,/\brepro(duce|duction)?\b/,/\brace condition\b/,/\bleak\b/,/\bkeeps? getting\b/,/\bdoesn'?t (seem|look) right\b/,/\bseems? (wrong|off|broken)\b/,/\b(too slow|slower than|much slower)\b/,/\bfail(ed|ing|s)\b/,/\b(another|one other|one more|next) (thing|issue|bug|problem) i (found|noticed|spotted|saw)\b/],"feature-build":[/\bimplement/,/\bbuild(ing)?\b/,/\badd(ing)?\b/,/\bcreate\b/,/\bwire (it |them )?(up|in)\b/,/\bship(ping)?\b/,/\bintegrat/,/\bsupport for\b/,/\bfeature\b/,/\bendpoint\b/,/\badapter\b/,/\bprototype\b/,/\bupdate (the |this |that |my |our )?[\w-]+( [\w-]+){0,3} to\b/,/\bset up\b/,/\b(can|could) we (also |maybe )?(tag|allow for|support|enable)\b/,/\bnow needs to be\b/],design:[/\bdesign/,/\bui\b/,/\bux\b/,/\bstyl(e|es|ing)\b/,/\bcss\b/,/\blayout\b/,/\bmock-?up/,/\brender(ing|s)?\b/,/\bpolish/,/\btheme\b/,/\bfonts?\b/,/\bresponsive\b/,/\banimation/,/\bvisual/,/\bpopover\b/],orchestration:[/\bhand[ -]?(off|over)/,/\bhand (it|the lead|this) (over|off)\b/,/\bdelegat/,/\bsubtask/,/\bmerge[ -]?back\b/,/\borchestrat/,/\b(switch|change) (the )?model\b/,/\bmodel switch/,/\bpropose (the |a |these |those |new )?loops?\b/],"docs-writing":[/\bdocs?\b/,/\bdocument/,/\bmemo\b/,/\breadme\b/,/\bwrite-?up\b/,/\bchangelog\b/,/\bclaude\.md\b/,/\bblog/,/\bproofread/,/\bcopy(edit|writ)/,/\b(linked[ -]?in|social( media)?) post\b/,/\b(framed|phrased|worded) as\b/,/\bwhat'?s a good (response|reply|answer)\b/],"ops-infra":[/\bdeploy/,/\bsystemd\b/,/\bdaemon\b/,/\btmux\b/,/\bdns\b/,/\bwireguard\b/,/\brestart/,/\bnginx\b/,/\bprovision/,/\binstall/,/\bupgrade/,/\bcron\b/,/\bbackups?\b/,/\bcertificat/,/\bssh\b/,/\bsecrets?\b/,/\b(unzip|decompress|untar)\b/]};L1=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),F1=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);U1=12,H1=/^\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 Xx(e){let t=ic(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||W1.has(r)||n.push(r);return n}function q1(e){let t=[];for(let n of e||[]){let r=n.payload||{},o=String(r.text||"").trim();if(n.type==="turn.user"){if(n.source==="hook"||!o)continue;t.push(Yx(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(Yx(n,"assistant",o))}}return t}function Yx(e,t,n){let r=mr(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 cc(e){let t=q1(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=Xx(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 Hu(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(Xx(t))];if(!i.length)return[];let c=new Map;for(let u of i){let d=e.postings.get(u);if(!d)continue;let p=d.size,f=Math.log(1+(e.N-p+.5)/(p+.5));for(let[w,m]of d){let y=e.lengths[w],k=m+zx*(1-Jx+Jx*(y/(e.avgdl||1))),g=f*(m*(zx+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 zx,Jx,W1,Gm=q(()=>{Uu();zx=1.5,Jx=.75,W1=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 Wu from"node:fs/promises";import Zx from"node:path";async function G1(e){let t=Zx.join(e,".bostrat","events"),n;try{n=await Wu.readdir(t)}catch{return cc([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await Wu.readFile(Zx.join(t,o),"utf8");for(let a of s.split(`
|
|
124
|
+
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return cc(r)}function K1(e){let t=String(e||""),n=a=>{let i=t.match(a);return i?i[1].trim():""},r=n(/#+\s*Objective\s*\n([\s\S]*?)(?:\n#+\s|$)/i),o=n(/#+\s*Next steps?[^\n]*\n([\s\S]*?)(?:\n#+\s|$)/i);return`${r}
|
|
125
|
+
${o}`.trim()||t.slice(0,2e3)}function V1(e,t,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=Hu(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?["",`## ${Qx}, not a summary)`,"_Auto-selected from the Track's event log by lexical relevance to the Objective / Next steps. Verbatim excerpts, not re-summarization (docs/memory-segmentation-indexing-scope.md \xA71.3)._","",...a,""].join(`
|
|
126
|
+
`):""}async function ev(e,t,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await Wu.readFile(t,"utf8");if(a.includes(Qx))return{appended:!1,reason:"already-enriched"};let i=await G1(e);if(!i.N)return{appended:!1,reason:"empty-index"};let c=V1(i,K1(a),{k:r,maxChars:o,exclude:s});return c?(await Wu.appendFile(t,`
|
|
127
127
|
`+c),{appended:!0,chars:c.length,chunks:c.split(`
|
|
128
|
-
- `).length-1,indexed:i.N}):{appended:!1,reason:"no-hits",indexed:i.N}}catch(a){return{appended:!1,reason:"error",error:a?.message||String(a)}}}var
|
|
129
|
-
`).map(t=>t.replace(/^[ \t]*(?:[-*•]|\d+[.)]|[a-z][.)])[ \t]+/i,"").trim()).filter(t=>t&&!/^#{1,6}[ \t]/.test(t)).join(" ").replace(/\*\*(.+?)\*\*/g,"$1").replace(/`([^`]+)`/g,"$1").replace(/\s+/g," ").trim():""}function
|
|
128
|
+
- `).length-1,indexed:i.N}):{appended:!1,reason:"no-hits",indexed:i.N}}catch(a){return{appended:!1,reason:"error",error:a?.message||String(a)}}}var Qx,tv=q(()=>{Gm();Qx="Relevant prior discussion (verbatim, retrieved"});function Q1(e){return z1.test(String(e||"").trimStart())}function ov(e){let t=String(e||"");if(!Q1(t.trimStart()))return null;let n=t.replace(J1,"").trim(),r=eB(n),o=[];for(let u of X1){let d=u.from.map(p=>ma(r.get(p))).find(p=>p&&!nv(p));d&&o.push({h:u.h,text:lc(d,Y1)})}let s=ma(r.get("Current Work"))||ma(r.get("Primary Request and Intent"))||nB(n),a=Gu(s),i=lc(qu(zm(a[0]||"")),Z1)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(lc(qu(zm(l)),Vm));for(let u of["Pending Tasks","Optional Next Step"]){let d=Gu(ma(r.get(u)))[0]||"";if(!(!d||nv(d))){c.push(lc(`Left to do: ${qu(zm(d))}`,Vm));break}}if(!c.length){let u=Gu(ma(r.get("Problem Solving"))||"")[0];u&&c.push(lc(qu(u),Vm))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function eB(e){let t=new Map,n=[];Km.lastIndex=0;for(let r=Km.exec(e);r;r=Km.exec(e))n.push({name:tB(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 tB(e){return rv.find(n=>n.toLowerCase()===e.toLowerCase())||e}function ma(e){return e?String(e).replace(/```[\s\S]*?(?:```|$)/g," ").split(`
|
|
129
|
+
`).map(t=>t.replace(/^[ \t]*(?:[-*•]|\d+[.)]|[a-z][.)])[ \t]+/i,"").trim()).filter(t=>t&&!/^#{1,6}[ \t]/.test(t)).join(" ").replace(/\*\*(.+?)\*\*/g,"$1").replace(/`([^`]+)`/g,"$1").replace(/\s+/g," ").trim():""}function nB(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return ma(t.split(/\n{2,}/).slice(0,2).join(" "))}function Gu(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 zm(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 qu(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function nv(e){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(e||"").trim())}function lc(e,t){let n=String(e||"").trim();if(n.length<=t)return n;let r=Gu(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 z1,rv,Km,J1,Y1,X1,Z1,Vm,sv=q(()=>{z1=/^This session is being continued from a previous conversation that ran out of context\./,rv=["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"],Km=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${rv.join("|")})[:*\t ]*$`,"gim"),J1=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,Y1=420,X1=[{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"]}],Z1=116,Vm=168});function cv(e){return Jm.has(String(e||"").replace(/^\//,"").toLowerCase())}function uc(e){let t=String(e||"").trim();if(!t)return null;let n=ov(t);if(n)return n;let r=t.match(rB);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(oB);if(o)return{kind:"skill",name:o[1]};let s=t.match(sB);if(s)return{kind:"skill",name:s[1].toLowerCase()};let a=t.match(aB);if(a){let c=a[1].toLowerCase();return Jm.has(c)?{kind:"suppress"}:{kind:"command",command:`/${c}`}}let i=t.match(wB);if(i&&Jm.has(i[1].toLowerCase()))return{kind:"suppress"};if(iB.test(t))return{kind:"suppress"};if(lB.test(t))return{kind:"suppress"};if(cB.test(t))return{kind:"suppress"};if(uB.test(t)){let c=u=>kB(t.match(u)?.[1].trim()||"")||null,l=c(hB);return{kind:"task",summary:c(dB)||"",taskId:c(pB),toolUseId:c(fB),status:c(mB)?.toLowerCase()||null,resultHead:l?l.length>av?`${l.slice(0,av).trimEnd()}\u2026`:l:null}}return null}function lv(e){let t=uc(e?.text);return t||(e?.injected?{kind:"skill",name:e.injected.skill||"skill"}:null)}function uv(e){let t=String(e||""),n=t.match(gB);if(n)return{kind:"shell-input",command:n[1].trim()};let r=t.match(yB);return r?{kind:"shell-output",stdout:iv(r[1]),stderr:iv(r[2])}:null}function kB(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function dv(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 pv(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var rB,oB,sB,aB,iB,cB,lB,uB,dB,pB,fB,mB,hB,av,Jm,wB,gB,yB,bB,iv,Ku=q(()=>{sv();rB=/^Base directory for this skill:\s*(\S+)/,oB=/^Run the "([^"]+)" workflow\./,sB=/^#\s*\/([a-z0-9:_-]+)\s*[—–:-]/i,aB=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,iB=/^<local-command-(?:stdout|caveat)>/i,cB=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,lB=/^Continue from where you left off\.$/,uB=/^<task-notification>/i,dB=/<summary>([\s\S]*?)<\/summary>/i,pB=/<task-id>([\s\S]*?)<\/task-id>/i,fB=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,mB=/<status>([\s\S]*?)<\/status>/i,hB=/<result>([\s\S]*?)(?:<\/result>|$)/i,av=600,Jm=new Set(["model","clear","compact","login","exit","quit"]);wB=/^\/([a-z0-9:_-]+)$/i;gB=/^\s*<bash-input>([\s\S]*?)<\/bash-input>\s*$/i,yB=/^\s*<bash-stdout>([\s\S]*?)<\/bash-stdout>\s*(?:<bash-stderr>([\s\S]*?)<\/bash-stderr>)?\s*$/i,bB=/\x1b\[[0-9;?]*[ -/]*[@-~]/g,iv=e=>String(e||"").replace(bB,"").replace(/^\n+/,"").replace(/\s+$/,"")});import xB from"node:crypto";function TB(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,mv)){let o=ha(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,vB)){let c=ha(i?.label);if(!c)return null;let l=ha(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<SB)return null;n.push({question:o,header:ha(r?.header).slice(0,AB),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function Ym(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;fv.lastIndex=0;let t=null;for(let o;o=fv.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=TB(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function Xm(e,t,n){let r=xB.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${e}:ask:${t||"turn"}:${r}`}function Zm(e,{leadBoxId:t,provider:n,isAnswered:r,maxQuestions:o=mv}={}){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||Ym(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=Xm(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 hv(e,t,n,r){let o=[];(e?.questions||[]).forEach((i,c)=>{let l=(t?.[c]||[]).map(d=>i.options?.[d]?.label).filter(Boolean),u=ha(Array.isArray(r)?r[c]:"");u&&l.push(u),l.length&&o.push({name:i.header||i.question,picks:l.join(", ")})});let s=ha(n),a;return o.length?o.length===1?a=`${o[0].name}: ${o[0].picks}`:a=`Answers:
|
|
130
130
|
${o.map(i=>`- ${i.name}: ${i.picks}`).join(`
|
|
131
131
|
`)}`:a=s,o.length&&s&&(a+=`
|
|
132
|
-
${s}`),a||""}var
|
|
132
|
+
${s}`),a||""}var mv,vB,SB,AB,fv,ha,wv,Qm=q(()=>{mv=4,vB=4,SB=2,AB=24,fv=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,ha=e=>typeof e=="string"?e.trim():"";wv=`
|
|
133
133
|
When you are BLOCKED on a decision only the operator can make \u2014 an ambiguous
|
|
134
134
|
requirement, an irreversible choice, several valid approaches \u2014 do not bury the
|
|
135
135
|
question in prose. End your message with exactly ONE fenced code block tagged
|
|
@@ -150,91 +150,91 @@ your turn. The operator sees the questions as an interactive card in their chat
|
|
|
150
150
|
They may instead reply in free text \u2014 treat that as the answer. Never use the block for rhetorical questions, progress updates, or
|
|
151
151
|
anything you can decide yourself; keep asking in prose for questions without
|
|
152
152
|
enumerable options.
|
|
153
|
-
`});function SB(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 gv(e,t,n){let r=eh(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=(k,h)=>{h!=null&&(u.has(k)||u.set(k,[]),u.get(k).push(h))},p=(k,h)=>{let v=null;for(let B of[k,null])for(let Y of u.get(B)||[])Y>h&&(v==null||Y<v)&&(v=Y);return v},f=new Set,w=new Map,m=new Map,g=(k,h)=>{m.set(k,h),n.push(h)},b=k=>{let h=m.get(k);h&&(h.retired=!0)},y=/^Agent "([\s\S]+)" finished$/,x=new Map;for(let k of e||[]){if(k.type!=="tool.called"||k.source!=="transcript")continue;let h=String(k.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(h))for(let v of h.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let B=v[0].split("/").pop().toLowerCase();x.has(B)||x.set(B,[]),x.get(B).push(k.payload?.ts??k.ts)}}let S=3*6e4,$=20*6e4,M=(k,h)=>!!(k?.name&&x.get(String(k.name).toLowerCase()))?.some(B=>B>=h-S&&B<=h+$);for(let k of e||[])if(k.type==="turn.retracted"){o.add(`${k.boxId}|${k.payload?.saidTs}`);let h=Ue(k.payload?.text);h&&(s.has(k.boxId)||s.set(k.boxId,[]),s.get(k.boxId).push(h))}else if(k.type==="turn.user"&&k.source==="orchestrator"){let h=Ue(k.payload?.text);h&&(a.has(k.boxId)||a.set(k.boxId,new Set),a.get(k.boxId).add(h))}else if(k.type==="job.stalled"&&k.payload?.id)c.set(k.payload.id,{ts:pe(k),quietMs:Number(k.payload.quietMs)||null});else if(k.type==="job.stopped"&&k.payload?.id)l.has(k.payload.id)||l.set(k.payload.id,{status:"killed",summary:null,resultHead:null,ts:pe(k)});else if(k.type==="session.ready"){let h=k.payload?.source;h!=="compact"&&h!=="clear"&&d(k.boxId??null,pe(k))}else if(k.type==="handoff.done")d(k.boxId??k.payload?.from??null,pe(k));else if(k.type==="box.state.changed"&&k.payload?.change==="archived")d(null,pe(k));else if(k.type==="job.started"&&k.payload?.id)i.add(k.payload.id),(k.payload.kind||"agent")==="agent"&&k.payload.name&&f.add(k.payload.name);else if(k.type==="turn.user"&&k.payload?.note?.kind==="task"){let h=k.payload.note,v={status:h.status||null,summary:h.summary||null,resultHead:h.resultHead??null,ts:pe(k)};h.toolUseId&&l.set(h.toolUseId,v);let B=String(h.summary||"").match(y);B&&w.set(B[1],v)}let D=new Map,j=new Map,A=null,_=new Map,L=new Map,G=new Map,O=new Map,T=new Map,C=new Map,P=new Map,W=new Map,X=new Map,ge=new Map,Ie=new Map,Pe=new Map,ee=new Map,Ze=new Map,Ee=new Map,le=new Map,re=(k,h)=>{k.arm.status=h;let v=k.line,B=v.arms.map(Y=>Y.status);v.status=B.includes("armed")?"armed":B.includes("offered")?"offered":B.includes("replaced")?"replaced":"cancelled"},$e=[],J=new Set,he=new Map,Be=(k,h)=>{he.delete(k);let v=n.indexOf(k);v>=0&&n.splice(v,1),h!=null&&(k.ts=h),n.push(k)},R=k=>{if($e.push(k),!(k.box?J.has(k.box):J.size>0))return;let v=n.indexOf(k);v>=0&&n.splice(v,1),he.set(k,"held")},ie=k=>{if(!$e.length)return;let h=pe(k);for(let v of $e)v.status!=="open"||he.get(v)==="superseded"||v.box&&v.box!==k.boxId||(v.ts??0)>h||Be(v,h)},De=k=>{if(!$e.length)return;let h=pe(k);for(let v of $e)he.get(v)!=="held"||v.status!=="open"||v.box&&k.boxId&&v.box!==k.boxId||Be(v,Math.max(v.ts??0,h))},Ot=()=>{for(let[k,h]of[...he])h==="held"&&Be(k,null);$e.length=0,Z.clear()},Z=new Set,ke=k=>{for(let h of Z){if(h.status!=="open"||h.box&&k&&h.box!==k)continue;h.status="superseded";let v=n.indexOf(h);v>=0&&n.splice(v,1),he.set(h,"superseded"),_.delete(h.offerId)}Z.clear()},nt=new Map,Qe=new Map,Tt=new Map,Ht=new Map,lt=new Map,Mt=new Map,yt=new Map,mn=new Map,jo=()=>{for(let k of mn.values())k.questions.length&&n.push(k);mn.clear()},mt=null,Pn=new Map,Lr=6e4,en=new Map,hn=(k,h)=>{let v=s.get(k);if(!v?.length)return!1;let B=Ue(h),Y=a.get(k);return v.some(we=>{if(!B.startsWith(we))return!1;let ce=Ue(B.slice(we.length));return!!ce&&!!Y?.has(ce)})};for(let k of e||[]){let h=k.payload||{};switch(k.type){case"turn.user":{if(k.source==="hook"){k.boxId&&J.add(k.boxId);break}if(Ot(),!String(h.text||"").trim()&&!h.images?.length&&!h.attachments?.length)break;{let v=lv(h.text);if(v){if(v.kind==="shell-input"){let B={id:k.id,kind:"shell",command:v.command,stdout:null,stderr:null,done:!1,ts:pe(k),box:k.boxId,agent:k.provider};n.push(B),Pn.set(k.boxId,B)}else{let B=Pn.get(k.boxId);B?(B.stdout=v.stdout||null,B.stderr=v.stderr||null,B.done=!0,Pn.delete(k.boxId)):n.push({id:k.id,kind:"shell",command:null,stdout:v.stdout||null,stderr:v.stderr||null,done:!0,ts:pe(k),box:k.boxId,agent:k.provider})}break}}{let v=h.note||uc(h.text);if(v){if(v.kind==="task"){if(v.toolUseId&&i.has(v.toolUseId))break;let B=String(v.summary||"").match(y);if(!v.toolUseId&&B&&f.has(B[1]))break}if(v.kind==="command"&&iv(v.command))break;if(v.kind==="resume-summary"){n.push({id:k.id,kind:"resume-summary",title:v.title||"Context resumed from summary",bullets:v.bullets||[],sections:v.sections||[],rawChars:v.rawChars??null,ts:pe(k),box:k.boxId,agent:k.provider});break}v.kind!=="suppress"&&n.push({id:k.id,kind:"event",type:dv(v),name:v.name??null,command:v.command??null,summary:v.summary??null,status:v.status??null,ts:pe(k),box:k.boxId,agent:k.provider});break}}if(o.has(`${k.boxId}|${pe(k)}`)||k.source==="transcript"&&hn(k.boxId,h.text))break;if(h.kind==="merge-notify"||h.kind==="ship-notify"){n.push({id:k.id,kind:"event",type:h.kind==="ship-notify"?"ship-notified":"merge-notified",text:h.text,ts:pe(k),box:k.boxId,agent:k.provider});break}if(h.kind==="mock-picked"||h.kind==="ci-fix"||h.kind==="reconcile"||h.kind==="pr-go"||h.kind==="push-go"||h.kind==="spin-up-view"||h.kind==="prompt-answer"||h.kind==="followup-go"){h.kind==="prompt-answer"&&(mt=null);break}for(let v of _.values())v.status="stale";_.clear();for(let v of G.values())v.status="stale";G.clear();for(let v of ee.values())v.rows.some(B=>B.rowStatus==="sent")||(v.status="stale",ee.delete(v.offerId));mt&&mt.box===k.boxId&&(n.push(SB(mt)),mt=null),n.push({id:k.id,kind:"message",role:"user",text:h.text,ts:pe(k),box:k.boxId,agent:k.provider,boxRole:h.boxRole,...k.actor==="orchestrator"?{orchestration:h.kind||!0}:{},...h.sourceId?{sourceId:h.sourceId}:{},...h.attachments?.length?{attachments:h.attachments}:{},...h.images?.length?{images:h.images}:{}});break}case"peer.message":{let v=h.from||{};n.push({id:k.id,kind:"event",type:"peer-message",text:h.text,from:v.trackName||v.boxId||v.name||null,fromTrackId:v.trackId||null,ts:pe(k),box:k.boxId,agent:k.provider});break}case"turn.agent":{if(h.model==="<synthetic>"&&/^No response requested\.$/.test(String(h.text||"").trim()))break;let v=xx(h,{codexFailedText:Yo});if(v){n.push({id:k.id,kind:"event",type:"provider-error",cls:v.cls,status:v.status??void 0,text:v.text,provider:k.provider,legacy:!0,at:pe(k),ts:pe(k),box:k.boxId,agent:k.provider});break}jo();let B=(h.images||[]).filter(ht=>!M(ht,pe(k)));if(k.source==="hook"&&!String(h.text||"").trim()&&(De(k),k.boxId&&J.delete(k.boxId)),!String(h.text||"").trim()&&!B.length)break;if(k.provider==="cursor"){let ht=`${k.boxId}|${h.text}`;if(r.has(ht))break;r.add(ht)}(!mt||mt.box===k.boxId)&&(mt=null);let Y=k.provider==="codex"?(h.tools||[]).filter(ht=>!Om.has(ht)&&!Yi.has(ht)):h.tools||[],we=h.text,ce=null,ot=Ym(String(h.text||""));ot&&(we=ot.text,ce={questions:ot.questions}),n.push({id:k.id,kind:"message",role:"assistant",text:we,ts:pe(k),box:k.boxId,agent:k.provider,boxRole:h.boxRole,...ce?{ask:ce}:{},...Y.length?{tools:Y}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},...B.length?{images:B}:{}}),ie(k),ce&&(mt={id:k.id,groupId:Xm(k.provider,h.sourceId||k.id,ce.questions),provider:k.provider,box:k.boxId,questions:ce.questions,ts:pe(k)});break}case"job.started":{if(!h.id)break;let v=pe(k),B=l.get(h.id);if(!B&&(h.kind||"agent")==="agent"&&h.name){let tn=w.get(h.name);tn&&tn.ts>=v&&(B=tn)}let Y=B?null:c.get(h.id)||null,we=B||Y?null:p(k.boxId??null,v),ce={jobId:h.id,jobKind:h.kind||"agent",name:h.name||null,detail:h.detail||null,status:B?B.status==="killed"?"stopped":B.status&&B.status!=="completed"?"failed":"done":Y?"stalled":we!=null?"lost":"running",startedTs:v,finishedTs:B?.ts??Y?.ts??we??null,quietMs:Y?.quietMs??null,summary:B?.summary??null,resultHead:B?.resultHead??null},ot=en.get(k.boxId),ht=ot?.jobs[ot.jobs.length-1]?.startedTs;if(ot&&ht!=null&&v-ht<=Lr)ot.jobs.push(ce);else{let tn={id:k.id,kind:"job-dock",dockId:h.id,ts:v,box:k.boxId,agent:k.provider,jobs:[ce]};en.set(k.boxId,tn),n.push(tn)}break}case"handoff.done":n.push(wt(k,"lead-handoff"));break;case"subtask.delegated":n.push({...wt(k,"subtask-delegated"),to:k.boxId,toAgent:k.provider});break;case"subtask.merged":n.push(wt(k,"subtask-merged"));break;case"pr.state.changed":{if(!h.change)break;if(h.change==="opened"){for(let B of _.values())B.status="stale";_.clear();for(let B of G.values())B.status="stale";G.clear()}let v=wt(k,`pr-${h.change}`);if(v.box=k.boxId??null,wv.has(h.change)&&h.number!=null){let B=gv(D,j,h);if(D.size>=2){for(let we of j.values())for(let ce of we)ce.grouped=!0;v.groupPrs=[...D.values()];let Y=[v];for(let we of D.keys())j.set(we,Y)}else{let Y=j.get(B)??[];Y.push(v),j.set(B,Y)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let B=eh(h),Y=A&&!A.complete||D.size>=2;if(D.has(B))D.delete(B),j.delete(B);else if(h.number!=null)for(let we of[...D.keys()])we.endsWith(`#${h.number}`)&&(D.delete(we),j.delete(we));if(Y&&h.number!=null){(!A||A.complete)&&(A={id:k.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:pe(k),ts:pe(k),box:k.boxId,agent:k.provider},n.push(A)),A.prs.push({number:h.number,url:h.url??null,title:h.title??null,repo:h.repo??null,branch:h.branch??null,change:h.change,ts:pe(k)}),A.remaining=[...D.values()],A.complete=D.size===0;break}}n.push(v);break}case"pr.offered":{if(!h.offerId)break;ke(k.boxId);for(let B of _.values())B.status="stale";_.clear();for(let B of G.values())B.status="stale";G.clear();let v={id:k.id,kind:"pr-offer",offerId:h.offerId,excerpt:h.excerpt||null,branch:h.branch||null,base:h.base||null,offered:Array.isArray(h.offered)&&h.offered.length?h.offered:null,toPr:h.toPr&&typeof h.toPr=="object"?{number:h.toPr.number??null}:null,available:Array.isArray(h.available)?h.available:null,defaults:Array.isArray(h.defaults)?h.defaults:null,prState:h.prState||null,openPr:h.openPr&&typeof h.openPr=="object"?h.openPr:null,repos:Array.isArray(h.repos)&&h.repos.length?h.repos:null,status:"open",source:k.source==="cli"||h.via==="cli"?"cli":"transcript",ts:pe(k),box:k.boxId,agent:k.provider};_.set(h.offerId,v),L.set(h.offerId,v),n.push(v),Z.add(v),v.source==="cli"&&R(v);break}case"pr.offer.answered":{let v=L.get(h.offerId);v&&(v.status=h.action||"dismiss",v.actions=Array.isArray(h.actions)?h.actions:null,v.notify=h.notify===!0,h.existingPr&&typeof h.existingPr=="object"&&(v.existingPr={number:h.existingPr.number??null}),_.delete(h.offerId));break}case"pr.exists.offered":{if(!h.offerId)break;for(let B of G.values())B.status="stale";G.clear();let v={id:k.id,kind:"pr-exists-offer",offerId:h.offerId,srcOfferId:h.srcOfferId??null,branch:h.branch||null,base:h.base||null,pr:h.pr&&typeof h.pr=="object"?{number:h.pr.number??null,url:h.pr.url??null,title:h.pr.title??null}:null,actions:Array.isArray(h.actions)&&h.actions.length?h.actions:null,notify:h.notify===!0,status:"open",ts:pe(k),box:k.boxId,agent:k.provider};G.set(h.offerId,v),O.set(h.offerId,v),n.push(v);break}case"pr.exists.answered":{let v=O.get(h.offerId);v&&(v.status=h.choice||"dismiss",G.delete(h.offerId));break}case"box.state.changed":{let v=h.change==="model"?"model-change":h.change;if(!v)break;let B=wt(k,v);if(v==="model-change"&&(B.to=h.model),v==="node-update"&&B.latest){let Y=n.findIndex(we=>we.kind==="event"&&we.type==="node-update"&&we.latest===B.latest);if(Y>=0){let we=n[Y];n[Y]={...B,ts:we.ts,at:we.at,firstAt:we.firstAt??we.ts,updatedAt:B.ts};break}}n.push(B);break}case"track.transferred":case"track.recovered":n.push(wt(k,"track-moved"));break;case"view.state.changed":{if(h.change==="deleted"){b(`view:${h.viewId}`);break}if(h.change!=="created"||h.restarted)break;g(`view:${h.viewId}`,{id:k.id,kind:"view-card",viewId:h.viewId,name:h.name,slug:h.slug,port:h.port,origin:h.origin,ts:pe(k),box:k.boxId,agent:k.provider});break}case"view.spinup.failed":{n.push({id:k.id,kind:"view-spinup-failed",name:h.name||null,reason:h.reason||"no-view",ts:pe(k),box:k.boxId,agent:k.provider});break}case"mock.state.changed":{if(h.change==="picked"){n.push({id:k.id,kind:"mock-picked",mockId:h.mockId,name:h.name,group:h.group??null,version:h.version??null,siblings:h.siblings??[],ts:pe(k),box:k.boxId,agent:k.provider});break}if(h.change==="deleted"){b(`mock:${h.mockId}`);break}if(h.change!=="created")break;g(`mock:${h.mockId}`,{id:k.id,kind:"mock-card",mockId:h.mockId,name:h.name,group:h.group??null,ts:pe(k),box:k.boxId,agent:k.provider});break}case"flow.state.changed":{if(h.change==="deleted"){b(`flow:${h.flowId}`);break}if(h.change!=="created")break;g(`flow:${h.flowId}`,{id:k.id,kind:"flow-card",flowId:h.flowId,name:h.name,stepCount:h.stepCount??null,mockIds:h.mockIds??[],ts:pe(k),box:k.boxId,agent:k.provider});break}case"runbook.state.changed":{if(h.change==="deleted"){b(`runbook:${h.runbookId}`);break}if(h.change!=="created")break;g(`runbook:${h.runbookId}`,{id:k.id,kind:"runbook-card",runbookId:h.runbookId,name:h.name,ts:pe(k),box:k.boxId,agent:k.provider});break}case"ticket.linked":{n.push({id:k.id,kind:"ticket-card",provider:h.provider||"linear",key:h.key,url:h.url,title:h.title??null,linkSource:h.linkSource??null,ts:pe(k),box:k.boxId,agent:k.provider});break}case"box.fullscreen.detected":{if(!h.offerId)break;for(let B of P.values())B.box===(h.box||k.boxId)&&(B.status="stale",P.delete(B.offerId));let v={id:k.id,kind:"fullscreen-offer",offerId:h.offerId,status:"open",ts:pe(k),box:h.box||k.boxId,agent:h.agent||k.provider};P.set(h.offerId,v),W.set(h.offerId,v),n.push(v);break}case"box.fullscreen.answered":{let v=W.get(h.offerId);v&&(v.status=h.action==="restarted"?"restarted":h.action==="cleared"?"cleared":"dismiss",P.delete(h.offerId));break}case"ticket.move.offered":{if(!h.offerId)break;for(let B of T.values())B.key===h.key&&(B.status="stale",T.delete(B.offerId));let v={id:k.id,kind:"ticket-offer",offerId:h.offerId,provider:h.provider||"linear",key:h.key,url:h.url??null,phase:h.phase??null,from:h.from??null,to:h.to?.name??null,prNumber:h.prNumber??null,status:"open",ts:pe(k),box:k.boxId,agent:k.provider};T.set(h.offerId,v),C.set(h.offerId,v),n.push(v);break}case"ticket.move.answered":{let v=C.get(h.offerId);v&&(v.status=h.action==="moved"?"moved":"dismiss",T.delete(h.offerId));break}case"ticket.state.changed":{for(let v of T.values())v.key===h.key&&v.to===h.to&&(v.status="stale",T.delete(v.offerId));if(h.mode==="offer")break;n.push(wt(k,"ticket-state"));break}case"ticket.commented":{n.push({id:k.id,kind:"ticket-comment",key:h.key,url:h.url??null,excerpt:h.excerpt??null,ts:pe(k),box:k.boxId,agent:k.provider});break}case"ticket.flow.changed":n.push(wt(k,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let B of X.values())B.status="stale";X.clear();let v={id:k.id,kind:"ticket-create-offer",proposalId:h.proposalId,provider:h.provider||"linear",epic:h.epic??null,issues:Array.isArray(h.issues)?h.issues:[],team:h.team??null,needsTeam:!!h.needsTeam,linkCurrentTrack:!!h.linkCurrentTrack,status:"open",ts:pe(k),box:k.boxId,agent:k.provider};X.set(h.proposalId,v),ge.set(h.proposalId,v),n.push(v),R(v);break}case"ticket.create.answered":{let v=ge.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",v.createdCount=h.createdCount??null,v.failedCount=h.failedCount??null,X.delete(h.proposalId));break}case"loop.create.offered":{if(!h.proposalId)break;for(let B of Ie.values())B.status="stale";Ie.clear();let v={id:k.id,kind:"loop-create-offer",proposalId:h.proposalId,name:h.name??"",schedule:h.schedule??"",prompt:h.prompt??"",expectation:h.expectation??null,notify:h.notify??"off",assignment:h.assignment??{provider:"claude"},repo:h.repo??null,nodeId:h.nodeId??null,nodeName:h.nodeName??null,status:"open",ts:pe(k),box:k.boxId,agent:k.provider};Ie.set(h.proposalId,v),Pe.set(h.proposalId,v),n.push(v),R(v);break}case"loop.create.answered":{let v=Pe.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",Ie.delete(h.proposalId));break}case"followup.armed":{if(!h.armId)break;let v=h.replaces?Ee.get(h.replaces):null;v&&v.arm.status==="armed"&&re(v,"replaced");let B={armId:h.armId,seq:Number.isInteger(h.seq)?h.seq:0,label:h.label??"",prompt:h.prompt??"",why:h.why??null,standing:h.standing==="never"?"never":"offerable",status:"armed"},Y=h.groupId??null,we=Y?le.get(Y):null;we||(we={id:k.id,kind:"followup-armed",groupId:Y,armId:B.armId,trigger:h.trigger??"now",label:B.label,prompt:B.prompt,why:B.why,planWhy:h.planWhy??null,arms:[],status:"armed",ts:pe(k),box:k.boxId,agent:k.provider},Y&&le.set(Y,we),we.trigger!=="now"&&n.push(we)),we.arms.push(B),Ee.set(h.armId,{arm:B,line:we});break}case"followup.armed.cancelled":{let v=Ee.get(h.armId);v&&v.arm.status==="armed"&&re(v,"cancelled");break}case"followup.offered":{if(!h.offerId)break;for(let Y of ee.values())Y.trigger===h.trigger&&(Y.status="stale",ee.delete(Y.offerId));let v=Array.isArray(h.rows)?h.rows:[];for(let Y of v){let we=Y.armId?Ee.get(Y.armId):null;we&&we.arm.status==="armed"&&re(we,"offered")}let B={id:k.id,kind:"followup-offer",offerId:h.offerId,trigger:h.trigger??"now",repo:h.repo??null,context:h.context&&typeof h.context=="object"?h.context:{},groupId:h.groupId??null,why:h.why??null,rows:v.map(Y=>({followupId:Y.followupId??null,armId:Y.armId??null,label:Y.label??"",prompt:Y.prompt??null,promptPreview:Y.promptPreview??null,promptHash:Y.promptHash??null,origin:Y.origin??"operator",why:Y.why??null,groupId:Y.groupId??null,seq:Number.isInteger(Y.seq)?Y.seq:0,standing:Y.standing==="never"?"never":"offerable",promotable:Y.armId?Y.promotable!==!1:!1,promotableReason:Y.promotableReason??null,rowStatus:"open"})),status:"open",answer:null,answers:[],ts:pe(k),box:k.boxId,agent:k.provider};ee.set(h.offerId,B),Ze.set(h.offerId,B),n.push(B),B.trigger==="now"&&R(B);break}case"followup.answered":{let v=Ze.get(h.offerId);if(!v)break;let B=new Set;h.armId&&B.add(`arm:${h.armId}`),h.followupId&&B.add(`fu:${h.followupId}`);for(let ce of Array.isArray(h.armIds)?h.armIds:[])B.add(`arm:${ce}`);for(let ce of Array.isArray(h.followupIds)?h.followupIds:[])B.add(`fu:${ce}`);let Y=h.action==="sent"?"sent":h.action==="removed"?"removed":"dismiss";for(let ce of v.rows){if(ce.rowStatus!=="open")continue;let ot=ce.armId?`arm:${ce.armId}`:`fu:${ce.followupId}`;B.size&&!B.has(ot)||(ce.rowStatus=Y)}let we={followupId:h.followupId??null,armId:h.armId??null,label:h.label??null,promoted:h.promoted??null,revived:h.revived===!0,action:Y,batchId:h.batchId??null};v.answers.push(we),v.answer=we,v.rows.some(ce=>ce.rowStatus==="open")?v.status="open":(v.status=v.rows.some(ce=>ce.rowStatus==="sent")?"sent":v.rows.some(ce=>ce.rowStatus==="removed")?"removed":"dismiss",ee.delete(h.offerId));break}case"followup.create.offered":{if(!h.proposalId)break;for(let B of nt.values())B.status="stale";nt.clear();let v={id:k.id,kind:"followup-create-offer",proposalId:h.proposalId,label:h.label??"",prompt:h.prompt??"",trigger:h.trigger??"pr.merged",repo:h.repo??null,why:h.why??null,status:"open",created:null,ts:pe(k),box:k.boxId,agent:k.provider};nt.set(h.proposalId,v),Qe.set(h.proposalId,v),n.push(v),R(v);break}case"followup.create.answered":{let v=Qe.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",nt.delete(h.proposalId));break}case"followup.created":{let v=h.proposalId?Qe.get(h.proposalId):null;v&&(v.created={followupId:h.followupId??null});break}case"loop.created":{let v=h.proposalId?Pe.get(h.proposalId):null;v&&(v.created={loopId:h.loopId??null,nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null}),n.push(wt(k,"loop-created"));break}case"loop.update.offered":case"loop.delete.offered":{if(!h.proposalId||!h.loopId)break;for(let Y of Tt.values())Y.loopId===h.loopId&&(Y.status="stale",Y.staleReason="superseded",Tt.delete(Y.proposalId));let v=k.type==="loop.update.offered",B={id:k.id,kind:v?"loop-update-offer":"loop-delete-offer",proposalId:h.proposalId,loopId:String(h.loopId),name:h.name??h.base?.name??"",base:h.base??null,...v?{patch:h.patch??{},changed:Array.isArray(h.changed)?h.changed:Object.keys(h.patch||{})}:{reason:h.reason??""},selfEdit:!!h.selfEdit,status:"open",staleReason:null,ts:pe(k),box:k.boxId,agent:k.provider};Tt.set(h.proposalId,B),Ht.set(h.proposalId,B),n.push(B),R(B);break}case"loop.update.answered":{let v=Ht.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="applied"?"applied":"dismiss",Tt.delete(h.proposalId));break}case"loop.delete.answered":{let v=Ht.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="delete"?"deleted":h.action==="pause"?"paused":"keep",Tt.delete(h.proposalId));break}case"loop.updated":{let v=h.proposalId?Ht.get(h.proposalId):null;v&&(v.applied={changed:Array.isArray(h.changed)?h.changed:[],nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null}),n.push(wt(k,"loop-updated"));break}case"loop.deleted":n.push(wt(k,"loop-deleted"));break;case"loop.paused":n.push(wt(k,"loop-paused"));break;case"planning.change.offered":{if(!h.proposalId||!Array.isArray(h.ops)||!h.ops.length)break;for(let B of lt.values())B.status="stale",B.staleReason="superseded",lt.delete(B.proposalId);let v={id:k.id,kind:"planning-offer",proposalId:h.proposalId,why:h.why??"",ops:h.ops,base:h.base??{},resolved:h.resolved??{},snapped:!!h.snapped,createsMeRow:!!h.createsMeRow,status:"open",staleReason:null,applied:null,ts:pe(k),box:k.boxId,agent:k.provider};lt.set(h.proposalId,v),Mt.set(h.proposalId,v),n.push(v),R(v);break}case"planning.change.answered":{let v=Mt.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="applied"?"applied":"dismiss",lt.delete(h.proposalId));break}case"planning.changed":{let v=h.proposalId?Mt.get(h.proposalId):null;v&&(v.applied={results:Array.isArray(h.results)?h.results:[]}),n.push(wt(k,"planning-changed"));break}case"rework.cause.offered":{if(!h.sha||yt.has(h.sha))break;let v={id:k.id,kind:"rework-cause-offer",sha:h.sha,subject:h.subject??"",linesReworked:Number(h.linesReworked)||0,origins:Array.isArray(h.origins)?h.origins:[],heuristic:h.heuristic??null,intent:h.intent??null,shas:Array.isArray(h.shas)?h.shas:null,pr:h.pr??null,retracts:0,status:"open",cause:null,ts:pe(k),box:k.boxId,agent:k.provider};yt.set(h.sha,v),n.push(v);break}case"rework.cause.answered":{let v=h.sha?yt.get(h.sha):null;v&&(v.status="answered",v.cause=h.cause??null);break}case"rework.cause.retracted":{let v=h.sha?yt.get(h.sha):null;v&&(v.status="open",v.cause=null,v.retracts=(v.retracts??0)+1);break}case"ticket.created":{let v={items:Array.isArray(h.items)?h.items:[],failed:Array.isArray(h.failed)?h.failed:[],team:h.team??null},B=h.proposalId?ge.get(h.proposalId):null;if(B){B.created=v;break}n.push({id:k.id,kind:"ticket-created",...v,ts:pe(k),box:k.boxId,agent:k.provider});break}case"error":{if(!h.kind)break;n.push(wt(k,h.kind));break}case"provider.error":n.push({...wt(k,"provider-error"),provider:k.provider,box:k.boxId,agent:k.provider});break;case"usage.cap.hit":n.push(wt(k,"usage-cap-hit"));break;case"usage.cap.warning":n.push(wt(k,"usage-cap-warning"));break;case"provider.connected":{n.push({id:k.id,kind:"event",type:"provider-connected",provider:h.provider||k.provider,ts:pe(k),at:pe(k),box:k.boxId,agent:k.provider});break}case"prompt.answered":{if(mt=null,h.control||!h.question)break;let v=h.groupId||h.promptId;if(!v)break;let B=mn.get(v);B||(B={id:k.id,kind:"prompt",groupId:v,provider:k.provider,box:k.boxId,agent:k.provider,questionTotal:h.questionTotal??1,questions:[],ts:pe(k)},mn.set(v,B));let Y=h.questionIndex??0,we={index:Y,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},ce=B.questions.findIndex(ht=>ht.index===Y&&Ue(ht.question)===Ue(we.question));ce>=0?B.questions[ce]=we:B.questions.push(we),B.questions.sort((ht,qs)=>ht.index-qs.index),B.questionTotal=Math.max(B.questionTotal,h.questionTotal??1),B.questions.length>=B.questionTotal&&(n.push(B),mn.delete(v));break}case"prompt.dismissed":{mt=null;let v=h.groupId||h.promptId;if(!v||!h.question)break;let Y=mn.get(v)||{id:k.id,kind:"prompt",groupId:v,provider:k.provider,box:k.boxId,agent:k.provider,questionTotal:h.questionTotal??1,questions:[],ts:pe(k)},we=h.questionIndex??0;Y.questions.some(ce=>ce.index===we&&Ue(ce.question)===Ue(h.question))||(Y.questions.push({index:we,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:[]}),Y.questions.sort((ce,ot)=>ce.index-ot.index)),Y.answeredVia="chat",n.push(Y),mn.delete(v);break}default:break}}for(let[k,h]of[...he]){if(h==="superseded"){k.status!=="superseded"&&Be(k,null);continue}(k.status!=="open"||t-(k.ts??0)>=AB)&&Be(k,null)}let bi=m.size?n.filter(k=>!k.retired):n;return EB(bi.sort((k,h)=>(k.ts??0)-(h.ts??0)))}function EB(e){let t=new Map,n=[];for(let r of e){if(r.kind!=="message"||!r.box){n.push(r);continue}let o=r.role==="assistant"&&!String(r.text||"").trim()&&r.images?.length,s=t.get(r.box);if(o&&s&&(r.ts??0)-s.lastTs<=TB){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 th(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:_B(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 fx(n,r)}function _B(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==ua(e.command)?t:Yr(e.command,e.description||"")}function wt(e,t){let{ts:n,change:r,kind:o,...s}=e.payload||{};return{id:e.id,kind:"event",type:t,...s,at:pe(e),ts:pe(e)}}function yv(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)+IB,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&$B.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function bv(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!OB.has(r.type)||!r.box)continue;let o=(r.ts??0)+RB,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 Vu(e){return(e||[]).filter(t=>MB.has(t?.kind)&&t.status==="open")}function kv(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(wv.has(o.change)&&o.number!=null)gv(t,n,o);else if(o.change==="merged"||o.change==="closed"||o.change==="lost"){let s=eh(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,wv,eh,AB,TB,IB,$B,RB,OB,MB,zu=q(()=>{da();Xo();Cu();oc();Ku();Qm();pe=e=>e.payload?.ts??e.ts;wv=new Set(["opened","ci-fixed","reconciled","pushed"]),eh=e=>`${e.repo??""}#${e.number}`;AB=3e4;TB=2*6e4;IB=12e4,$B=new Set(["archived","unarchived","opened"]);RB=2500,OB=new Set(["pr-opened","pr-merged","pr-auto-armed"]);MB=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 BB(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 LB(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 FB(e,t){let n=[],r=[],o=new Set,s=new Set;for(let a=(e?.length??0)-1;a>=0;a--){let i=e[a];if(i?.type!=="tool.called")continue;if(t!=null&&(i.ts??0)<=t)break;let c=i.payload||{};c.path&&!o.has(c.path)&&n.length<PB&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<DB&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function Av(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=BB(e),s=LB(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(-CB);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(Sv[r]??Sv.failover),l.push(""),o.lastPush?.sha){let p=vv(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=vv(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=FB(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"}:** ${jB(p.text,NB)}`),l.push("")}l.push("---"),l.push("Resume: continue the objective above from the verified repository state; ask the operator before redoing anything the log says was already done.");let d=l.join(`
|
|
154
|
-
`);return d.length<=
|
|
155
|
-
|
|
156
|
-
[\u2026 brief truncated]`}var
|
|
157
|
-
`),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(es);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let p=r[u].trim();p&&!Qr.test(r[u])&&a.push(p)}}if(s.length<2)return null;let i=s.map(u=>({label:eo(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=["Switch model?",...a].join(" ");return{provider:"claude",box:t,multiSelect:!1,cursorIndex:c,promptId:`claude:model-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Model",multiSelect:!1,options:i}]}}function
|
|
158
|
-
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0
|
|
159
|
-
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(es);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
|
|
160
|
-
`),o=-1;for(let u=r.length-1;u>=0;u--)if(
|
|
161
|
-
`),o=ah(r);return o?{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"theme",promptId:`claude:setup:theme:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks: choose the text style that looks best with your terminal. You can change it later with /theme.",header:"Theme",multiSelect:!1,options:o.map(s=>({label:eo(s.label),description:""}))}]}:null}function
|
|
162
|
-
`),o=ah(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:eo(i),description:c.join("\xB7").trim()}});return{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(a=>a.cursor)),setup:"login-method",promptId:`claude:setup:login-method:${s.map(a=>a.label).join("|")}`.slice(0,200),questions:[{question:"How do you want to sign in to Claude? A Claude subscription (Pro/Max/Team) uses your existing plan; a Console account bills API usage. Your login stays on this box \u2014 bostrat never sees it.",header:"Log in",multiSelect:!1,options:s}]}}function
|
|
163
|
-
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=
|
|
164
|
-
`)].reverse().find(a=>rh.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
|
|
165
|
-
`),o=ah(r);return o?{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"trust",promptId:`claude:setup:trust:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks whether it can read, edit, and run files in this workspace. This box's workspace is bostrat-managed scratch space, so trusting it is safe.",header:"Folder trust",multiSelect:!1,options:o.map(s=>({label:eo(s.label),description:""}))}]}:null}function
|
|
166
|
-
`),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(
|
|
167
|
-
`),o=
|
|
168
|
-
`).pop()||"")?!0:ts.test(n)?!1:
|
|
169
|
-
`).pop()||""):e==="codex"&&/Sign in with ChatGPT|one-time code|Paste or type your API key|link to authenticate|Successfully logged in\.[\s\S]*Press Enter to continue\./i.test(n)?!0:e==="cursor"||e==="codex"?/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i.test(n):!1:!1}function
|
|
170
|
-
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];
|
|
171
|
-
`)){let f=p.trim();if(!f||f[0]!=="{")continue;let w;try{w=JSON.parse(f)}catch{continue}if(w.isSidechain)continue;let m=
|
|
172
|
-
`).trim():"",
|
|
173
|
-
|
|
174
|
-
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function Xj(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(dS.test(s)){if(r.length>=SS)continue;let a=Yj(s.replace(dS,"").replace(/[.;,:]+$/,""),AS,zj);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>uS&&(o=`${o.slice(0,uS-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function TS(e,t,{timeoutMs:n=9e4}={}){let r=so.join(t,Kj),o=so.join(r,Vj);await oo.mkdir(r,{recursive:!0});let s=await rd(o),a=await xe(e);if(!a)throw new Error(`session "${e}" not found`);return await od(e,a,Jj(o)),await MS(o,s,n),Xj(await oo.readFile(o,"utf8"))}function ES(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===bS||n===vS;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(yS)||n===Wj||r.startsWith(kS)||r.startsWith(xS)}function _S(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-Zj;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 IS(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 $S(e,{windows:t=[]}={}){if(ES(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 vh.get(e)||null}async function RS(e){let t=vh.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await xe(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);let r={name:e,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:so.join(n.cwd,hS,wS)};return vh.set(e,r),tL(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function tL(e,t){e.step="summarize",await OS(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 xe(e.name);await od(e.name,n,"/clear"),await Ah(Qj),e.step="resume",await od(e.name,n,Gj),e.step="done",e.finishedAt=Date.now()}async function pS(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 iS({...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||nL)===rL)throw i;return await oe({flow:"checkpoint",phase:"quiescence-timeout",sessionName:t,gate:e,waitedMs:i.waitedMs,detail:i.detail,still:xh(i.detail),paneTail:!0}),r.onResume&&await r.onResume(e),{outcome:"timeout",waitedMs:i.waitedMs,waited:!0,detail:i.detail}}}async function OS(e,t,{timeoutMs:n=eL,quiesce:r=null}={}){let o=so.join(t,hS),s=so.join(o,wS);await oo.mkdir(o,{recursive:!0});let a=await xe(e),i=await pS("pre-drive",e,a,r),c=await rd(s);await oe({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:s,beforeMtime:c,quiescence:i.outcome,waitedMs:i.waitedMs});try{await od(e,a,qj(s)),await MS(s,c,n)}catch(f){throw f&&typeof f=="object"&&(f.quiescence={pre:i,post:{outcome:"skipped"}}),f}let l=await pS("post-write",e,a,r);await oe({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:s,afterMtime:await rd(s),quiescence:l.outcome,waitedMs:l.waitedMs});let u=await Qx(t,s,{exclude:f=>ES(f.role,f.text)});await oe({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 oo.readFile(s,"utf8");await N({id:d,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...gS(f),ts:Date.now()}})}catch{}}await am(t);let p=await oL(o,s);return d&&await C0(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 xe(e);if(!r)throw new Error(`session "${e}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${e}"`);return OS(e,r.cwd,{...t?{timeoutMs:t}:{},quiesce:n})}function Sh(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 id(e,t,{trackName:n="this Track"}={}){let r=await K({id:e,cwd:t}),o=Av(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=so.join(t,fS);return await oo.mkdir(so.dirname(s),{recursive:!0}),await oo.writeFile(s,o,"utf8"),await N({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...gS(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),fS}async function rd(e){try{return(await oo.stat(e)).mtimeMs}catch{return 0}}async function MS(e,t,n){let r=Date.now()+n;for(;;){if(await rd(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await Ah(2e3)}}async function oL(e,t){try{if(!Hj.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=so.join(e,`${n}.md`);return await oo.copyFile(t,r),r}catch{return null}}function Ah(e){return new Promise(t=>setTimeout(t,e))}function mS(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 od(e,t,n){if(t){let o=mS(t.agent);await pr({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await xe(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await Ge(e,["Escape"],{kind:"drive-wipe"}),await Ah(150),await aa(e,$u,{kind:"drive-wipe"}),await it(e,n,{kind:"orchestration-drive",...Un(r.agent,n)}),await ac({sessionName:e,agent:r.agent,agentCmd:mS(r.agent),text:n,flow:"orchestration-drive"})}var hS,wS,lS,yS,bS,kS,Wj,qj,Gj,xS,vS,Kj,Vj,uS,SS,AS,zj,Jj,dS,Yj,Zj,Qj,eL,vh,nL,rL,fS,Th=q(()=>{dt();Qo();gn();nc();qm();ev();Ci();ye();ku();Tv();cS();hS=".bostrat/handoff",wS="latest.md",lS=32*1024;yS="Checkpoint: write a work-in-progress handoff",bS="HANDOFF WRITTEN",kS="Resume from .bostrat/handoff/latest.md",Wj="/clear",qj=e=>`${yS} 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 ${bS} and take no further action.`,Gj=`${kS}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,xS="Beat: write a",vS="BEAT WRITTEN",Kj=".bostrat/arc",Vj="beat.txt",uS=200,SS=4,AS=5,zj=36,Jj=e=>`${xS} 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 ${SS} further lines, each starting with "- " and naming ONE piece in at most ${AS} 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 ${vS} and take no further action.`,dS=/^(?:[-–—*•·]|\d+[.)])\s+/,Yj=(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};Zj=6e5;Qj=3500,eL=3e5,vh=new Map;nL="proceed",rL="reject";fS=".bostrat/handoff/status-latest.md"});function io(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!cd.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 PS(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:io(o.from,"from",n),toMs:io(o.to,"to",n)}))}return t}function _h(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 aL(e){if(!e)return null;let t=String(e).trim();if(ao[t])return t;if(CS[t])return CS[t];let n=null;for(let r of Object.keys(ao))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function iL(e){if(!e||e===ao)return sL;let t=NS.get(e);return t||(t=PS(e),NS.set(e,t)),t}function DS(e,t,n={}){let{table:r,speed:o}=n,s=iL(r),a=r&&r!==ao?s[e]?e:null:aL(e);if(!a)return null;let i=_h(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 qn(e,t={},n,r){let o=DS(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 cL(e=ao){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:io(a.from,"from",n),toMs:io(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`),cd.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 uL({now:e=Date.now(),table:t=ao,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...lL,...n},a=[],i=_h(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:io(d.from,"from",c),toMs:io(d.to,"to",c),verifiedMs:cd.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*Eh;p&&i-d.verifiedMs>r*Eh&&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*Eh&&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 ka({now:e=Date.now(),table:t=ao,...n}={}){let r=_h(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=io(l.from,"from",i),d=io(l.to,"to",i);r>=u&&r<=d&&cd.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=[...cL(t),...uL({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var ao,CS,cd,sL,NS,BS,lL,Eh,mr=q(()=>{ao={"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)."}]},CS={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"},cd=/^\d{4}-\d{2}-\d{2}$/;sL=PS(ao);NS=new WeakMap;BS=(e,t,n)=>DS(e,t,n)!==null;lL=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),Eh=864e5});function dL(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function LS(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function rn(e){let t=e.payload?.model||"unknown",n=dL(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=qn(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&&!BS(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function co(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=jS();for(let p of n){let{usd:f,model:w,tokens:m,repriced:g,unpriced:b,asRecorded:y}=rn(p);i+=y;let x=p.provider||"unknown",S=p.boxId||"unattributed";if(g&&c++,b&&(l++,u.add(w)),a+=f,LS(d,m),r[w]||={usd:0,tokens:jS(),events:0},r[w].usd+=f,r[w].events+=1,LS(r[w].tokens,m),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:xa(a),asRecordedUsd:xa(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:ld(r,p=>({...p,usd:xa(p.usd)})),byProvider:ld(o,p=>({...p,usd:xa(p.usd)})),byBox:ld(s,p=>({usd:xa(p.usd),events:p.events,provider:p.provider,label:p.label,models:[...p.models],...p.epochs?{epochs:ld(p.epochs,f=>({usd:xa(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var jS,xa,ld,lo=q(()=>{mr();jS=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});xa=e=>Math.round(e*1e4)/1e4,ld=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function pL(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 FS(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(Ih(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Ih(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(`${Ih(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=co(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?pL(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Ih,US=q(()=>{lo();Ih=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as fL}from"node:child_process";import{promisify as mL}from"node:util";import{randomUUID as hL}from"node:crypto";import HS from"node:fs/promises";async function $h(e,t){let n=Date.now()+t;for(;;){if(await Ln(e))return!0;if(Date.now()>n)return!1;await ss(VS)}}function xL(e){let t=String(e||"").split(`
|
|
175
|
-
`).map(n=>n.trimEnd()).filter(n=>n.trim());return!t.length||!
|
|
176
|
-
`))}async function
|
|
177
|
-
`).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 cs(e,t){if(!t)return 0;try{let{stdout:n}=await Je(e,["rev-list","--count",`${t}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function ls(e,t,n,r){return await Je(e,["worktree","add","--no-track","-b",n,t,r]),t}async function
|
|
178
|
-
`).map(s=>s.trim()).filter(Boolean)}async function
|
|
179
|
-
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var
|
|
180
|
-
`),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
|
|
181
|
-
`),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
|
|
182
|
-
`);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
|
|
183
|
-
`),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 vA(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&&!kA.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=xA.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!ZL.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function yc(e,t,{timeoutMs:n=YL}={}){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(XL)}}async function tF(e,t){return await it(e,`/model ${t.model}`,Un("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function nF(e,t){await Iu(e),await it(e,"/model",{enter:!1}),await bd(250),await Ge(e,["Enter"],{kind:"model-switch"});let n=await yc(e,wA);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await Ge(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${t.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=eF(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await it(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await yc(e,i=>gA(i)||!wA(i)&&o(i)&&{done:o(i)});if(!s)throw await Ge(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?kA.get(t.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await Ge(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await it(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await yc(e,i=>!gA(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 rF(){let e=hA.join(process.env.CURSOR_CONFIG_DIR||hA.join(JL.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await zL.readFile(e,"utf8"))?.model||null}catch{return null}}async function oF(e,t){await Iu(e),await Ru(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await yc(e,yA),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 yc(e,yA,{timeoutMs:1500});if(s=l?.rows.find(u=>u.active)||null,s&&o(s))break;if(!l||!l.rows.some(o)){s=null;break}await Ge(e,["Down"],{kind:"model-switch"}),await bd(250)}if(!n||!s||!o(s)){await Ge(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await Ge(e,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${t.filter}"`:"cursor model picker didn't open")}await Ge(e,["Enter"],{kind:"model-switch"}),await bd(800);let a=await rF();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 SA(e,t,n){return e==="codex"?nF(t,n):e==="cursor"?oF(t,n):tF(t,n)}var bd,YL,XL,bA,ZL,QL,kA,xA,AA=q(()=>{dt();nc();bd=e=>new Promise(t=>setTimeout(t,e)),YL=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,XL=300,bA=[{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"}],ZL=new Set([...bA.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"]),QL=[{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"}],kA=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),xA=[{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 vd(e){let t=e||{},n=sF(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function sF(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{text:e.text,boxRole:e.boxRole,...e.kind?{kind:e.kind}:{},...e.attachments?.length?{attachments:e.attachments}:{},...e.deliveryId?{deliveryId:e.deliveryId}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{saidTs:e.saidTs,text:e.text,boxRole:e.boxRole}};case"orchestration-prompt":return{type:"turn.user",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{text:e.prompt,kind:e.kind,boxRole:e.boxRole,...e.deliveryId?{deliveryId:e.deliveryId}:{}}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:ct(e.toAgent),payload:{from:e.from,fromAgent:e.fromAgent,to:e.to,toAgent:e.toAgent,reason:e.reason,handoffStartedAt:e.handoffStartedAt,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,singleBox:e.singleBox,forced:e.forced,recovered:e.recovered,checkpoint:e.checkpoint,cliVersion:e.cliVersion??null,...e.standing?{standing:e.standing}:{}}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.toAgent),payload:{box:e.box,fromAgent:e.fromAgent,toAgent:e.toAgent,reason:e.reason,error:e.error,rolledBack:e.rolledBack,handoffStartedAt:e.handoffStartedAt}};case"subtask-delegated":return{type:"subtask.delegated",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:ct(e.toAgent),payload:{task:e.task,subtaskId:e.subtaskId,reason:e.reason}};case"subtask-merged":return{type:"subtask.merged",actor:"orchestrator",source:"orchestrator",boxId:e.box||e.from,provider:ct(e.agent),payload:{...Gn(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:ct(e.agent||e.toAgent),payload:{kind:"subtask-failed",...Gn(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{change:"subtask-resumed",...Gn(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",...Gn(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...Gn(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...Gn(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{change:"rehydrated",...e.fresh?{fresh:!0}:{}}};case"lead-refreshed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{change:"lead-refreshed",box:e.box,agent:e.agent,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,reason:e.reason,cliVersion:e.cliVersion??null,...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:ct(e.agent),payload:{...Gn(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Gn(e)}};case"retag-failed":return{type:"error",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{kind:"retag-failed",action:e.action??"reshape",reason:e.reason,rolledBack:e.rolledBack!==!1,...e.recovered?{recovered:!0}:{}}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};case"delivery-held":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{kind:"delivery-held",...Gn(e)}};case"startup-gate":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{kind:"startup-gate",...Gn(e)}};default:return null}}function _A(e,t){let n=vd(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;Sd(()=>N(r,n))}async function IA(e){if(!(!e?.id||TA.has(e.id))){TA.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=vd(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await N(e,o)}}catch{}}}function Gn(e){let{type:t,at:n,...r}=e;return r}function $A(e,t,n){!n?.promptId||xd.get(e.id)===n.promptId||(xd.set(e.id,n.promptId),Sd(()=>N(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:ct(n.provider||t?.agent),payload:{promptId:n.promptId,question:n.question||n.title||null,options:(n.options||[]).map(r=>r?.label??r).slice(0,12)}})))}function RA(e,t,n,r={}){xd.delete(e.id),Sd(()=>N(e,{type:"prompt.dismissed",actor:"user",source:"orchestrator",boxId:t?.name,provider:ct(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}function Jh(e,t,n,r={}){xd.delete(e.id),Sd(()=>N(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:ct(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var ct,kd,Sd,EA,TA,xd,Yh=q(()=>{ye();ct=e=>e||null;kd=Promise.resolve(),Sd=e=>(kd=kd.then(e).catch(()=>{}),kd),EA=()=>kd;TA=new Set;xd=new Map});function uF(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function DA(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of uF(n).reverse()){if(!aF.test(r))continue;let o=r.endsWith("?")&&r.length<=lF&&cF.test(r);if(iF.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function jA(e){let t=String(e||""),n=[];dF.test(t)&&n.push("commit"),BA.test(t)&&n.push("rebase"),pF.test(t)&&n.push("push");let r=mF.test(t);!r&&fF.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 LA(e){return BA.test(String(e||""))}function _a(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 FA(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 UA(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<=hF&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function HA(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+wF||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function WA(e){let t=(Array.isArray(e)?e:[]).filter(r=>OA[r]).map(r=>OA[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 MA,CA,NA,PA,aF,iF,cF,lF,po,dF,BA,pF,fF,mF,hF,wF,OA,ps=q(()=>{MA=/(?<!\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,CA=/(?<!\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,NA=/open(?:\s+up)?\s+(?:a|the|that)\s+(?:draft\s+)?(?:pr|pull\s+request)|creat(?:e|ing)\s+(?:a|the)\s+(?:draft\s+)?(?:pr|pull\s+request)|rais(?:e|ing)\s+(?:a|the)\s+(?:pr|pull\s+request)|put\s+up\s+(?:a|the)\s+(?:pr|pull\s+request)|send\s+(?:up\s+)?(?:a|the)\s+(?:pr|pull\s+request)/.source,PA=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|clean|interactive)\s)rebase(?!s\b)(?:\s+(?:it|this|that|them|(?:this|that|the)\s+branch))?(?:\s+(?:on(?:to)?|against|atop|over)\s+\S+)?/.source,aF=new RegExp(`\\b(?:${MA}|${CA}|${NA}|${PA})\\b`,"i"),iF=/\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,cF=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,lF=90;po=["commit","rebase","push","pr"],dF=new RegExp(`\\b(?:${MA})\\b`,"i"),BA=new RegExp(`\\b(?:${PA})\\b`,"i"),pF=new RegExp(`\\b(?:${CA})\\b`,"i"),fF=new RegExp(`\\b(?:${NA})\\b`,"i"),mF=/\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;hF=3e4;wF=2500;OA={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import gF from"node:crypto";import Ad from"node:fs/promises";import qA from"node:path";import yF from"node:os";function zA(e){VA=typeof e=="function"?e:async()=>new Set}function bF(e,t,n){if(e.originNode&&e.originNode===t)return!0;let r=e.data?.trackId;return!!r&&n.has(r)}async function He(){return wr||(Xh??=Ad.readFile(Qh,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(wr=e,!t&&qe()){let{records:n,source:r}=await ut("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){let o=R0(),s=await VA().catch(()=>new Set),a=0;for(let i of n){if(i.deletedAt||!i.data?.repo||i.data?.number==null)continue;if(!bF(i,o,s)){a++;continue}let{id:c,updatedAt:l,...u}=i.data;wr[ue(u.repo,u.number)]=u}a&&console.log(`[pr-links] restore: ${Object.keys(wr).length} link(s) re-seeded, ${a} other-node record(s) left on the plane`),Object.keys(wr).length&&await ew().catch(()=>{})}}return wr}).finally(()=>Xh=null),Xh)}async function ew(){let e=JSON.stringify({links:wr},null,2),t=`${Qh}.${process.pid}.tmp`;await Ad.mkdir(GA,{recursive:!0}),await Ad.writeFile(t,e),await Ad.rename(t,Qh)}async function Xe(e){Zh=Zh.then(ew,ew),await Zh.catch(()=>{});let t=wr?.[e];t&&Et("prlinks",{id:KA(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function Gt({repo:e,number:t,trackId:n,url:r=null,headRef:o=null,title:s=null,source:a="matched"}){if(!e||!Number.isInteger(Number(t))||!n)return{link:null,created:!1};let i=await He(),c=ue(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:kF.has(a),firstSeen:Date.now()},await Xe(ue(e,t)),{link:i[c],created:!0})}async function gr(e,t){if(!e||t==null)return null;let n=(await He())[ue(e,t)];return!n||n.announced?null:(n.announced=!0,await Xe(ue(e,t)),n)}async function JA(e,t){if(!e||t==null)return!1;let n=(await He())[ue(e,t)];return n?.notifyOnMerge?(delete n.notifyOnMerge,await Xe(ue(e,t)),!0):!1}async function pt(e,t){return!e||t==null?null:(await He())[ue(e,t)]??null}async function fo(e,t,n){if(!e||t==null)return null;let r=(await He())[ue(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 Xe(ue(e,t)),r):null}async function YA(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await He())[ue(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Xe(ue(e,t)),o):null}async function XA(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await He())[ue(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Xe(ue(e,t)),o):null}async function Td(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await He())[ue(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Xe(ue(e,t)),r)}async function Ed(e,t){if(!e||t==null)return null;let n=(await He())[ue(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Xe(ue(e,t)),n)}async function _d(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await He())[ue(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Xe(ue(e,t)),r)}async function Id(e,t){if(!e||t==null)return null;let n=(await He())[ue(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Xe(ue(e,t)),n)}async function ZA(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await He())[ue(e,t)];return r?(r.baseFoldAt=n,await Xe(ue(e,t)),r):null}async function QA(e,t,{misses:n,until:r}){if(!e||t==null)return null;let o=(await He())[ue(e,t)];return o?(o.brokerRefused={misses:Number(n)||0,until:Number(r)||0},await Xe(ue(e,t)),o):null}async function eT(e,t){if(!e||t==null)return null;let n=(await He())[ue(e,t)];return n?.brokerRefused?(delete n.brokerRefused,await Xe(ue(e,t)),n):null}async function tT(e,t){if(!e||t==null)return null;let n=(await He())[ue(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Xe(ue(e,t)),n)}async function nT(e,t,n){if(!e||t==null||!n)return null;let r=(await He())[ue(e,t)];return r?(r.lastHeadSha=n,await Xe(ue(e,t)),r):null}async function rT(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await He())[ue(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 Xe(ue(e,t))),{prev:o,link:r}}async function bc(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await He())[ue(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Xe(ue(e,t)),r):null}async function fs(e,t,n=!0){if(!e||t==null)return null;let r=(await He())[ue(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Xe(ue(e,t)),r):null}async function ms(e,t,n=!0){if(!e||t==null)return null;let r=(await He())[ue(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Xe(ue(e,t)),r):null}async function oT(e,t,n){if(!e||t==null)return null;let r=(await He())[ue(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 Xe(ue(e,t)),r}async function sT(e,t,n){if(!e||t==null)return null;let r=(await He())[ue(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 Xe(ue(e,t)),r}async function kc(e,t){if(!e||t==null)return null;let n=(await He())[ue(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 Xe(ue(e,t)),n)}async function aT(){return Object.values(await He()).filter(e=>!xF.has(e.state))}async function An(){return Object.values(await He()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function iT(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await ut("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await He(),r=0;for(let[o,s]of Object.entries(n))await Et("prlinks",{id:KA(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var GA,Qh,ue,KA,wr,Xh,Zh,VA,kF,xF,Kn=q(()=>{Vr();GA=process.env.BOSTRAT_STATE_DIR||qA.join(process.env.HOME||yF.tmpdir(),".bostrat"),Qh=qA.join(GA,"pr-links.json"),ue=(e,t)=>`${e}#${Number(t)}`,KA=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=gF.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},wr=null,Xh=null,Zh=Promise.resolve(),VA=async()=>new Set;kF=new Set(["api","observed","reconciled"]);xF=new Set(["merged","closed","lost"])});import vF from"node:fs";import tw from"node:fs/promises";import dT from"node:path";import SF from"node:os";function mT(e,t=!1){if(!t&&e-lT<AF)return;lT=e;let n=JSON.stringify({windowStart:Ia,byCaller:Object.fromEntries(yr),lastHour:mo});uT=uT.then(()=>tw.mkdir(pT,{recursive:!0})).then(()=>tw.writeFile(`${$d}.tmp`,n)).then(()=>tw.rename(`${$d}.tmp`,$d)).catch(()=>{})}function hT(e){e-Ia<fT||(mo={start:Ia,total:Rd(yr),byCaller:Object.fromEntries(yr)},mo.total&&console.log(`[github] ${mo.total} calls last hour`,mo.byCaller),Ia=e,yr=new Map,mT(e,!0))}function br(e="other",t=Date.now()){hT(t),yr.set(e,(yr.get(e)||0)+1),mT(t)}function wT(e=Date.now()){return hT(e),{windowStart:Ia,total:Rd(yr),byCaller:Object.fromEntries(yr),lastHour:mo,backoffUntil:hs>e?hs:null}}function ho(e){return TF.test(String(e?.message||e||""))}function wo(e,t=Date.now()){return hs<=t&&console.warn(`[github] rate limited \u2014 backing off ${cT/6e4} min:`,String(e?.message||e||"").slice(0,200)),hs=Math.max(hs,t+cT),hs}function Od(e=Date.now()){return Math.max(0,hs-e)}var pT,$d,fT,cT,AF,Ia,yr,mo,hs,lT,Rd,uT,TF,xc=q(()=>{pT=process.env.BOSTRAT_STATE_DIR||dT.join(process.env.HOME||SF.tmpdir(),".bostrat"),$d=dT.join(pT,"github-metrics.json"),fT=60*6e4,cT=5*6e4,AF=6e4,Ia=Date.now(),yr=new Map,mo=null,hs=0,lT=0,Rd=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(vF.readFileSync($d,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<fT?(Ia=e.windowStart,yr=new Map(Object.entries(e.byCaller||{})),mo=e.lastHour||null):Rd(new Map(Object.entries(e.byCaller||{})))&&(mo={start:e.windowStart,total:Rd(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}uT=Promise.resolve();TF=/rate limit|too quickly|abuse detection|HTTP 429/i});import gT from"node:fs/promises";import yT from"node:path";import EF from"node:os";function Cd({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};Md.push(l),Md.length>IF&&Md.shift();let u=nw.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)),nw.set(e,u),$F(l),l}async function $F(e){try{await gT.mkdir(bT,{recursive:!0}),await gT.appendFile(_F,JSON.stringify(e)+`
|
|
184
|
-
`)}catch{}}function kT(){let e={};for(let[t,n]of nw)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:Md.slice(-20)}}var bT,_F,IF,Md,nw,Nd=q(()=>{bT=process.env.BOSTRAT_STATE_DIR||yT.join(process.env.HOME||EF.tmpdir(),".bostrat"),_F=yT.join(bT,"github-latency.ndjson"),IF=100,Md=[],nw=new Map});import{execFile as RF}from"node:child_process";import{promisify as OF}from"node:util";async function MF(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Ra("git",["-C",e,"rev-parse","HEAD"]),Ra("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 sn(e,t,n={}){let{caller:r="prs",...o}=n,s=await Dn({cwd:e});br(r);try{let{stdout:a}=await Ra("gh",t,{cwd:e,...CF,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw ho(a)&&wo(a),a}}async function sw(){if(Pd&&Date.now()-Pd.at<6e4)return Pd.data;let e;try{br("auth");let t=await Ra("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 Ii();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return Pd={at:Date.now(),data:e},e}async function NF(e){try{let{stdout:t}=await Ra("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function vT(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 rw(e){let t=(e||[]).map(vT),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 iw(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function AT(e,t){if(!t)return null;let n=await Oe(e);if(!n)return null;try{let r=await sn(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 TT(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 ET(e,t){let n=await AT(e,t);return n===null?{...rw([]),unavailable:!0}:rw(TT(n))}function Dd(e,t=rw(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 DF(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){$a.delete(s);continue}r.add(s),$a.has(s)||$a.set(s,n),o.mergeabilityPending=n-$a.get(s)<PF}for(let o of $a.keys())o.startsWith(`${e}#`)&&!r.has(o)&&$a.delete(o);return t}async function Oa(e,t){let n=await kr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await sn(n.path,["pr","view",String(r),"--json",aw]);return Dd(JSON.parse(o))}catch(o){if(!iw(o))throw o;let s=await sn(n.path,["pr","view",String(r),"--json",ST]),a=JSON.parse(s);return Dd(a,await ET(n.path,a.headRefOid))}}async function Vn(e,{caller:t="prs"}={}){try{let n=await sn(e,["pr","list","--json",aw,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>Dd(r)).sort(xT)}catch(n){if(!iw(n))throw n;let r=await sn(e,["pr","list","--json",ST,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>Dd(o,await ET(e,o.headRefOid))))).sort(xT)}}function an(){vc.clear()}async function LF(e,t){if(!e)return t;for(let n of t){let r=await pt(e,n.number).catch(()=>null);if(!r)continue;r.trackId&&(n.trackId=r.trackId),r.autoMerge&&(n.autoMergeArmed=!0),r.archiveOnMerge&&(n.archiveOnMerge=!0),r.notifyOnMerge&&(n.notifyOnMerge=!0);let o=s=>n.headSha&&s.headSha&&n.headSha!==s.headSha;r.ciFix&&(o(r.ciFix)?await Td(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await _d(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function _T(){let e=await sw();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await ur())await NF(a.path)&&t.push(a);let n=Date.now(),r=Od(n)>0,o=await Promise.all(t.map(async a=>{let i=vc.get(a.path),c=i?.pending?jF:BF;if(i&&(r||n-i.at<c))return i.entry;try{let l=await Oe(a.path).catch(()=>null),u=DF(a.path,await Vn(a.path,{caller:"tab"}),n);if(await LF(l,u),i?.entry?.prs)for(let f of u)i.entry.prs.find(m=>m.number===f.number)?.checks?.pending>0&&f.checks.total>0&&f.checks.pending===0&&Cd({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 vc.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:zn(l)}}})),s=new Set(t.map(a=>a.path));for(let a of vc.keys())s.has(a)||vc.delete(a);return{auth:e,repos:o}}async function kr(e){let t=(await ur()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function ws(e){return zs(e)}async function Ma(e,t,{method:n="squash"}={}){let r=await kr(e);if(!Sc.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 Dn({cwd:r.path})).GH_TOKEN){let a=await Xf().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await sn(r.path,s)}catch(a){throw new Error(zn(a))}finally{an()}return{ok:!0,number:o}}async function Ac(e,t){let n=await kr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await sn(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(zn(s))}finally{an()}let o=await Oe(n.path).catch(()=>null);return o&&await ZA(o,r).catch(()=>{}),{ok:!0,number:r}}async function cw(e,t){let n=await kr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await sn(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(zn(o))}finally{an()}return{ok:!0,number:r}}function lw(e){return e.green&&!e.isDraft}function WF(e){return e.isDraft?"draft":e.mergeable==="CONFLICTING"?`conflicts with ${e.baseRefName}`:e.checks.failed>0?"checks failing":e.checks.pending>0?"checks running":e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN"?"mergeability unresolved":e.mergeStateStatus==="BEHIND"?`behind ${e.baseRefName}`:e.mergeStateStatus==="BLOCKED"?"blocked by branch protection":`not clean (${e.mergeStateStatus})`}function IT(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:lw(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function $T(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function Tc(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 RT(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(Tc))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function OT(){let e={};for(let[t,n]of ow)e[t]={...n};return e}async function MT(e,{method:t="merge",onMerged:n=null}={}){let r=await kr(e);if(!Sc.has(t))throw new Error(`invalid merge method: ${t}`);if(ow.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Vn(r.path)).filter(lw).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 ow.set(r.path,s),GF(s,o,{listPrs:()=>Vn(r.path),merge:a=>Ma(r.path,a,{method:t}),sleep:HF,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function qF(e,t){let n=Date.now()+(t.settleMaxMs??UF),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(FF)}}async function GF(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 qF(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: ${zn(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(!lw(a)){e.skipped.push({number:a.number,title:a.title,reason:WF(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:zn(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),an()}}async function uw(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Ra("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${zn(l)}`)}await MF(e);let o=n||"";if((await Dn({cwd:e})).GH_TOKEN){let l=await Xf().catch(()=>null);l&&(o=`${o?`${o}
|
|
185
|
-
|
|
186
|
-
`:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",t.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await
|
|
153
|
+
`});function EB(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 yv(e,t,n){let r=eh(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 Wn(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,h)=>{h!=null&&(u.has(b)||u.set(b,[]),u.get(b).push(h))},p=(b,h)=>{let v=null;for(let B of[b,null])for(let Y of u.get(B)||[])Y>h&&(v==null||Y<v)&&(v=Y);return v},f=new Set,w=new Map,m=new Map,y=(b,h)=>{m.set(b,h),n.push(h)},k=b=>{let h=m.get(b);h&&(h.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 h=String(b.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(h))for(let v of h.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let B=v[0].split("/").pop().toLowerCase();x.has(B)||x.set(B,[]),x.get(B).push(b.payload?.ts??b.ts)}}let S=3*6e4,$=20*6e4,M=(b,h)=>!!(b?.name&&x.get(String(b.name).toLowerCase()))?.some(B=>B>=h-S&&B<=h+$);for(let b of e||[])if(b.type==="turn.retracted"){o.add(`${b.boxId}|${b.payload?.saidTs}`);let h=Ue(b.payload?.text);h&&(s.has(b.boxId)||s.set(b.boxId,[]),s.get(b.boxId).push(h))}else if(b.type==="turn.user"&&b.source==="orchestrator"){let h=Ue(b.payload?.text);h&&(a.has(b.boxId)||a.set(b.boxId,new Set),a.get(b.boxId).add(h))}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 h=b.payload?.source;h!=="compact"&&h!=="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 h=b.payload.note,v={status:h.status||null,summary:h.summary||null,resultHead:h.resultHead??null,ts:pe(b)};h.toolUseId&&l.set(h.toolUseId,v);let B=String(h.summary||"").match(g);B&&w.set(B[1],v)}let D=new Map,j=new Map,A=null,_=new Map,L=new Map,G=new Map,O=new Map,T=new Map,C=new Map,P=new Map,W=new Map,X=new Map,ge=new Map,Ie=new Map,Ce=new Map,Z=new Map,Ze=new Map,Ee=new Map,le=new Map,re=(b,h)=>{b.arm.status=h;let v=b.line,B=v.arms.map(Y=>Y.status);v.status=B.includes("armed")?"armed":B.includes("offered")?"offered":B.includes("replaced")?"replaced":"cancelled"},$e=[],J=new Set,he=new Map,Be=(b,h)=>{he.delete(b);let v=n.indexOf(b);v>=0&&n.splice(v,1),h!=null&&(b.ts=h),n.push(b)},R=b=>{if($e.push(b),!(b.box?J.has(b.box):J.size>0))return;let v=n.indexOf(b);v>=0&&n.splice(v,1),he.set(b,"held")},ie=b=>{if(!$e.length)return;let h=pe(b);for(let v of $e)v.status!=="open"||he.get(v)==="superseded"||v.box&&v.box!==b.boxId||(v.ts??0)>h||Be(v,h)},De=b=>{if(!$e.length)return;let h=pe(b);for(let v of $e)he.get(v)!=="held"||v.status!=="open"||v.box&&b.boxId&&v.box!==b.boxId||Be(v,Math.max(v.ts??0,h))},Ot=()=>{for(let[b,h]of[...he])h==="held"&&Be(b,null);$e.length=0,Q.clear()},Q=new Set,ke=b=>{for(let h of Q){if(h.status!=="open"||h.box&&b&&h.box!==b)continue;h.status="superseded";let v=n.indexOf(h);v>=0&&n.splice(v,1),he.set(h,"superseded"),_.delete(h.offerId)}Q.clear()},nt=new Map,Qe=new Map,Tt=new Map,Ht=new Map,lt=new Map,Mt=new Map,yt=new Map,hn=new Map,jo=()=>{for(let b of hn.values())b.questions.length&&n.push(b);hn.clear()},mt=null,Dn=new Map,Lr=6e4,tn=new Map,wn=(b,h)=>{let v=s.get(b);if(!v?.length)return!1;let B=Ue(h),Y=a.get(b);return v.some(we=>{if(!B.startsWith(we))return!1;let ce=Ue(B.slice(we.length));return!!ce&&!!Y?.has(ce)})};for(let b of e||[]){let h=b.payload||{};switch(b.type){case"turn.user":{if(b.source==="hook"){b.boxId&&J.add(b.boxId);break}if(Ot(),!String(h.text||"").trim()&&!h.images?.length&&!h.attachments?.length)break;{let v=uv(h.text);if(v){if(v.kind==="shell-input"){let B={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(B),Dn.set(b.boxId,B)}else{let B=Dn.get(b.boxId);B?(B.stdout=v.stdout||null,B.stderr=v.stderr||null,B.done=!0,Dn.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=h.note||uc(h.text);if(v){if(v.kind==="task"){if(v.toolUseId&&i.has(v.toolUseId))break;let B=String(v.summary||"").match(g);if(!v.toolUseId&&B&&f.has(B[1]))break}if(v.kind==="command"&&cv(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:pv(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"&&wn(b.boxId,h.text))break;if(h.kind==="merge-notify"||h.kind==="ship-notify"){n.push({id:b.id,kind:"event",type:h.kind==="ship-notify"?"ship-notified":"merge-notified",text:h.text,ts:pe(b),box:b.boxId,agent:b.provider});break}if(h.kind==="mock-picked"||h.kind==="ci-fix"||h.kind==="reconcile"||h.kind==="pr-go"||h.kind==="push-go"||h.kind==="spin-up-view"||h.kind==="prompt-answer"||h.kind==="followup-go"){h.kind==="prompt-answer"&&(mt=null);break}for(let v of _.values())v.status="stale";_.clear();for(let v of G.values())v.status="stale";G.clear();for(let v of Z.values())v.rows.some(B=>B.rowStatus==="sent")||(v.status="stale",Z.delete(v.offerId));mt&&mt.box===b.boxId&&(n.push(EB(mt)),mt=null),n.push({id:b.id,kind:"message",role:"user",text:h.text,ts:pe(b),box:b.boxId,agent:b.provider,boxRole:h.boxRole,...b.actor==="orchestrator"?{orchestration:h.kind||!0}:{},...h.sourceId?{sourceId:h.sourceId}:{},...h.attachments?.length?{attachments:h.attachments}:{},...h.images?.length?{images:h.images}:{}});break}case"peer.message":{let v=h.from||{};n.push({id:b.id,kind:"event",type:"peer-message",text:h.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(h.model==="<synthetic>"&&/^No response requested\.$/.test(String(h.text||"").trim()))break;let v=vx(h,{codexFailedText:Yo});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}jo();let B=(h.images||[]).filter(ht=>!M(ht,pe(b)));if(b.source==="hook"&&!String(h.text||"").trim()&&(De(b),b.boxId&&J.delete(b.boxId)),!String(h.text||"").trim()&&!B.length)break;if(b.provider==="cursor"){let ht=`${b.boxId}|${h.text}`;if(r.has(ht))break;r.add(ht)}(!mt||mt.box===b.boxId)&&(mt=null);let Y=b.provider==="codex"?(h.tools||[]).filter(ht=>!Om.has(ht)&&!Yi.has(ht)):h.tools||[],we=h.text,ce=null,ot=Ym(String(h.text||""));ot&&(we=ot.text,ce={questions:ot.questions}),n.push({id:b.id,kind:"message",role:"assistant",text:we,ts:pe(b),box:b.boxId,agent:b.provider,boxRole:h.boxRole,...ce?{ask:ce}:{},...Y.length?{tools:Y}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},...B.length?{images:B}:{}}),ie(b),ce&&(mt={id:b.id,groupId:Xm(b.provider,h.sourceId||b.id,ce.questions),provider:b.provider,box:b.boxId,questions:ce.questions,ts:pe(b)});break}case"job.started":{if(!h.id)break;let v=pe(b),B=l.get(h.id);if(!B&&(h.kind||"agent")==="agent"&&h.name){let nn=w.get(h.name);nn&&nn.ts>=v&&(B=nn)}let Y=B?null:c.get(h.id)||null,we=B||Y?null:p(b.boxId??null,v),ce={jobId:h.id,jobKind:h.kind||"agent",name:h.name||null,detail:h.detail||null,status:B?B.status==="killed"?"stopped":B.status&&B.status!=="completed"?"failed":"done":Y?"stalled":we!=null?"lost":"running",startedTs:v,finishedTs:B?.ts??Y?.ts??we??null,quietMs:Y?.quietMs??null,summary:B?.summary??null,resultHead:B?.resultHead??null},ot=tn.get(b.boxId),ht=ot?.jobs[ot.jobs.length-1]?.startedTs;if(ot&&ht!=null&&v-ht<=Lr)ot.jobs.push(ce);else{let nn={id:b.id,kind:"job-dock",dockId:h.id,ts:v,box:b.boxId,agent:b.provider,jobs:[ce]};tn.set(b.boxId,nn),n.push(nn)}break}case"handoff.done":n.push(wt(b,"lead-handoff"));break;case"subtask.delegated":n.push({...wt(b,"subtask-delegated"),to:b.boxId,toAgent:b.provider});break;case"subtask.merged":n.push(wt(b,"subtask-merged"));break;case"pr.state.changed":{if(!h.change)break;if(h.change==="opened"){for(let B of _.values())B.status="stale";_.clear();for(let B of G.values())B.status="stale";G.clear()}let v=wt(b,`pr-${h.change}`);if(v.box=b.boxId??null,gv.has(h.change)&&h.number!=null){let B=yv(D,j,h);if(D.size>=2){for(let we of j.values())for(let ce of we)ce.grouped=!0;v.groupPrs=[...D.values()];let Y=[v];for(let we of D.keys())j.set(we,Y)}else{let Y=j.get(B)??[];Y.push(v),j.set(B,Y)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let B=eh(h),Y=A&&!A.complete||D.size>=2;if(D.has(B))D.delete(B),j.delete(B);else if(h.number!=null)for(let we of[...D.keys()])we.endsWith(`#${h.number}`)&&(D.delete(we),j.delete(we));if(Y&&h.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:h.number,url:h.url??null,title:h.title??null,repo:h.repo??null,branch:h.branch??null,change:h.change,ts:pe(b)}),A.remaining=[...D.values()],A.complete=D.size===0;break}}n.push(v);break}case"pr.offered":{if(!h.offerId)break;ke(b.boxId);for(let B of _.values())B.status="stale";_.clear();for(let B of G.values())B.status="stale";G.clear();let v={id:b.id,kind:"pr-offer",offerId:h.offerId,excerpt:h.excerpt||null,branch:h.branch||null,base:h.base||null,offered:Array.isArray(h.offered)&&h.offered.length?h.offered:null,toPr:h.toPr&&typeof h.toPr=="object"?{number:h.toPr.number??null}:null,available:Array.isArray(h.available)?h.available:null,defaults:Array.isArray(h.defaults)?h.defaults:null,prState:h.prState||null,openPr:h.openPr&&typeof h.openPr=="object"?h.openPr:null,repos:Array.isArray(h.repos)&&h.repos.length?h.repos:null,status:"open",source:b.source==="cli"||h.via==="cli"?"cli":"transcript",ts:pe(b),box:b.boxId,agent:b.provider};_.set(h.offerId,v),L.set(h.offerId,v),n.push(v),Q.add(v),v.source==="cli"&&R(v);break}case"pr.offer.answered":{let v=L.get(h.offerId);v&&(v.status=h.action||"dismiss",v.actions=Array.isArray(h.actions)?h.actions:null,v.notify=h.notify===!0,h.existingPr&&typeof h.existingPr=="object"&&(v.existingPr={number:h.existingPr.number??null}),_.delete(h.offerId));break}case"pr.exists.offered":{if(!h.offerId)break;for(let B of G.values())B.status="stale";G.clear();let v={id:b.id,kind:"pr-exists-offer",offerId:h.offerId,srcOfferId:h.srcOfferId??null,branch:h.branch||null,base:h.base||null,pr:h.pr&&typeof h.pr=="object"?{number:h.pr.number??null,url:h.pr.url??null,title:h.pr.title??null}:null,actions:Array.isArray(h.actions)&&h.actions.length?h.actions:null,notify:h.notify===!0,status:"open",ts:pe(b),box:b.boxId,agent:b.provider};G.set(h.offerId,v),O.set(h.offerId,v),n.push(v);break}case"pr.exists.answered":{let v=O.get(h.offerId);v&&(v.status=h.choice||"dismiss",G.delete(h.offerId));break}case"box.state.changed":{let v=h.change==="model"?"model-change":h.change;if(!v)break;let B=wt(b,v);if(v==="model-change"&&(B.to=h.model),v==="node-update"&&B.latest){let Y=n.findIndex(we=>we.kind==="event"&&we.type==="node-update"&&we.latest===B.latest);if(Y>=0){let we=n[Y];n[Y]={...B,ts:we.ts,at:we.at,firstAt:we.firstAt??we.ts,updatedAt:B.ts};break}}n.push(B);break}case"track.transferred":case"track.recovered":n.push(wt(b,"track-moved"));break;case"view.state.changed":{if(h.change==="deleted"){k(`view:${h.viewId}`);break}if(h.change!=="created"||h.restarted)break;y(`view:${h.viewId}`,{id:b.id,kind:"view-card",viewId:h.viewId,name:h.name,slug:h.slug,port:h.port,origin:h.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:h.name||null,reason:h.reason||"no-view",ts:pe(b),box:b.boxId,agent:b.provider});break}case"mock.state.changed":{if(h.change==="picked"){n.push({id:b.id,kind:"mock-picked",mockId:h.mockId,name:h.name,group:h.group??null,version:h.version??null,siblings:h.siblings??[],ts:pe(b),box:b.boxId,agent:b.provider});break}if(h.change==="deleted"){k(`mock:${h.mockId}`);break}if(h.change!=="created")break;y(`mock:${h.mockId}`,{id:b.id,kind:"mock-card",mockId:h.mockId,name:h.name,group:h.group??null,ts:pe(b),box:b.boxId,agent:b.provider});break}case"flow.state.changed":{if(h.change==="deleted"){k(`flow:${h.flowId}`);break}if(h.change!=="created")break;y(`flow:${h.flowId}`,{id:b.id,kind:"flow-card",flowId:h.flowId,name:h.name,stepCount:h.stepCount??null,mockIds:h.mockIds??[],ts:pe(b),box:b.boxId,agent:b.provider});break}case"runbook.state.changed":{if(h.change==="deleted"){k(`runbook:${h.runbookId}`);break}if(h.change!=="created")break;y(`runbook:${h.runbookId}`,{id:b.id,kind:"runbook-card",runbookId:h.runbookId,name:h.name,ts:pe(b),box:b.boxId,agent:b.provider});break}case"ticket.linked":{n.push({id:b.id,kind:"ticket-card",provider:h.provider||"linear",key:h.key,url:h.url,title:h.title??null,linkSource:h.linkSource??null,ts:pe(b),box:b.boxId,agent:b.provider});break}case"box.fullscreen.detected":{if(!h.offerId)break;for(let B of P.values())B.box===(h.box||b.boxId)&&(B.status="stale",P.delete(B.offerId));let v={id:b.id,kind:"fullscreen-offer",offerId:h.offerId,status:"open",ts:pe(b),box:h.box||b.boxId,agent:h.agent||b.provider};P.set(h.offerId,v),W.set(h.offerId,v),n.push(v);break}case"box.fullscreen.answered":{let v=W.get(h.offerId);v&&(v.status=h.action==="restarted"?"restarted":h.action==="cleared"?"cleared":"dismiss",P.delete(h.offerId));break}case"ticket.move.offered":{if(!h.offerId)break;for(let B of T.values())B.key===h.key&&(B.status="stale",T.delete(B.offerId));let v={id:b.id,kind:"ticket-offer",offerId:h.offerId,provider:h.provider||"linear",key:h.key,url:h.url??null,phase:h.phase??null,from:h.from??null,to:h.to?.name??null,prNumber:h.prNumber??null,status:"open",ts:pe(b),box:b.boxId,agent:b.provider};T.set(h.offerId,v),C.set(h.offerId,v),n.push(v);break}case"ticket.move.answered":{let v=C.get(h.offerId);v&&(v.status=h.action==="moved"?"moved":"dismiss",T.delete(h.offerId));break}case"ticket.state.changed":{for(let v of T.values())v.key===h.key&&v.to===h.to&&(v.status="stale",T.delete(v.offerId));if(h.mode==="offer")break;n.push(wt(b,"ticket-state"));break}case"ticket.commented":{n.push({id:b.id,kind:"ticket-comment",key:h.key,url:h.url??null,excerpt:h.excerpt??null,ts:pe(b),box:b.boxId,agent:b.provider});break}case"ticket.flow.changed":n.push(wt(b,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let B of X.values())B.status="stale";X.clear();let v={id:b.id,kind:"ticket-create-offer",proposalId:h.proposalId,provider:h.provider||"linear",epic:h.epic??null,issues:Array.isArray(h.issues)?h.issues:[],team:h.team??null,needsTeam:!!h.needsTeam,linkCurrentTrack:!!h.linkCurrentTrack,status:"open",ts:pe(b),box:b.boxId,agent:b.provider};X.set(h.proposalId,v),ge.set(h.proposalId,v),n.push(v),R(v);break}case"ticket.create.answered":{let v=ge.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",v.createdCount=h.createdCount??null,v.failedCount=h.failedCount??null,X.delete(h.proposalId));break}case"loop.create.offered":{if(!h.proposalId)break;for(let B of Ie.values())B.status="stale";Ie.clear();let v={id:b.id,kind:"loop-create-offer",proposalId:h.proposalId,name:h.name??"",schedule:h.schedule??"",prompt:h.prompt??"",expectation:h.expectation??null,notify:h.notify??"off",assignment:h.assignment??{provider:"claude"},repo:h.repo??null,nodeId:h.nodeId??null,nodeName:h.nodeName??null,status:"open",ts:pe(b),box:b.boxId,agent:b.provider};Ie.set(h.proposalId,v),Ce.set(h.proposalId,v),n.push(v),R(v);break}case"loop.create.answered":{let v=Ce.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",Ie.delete(h.proposalId));break}case"followup.armed":{if(!h.armId)break;let v=h.replaces?Ee.get(h.replaces):null;v&&v.arm.status==="armed"&&re(v,"replaced");let B={armId:h.armId,seq:Number.isInteger(h.seq)?h.seq:0,label:h.label??"",prompt:h.prompt??"",why:h.why??null,standing:h.standing==="never"?"never":"offerable",status:"armed"},Y=h.groupId??null,we=Y?le.get(Y):null;we||(we={id:b.id,kind:"followup-armed",groupId:Y,armId:B.armId,trigger:h.trigger??"now",label:B.label,prompt:B.prompt,why:B.why,planWhy:h.planWhy??null,arms:[],status:"armed",ts:pe(b),box:b.boxId,agent:b.provider},Y&&le.set(Y,we),we.trigger!=="now"&&n.push(we)),we.arms.push(B),Ee.set(h.armId,{arm:B,line:we});break}case"followup.armed.cancelled":{let v=Ee.get(h.armId);v&&v.arm.status==="armed"&&re(v,"cancelled");break}case"followup.offered":{if(!h.offerId)break;for(let Y of Z.values())Y.trigger===h.trigger&&(Y.status="stale",Z.delete(Y.offerId));let v=Array.isArray(h.rows)?h.rows:[];for(let Y of v){let we=Y.armId?Ee.get(Y.armId):null;we&&we.arm.status==="armed"&&re(we,"offered")}let B={id:b.id,kind:"followup-offer",offerId:h.offerId,trigger:h.trigger??"now",repo:h.repo??null,context:h.context&&typeof h.context=="object"?h.context:{},groupId:h.groupId??null,why:h.why??null,rows:v.map(Y=>({followupId:Y.followupId??null,armId:Y.armId??null,label:Y.label??"",prompt:Y.prompt??null,promptPreview:Y.promptPreview??null,promptHash:Y.promptHash??null,origin:Y.origin??"operator",why:Y.why??null,groupId:Y.groupId??null,seq:Number.isInteger(Y.seq)?Y.seq:0,standing:Y.standing==="never"?"never":"offerable",promotable:Y.armId?Y.promotable!==!1:!1,promotableReason:Y.promotableReason??null,rowStatus:"open"})),status:"open",answer:null,answers:[],ts:pe(b),box:b.boxId,agent:b.provider};Z.set(h.offerId,B),Ze.set(h.offerId,B),n.push(B),B.trigger==="now"&&R(B);break}case"followup.answered":{let v=Ze.get(h.offerId);if(!v)break;let B=new Set;h.armId&&B.add(`arm:${h.armId}`),h.followupId&&B.add(`fu:${h.followupId}`);for(let ce of Array.isArray(h.armIds)?h.armIds:[])B.add(`arm:${ce}`);for(let ce of Array.isArray(h.followupIds)?h.followupIds:[])B.add(`fu:${ce}`);let Y=h.action==="sent"?"sent":h.action==="removed"?"removed":"dismiss";for(let ce of v.rows){if(ce.rowStatus!=="open")continue;let ot=ce.armId?`arm:${ce.armId}`:`fu:${ce.followupId}`;B.size&&!B.has(ot)||(ce.rowStatus=Y)}let we={followupId:h.followupId??null,armId:h.armId??null,label:h.label??null,promoted:h.promoted??null,revived:h.revived===!0,action:Y,batchId:h.batchId??null};v.answers.push(we),v.answer=we,v.rows.some(ce=>ce.rowStatus==="open")?v.status="open":(v.status=v.rows.some(ce=>ce.rowStatus==="sent")?"sent":v.rows.some(ce=>ce.rowStatus==="removed")?"removed":"dismiss",Z.delete(h.offerId));break}case"followup.create.offered":{if(!h.proposalId)break;for(let B of nt.values())B.status="stale";nt.clear();let v={id:b.id,kind:"followup-create-offer",proposalId:h.proposalId,label:h.label??"",prompt:h.prompt??"",trigger:h.trigger??"pr.merged",repo:h.repo??null,why:h.why??null,status:"open",created:null,ts:pe(b),box:b.boxId,agent:b.provider};nt.set(h.proposalId,v),Qe.set(h.proposalId,v),n.push(v),R(v);break}case"followup.create.answered":{let v=Qe.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",nt.delete(h.proposalId));break}case"followup.created":{let v=h.proposalId?Qe.get(h.proposalId):null;v&&(v.created={followupId:h.followupId??null});break}case"loop.created":{let v=h.proposalId?Ce.get(h.proposalId):null;v&&(v.created={loopId:h.loopId??null,nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null}),n.push(wt(b,"loop-created"));break}case"loop.update.offered":case"loop.delete.offered":{if(!h.proposalId||!h.loopId)break;for(let Y of Tt.values())Y.loopId===h.loopId&&(Y.status="stale",Y.staleReason="superseded",Tt.delete(Y.proposalId));let v=b.type==="loop.update.offered",B={id:b.id,kind:v?"loop-update-offer":"loop-delete-offer",proposalId:h.proposalId,loopId:String(h.loopId),name:h.name??h.base?.name??"",base:h.base??null,...v?{patch:h.patch??{},changed:Array.isArray(h.changed)?h.changed:Object.keys(h.patch||{})}:{reason:h.reason??""},selfEdit:!!h.selfEdit,status:"open",staleReason:null,ts:pe(b),box:b.boxId,agent:b.provider};Tt.set(h.proposalId,B),Ht.set(h.proposalId,B),n.push(B),R(B);break}case"loop.update.answered":{let v=Ht.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="applied"?"applied":"dismiss",Tt.delete(h.proposalId));break}case"loop.delete.answered":{let v=Ht.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="delete"?"deleted":h.action==="pause"?"paused":"keep",Tt.delete(h.proposalId));break}case"loop.updated":{let v=h.proposalId?Ht.get(h.proposalId):null;v&&(v.applied={changed:Array.isArray(h.changed)?h.changed:[],nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null}),n.push(wt(b,"loop-updated"));break}case"loop.deleted":n.push(wt(b,"loop-deleted"));break;case"loop.paused":n.push(wt(b,"loop-paused"));break;case"planning.change.offered":{if(!h.proposalId||!Array.isArray(h.ops)||!h.ops.length)break;for(let B of lt.values())B.status="stale",B.staleReason="superseded",lt.delete(B.proposalId);let v={id:b.id,kind:"planning-offer",proposalId:h.proposalId,why:h.why??"",ops:h.ops,base:h.base??{},resolved:h.resolved??{},snapped:!!h.snapped,createsMeRow:!!h.createsMeRow,status:"open",staleReason:null,applied:null,ts:pe(b),box:b.boxId,agent:b.provider};lt.set(h.proposalId,v),Mt.set(h.proposalId,v),n.push(v),R(v);break}case"planning.change.answered":{let v=Mt.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="applied"?"applied":"dismiss",lt.delete(h.proposalId));break}case"planning.changed":{let v=h.proposalId?Mt.get(h.proposalId):null;v&&(v.applied={results:Array.isArray(h.results)?h.results:[]}),n.push(wt(b,"planning-changed"));break}case"rework.cause.offered":{if(!h.sha||yt.has(h.sha))break;let v={id:b.id,kind:"rework-cause-offer",sha:h.sha,subject:h.subject??"",linesReworked:Number(h.linesReworked)||0,origins:Array.isArray(h.origins)?h.origins:[],heuristic:h.heuristic??null,intent:h.intent??null,shas:Array.isArray(h.shas)?h.shas:null,pr:h.pr??null,retracts:0,status:"open",cause:null,ts:pe(b),box:b.boxId,agent:b.provider};yt.set(h.sha,v),n.push(v);break}case"rework.cause.answered":{let v=h.sha?yt.get(h.sha):null;v&&(v.status="answered",v.cause=h.cause??null);break}case"rework.cause.retracted":{let v=h.sha?yt.get(h.sha):null;v&&(v.status="open",v.cause=null,v.retracts=(v.retracts??0)+1);break}case"ticket.created":{let v={items:Array.isArray(h.items)?h.items:[],failed:Array.isArray(h.failed)?h.failed:[],team:h.team??null},B=h.proposalId?ge.get(h.proposalId):null;if(B){B.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(!h.kind)break;n.push(wt(b,h.kind));break}case"provider.error":n.push({...wt(b,"provider-error"),provider:b.provider,box:b.boxId,agent:b.provider});break;case"usage.cap.hit":n.push(wt(b,"usage-cap-hit"));break;case"usage.cap.warning":n.push(wt(b,"usage-cap-warning"));break;case"provider.connected":{n.push({id:b.id,kind:"event",type:"provider-connected",provider:h.provider||b.provider,ts:pe(b),at:pe(b),box:b.boxId,agent:b.provider});break}case"prompt.answered":{if(mt=null,h.control||!h.question)break;let v=h.groupId||h.promptId;if(!v)break;let B=hn.get(v);B||(B={id:b.id,kind:"prompt",groupId:v,provider:b.provider,box:b.boxId,agent:b.provider,questionTotal:h.questionTotal??1,questions:[],ts:pe(b)},hn.set(v,B));let Y=h.questionIndex??0,we={index:Y,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},ce=B.questions.findIndex(ht=>ht.index===Y&&Ue(ht.question)===Ue(we.question));ce>=0?B.questions[ce]=we:B.questions.push(we),B.questions.sort((ht,qs)=>ht.index-qs.index),B.questionTotal=Math.max(B.questionTotal,h.questionTotal??1),B.questions.length>=B.questionTotal&&(n.push(B),hn.delete(v));break}case"prompt.dismissed":{mt=null;let v=h.groupId||h.promptId;if(!v||!h.question)break;let Y=hn.get(v)||{id:b.id,kind:"prompt",groupId:v,provider:b.provider,box:b.boxId,agent:b.provider,questionTotal:h.questionTotal??1,questions:[],ts:pe(b)},we=h.questionIndex??0;Y.questions.some(ce=>ce.index===we&&Ue(ce.question)===Ue(h.question))||(Y.questions.push({index:we,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:[]}),Y.questions.sort((ce,ot)=>ce.index-ot.index)),Y.answeredVia="chat",n.push(Y),hn.delete(v);break}default:break}}for(let[b,h]of[...he]){if(h==="superseded"){b.status!=="superseded"&&Be(b,null);continue}(b.status!=="open"||t-(b.ts??0)>=_B)&&Be(b,null)}let bi=m.size?n.filter(b=>!b.retired):n;return $B(bi.sort((b,h)=>(b.ts??0)-(h.ts??0)))}function $B(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<=IB){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 th(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:RB(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??Wn(e)).filter(o=>o.kind==="message");return mx(n,r)}function RB(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==ua(e.command)?t:Yr(e.command,e.description||"")}function wt(e,t){let{ts:n,change:r,kind:o,...s}=e.payload||{};return{id:e.id,kind:"event",type:t,...s,at:pe(e),ts:pe(e)}}function bv(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)+OB,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&MB.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function kv(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!NB.has(r.type)||!r.box)continue;let o=(r.ts??0)+CB,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 Vu(e){return(e||[]).filter(t=>PB.has(t?.kind)&&t.status==="open")}function xv(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(gv.has(o.change)&&o.number!=null)yv(t,n,o);else if(o.change==="merged"||o.change==="closed"||o.change==="lost"){let s=eh(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,gv,eh,_B,IB,OB,MB,CB,NB,PB,zu=q(()=>{da();Xo();Cu();oc();Ku();Qm();pe=e=>e.payload?.ts??e.ts;gv=new Set(["opened","ci-fixed","reconciled","pushed"]),eh=e=>`${e.repo??""}#${e.number}`;_B=3e4;IB=2*6e4;OB=12e4,MB=new Set(["archived","unarchived","opened"]);CB=2500,NB=new Set(["pr-opened","pr-merged","pr-auto-armed"]);PB=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 FB(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 HB(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 WB(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<jB&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<LB&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function Tv(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=FB(e),s=HB(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=Wn(e??[]).filter(p=>p?.kind==="message"&&typeof p.text=="string"&&p.text.trim()).slice(-DB);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(Av[r]??Av.failover),l.push(""),o.lastPush?.sha){let p=Sv(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=Sv(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=WB(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"}:** ${UB(p.text,BB)}`),l.push("")}l.push("---"),l.push("Resume: continue the objective above from the verified repository state; ask the operator before redoing anything the log says was already done.");let d=l.join(`
|
|
154
|
+
`);return d.length<=vv?d:`${d.slice(0,vv)}
|
|
155
|
+
|
|
156
|
+
[\u2026 brief truncated]`}var DB,BB,vv,jB,LB,UB,Sv,Av,Ev=q(()=>{zu();DB=12,BB=2e3,vv=48e3,jB=30,LB=10;UB=(e,t)=>{let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1)}\u2026`},Sv=(e,t)=>e&&t?Math.max(1,Math.round((e-t)/6e4)):null;Av={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 eo(e){let t=e.search(sh);return(t>=0?e.slice(0,t):e).trim()}function qB(e){if(!e)return"";let t=e.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!t||/hidden|\d+\s+lines?/i.test(t)?"":t}function GB(e){let t=String(e).trim();return!!(!t||Qr.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 KB(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=VB(n,t);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function VB(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 _v(e){return e.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function zB(e,t=""){let n=String(e);if(!$v.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
157
|
+
`),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(es);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let p=r[u].trim();p&&!Qr.test(r[u])&&a.push(p)}}if(s.length<2)return null;let i=s.map(u=>({label:eo(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=["Switch model?",...a].join(" ");return{provider:"claude",box:t,multiSelect:!1,cursorIndex:c,promptId:`claude:model-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Model",multiSelect:!1,options:i}]}}function YB(e,t=""){let n=String(e).replace(/\r/g,"").split(`
|
|
158
|
+
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!Rv.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(es);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&&!(JB.test(n[c])||Qr.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:eo(c.label),description:""}));return{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,s.findIndex(c=>c.cursor)),promptId:`claude:confirm-gate:${i.map(c=>c.label).join("|")}`.slice(0,240),questions:[{question:a.join(" ")||"Claude Code is asking you to confirm",header:"Confirm",multiSelect:!1,options:i}]}}function XB(e,t=""){let n=String(e);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
159
|
+
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(es);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 QB(e,t=""){let n=String(e);if(!nh.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
160
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(ZB.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(es);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let p=r[u].trim();p&&!Qr.test(r[u])&&a.push(p)}}if(s.length<2||!s.some(u=>nh.test(u.label)))return null;let i=s.map(u=>({label:eo(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=a.join(" ");return{provider:"claude",box:t,multiSelect:!1,cursorIndex:c,promptId:`claude:resume-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Resume",multiSelect:!1,options:i}]}}function ah(e){let t=[];for(let r=0;r<e.length;r++){let o=e[r].match(es);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 ej(e,t=""){let n=String(e);if(!Ov.test(n)||!Mv.test(n)||ts.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
161
|
+
`),o=ah(r);return o?{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"theme",promptId:`claude:setup:theme:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks: choose the text style that looks best with your terminal. You can change it later with /theme.",header:"Theme",multiSelect:!1,options:o.map(s=>({label:eo(s.label),description:""}))}]}:null}function tj(e,t=""){let n=String(e);if(!Cv.test(n)||ts.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
162
|
+
`),o=ah(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:eo(i),description:c.join("\xB7").trim()}});return{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(a=>a.cursor)),setup:"login-method",promptId:`claude:setup:login-method:${s.map(a=>a.label).join("|")}`.slice(0,200),questions:[{question:"How do you want to sign in to Claude? A Claude subscription (Pro/Max/Team) uses your existing plan; a Console account bills API usage. Your login stays on this box \u2014 bostrat never sees it.",header:"Log in",multiSelect:!1,options:s}]}}function rj(e,t){let n=e[t].trim();for(let r=t+1;r<e.length;r++){let o=e[r].trim();if(!o||!nj.test(o)||ih.test(e[r]))break;n+=o}return n}function oj(e,t=""){let n=String(e);if(!ih.test(n)||ts.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
163
|
+
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=rj(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 aj(e,t=""){let n=String(e);if(!rh.test(n)||!sj.test(n)||ts.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
164
|
+
`)].reverse().find(a=>rh.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 ij(e,t=""){let n=String(e);if(!Nv.test(n)||ts.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
165
|
+
`),o=ah(r);return o?{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"trust",promptId:`claude:setup:trust:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks whether it can read, edit, and run files in this workspace. This box's workspace is bostrat-managed scratch space, so trusting it is safe.",header:"Folder trust",multiSelect:!1,options:o.map(s=>({label:eo(s.label),description:""}))}]}:null}function cj(e,t=""){let n=String(e);if(ts.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
166
|
+
`),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(Pv);if(!s)return null;let a=[];for(let l=o-1;l>=0&&a.length<3;l--){let u=r[l].trim();if(!u){if(a.length)break;continue}if(Qr.test(r[l])||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 lj(e){let t=-1;for(let r=e.length-1;r>=0;r--)if(oh.test(e[r])){t=r;break}if(t<0)return null;let n=-1;for(let r=t-1;r>=0;r--)if(/[☐☒]/.test(e[r])){n=r;break}return n<0?null:{headerLine:n,chatLine:t,optStart:n+1,optEnd:t}}function uj(e,t){let n=e.find(s=>/^type something\b/i.test(s.label));if(n)return n;if(e.length<2)return null;let r=e[e.length-1];if(Ju.test(r.label))return null;let o=null;for(let s=r.line+1;s<Math.min(t.length,r.line+3);s++){let a=t[s].replace(/^\s*❯/,"").trim();if(a){o=a;break}}return o&&(Qr.test(o)||/^submit\b/i.test(o))?r:null}function dj(e,t=""){let n=String(e);if(!Iv.test(n)||!oh.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
167
|
+
`),o=lj(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let _=i;_<c;_++){let L=r[_].match(es);L&&(u<0&&(u=_),l.push({line:_,cursor:!!L[1],checked:L[3]!=null&&L[3]!==" ",hasCheckbox:L[3]!=null,label:L[4].trim(),desc:""}))}if(!l.length)return null;for(let _ of l)for(let L=_.line+1;L<Math.min(r.length,_.line+4)&&!(es.test(r[L])||Qr.test(r[L]));L++){let G=r[L].trim();if(!(!G||GB(r[L])||Ju.test(G))){_.desc=qB(G);break}}let d=uj(l,r),p=l.filter(_=>_!==d&&!Ju.test(_.label));if(!p.length)return null;let f=p.some(_=>_.hasCheckbox),w=d?.cursor?p.length:Math.max(0,p.findIndex(_=>_.cursor)),m=d&&!Ju.test(d.label)?eo(d.label):"",y=s>=0?s+1:0,k=[];for(let _=y;_<u;_++){let L=r[_].trim();L&&!Qr.test(r[_])&&!/[☐☒]/.test(r[_])&&k.push(L)}let g=k.join(" ")||(s>=0?_v(r[s]):"")||"The agent is asking a question",x=s>=0?KB(r[s],g):null,S=x?.currentTabLabel||(s>=0?_v(r[s]):""),$=p.map(_=>({label:eo(_.label),description:_.desc})),M=$.map(_=>_.label).join("|"),j=`claude:${x?`q${x.questionIndex+1}/${x.questionTotal}`:"q1/1"}:${S}:${g}:${M}`.slice(0,240),A=x?`claude:ask:${x.tabs.map(_=>_.label).join("|")}`.slice(0,240):j;return{provider:"claude",box:t,multiSelect:f,cursorIndex:w,promptId:j,groupId:A,...d?{custom:!0,customIndex:p.length,customText:m}:{},dismissable:!0,...x?{questionIndex:x.questionIndex,questionTotal:x.questionTotal,currentTabLabel:x.currentTabLabel,tabs:x.tabs}:{},questions:[{question:g,header:S,multiSelect:f,options:$}]}}function to(e,t){let n=String(t||"");return n?e==="claude"?Iv.test(n)&&oh.test(n)||$v.test(n)||nh.test(n)||Rv.test(n.trimEnd().split(`
|
|
168
|
+
`).pop()||"")?!0:ts.test(n)?!1:Ov.test(n)&&Mv.test(n)||Cv.test(n)||ih.test(n)||rh.test(n)||Nv.test(n)||Pv.test(n.trimEnd().split(`
|
|
169
|
+
`).pop()||""):e==="codex"&&/Sign in with ChatGPT|one-time code|Paste or type your API key|link to authenticate|Successfully logged in\.[\s\S]*Press Enter to continue\./i.test(n)?!0:e==="cursor"||e==="codex"?/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i.test(n):!1:!1}function vn(e,t){return!e||!t?null:e.agent==="claude"?XB(t,e.name)||dj(t,e.name)||zB(t,e.name)||QB(t,e.name)||aj(t,e.name)||oj(t,e.name)||tj(t,e.name)||ej(t,e.name)||ij(t,e.name)||cj(t,e.name)||YB(t,e.name):e.agent==="cursor"||e.agent==="codex"?Nx(e.agent,t,e.name):null}async function Dv(e){if(!e)return null;let t=await Gt(e.name).catch(()=>"");return vn(e,t)}function ch(e,t){let n=Array.isArray(t?.[0])?t[0].filter(a=>Number.isInteger(a)&&a>=0):[],r=Number.isInteger(e?.cursorIndex)?e.cursorIndex:0,o=(a,i)=>{let c=i-a;return Array.from({length:Math.abs(c)},()=>c>0?"Down":"Up")},s=[];if(e?.multiSelect){let a=r;for(let i of[...n].sort((c,l)=>c-l))s.push(...o(a,i),"Enter"),a=i;s.push("Tab","Enter")}else{let a=n.length?n[0]:0;s.push(...o(r,a),"Enter")}return s}function Bv(e,t,n=""){let r=typeof n=="string"?n.trim():"";if(!r||!e?.custom)return{pre:ch(e,t).map(d=>({key:d})),text:"",post:[]};let o=Array.isArray(t?.[0])?t[0].filter(d=>Number.isInteger(d)&&d>=0):[],s=Number.isInteger(e.customIndex)?e.customIndex:e.questions?.[0]?.options?.length??0,a=Number.isInteger(e.cursorIndex)?e.cursorIndex:0,i=(d,p)=>{let f=p-d;return Array.from({length:Math.abs(f)},()=>({key:f>0?"Down":"Up"}))},c=[],l=a,u=typeof e.customText=="string"?e.customText.length:0;if(u>0&&a===s&&c.push(...Array.from({length:u+2},()=>({key:"BSpace"}))),e.multiSelect){for(let d of[...o].sort((p,f)=>p-f))c.push(...i(l,d),{key:"Enter"}),l=d;return c.push(...i(l,s)),{pre:c,text:r,post:[{key:"Down"},{key:"Enter"}]}}return c.push(...i(l,s)),{pre:c,text:r,post:[{key:"Enter"}]}}function jv(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 Iv,oh,es,Ju,Qr,sh,$v,Rv,JB,nh,ZB,ts,Ov,Mv,Cv,ih,nj,rh,sj,Nv,Pv,lh=q(()=>{dt();Du();Iv=/to navigate\b.*\bEsc to cancel|Enter to select\b/,oh=/Chat about this/i,es=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Ju=/^(type something|chat about this|submit)\b/i,Qr=/^[─—-]{6,}\s*$/,sh=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;$v=/^\s*Switch model\?\s*$/m;Rv=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,JB=/^\s*[─—▔-]{6,}\s*$/;nh=/Resume from summary/i,ZB=/^\s*This session is\b/;ts=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;Ov=/Choose the text style that looks best with your terminal/,Mv=/Let's get started\./;Cv=/Select login method:/;ih=/Paste code here if prompted/,nj=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;rh=/OAuth error:/,sj=/Press Enter to retry\./;Nv=/Quick safety check: Is this a project you created or one you trust/;Pv=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function Yu(e,t,n){if(!e||!t)return null;for(let r of pj)if(r.provider===e.agent)try{if(r.match(t,n))return r}catch{}return null}function mj(e){let t=uh.get(e);return t||(t={sentAt:[],last:0,emitted:new Set},uh.set(e,t)),t}function Uv(e,t,n=Date.now()){let r=mj(e);if(r.sentAt=r.sentAt.filter(s=>n-s<Fv),r.sentAt.length>=Lv)return{action:"give-up"};if(n-r.last<fj)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 Hv(e,t=Date.now()){let n=uh.get(e);return n?n.sentAt.filter(r=>t-r<Fv).length>=Lv:!1}var pj,fj,Lv,Fv,uh,dh=q(()=>{pj=[{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||""}
|
|
170
|
+
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];fj=5e3,Lv=5,Fv=3600*1e3,uh=new Map});function Xu(e,t,n){let{state:r,reason:o}=Qi(t,n);if(r!=="unknown"){if(e){let a=Date.now();for(let[i,c]of dc)a-c.at>=Wv()&&dc.delete(i);dc.set(e,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!e)return!1;let s=dc.get(e);return s?Date.now()-s.at>=Wv()?(dc.delete(e),!1):s.busy:!1}async function pc(e,t){let n={working:!1,prompted:!1};if(!e)return n;let r=await Pe(e,{lines:Ve}).catch(()=>"");if(!Xu(e,t,r))return Lu(t,r)?{working:!0,prompted:!1}:n;if(to(t,r)){let o=await Gt(e).catch(()=>r),s=vn({name:e,agent:t},o);if(s)return!Yu({name:e,agent:t},s,o)||Hv(e)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function ns(e,t){return(await pc(e,t)).working}var Wv,dc,wa=q(()=>{dt();Qo();lh();dh();Wv=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,dc=new Map});function vj(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 Aj(e){if(e.type==="queue-operation"&&e.operation==="enqueue")return qv.test(String(e.content||""))?String(e.content):null;if(e.type==="attachment"){let n=e.attachment;return n?.type==="queued_command"&&n.commandMode==="task-notification"&&typeof n.prompt=="string"?n.prompt:null}let t=e.message?.content;return typeof t=="string"&&qv.test(t)?t:null}async function Gv(e,t={}){return(await Vv(e,t)).launches}async function Kv(e,t={}){let{launches:n,done:r}=await Vv(e,t);return n.filter(o=>!r.has(o.id))}async function Vv(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await Bt(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(`
|
|
171
|
+
`)){let f=p.trim();if(!f||f[0]!=="{")continue;let w;try{w=JSON.parse(f)}catch{continue}if(w.isSidechain)continue;let m=Aj(w);if(m){let g=m.match(Sj)?.[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?ua(x):null),detail:x?ua(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(kj.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=Tj(g.content);if(hj.test(x)){s.add(g.tool_use_id);let D=x.match(bj)?.[1];D&&l.set(D,g.tool_use_id)}let S=x.match(yj)?.[1];S&&l.set(S,g.tool_use_id);let $=x.match(wj);if($){l.set($[1].trim(),g.tool_use_id);let D=c.get(g.tool_use_id);D&&(c.delete(g.tool_use_id),D.proven=!0,D.outputFile=x.match(gj)?.[1]?.replace(/\.$/,"")||null,o.set(D.id,D))}let M=u.get(g.tool_use_id);if(M){u.delete(g.tool_use_id);let{message:D,taskId:j}=vj(x);if(!g.is_error&&xj.test(D)){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 hj,wj,gj,yj,bj,kj,xj,qv,Sj,Tj,zv=q(()=>{xn();da();hj=/^Async (?:agent|task) launched/i,wj=/^Command did not complete within its [^\n]*?moved to the background \(ID: ([^)]+)\)/i,gj=/Output is being written to:\s*(\S+)/i,yj=/^Command running in background with ID:\s*([^\s.]+)/i,bj=/\bagentId:\s*([A-Za-z0-9_-]+)/,kj=new Set(["TaskStop","KillShell"]),xj=/^Successfully (?:stopped|killed)\b/i;qv=/^\s*<task-notification>/i,Sj=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i;Tj=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 no(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 Ej(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 Jv(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:Sn(`read ${no(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${ga(r)}`:`Read ${ga(r)}`}}case"Grep":{let r=t?.pattern;if(!r)return null;let o=t?.path?no(t.path,n):null,s=t?.glob?` --glob ${ph(t.glob)}`:"",a=Sn(String(r),40);return{command:Sn(`grep ${ph(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?no(t.target_directory,n):null;return{command:Sn(`glob ${ph(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${Sn(String(r),40)}\u201D`}}case"StrReplace":return t?.path?{command:Sn(`edit ${no(t.path,n)}`,140),description:`Edit ${ga(t.path)}`}:null;case"Write":return t?.path?{command:Sn(`write ${no(t.path,n)}`,140),description:`Write ${ga(t.path)}`}:null;case"Delete":return t?.path?{command:Sn(`delete ${no(t.path,n)}`,140),description:`Delete ${ga(t.path)}`}:null;case"ReadLints":{let o=(Array.isArray(t?.paths)?t.paths:[]).map(s=>no(s,n)).filter(Boolean);return{command:Sn(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=Ej(t);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:Sn(`apply_patch ${no(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${ga(r[0])}`}}case"Await":{let r=t?.shell_id??t?.task_id;return{command:Sn(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var Zu,Sn,ga,ph,Yv=q(()=>{Zu=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),Sn=(e,t=88)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,ga=e=>String(e).replace(/\/+$/,"").split("/").pop()||String(e);ph=e=>`"${String(e).replace(/"/g,'\\"')}"`});import fc from"node:fs";import _j from"node:fs/promises";import rs from"node:path";import Ij from"node:os";import $j from"node:crypto";import Rj from"node:zlib";function Qv(e){let t=$j.createHash("md5").update(String(e)).digest("hex");return rs.join(Oj,t)}function eS(e){try{let t=JSON.parse(fc.readFileSync(rs.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 Mj(e){let t=Qv(e),n;try{n=fc.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=rs.join(t,o.name),a=rs.join(s,"store.db");if(!fc.existsSync(a))continue;let i=eS(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 Qu(e,t){if(!e)return null;if(t){let n=rs.join(Qv(e),t),r=rs.join(n,"store.db");if(fc.existsSync(r))return{id:t,dir:n,store:r,...eS(n)||{createdAtMs:null,updatedAtMs:null}}}return Mj(e)}function Xv(e,t){let n=e.prepare("SELECT data FROM blobs WHERE id = ?").get(t);return n?Buffer.from(n.data):null}function Cj(e){if(!e)return null;let t=e;if(t.length>1&&t[0]===31&&t[1]===139)try{t=Rj.gunzipSync(t)}catch{}try{return JSON.parse(t.toString("utf8"))}catch{return null}}function Nj(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 Pj(e,t){if(!Array.isArray(e))return[];let n=[];for(let r of e){if(!r||r.type!=="tool-call"||!Zu.has(r.toolName))continue;let o=Jv(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 fh(e,t){if(!Zv)return ya;let{DatabaseSync:n}=Zv,r;try{r=new n(e,{readOnly:!0})}catch{return ya}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 ya;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return ya}let a=s.lastUsedModel||null,i=s.latestRootBlobId?Xv(r,s.latestRootBlobId):null;if(!i)return{...ya,model:a};let c=[],l=[],u=[],d=null,p=[];for(let f of Nj(i)){let w=Cj(Xv(r,f));if(!w)continue;let m=w.providerOptions?.cursor;if(m?.requestId&&p.push(m.requestId),m?.modelProviderMessageId&&p.push(m.modelProviderMessageId),w.role!=="assistant")continue;if(Array.isArray(w.content))for(let g of w.content){let x=g?.providerOptions?.cursor?.modelName;x&&(d=x)}l.push(...Pj(w.content,t).map((g,x)=>({...g,id:g.id||`${f}:${x}`}))),u.push(...Bj(w.content));let y=Dj(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 ya}finally{try{r.close()}catch{}}}function tS(e,t){let n=e.updatedAtMs;if(!n)try{n=Math.round(fc.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 nS(e,t,{limit:n=80}={}){let r=Qu(e,t);if(!r)return[];let{turns:o,model:s}=await fh(r.store,e);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=tS(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 rS(e,t){let n=Qu(e,t);if(!n)return null;let{turns:r,requestIds:o,model:s}=await fh(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 oS(e,t,{limit:n=80}={}){let r=Qu(e,t);if(!r)return[];let{turns:o,trailingCommands:s}=await fh(r.store,e);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=tS(r,a.length||1),l=[];a.forEach((u,d)=>{for(let p of u.commands)l.push({...p,summary:Yr(p.command,p.description),ts:c(d)})});for(let u of s)l.push({...u,summary:Yr(u.command,u.description),ts:i+1});return l}async function sS(e,t){let n=Qu(e,t);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await _j.stat(n.store)).mtimeMs)}catch{return null}}var Oj,Dj,Bj,Zv,ya,mh=q(()=>{da();Yv();Oj=process.env.BOSTRAT_CURSOR_CHATS||rs.join(Ij.homedir(),".cursor","chats");Dj=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(`
|
|
172
|
+
`).trim():"",Bj=e=>Array.isArray(e)?e.filter(t=>t&&t.type==="tool-call"&&t.toolName).map(t=>t.toolName):[],Zv=process.getBuiltinModule?.("node:sqlite"),ya={turns:[],trailingCommands:[],model:null,requestIds:[]}});import jj from"node:fs/promises";function yh(e){return aS.set(e.agent,e),e}function ro(e){return aS.get(e)||Lj}async function bh(e,t={}){return(await ro(e.agent).readMessages(e,t)).map(ed(e))}async function iS(e,t={}){return(await ro(e.agent).readCommands(e,t)).map(ed(e))}async function cS(e,t={}){return(await ro(e.agent).readJobs(e,t)).map(ed(e))}async function td(e,t={}){return(await ro(e.agent).readOpenJobs(e,t)).map(ed(e))}var ba,hh,wh,gh,Lj,aS,ed,nd=q(()=>{xn();Nu();da();zv();mh();Xo();ba=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}},hh=class extends ba{transcript(t){return!t?.cwd||!t?.sessionId?null:kn(t.cwd,t.sessionId)}async readMessages(t,n){let r=this.transcript(t);return r?Cm(r,n):[]}async readCommands(t,n){let r=this.transcript(t);return r?fx(r,n):[]}async readJobs(t,n){let r=this.transcript(t);return r?Gv(r,n):[]}async readOpenJobs(t,n){let r=this.transcript(t);return r?Kv(r,n):[]}async updatedAt(t){let n=this.transcript(t);if(!n)return null;try{return Math.round((await jj.stat(n)).mtimeMs)}catch{return null}}},wh=class extends ba{async readMessages(t,n){return nS(t.cwd,t.sessionId,n)}async readCommands(t,n){return oS(t.cwd,t.sessionId,n)}async updatedAt(t){return sS(t.cwd,t.sessionId)}},gh=class extends ba{sessionId(t){return t?.pinned?t.sessionId:null}async readMessages(t,n){return gx(t.cwd,this.sessionId(t),n)}async readCommands(t,n){return yx(t.cwd,this.sessionId(t),n)}async updatedAt(t){return bx(t.cwd,this.sessionId(t))}},Lj=new ba("unknown"),aS=new Map;yh(new hh("claude",Im));yh(new wh("cursor",Zu));yh(new gh("codex",Yi));ed=e=>t=>({...t,box:e.name,agent:e.agent,boxRole:e.role})});function kh(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 lS(e,{timeoutMs:t,reason:n=null,onWait:r=null,deps:o=null}={}){let s={...qj,...o||{}},a=e?.name,i=e?.agent||null;if(!a)throw new Error("awaitQuiescence: box.name is required");let c=Number(t)||Fj(),l=Uj(),u=s.now(),d=0,p=0,f=!1,w=null;for(;;){p++;let m=await s.capture(a),y=s.busyRead(i,m);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,m),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>=Hj){let g=s.now()-u;return f&&oe({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,oe({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 ${kh(w)} in "${a}" to finish`);throw g.code="quiescence-timeout",g.detail=w,g.waitedMs=s.now()-u,oe({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 Fj,Uj,Hj,Wj,qj,uS=q(()=>{dt();Qo();wa();nd();yn();Fj=()=>Number(process.env.BOSTRAT_QUIESCENCE_TIMEOUT_MS)||6e5,Uj=()=>Number(process.env.BOSTRAT_QUIESCENCE_POLL_MS)||1500,Hj=2,Wj=e=>new Promise(t=>setTimeout(t,e)),qj={capture:e=>Pe(e,{lines:Ve}),busyRead:Qi,awaitingBackground:Lu,turnState:pc,openJobs:td,now:()=>Date.now(),sleep:Wj}});import Gj from"node:fs";import oo from"node:fs/promises";import so from"node:path";function bS(e){let t=Go(String(e??"")),n=Buffer.byteLength(t,"utf8");return n<=dS?{content:t,bytes:n,truncated:!1}:{content:`${Buffer.from(t,"utf8").subarray(0,dS).toString("utf8").replace(/�+$/,"")}
|
|
173
|
+
|
|
174
|
+
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function eL(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(fS.test(s)){if(r.length>=TS)continue;let a=Qj(s.replace(fS,"").replace(/[.;,:]+$/,""),ES,Xj);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>pS&&(o=`${o.slice(0,pS-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function _S(e,t,{timeoutMs:n=9e4}={}){let r=so.join(t,Jj),o=so.join(r,Yj);await oo.mkdir(r,{recursive:!0});let s=await rd(o),a=await xe(e);if(!a)throw new Error(`session "${e}" not found`);return await od(e,a,Zj(o)),await NS(o,s,n),eL(await oo.readFile(o,"utf8"))}function IS(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===xS||n===AS;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(kS)||n===Kj||r.startsWith(vS)||r.startsWith(SS)}function $S(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-tL;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 RS(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 OS(e,{windows:t=[]}={}){if(IS(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 xh.get(e)||null}async function MS(e){let t=xh.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await xe(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);let r={name:e,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:so.join(n.cwd,gS,yS)};return xh.set(e,r),oL(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function oL(e,t){e.step="summarize",await CS(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 xe(e.name);await od(e.name,n,"/clear"),await Sh(nL),e.step="resume",await od(e.name,n,zj),e.step="done",e.finishedAt=Date.now()}async function mS(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 lS({...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||sL)===aL)throw i;return await oe({flow:"checkpoint",phase:"quiescence-timeout",sessionName:t,gate:e,waitedMs:i.waitedMs,detail:i.detail,still:kh(i.detail),paneTail:!0}),r.onResume&&await r.onResume(e),{outcome:"timeout",waitedMs:i.waitedMs,waited:!0,detail:i.detail}}}async function CS(e,t,{timeoutMs:n=rL,quiesce:r=null}={}){let o=so.join(t,gS),s=so.join(o,yS);await oo.mkdir(o,{recursive:!0});let a=await xe(e),i=await mS("pre-drive",e,a,r),c=await rd(s);await oe({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:s,beforeMtime:c,quiescence:i.outcome,waitedMs:i.waitedMs});try{await od(e,a,Vj(s)),await NS(s,c,n)}catch(f){throw f&&typeof f=="object"&&(f.quiescence={pre:i,post:{outcome:"skipped"}}),f}let l=await mS("post-write",e,a,r);await oe({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:s,afterMtime:await rd(s),quiescence:l.outcome,waitedMs:l.waitedMs});let u=await ev(t,s,{exclude:f=>IS(f.role,f.text)});await oe({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 oo.readFile(s,"utf8");await N({id:d,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...bS(f),ts:Date.now()}})}catch{}}await am(t);let p=await iL(o,s);return d&&await N0(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 xe(e);if(!r)throw new Error(`session "${e}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${e}"`);return CS(e,r.cwd,{...t?{timeoutMs:t}:{},quiesce:n})}function vh(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 id(e,t,{trackName:n="this Track"}={}){let r=await K({id:e,cwd:t}),o=Tv(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=so.join(t,hS);return await oo.mkdir(so.dirname(s),{recursive:!0}),await oo.writeFile(s,o,"utf8"),await N({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...bS(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),hS}async function rd(e){try{return(await oo.stat(e)).mtimeMs}catch{return 0}}async function NS(e,t,n){let r=Date.now()+n;for(;;){if(await rd(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await Sh(2e3)}}async function iL(e,t){try{if(!Gj.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=so.join(e,`${n}.md`);return await oo.copyFile(t,r),r}catch{return null}}function Sh(e){return new Promise(t=>setTimeout(t,e))}function wS(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 od(e,t,n){if(t){let o=wS(t.agent);await fr({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await xe(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await Ge(e,["Escape"],{kind:"drive-wipe"}),await Sh(150),await aa(e,$u,{kind:"drive-wipe"}),await it(e,n,{kind:"orchestration-drive",...Hn(r.agent,n)}),await ac({sessionName:e,agent:r.agent,agentCmd:wS(r.agent),text:n,flow:"orchestration-drive"})}var gS,yS,dS,kS,xS,vS,Kj,Vj,zj,SS,AS,Jj,Yj,pS,TS,ES,Xj,Zj,fS,Qj,tL,nL,rL,xh,sL,aL,hS,Ah=q(()=>{dt();Qo();yn();nc();qm();tv();Ci();ye();ku();Ev();uS();gS=".bostrat/handoff",yS="latest.md",dS=32*1024;kS="Checkpoint: write a work-in-progress handoff",xS="HANDOFF WRITTEN",vS="Resume from .bostrat/handoff/latest.md",Kj="/clear",Vj=e=>`${kS} 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 ${xS} and take no further action.`,zj=`${vS}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,SS="Beat: write a",AS="BEAT WRITTEN",Jj=".bostrat/arc",Yj="beat.txt",pS=200,TS=4,ES=5,Xj=36,Zj=e=>`${SS} 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 ${TS} further lines, each starting with "- " and naming ONE piece in at most ${ES} 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 ${AS} and take no further action.`,fS=/^(?:[-–—*•·]|\d+[.)])\s+/,Qj=(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};tL=6e5;nL=3500,rL=3e5,xh=new Map;sL="proceed",aL="reject";hS=".bostrat/handoff/status-latest.md"});function io(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!cd.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 BS(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:io(o.from,"from",n),toMs:io(o.to,"to",n)}))}return t}function Eh(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 lL(e){if(!e)return null;let t=String(e).trim();if(ao[t])return t;if(PS[t])return PS[t];let n=null;for(let r of Object.keys(ao))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function uL(e){if(!e||e===ao)return cL;let t=DS.get(e);return t||(t=BS(e),DS.set(e,t)),t}function jS(e,t,n={}){let{table:r,speed:o}=n,s=uL(r),a=r&&r!==ao?s[e]?e:null:lL(e);if(!a)return null;let i=Eh(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 qn(e,t={},n,r){let o=jS(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 dL(e=ao){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:io(a.from,"from",n),toMs:io(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`),cd.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 fL({now:e=Date.now(),table:t=ao,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...pL,...n},a=[],i=Eh(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:io(d.from,"from",c),toMs:io(d.to,"to",c),verifiedMs:cd.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*Th;p&&i-d.verifiedMs>r*Th&&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*Th&&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 ka({now:e=Date.now(),table:t=ao,...n}={}){let r=Eh(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=io(l.from,"from",i),d=io(l.to,"to",i);r>=u&&r<=d&&cd.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=[...dL(t),...fL({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var ao,PS,cd,cL,DS,LS,pL,Th,hr=q(()=>{ao={"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)."}]},PS={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"},cd=/^\d{4}-\d{2}-\d{2}$/;cL=BS(ao);DS=new WeakMap;LS=(e,t,n)=>jS(e,t,n)!==null;pL=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),Th=864e5});function mL(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function US(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function on(e){let t=e.payload?.model||"unknown",n=mL(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=qn(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&&!LS(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function co(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=FS();for(let p of n){let{usd:f,model:w,tokens:m,repriced:y,unpriced:k,asRecorded:g}=on(p);i+=g;let x=p.provider||"unknown",S=p.boxId||"unattributed";if(y&&c++,k&&(l++,u.add(w)),a+=f,US(d,m),r[w]||={usd:0,tokens:FS(),events:0},r[w].usd+=f,r[w].events+=1,US(r[w].tokens,m),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:xa(a),asRecordedUsd:xa(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:ld(r,p=>({...p,usd:xa(p.usd)})),byProvider:ld(o,p=>({...p,usd:xa(p.usd)})),byBox:ld(s,p=>({usd:xa(p.usd),events:p.events,provider:p.provider,label:p.label,models:[...p.models],...p.epochs?{epochs:ld(p.epochs,f=>({usd:xa(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var FS,xa,ld,lo=q(()=>{hr();FS=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});xa=e=>Math.round(e*1e4)/1e4,ld=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function hL(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 HS(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(_h(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(_h(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(`${_h(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=co(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?hL(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var _h,WS=q(()=>{lo();_h=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as wL}from"node:child_process";import{promisify as gL}from"node:util";import{randomUUID as yL}from"node:crypto";import qS from"node:fs/promises";async function Ih(e,t){let n=Date.now()+t;for(;;){if(await Fn(e))return!0;if(Date.now()>n)return!1;await ss(JS)}}function AL(e){let t=String(e||"").split(`
|
|
175
|
+
`).map(n=>n.trimEnd()).filter(n=>n.trim());return!t.length||!xL.test(t[t.length-1])?!1:kL.test(t.slice(-vL).join(`
|
|
176
|
+
`))}async function TL(e,t){if(t!=="claude")return null;let n=Date.now()+SL;for(;;){if(await Fn(e))return null;let r=await Pe(e,{lines:30});if(AL(r))return await oe({flow:"swap",phase:"exit-chooser",sessionName:e,from:t,paneTail:!0}),await Ge(e,["Enter"],{kind:"swap-exit"}),"answered";if(Date.now()>n)return null;await ss(JS)}}async function EL(e,t){let n=GS[t]||GS.default;n.escape&&(await Ge(e,["Escape"],{kind:"swap-exit"}),await ss(150)),n.wipe&&await aa(e,$u,{kind:"swap-exit"}),await Ru(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Hn(t).submitDelayMs||0;return await ss(Math.max(r,250)),await Ge(e,["Enter"],{kind:"swap-exit"}),{chooser:await TL(e,t)}}async function RL(e,{settleMs:t=_L,timeoutMs:n=IL,parse:r=!0}={}){let o=Date.now(),s=null,a=null;for(;;){let i=null,c=!0;try{let u=await qS.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 qS.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 ss($L)}}async function KS(e,t){try{let{stdout:n}=await os(ne,se("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function YS(e){await os(ne,se("respawn-pane","-k","-t",e))}async function $h(e,{exitTimeoutMs:t=VS,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 xe(e))?.agent||"agent",{chooser:s}=await EL(e,o),a=await Ih(e,t);if(await oe({flow:"swap",phase:"exit",sessionName:e,from:o,graceful:a,chooser:s,paneTail:!0}),!a&&(r=!0,await oe({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await YS(e),!await Ih(e,zS)))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);return{forced:r,chooser:s}}async function Rh(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 xe(e);if(!r)throw new Error(`session "${e}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${e}"`);let o=r.agent||"agent",s=t.agent||zr(n),a=Number(t.exitTimeoutMs)||VS;await oe({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let{forced:i,chooser:c}=await $h(e,{exitTimeoutMs:a,fromAgent:o});if(o==="claude"){let k=await RL(qf());await oe({flow:"swap",phase:"config-settled",sessionName:e,from:o,...k})}let l=s==="claude"&&Vo(n),u=l?Xl():null,d=await KS(e,"ANTHROPIC_API_KEY"),p=!1;try{u?(await os(ne,se("set-environment","-t",e,"ANTHROPIC_API_KEY",u)),p=!0):d&&(await os(ne,se("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),p=!0)}catch(k){throw u?Su(k,u):k}if(p&&!i&&(await YS(e),!await Ih(e,zS)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let f=!!t.resumeSessionId,w=t.resumeSessionId||yL(),m=f?n:Wi(n,w,e),y=f?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return l&&await Fr(r.cwd),s==="codex"&&await Ks(r.cwd),s==="cursor"&&await Vs(r.cwd),await zo(e),await os(ne,se("set-option","-t",e,"@bostrat-sid",w)),await os(ne,se("set-option","-t",e,"@bostrat-agent",s)),await ss(bL),l&&Hi().length&&(await KS(e,Ui)||await os(ne,se("set-environment","-t",e,Ui,"1")).catch(()=>{}),await it(e,`export ${Ui}=1`,{kind:"swap-env"}),await ss(150)),await it(e,m,{kind:"swap-launch"}),await oe({flow:"swap",phase:"launched",sessionName:e,from:o,to:s,paneTail:!0}),await oe({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:m}}var os,ss,VS,zS,JS,bL,GS,kL,xL,vL,SL,_L,IL,$L,XS=q(()=>{dt();vi();Ql();eu();nc();yn();os=gL(wL),ss=e=>new Promise(t=>setTimeout(t,e)),VS=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,zS=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,JS=250,bL=400,GS={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"}};kL=/Background work is running/,xL=/Enter to confirm\s*·\s*Esc to cancel/,vL=12,SL=Number(process.env.BOSTRAT_SWAP_CHOOSER_WAIT_MS)||3e3;_L=Number(process.env.BOSTRAT_CLAUDE_CONFIG_SETTLE_MS)||500,IL=Number(process.env.BOSTRAT_CLAUDE_CONFIG_SETTLE_TIMEOUT_MS)||5e3,$L=100});function Oh(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Mh({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!Oh(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),ZS);try{let a=await QS(`${Ne(r)}/v1/loops/${encodeURIComponent(e)}/report`,{method:"POST",headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN||""}`,"content-type":"application/json"},body:JSON.stringify({outcome:t,...n?{note:n}:{}}),signal:o.signal}),i=await a.json().catch(()=>({}));return a.ok?{ok:!0,source:"plane",status:a.status,run:i?.run||null}:{ok:!1,source:"plane",status:a.status,error:i?.error||`plane ${a.status}`}}catch(a){return{ok:!1,source:"unreachable",error:a?.message||String(a)}}finally{clearTimeout(s)}}async function mc(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(!Oh(a))return{ok:!1,source:"unconfigured"};let c=ud.get(e.id)||0;if(i()-c<OL)return{ok:!1,source:"debounced"};ud.set(e.id,i());let l=await hc({env:a}),u=l.loops.find(f=>f.trackId===e.id);if(!u)return ud.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 Mh({loopId:u.id,outcome:"errored",note:d.slice(0,500),env:a});return p.ok||ud.delete(e.id),{...p,loop:{id:u.id,name:u.name}}}async function hc({env:e=process.env}={}){if(!Oh(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),ZS);try{let r=await QS(`${Ne(e)}/v1/loops`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`},signal:t.signal});if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json();return{loops:Array.isArray(o?.loops)?o.loops:[],source:"plane"}}catch(r){return{loops:[],source:"unreachable",error:r?.message||String(r)}}finally{clearTimeout(n)}}var ZS,QS,OL,ud,dd=q(()=>{bt();ZS=1e4,QS=(...e)=>fetch(...e);OL=10*6e4,ud=new Map});import{execFile as ML}from"node:child_process";import pd from"node:fs/promises";import Ch from"node:path";import{promisify as CL}from"node:util";function FL(e){let n=String(e||"").trim().match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.+-]+)?/);return n?n[0]:null}async function UL(e,{env:t=process.env}={}){let n=String(e||"").trim();if(!n)return null;let r=n.includes("/")?[Ch.resolve(n)]:String(t.PATH||"").split(Ch.delimiter).filter(Boolean).map(o=>Ch.join(o,n));for(let o of r)try{await pd.access(o,pd.constants.X_OK);let s=await pd.realpath(o),a=await pd.stat(s);if(!a.isFile())continue;return{path:s,mtimeMs:a.mtimeMs}}catch{}return null}async function as({agent:e=null,agentCmd:t=null,env:n=process.env}={}){let r=String(t||(e?n[BL[e]]||DL[e]:"")||"").trim();if(!r)return null;let{realClaude:o,realCodex:s,realCursor:a}=tc({agent:e,agentCmd:r});if(!(o||s||a))return null;let i=await UL(r.split(/\s+/)[0],{env:n});if(!i)return null;let c=`${i.path}@${i.mtimeMs}`;return Nh.has(c)||Nh.set(c,Promise.resolve().then(()=>LL(i.path,{agent:e})).then(l=>FL(l)).catch(()=>null)),Nh.get(c)}var NL,PL,DL,BL,Nh,jL,LL,eA=q(()=>{Qo();NL=CL(ML),PL=8e3,DL={claude:"claude",codex:"codex",cursor:"cursor-agent"},BL={claude:"BOSTRAT_AGENT_CMD",codex:"BOSTRAT_CODEX_CMD",cursor:"BOSTRAT_CURSOR_CMD"},Nh=new Map,jL=async e=>(await NL(e,["--version"],{timeout:PL})).stdout,LL=jL});function va(e){return typeof e=="string"?e.trim():""}var tA=q(()=>{});import{execFile as HL}from"node:child_process";import{promisify as WL}from"node:util";async function Je(e,t,{input:n}={}){let r=Dh("git",["-C",e,...t],{maxBuffer:nA});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Sa(e){if(!e)return!1;try{let{stdout:t}=await Je(e,["rev-parse","--is-inside-work-tree"]);return t.trim()==="true"}catch{return!1}}async function rA(e){let{stdout:t}=await Je(e,["rev-parse","--show-toplevel"]);return t.trim()}async function qL(e){try{return await Je(e,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function Aa(e,t="bostrat: subtask snapshot"){let{stdout:n}=await Je(e,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>Dh("git",["-C",e,...a],{maxBuffer:nA,env:o,...i});try{let a=await qL(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 Dh("rm",["-f",r])}catch{}}}async function Bh(e,t,{trailers:n=[]}={}){let{stdout:r}=await Je(e,["status","--porcelain"]);return r.trim()?(await Je(e,["add","-A"]),await Je(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",t,...n.flatMap(o=>["--trailer",o])]),!0):!1}async function is(e,{ignorePlumbing:t=!1}={}){let{stdout:n}=await Je(e,["status","--porcelain"]),r=n.split(`
|
|
177
|
+
`).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 cs(e,t){if(!t)return 0;try{let{stdout:n}=await Je(e,["rev-list","--count",`${t}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function ls(e,t,n,r){return await Je(e,["worktree","add","--no-track","-b",n,t,r]),t}async function jh(e,t){try{return await Je(e,["rev-parse","--verify","--quiet",`refs/heads/${t}`]),!0}catch{return!1}}async function wc(e,t,n){return await Je(e,["worktree","add","--end-of-options",t,n]),t}async function us(e){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:t}=await Je(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 Je(e,["rev-parse","--verify","--quiet",t]),n}catch{}return"HEAD"}async function Lh(e){await Je(e,["init","-q"]),await Je(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 Je(e,["worktree","remove","--force",t])}catch{try{await Je(e,["worktree","prune"])}catch{}}}function GL(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 Fh(e,t){return!t||!t.length?e:[...e,"--",".",...t.map(n=>`:(exclude)${n}`)]}async function Uh(e,t,n,r){let{stdout:o}=await Je(e,Fh(["diff","--shortstat",t,n],r));return GL(o)}async function Hh(e,t,n,r){let{stdout:o}=await Je(e,Fh(["diff","--name-only",t,n],r));return o.split(`
|
|
178
|
+
`).map(s=>s.trim()).filter(Boolean)}async function Wh(e,t,n,r){let{stdout:o}=await Je(e,Fh(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await Ph(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await Ph(e,o,["--3way","--whitespace=nowarn"]),a=await KL(e);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await Ph(e,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function Ph(e,t,n){try{return await Je(e,["apply",...n,"-"],{input:t}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function KL(e){try{let{stdout:t}=await Je(e,["diff","--name-only","--diff-filter=U"]);return t.split(`
|
|
179
|
+
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var Dh,nA,fd=q(()=>{Dh=WL(HL),nA=64*1024*1024});function md(e,{at:t=Date.now(),reason:n="node-transfer"}={}){let r=!1;for(let o of e?.boxes||[]){if(!o||typeof o!="object")continue;!VL.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 Fe,VL,sn=q(()=>{Fe=e=>!!(e&&(e.archivedAt||e.transferredTo)),VL=new Set(["merged","conflict","canceled","failed","handed-off"])});import qh from"node:fs/promises";import sA from"node:path";import zL from"node:os";function lA(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function uA(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function dA(e){let t=new Date(lA(e));return t.setDate(t.getDate()+7),t.getTime()}function pA(e){let t=new Date(uA(e));return t.setMonth(t.getMonth()+1),t.getTime()}function wd(e=Date.now()){for(let[t,n]of Object.entries(Ta)){let r=n.startOf(e);if(r!==wr[t]){wr[t]=r;for(let o of ds.values())o[t]=0;for(let o of Ea)o.includes(`:${t}:`)&&!o.endsWith(`:${r}`)&&Ea.delete(o)}}}function fA(e,t){let n=An?.caps?.[e]?.[Ta[t].capKey];return Number.isFinite(n)&&n>0?n:null}function uo(e,t=Date.now()){if(!e||!Kh||!An?.caps)return null;wd(t);let n=ds.get(e);for(let r of Object.keys(Ta)){let o=fA(e,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:e,window:r,limitUsd:o,spentUsd:hd(s),resetsAt:Ta[r].nextStart(t)}}return null}function mA(e,t){let n=`hit:${e}:${t.provider}:${t.window}:${wr[t.window]}`;return Ea.has(n)?!1:(Ea.add(n),!0)}function oA(e,t,n,r=Date.now()){if(!e||!t)return;wd(r);let o=JL(e);for(let[s,a]of Object.entries(Ta))n>=wr[s]&&n<a.nextStart(r)&&(o[s]+=t)}async function YL(e,t,n=Date.now()){if(!An?.caps||!Gh)return;let r=ds.get(e);if(r)for(let[o,s]of Object.entries(Ta)){let a=fA(e,o);if(a==null)continue;let i=r[o]||0;if(i<a*cA||i>=a)continue;let c=`warn:${e}:${o}:${wr[o]}`;if(!Ea.has(c)){Ea.add(c);try{let l=await Gh(t);if(!l)continue;await N(l,{type:"usage.cap.warning",actor:"system",provider:e,payload:{provider:e,window:o,limitUsd:a,spentUsd:hd(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function hA(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 gd(e,{source:t="local",updatedAt:n=Date.now()}={}){An={caps:hA(e),updatedAt:n,source:t};try{await qh.mkdir(aA,{recursive:!0}),await qh.writeFile(iA(),JSON.stringify(An,null,2))}catch{}return gc()}function gc(e=Date.now()){wd(e);let t=new Set([...ds.keys(),...Object.keys(An?.caps||{})]),n={};for(let r of t){let o=ds.get(r);n[r]={weeklyUsd:hd(o?.weekly||0),monthlyUsd:hd(o?.monthly||0)}}return{caps:An?.caps||{},configured:!!An,seeded:Kh,updatedAt:An?.updatedAt??null,spent:n,windows:{weekly:{start:wr.weekly,resetsAt:dA(e)},monthly:{start:wr.monthly,resetsAt:pA(e)}},warnPct:cA}}async function wA({listTracks:e,getTrack:t}={}){Gh=t||null,wd();try{let o=JSON.parse(await qh.readFile(iA(),"utf8"));An={caps:hA(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{An=null}let n=Date.now();Ye.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=on(o);oA(o.provider||"unknown",s,o.ts??Date.now()),YL(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(wr.weekly,wr.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||oA(a.provider||"unknown",on(a).usd,i,n)}}return Kh=!0,gc()}var aA,iA,cA,Ta,An,Kh,wr,ds,Ea,Gh,JL,hd,yd=q(()=>{ye();lo();aA=process.env.BOSTRAT_STATE_DIR||sA.join(process.env.HOME||zL.tmpdir(),".bostrat"),iA=()=>sA.join(aA,"usage-caps.json"),cA=.8;Ta={weekly:{startOf:lA,nextStart:dA,capKey:"weeklyUsd"},monthly:{startOf:uA,nextStart:pA,capKey:"monthlyUsd"}},An=null,Kh=!1,wr={weekly:0,monthly:0},ds=new Map,Ea=new Set,Gh=null,JL=e=>{let t=ds.get(e);return t||(t={weekly:0,monthly:0},ds.set(e,t)),t};hd=e=>Math.round(e*1e4)/1e4});import XL from"node:fs/promises";import ZL from"node:os";import gA from"node:path";function Vh(e){return e==="codex"?nF:e==="cursor"?SA:xA}function yA(e){let t=String(e||"").split(`
|
|
180
|
+
`),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 bA(e){let t=String(e||"").split(`
|
|
181
|
+
`),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 rF(e){let t=String(e||"").split(`
|
|
182
|
+
`);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 kA(e){let t=String(e||"").split(`
|
|
183
|
+
`),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 AA(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&&!vA.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=SA.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!tF.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function yc(e,t,{timeoutMs:n=QL}={}){let r=Date.now()+n;for(;;){let o=await Pe(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await bd(eF)}}async function oF(e,t){return await it(e,`/model ${t.model}`,Hn("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function sF(e,t){await Iu(e),await it(e,"/model",{enter:!1}),await bd(250),await Ge(e,["Enter"],{kind:"model-switch"});let n=await yc(e,yA);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await Ge(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${t.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=rF(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await it(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await yc(e,i=>bA(i)||!yA(i)&&o(i)&&{done:o(i)});if(!s)throw await Ge(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?vA.get(t.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await Ge(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await it(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await yc(e,i=>!bA(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 aF(){let e=gA.join(process.env.CURSOR_CONFIG_DIR||gA.join(ZL.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await XL.readFile(e,"utf8"))?.model||null}catch{return null}}async function iF(e,t){await Iu(e),await Ru(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await yc(e,kA),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 yc(e,kA,{timeoutMs:1500});if(s=l?.rows.find(u=>u.active)||null,s&&o(s))break;if(!l||!l.rows.some(o)){s=null;break}await Ge(e,["Down"],{kind:"model-switch"}),await bd(250)}if(!n||!s||!o(s)){await Ge(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await Ge(e,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${t.filter}"`:"cursor model picker didn't open")}await Ge(e,["Enter"],{kind:"model-switch"}),await bd(800);let a=await aF();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 TA(e,t,n){return e==="codex"?sF(t,n):e==="cursor"?iF(t,n):oF(t,n)}var bd,QL,eF,xA,tF,nF,vA,SA,EA=q(()=>{dt();nc();bd=e=>new Promise(t=>setTimeout(t,e)),QL=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,eF=300,xA=[{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"}],tF=new Set([...xA.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"]),nF=[{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"}],vA=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),SA=[{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 vd(e){let t=e||{},n=cF(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function cF(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{text:e.text,boxRole:e.boxRole,...e.kind?{kind:e.kind}:{},...e.attachments?.length?{attachments:e.attachments}:{},...e.deliveryId?{deliveryId:e.deliveryId}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{saidTs:e.saidTs,text:e.text,boxRole:e.boxRole}};case"orchestration-prompt":return{type:"turn.user",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{text:e.prompt,kind:e.kind,boxRole:e.boxRole,...e.deliveryId?{deliveryId:e.deliveryId}:{}}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:ct(e.toAgent),payload:{from:e.from,fromAgent:e.fromAgent,to:e.to,toAgent:e.toAgent,reason:e.reason,handoffStartedAt:e.handoffStartedAt,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,singleBox:e.singleBox,forced:e.forced,recovered:e.recovered,checkpoint:e.checkpoint,cliVersion:e.cliVersion??null,...e.standing?{standing:e.standing}:{}}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.toAgent),payload:{box:e.box,fromAgent:e.fromAgent,toAgent:e.toAgent,reason:e.reason,error:e.error,rolledBack:e.rolledBack,handoffStartedAt:e.handoffStartedAt}};case"subtask-delegated":return{type:"subtask.delegated",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:ct(e.toAgent),payload:{task:e.task,subtaskId:e.subtaskId,reason:e.reason}};case"subtask-merged":return{type:"subtask.merged",actor:"orchestrator",source:"orchestrator",boxId:e.box||e.from,provider:ct(e.agent),payload:{...Gn(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:ct(e.agent||e.toAgent),payload:{kind:"subtask-failed",...Gn(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{change:"subtask-resumed",...Gn(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",...Gn(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...Gn(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...Gn(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{change:"rehydrated",...e.fresh?{fresh:!0}:{}}};case"lead-refreshed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{change:"lead-refreshed",box:e.box,agent:e.agent,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,reason:e.reason,cliVersion:e.cliVersion??null,...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:ct(e.agent),payload:{...Gn(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Gn(e)}};case"retag-failed":return{type:"error",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{kind:"retag-failed",action:e.action??"reshape",reason:e.reason,rolledBack:e.rolledBack!==!1,...e.recovered?{recovered:!0}:{}}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};case"delivery-held":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{kind:"delivery-held",...Gn(e)}};case"startup-gate":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{kind:"startup-gate",...Gn(e)}};default:return null}}function $A(e,t){let n=vd(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;Sd(()=>N(r,n))}async function RA(e){if(!(!e?.id||_A.has(e.id))){_A.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=vd(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await N(e,o)}}catch{}}}function Gn(e){let{type:t,at:n,...r}=e;return r}function OA(e,t,n){!n?.promptId||xd.get(e.id)===n.promptId||(xd.set(e.id,n.promptId),Sd(()=>N(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:ct(n.provider||t?.agent),payload:{promptId:n.promptId,question:n.question||n.title||null,options:(n.options||[]).map(r=>r?.label??r).slice(0,12)}})))}function MA(e,t,n,r={}){xd.delete(e.id),Sd(()=>N(e,{type:"prompt.dismissed",actor:"user",source:"orchestrator",boxId:t?.name,provider:ct(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}function zh(e,t,n,r={}){xd.delete(e.id),Sd(()=>N(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:ct(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var ct,kd,Sd,IA,_A,xd,Jh=q(()=>{ye();ct=e=>e||null;kd=Promise.resolve(),Sd=e=>(kd=kd.then(e).catch(()=>{}),kd),IA=()=>kd;_A=new Set;xd=new Map});function fF(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function jA(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of fF(n).reverse()){if(!lF.test(r))continue;let o=r.endsWith("?")&&r.length<=pF&&dF.test(r);if(uF.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function FA(e){let t=String(e||""),n=[];mF.test(t)&&n.push("commit"),LA.test(t)&&n.push("rebase"),hF.test(t)&&n.push("push");let r=gF.test(t);!r&&wF.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 UA(e){return LA.test(String(e||""))}function _a(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 HA(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 WA(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<=yF&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function qA(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+bF||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function GA(e){let t=(Array.isArray(e)?e:[]).filter(r=>CA[r]).map(r=>CA[r]);return`The ship you were given the go-ahead for${t.length?` (${t.join(", then ")})`:""} should now be complete. This is an automated ship notification the operator armed on the offer: carry straight on with this Track's follow-up work (dependent tasks, cleanup, next steps) \u2014 don't wait for review or merge. If a ship step failed or is unfinished, finish the ship first. If nothing needs doing, say so briefly.`}var NA,PA,DA,BA,lF,uF,dF,pF,po,mF,LA,hF,wF,gF,yF,bF,CA,ps=q(()=>{NA=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional|amend(?:ed)?|squash|merge|follow[\s-]?up|clean[\s-]?up)\s)commit(?!s\b)(?!\s+(?:history|log|message|hash|sha)s?\b)/.source,PA=/(?<!\b(?:a|an|the|this|that|another|one|each|any|no|separate|single|new|initial|final|first|second|third|last|later|next|subsequent|extra|additional)\s)push(?!\s+notifications?\b)(?:\s+(?:it|this|that|them|everything|the\s+\w+))?\s*(?:up)?/.source,DA=/open(?:\s+up)?\s+(?:a|the|that)\s+(?:draft\s+)?(?:pr|pull\s+request)|creat(?:e|ing)\s+(?:a|the)\s+(?:draft\s+)?(?:pr|pull\s+request)|rais(?:e|ing)\s+(?:a|the)\s+(?:pr|pull\s+request)|put\s+up\s+(?:a|the)\s+(?:pr|pull\s+request)|send\s+(?:up\s+)?(?:a|the)\s+(?:pr|pull\s+request)/.source,BA=/(?<!\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,lF=new RegExp(`\\b(?:${NA}|${PA}|${DA}|${BA})\\b`,"i"),uF=/\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,dF=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,pF=90;po=["commit","rebase","push","pr"],mF=new RegExp(`\\b(?:${NA})\\b`,"i"),LA=new RegExp(`\\b(?:${BA})\\b`,"i"),hF=new RegExp(`\\b(?:${PA})\\b`,"i"),wF=new RegExp(`\\b(?:${DA})\\b`,"i"),gF=/\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;yF=3e4;bF=2500;CA={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import kF from"node:crypto";import Ad from"node:fs/promises";import KA from"node:path";import xF from"node:os";function YA(e){JA=typeof e=="function"?e:async()=>new Set}function vF(e,t,n){if(e.originNode&&e.originNode===t)return!0;let r=e.data?.trackId;return!!r&&n.has(r)}async function He(){return gr||(Yh??=Ad.readFile(Zh,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(gr=e,!t&&qe()){let{records:n,source:r}=await ut("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){let o=O0(),s=await JA().catch(()=>new Set),a=0;for(let i of n){if(i.deletedAt||!i.data?.repo||i.data?.number==null)continue;if(!vF(i,o,s)){a++;continue}let{id:c,updatedAt:l,...u}=i.data;gr[ue(u.repo,u.number)]=u}a&&console.log(`[pr-links] restore: ${Object.keys(gr).length} link(s) re-seeded, ${a} other-node record(s) left on the plane`),Object.keys(gr).length&&await Qh().catch(()=>{})}}return gr}).finally(()=>Yh=null),Yh)}async function Qh(){let e=JSON.stringify({links:gr},null,2),t=`${Zh}.${process.pid}.tmp`;await Ad.mkdir(VA,{recursive:!0}),await Ad.writeFile(t,e),await Ad.rename(t,Zh)}async function Xe(e){Xh=Xh.then(Qh,Qh),await Xh.catch(()=>{});let t=gr?.[e];t&&Et("prlinks",{id:zA(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function Kt({repo:e,number:t,trackId:n,url:r=null,headRef:o=null,title:s=null,source:a="matched"}){if(!e||!Number.isInteger(Number(t))||!n)return{link:null,created:!1};let i=await He(),c=ue(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:SF.has(a),firstSeen:Date.now()},await Xe(ue(e,t)),{link:i[c],created:!0})}async function yr(e,t){if(!e||t==null)return null;let n=(await He())[ue(e,t)];return!n||n.announced?null:(n.announced=!0,await Xe(ue(e,t)),n)}async function XA(e,t){if(!e||t==null)return!1;let n=(await He())[ue(e,t)];return n?.notifyOnMerge?(delete n.notifyOnMerge,await Xe(ue(e,t)),!0):!1}async function pt(e,t){return!e||t==null?null:(await He())[ue(e,t)]??null}async function fo(e,t,n){if(!e||t==null)return null;let r=(await He())[ue(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 Xe(ue(e,t)),r):null}async function ZA(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await He())[ue(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Xe(ue(e,t)),o):null}async function QA(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await He())[ue(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Xe(ue(e,t)),o):null}async function Td(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await He())[ue(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Xe(ue(e,t)),r)}async function Ed(e,t){if(!e||t==null)return null;let n=(await He())[ue(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Xe(ue(e,t)),n)}async function _d(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await He())[ue(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Xe(ue(e,t)),r)}async function Id(e,t){if(!e||t==null)return null;let n=(await He())[ue(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Xe(ue(e,t)),n)}async function eT(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await He())[ue(e,t)];return r?(r.baseFoldAt=n,await Xe(ue(e,t)),r):null}async function tT(e,t,{misses:n,until:r}){if(!e||t==null)return null;let o=(await He())[ue(e,t)];return o?(o.brokerRefused={misses:Number(n)||0,until:Number(r)||0},await Xe(ue(e,t)),o):null}async function nT(e,t){if(!e||t==null)return null;let n=(await He())[ue(e,t)];return n?.brokerRefused?(delete n.brokerRefused,await Xe(ue(e,t)),n):null}async function rT(e,t){if(!e||t==null)return null;let n=(await He())[ue(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Xe(ue(e,t)),n)}async function oT(e,t,n){if(!e||t==null||!n)return null;let r=(await He())[ue(e,t)];return r?(r.lastHeadSha=n,await Xe(ue(e,t)),r):null}async function sT(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await He())[ue(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 Xe(ue(e,t))),{prev:o,link:r}}async function bc(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await He())[ue(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Xe(ue(e,t)),r):null}async function fs(e,t,n=!0){if(!e||t==null)return null;let r=(await He())[ue(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Xe(ue(e,t)),r):null}async function ms(e,t,n=!0){if(!e||t==null)return null;let r=(await He())[ue(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Xe(ue(e,t)),r):null}async function aT(e,t,n){if(!e||t==null)return null;let r=(await He())[ue(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 Xe(ue(e,t)),r}async function iT(e,t,n){if(!e||t==null)return null;let r=(await He())[ue(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 Xe(ue(e,t)),r}async function kc(e,t){if(!e||t==null)return null;let n=(await He())[ue(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 Xe(ue(e,t)),n)}async function cT(){return Object.values(await He()).filter(e=>!AF.has(e.state))}async function Tn(){return Object.values(await He()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function lT(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await ut("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await He(),r=0;for(let[o,s]of Object.entries(n))await Et("prlinks",{id:zA(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var VA,Zh,ue,zA,gr,Yh,Xh,JA,SF,AF,Kn=q(()=>{Vr();VA=process.env.BOSTRAT_STATE_DIR||KA.join(process.env.HOME||xF.tmpdir(),".bostrat"),Zh=KA.join(VA,"pr-links.json"),ue=(e,t)=>`${e}#${Number(t)}`,zA=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=kF.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},gr=null,Yh=null,Xh=Promise.resolve(),JA=async()=>new Set;SF=new Set(["api","observed","reconciled"]);AF=new Set(["merged","closed","lost"])});import TF from"node:fs";import ew from"node:fs/promises";import fT from"node:path";import EF from"node:os";function wT(e,t=!1){if(!t&&e-dT<_F)return;dT=e;let n=JSON.stringify({windowStart:Ia,byCaller:Object.fromEntries(br),lastHour:mo});pT=pT.then(()=>ew.mkdir(mT,{recursive:!0})).then(()=>ew.writeFile(`${$d}.tmp`,n)).then(()=>ew.rename(`${$d}.tmp`,$d)).catch(()=>{})}function gT(e){e-Ia<hT||(mo={start:Ia,total:Rd(br),byCaller:Object.fromEntries(br)},mo.total&&console.log(`[github] ${mo.total} calls last hour`,mo.byCaller),Ia=e,br=new Map,wT(e,!0))}function kr(e="other",t=Date.now()){gT(t),br.set(e,(br.get(e)||0)+1),wT(t)}function yT(e=Date.now()){return gT(e),{windowStart:Ia,total:Rd(br),byCaller:Object.fromEntries(br),lastHour:mo,backoffUntil:hs>e?hs:null}}function ho(e){return IF.test(String(e?.message||e||""))}function wo(e,t=Date.now()){return hs<=t&&console.warn(`[github] rate limited \u2014 backing off ${uT/6e4} min:`,String(e?.message||e||"").slice(0,200)),hs=Math.max(hs,t+uT),hs}function Od(e=Date.now()){return Math.max(0,hs-e)}var mT,$d,hT,uT,_F,Ia,br,mo,hs,dT,Rd,pT,IF,xc=q(()=>{mT=process.env.BOSTRAT_STATE_DIR||fT.join(process.env.HOME||EF.tmpdir(),".bostrat"),$d=fT.join(mT,"github-metrics.json"),hT=60*6e4,uT=5*6e4,_F=6e4,Ia=Date.now(),br=new Map,mo=null,hs=0,dT=0,Rd=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(TF.readFileSync($d,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<hT?(Ia=e.windowStart,br=new Map(Object.entries(e.byCaller||{})),mo=e.lastHour||null):Rd(new Map(Object.entries(e.byCaller||{})))&&(mo={start:e.windowStart,total:Rd(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}pT=Promise.resolve();IF=/rate limit|too quickly|abuse detection|HTTP 429/i});import bT from"node:fs/promises";import kT from"node:path";import $F from"node:os";function Cd({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};Md.push(l),Md.length>OF&&Md.shift();let u=tw.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)),tw.set(e,u),MF(l),l}async function MF(e){try{await bT.mkdir(xT,{recursive:!0}),await bT.appendFile(RF,JSON.stringify(e)+`
|
|
184
|
+
`)}catch{}}function vT(){let e={};for(let[t,n]of tw)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:Md.slice(-20)}}var xT,RF,OF,Md,tw,Nd=q(()=>{xT=process.env.BOSTRAT_STATE_DIR||kT.join(process.env.HOME||$F.tmpdir(),".bostrat"),RF=kT.join(xT,"github-latency.ndjson"),OF=100,Md=[],tw=new Map});import{execFile as CF}from"node:child_process";import{promisify as NF}from"node:util";async function PF(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Ra("git",["-C",e,"rev-parse","HEAD"]),Ra("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 an(e,t,n={}){let{caller:r="prs",...o}=n,s=await Bn({cwd:e});kr(r);try{let{stdout:a}=await Ra("gh",t,{cwd:e,...DF,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw ho(a)&&wo(a),a}}async function ow(){if(Pd&&Date.now()-Pd.at<6e4)return Pd.data;let e;try{kr("auth");let t=await Ra("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 Ii();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return Pd={at:Date.now(),data:e},e}async function BF(e){try{let{stdout:t}=await Ra("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function AT(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 nw(e){let t=(e||[]).map(AT),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 aw(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function ET(e,t){if(!t)return null;let n=await Oe(e);if(!n)return null;try{let r=await an(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 _T(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 IT(e,t){let n=await ET(e,t);return n===null?{...nw([]),unavailable:!0}:nw(_T(n))}function Dd(e,t=nw(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 LF(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){$a.delete(s);continue}r.add(s),$a.has(s)||$a.set(s,n),o.mergeabilityPending=n-$a.get(s)<jF}for(let o of $a.keys())o.startsWith(`${e}#`)&&!r.has(o)&&$a.delete(o);return t}async function Oa(e,t){let n=await xr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await an(n.path,["pr","view",String(r),"--json",sw]);return Dd(JSON.parse(o))}catch(o){if(!aw(o))throw o;let s=await an(n.path,["pr","view",String(r),"--json",TT]),a=JSON.parse(s);return Dd(a,await IT(n.path,a.headRefOid))}}async function Vn(e,{caller:t="prs"}={}){try{let n=await an(e,["pr","list","--json",sw,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>Dd(r)).sort(ST)}catch(n){if(!aw(n))throw n;let r=await an(e,["pr","list","--json",TT,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>Dd(o,await IT(e,o.headRefOid))))).sort(ST)}}function cn(){vc.clear()}async function HF(e,t){if(!e)return t;for(let n of t){let r=await pt(e,n.number).catch(()=>null);if(!r)continue;r.trackId&&(n.trackId=r.trackId),r.autoMerge&&(n.autoMergeArmed=!0),r.archiveOnMerge&&(n.archiveOnMerge=!0),r.notifyOnMerge&&(n.notifyOnMerge=!0);let o=s=>n.headSha&&s.headSha&&n.headSha!==s.headSha;r.ciFix&&(o(r.ciFix)?await Td(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await _d(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function $T(){let e=await ow();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await dr())await BF(a.path)&&t.push(a);let n=Date.now(),r=Od(n)>0,o=await Promise.all(t.map(async a=>{let i=vc.get(a.path),c=i?.pending?UF:FF;if(i&&(r||n-i.at<c))return i.entry;try{let l=await Oe(a.path).catch(()=>null),u=LF(a.path,await Vn(a.path,{caller:"tab"}),n);if(await HF(l,u),i?.entry?.prs)for(let f of u)i.entry.prs.find(m=>m.number===f.number)?.checks?.pending>0&&f.checks.total>0&&f.checks.pending===0&&Cd({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 vc.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:zn(l)}}})),s=new Set(t.map(a=>a.path));for(let a of vc.keys())s.has(a)||vc.delete(a);return{auth:e,repos:o}}async function xr(e){let t=(await dr()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function ws(e){return zs(e)}async function Ma(e,t,{method:n="squash"}={}){let r=await xr(e);if(!Sc.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 Xf().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await an(r.path,s)}catch(a){throw new Error(zn(a))}finally{cn()}return{ok:!0,number:o}}async function Ac(e,t){let n=await xr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await an(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(zn(s))}finally{cn()}let o=await Oe(n.path).catch(()=>null);return o&&await eT(o,r).catch(()=>{}),{ok:!0,number:r}}async function iw(e,t){let n=await xr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await an(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(zn(o))}finally{cn()}return{ok:!0,number:r}}function cw(e){return e.green&&!e.isDraft}function KF(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 RT(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:cw(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function OT(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function Tc(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 MT(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(Tc))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function CT(){let e={};for(let[t,n]of rw)e[t]={...n};return e}async function NT(e,{method:t="merge",onMerged:n=null}={}){let r=await xr(e);if(!Sc.has(t))throw new Error(`invalid merge method: ${t}`);if(rw.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Vn(r.path)).filter(cw).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 rw.set(r.path,s),zF(s,o,{listPrs:()=>Vn(r.path),merge:a=>Ma(r.path,a,{method:t}),sleep:GF,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function VF(e,t){let n=Date.now()+(t.settleMaxMs??qF),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(WF)}}async function zF(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 VF(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: ${zn(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(!cw(a)){e.skipped.push({number:a.number,title:a.title,reason:KF(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:zn(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),cn()}}async function lw(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Ra("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${zn(l)}`)}await PF(e);let o=n||"";if((await Bn({cwd:e})).GH_TOKEN){let l=await Xf().catch(()=>null);l&&(o=`${o?`${o}
|
|
185
|
+
|
|
186
|
+
`:""}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 an(e,s)}catch(l){throw new Error(zn(l))}finally{cn()}let i=a.match(/https:\/\/\S+\/pull\/\d+/)?.[0]??null,c=i?Number(i.match(/\/pull\/(\d+)/)?.[1]):null;return{ok:!0,url:i,number:c}}async function PT(e,t){let n=await xr(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await an(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(zn(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!aw(i))throw i;let c=await an(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await ET(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(zn(i));o=_T(l).map(AT).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=JF(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await an(n.path,["run","view",c,"--log-failed"]).then(u=>YF(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function JF(e){return String(e||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function YF(e,{maxLines:t=400,maxBytes:n=64*1024}={}){let r=String(e||""),o=r.split(`
|
|
187
187
|
`);return o.length>t&&(r=`\u2026 (${o.length-t} earlier lines truncated)
|
|
188
188
|
`+o.slice(-t).join(`
|
|
189
189
|
`)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
|
|
190
190
|
`+r.slice(-n)),r}function zn(e){return String(e?.stderr||e?.message||e||"").trim().split(`
|
|
191
|
-
`).find(n=>n.trim())||"gh failed"}var Ra,
|
|
192
|
-
`)){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===
|
|
193
|
-
`)){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
|
|
194
|
-
`)){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
|
|
191
|
+
`).find(n=>n.trim())||"gh failed"}var Ra,DF,Pd,sw,TT,jF,$a,ST,FF,UF,vc,Sc,WF,qF,GF,rw,gs=q(()=>{Hr();bt();Kn();xc();Nd();ye();Ra=NF(CF),DF={timeout:3e4,maxBuffer:8*1024*1024};Pd=null;sw="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt,state,mergedAt,closedAt",TT=sw.replace("statusCheckRollup,","");jF=9e4,$a=new Map;ST=(e,t)=>e.number-t.number;FF=3e4,UF=1500,vc=new Map;Sc=new Set(["squash","merge","rebase"]);WF=2500,qF=9e4,GF=e=>new Promise(t=>setTimeout(t,e));rw=new Map});import{execFile as XF}from"node:child_process";import{promisify as ZF}from"node:util";async function eU(e,t){try{let{stdout:n}=await QF("git",["-C",e,"rev-list","--count",t]);return Number(n.trim())||0}catch{return null}}async function tU(e,t,n,{rebase:r=!1,listPrs:o=Vn}={}){let s=await is(e.dir,{ignorePlumbing:!0}).catch(()=>!1),a=await cs(e.dir,n).catch(()=>0),i=t?await eU(e.dir,`refs/remotes/origin/${t}..HEAD`):null,c=i==null?a>0:i>0,l=null,u="unknown";try{l=(await o(e.dir,{caller:"ship-state"})||[]).find(m=>m.headRefName===t)||null,u=l?"open":"none"}catch{}let d=s||a>0,p=[];s&&p.push("commit"),r&&p.push("rebase"),(s||c)&&p.push("push"),d&&(u!=="open"||s||c)&&p.push("pr");let f=p.filter(w=>!(w==="pr"&&u==="open"));return{name:e.name??null,slug:e.repo??null,dirty:s,unpushed:c,aheadBase:a,prState:u,openPr:l?{number:l.number,url:l.url??null,title:l.title??null}:null,available:Bd.filter(w=>p.includes(w)),defaults:Bd.filter(w=>f.includes(w))}}async function jd(e,{branch:t,rebase:n=!1,listPrs:r=Vn}={}){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 Sa(s[0].dir).catch(()=>!1))return null;let a=[];for(let p of s)a.push(await tU(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=Bd.filter(p=>c.some(f=>f.available.includes(p))),u=Bd.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 QF,Bd,uw=q(()=>{fd();gs();QF=ZF(XF),Bd=["commit","rebase","push","pr"]});import{execFile as nU}from"node:child_process";import{promisify as rU}from"node:util";async function go(e){if(!e)return null;let t=DT.get(e);if(t&&Date.now()-t.at<sU)return t.branch;let n=null;try{let{stdout:r}=await oU("git",["-C",e,"symbolic-ref","--short","-q","HEAD"],{timeout:5e3});n=r.trim()||null}catch{n=null}return DT.set(e,{at:Date.now(),branch:n}),n}function BT(e){return String(e||"").match(aU)?.[1]??null}function jT(e,t){let n=BT(e);return!!n&&!!t&&n===t}function LT(e,t){let n=BT(e);return!!n&&n!==t}function iU(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 Ca(e){let t=[],n=r=>{r&&!t.includes(r)&&t.push(r)};n(e?.workspace?.branch??null);for(let r of iU(e))n(await go(r));return t}async function Ld(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=>jT(t,a.id));if(s)return s;for(let a of r)if(!Fe(a)&&(await Ca(a)).includes(t))return a;return null}function cU(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 Fd(e,{branch:t=null,repo:n=null,slug:r=null}={}){return jT(t,e?.id)?!0:cU(e,n,r)}var oU,sU,DT,aU,ys=q(()=>{sn();oU=rU(nU),sU=5e3,DT=new Map;aU=/^track\/.+-([0-9a-f]{8})$/});import{execFile as lU}from"node:child_process";import{promisify as uU}from"node:util";function Na({trackId:e,boxId:t,epoch:n}){return`${e}/${t}/${Number.isFinite(Number(n))?Number(n):0}`}function Hd(e){let t=/^([^/\s]+)\/([^/\s]+)\/(\d+)$/.exec(String(e||"").trim());return t?{trackId:t[1],boxId:t[2],epoch:Number(t[3])}:null}function qd({trackId:e,boxId:t,epoch:n=0,provider:r=null,model:o=null}){let s=[],a=r?wU[r]:null;return a&&s.push(a),s.push(`${qT}: ${Na({trackId:e,boxId:t,epoch:n})}`),o&&s.push(`${GT}: ${String(o).trim()}`),s}function FT(e){let t={epoch:null,model:null,provider:null,cause:null};for(let n of String(e||"").split(`
|
|
192
|
+
`)){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===qT.toLowerCase()&&!t.epoch)t.epoch=Hd(a);else if(s===GT.toLowerCase()&&!t.model)t.model=a;else if(s===KT.toLowerCase()&&!t.cause){let i=a.toLowerCase();Ec.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 gU(e,t=yo){return e<=t?"rework":"drift"}function yU(e,{window:t=yo,end:n=hw}={}){return!Number.isFinite(e)||e<=t?1:e>=n?0:1-(e-t)/(n-t)}function Gd({subject:e="",trailerCause:t=null,wallReason:n=null,fixerCategory:r=null,ciFix:o=!1}={}){let s=String(e||"");return bU.test(s)?{cause:"defect",level:"A",basis:"revert"}:o?{cause:"defect",level:"A",basis:"ci-fix"}:t?{cause:t,level:"E",basis:`${KT} trailer`}:xU.test(s)&&!UT.test(s)?{cause:"style",level:"C",basis:"subject"}:UT.test(s)?{cause:"defect",level:"C",basis:"subject"}:kU.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 gw({heuristic:e=null,subject:t="",ref:n=null,promptKind:r=null,fixerCategory:o=null,ticketKind:s=null}={}){let a=String(t||""),i=vU.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&&SU.has(c)?{intent:"fix",basis:`subject prefix: ${c}:`}:c&&AU.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"}:HT.test(String(n||""))||HT.test(a)?{intent:"planned",basis:"blueprint item"}:o&&TU.has(o)?{intent:"planned",basis:`prompt: ${o}`}:{intent:"unknown",basis:null}}function $U(e){let t=String(e||"");return EU.some(n=>n.test(t))?"excluded":_U.some(n=>n.test(t))?"docs":IU.some(n=>n.test(t))?"config":"code"}function RU(e){let t=[],n=null,r=null;for(let o of String(e||"").split(`
|
|
193
|
+
`)){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 OU(e,t){let n=mw(e),r=mw(t);if(n===r)return 1;if(!n.length||!r.length)return 0;n.length>Ud&&(n=n.slice(0,Ud)),r.length>Ud&&(r=r.slice(0,Ud));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 MU(e,t=VT){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&&OU(r[s],o[s])>=t&&n.add(a)}return n}function CU(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 NU(e){let t=new Map,n=null;for(let r of String(e||"").split(`
|
|
194
|
+
`)){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 WT(e,t){let{stdout:n}=await fw(e,["show","-s","--format=%H%n%P%n%ct%n%s%n%B",t]),[r,o,s,a,...i]=n.split(`
|
|
195
195
|
`);return{sha:r.trim(),parents:o.trim()?o.trim().split(" "):[],ts:Number(s)*1e3,subject:a??"",message:i.join(`
|
|
196
|
-
`)}}async function KT(e,t,{fixKey:n=null,window:r=yo,resolveOrigin:o=null,context:s={}}={}){let a=await UT(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=jT(a.message),l=n?Hd(n):c.epoch,u=l?Na(l):null,{stdout:d}=await mw(e,["diff","-U0","--no-color","-M","-C",i,t]),p=_U(d),f=0,w=0,m=0,g=0,b=0,y=0,x=0,S=new Map;for(let _ of p){if(_.binary)continue;let L=_.oldPath||_.newPath||"",G=_.deleted.reduce((W,[,X])=>W+X,0),O=EU(L);if(O==="excluded"){m+=G;continue}if(O==="docs"){g+=G,b+=_.added;continue}if(O==="config"){y+=G;continue}f+=_.added;let T=new Set;for(let W of _.hunks||[])for(let X of $U(W))T.add(X);x+=T.size;let C=[];for(let[W,X]of _.deleted)for(let ge=W;ge<W+X;ge++)T.has(ge)||C.push(ge);if(w+=C.length,!_.oldPath||!C.length)continue;let P;try{let W=["blame","-w","-M","-C","--line-porcelain"];for(let[X,ge]of RU(C))W.push("-L",`${X},${X+ge-1}`);W.push(i,"--",_.oldPath),P=OU((await mw(e,W)).stdout)}catch{continue}for(let{sha:W,time:X}of P.values()){let ge=S.get(W)??{lines:0,time:X};ge.lines+=1,S.set(W,ge)}}let $=[],M=0,D=new Map;for(let[_,{lines:L,time:G}]of S){let O=D.get(_);if(!O){try{O=await UT(e,_)}catch{O=null}D.set(_,O)}let T=O?jT(O.message):{epoch:null},C=T.epoch,P=T.provider,W=T.model;if(!C&&o){let ee=await o(_,O);ee?.trackId&&ee?.boxId!=null&&(C={trackId:ee.trackId,boxId:ee.boxId,epoch:ee.epoch??0},P=P??ee.provider??null,W=W??ee.model??null)}if(!C){M+=L;continue}let X=Number.isFinite(G)?G:O?.ts??a.ts,ge=Math.max(0,(a.ts-X)/uU),Ie=Na(C),Pe=Ie===u?"self":mU(ge,r);$.push({sha:_,key:Ie,trackId:C.trackId,boxId:C.boxId,epoch:C.epoch,provider:P??null,model:W??null,lines:L,ageDays:Math.round(ge*100)/100,class:Pe,decay:Pe==="rework"?1:Pe==="drift"?Math.round(hU(ge,{window:r})*1e3)/1e3:0})}$.sort((_,L)=>L.lines-_.lines);let j=Gd({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0}),A=yw({heuristic:j,subject:a.subject,ref:s.ref??null,promptKind:s.promptKind??null,fixerCategory:s.fixerCategory??null,ticketKind:s.ticketKind??null});return{sha:a.sha,parent:i,subject:a.subject.slice(0,200),ts:a.ts,fixKey:u,fixProvider:c.provider??s.provider??null,fixModel:c.model??s.model??null,window:r,added:f,deleted:w,excludedDeleted:m,cosmeticDeleted:x,docsDeleted:g,docsAdded:b,configDeleted:y,codeDelta:f+w+x>0,origins:$,unattributedLines:M,ghost:w===0&&f>0,heuristic:j,intent:A}}function VT(e,{minLines:t=gw}={}){if(!e)return!1;let n=e.intent?.intent??"unknown";return Pa.has(n)?_c(e)>=t:!1}function _c(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function fw(e){let t=e.genUsd;return{...e,genUsd:Tn(t),reworkChargedUsd:Tn(e.reworkChargedUsd),reworkPendingUsd:Tn(e.reworkPendingUsd),trueUsd:Tn(t+e.reworkChargedUsd),multiplier:t>0?Tn((t+e.reworkChargedUsd)/t):null}}function zT(e,{window:t=yo,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)=>Na({trackId:A,boxId:_,epoch:L??0}),p=(A,_={})=>{if(!u[A]){let G=Hd(A)||{};u[A]={key:A,...G,provider:null,model:null,...pw()}}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:G}=rn(A),O=p(_,{provider:A.provider,model:G!=="unknown"?G:null});O.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,G)=>{let O=f.get(`${A}/${_}`)||[],T=-1/0;for(let W of O)W.ts<G&&(T=W.ts);let C=0,P=0;for(let W of o)W.trackId!==A||W.boxId!==_||(W.epoch??0)===(L??0)&&(W.ts<=T||W.ts>G||(C+=rn(W).usd,P++));return{usd:C,turns:P,from:Number.isFinite(T)?T:null}},m={charged:0,pending:0,self:0,decayed:0,unattributed:0,ghost:0,planned:0},g={A:0,B:0,C:0,E:0,none:0},b={fix:0,"likely-fix":0,planned:0,unknown:0},y=0,x=[],S=[],$={},M={},D=new Set;for(let A of a){let _=A.payload||{};if(!_.sha||D.has(_.sha))continue;if(D.add(_.sha),_.codeDelta===!1){y++;continue}let L=Hd(_.fixKey)||{trackId:A.trackId,boxId:A.boxId,epoch:A.epoch??0},{usd:G,turns:O}=w(L.trackId,L.boxId,L.epoch,_.ts??A.ts),T=pU.includes(_.intent?.intent)?_.intent.intent:"unknown";b[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 P=C?.level??"none";g[P]=(g[P]||0)+1;let W=dU.has(P),X=Number(_.deleted)||0,ge=Array.isArray(_.origins)?_.origins:[],Ie=ge.filter(ee=>ee.class==="rework").reduce((ee,Ze)=>ee+Ze.lines,0),Pe={sha:_.sha,subject:_.subject??"",ts:_.ts??A.ts,trackId:A.trackId,fixKey:_.fixKey??null,fixProvider:_.fixProvider??A.provider??null,fixUsd:Tn(G),fixTurns:O,linesReworked:Ie,unattributedLines:Number(_.unattributedLines)||0,ghost:_.ghost===!0,intent:{intent:T,basis:_.intent?.basis??null},verdict:C?{cause:C.cause,level:P,basis:C.basis??null}:null,charged:Tn(0),origins:[]};if(T==="planned"||C?.cause===Wd){m.planned+=G;for(let ee of ge)ee.class==="rework"&&Pe.origins.push({key:ee.key,provider:ee.provider??null,model:ee.model??null,lines:ee.lines,ageDays:ee.ageDays,class:ee.class,usd:0})}else if(_.ghost||X===0)m.ghost+=G;else{let ee=0;for(let Ee of ge){let le=Ee.lines/X,re=G*le;if(ee+=re,Ee.class==="self"){m.self+=re;continue}let $e=Ee.class==="rework"?1:Number(Ee.decay)||0,J=re*$e;if(m.decayed+=re-J,J<=0)continue;let he=p(Ee.key,{provider:Ee.provider,model:Ee.model});he.fixes+=1,he.linesReworked+=Ee.class==="rework"?Ee.lines:0;let Be=C?.cause&&Ec.includes(C.cause)?C.cause:"unclassified";he.causes[Be]+=1,W?(he.reworkChargedUsd+=J,m.charged+=J,Pe.charged=Tn(Pe.charged+J)):(he.reworkPendingUsd+=J,m.pending+=J),Pe.origins.push({key:Ee.key,provider:he.provider,model:he.model,lines:Ee.lines,ageDays:Ee.ageDays,class:Ee.class,usd:Tn(J)})}let Ze=Math.max(0,G-ee);m.unattributed+=Ze}x.push(Pe),!c.has(_.sha)&&l.has(_.sha)&&S.push(_.sha)}for(let A of Object.values(u)){let _=A.provider||"unknown",L=A.model||"unknown",G=O=>{O.genUsd+=A.genUsd,O.reworkChargedUsd+=A.reworkChargedUsd,O.reworkPendingUsd+=A.reworkPendingUsd,O.linesReworked+=A.linesReworked,O.fixes+=A.fixes;for(let T of Object.keys(O.causes))O.causes[T]+=A.causes[T]};G($[_]||=pw()),G(M[L]||=pw())}x.sort((A,_)=>_.ts-A.ts);let j=Object.values(u).map(A=>{let _=n[`${A.trackId}/${A.boxId}`]||{};return fw({...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${GT} 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:b,noCodeDelta:y,byEpoch:Object.fromEntries(j.map(A=>[A.key,A])),epochs:j,byProvider:Object.fromEntries(Object.entries($).map(([A,_])=>[A,fw(_)])),byModel:Object.fromEntries(Object.entries(M).map(([A,_])=>[A,fw(_)])),buckets:Object.fromEntries(Object.entries(m).map(([A,_])=>[A,Tn(_)])),fixUsdTotal:Tn(Object.values(m).reduce((A,_)=>A+_,0)),fixes:x,verdicts:g,pendingTaps:S,commits:s.length}}var cU,lU,mw,HT,WT,qT,uU,yo,ww,gw,Ec,Wd,dU,pU,Pa,GT,fU,wU,LT,gU,yU,bU,kU,xU,FT,vU,SU,AU,TU,hw,Ud,Tn,pw,Kd=q(()=>{lo();cU=iU(aU),lU=64*1024*1024,mw=(e,t)=>cU("git",["-C",e,...t],{maxBuffer:lU,timeout:6e4}),HT="Bostrat-Epoch",WT="Bostrat-Model",qT="Bostrat-Fix-Cause",uU=24*36e5,yo=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,ww=90,gw=3,Ec=Object.freeze(["defect","design","requirements","style"]),Wd="not-a-fix",dU=new Set(["A","B","E"]),pU=Object.freeze(["fix","likely-fix","planned","unknown"]),Pa=new Set(["fix","likely-fix"]),GT=.8;fU={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};wU=/^revert\b|^revert "/i,LT=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,gU=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,yU=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;bU=/^([a-z]+)(\([^)]*\))?!?:\s/i,kU=new Set(["fix","hotfix","bugfix","revert"]),xU=new Set(["feat","feature","docs","doc","test","tests","build","ci","chore"]),FT=/\bBP-[A-Z0-9]+(?:-[A-Z0-9]+)*\b/i,vU=new Set(["feature-build","docs-writing","research","design","ops-infra","orchestration"]);SU=[/(^|\/)\.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)\//],AU=[/\.(md|mdx|markdown|rst|txt|adoc|asciidoc)$/i,/(^|\/)(docs?|notes|business|design)\//i,/(^|\/)(README|CHANGELOG|CHANGES|LICENSE|CONTRIBUTING|AUTHORS|NOTICE)(\.[a-z]+)?$/i],TU=[/\.(json|jsonc|json5|ya?ml|toml|ini|cfg|conf|properties|env|example)$/i,/(^|\/)\.[^/]+$/,/(^|\/)\.github\//,/(^|\/)(Dockerfile|Makefile|Procfile|fly\.toml)$/];hw=e=>String(e??"").replace(/\s+/g," ").trim(),Ud=400;Tn=e=>Math.round(e*1e4)/1e4,pw=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import{execFile as MU}from"node:child_process";import{promisify as CU}from"node:util";function JT(e,t=Date.now()){return $i(e,t)??0}function PU(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 YT(e,t,{now:n=Date.now()}={}){let r=JT(t,n),o=await K(e);return qd({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:PU(o,t.name,r)})}function DU(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 BU(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=DU(n,t.name),c=i?fr(i.text)?.category??null:null;return{wallReason:a,fixerCategory:c,promptKind:i?.kind??null}}async function jU(e,t){try{let{stdout:n}=await bw(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 LU(e){try{let{stdout:t}=await bw(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function XT(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(m=>m.type==="code.committed"&&m.payload?.sha===n))return{recorded:!1,reason:"duplicate"};let i=s,c=null;try{let{stdout:m}=await bw(r,["show","-s","--format=%ct%n%s",n]),[g,...b]=m.split(`
|
|
197
|
-
`),
|
|
198
|
-
`).trim().slice(0,300)||null}catch{return{recorded:!1,reason:"unknown-sha"}}let l=
|
|
199
|
-
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+YU(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 xw(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function cE(e){let t=aE(e),n=t.match(VU);if(!n)return null;let r=xw(t,n.index);return JU.test(r)?null:{repo:r.match(iE)?.[1]??null,head:r.match(XU)?.[1]??null}}function lE(e){let t=aE(e),n=t.match(zU);if(!n)return null;let r=xw(t,n.index),o=xw(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("-")){QU.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(iE)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function t4(e,t,{exclude:n=()=>!1}={}){let r=async i=>{br("observer");let{stdout:c}=await nE("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",e4],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw ho(l)&&wo(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-qU&&!n(c)),s=await Dn({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 n4(e,t){if(t)return[t];let n=[];for(let r of uE(e)){let o=await Oe(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await ur().catch(()=>[])){let o=await Oe(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function uE(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 r4(e,t,n){let r=await Ca(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)||ZU(c.headRefName,e.id),i=[];for(let c of await n4(e,t?.repo??null)){let l;try{l=await t4(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await pt(c,u.number);if(s(u.headRefName)){if(d&&d.trackId===e.id&&d.announced)return{settled:!0};if(d&&d.trackId!==e.id)continue;return{slug:c,pr:u}}d&&(d.trackId!==e.id||d.announced)||i.push({slug:c,pr:u})}}return i.length?(i.sort((c,l)=>Date.parse(l.pr.createdAt)-Date.parse(c.pr.createdAt)),i[0]):null}function dE(e){if(!e)return 0;let t=0;for(let n of xr)n.startsWith(`${e}|`)&&t++;return t}function pE(){for(let e of[...vw])e()}function fE(e,t,{delays:n=rE,turnEnd:r}={}){let o=cE(t.command);if(!o)return null;let s=!!lE(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(xr.has(a))return null;xr.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(m=>{let g=()=>{clearTimeout(b),vw.delete(g),m()},b=setTimeout(g,l);vw.add(g)});let u=await r4(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:d,pr:p}=u,{link:f,created:w}=await Gt({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 gr(d,p.number))?null:(await kw(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{xr.delete(a)}})();return c.catch(()=>xr.delete(a)),c}function mE(e,t,{delays:n=rE,turnEnd:r,openedWait:o}={}){let s=lE(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(xr.has(a))return null;xr.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=uE(e);if(w.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let m of w){let g=await Oe(m).catch(()=>null);g&&!d.includes(g)&&d.push(g)}}if(l||=(await Ca(e).catch(()=>[])).filter(w=>w!==e.workspace?.branch)[0]||e.workspace?.branch,!d.length||!l)return null;c=d[0];let p=async(w,m)=>{let g=await pt(c,w.number);return g&&g.trackId!==e.id?null:(g||await Gt({repo:c,number:w.number,trackId:e.id,url:w.url,headRef:w.headRefName,title:w.title,source:"merge-observed"}),await bc(c,w.number),m&&((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 kw(t.box,t.agent,r),await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:w.number,url:w.url,title:w.title,branch:w.headRefName,repo:c}}))),w)},f=null;for(let w of n){await new Promise(M=>setTimeout(M,w));let m=[];for(let M of d)try{br("observer");let{stdout:D}=await nE("gh",["pr","view",String(l),"--repo",M,"--json",o4],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await Dn({slug:M})}}),j=JSON.parse(D||"null");j?.number&&m.push({slug:M,pr:j})}catch(D){ho(D)&&wo(D)}let g=m.find(M=>M.pr.state==="MERGED")??(m.length===1?m[0]:null);if(!g)continue;c=g.slug;let b=g.pr;if(!b?.number)continue;if(b.state==="CLOSED")return null;if(b.state!=="MERGED"){if(f=b,s.auto)return p(b,!0);continue}let y=await pt(c,b.number);if(y&&y.trackId!==e.id)return null;y||await Gt({repo:c,number:b.number,trackId:e.id,url:b.url,headRef:b.headRefName,title:b.title,source:"merge-observed"}),await kw(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===b.number))return null;let S=M=>M.some(D=>D.type==="pr.state.changed"&&D.payload?.change==="opened"&&D.payload?.number===b.number),$=0;if(!S(x)){if(cE(t.command)&&await gr(c,b.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:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c,selfMerge:!0}})}else if((await pt(c,b.number))?.announced){let M=o?.capMs??GU,D=o?.pollMs??KU,j=Date.now()+M;for(;Date.now()<j&&(await new Promise(A=>setTimeout(A,D)),!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:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c}}),await Vd(e,{repo:c,number:b.number,title:b.title,branch:b.headRefName,url:b.url}).catch(()=>{}),await Sr(c,b.number).catch(()=>{}),await vr(c,b.number).catch(()=>{}),await fo(c,b.number,"merged"),b}return f?p(f,!1):null}finally{xr.delete(a)}})();return i.catch(()=>xr.delete(a)),i}var nE,rE,HU,WU,qU,GU,KU,oE,sE,VU,zU,JU,tE,YU,iE,XU,ZU,QU,e4,xr,vw,o4,Jd=q(()=>{ye();Hr();bt();xc();Kn();Ar();wa();ys();zd();nE=UU(FU),rE=[4e3,15e3,45e3],HU=2500,WU=5*6e4;qU=15e3,GU=9e4,KU=2e3,oE=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,sE=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,VU=new RegExp(`${oE}${sE}create\\b`),zU=new RegExp(`${oE}${sE}merge\\b`),JU=/\s--web\b/,tE=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,YU=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");iE=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,XU=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,ZU=BT;QU=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);e4="number,url,title,headRefName,createdAt";xr=new Set;vw=new Set;o4="number,url,title,headRefName,state"});import s4 from"node:fs/promises";import Aw from"node:path";import a4 from"node:os";function Sw(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function Yd(e){let t=i4.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=Sw(n,"from"),s=o?o.replace(/^uds:/,""):null,a=hE(s);return{socket:s,pid:a,name:Sw(n,"from-name"),mode:Sw(n,"from-mode"),text:r.trim()}}function hE(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function c4(){return process.env.CLAUDE_CONFIG_DIR?Aw.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):Aw.join(a4.homedir(),".claude","sessions")}async function l4(e){let t=hE(e);if(!t)return null;try{let n=await s4.readFile(Aw.join(c4(),`${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 wE(e,t){let n=await l4(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 i4,Tw=q(()=>{i4=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function gE(e=process.env){return!!(Ce(e)&&e.BOSTRAT_PLANE_TOKEN)}async function yE(e,t,n=Ew){let r=new AbortController,o=setTimeout(()=>r.abort(),n);try{return await d4(`${Ce(t)}${e}`,{headers:p4(t),signal:r.signal})}finally{clearTimeout(o)}}async function Ic({env:e=process.env,force:t=!1,timeoutMs:n=Ew}={}){if(!gE(e))return{followups:[],source:"unconfigured"};if(!t&&bs&&Date.now()-bs.at<u4)return{followups:bs.followups,source:"cache"};try{let r=await yE("/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 bs={at:Date.now(),followups:s},{followups:s,source:"plane"}}catch(r){return bs?{followups:bs.followups,source:"stale",error:r?.message}:{followups:[],source:"unreachable",error:r?.message}}}async function bE(e,{env:t=process.env,timeoutMs:n=Ew}={}){if(!e)return{followup:null,source:"unconfigured"};if(!gE(t))return{followup:null,source:"unconfigured"};try{let r=await yE(`/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=bs?.followups.find(s=>s.id===e)??null;return o?{followup:o,source:"stale",error:r?.message}:{followup:null,source:"unreachable",error:r?.message}}}var u4,Ew,bs,d4,p4,kE=q(()=>{bt();u4=6e4,Ew=5e3,bs=null,d4=(...e)=>fetch(...e);p4=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});async function bo(e,t){if(_w.has(e)){let n=new Error("offer already answered (answer in flight)");throw n.code="answer-in-flight",n}_w.add(e);try{return await t()}finally{_w.delete(e)}}var _w,$c=q(()=>{_w=new Set});import{randomUUID as ks,createHash as f4}from"node:crypto";function Xd(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function $E(e){for(let[t,n]of g4)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 AE(e,{at:t="",promptRequired:n=!0,standing:r=!1}={}){let o=e&&typeof e=="object"?e:{},s=xs(o.label).replace(/\s+/g," ");if(!s)throw new Error(`${t}label required (\u2264${Iw} chars, e.g. "Deploy to Pi")`);if(s.length>Iw)throw new Error(`${t}label must be \u2264${Iw} characters`);let a=xs(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")>xE)throw new Error(`${t}prompt must be \u2264${xE} bytes`);if(r){let l=$E(a);if(l)throw new Error(`${t}${l}`)}let i=o.standing==null||o.standing===""?"offerable":String(o.standing).trim().toLowerCase();if(!w4.includes(i))throw new Error(`${t}standing must be "offerable" or "never", got "${o.standing}"`);let c=xs(o.why).slice(0,EE)||null;return{label:s,prompt:a,why:c,standing:i}}function RE(e,{standing:t=!1}={}){let n=e&&typeof e=="object"?e:{},r=xs(n.trigger)||(n.ship?"now":""),o=t?m4:_E.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"?k4(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>SE)throw new Error(`items must be \u2264${SE} \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)=>AE(f,{at:`items[${w}]: `,standing:t})),c=xs(n.why).slice(0,EE)||null;let p=new Set;for(let f of i){let w=Xd(f.label);if(p.has(w))throw new Error(`items must have distinct labels ("${f.label}" appears twice)`);p.add(w)}}else i=[AE(n,{promptRequired:!s,standing:t})];let l=i[0],u=c??l.why,d=t?b4(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 y4(e){return e.standing==="never"?{promotable:!1,promotableReason:"one-off"}:$E(e.prompt)?{promotable:!1,promotableReason:"specifics"}:{promotable:!0,promotableReason:null}}function b4(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 k4(e){let t=e&&typeof e=="object"?e:{},n=Array.isArray(t.actions)?t.actions.map(i=>String(i).trim().toLowerCase()):$w.slice();for(let i of n)if(!$w.includes(i))throw new Error(`ship.actions may only contain ${$w.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=xs(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=>xs(i)).filter(Boolean),a.length||(a=null)}return{actions:[...new Set(n)],rebase:r,toPr:o,base:s,repos:a}}function Zd(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 OE(e){return{armId:e.armId,label:e.label,prompt:e.prompt,promptHash:IE(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",...y4(e)}}async function ME(e,t,{pull:n=Ic,ship:r={}}={}){if(Ow(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=RE(t);if(o.ship)return{kind:"ship",...await T4(e,o,r)};let s={id:e.id,cwd:e.cwd},a=(e.boxes||[]).find(y=>y.name===e.leadBox)||null,i={boxId:e.leadBox??null,provider:a?.agent??null},c=ks(),l=(y,x,S={})=>({ts:Date.now(),armId:ks(),trigger:o.trigger,label:y.label,prompt:y.prompt,why:y.why,standing:y.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 y=ks(),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:y,trigger:"now",repo:e.workspace?.repo||null,context:{},groupId:c,why:o.planWhy,rows:x.map(OE)}}),{kind:"now",offerId:y,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=Zd(f,{trigger:o.trigger}),m=[],g=0;for(let y of o.items){let x=Xd(y.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){m.push({label:y.label,standing:y.standing,covered:{id:S.id,label:S.label}});continue}let $=w.filter(j=>Xd(j.label)===x),M=$.length?$[$.length-1].armId:null,D=l(y,g++,M?{replaces:M}:{});await N(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:D}),m.push({armId:D.armId,label:y.label,standing:y.standing,replaces:M})}let b=m.filter(y=>y.armId);return b.length?{kind:"armed",armId:b[0].armId,replaces:b[0].replaces,items:m,...u}:{kind:"covered",covered:m[0].covered,items:m,...u}}async function CE(e,t){if(!t)throw new Error("armId required");let n={id:e.id,cwd:e.cwd},r=await K(n);if(!Zd(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 NE(e,t,{pull:n=Ic}={}){if(Ow(e))throw new Error("propose from a lead Track, not a loop's run Track");let r=RE(t,{standing:!0}),o=r.repo??e.workspace?.repo??null,s={id:e.id,cwd:e.cwd};if((await K(s)).reduce((g,b)=>b.type==="followup.create.offered"?b.payload?.proposalId??g:b.type==="followup.create.answered"&&b.payload?.proposalId===g?null:g,null))throw new Error("a follow-up proposal is already waiting on the operator in this Track \u2014 one at a time");let{followups:c,source:l}=await n().catch(()=>({followups:[],source:"unreachable"}));if(l==="unreachable")throw new Error("plane unreachable \u2014 standing follow-ups live in the plane; try again shortly");let u=Xd(r.label),d=c.filter(g=>g.dedupeKey===u&&g.trigger===r.trigger),p=d.find(g=>g.status==="deleted"),f=d.find(g=>g.status!=="deleted");if(f)throw new Error(`"${f.label}" (${Rw[f.trigger]||f.trigger}) already exists as a standing follow-up \u2014 not re-proposed`);if(p){let g=p.deletedAt?new Date(p.deletedAt).toISOString().slice(0,10):"earlier";throw new Error(`the operator deleted "${p.label}" on ${g} \u2014 do not re-propose it`)}let w=ks(),m=(e.boxes||[]).find(g=>g.name===e.leadBox)||null;return await N(s,{type:"followup.create.offered",actor:"agent",source:"cli",boxId:e.leadBox??null,provider:m?.agent??null,payload:{ts:Date.now(),proposalId:w,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why,origin:"agent"}}),{proposalId:w,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why}}async function PE(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 Rc(e,t,n={},{pull:r=Ic}={}){if(!e||Ow(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 b=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)}`),$},y=new Set(b(n));if(s.some(S=>S.type==="followup.offered"&&S.payload?.trigger==="pr.merged"&&b(S.payload?.context).some($=>y.has($))))return null}let i=Zd(s,{trigger:t}),c=i.length?i[i.length-1].groupId??null:null,l=new Map(i.map((b,y)=>[b.armId,y])),u=b=>b.groupId&&b.groupId===c?0:1,d=i.slice().sort((b,y)=>u(b)-u(y)||l.get(b.armId)-l.get(y.armId)).slice(0,vE).map(OE),p=c?i.find(b=>b.groupId===c&&b.planWhy)?.planWhy??null:null,f=[];if(t!=="now"){let{followups:b}=await r().catch(()=>({followups:[]}));f=b.filter(y=>y.status==="active"&&y.mode!=="off"&&y.trigger===t&&(y.repo==null||y.repo===a)).sort((y,x)=>{let S=$=>$.stats?.offered?($.stats.accepted||0)/$.stats.offered:0;return S(x)-S(y)||(y.origin==="operator"?-1:0)-(x.origin==="operator"?-1:0)||(x.createdAt||0)-(y.createdAt||0)}).slice(0,h4).map(y=>({followupId:y.id,label:y.label,promptPreview:x4(y.prompt),promptHash:IE(y.prompt),origin:y.origin,repo:y.repo??null}))}let w=[...d,...f].slice(0,vE);if(!w.length)return null;let m=ks(),g={ts:Date.now(),offerId:m,trigger:t,repo:a,context:{...n,repo:a},groupId:c,why:p,rows:w};return await N(o,{type:"followup.offered",actor:"system",source:"orchestrator",payload:g}),g}function v4(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 TE(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 DE(e,{offerId:t,followupId:n=null,armId:r=null,promoted:o=null,all:s=!1}={},a={}){if(!t)throw new Error("offerId required");return bo(`fu:${e.id}:${t}`,()=>A4(e,{offerId:t,followupId:n,armId:r,promoted:o,all:s===!0},a))}async function A4(e,{offerId:t,followupId:n,armId:r,promoted:o,all:s},a){let{sayToLead:i,unarchiveTrack:c,getFollowup:l=bE}=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=v4(d,p.payload),m=S4(w,f);if(!m.length)throw new Error("offer already answered");let g=A=>N(u,{type:"followup.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,...A}}),b=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},y=A=>{let L=d.find(G=>G.type==="followup.armed"&&G.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,_=m.filter(W=>W.armId&&(!A||W.groupId===A)).sort((W,X)=>(W.seq??0)-(X.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(W=>({row:W,prompt:y(W)})),G=await b();if(!i)throw new Error("sayToLead unavailable");let O=`${TE(p.payload).replace(/:$/,"")} \u2014 ${L.length} steps, in order:`,T=L.map((W,X)=>`${X+1}. ${W.row.label}
|
|
196
|
+
`)}}async function zT(e,t,{fixKey:n=null,window:r=yo,resolveOrigin:o=null,context:s={}}={}){let a=await WT(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=FT(a.message),l=n?Hd(n):c.epoch,u=l?Na(l):null,{stdout:d}=await fw(e,["diff","-U0","--no-color","-M","-C",i,t]),p=RU(d),f=0,w=0,m=0,y=0,k=0,g=0,x=0,S=new Map;for(let _ of p){if(_.binary)continue;let L=_.oldPath||_.newPath||"",G=_.deleted.reduce((W,[,X])=>W+X,0),O=$U(L);if(O==="excluded"){m+=G;continue}if(O==="docs"){y+=G,k+=_.added;continue}if(O==="config"){g+=G;continue}f+=_.added;let T=new Set;for(let W of _.hunks||[])for(let X of MU(W))T.add(X);x+=T.size;let C=[];for(let[W,X]of _.deleted)for(let ge=W;ge<W+X;ge++)T.has(ge)||C.push(ge);if(w+=C.length,!_.oldPath||!C.length)continue;let P;try{let W=["blame","-w","-M","-C","--line-porcelain"];for(let[X,ge]of CU(C))W.push("-L",`${X},${X+ge-1}`);W.push(i,"--",_.oldPath),P=NU((await fw(e,W)).stdout)}catch{continue}for(let{sha:W,time:X}of P.values()){let ge=S.get(W)??{lines:0,time:X};ge.lines+=1,S.set(W,ge)}}let $=[],M=0,D=new Map;for(let[_,{lines:L,time:G}]of S){let O=D.get(_);if(!O){try{O=await WT(e,_)}catch{O=null}D.set(_,O)}let T=O?FT(O.message):{epoch:null},C=T.epoch,P=T.provider,W=T.model;if(!C&&o){let Z=await o(_,O);Z?.trackId&&Z?.boxId!=null&&(C={trackId:Z.trackId,boxId:Z.boxId,epoch:Z.epoch??0},P=P??Z.provider??null,W=W??Z.model??null)}if(!C){M+=L;continue}let X=Number.isFinite(G)?G:O?.ts??a.ts,ge=Math.max(0,(a.ts-X)/fU),Ie=Na(C),Ce=Ie===u?"self":gU(ge,r);$.push({sha:_,key:Ie,trackId:C.trackId,boxId:C.boxId,epoch:C.epoch,provider:P??null,model:W??null,lines:L,ageDays:Math.round(ge*100)/100,class:Ce,decay:Ce==="rework"?1:Ce==="drift"?Math.round(yU(ge,{window:r})*1e3)/1e3:0})}$.sort((_,L)=>L.lines-_.lines);let j=Gd({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0}),A=gw({heuristic:j,subject:a.subject,ref:s.ref??null,promptKind:s.promptKind??null,fixerCategory:s.fixerCategory??null,ticketKind:s.ticketKind??null});return{sha:a.sha,parent:i,subject:a.subject.slice(0,200),ts:a.ts,fixKey:u,fixProvider:c.provider??s.provider??null,fixModel:c.model??s.model??null,window:r,added:f,deleted:w,excludedDeleted:m,cosmeticDeleted: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 JT(e,{minLines:t=ww}={}){if(!e)return!1;let n=e.intent?.intent??"unknown";return Pa.has(n)?_c(e)>=t:!1}function _c(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function pw(e){let t=e.genUsd;return{...e,genUsd:En(t),reworkChargedUsd:En(e.reworkChargedUsd),reworkPendingUsd:En(e.reworkPendingUsd),trueUsd:En(t+e.reworkChargedUsd),multiplier:t>0?En((t+e.reworkChargedUsd)/t):null}}function YT(e,{window:t=yo,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)=>Na({trackId:A,boxId:_,epoch:L??0}),p=(A,_={})=>{if(!u[A]){let G=Hd(A)||{};u[A]={key:A,...G,provider:null,model:null,...dw()}}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:G}=on(A),O=p(_,{provider:A.provider,model:G!=="unknown"?G:null});O.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,G)=>{let O=f.get(`${A}/${_}`)||[],T=-1/0;for(let W of O)W.ts<G&&(T=W.ts);let C=0,P=0;for(let W of o)W.trackId!==A||W.boxId!==_||(W.epoch??0)===(L??0)&&(W.ts<=T||W.ts>G||(C+=on(W).usd,P++));return{usd:C,turns:P,from:Number.isFinite(T)?T:null}},m={charged:0,pending:0,self:0,decayed:0,unattributed:0,ghost:0,planned:0},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={},D=new Set;for(let A of a){let _=A.payload||{};if(!_.sha||D.has(_.sha))continue;if(D.add(_.sha),_.codeDelta===!1){g++;continue}let L=Hd(_.fixKey)||{trackId:A.trackId,boxId:A.boxId,epoch:A.epoch??0},{usd:G,turns:O}=w(L.trackId,L.boxId,L.epoch,_.ts??A.ts),T=hU.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 P=C?.level??"none";y[P]=(y[P]||0)+1;let W=mU.has(P),X=Number(_.deleted)||0,ge=Array.isArray(_.origins)?_.origins:[],Ie=ge.filter(Z=>Z.class==="rework").reduce((Z,Ze)=>Z+Ze.lines,0),Ce={sha:_.sha,subject:_.subject??"",ts:_.ts??A.ts,trackId:A.trackId,fixKey:_.fixKey??null,fixProvider:_.fixProvider??A.provider??null,fixUsd:En(G),fixTurns:O,linesReworked:Ie,unattributedLines:Number(_.unattributedLines)||0,ghost:_.ghost===!0,intent:{intent:T,basis:_.intent?.basis??null},verdict:C?{cause:C.cause,level:P,basis:C.basis??null}:null,charged:En(0),origins:[]};if(T==="planned"||C?.cause===Wd){m.planned+=G;for(let Z of ge)Z.class==="rework"&&Ce.origins.push({key:Z.key,provider:Z.provider??null,model:Z.model??null,lines:Z.lines,ageDays:Z.ageDays,class:Z.class,usd:0})}else if(_.ghost||X===0)m.ghost+=G;else{let Z=0;for(let Ee of ge){let le=Ee.lines/X,re=G*le;if(Z+=re,Ee.class==="self"){m.self+=re;continue}let $e=Ee.class==="rework"?1:Number(Ee.decay)||0,J=re*$e;if(m.decayed+=re-J,J<=0)continue;let he=p(Ee.key,{provider:Ee.provider,model:Ee.model});he.fixes+=1,he.linesReworked+=Ee.class==="rework"?Ee.lines:0;let Be=C?.cause&&Ec.includes(C.cause)?C.cause:"unclassified";he.causes[Be]+=1,W?(he.reworkChargedUsd+=J,m.charged+=J,Ce.charged=En(Ce.charged+J)):(he.reworkPendingUsd+=J,m.pending+=J),Ce.origins.push({key:Ee.key,provider:he.provider,model:he.model,lines:Ee.lines,ageDays:Ee.ageDays,class:Ee.class,usd:En(J)})}let Ze=Math.max(0,G-Z);m.unattributed+=Ze}x.push(Ce),!c.has(_.sha)&&l.has(_.sha)&&S.push(_.sha)}for(let A of Object.values(u)){let _=A.provider||"unknown",L=A.model||"unknown",G=O=>{O.genUsd+=A.genUsd,O.reworkChargedUsd+=A.reworkChargedUsd,O.reworkPendingUsd+=A.reworkPendingUsd,O.linesReworked+=A.linesReworked,O.fixes+=A.fixes;for(let T of Object.keys(O.causes))O.causes[T]+=A.causes[T]};G($[_]||=dw()),G(M[L]||=dw())}x.sort((A,_)=>_.ts-A.ts);let j=Object.values(u).map(A=>{let _=n[`${A.trackId}/${A.boxId}`]||{};return pw({...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${VT} 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,pw(_)])),byModel:Object.fromEntries(Object.entries(M).map(([A,_])=>[A,pw(_)])),buckets:Object.fromEntries(Object.entries(m).map(([A,_])=>[A,En(_)])),fixUsdTotal:En(Object.values(m).reduce((A,_)=>A+_,0)),fixes:x,verdicts:y,pendingTaps:S,commits:s.length}}var dU,pU,fw,qT,GT,KT,fU,yo,hw,ww,Ec,Wd,mU,hU,Pa,VT,wU,bU,UT,kU,xU,vU,SU,AU,HT,TU,EU,_U,IU,mw,Ud,En,dw,Kd=q(()=>{lo();dU=uU(lU),pU=64*1024*1024,fw=(e,t)=>dU("git",["-C",e,...t],{maxBuffer:pU,timeout:6e4}),qT="Bostrat-Epoch",GT="Bostrat-Model",KT="Bostrat-Fix-Cause",fU=24*36e5,yo=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,hw=90,ww=3,Ec=Object.freeze(["defect","design","requirements","style"]),Wd="not-a-fix",mU=new Set(["A","B","E"]),hU=Object.freeze(["fix","likely-fix","planned","unknown"]),Pa=new Set(["fix","likely-fix"]),VT=.8;wU={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};bU=/^revert\b|^revert "/i,UT=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,kU=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,xU=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;vU=/^([a-z]+)(\([^)]*\))?!?:\s/i,SU=new Set(["fix","hotfix","bugfix","revert"]),AU=new Set(["feat","feature","docs","doc","test","tests","build","ci","chore"]),HT=/\bBP-[A-Z0-9]+(?:-[A-Z0-9]+)*\b/i,TU=new Set(["feature-build","docs-writing","research","design","ops-infra","orchestration"]);EU=[/(^|\/)\.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)\//],_U=[/\.(md|mdx|markdown|rst|txt|adoc|asciidoc)$/i,/(^|\/)(docs?|notes|business|design)\//i,/(^|\/)(README|CHANGELOG|CHANGES|LICENSE|CONTRIBUTING|AUTHORS|NOTICE)(\.[a-z]+)?$/i],IU=[/\.(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(),Ud=400;En=e=>Math.round(e*1e4)/1e4,dw=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import{execFile as PU}from"node:child_process";import{promisify as DU}from"node:util";function XT(e,t=Date.now()){return $i(e,t)??0}function jU(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 ZT(e,t,{now:n=Date.now()}={}){let r=XT(t,n),o=await K(e);return qd({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:jU(o,t.name,r)})}function LU(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 FU(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=LU(n,t.name),c=i?mr(i.text)?.category??null:null;return{wallReason:a,fixerCategory:c,promptKind:i?.kind??null}}async function UU(e,t){try{let{stdout:n}=await yw(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 HU(e){try{let{stdout:t}=await yw(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function QT(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(m=>m.type==="code.committed"&&m.payload?.sha===n))return{recorded:!1,reason:"duplicate"};let i=s,c=null;try{let{stdout:m}=await yw(r,["show","-s","--format=%ct%n%s",n]),[y,...k]=m.split(`
|
|
197
|
+
`),g=Number(y.trim())*1e3;Number.isFinite(g)&&g>0&&(i=g),c=k.join(`
|
|
198
|
+
`).trim().slice(0,300)||null}catch{return{recorded:!1,reason:"unknown-sha"}}let l=XT(t,i),u=await UU(r,n),d=await HU(r),p=await N(e,{type:"code.committed",actor:"agent",source:"hook",boxId:t.name,provider:t.agent??null,payload:{sha:n,ref:d,subject:c,...u,ts:i}}),f=null,w=!1;try{let m=await zT(r,n,{fixKey:Na({trackId:e.id,boxId:t.name,epoch:l}),window:yo,context:{...FU(e,t,a,l),ref:d,provider:t.agent??null}});if(m&&(f=await N(e,{type:"code.rework",actor:"system",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:p.id,payload:m}),!(e?.workspace?.kind==="worktree"&&!!e?.workspace?.branch)&&JT(m))){let k=_c(m),g=m.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:m.subject,linesReworked:k,origins:g.map(x=>({key:x.key,provider:x.provider,model:x.model,lines:x.lines,ageDays:x.ageDays})),heuristic:m.heuristic,intent:m.intent,ts:i}}),w=!0}}catch(m){o.warn?.(`[rework] ${e.id}: analysis of ${String(n).slice(0,7)} failed: ${m?.message||m}`)}return{recorded:!0,event:p,rework:f,offered:w}}function eE(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 tE(e,t,n){if(!Ec.includes(n)&&n!==Wd)throw new Error(`bad cause "${n}" (${Ec.join("|")}|${Wd})`);let r=await K(e),o=r.find(s=>s.type==="rework.cause.offered"&&s.payload?.sha===t);if(!o)throw new Error("unknown rework offer");if(eE(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 nE(e,t){let n=await K(e);if(!eE(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 Vd(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=gw({heuristic:Gd({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";(Pa.has(S)||S==="unknown"&&Pa.has(d.intent))&&_c(x)!==0&&p.push(x)}let f=p.reduce((g,x)=>g+_c(x),0);if(f<ww)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 m=p.map(g=>g.sha),y=p.map(g=>g.heuristic).find(g=>g&&g.level!=="C")??Gd({subject:r})??p.map(g=>g.heuristic).find(Boolean)??null,k=Pa.has(d.intent)?d:p.find(g=>Pa.has(g.intent?.intent))?.intent??d;return await N(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:null,payload:{sha:m[m.length-1],shas:m,pr:{repo:t,number:n,title:String(r||"").slice(0,200),url:s,branch:o},subject:String(r||"").slice(0,200)||p[p.length-1].subject,linesReworked:f,origins:[...w.values()].sort((g,x)=>x.lines-g.lines).slice(0,3),heuristic:y,intent:k,ts:i}}),{offered:!0,shas:m}}catch(c){return a.warn?.(`[rework] ${e?.id}: PR-level offer for #${n} failed: ${c?.message||c}`),{offered:!1,reason:"error"}}}var BU,yw,zd=q(()=>{ye();Uu();Kd();BU=DU(PU),yw=(e,t)=>BU("git",["-C",e,...t],{maxBuffer:16*1024*1024,timeout:3e4})});import{execFile as WU}from"node:child_process";import{promisify as qU}from"node:util";async function bw(e,t,{midTurn:n=ns,pollMs:r=GU,capMs:o=KU}={}){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 cE(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){rE.lastIndex=n;let o=rE.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(`
|
|
199
|
+
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+QU(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 kw(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function uE(e){let t=cE(e),n=t.match(YU);if(!n)return null;let r=kw(t,n.index);return ZU.test(r)?null:{repo:r.match(lE)?.[1]??null,head:r.match(e4)?.[1]??null}}function dE(e){let t=cE(e),n=t.match(XU);if(!n)return null;let r=kw(t,n.index),o=kw(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("-")){n4.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(lE)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function o4(e,t,{exclude:n=()=>!1}={}){let r=async i=>{kr("observer");let{stdout:c}=await oE("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",r4],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw ho(l)&&wo(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-VU&&!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 s4(e,t){if(t)return[t];let n=[];for(let r of pE(e)){let o=await Oe(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await dr().catch(()=>[])){let o=await Oe(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function pE(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 a4(e,t,n){let r=await Ca(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)||t4(c.headRefName,e.id),i=[];for(let c of await s4(e,t?.repo??null)){let l;try{l=await o4(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await pt(c,u.number);if(s(u.headRefName)){if(d&&d.trackId===e.id&&d.announced)return{settled:!0};if(d&&d.trackId!==e.id)continue;return{slug:c,pr:u}}d&&(d.trackId!==e.id||d.announced)||i.push({slug:c,pr:u})}}return i.length?(i.sort((c,l)=>Date.parse(l.pr.createdAt)-Date.parse(c.pr.createdAt)),i[0]):null}function fE(e){if(!e)return 0;let t=0;for(let n of vr)n.startsWith(`${e}|`)&&t++;return t}function mE(){for(let e of[...xw])e()}function hE(e,t,{delays:n=sE,turnEnd:r}={}){let o=uE(t.command);if(!o)return null;let s=!!dE(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(vr.has(a))return null;vr.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(m=>{let y=()=>{clearTimeout(k),xw.delete(y),m()},k=setTimeout(y,l);xw.add(y)});let u=await a4(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:d,pr:p}=u,{link:f,created:w}=await Kt({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 yr(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{vr.delete(a)}})();return c.catch(()=>vr.delete(a)),c}function wE(e,t,{delays:n=sE,turnEnd:r,openedWait:o}={}){let s=dE(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(vr.has(a))return null;vr.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=pE(e);if(w.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let m of w){let y=await Oe(m).catch(()=>null);y&&!d.includes(y)&&d.push(y)}}if(l||=(await Ca(e).catch(()=>[])).filter(w=>w!==e.workspace?.branch)[0]||e.workspace?.branch,!d.length||!l)return null;c=d[0];let p=async(w,m)=>{let y=await pt(c,w.number);return y&&y.trackId!==e.id?null:(y||await Kt({repo:c,number:w.number,trackId:e.id,url:w.url,headRef:w.headRefName,title:w.title,source:"merge-observed"}),await bc(c,w.number),m&&((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 m=[];for(let M of d)try{kr("observer");let{stdout:D}=await oE("gh",["pr","view",String(l),"--repo",M,"--json",i4],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await Bn({slug:M})}}),j=JSON.parse(D||"null");j?.number&&m.push({slug:M,pr:j})}catch(D){ho(D)&&wo(D)}let y=m.find(M=>M.pr.state==="MERGED")??(m.length===1?m[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 pt(c,k.number);if(g&&g.trackId!==e.id)return null;g||await Kt({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(D=>D.type==="pr.state.changed"&&D.payload?.change==="opened"&&D.payload?.number===k.number),$=0;if(!S(x)){if(uE(t.command)&&await yr(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 pt(c,k.number))?.announced){let M=o?.capMs??zU,D=o?.pollMs??JU,j=Date.now()+M;for(;Date.now()<j&&(await new Promise(A=>setTimeout(A,D)),!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 Vd(e,{repo:c,number:k.number,title:k.title,branch:k.headRefName,url:k.url}).catch(()=>{}),await Ar(c,k.number).catch(()=>{}),await Sr(c,k.number).catch(()=>{}),await fo(c,k.number,"merged"),k}return f?p(f,!1):null}finally{vr.delete(a)}})();return i.catch(()=>vr.delete(a)),i}var oE,sE,GU,KU,VU,zU,JU,aE,iE,YU,XU,ZU,rE,QU,lE,e4,t4,n4,r4,vr,xw,i4,Jd=q(()=>{ye();Hr();bt();xc();Kn();Tr();wa();ys();zd();oE=qU(WU),sE=[4e3,15e3,45e3],GU=2500,KU=5*6e4;VU=15e3,zU=9e4,JU=2e3,aE=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,iE=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,YU=new RegExp(`${aE}${iE}create\\b`),XU=new RegExp(`${aE}${iE}merge\\b`),ZU=/\s--web\b/,rE=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,QU=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");lE=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,e4=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,t4=LT;n4=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);r4="number,url,title,headRefName,createdAt";vr=new Set;xw=new Set;i4="number,url,title,headRefName,state"});import c4 from"node:fs/promises";import Sw from"node:path";import l4 from"node:os";function vw(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function Yd(e){let t=u4.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=vw(n,"from"),s=o?o.replace(/^uds:/,""):null,a=gE(s);return{socket:s,pid:a,name:vw(n,"from-name"),mode:vw(n,"from-mode"),text:r.trim()}}function gE(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function d4(){return process.env.CLAUDE_CONFIG_DIR?Sw.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):Sw.join(l4.homedir(),".claude","sessions")}async function p4(e){let t=gE(e);if(!t)return null;try{let n=await c4.readFile(Sw.join(d4(),`${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 yE(e,t){let n=await p4(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 u4,Aw=q(()=>{u4=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function bE(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function kE(e,t,n=Tw){let r=new AbortController,o=setTimeout(()=>r.abort(),n);try{return await m4(`${Ne(t)}${e}`,{headers:h4(t),signal:r.signal})}finally{clearTimeout(o)}}async function Ic({env:e=process.env,force:t=!1,timeoutMs:n=Tw}={}){if(!bE(e))return{followups:[],source:"unconfigured"};if(!t&&bs&&Date.now()-bs.at<f4)return{followups:bs.followups,source:"cache"};try{let r=await kE("/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 bs={at:Date.now(),followups:s},{followups:s,source:"plane"}}catch(r){return bs?{followups:bs.followups,source:"stale",error:r?.message}:{followups:[],source:"unreachable",error:r?.message}}}async function xE(e,{env:t=process.env,timeoutMs:n=Tw}={}){if(!e)return{followup:null,source:"unconfigured"};if(!bE(t))return{followup:null,source:"unconfigured"};try{let r=await kE(`/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=bs?.followups.find(s=>s.id===e)??null;return o?{followup:o,source:"stale",error:r?.message}:{followup:null,source:"unreachable",error:r?.message}}}var f4,Tw,bs,m4,h4,vE=q(()=>{bt();f4=6e4,Tw=5e3,bs=null,m4=(...e)=>fetch(...e);h4=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});async function bo(e,t){if(Ew.has(e)){let n=new Error("offer already answered (answer in flight)");throw n.code="answer-in-flight",n}Ew.add(e);try{return await t()}finally{Ew.delete(e)}}var Ew,$c=q(()=>{Ew=new Set});import{randomUUID as ks,createHash as w4}from"node:crypto";function Xd(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function OE(e){for(let[t,n]of k4)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 EE(e,{at:t="",promptRequired:n=!0,standing:r=!1}={}){let o=e&&typeof e=="object"?e:{},s=xs(o.label).replace(/\s+/g," ");if(!s)throw new Error(`${t}label required (\u2264${_w} chars, e.g. "Deploy to Pi")`);if(s.length>_w)throw new Error(`${t}label must be \u2264${_w} characters`);let a=xs(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")>SE)throw new Error(`${t}prompt must be \u2264${SE} bytes`);if(r){let l=OE(a);if(l)throw new Error(`${t}${l}`)}let i=o.standing==null||o.standing===""?"offerable":String(o.standing).trim().toLowerCase();if(!b4.includes(i))throw new Error(`${t}standing must be "offerable" or "never", got "${o.standing}"`);let c=xs(o.why).slice(0,IE)||null;return{label:s,prompt:a,why:c,standing:i}}function ME(e,{standing:t=!1}={}){let n=e&&typeof e=="object"?e:{},r=xs(n.trigger)||(n.ship?"now":""),o=t?g4:$E.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"?S4(n.ship):null,a=n.items!=null;if(a&&s)throw new Error("a ship offer has its own card \u2014 `items` cannot be combined with a `ship` block");let i,c=null;if(a){if(!Array.isArray(n.items)||!n.items.length)throw new Error("items must be a non-empty array of {label, prompt, why?, standing?}");if(n.items.length>TE)throw new Error(`items must be \u2264${TE} \u2014 one item per step the operator might run, save or skip separately`);if(t&&n.items.length>1)throw new Error("propose one standing follow-up at a time \u2014 a standing follow-up is one prompt");i=n.items.map((f,w)=>EE(f,{at:`items[${w}]: `,standing:t})),c=xs(n.why).slice(0,IE)||null;let p=new Set;for(let f of i){let w=Xd(f.label);if(p.has(w))throw new Error(`items must have distinct labels ("${f.label}" appears twice)`);p.add(w)}}else i=[EE(n,{promptRequired:!s,standing:t})];let l=i[0],u=c??l.why,d=t?v4(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 x4(e){return e.standing==="never"?{promotable:!1,promotableReason:"one-off"}:OE(e.prompt)?{promotable:!1,promotableReason:"specifics"}:{promotable:!0,promotableReason:null}}function v4(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 S4(e){let t=e&&typeof e=="object"?e:{},n=Array.isArray(t.actions)?t.actions.map(i=>String(i).trim().toLowerCase()):Iw.slice();for(let i of n)if(!Iw.includes(i))throw new Error(`ship.actions may only contain ${Iw.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=xs(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=>xs(i)).filter(Boolean),a.length||(a=null)}return{actions:[...new Set(n)],rebase:r,toPr:o,base:s,repos:a}}function Zd(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 CE(e){return{armId:e.armId,label:e.label,prompt:e.prompt,promptHash:RE(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",...x4(e)}}async function NE(e,t,{pull:n=Ic,ship:r={}}={}){if(Rw(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=ME(t);if(o.ship)return{kind:"ship",...await I4(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=ks(),l=(g,x,S={})=>({ts:Date.now(),armId:ks(),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=ks(),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(CE)}}),{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=Zd(f,{trigger:o.trigger}),m=[],y=0;for(let g of o.items){let x=Xd(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){m.push({label:g.label,standing:g.standing,covered:{id:S.id,label:S.label}});continue}let $=w.filter(j=>Xd(j.label)===x),M=$.length?$[$.length-1].armId:null,D=l(g,y++,M?{replaces:M}:{});await N(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:D}),m.push({armId:D.armId,label:g.label,standing:g.standing,replaces:M})}let k=m.filter(g=>g.armId);return k.length?{kind:"armed",armId:k[0].armId,replaces:k[0].replaces,items:m,...u}:{kind:"covered",covered:m[0].covered,items:m,...u}}async function PE(e,t){if(!t)throw new Error("armId required");let n={id:e.id,cwd:e.cwd},r=await K(n);if(!Zd(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 DE(e,t,{pull:n=Ic}={}){if(Rw(e))throw new Error("propose from a lead Track, not a loop's run Track");let r=ME(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=Xd(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}" (${$w[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=ks(),m=(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:m?.agent??null,payload:{ts:Date.now(),proposalId:w,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why,origin:"agent"}}),{proposalId:w,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why}}async function BE(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 Rc(e,t,n={},{pull:r=Ic}={}){if(!e||Rw(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=Zd(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,AE).map(CE),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,y4).map(g=>({followupId:g.id,label:g.label,promptPreview:A4(g.prompt),promptHash:RE(g.prompt),origin:g.origin,repo:g.repo??null}))}let w=[...d,...f].slice(0,AE);if(!w.length)return null;let m=ks(),y={ts:Date.now(),offerId:m,trigger:t,repo:a,context:{...n,repo:a},groupId:c,why:p,rows:w};return await N(o,{type:"followup.offered",actor:"system",source:"orchestrator",payload:y}),y}function T4(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 _E(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 jE(e,{offerId:t,followupId:n=null,armId:r=null,promoted:o=null,all:s=!1}={},a={}){if(!t)throw new Error("offerId required");return bo(`fu:${e.id}:${t}`,()=>_4(e,{offerId:t,followupId:n,armId:r,promoted:o,all:s===!0},a))}async function _4(e,{offerId:t,followupId:n,armId:r,promoted:o,all:s},a){let{sayToLead:i,unarchiveTrack:c,getFollowup:l=xE}=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=T4(d,p.payload),m=E4(w,f);if(!m.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(G=>G.type==="followup.armed"&&G.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,_=m.filter(W=>W.armId&&(!A||W.groupId===A)).sort((W,X)=>(W.seq??0)-(X.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(W=>({row:W,prompt:g(W)})),G=await k();if(!i)throw new Error("sayToLead unavailable");let O=`${_E(p.payload).replace(/:$/,"")} \u2014 ${L.length} steps, in order:`,T=L.map((W,X)=>`${X+1}. ${W.row.label}
|
|
200
200
|
${W.prompt}`).join(`
|
|
201
201
|
|
|
202
202
|
`),C=await i(e.id,`${O}
|
|
203
203
|
|
|
204
|
-
${T}`,{kind:"followup-go"}),P=ks();for(let[W,X]of L.entries())await
|
|
204
|
+
${T}`,{kind:"followup-go"}),P=ks();for(let[W,X]of L.entries())await y({action:"sent",armId:X.row.armId,label:X.row.label,batchId:P,...W===0&&G?{revived:!0}:{},source:`arm:${X.row.armId}`});return{offerId:t,action:"sent",all:!0,batchId:P,armIds:L.map(W=>W.row.armId),revived:G,remaining:m.length-L.length,queued:!!C?.queued}}if(!n&&!r)return await y({action:"dismiss",followupIds:m.map(A=>A.followupId).filter(Boolean),armIds:m.map(A=>A.armId).filter(Boolean)}),{offerId:t,action:"dismiss",remaining:0};let x=null,S=null,$=n?`fu:${n}`:`arm:${r}`;if(n){if(!f.find(G=>G.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 G=new Error("removed in Settings since it was offered");throw G.code="removed",G}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 D=`${_E(p.payload)}
|
|
205
205
|
|
|
206
|
-
${x}`,j=await i(e.id,D,{kind:"followup-go"});return await
|
|
206
|
+
${x}`,j=await i(e.id,D,{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:m.length-1,queued:!!j?.queued}}async function I4(e,t,{changedRepos:n=null,probe:r=jd,branchOf:o=go}={}){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=>po.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=ks(),m=t.why?`${t.label} \u2014 ${t.why}`:t.label,y=Ow(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:m,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 Ow(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 LE(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 FE(e,{pull:t=Ic}={}){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?Zd(s):[]}}var _w,SE,IE,$E,g4,$w,y4,AE,TE,b4,Iw,RE,xs,k4,Rw,A4,E4,Qd=q(()=>{ye();ps();uw();ys();vE();$c();_w=60,SE=8*1024,IE=200,$E=["now","pr.merged","ship.done","pr.opened","checks.green","track.opened"],g4=$E.filter(e=>e!=="now"&&e!=="track.opened"),$w={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"},y4=3,AE=6,TE=5,b4=["offerable","never"],Iw=["commit","push","pr"];RE=e=>w4("sha256").update(String(e)).digest("hex").slice(0,16),xs=e=>typeof e=="string"?e.trim():"",k4=[[/(^|[^\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"]];Rw=e=>String(e?.name||"").startsWith("\u21BB");A4=e=>String(e||"").split(`
|
|
207
207
|
`).slice(0,3).join(`
|
|
208
|
-
`).slice(0,400);
|
|
209
|
-
`)){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 I4(e,t){let n=`${e.id}:${t.name}`;if(!Pw.has(n)){let r=await K(e),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===t.name&&s.payload?.codexTs!=null&&o.add(s.payload.codexTs);Pw.set(n,o)}return Pw.get(n)}async function KE(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await Jo(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await I4(e,t),o=_4(await Bt(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:qn(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 Pw,VE=q(()=>{ye();Xo();xn();mr();Pw=new Map});import tp from"node:fs";import Bw from"node:path";import $4 from"node:os";async function M4({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(O4,{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:zE})});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<zE)break}return r}function C4(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 YE(){try{return JSON.parse(tp.readFileSync(np(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function P4(e){try{tp.mkdirSync(Bw.dirname(np()),{recursive:!0});let t=np()+".tmp";tp.writeFileSync(t,JSON.stringify(e)),tp.renameSync(t,np())}catch{}}async function B4({fetchImpl:e,now:t=Date.now()}={}){if(ko.rows&&t-ko.at<N4)return ko.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=YE(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await M4({sinceMs:o,cookie:n,fetchImpl:e});ko={at:t,rows:s.map(C4).filter(Boolean),error:null}}catch(s){ko={at:t,rows:null,error:String(s?.message||s)}}return ko.rows}async function j4(e,t){let n=`${e.id}:${t.name}`;if(!Dw.has(n)){let r=await K(e),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===t.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);Dw.set(n,o)}return Dw.get(n)}async function XE(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!JE())return;let r=await B4({fetchImpl:n});if(!r||!r.length)return;let o=await tS(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await j4(e,t),a=YE(),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=qn(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}})}P4({sinceMs:c,seen:[...i].slice(-D4)})}catch{}}var R4,JE,O4,zE,N4,ko,np,D4,jw,Dw,Lw=q(()=>{ye();hh();mr();R4=process.env.BOSTRAT_STATE_DIR||Bw.join(process.env.HOME||$4.tmpdir(),".bostrat"),JE=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,O4="https://cursor.com/api/dashboard/get-filtered-usage-events",zE=100;N4=5*6e4,ko={at:0,rows:null,error:null},np=()=>Bw.join(R4,"cursor-usage.json");D4=5e3;jw=()=>({enabled:JE(),lastPollAt:ko.at||null,error:ko.error}),Dw=new Map});import{randomUUID as L4,createHmac as F4}from"node:crypto";function QE(e){Uw=typeof e=="string"&&e?e:null}function U4(e){if(!Uw)return null;let t=Ue(e);return t?F4("sha256",Buffer.from(Uw,"base64url")).update(t,"utf8").digest("hex").slice(0,16):null}function n_(){let e={};for(let t of t_)e[String(t)]=0;return e.inf=0,{buckets:e,sumMs:0,count:0}}function ZE(e,t){let n=Math.max(0,Number(t)||0);for(let r of t_)n<=r&&(e.buckets[String(r)]+=1);e.buckets.inf+=1,e.sumMs+=n,e.count+=1}function r_(){let e={};for(let t of e_)e[t]=0;return e}function W4(){Jn={total:new Map,holds:r_(),queueWaitMs:n_(),readyWaitMs:new Map,verify:new Map,retries:new Map}}function o_({queueDepth:e=0,queueOldestAgeMs:t=0}={}){let n=Jn;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 Cc({kind:e=null,producer:t=null,orchestration:n=!1}={}){return n?`orchestration:${e||"orchestration"}`:e?`scripted:${e}`:H4.has(t)?t:"composer"}function Nc({text:e="",producer:t="composer",kind:n=null,provider:r=null,attachments:o=0,now:s=Date.now}={}){let a=s();return{id:L4(),producer:t,kind:n||null,provider:r||null,textBytes:Buffer.byteLength(String(e||""),"utf8"),textHmac16:U4(e),attachments:Number(o)||0,paste:null,t0:a,queuedAt:null,dequeuedAt:null,holds:r_(),requeued:0,readyWaitMs:null,readyOutcome:null,verify:null,settled:!1,outcome:null}}async function Pc(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 s_(e,t){e_.includes(t)&&(e?.holds&&(e.holds[t]+=1),Jn.holds[t]+=1)}function Dc(e,{ms:t,outcome:n}){e&&(e.readyWaitMs=Math.max(0,Number(t)||0),e.readyOutcome=n||null)}function q4(e){let t=e?.boxes?.find?.(n=>n.name===e.leadBox);return typeof t?.cliVersion=="string"&&t.cliVersion?t.cliVersion:null}async function En(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(Fw(Jn.total,`${s}|${t.producer}|${n}`),a&&ZE(Jn.queueWaitMs,i),t.readyWaitMs!=null){let u=`${s}|${t.readyOutcome||"unknown"}`;Jn.readyWaitMs.has(u)||Jn.readyWaitMs.set(u,n_()),ZE(Jn.readyWaitMs.get(u),t.readyWaitMs)}let c=q4(e);t.verify?.verdict&&(Fw(Jn.verify,`${s}|${t.verify.verdict}|${c||""}`),t.verify.retries&&Fw(Jn.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 Da(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 Uw,e_,t_,H4,Jn,Fw,Hw=q(()=>{ye();oc();Uw=null;e_=["busy","prompt","handoff","noSession","cap","grace"],t_=[500,2e3,1e4,6e4,6e5],H4=new Set(["composer","voice","loop"]);Jn=null;W4();Fw=(e,t,n=1)=>e.set(t,(e.get(t)||0)+n)});import Lc from"node:fs/promises";import jt from"node:path";import G4 from"node:os";import K4 from"node:net";import{randomUUID as V4}from"node:crypto";import{execFile as z4}from"node:child_process";import{promisify as J4}from"node:util";function n2(e){for(let t of Jw(e))if(!d_.test(t))return t;return null}function p_(e){let t=Jw(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function r2(e,t){let n=p_(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return jt.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return jt.resolve(e,n[r+1])}return e}function s2(e){let t=!1,n=!1;for(let r of Jw(e)){if(!t){if(d_.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&Ww(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,Ww(r))return`serves the filesystem root ("${r}")`;continue}if(Ww(r))return`serves the filesystem root ("${r}")`;if(o2(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function i2(e,t){if(jt.resolve(e)===jt.resolve(t))return!0;let[n,r]=await Promise.all([rp(e),rp(t)]);return n===r}async function c2(e){let n=(await Lc.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 f_(jt.join(e,o.name,Kw))?`./${o.name}`:null))).filter(Boolean)}async function l2(e,t){let[n,r]=await Promise.all([rp(e),rp(t)]),o=jt.relative(n,r);if(o&&(o.startsWith("..")||jt.isAbsolute(o)))throw Bc(`serve directory escapes the Track workspace: ${t}`)}async function u2(e,t){let n=s2(t);if(n)throw Bc(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await a2(e))throw Bc(`serve directory does not exist: ${e}`);let r=n2(t);if(!r)return;if(!r.includes("/")&&!await K0(r,{loginShell:!0,failOpen:!0}))throw Bc(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(Q4.test(t)||e2.has(r)||!Z4.has(r)||!p_(t).includes("run")||t2.test(t))return;let o=r2(e,t);if(await f_(jt.join(o,Kw)))return;let s=await c2(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Bc(`no ${Kw} in ${o}${a}`)}async function _r(){return Yn||(qw||=(async()=>{try{Yn=JSON.parse(await Lc.readFile(u_,"utf8"))}catch{Yn={views:[],adoptWatermarks:{}}}finally{qw=null}return Yn.views||=[],Yn.adoptWatermarks||={},Yn})(),qw)}async function Zn(){await Pt(u_,Yn)}function Er(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 Xw(e){return jc.get(e)||null}function h_(e,{name:t,box:n}={}){let r=jc.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 jc.set(e,o),o}function Vw(e,t){let n=jc.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function op(e,{error:t=null}={}){let n=jc.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function w_(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 zw(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function d2(e,t){let n=zw(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!c_.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!c_.has(s))return s}}function Fc(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=K4.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 Zw(e){if(process.platform!=="darwin"){await Xn("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await Xn("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 g_(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function p2(e,t){let n=new Set(e.views.map(o=>o.port)),r=g_();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=a_;o<=i_;o++)if(!(n.has(o)||r.has(o))&&!await Fc(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${a_}-${i_}`)}async function f2(e){try{let{stdout:t}=await Xn(ne,se("list-sessions","-F","#{session_name}"));return t.split(`
|
|
210
|
-
`).some(n=>n.trim()===Ba(e))}catch{return!1}}async function Uc(e){try{await Xn(ne,se("kill-session","-t",Ba(e)))}catch{}}async function
|
|
208
|
+
`).slice(0,400);E4=(e,t)=>t.filter(n=>e.get(n.armId?`arm:${n.armId}`:`fu:${n.followupId}`)==null)});import{randomUUID as $4}from"node:crypto";function Oc(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function UE(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function HE(e){return`job|${e.box}|id:${e.id}`}function WE(e){return`jobstop|${e.box}|id:${e.id}`}async function Cw(e){return Mw.has(e.id)?Mw.get(e.id):(ep.has(e.id)||ep.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(Oc({role:o,sourceId:r.payload.sourceId})),n.keys.add(Oc({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(Oc({role:"user",sourceId:r.payload.sourceId})),n.keys.add(Oc({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(UE({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(HE({id:r.payload.id,box:r.boxId})):r.type==="job.stopped"&&r.payload?.id&&n.keys.add(WE({id:r.payload.id,box:r.boxId}));return Mw.set(e.id,n),n}).finally(()=>ep.delete(e.id))),ep.get(e.id))}async function qE(e,t){let n=new Set;try{let r=(await Cw(e)).keys;for(let o of t||[]){if(!o||o.ts==null||!String(o.text||"").trim()&&!o.images?.length)continue;let s=Oc(o);if(r.has(s))continue;r.add(s);let a=o.role==="user"?Yd(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 yE(a,Mc),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=xx(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}}),mc(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=jA(o.text);if(c&&Ow(await K(e).catch(()=>[])))continue;if(c){let l=UA(c),u;if(e.workspace?.kind==="multi"){let w=await vs(e).catch(()=>[]);u=w.length?w.map(m=>({name:m.name,slug:m.repo??null})):void 0}let d=(e.workspace?.kind==="multi"?null:await go(e.cwd).catch(()=>null))||e.workspace.branch,p=await jd(e,{branch:d,rebase:l}).catch(()=>null),f=p?null:FA(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:$4(),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 GE(e,t){try{let n=await Cw(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=UE(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}}),hE(e,o),wE(e,o)}}catch{}}async function KE(e,t){try{let n=await Cw(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=HE(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=WE(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 Mw,ep,VE=q(()=>{ye();ps();uw();Jd();ys();Aw();Tr();Cu();dd();Qd();Mw=new Map,ep=new Map});function R4(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
209
|
+
`)){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 O4(e,t){let n=`${e.id}:${t.name}`;if(!Nw.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);Nw.set(n,o)}return Nw.get(n)}async function zE(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await Jo(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await O4(e,t),o=R4(await Bt(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:qn(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 Nw,JE=q(()=>{ye();Xo();xn();hr();Nw=new Map});import tp from"node:fs";import Dw from"node:path";import M4 from"node:os";async function P4({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(N4,{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:YE})});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<YE)break}return r}function D4(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 ZE(){try{return JSON.parse(tp.readFileSync(np(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function j4(e){try{tp.mkdirSync(Dw.dirname(np()),{recursive:!0});let t=np()+".tmp";tp.writeFileSync(t,JSON.stringify(e)),tp.renameSync(t,np())}catch{}}async function F4({fetchImpl:e,now:t=Date.now()}={}){if(ko.rows&&t-ko.at<B4)return ko.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=ZE(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await P4({sinceMs:o,cookie:n,fetchImpl:e});ko={at:t,rows:s.map(D4).filter(Boolean),error:null}}catch(s){ko={at:t,rows:null,error:String(s?.message||s)}}return ko.rows}async function U4(e,t){let n=`${e.id}:${t.name}`;if(!Pw.has(n)){let r=await K(e),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===t.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);Pw.set(n,o)}return Pw.get(n)}async function QE(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!XE())return;let r=await F4({fetchImpl:n});if(!r||!r.length)return;let o=await rS(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await U4(e,t),a=ZE(),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=qn(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}})}j4({sinceMs:c,seen:[...i].slice(-L4)})}catch{}}var C4,XE,N4,YE,B4,ko,np,L4,Bw,Pw,jw=q(()=>{ye();mh();hr();C4=process.env.BOSTRAT_STATE_DIR||Dw.join(process.env.HOME||M4.tmpdir(),".bostrat"),XE=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,N4="https://cursor.com/api/dashboard/get-filtered-usage-events",YE=100;B4=5*6e4,ko={at:0,rows:null,error:null},np=()=>Dw.join(C4,"cursor-usage.json");L4=5e3;Bw=()=>({enabled:XE(),lastPollAt:ko.at||null,error:ko.error}),Pw=new Map});import{randomUUID as H4,createHmac as W4}from"node:crypto";function t_(e){Fw=typeof e=="string"&&e?e:null}function q4(e){if(!Fw)return null;let t=Ue(e);return t?W4("sha256",Buffer.from(Fw,"base64url")).update(t,"utf8").digest("hex").slice(0,16):null}function o_(){let e={};for(let t of r_)e[String(t)]=0;return e.inf=0,{buckets:e,sumMs:0,count:0}}function e_(e,t){let n=Math.max(0,Number(t)||0);for(let r of r_)n<=r&&(e.buckets[String(r)]+=1);e.buckets.inf+=1,e.sumMs+=n,e.count+=1}function s_(){let e={};for(let t of n_)e[t]=0;return e}function K4(){Jn={total:new Map,holds:s_(),queueWaitMs:o_(),readyWaitMs:new Map,verify:new Map,retries:new Map}}function a_({queueDepth:e=0,queueOldestAgeMs:t=0}={}){let n=Jn;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 Cc({kind:e=null,producer:t=null,orchestration:n=!1}={}){return n?`orchestration:${e||"orchestration"}`:e?`scripted:${e}`:G4.has(t)?t:"composer"}function Nc({text:e="",producer:t="composer",kind:n=null,provider:r=null,attachments:o=0,now:s=Date.now}={}){let a=s();return{id:H4(),producer:t,kind:n||null,provider:r||null,textBytes:Buffer.byteLength(String(e||""),"utf8"),textHmac16:q4(e),attachments:Number(o)||0,paste:null,t0:a,queuedAt:null,dequeuedAt:null,holds:s_(),requeued:0,readyWaitMs:null,readyOutcome:null,verify:null,settled:!1,outcome:null}}async function Pc(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 i_(e,t){n_.includes(t)&&(e?.holds&&(e.holds[t]+=1),Jn.holds[t]+=1)}function Dc(e,{ms:t,outcome:n}){e&&(e.readyWaitMs=Math.max(0,Number(t)||0),e.readyOutcome=n||null)}function V4(e){let t=e?.boxes?.find?.(n=>n.name===e.leadBox);return typeof t?.cliVersion=="string"&&t.cliVersion?t.cliVersion:null}async function _n(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(Lw(Jn.total,`${s}|${t.producer}|${n}`),a&&e_(Jn.queueWaitMs,i),t.readyWaitMs!=null){let u=`${s}|${t.readyOutcome||"unknown"}`;Jn.readyWaitMs.has(u)||Jn.readyWaitMs.set(u,o_()),e_(Jn.readyWaitMs.get(u),t.readyWaitMs)}let c=V4(e);t.verify?.verdict&&(Lw(Jn.verify,`${s}|${t.verify.verdict}|${c||""}`),t.verify.retries&&Lw(Jn.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 Da(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 Fw,n_,r_,G4,Jn,Lw,Uw=q(()=>{ye();oc();Fw=null;n_=["busy","prompt","handoff","noSession","cap","grace"],r_=[500,2e3,1e4,6e4,6e5],G4=new Set(["composer","voice","loop"]);Jn=null;K4();Lw=(e,t,n=1)=>e.set(t,(e.get(t)||0)+n)});import Lc from"node:fs/promises";import jt from"node:path";import z4 from"node:os";import J4 from"node:net";import{randomUUID as Y4}from"node:crypto";import{execFile as X4}from"node:child_process";import{promisify as Z4}from"node:util";function s2(e){for(let t of zw(e))if(!f_.test(t))return t;return null}function m_(e){let t=zw(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function a2(e,t){let n=m_(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return jt.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return jt.resolve(e,n[r+1])}return e}function c2(e){let t=!1,n=!1;for(let r of zw(e)){if(!t){if(f_.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&Hw(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,Hw(r))return`serves the filesystem root ("${r}")`;continue}if(Hw(r))return`serves the filesystem root ("${r}")`;if(i2(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function u2(e,t){if(jt.resolve(e)===jt.resolve(t))return!0;let[n,r]=await Promise.all([rp(e),rp(t)]);return n===r}async function d2(e){let n=(await Lc.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 h_(jt.join(e,o.name,Gw))?`./${o.name}`:null))).filter(Boolean)}async function p2(e,t){let[n,r]=await Promise.all([rp(e),rp(t)]),o=jt.relative(n,r);if(o&&(o.startsWith("..")||jt.isAbsolute(o)))throw Bc(`serve directory escapes the Track workspace: ${t}`)}async function f2(e,t){let n=c2(t);if(n)throw Bc(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await l2(e))throw Bc(`serve directory does not exist: ${e}`);let r=s2(t);if(!r)return;if(!r.includes("/")&&!await V0(r,{loginShell:!0,failOpen:!0}))throw Bc(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(n2.test(t)||r2.has(r)||!t2.has(r)||!m_(t).includes("run")||o2.test(t))return;let o=a2(e,t);if(await h_(jt.join(o,Gw)))return;let s=await d2(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Bc(`no ${Gw} in ${o}${a}`)}async function Ir(){return Yn||(Ww||=(async()=>{try{Yn=JSON.parse(await Lc.readFile(p_,"utf8"))}catch{Yn={views:[],adoptWatermarks:{}}}finally{Ww=null}return Yn.views||=[],Yn.adoptWatermarks||={},Yn})(),Ww)}async function Zn(){await Pt(p_,Yn)}function _r(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 Yw(e){return jc.get(e)||null}function g_(e,{name:t,box:n}={}){let r=jc.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 jc.set(e,o),o}function Kw(e,t){let n=jc.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function op(e,{error:t=null}={}){let n=jc.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function y_(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 Vw(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function m2(e,t){let n=Vw(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!u_.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!u_.has(s))return s}}function Fc(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=J4.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 Xw(e){if(process.platform!=="darwin"){await Xn("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await Xn("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 b_(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function h2(e,t){let n=new Set(e.views.map(o=>o.port)),r=b_();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=c_;o<=l_;o++)if(!(n.has(o)||r.has(o))&&!await Fc(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${c_}-${l_}`)}async function w2(e){try{let{stdout:t}=await Xn(ne,se("list-sessions","-F","#{session_name}"));return t.split(`
|
|
210
|
+
`).some(n=>n.trim()===Ba(e))}catch{return!1}}async function Uc(e){try{await Xn(ne,se("kill-session","-t",Ba(e)))}catch{}}async function Zw(e){if(!await qi())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=Ba(e.slug),n=e.serveCwd??e.cwd,r=jt.dirname(process.execPath);await Xn(ne,se("new-session","-d","-s",t,"-c",n)),await Xn(ne,se("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await Xn(ne,se("send-keys","-t",t,"Enter")),await Xn(ne,se("send-keys","-t",t,"-l",e.cmd)),await Xn(ne,se("send-keys","-t",t,"Enter"))}async function d_(e,t=12){try{let{stdout:n}=await Xn(ne,se("capture-pane","-t",Ba(e),"-p"));return String(n||"").trimEnd().split(`
|
|
211
211
|
`).filter(Boolean).slice(-t).join(`
|
|
212
|
-
`)}catch{return""}}async function h2(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await f2(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await l_(e.slug)||"server exited before listening"),Tr.delete(e.id),await Er(e,e.state,e.error?{error:e.error}:{}),!0}if(await Fc(e.port))return Tr.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await Er(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>m2?(e.state="error",e.error=await l_(e.slug)||"never started listening",await Er(e,"error",{error:e.error}),!0):!1;let o=(Tr.get(e.id)||0)+1;return Tr.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function sp(){if(!Gw){Gw=!0;try{let e=await _r(),t=!1;for(let n of e.views)await h2(n)&&(t=!0);t&&await Zn()}finally{Gw=!1}}}async function eg({trackId:e}={}){await sp();let t=await _r();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function y_(e){let n=(await _r()).views.find(r=>r.id===e);return n?{...n}:null}function tg(e){return Yn&&Yn.views.find(t=>t.slug===e)||null}async function ng({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=jt.resolve(r,o??"."),d=i==="detected"?null:async()=>{await l2(r,u),await u2(u,n)},p=await _r();if(i!=="detected"){let m=zw(t),g=String(n).trim(),b=p.views.find(y=>y.trackId===e&&(y.slug===m||zw(y.name)===m||y.cmd===g&&jt.resolve(y.serveCwd??y.cwd)===u));if(b)return w2(p,b,{cmd:g,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),p.views.filter(m=>m.state==="live"||m.state==="starting").length>=xo)throw new Error(`view cap reached (${xo} live) \u2014 stop one first`);let w={id:V4().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:d2(p,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await p2(p,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Qw(w),p.views.push(w),await Zn(),await Er(w,"created",{origin:i}),Vw(e,w.id),i==="detected"&&await Fc(w.port)&&(w.state="live",w.lastHealthyAt=Date.now(),await Zn(),await Er(w,"live")),{...w}}async function w2(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 i2(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Fc(t.port))return await Zn(),Vw(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>=xo)throw new Error(`view cap reached (${xo} live) \u2014 stop one first`);return t.origin==="detected"?(await Zw(t.port),t.origin="agent"):await Uc(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),Tr.delete(t.id),await Qw(t),await Zn(),await Er(t,"created",{restarted:!0,reused:!0}),Vw(t.trackId,t.id),{...t,reused:!0}}async function b_(e){let n=(await _r()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await Zw(n.port):await Uc(n.slug),n.state!=="stopped"&&(n.state="stopped",Tr.delete(n.id),await Zn(),await Er(n,"stopped")),{...n}}async function k_(e){let t=await _r(),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>=xo)throw new Error(`view cap reached (${xo} live) \u2014 stop one first`);return await Uc(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Tr.delete(n.id),await Qw(n),await Zn(),await Er(n,"created",{restarted:!0}),{...n}}async function x_(e){let t=await _r(),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 Zw(r.port):await Uc(r.slug),t.views.splice(n,1),Tr.delete(r.id),await Zn(),await Er(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function rg(e){let t=await _r(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Uc(r.slug),Tr.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await Zn()),n.length}async function v_(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await _r(),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 Zn()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(g2)){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(!g_().has(c)&&!n.views.some(l=>l.port===c)&&await Fc(c))try{i.push(await ng({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 Xn,Y4,u_,a_,i_,xo,X4,Ba,c_,Bc,Z4,Kw,Q4,e2,t2,Jw,d_,Ww,o2,a2,f_,rp,Yn,qw,jc,Yw,m_,Tr,m2,Gw,g2,ap=q(()=>{dt();ye();Gr();Xn=J4(z4),Y4=process.env.BOSTRAT_STATE_DIR||jt.join(process.env.HOME||G4.tmpdir(),".bostrat"),u_=jt.join(Y4,"views.json"),[a_,i_]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),xo=Number(process.env.BOSTRAT_VIEW_MAX)||3,X4="bostrat-view-",Ba=e=>`${X4}${e}`,c_=new Set(["bostrat","www","api","views","app"]),Bc=e=>Object.assign(new Error(e),{status:400}),Z4=new Set(["npm","pnpm","yarn","bun"]),Kw="package.json",Q4=/[|;&`]|\$\(/,e2=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),t2=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Jw=e=>String(e).trim().split(/\s+/).filter(Boolean),d_=/^[A-Za-z_][A-Za-z0-9_]*=/;Ww=e=>e==="/",o2=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");a2=e=>Lc.stat(e).then(t=>t.isDirectory(),()=>!1),f_=e=>Lc.stat(e).then(t=>t.isFile(),()=>!1),rp=e=>Lc.realpath(e).catch(()=>jt.resolve(e));Yn=null,qw=null;jc=new Map,Yw=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,m_=12e3;Tr=new Map,m2=12e4;Gw=!1;g2=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});import{execFile as y2}from"node:child_process";import{promisify as b2}from"node:util";function v2(e){return A_(e?.toPr?.number??e?.openPr?.number)}function A_(e){if(e==null||e==="")return null;let t=Number(e);return Number.isFinite(t)&&t>0?t:null}function S2(e){return Array.isArray(e?.actions)?e.actions.includes("push")||e.actions.includes("pr"):e?.action==="push"||e?.action==="pr"}function ja(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=v2(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&&S2(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:A_(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>og?null:n}function A2(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function cp(e,{bypassThrottle:t=!1}={}){let n=e?.workspace?.branch;if(!n)return;let r;try{r=await K(e)}catch{return}let o=ja(r);if(o==null)return;let s=(e.boxes||[]).find(d=>d.name===e.leadBox);if(await ns(e.leadBox,s?.agent).catch(()=>!1))return;let a=Date.now();if(!t&&a<(Hc.get(e.id)||0))return;Hc.set(e.id,a+S_);let i;try{i=await Vn(e.cwd,{caller:"reconcile"})}catch{return}let c=await Ca(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 Oe(e.cwd).catch(()=>null);if(u){let{link:d,created:p}=await Gt({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 gr(u,l.number))){if(!A2(d,o))return;let f=await T2(e);if(f&&l.headSha&&f!==l.headSha){let m=Math.min(x2,(ip.get(e.id)||S_)*2);ip.set(e.id,m),Hc.set(e.id,a+m);return}let w=await K(e).catch(()=>null);if(w&&ja(w)==null)return;await N(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:E2(o,l,{branch:n,repo:u})}),Hc.delete(e.id),ip.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(()=>{}),Hc.delete(e.id),ip.delete(e.id)}async function T2(e){try{let{stdout:t}=await k2("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 E2(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 k2,S_,Hc,x2,ip,og,Wc=q(()=>{ye();gs();Hr();Kn();wa();ys();k2=b2(y2),S_=6e3,Hc=new Map,x2=5*6e4,ip=new Map,og=30*6e4});import sg from"node:os";import{execFile as _2}from"node:child_process";import{promisify as I2}from"node:util";async function lp(){if(process.platform!=="darwin")return sg.freemem();try{let{stdout:e}=await $2("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:sg.freemem()}catch{return sg.freemem()}}var $2,ag=q(()=>{$2=I2(_2)});import tt from"node:fs/promises";import rt from"node:path";import I_ from"node:os";import{randomUUID as R2}from"node:crypto";import{execFile as O2}from"node:child_process";import{promisify as M2}from"node:util";import{chromium as C2}from"playwright-core";async function tr(){return Ss||(ig||=(async()=>{try{Ss=JSON.parse(await tt.readFile(R_,"utf8"))}catch{Ss={mocks:[]}}finally{ig=null}return Ss.mocks||=[],Ss})(),ig)}async function cn(){await Pt(R_,Ss)}function N_(e,t){er(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&er(e,t.v,"png")}function It(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 ln(e,t){let n=Gc(e.cwd);return{html:rt.join(n,`${e.id}.v${t}.html`),png:rt.join(n,`${e.id}.v${t}.png`)}}function qc(e,t,n){if(!t)return null;let r=n==="html"?t.htmlRel:n==="png"?t.pngRel??t.posterRel??null:n==="video"?t.videoRel??null:null;return r?rt.join(e.cwd,r):null}function _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 ug(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function up(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(F2)}function U2(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 H2(e){let t=Buffer.alloc(24),n=await tt.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function W2({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 q2(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((w,m)=>{r.onload=w,r.onerror=()=>m(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,d=s,p=-1,f=-1;for(let w=0;w<s;w++)for(let m=w*o*4,g=0;g<o;g++,m+=4)Math.abs(c[m]-l[0])+Math.abs(c[m+1]-l[1])+Math.abs(c[m+2]-l[2])>24&&(g<u&&(u=g),g>p&&(p=g),w<d&&(d=w),f=w);return p<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:p,bottom:f}}},t.toString("base64"))}async function G2(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 K2({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await C2.launch({executablePath:O_,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Kt("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:La})).newPage();a.setDefaultTimeout(Fa),await a.goto(`file://${e}`,{waitUntil:"load",timeout:Fa}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Fa});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await q2(a,i),u=l&&W2({...l,pad:D2*La,align:La});u&&(i=await G2(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<M_)throw new Kt(`capture produced a ${i.length}-byte image`);let c=ug(i);if(!c)throw new Kt("output is not a PNG");return await tt.writeFile(t,i),{width:Math.round(c.width/La),height:Math.round(c.height/La),dpr:La,renderMs:Date.now()-r}}catch(s){throw s instanceof Kt?s:new Kt(/timeout/i.test(s?.message||"")?`render timed out after ${Fa}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function __({htmlPath:e,outPath:t,width:n}){let r=await tt.mkdtemp(rt.join(I_.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await lg(T_,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Fa,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new Kt(`firefox not found (${T_})`,""):new Kt(c?.killed?`render timed out after ${Fa}ms`:"firefox exited non-zero",s)}let a=await tt.stat(t).catch(()=>null);if(!a||a.size<M_)throw new Kt(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=ug(await H2(t));if(!i)throw new Kt("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await tt.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function V2(e){if(N2==="firefox")return __(e);try{return await K2(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await __(e)}catch(n){throw/not found/.test(n?.message||"")?new Kt(`no browser installed \u2014 chromium missing at ${O_} 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 P_(e){return cg=cg.then(e,e),cg}async function D_(e,t){let n=await tr(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await cn(),await It(r,"rendering",{version:t});try{let o=await lp();if(o<B2)throw new Kt(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=ln(r,t),a=await z2({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await tt.rm(s.png,{force:!0}).catch(()=>{});return}dg(r,{v:t,source:"html",htmlRel:rt.relative(r.cwd,s.html),pngRel:rt.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await cn(),await It(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),Qn(r),er(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await cn(),await It(r,"error",{version:t,error:r.error}),Qn(r)}}}function dg(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 J2({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?P2:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Kc({trackId:e,group:t}={}){let n=await tr(),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 Ir(e){let n=(await tr()).mocks.find(r=>r.id===e);return n?{...n}:null}function B_({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&&!up(t))throw new Error("image must be a PNG");if(n){let o=U2(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>E_)throw new Error(`video must be ${E_/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function j_({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=J2({trackId:e,name:t,cwd:s,width:a}),p=typeof n=="string"&&n.trim(),f=B_({html:n,image:r,video:o}),w=await tr(),m={id:R2().slice(0,8),trackId:e,box:c||null,agent:l||null,name:t.trim(),prompt:u?String(u).slice(0,500):null,group:i?String(i).trim():null,cwd:s,width:d,height:null,dpr:null,state:"queued",source:p?"html":o?"video":"image",kind:o?"video":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await tt.mkdir(Gc(s),{recursive:!0}),w.mocks.push(m),p?(await tt.writeFile(ln(m,1).html,n),await cn(),await It(m,"created"),Qn(m),er(m,1,"html"),P_(()=>D_(m.id,1))):o?(await F_(m,1,o,f),await cn(),await It(m,"created"),await It(m,"ready",{version:1,width:m.width,height:m.height,dpr:m.dpr,durationS:m.versions[0]?.durationS??null}),Qn(m),N_(m,m.versions[0])):(await U_(m,1,r),await cn(),await It(m,"created"),await It(m,"ready",{version:1,width:m.width,height:m.height,dpr:m.dpr}),Qn(m),er(m,1,"png")),{...m}}async function L_(e,{html:t,image:n,video:r,width:o}={}){let a=(await tr()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=B_({html:t,image:n,video:r});if(o!=null){let u=Number(o);if(!Number.isInteger(u)||u<320||u>4e3)throw new Error("invalid width (320-4000)");a.width=u}let l=a.version+1;return a.version=l,a.updatedAt=Date.now(),a.source=i?"html":r?"video":"image",a.kind=r?"video":"image",await tt.mkdir(Gc(a.cwd),{recursive:!0}),i?(a.state="queued",await tt.writeFile(ln(a,l).html,t),await cn(),await It(a,"updated",{version:l}),Qn(a),er(a,l,"html"),P_(()=>D_(a.id,l))):r?(await F_(a,l,r,c),await cn(),await It(a,"updated",{version:l}),await It(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:_n(a)?.durationS??null}),Qn(a),N_(a,a.versions.find(u=>u.v===l))):(await U_(a,l,n),await cn(),await It(a,"updated",{version:l}),await It(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Qn(a),er(a,l,"png")),{...a}}async function Y2(e){try{let{stdout:t}=await lg(j2,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:C_,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 X2(e,t){try{await lg(L2,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:C_,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await tt.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function F_(e,t,n,r){let o=Gc(e.cwd),s=rt.join(o,`${e.id}.v${t}.${r.ext}`),a=rt.join(o,`${e.id}.v${t}.poster.png`);await tt.writeFile(s,n);let i=await Y2(s),c=await X2(s,a);dg(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:rt.relative(e.cwd,s),posterRel:c?rt.relative(e.cwd,a):null,mimeType:r.mime,bytes:n.length,durationS:i?.durationS??null,width:i?.width??null,height:i?.height??null,dpr:1,renderMs:null,createdAt:Date.now()}),i?.width&&(e.width=i.width)}async function U_(e,t,n){let r=ug(n);if(!r)throw new Error("image must be a PNG");let o=ln(e,t);await tt.writeFile(o.png,n),dg(e,{v:t,source:"image",htmlRel:null,pngRel:rt.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function H_(e){let t=await tr(),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 It(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function W_(e){let t=await tr(),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=ln(r,o);await tt.rm(s.html,{force:!0}).catch(()=>{}),await tt.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await tt.rm(rt.join(r.cwd,s),{force:!0}).catch(()=>{});return await cn(),await It(r,"deleted"),wn("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function q_(e){let n=(await tr()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=rt.join($_,"mock-archive",e.id),o=Gc(e.cwd),s=rt.join(r,".bostrat","mocks");await tt.mkdir(s,{recursive:!0});let a=await tt.readdir(o).catch(()=>[]);for(let i of a)await tt.copyFile(rt.join(o,i),rt.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await tt.stat(rt.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=e.name||e.id,i.updatedAt=Date.now(),Qn(i)}return await cn(),n.length}async function G_(e){let t=await tr(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await cn();for(let r of n)wn("mocks",r.id).catch(()=>{})}return n.length}async function K_(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await ut("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await tr();for(let r of n.mocks){await Qn(r);for(let o of r.versions||[])o.htmlRel&&await er(r,o.v,"html"),(o.pngRel||o.posterRel)&&await er(r,o.v,"png"),o.videoRel&&await er(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var lg,$_,R_,N2,O_,T_,P2,La,D2,M_,Fa,B2,E_,j2,L2,C_,Kt,Ss,ig,Qn,er,Gc,F2,z2,cg,Ua=q(()=>{ye();Gr();ag();Vr();lg=M2(O2),$_=process.env.BOSTRAT_STATE_DIR||rt.join(process.env.HOME||I_.tmpdir(),".bostrat"),R_=rt.join($_,"mocks.json"),N2=process.env.BOSTRAT_MOCK_ENGINE||"chromium",O_=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",T_=process.env.BOSTRAT_FIREFOX_BIN||"firefox",P2=1300,La=2,D2=32,M_=1500,Fa=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,B2=700*1024*1024,E_=100*1024*1024,j2=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",L2=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",C_=2e4,Kt=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
212
|
+
`)}catch{return""}}async function y2(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await w2(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await d_(e.slug)||"server exited before listening"),Er.delete(e.id),await _r(e,e.state,e.error?{error:e.error}:{}),!0}if(await Fc(e.port))return Er.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await _r(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>g2?(e.state="error",e.error=await d_(e.slug)||"never started listening",await _r(e,"error",{error:e.error}),!0):!1;let o=(Er.get(e.id)||0)+1;return Er.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function sp(){if(!qw){qw=!0;try{let e=await Ir(),t=!1;for(let n of e.views)await y2(n)&&(t=!0);t&&await Zn()}finally{qw=!1}}}async function Qw({trackId:e}={}){await sp();let t=await Ir();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function k_(e){let n=(await Ir()).views.find(r=>r.id===e);return n?{...n}:null}function eg(e){return Yn&&Yn.views.find(t=>t.slug===e)||null}async function tg({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=jt.resolve(r,o??"."),d=i==="detected"?null:async()=>{await p2(r,u),await f2(u,n)},p=await Ir();if(i!=="detected"){let m=Vw(t),y=String(n).trim(),k=p.views.find(g=>g.trackId===e&&(g.slug===m||Vw(g.name)===m||g.cmd===y&&jt.resolve(g.serveCwd??g.cwd)===u));if(k)return b2(p,k,{cmd:y,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),p.views.filter(m=>m.state==="live"||m.state==="starting").length>=xo)throw new Error(`view cap reached (${xo} live) \u2014 stop one first`);let w={id:Y4().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:m2(p,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await h2(p,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Zw(w),p.views.push(w),await Zn(),await _r(w,"created",{origin:i}),Kw(e,w.id),i==="detected"&&await Fc(w.port)&&(w.state="live",w.lastHealthyAt=Date.now(),await Zn(),await _r(w,"live")),{...w}}async function b2(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 u2(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Fc(t.port))return await Zn(),Kw(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>=xo)throw new Error(`view cap reached (${xo} live) \u2014 stop one first`);return t.origin==="detected"?(await Xw(t.port),t.origin="agent"):await Uc(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),Er.delete(t.id),await Zw(t),await Zn(),await _r(t,"created",{restarted:!0,reused:!0}),Kw(t.trackId,t.id),{...t,reused:!0}}async function x_(e){let n=(await Ir()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await Xw(n.port):await Uc(n.slug),n.state!=="stopped"&&(n.state="stopped",Er.delete(n.id),await Zn(),await _r(n,"stopped")),{...n}}async function v_(e){let t=await Ir(),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>=xo)throw new Error(`view cap reached (${xo} live) \u2014 stop one first`);return await Uc(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Er.delete(n.id),await Zw(n),await Zn(),await _r(n,"created",{restarted:!0}),{...n}}async function S_(e){let t=await Ir(),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 Xw(r.port):await Uc(r.slug),t.views.splice(n,1),Er.delete(r.id),await Zn(),await _r(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function ng(e){let t=await Ir(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Uc(r.slug),Er.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await Zn()),n.length}async function A_(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await Ir(),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 Zn()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(k2)){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(!b_().has(c)&&!n.views.some(l=>l.port===c)&&await Fc(c))try{i.push(await tg({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 Xn,Q4,p_,c_,l_,xo,e2,Ba,u_,Bc,t2,Gw,n2,r2,o2,zw,f_,Hw,i2,l2,h_,rp,Yn,Ww,jc,Jw,w_,Er,g2,qw,k2,ap=q(()=>{dt();ye();Gr();Xn=Z4(X4),Q4=process.env.BOSTRAT_STATE_DIR||jt.join(process.env.HOME||z4.tmpdir(),".bostrat"),p_=jt.join(Q4,"views.json"),[c_,l_]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),xo=Number(process.env.BOSTRAT_VIEW_MAX)||3,e2="bostrat-view-",Ba=e=>`${e2}${e}`,u_=new Set(["bostrat","www","api","views","app"]),Bc=e=>Object.assign(new Error(e),{status:400}),t2=new Set(["npm","pnpm","yarn","bun"]),Gw="package.json",n2=/[|;&`]|\$\(/,r2=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),o2=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,zw=e=>String(e).trim().split(/\s+/).filter(Boolean),f_=/^[A-Za-z_][A-Za-z0-9_]*=/;Hw=e=>e==="/",i2=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");l2=e=>Lc.stat(e).then(t=>t.isDirectory(),()=>!1),h_=e=>Lc.stat(e).then(t=>t.isFile(),()=>!1),rp=e=>Lc.realpath(e).catch(()=>jt.resolve(e));Yn=null,Ww=null;jc=new Map,Jw=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,w_=12e3;Er=new Map,g2=12e4;qw=!1;k2=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});import{execFile as x2}from"node:child_process";import{promisify as v2}from"node:util";function T2(e){return E_(e?.toPr?.number??e?.openPr?.number)}function E_(e){if(e==null||e==="")return null;let t=Number(e);return Number.isFinite(t)&&t>0?t:null}function E2(e){return Array.isArray(e?.actions)?e.actions.includes("push")||e.actions.includes("pr"):e?.action==="push"||e?.action==="pr"}function ja(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=T2(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&&E2(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:E_(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>rg?null:n}function _2(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function cp(e,{bypassThrottle:t=!1}={}){let n=e?.workspace?.branch;if(!n)return;let r;try{r=await K(e)}catch{return}let o=ja(r);if(o==null)return;let s=(e.boxes||[]).find(d=>d.name===e.leadBox);if(await ns(e.leadBox,s?.agent).catch(()=>!1))return;let a=Date.now();if(!t&&a<(Hc.get(e.id)||0))return;Hc.set(e.id,a+T_);let i;try{i=await Vn(e.cwd,{caller:"reconcile"})}catch{return}let c=await Ca(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 Oe(e.cwd).catch(()=>null);if(u){let{link:d,created:p}=await Kt({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 yr(u,l.number))){if(!_2(d,o))return;let f=await I2(e);if(f&&l.headSha&&f!==l.headSha){let m=Math.min(A2,(ip.get(e.id)||T_)*2);ip.set(e.id,m),Hc.set(e.id,a+m);return}let w=await K(e).catch(()=>null);if(w&&ja(w)==null)return;await N(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:$2(o,l,{branch:n,repo:u})}),Hc.delete(e.id),ip.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(()=>{}),Hc.delete(e.id),ip.delete(e.id)}async function I2(e){try{let{stdout:t}=await S2("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 $2(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 S2,T_,Hc,A2,ip,rg,Wc=q(()=>{ye();gs();Hr();Kn();wa();ys();S2=v2(x2),T_=6e3,Hc=new Map,A2=5*6e4,ip=new Map,rg=30*6e4});import og from"node:os";import{execFile as R2}from"node:child_process";import{promisify as O2}from"node:util";async function lp(){if(process.platform!=="darwin")return og.freemem();try{let{stdout:e}=await M2("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:og.freemem()}catch{return og.freemem()}}var M2,sg=q(()=>{M2=O2(R2)});import tt from"node:fs/promises";import rt from"node:path";import R_ from"node:os";import{randomUUID as C2}from"node:crypto";import{execFile as N2}from"node:child_process";import{promisify as P2}from"node:util";import{chromium as D2}from"playwright-core";async function tr(){return Ss||(ag||=(async()=>{try{Ss=JSON.parse(await tt.readFile(M_,"utf8"))}catch{Ss={mocks:[]}}finally{ag=null}return Ss.mocks||=[],Ss})(),ag)}async function ln(){await Pt(M_,Ss)}function D_(e,t){er(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&er(e,t.v,"png")}function It(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 un(e,t){let n=Gc(e.cwd);return{html:rt.join(n,`${e.id}.v${t}.html`),png:rt.join(n,`${e.id}.v${t}.png`)}}function qc(e,t,n){if(!t)return null;let r=n==="html"?t.htmlRel:n==="png"?t.pngRel??t.posterRel??null:n==="video"?t.videoRel??null:null;return r?rt.join(e.cwd,r):null}function In(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 lg(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function up(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(W2)}function q2(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 G2(e){let t=Buffer.alloc(24),n=await tt.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function K2({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 V2(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((w,m)=>{r.onload=w,r.onerror=()=>m(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,d=s,p=-1,f=-1;for(let w=0;w<s;w++)for(let m=w*o*4,y=0;y<o;y++,m+=4)Math.abs(c[m]-l[0])+Math.abs(c[m+1]-l[1])+Math.abs(c[m+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 z2(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 J2({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await D2.launch({executablePath:C_,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Vt("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:La})).newPage();a.setDefaultTimeout(Fa),await a.goto(`file://${e}`,{waitUntil:"load",timeout:Fa}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Fa});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await V2(a,i),u=l&&K2({...l,pad:L2*La,align:La});u&&(i=await z2(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<N_)throw new Vt(`capture produced a ${i.length}-byte image`);let c=lg(i);if(!c)throw new Vt("output is not a PNG");return await tt.writeFile(t,i),{width:Math.round(c.width/La),height:Math.round(c.height/La),dpr:La,renderMs:Date.now()-r}}catch(s){throw s instanceof Vt?s:new Vt(/timeout/i.test(s?.message||"")?`render timed out after ${Fa}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function $_({htmlPath:e,outPath:t,width:n}){let r=await tt.mkdtemp(rt.join(R_.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await cg(__,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Fa,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new Vt(`firefox not found (${__})`,""):new Vt(c?.killed?`render timed out after ${Fa}ms`:"firefox exited non-zero",s)}let a=await tt.stat(t).catch(()=>null);if(!a||a.size<N_)throw new Vt(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=lg(await G2(t));if(!i)throw new Vt("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await tt.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function Y2(e){if(B2==="firefox")return $_(e);try{return await J2(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await $_(e)}catch(n){throw/not found/.test(n?.message||"")?new Vt(`no browser installed \u2014 chromium missing at ${C_} 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 B_(e){return ig=ig.then(e,e),ig}async function j_(e,t){let n=await tr(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await ln(),await It(r,"rendering",{version:t});try{let o=await lp();if(o<F2)throw new Vt(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=un(r,t),a=await X2({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await tt.rm(s.png,{force:!0}).catch(()=>{});return}ug(r,{v:t,source:"html",htmlRel:rt.relative(r.cwd,s.html),pngRel:rt.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await ln(),await It(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),Qn(r),er(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await ln(),await It(r,"error",{version:t,error:r.error}),Qn(r)}}}function ug(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 Z2({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?j2:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Kc({trackId:e,group:t}={}){let n=await tr(),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 $r(e){let n=(await tr()).mocks.find(r=>r.id===e);return n?{...n}:null}function L_({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&&!up(t))throw new Error("image must be a PNG");if(n){let o=q2(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>I_)throw new Error(`video must be ${I_/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function F_({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=Z2({trackId:e,name:t,cwd:s,width:a}),p=typeof n=="string"&&n.trim(),f=L_({html:n,image:r,video:o}),w=await tr(),m={id:C2().slice(0,8),trackId:e,box:c||null,agent:l||null,name:t.trim(),prompt:u?String(u).slice(0,500):null,group:i?String(i).trim():null,cwd:s,width:d,height:null,dpr:null,state:"queued",source:p?"html":o?"video":"image",kind:o?"video":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await tt.mkdir(Gc(s),{recursive:!0}),w.mocks.push(m),p?(await tt.writeFile(un(m,1).html,n),await ln(),await It(m,"created"),Qn(m),er(m,1,"html"),B_(()=>j_(m.id,1))):o?(await H_(m,1,o,f),await ln(),await It(m,"created"),await It(m,"ready",{version:1,width:m.width,height:m.height,dpr:m.dpr,durationS:m.versions[0]?.durationS??null}),Qn(m),D_(m,m.versions[0])):(await W_(m,1,r),await ln(),await It(m,"created"),await It(m,"ready",{version:1,width:m.width,height:m.height,dpr:m.dpr}),Qn(m),er(m,1,"png")),{...m}}async function U_(e,{html:t,image:n,video:r,width:o}={}){let a=(await tr()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=L_({html:t,image:n,video:r});if(o!=null){let u=Number(o);if(!Number.isInteger(u)||u<320||u>4e3)throw new Error("invalid width (320-4000)");a.width=u}let l=a.version+1;return a.version=l,a.updatedAt=Date.now(),a.source=i?"html":r?"video":"image",a.kind=r?"video":"image",await tt.mkdir(Gc(a.cwd),{recursive:!0}),i?(a.state="queued",await tt.writeFile(un(a,l).html,t),await ln(),await It(a,"updated",{version:l}),Qn(a),er(a,l,"html"),B_(()=>j_(a.id,l))):r?(await H_(a,l,r,c),await ln(),await It(a,"updated",{version:l}),await It(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:In(a)?.durationS??null}),Qn(a),D_(a,a.versions.find(u=>u.v===l))):(await W_(a,l,n),await ln(),await It(a,"updated",{version:l}),await It(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Qn(a),er(a,l,"png")),{...a}}async function Q2(e){try{let{stdout:t}=await cg(U2,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:P_,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 eH(e,t){try{await cg(H2,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:P_,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await tt.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function H_(e,t,n,r){let o=Gc(e.cwd),s=rt.join(o,`${e.id}.v${t}.${r.ext}`),a=rt.join(o,`${e.id}.v${t}.poster.png`);await tt.writeFile(s,n);let i=await Q2(s),c=await eH(s,a);ug(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:rt.relative(e.cwd,s),posterRel:c?rt.relative(e.cwd,a):null,mimeType:r.mime,bytes:n.length,durationS:i?.durationS??null,width:i?.width??null,height:i?.height??null,dpr:1,renderMs:null,createdAt:Date.now()}),i?.width&&(e.width=i.width)}async function W_(e,t,n){let r=lg(n);if(!r)throw new Error("image must be a PNG");let o=un(e,t);await tt.writeFile(o.png,n),ug(e,{v:t,source:"image",htmlRel:null,pngRel:rt.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function q_(e){let t=await tr(),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 It(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function G_(e){let t=await tr(),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=un(r,o);await tt.rm(s.html,{force:!0}).catch(()=>{}),await tt.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await tt.rm(rt.join(r.cwd,s),{force:!0}).catch(()=>{});return await ln(),await It(r,"deleted"),gn("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function K_(e){let n=(await tr()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=rt.join(O_,"mock-archive",e.id),o=Gc(e.cwd),s=rt.join(r,".bostrat","mocks");await tt.mkdir(s,{recursive:!0});let a=await tt.readdir(o).catch(()=>[]);for(let i of a)await tt.copyFile(rt.join(o,i),rt.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await tt.stat(rt.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=e.name||e.id,i.updatedAt=Date.now(),Qn(i)}return await ln(),n.length}async function V_(e){let t=await tr(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await ln();for(let r of n)gn("mocks",r.id).catch(()=>{})}return n.length}async function z_(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await ut("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await tr();for(let r of n.mocks){await Qn(r);for(let o of r.versions||[])o.htmlRel&&await er(r,o.v,"html"),(o.pngRel||o.posterRel)&&await er(r,o.v,"png"),o.videoRel&&await er(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var cg,O_,M_,B2,C_,__,j2,La,L2,N_,Fa,F2,I_,U2,H2,P_,Vt,Ss,ag,Qn,er,Gc,W2,X2,ig,Ua=q(()=>{ye();Gr();sg();Vr();cg=P2(N2),O_=process.env.BOSTRAT_STATE_DIR||rt.join(process.env.HOME||R_.tmpdir(),".bostrat"),M_=rt.join(O_,"mocks.json"),B2=process.env.BOSTRAT_MOCK_ENGINE||"chromium",C_=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",__=process.env.BOSTRAT_FIREFOX_BIN||"firefox",j2=1300,La=2,L2=32,N_=1500,Fa=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,F2=700*1024*1024,I_=100*1024*1024,U2=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",H2=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",P_=2e4,Vt=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
213
213
|
${n.trim().split(`
|
|
214
214
|
`).slice(-6).join(`
|
|
215
|
-
`)}`:t),this.name="MockRenderError"}},Ss=null,ig=null;Qn=e=>Et("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),er=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?qc(e,r,"video"):r&&qc(e,r,n)||(n==="html"?ln(e,t).html:null);return o?Kr("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Gc=e=>rt.join(e,".bostrat","mocks");F2=Buffer.from([137,80,78,71,13,10,26,10]);z2=V2,cg=Promise.resolve()});import Z2 from"node:fs/promises";import X_ from"node:path";import Q2 from"node:os";import{randomUUID as Z_}from"node:crypto";async function Rr(){return $r||(pg||=(async()=>{let e=!0;try{$r=JSON.parse(await Z2.readFile(Q_,"utf8"))}catch{$r={flows:[]},e=!1}finally{pg=null}if($r.flows||=[],!e&&qe()){let{records:t,source:n}=await ut("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&$r.flows.push(r.data);$r.flows.length&&await Ha().catch(()=>{})}}return $r})(),pg)}async function Ha(){await Pt(Q_,$r)}function fg(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 eI({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>V_)throw new Error(`steps must be an array of 1-${V_}`);let r=await Kc({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>J_)throw new Error(`${l}: caption over ${J_} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>Y_)throw new Error(`${l}: title over ${Y_} chars`);let p=String(c.mock??"").trim().replace(/^mock:/,"");if(!p)throw new Error(`${l}: mock (name or id) required`);let f=r.filter(y=>y.id===p);if(f.length||(f=r.filter(y=>y.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(y=>`mock:${y.id}`).join(", ")}) \u2014 use the id`);let w=f[0],m=null;if(c.v!=null&&(m=Number(c.v),!Number.isInteger(m)||!(w.versions||[]).some(y=>y.v===m)))throw new Error(`${l}: "${w.name}" has no captured v${c.v} to pin`);let g=o.findIndex(y=>y.mockId===w.id),b=g>=0?o.splice(g,1)[0].id:Z_().slice(0,8);s.push({id:b,mockId:w.id,caption:u,title:d,v:m})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function tH(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>z_)throw new Error(`name over ${z_} chars`);return e.trim()}async function tI({trackId:e}={}){let t=await Rr();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function nH(e){let n=(await Rr()).flows.find(r=>r.id===e);return n?{...n}:null}async function nI(e){let t=await nH(e);if(!t)return null;let n=new Map((await Kc({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 rI({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=tH(t),i=await Rr(),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 eI({trackId:e,steps:n}),d=Date.now(),p={id:Z_().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 Ha(),await fg(p,"created"),dp(p),{...p}}async function oI(e,{steps:t,note:n}={}){let o=(await Rr()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await eI({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 Ha(),await fg(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),dp(o),{...o}}async function sI(e){let t=await Rr(),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 Ha(),await fg(r,"deleted"),wn("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function aI(e){return(await Rr()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function iI(e){let n=(await Rr()).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(),dp(r);return n.length&&await Ha(),n.length}async function cI(e){let t=await Rr(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await Ha();for(let r of n)wn("flows",r.id).catch(()=>{})}return n.length}async function lI(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await ut("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Rr();for(let r of n.flows)await dp(r);return{pushed:n.flows.length,source:"plane"}}var eH,Q_,V_,z_,J_,Y_,$r,pg,dp,mg=q(()=>{ye();Gr();Ua();Vr();eH=process.env.BOSTRAT_STATE_DIR||X_.join(process.env.HOME||Q2.tmpdir(),".bostrat"),Q_=X_.join(eH,"flows.json"),V_=20,z_=80,J_=500,Y_=80,$r=null,pg=null;dp=e=>Et("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import Cr from"node:fs/promises";import Nr from"node:path";import rH from"node:os";import{randomUUID as oH}from"node:crypto";function Vc(e){return sH.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function nr(){return Or||(hg||=(async()=>{let e=!0;try{Or=JSON.parse(await Cr.readFile(dI,"utf8"))}catch{Or={runbooks:[]},e=!1}finally{hg=null}return Or.runbooks||=[],!e&&qe()&&await aH().catch(()=>{}),Or})(),hg)}async function Ts(){await Pt(dI,Or)}function fp(e){return zc(Et("runbooks",{...e},{trackId:e.trackId}))}async function aH(){let{records:e,source:t}=await ut("runbooks");if(!(t!=="plane"||!e.length)){await Cr.mkdir(pp,{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 Bi("runbooks",r.id,o.v,"md");typeof s=="string"&&await Cr.writeFile(Mr(r,o.v),s).catch(()=>{})}Or.runbooks.push(r)}Or.runbooks.length&&await Ts()}}function Wa(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 Mr(e,t){return Nr.join(pp,`${e.id}.v${t}.md`)}function pI(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 fI(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>uI)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${uI/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Vc(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 iH(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function cH({trackId:e,status:t}={}){let n=await nr(),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 mI({trackId:e,status:t}={}){let n=await nr(),r=await cH({trackId:e,status:t});if(!qe())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await ut("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 wg(e){let t=await lH(e);if(t)return t;if(!qe())return null;let{records:n,source:r}=await ut("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 hI(e,t){let n=await Bi("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function lH(e){let n=(await nr()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function wI({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=fI(r),c=await nr(),l={id:oH().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 Cr.mkdir(pp,{recursive:!0}),await Cr.writeFile(Mr(l,1),r),l.versions.push({v:1,mdRel:Nr.relative(As,Mr(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Ts(),await Wa(l,"created",{version:1}),fp(l),zc(Kr("runbooks",l.id,1,"md",Mr(l,1))),{...l}}async function gI(e,{md:t,note:n}={}){let o=(await nr()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");iH(o,"update");let s=fI(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Cr.mkdir(pp,{recursive:!0}),await Cr.writeFile(Mr(o,a),t),o.versions.push({v:a,mdRel:Nr.relative(As,Mr(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Ts(),await Wa(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),fp(o),zc(Kr("runbooks",o.id,a,"md",Mr(o,a))),{...o}}async function yI(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await nr()).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 Ts(),await Wa(r,t==="done"?"done":"reopened"),fp(r),{...r})}async function bI(e){let t=await nr(),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 Cr.rm(Nr.join(As,o.mdRel),{force:!0}).catch(()=>{});return await Ts(),r.trackDeletedAt||await Wa(r,"deleted"),zc(wn("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function kI(e){let n=(await nr()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Wa(r,"orphaned"),r.cwd=null,fp(r);return n.length&&await Ts(),n.length}async function xI(e){let t=await nr(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await Wa(r,"deleted");for(let o of r.versions||[])await Cr.rm(Nr.join(As,o.mdRel),{force:!0}).catch(()=>{});zc(wn("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await Ts()),n.length}async function vI(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await ut("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await nr();for(let r of n.runbooks){await Et("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await Kr("runbooks",r.id,o.v,"md",Nr.join(As,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var As,dI,pp,uI,sH,Or,hg,zc,mp=q(()=>{Gr();ye();Vr();As=process.env.BOSTRAT_STATE_DIR||Nr.join(process.env.HOME||rH.tmpdir(),".bostrat"),dI=Nr.join(As,"runbooks.json"),pp=Nr.join(As,"runbooks"),uI=256*1024,sH=[{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/}];Or=null,hg=null;zc=e=>Promise.resolve(e).catch(()=>{})});import gg from"node:fs/promises";import Jc from"node:path";import{randomUUID as uH}from"node:crypto";function bg(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return dH.has(t)?"image":pH.has(t)?"doc":null}function fH(e){let r=Jc.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 TI(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>SI)throw new Error(`file too large \u2014 max ${Math.floor(SI/(1024*1024))} MB`);let r=fH(t),o=Jc.extname(r).slice(1),s=bg(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=uH().slice(0,8),i=yg(e);await gg.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await gg.writeFile(Jc.join(i,c),n,{mode:384}),await gg.chmod(Jc.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${AI}/${c}`,kind:s,size:n.length}}function EI(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function kg(e){return e.role==="user"&&mH.test((e.text||"").trim())}var AI,yg,SI,dH,pH,mH,xg=q(()=>{AI=".bostrat/uploads",yg=e=>Jc.join(e,AI),SI=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,dH=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),pH=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"]);mH=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function Es(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}function vg(){let e=Number(process.env.BOSTRAT_LOOP_QUEUE_HOLD_MS);return Number.isFinite(e)&&e>0?e:hH}function gH(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 _I(e,t,{capMs:n=vg(),now:r=Date.now()}={}){if(!e)return!1;let o=gH(e,t,r);return e.delivery?.producer!=="loop"||!wH.has(t)?!1:o>=n}var hH,wH,II=q(()=>{hH=5*6e4,wH=new Set(["busy","noSession"])});import{execFile as yH}from"node:child_process";import{promisify as bH}from"node:util";function Sg(e){return`refs/bostrat/wip/${e}`}function vH(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function OI(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function SH(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:xH}function MI(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 AH(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 vo(s,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await vo(s,["remote","get-url","origin"])}catch{return $I.has(s)||($I.add(s),t.log?.(`[wip-sync] ${a}: no origin remote \u2014 durable code fidelity is chat/memory-only for this checkout`)),{pushed:!1,reason:"no-origin"}}try{let i=await Aa(s,"bostrat: wip snapshot"),{stdout:c}=await vo(s,["rev-parse",`${i}^{tree}`]),l=c.trim();if(!n&&hp.get(s)===l)return{pushed:!1,reason:"unchanged"};let u=Sg(o);return await vo(s,["push","--force","origin",`${i}:${u}`],RI),hp.set(s,l),await N(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:u,sha:i,wip:!0,...CI(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(`
|
|
216
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function
|
|
217
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function
|
|
215
|
+
`)}`:t),this.name="MockRenderError"}},Ss=null,ag=null;Qn=e=>Et("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),er=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?qc(e,r,"video"):r&&qc(e,r,n)||(n==="html"?un(e,t).html:null);return o?Kr("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Gc=e=>rt.join(e,".bostrat","mocks");W2=Buffer.from([137,80,78,71,13,10,26,10]);X2=Y2,ig=Promise.resolve()});import tH from"node:fs/promises";import Q_ from"node:path";import nH from"node:os";import{randomUUID as eI}from"node:crypto";async function Or(){return Rr||(dg||=(async()=>{let e=!0;try{Rr=JSON.parse(await tH.readFile(tI,"utf8"))}catch{Rr={flows:[]},e=!1}finally{dg=null}if(Rr.flows||=[],!e&&qe()){let{records:t,source:n}=await ut("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&Rr.flows.push(r.data);Rr.flows.length&&await Ha().catch(()=>{})}}return Rr})(),dg)}async function Ha(){await Pt(tI,Rr)}function pg(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 nI({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>J_)throw new Error(`steps must be an array of 1-${J_}`);let r=await Kc({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>X_)throw new Error(`${l}: caption over ${X_} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>Z_)throw new Error(`${l}: title over ${Z_} 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],m=null;if(c.v!=null&&(m=Number(c.v),!Number.isInteger(m)||!(w.versions||[]).some(g=>g.v===m)))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:eI().slice(0,8);s.push({id:k,mockId:w.id,caption:u,title:d,v:m})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function oH(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>Y_)throw new Error(`name over ${Y_} chars`);return e.trim()}async function rI({trackId:e}={}){let t=await Or();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function sH(e){let n=(await Or()).flows.find(r=>r.id===e);return n?{...n}:null}async function oI(e){let t=await sH(e);if(!t)return null;let n=new Map((await Kc({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=In(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 sI({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=oH(t),i=await Or(),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 nI({trackId:e,steps:n}),d=Date.now(),p={id:eI().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 Ha(),await pg(p,"created"),dp(p),{...p}}async function aI(e,{steps:t,note:n}={}){let o=(await Or()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await nI({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 Ha(),await pg(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),dp(o),{...o}}async function iI(e){let t=await Or(),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 Ha(),await pg(r,"deleted"),gn("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function cI(e){return(await Or()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function lI(e){let n=(await Or()).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(),dp(r);return n.length&&await Ha(),n.length}async function uI(e){let t=await Or(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await Ha();for(let r of n)gn("flows",r.id).catch(()=>{})}return n.length}async function dI(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await ut("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Or();for(let r of n.flows)await dp(r);return{pushed:n.flows.length,source:"plane"}}var rH,tI,J_,Y_,X_,Z_,Rr,dg,dp,fg=q(()=>{ye();Gr();Ua();Vr();rH=process.env.BOSTRAT_STATE_DIR||Q_.join(process.env.HOME||nH.tmpdir(),".bostrat"),tI=Q_.join(rH,"flows.json"),J_=20,Y_=80,X_=500,Z_=80,Rr=null,dg=null;dp=e=>Et("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import Nr from"node:fs/promises";import Pr from"node:path";import aH from"node:os";import{randomUUID as iH}from"node:crypto";function Vc(e){return cH.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function nr(){return Mr||(mg||=(async()=>{let e=!0;try{Mr=JSON.parse(await Nr.readFile(fI,"utf8"))}catch{Mr={runbooks:[]},e=!1}finally{mg=null}return Mr.runbooks||=[],!e&&qe()&&await lH().catch(()=>{}),Mr})(),mg)}async function Ts(){await Pt(fI,Mr)}function fp(e){return zc(Et("runbooks",{...e},{trackId:e.trackId}))}async function lH(){let{records:e,source:t}=await ut("runbooks");if(!(t!=="plane"||!e.length)){await Nr.mkdir(pp,{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 Bi("runbooks",r.id,o.v,"md");typeof s=="string"&&await Nr.writeFile(Cr(r,o.v),s).catch(()=>{})}Mr.runbooks.push(r)}Mr.runbooks.length&&await Ts()}}function Wa(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 Cr(e,t){return Pr.join(pp,`${e.id}.v${t}.md`)}function mI(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 hI(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>pI)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${pI/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Vc(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 uH(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function dH({trackId:e,status:t}={}){let n=await nr(),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 wI({trackId:e,status:t}={}){let n=await nr(),r=await dH({trackId:e,status:t});if(!qe())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await ut("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 hg(e){let t=await pH(e);if(t)return t;if(!qe())return null;let{records:n,source:r}=await ut("runbooks");if(r!=="plane")return null;let o=n.find(s=>!s.deletedAt&&s.data?.id===e);return o?{...o.data,originNode:o.originNode||null,remote:!0}:null}async function gI(e,t){let n=await Bi("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function pH(e){let n=(await nr()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function yI({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=hI(r),c=await nr(),l={id:iH().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 Nr.mkdir(pp,{recursive:!0}),await Nr.writeFile(Cr(l,1),r),l.versions.push({v:1,mdRel:Pr.relative(As,Cr(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Ts(),await Wa(l,"created",{version:1}),fp(l),zc(Kr("runbooks",l.id,1,"md",Cr(l,1))),{...l}}async function bI(e,{md:t,note:n}={}){let o=(await nr()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");uH(o,"update");let s=hI(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Nr.mkdir(pp,{recursive:!0}),await Nr.writeFile(Cr(o,a),t),o.versions.push({v:a,mdRel:Pr.relative(As,Cr(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Ts(),await Wa(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),fp(o),zc(Kr("runbooks",o.id,a,"md",Cr(o,a))),{...o}}async function kI(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await nr()).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 Ts(),await Wa(r,t==="done"?"done":"reopened"),fp(r),{...r})}async function xI(e){let t=await nr(),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 Nr.rm(Pr.join(As,o.mdRel),{force:!0}).catch(()=>{});return await Ts(),r.trackDeletedAt||await Wa(r,"deleted"),zc(gn("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function vI(e){let n=(await nr()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Wa(r,"orphaned"),r.cwd=null,fp(r);return n.length&&await Ts(),n.length}async function SI(e){let t=await nr(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await Wa(r,"deleted");for(let o of r.versions||[])await Nr.rm(Pr.join(As,o.mdRel),{force:!0}).catch(()=>{});zc(gn("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await Ts()),n.length}async function AI(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await ut("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await nr();for(let r of n.runbooks){await Et("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await Kr("runbooks",r.id,o.v,"md",Pr.join(As,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var As,fI,pp,pI,cH,Mr,mg,zc,mp=q(()=>{Gr();ye();Vr();As=process.env.BOSTRAT_STATE_DIR||Pr.join(process.env.HOME||aH.tmpdir(),".bostrat"),fI=Pr.join(As,"runbooks.json"),pp=Pr.join(As,"runbooks"),pI=256*1024,cH=[{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/}];Mr=null,mg=null;zc=e=>Promise.resolve(e).catch(()=>{})});import wg from"node:fs/promises";import Jc from"node:path";import{randomUUID as fH}from"node:crypto";function yg(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return mH.has(t)?"image":hH.has(t)?"doc":null}function wH(e){let r=Jc.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 _I(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>TI)throw new Error(`file too large \u2014 max ${Math.floor(TI/(1024*1024))} MB`);let r=wH(t),o=Jc.extname(r).slice(1),s=yg(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=fH().slice(0,8),i=gg(e);await wg.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await wg.writeFile(Jc.join(i,c),n,{mode:384}),await wg.chmod(Jc.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${EI}/${c}`,kind:s,size:n.length}}function II(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"&&gH.test((e.text||"").trim())}var EI,gg,TI,mH,hH,gH,kg=q(()=>{EI=".bostrat/uploads",gg=e=>Jc.join(e,EI),TI=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,mH=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),hH=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"]);gH=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function Es(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}function xg(){let e=Number(process.env.BOSTRAT_LOOP_QUEUE_HOLD_MS);return Number.isFinite(e)&&e>0?e:yH}function kH(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 $I(e,t,{capMs:n=xg(),now:r=Date.now()}={}){if(!e)return!1;let o=kH(e,t,r);return e.delivery?.producer!=="loop"||!bH.has(t)?!1:o>=n}var yH,bH,RI=q(()=>{yH=5*6e4,bH=new Set(["busy","noSession"])});import{execFile as xH}from"node:child_process";import{promisify as vH}from"node:util";function vg(e){return`refs/bostrat/wip/${e}`}function TH(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function CI(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function EH(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:AH}function NI(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 _H(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 vo(s,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await vo(s,["remote","get-url","origin"])}catch{return OI.has(s)||(OI.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 Aa(s,"bostrat: wip snapshot"),{stdout:c}=await vo(s,["rev-parse",`${i}^{tree}`]),l=c.trim();if(!n&&hp.get(s)===l)return{pushed:!1,reason:"unchanged"};let u=vg(o);return await vo(s,["push","--force","origin",`${i}:${u}`],MI),hp.set(s,l),await N(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:u,sha:i,wip:!0,...PI(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(`
|
|
216
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function Sg(e,{log:t=console,force:n=!1}={}){let r=[],o=[];for(let s of NI(e)){let a=await _H(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 IH(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 vo(s,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await vo(s,["remote","get-url","origin"])}catch{return{pushed:!1,reason:"no-origin"}}try{let i=await Aa(s,"bostrat: transfer rescue snapshot"),{stdout:c}=await vo(s,["rev-parse",`${i}^{tree}`]),l=c.trim();if(hp.get(s)===l)return{pushed:!1,reason:"unchanged"};let u=TH(o,r);return await vo(s,["push","origin",`${i}:${u}`],MI),hp.set(s,l),await N(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:u,sha:i,rescue:!0,...PI(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(`
|
|
217
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function Ag(e,{log:t=console,ts:n=Date.now()}={}){let r=[],o=[];for(let s of NI(e)){let a=await IH(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 DI({listTracks:e,sweepMs:t=EH(),log:n=console}){let r=!1,o=async()=>{if(!r){r=!0;try{let a=await e()??[];for(let i of a)Fe(i)||i?.kind==="setup"||await Sg(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 SH,AH,MI,vo,hp,OI,PI,Tg=q(()=>{fd();ye();sn();SH=vH(xH),AH=12e4,MI=6e4;vo=(e,t,n=15e3)=>SH("git",["-C",e,...t],{timeout:n,maxBuffer:4*1024*1024}),hp=new Map,OI=new Set,PI=e=>e?.name||e?.repo?{name:e.name??null,repo:e.repo??null}:{}});import $H from"node:fs";import Eg from"node:fs/promises";import RH from"node:path";function NH(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function BI(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(!NH(t))return{restored:!1,reason:"plane not configured"};let s=Qs(e),a=RH.join(s,`${o}.ndjson`);try{if($H.statSync(a).size>0)return{restored:!1,reason:"log not empty"}}catch{}let i=Ne(t),c={authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`},l=[],u=0;for(;;){let p=new AbortController,f=setTimeout(()=>p.abort(),CH),w;try{w=await n(`${i}/v1/spine/${encodeURIComponent(o)}?after=${u}&limit=${OH}`,{headers:c,signal:p.signal})}finally{clearTimeout(f)}if(w.status===404)return{restored:!1,reason:"unknown to plane"};if(!w.ok)return{restored:!1,reason:`plane ${w.status}`};let m=await w.json();for(let y of m.events||[])l.push(JSON.stringify(y));if(l.length>MH)return{restored:!1,reason:"event count over safety cap"};if(m.next==null)break;u=m.next}if(!l.length)return{restored:!1,reason:"unknown to plane"};await Eg.mkdir(s,{recursive:!0});let d=`${a}.pull-${process.pid}-${Date.now()}`;return await Eg.writeFile(d,l.join(`
|
|
218
218
|
`)+`
|
|
219
|
-
`),await _g.rename(d,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var IH,$H,RH,DI=q(()=>{bt();ye();IH=1e3,$H=5e5,RH=3e4});function BI(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 jI(e){let t=(e||[]).filter(n=>!MH.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 LI({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?un("archived","lifecycle",e):n?un("needs-you","pane",o||w,{count:r||1}):t?un("archiving","lifecycle",e||w):i?.status==="working"?un("working","operation",i.since,{summary:i.summary??null,count:i.count??null}):(l||n)&&c?.phase?un("working","agent-phase",c.at,{summary:c.summary??null}):l?un("working","pane",u||w):r>0||s?un("needs-you",s?"loop":"obligations",o||w,{count:r||1}):a?.count?un("needs-you","pr",a.since,{count:a.count,summary:a.summary??null}):i?.status==="waiting"?un("waiting","operation",i.since,{summary:i.summary??null,count:i.count??null}):d?.count?un("waiting",d.source||"pending",d.since,{summary:d.summary??null,count:d.count}):p&&f?un("settled","obligations",p,{summary:"no open actions"}):un("idle","fallback",p||w)}var un,MH,FI=q(()=>{un=(e,t,n,r={})=>({kind:e,source:t,since:n||0,...r});MH=new Set(["merged","closed","lost"])});function CH(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 NH(e,t,n){let r=CH(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 Ig(e){if(wp.has(e.id))return wp.get(e.id);let t=await K(e).catch(()=>[]),n=[];try{n=Hn(t)}catch{}let r=Vu(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 wp.set(e.id,p),!Fe(e)&&e.kind!=="setup"&&NH(e,t,p),p}function UI({trackOf:e,debounceMs:t=PH,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(Yc.get(s));let a=setTimeout(()=>{Yc.delete(s),Promise.resolve(e(s)).then(i=>i&&!Fe(i)&&i.kind!=="setup"?Ig(i):null).catch(i=>n.warn?.(`[obligations] fold ${s} failed: ${i?.message||i}`))},t);a.unref?.(),Yc.set(s,a)};return Ye.on("event",r),()=>{Ye.off("event",r);for(let o of Yc.values())clearTimeout(o);Yc.clear()}}var wp,PH,Yc,$g=q(()=>{ye();zu();on();wp=new Map;Ye.on("event",e=>{e?.trackId&&wp.delete(e.trackId)});PH=750,Yc=new Map});import Rg from"node:fs/promises";async function BH(e){let t;try{t=await Rg.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/DH*1e3,ioBytes:await jH(e)}}async function jH(e){let t;try{t=await Rg.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 HI(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 Rg.readdir("/proc")}catch{return null}let r=new Map;await Promise.all(n.map(async l=>{if(!/^\d+$/.test(l))return;let u=await BH(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 DH,WI=q(()=>{DH=100});import LH from"node:fs/promises";function Ng(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 Pg(e,t,n){let r=null;try{r=await n.updatedAt(e)}catch{r=null}let o=qI.get(e.name);if(o&&o.mtime===r&&t-o.at<qH)return o.jobs;let s=[];try{s=await n.readOpenJobs(e)}catch{s=[]}return qI.set(e.name,{at:t,mtime:r,jobs:s}),s}async function GH(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 KH(e,t){return e?.outputFile?await t.statFile(e.outputFile):null}function VH(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>=HH||t.ioBytes!=null&&e.ioBytes!=null&&(t.ioBytes-e.ioBytes)/n>=WH}function GI(e,t,n,r){let o=[];for(let s of n){let a=Cg(e.id,t.name,s.id),i=In.get(a);i?i.job=s:In.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 KI(e,{now:t=Date.now(),deps:n=Mg}={}){let o=(await Promise.all(Ng([e]).map(({box:s})=>Pg(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 VI(e,{now:t=Date.now(),deps:n=Mg}={}){let r=new Map,o=new Set;for(let{track:s,box:a}of Ng(e)){let i=await Pg(a,t,n);for(let l of i)o.add(Cg(s.id,a.name,l.id));let c=GI(s,a,i,t).filter(l=>!In.get(l).stalled).map(l=>In.get(l).job);c.length&&r.set(s.id,[...r.get(s.id)??[],...c])}for(let s of[...In.keys()])o.has(s)||In.delete(s);return r}async function zH(e,{now:t=Date.now(),deps:n=Mg}={}){let r={checked:0,progressing:0,quiet:0,stalled:[]},o=Ng(e),s=new Set(o.map(({box:a})=>a.name));for(let[a,i]of[...In])s.has(i.boxName)||In.delete(a);for(let{track:a,box:i}of o){let c=await Pg(i,t,n),l=new Set(c.map(w=>Cg(a.id,i.name,w.id)));for(let[w,m]of[...In])m.boxName===i.name&&!l.has(w)&&In.delete(w);if(!c.length){Og.delete(i.name);continue}let u=GI(a,i,c,t),d=await GH(i,t,n),p=Og.get(i.name);Og.set(i.name,d);let f=VH(p,d);for(let w of u){let m=In.get(w);r.checked++;let g=await KH(m.job,n),b=!!g&&!!m.file&&(g.size>m.file.size||g.mtimeMs>m.file.mtimeMs),y=!!g&&!m.file;if(m.file=g??m.file,f||b||y){m.quiet=0,m.lastProgressAt=t,m.stalled=!1,r.progressing++;continue}if(m.stalled||(m.quiet++,r.quiet++,m.quiet<UH))continue;m.stalled=!0,m.stalledAt=t,r.stalled.push(w);let S=t-(m.lastProgressAt??m.firstSeenAt);try{await n.appendEvent(a,{type:"job.stalled",actor:"orchestrator",source:"orchestrator",boxId:i.name,provider:i.agent||null,payload:{id:m.job.id,kind:m.job.kind||"agent",name:m.job.name||null,detail:m.job.detail||null,startedTs:m.job.ts??m.firstSeenAt,quietMs:S,checks:m.quiet}})}catch{}console.log(`[job-watch] ${a.id}/${i.name} job ${m.job.id} (${m.job.name||m.job.kind}) quiet for ${Math.round(S/6e4)}m over ${m.quiet} check(s) \u2014 released; the node may sleep`)}}return r}function zI({listTracks:e,intervalMs:t=FH}={}){let n=!1,o=setInterval(async()=>{if(!n){n=!0;try{await zH(await e())}catch(s){console.error(`[job-watch] sweep: ${s?.message||s}`)}finally{n=!1}}},t);return o.unref?.(),o}var FH,UH,HH,WH,qH,Mg,In,qI,Og,Cg,Dg=q(()=>{ye();nd();dt();WI();FH=Number(process.env.BOSTRAT_JOB_CHECK_MS)||15*6e4,UH=Number(process.env.BOSTRAT_JOB_STALL_CHECKS)||3,HH=Number(process.env.BOSTRAT_JOB_CPU_RATE_MIN)||25,WH=Number(process.env.BOSTRAT_JOB_IO_RATE_MIN)||64*1024,qH=5*6e4,Mg={readOpenJobs:td,statFile:async e=>{try{let t=await LH.stat(e);return{size:t.size,mtimeMs:Math.round(t.mtimeMs)}}catch{return null}},updatedAt:e=>ro(e.agent).updatedAt(e),panePids:Y0,treeStats:HI,appendEvent:N},In=new Map,qI=new Map,Og=new Map,Cg=(e,t,n)=>`${e}|${t}|${n}`});import JH from"node:fs/promises";import YH from"node:os";import gp from"node:path";async function Bg(e){try{return(await JH.stat(e)).size}catch{return null}}async function qa(e){let[t,n,r]=await Promise.all([Bg(gp.join(XH,"CLAUDE.md")),e?Bg(gp.join(e,"CLAUDE.md")):null,e?Bg(gp.join(e,".bostrat","memory","MEMORY.md")):null]);return{bostratClaudeMd:t,repoClaudeMd:n,memoryIndex:r}}var XH,jg=q(()=>{XH=process.env.BOSTRAT_STATE_DIR||gp.join(process.env.HOME||YH.tmpdir(),".bostrat")});import{randomUUID as ZH}from"node:crypto";async function bp(e,t,n={}){let r={operationId:ZH(),kind:t,startedAt:Date.now(),...n};return yp.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 Lg(e,t,n="completed",r=null){yp.get(e.id)?.operationId===t?.operationId&&yp.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 yp,JI,Fg=q(()=>{ye();yp=new Map;JI=e=>yp.get(e)||null});var $$={};Uf($$,{HANDOFF_LATEST_REL:()=>To,KNOWN_AGENTS:()=>cl,TRACK_NAME_MAX:()=>Qg,addTrackRepo:()=>Pp,adoptTrack:()=>Dy,agentAvailability:()=>Rp,announceNodeUpdate:()=>or,answerFullscreenOffer:()=>$y,answerPrompt:()=>Mp,archiveTrack:()=>Rs,archiveTrackForMergedPr:()=>vr,augmentFirstPromptAfterHandoff:()=>s$,buildAgentCmd:()=>ey,canResumeSubtask:()=>E$,cancelQueued:()=>ul,cancelQueuedToDraft:()=>cy,cancelStopJob:()=>Qa,cancelSubtask:()=>Kp,changedRepos:()=>vs,commitTransferOut:()=>Wp,createSetupTrack:()=>ky,createTrack:()=>Bp,deleteTrack:()=>vy,demoteTransferredTracks:()=>Py,ensureLeadEpochs:()=>Ao,forceMergeSubtask:()=>Ly,getHandoffJob:()=>ni,getReshapeJob:()=>dl,getSubtaskJob:()=>pq,getTrack:()=>me,getTransferOutJob:()=>Cy,healDeadLead:()=>al,interruptLead:()=>Np,isArchiving:()=>XW,isHandoffNoteEcho:()=>zg,isInterruptEcho:()=>Jg,isLeadStarting:()=>d$,isModelSwitchEcho:()=>Yg,leadCliDead:()=>Ay,listSubtaskJobs:()=>Ms,listTracks:()=>be,matchTranscriptsToBoxes:()=>l$,mergeNotifyMessage:()=>v$,navigatePrompt:()=>ay,notifyTrackForMergedPr:()=>Sr,notifyTrackForResolvedPr:()=>jp,offerFollowupsForMergedPr:()=>Os,pendingOffers:()=>oy,queueGauges:()=>iy,queueShipCompleteNotify:()=>uy,queuedMessages:()=>_s,rawTrack:()=>ry,reapArchivedOrphans:()=>Iy,reconcileDemotedTracks:()=>_y,reconcileHandoffOutcome:()=>S$,reconcileInterruptedHandoffs:()=>Oy,reconcileInterruptedReshapes:()=>yy,reconcileInterruptedSubtasks:()=>By,reconcileNodeUpdateNotices:()=>ny,reconcileSubtaskOutcome:()=>T$,refreshStandingInstructions:()=>by,refuseIfTransferred:()=>Hp,rehydrateLead:()=>ti,removeTrackRepo:()=>Dp,renameTrack:()=>Ey,reposOf:()=>Ut,reshapeTrack:()=>gy,resolveTrackBox:()=>Mc,resolveTrackBoxByHookToken:()=>ll,restartLead:()=>Fp,resumeAgentCmd:()=>$p,reviveLeadForSession:()=>Ty,sayToLead:()=>zt,sendOptionsForAgent:()=>Un,setTrackDraft:()=>fy,setTrackModel:()=>my,startLeadHandoff:()=>Up,startSpinUpView:()=>dy,startSubtask:()=>jy,startTransferOut:()=>Ny,stopJobActive:()=>f$,subtaskMessages:()=>sy,trackMessages:()=>Op,unarchiveTrack:()=>Lp});import z from"node:fs/promises";import V from"node:path";import{randomUUID as Ep}from"node:crypto";import{execFile as QH}from"node:child_process";import{promisify as eW}from"node:util";function ey(e,{skipPermissions:t=!0,model:n=null}={}){let r=Ip[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=zh(e).find(i=>i.alias===n);if(!a)throw new Error(`unknown ${e} launch model "${n}"`);s+=` --model ${a.alias}`}return s}function $p(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=Ip[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&&!kn(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 $s(e){let t=Ip[e];if(t)return process.env[t.envVar]||t.base}async function Rp({maxAgeMs:e=6e4}={}){if(So.value&&Date.now()-So.at<e)return So.value;let t=So.value,n=await Promise.all(cl.map(async r=>{let o=await Eu($s(r),{retries:[]});return o===sa?[r,t?.[r]??!0]:[r,o===Tu]}));return So.value=Object.fromEntries(n),So.at=Date.now(),So.value}async function Za(e,t=!0,n=null){let r=ey(e,{skipPermissions:t,model:n});if(await Eu(r)===oa){So.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.":ym[o]?`Install it on this node: ${ym[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 s$(e,t=To){return`${e.trim()}
|
|
219
|
+
`),await Eg.rename(d,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var OH,MH,CH,jI=q(()=>{bt();ye();OH=1e3,MH=5e5,CH=3e4});function LI(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 FI(e){let t=(e||[]).filter(n=>!PH.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 UI({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?dn("archived","lifecycle",e):n?dn("needs-you","pane",o||w,{count:r||1}):t?dn("archiving","lifecycle",e||w):i?.status==="working"?dn("working","operation",i.since,{summary:i.summary??null,count:i.count??null}):(l||n)&&c?.phase?dn("working","agent-phase",c.at,{summary:c.summary??null}):l?dn("working","pane",u||w):r>0||s?dn("needs-you",s?"loop":"obligations",o||w,{count:r||1}):a?.count?dn("needs-you","pr",a.since,{count:a.count,summary:a.summary??null}):i?.status==="waiting"?dn("waiting","operation",i.since,{summary:i.summary??null,count:i.count??null}):d?.count?dn("waiting",d.source||"pending",d.since,{summary:d.summary??null,count:d.count}):p&&f?dn("settled","obligations",p,{summary:"no open actions"}):dn("idle","fallback",p||w)}var dn,PH,HI=q(()=>{dn=(e,t,n,r={})=>({kind:e,source:t,since:n||0,...r});PH=new Set(["merged","closed","lost"])});function DH(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 BH(e,t,n){let r=DH(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 _g(e){if(wp.has(e.id))return wp.get(e.id);let t=await K(e).catch(()=>[]),n=[];try{n=Wn(t)}catch{}let r=Vu(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 wp.set(e.id,p),!Fe(e)&&e.kind!=="setup"&&BH(e,t,p),p}function WI({trackOf:e,debounceMs:t=jH,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(Yc.get(s));let a=setTimeout(()=>{Yc.delete(s),Promise.resolve(e(s)).then(i=>i&&!Fe(i)&&i.kind!=="setup"?_g(i):null).catch(i=>n.warn?.(`[obligations] fold ${s} failed: ${i?.message||i}`))},t);a.unref?.(),Yc.set(s,a)};return Ye.on("event",r),()=>{Ye.off("event",r);for(let o of Yc.values())clearTimeout(o);Yc.clear()}}var wp,jH,Yc,Ig=q(()=>{ye();zu();sn();wp=new Map;Ye.on("event",e=>{e?.trackId&&wp.delete(e.trackId)});jH=750,Yc=new Map});import $g from"node:fs/promises";async function FH(e){let t;try{t=await $g.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/LH*1e3,ioBytes:await UH(e)}}async function UH(e){let t;try{t=await $g.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 qI(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 $g.readdir("/proc")}catch{return null}let r=new Map;await Promise.all(n.map(async l=>{if(!/^\d+$/.test(l))return;let u=await FH(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 LH,GI=q(()=>{LH=100});import HH from"node:fs/promises";function Cg(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 Ng(e,t,n){let r=null;try{r=await n.updatedAt(e)}catch{r=null}let o=KI.get(e.name);if(o&&o.mtime===r&&t-o.at<VH)return o.jobs;let s=[];try{s=await n.readOpenJobs(e)}catch{s=[]}return KI.set(e.name,{at:t,mtime:r,jobs:s}),s}async function zH(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 JH(e,t){return e?.outputFile?await t.statFile(e.outputFile):null}function YH(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>=GH||t.ioBytes!=null&&e.ioBytes!=null&&(t.ioBytes-e.ioBytes)/n>=KH}function VI(e,t,n,r){let o=[];for(let s of n){let a=Mg(e.id,t.name,s.id),i=$n.get(a);i?i.job=s:$n.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 zI(e,{now:t=Date.now(),deps:n=Og}={}){let o=(await Promise.all(Cg([e]).map(({box:s})=>Ng(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 JI(e,{now:t=Date.now(),deps:n=Og}={}){let r=new Map,o=new Set;for(let{track:s,box:a}of Cg(e)){let i=await Ng(a,t,n);for(let l of i)o.add(Mg(s.id,a.name,l.id));let c=VI(s,a,i,t).filter(l=>!$n.get(l).stalled).map(l=>$n.get(l).job);c.length&&r.set(s.id,[...r.get(s.id)??[],...c])}for(let s of[...$n.keys()])o.has(s)||$n.delete(s);return r}async function XH(e,{now:t=Date.now(),deps:n=Og}={}){let r={checked:0,progressing:0,quiet:0,stalled:[]},o=Cg(e),s=new Set(o.map(({box:a})=>a.name));for(let[a,i]of[...$n])s.has(i.boxName)||$n.delete(a);for(let{track:a,box:i}of o){let c=await Ng(i,t,n),l=new Set(c.map(w=>Mg(a.id,i.name,w.id)));for(let[w,m]of[...$n])m.boxName===i.name&&!l.has(w)&&$n.delete(w);if(!c.length){Rg.delete(i.name);continue}let u=VI(a,i,c,t),d=await zH(i,t,n),p=Rg.get(i.name);Rg.set(i.name,d);let f=YH(p,d);for(let w of u){let m=$n.get(w);r.checked++;let y=await JH(m.job,n),k=!!y&&!!m.file&&(y.size>m.file.size||y.mtimeMs>m.file.mtimeMs),g=!!y&&!m.file;if(m.file=y??m.file,f||k||g){m.quiet=0,m.lastProgressAt=t,m.stalled=!1,r.progressing++;continue}if(m.stalled||(m.quiet++,r.quiet++,m.quiet<qH))continue;m.stalled=!0,m.stalledAt=t,r.stalled.push(w);let S=t-(m.lastProgressAt??m.firstSeenAt);try{await n.appendEvent(a,{type:"job.stalled",actor:"orchestrator",source:"orchestrator",boxId:i.name,provider:i.agent||null,payload:{id:m.job.id,kind:m.job.kind||"agent",name:m.job.name||null,detail:m.job.detail||null,startedTs:m.job.ts??m.firstSeenAt,quietMs:S,checks:m.quiet}})}catch{}console.log(`[job-watch] ${a.id}/${i.name} job ${m.job.id} (${m.job.name||m.job.kind}) quiet for ${Math.round(S/6e4)}m over ${m.quiet} check(s) \u2014 released; the node may sleep`)}}return r}function YI({listTracks:e,intervalMs:t=WH}={}){let n=!1,o=setInterval(async()=>{if(!n){n=!0;try{await XH(await e())}catch(s){console.error(`[job-watch] sweep: ${s?.message||s}`)}finally{n=!1}}},t);return o.unref?.(),o}var WH,qH,GH,KH,VH,Og,$n,KI,Rg,Mg,Pg=q(()=>{ye();nd();dt();GI();WH=Number(process.env.BOSTRAT_JOB_CHECK_MS)||15*6e4,qH=Number(process.env.BOSTRAT_JOB_STALL_CHECKS)||3,GH=Number(process.env.BOSTRAT_JOB_CPU_RATE_MIN)||25,KH=Number(process.env.BOSTRAT_JOB_IO_RATE_MIN)||64*1024,VH=5*6e4,Og={readOpenJobs:td,statFile:async e=>{try{let t=await HH.stat(e);return{size:t.size,mtimeMs:Math.round(t.mtimeMs)}}catch{return null}},updatedAt:e=>ro(e.agent).updatedAt(e),panePids:X0,treeStats:qI,appendEvent:N},$n=new Map,KI=new Map,Rg=new Map,Mg=(e,t,n)=>`${e}|${t}|${n}`});import ZH from"node:fs/promises";import QH from"node:os";import gp from"node:path";async function Dg(e){try{return(await ZH.stat(e)).size}catch{return null}}async function qa(e){let[t,n,r]=await Promise.all([Dg(gp.join(eW,"CLAUDE.md")),e?Dg(gp.join(e,"CLAUDE.md")):null,e?Dg(gp.join(e,".bostrat","memory","MEMORY.md")):null]);return{bostratClaudeMd:t,repoClaudeMd:n,memoryIndex:r}}var eW,Bg=q(()=>{eW=process.env.BOSTRAT_STATE_DIR||gp.join(process.env.HOME||QH.tmpdir(),".bostrat")});import{randomUUID as tW}from"node:crypto";async function bp(e,t,n={}){let r={operationId:tW(),kind:t,startedAt:Date.now(),...n};return yp.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 jg(e,t,n="completed",r=null){yp.get(e.id)?.operationId===t?.operationId&&yp.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 yp,XI,Lg=q(()=>{ye();yp=new Map;XI=e=>yp.get(e)||null});var M$={};Uf(M$,{HANDOFF_LATEST_REL:()=>To,KNOWN_AGENTS:()=>cl,TRACK_NAME_MAX:()=>Qg,addTrackRepo:()=>Pp,adoptTrack:()=>By,agentAvailability:()=>Rp,announceNodeUpdate:()=>sr,answerFullscreenOffer:()=>Ry,answerPrompt:()=>Mp,answeringFor:()=>ay,archiveTrack:()=>Rs,archiveTrackForMergedPr:()=>Sr,augmentFirstPromptAfterHandoff:()=>i$,buildAgentCmd:()=>ey,canResumeSubtask:()=>$$,cancelQueued:()=>ul,cancelQueuedToDraft:()=>ly,cancelStopJob:()=>Qa,cancelSubtask:()=>Kp,changedRepos:()=>vs,commitTransferOut:()=>Wp,createSetupTrack:()=>xy,createTrack:()=>Bp,deleteTrack:()=>Sy,demoteTransferredTracks:()=>Dy,ensureLeadEpochs:()=>Ao,forceMergeSubtask:()=>Fy,getHandoffJob:()=>ni,getReshapeJob:()=>dl,getSubtaskJob:()=>bq,getTrack:()=>me,getTransferOutJob:()=>Ny,healDeadLead:()=>al,interruptLead:()=>Np,isArchiving:()=>oq,isHandoffNoteEcho:()=>Vg,isInterruptEcho:()=>zg,isLeadStarting:()=>f$,isModelSwitchEcho:()=>Jg,leadCliDead:()=>Ty,listSubtaskJobs:()=>Ms,listTracks:()=>be,matchTranscriptsToBoxes:()=>d$,mergeNotifyMessage:()=>T$,navigatePrompt:()=>iy,notifyTrackForMergedPr:()=>Ar,notifyTrackForResolvedPr:()=>jp,offerFollowupsForMergedPr:()=>Os,pendingOffers:()=>oy,queueGauges:()=>cy,queueShipCompleteNotify:()=>dy,queuedMessages:()=>_s,rawTrack:()=>ry,reapArchivedOrphans:()=>$y,reconcileDemotedTracks:()=>Iy,reconcileHandoffOutcome:()=>E$,reconcileInterruptedHandoffs:()=>My,reconcileInterruptedReshapes:()=>by,reconcileInterruptedSubtasks:()=>jy,reconcileNodeUpdateNotices:()=>ny,reconcileSubtaskOutcome:()=>I$,refreshStandingInstructions:()=>ky,refuseIfTransferred:()=>Hp,rehydrateLead:()=>ti,removeTrackRepo:()=>Dp,renameTrack:()=>_y,reposOf:()=>Ut,reshapeTrack:()=>yy,resolveTrackBox:()=>Mc,resolveTrackBoxByHookToken:()=>ll,restartLead:()=>Fp,resumeAgentCmd:()=>$p,reviveLeadForSession:()=>Ey,sayToLead:()=>Jt,sendOptionsForAgent:()=>Hn,setTrackDraft:()=>my,setTrackModel:()=>hy,startLeadHandoff:()=>Up,startSpinUpView:()=>py,startSubtask:()=>Ly,startTransferOut:()=>Py,stopJobActive:()=>w$,subtaskMessages:()=>sy,trackMessages:()=>Op,unarchiveTrack:()=>Lp});import z from"node:fs/promises";import V from"node:path";import{randomUUID as Ep,createHash as nW}from"node:crypto";import{execFile as rW}from"node:child_process";import{promisify as oW}from"node:util";function ey(e,{skipPermissions:t=!0,model:n=null}={}){let r=Ip[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=Vh(e).find(i=>i.alias===n);if(!a)throw new Error(`unknown ${e} launch model "${n}"`);s+=` --model ${a.alias}`}return s}function $p(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=Ip[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&&!kn(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 $s(e){let t=Ip[e];if(t)return process.env[t.envVar]||t.base}async function Rp({maxAgeMs:e=6e4}={}){if(So.value&&Date.now()-So.at<e)return So.value;let t=So.value,n=await Promise.all(cl.map(async r=>{let o=await Eu($s(r),{retries:[]});return o===sa?[r,t?.[r]??!0]:[r,o===Tu]}));return So.value=Object.fromEntries(n),So.at=Date.now(),So.value}async function Za(e,t=!0,n=null){let r=ey(e,{skipPermissions:t,model:n});if(await Eu(r)===oa){So.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.":ym[o]?`Install it on this node: ${ym[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 i$(e,t=To){return`${e.trim()}
|
|
220
220
|
|
|
221
|
-
(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
|
|
222
|
-
`;return Hg=Hg.then(()=>z.mkdir(rr,{recursive:!0})).then(()=>z.appendFile(tW,n)).catch(()=>{}),Hg}async function a$(e,{box:t,agent:n,message:r}){let o=fe(await Q(),e);!o||o.archivedAt||(Ae(o,"startup-gate",{box:t,agent:n??null,message:r}),await ae(),mc(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 z.readFile(Wg,"utf8"))}catch{}if(d?.latest===t&&d?.current===e)return{announced:0,deduped:!0}}let l=await Q(),u=0;for(let d of l.tracks)Fe(d)||!d.leadBox||(Ae(d,"node-update",{status:s,current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},...a?{policy:a}:{},...i?{error:String(i).slice(0,400)}:{},...c?{reason:c}:{},required:o===!0}),u++);if(await ae().catch(()=>{}),s==="available")try{await z.mkdir(V.dirname(Wg),{recursive:!0}),await z.writeFile(Wg,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function ny({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await Q(),o=0,s=new Set;for(let a of r.tracks){if(a.archivedAt||a.transferredTo||!a.leadBox)continue;let i=new Map;for(let c of a.events||[])c.type==="node-update"&&c.latest&&i.set(c.latest,c);for(let[c,l]of i)["done","failed","rolled-back"].includes(l.status)||iW(e,c)||(Ae(a,"node-update",{status:"done",current:l.current??null,latest:c,...t?{installKind:t}:{},...n?{policy:n}:{},reason:"version-reconciled",installed:e,required:!1}),o++,s.add(c))}return o&&await ae().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function iW(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 i$(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=>cW.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 c$(e,t,n,{kind:r}={}){let o=await xe(t);if(!o)throw new Error(`box "${t}" is not running`);let s=$s(o.agent),a=Nc({text:n,producer:Cc({kind:r,orchestration:!0}),kind:r||"orchestration",provider:o.agent}),i=fe(await Q(),e)||null,c=Date.now(),l=null;try{l=await pr({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s})}catch(g){throw Dc(a,{ms:Date.now()-c,outcome:g?.code==="cli-not-running"?"cli-not-running":"blocked"}),await En(i,a,Da(g)),g}Dc(a,{ms:Date.now()-c,outcome:l||"up"});let u=Un(o.agent,n);a.paste=!!u.paste;try{await it(t,n,{...u,kind:r||"orchestration"})}catch(g){throw await En(i,a,Da(g)),g}let d={},p=await ac({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration",stats:d});a.verify=d,await En(i,a,p?d.verdict==="unverified"?"unverified":"delivered":"unsubmitted"),await oe({flow:"orchestration",phase:"sent",submitted:p,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let f=await Q(),w=fe(f,e);if(!w)return;let m=w.boxes.find(g=>g.name===t);Ae(w,"orchestration-prompt",{box:t,agent:m?.agent||o.agent,boxRole:m?.role,prompt:n,kind:r||"orchestration",deliveryId:a.id}),await ae()}async function lW(e){let t=V.join(Qs(e),`${e.id}.ndjson`),n;try{n=await z.stat(t)}catch{return null}let r=YI.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 YI.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function uW(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:pc(n.name,n.agent)}async function Vt(e,t=null,n=null){let r=t??new Map((await yn()).map(S=>[S.name,S])),o=!!e.transferredTo,s=e.boxes.map(S=>{let $=o?null:r.get(S.name),M=Pr.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([lW(e),uW(e,s),Ig(e),n??An().catch(()=>[]),KI({...e,boxes:s})]),p=sl.has(e.id),f=(u||[]).filter(S=>S.trackId===e.id&&!["merged","closed","lost"].includes(S.state)),w=JI(e.id),m=w&&{status:"waiting",since:w.startedAt,summary:w.summary??null,count:w.count??null}||BI(f),g=s.find(S=>S.name===e.leadBox)?.state==="live",b=g?_s(e.id).filter(S=>S.kind!=="ship-notify"&&S.kind!=="prompt-answer"):[],y=d||(g&&l.runningJobs?{source:"jobs",count:l.runningJobs,since:l.runningJobsSince,summary:l.runningJobs===1?"a background job is still running":`${l.runningJobs} background jobs are still running`}:b.length?{source:"queue",count:b.length,since:Math.min(...b.map(S=>S.ts||Date.now())),summary:"a queued message is about to send"}:null),x=LI({archivedAt:e.archivedAt,archiving:p,prompted:c,openActions:l.openActions,openActionSince:l.openActionSince,prDecisions:jI(f),operation:m,phase:l.phase,working:i,workingSince:l.currentTurnAt,pending:y,completedTurnAt:l.completedTurnAt||a,live:g});return{...e,boxes:s,lastResponseAt:a,working:i,prompted:c,archiving:p,status:x}}function l$(e,t,n){let r=[...e].sort((l,u)=>l.createdAt-u.createdAt),o=[...t].sort((l,u)=>l.birthMs-u.birthMs),s=new Set,a=[];for(let l of r)l.sessionId&&n(l.sessionId)&&!s.has(l.sessionId)?s.add(l.sessionId):a.push(l);let i=new Map(r.map((l,u)=>[l,u])),c=[];for(let l of a){if(l.sessionId&&n(l.sessionId)){c.push({box:l.name,sessionId:null});continue}if(!l.sessionId||l.pinned)continue;let u=i.get(l),d=l.createdAt-dW,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 u$(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=ax(r),i=new Set(a.map(p=>p.id)),c=s.every(p=>p&&i.has(p)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(p=>p.box),d=l$(u,a,p=>i.has(p));for(let p of d){let f=u.find(w=>w.name===p.box);f&&(f.sessionId=p.sessionId),await J0(p.box,p.sessionId),n=!0}}return n}async function be(){let e=await Q();await u$(e)&&await ae();let t=new Map((await yn()).map(r=>[r.name,r])),n=await An().catch(()=>[]);return Promise.all(e.tracks.map(r=>Vt(r,t,n)))}async function Mc(e,t){let n=await Q();if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===e);if(o)return{track:r,box:o}}if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===t);if(o)return{track:r,box:o}}return null}async function ll(e){let t=Fo(e);if(!t)return null;let n=await Q();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Mk(o.hookTokenHash,t))return{track:r,box:o};return null}async function ry(e){return fe(await Q(),e)??null}async function me(e){let t=await Q(),n=fe(t,e);return n?(await u$(t)&&await ae(),Vt(n)):null}async function oy(e){let t=await K(e).catch(()=>[]),n;try{n=Hn(t)}catch{return[]}let r=Vu(n).map(s=>({kind:s.kind,id:s.offerId??s.proposalId??s.id,ts:s.ts??null})),o=[];try{o=kv(t)}catch{o=[]}return r.concat(o.map(s=>({kind:"pr-open",id:`${s.repo??""}#${s.number}`,ts:null})))}function Jg(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(pW[e.agent]||[]).some(n=>n.test(t))}function Yg(e){let t=(e.text||"").trim();return t?fW.some(n=>n.test(t)):!1}function d$(e,t,n=Date.now()){return!e||e.readyAt||e.state!=="live"?!1:n-(t||0)<mW}async function Op(e,t={}){let n=await me(e);if(!n)return null;let r=new Set([...n.events.filter(R=>R.type==="orchestration-prompt"&&R.prompt).map(R=>String(R.prompt).trim()),...n.events.filter(R=>R.type==="said"&&R.text).map(R=>String(R.text).trim())]),o=ni(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=Xw(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...w_(n),...l],p={windows:[..._S(n),...IS(n,sd),...a,...u]},w=(await Promise.all(n.boxes.map(R=>kh(R,t)))).flat(),m=w.map(R=>{let ie={kind:"message",...R};if(ie.role==="assistant"&&(ie.text||"").trim()&&ie.tools?.length){let De=ro(ie.agent).cliTools,Ot=ie.tools.filter(Z=>!De.has(Z));return{...ie,tools:Ot.length?Ot:void 0}}return ie}).map(R=>{if(R.role!=="user")return R;let ie=cv(R);return ie?ie.kind==="suppress"?null:{...R,note:ie,text:uv(ie)}:R}).filter(Boolean).filter(R=>!(R.role==="user"&&r.has((R.text||"").trim()))).filter(R=>!Jg(R)).filter(R=>!Yg(R)).filter(R=>!zg(R)).filter(R=>!kg(R)).filter(R=>R.role!=="assistant"||!!(R.text||"").trim()||!!R.images?.length).filter(R=>!$S(R,p)),g=m.filter(R=>R.failed&&R.text===Yo);if(g.length){let R=new Map;for(let ie of g){if(!R.has(ie.box)){let Z=[...(await Ne(ie.box,{lines:Ve}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(ke=>ke[1]);R.set(ie.box,Z.length?Z[Z.length-1]:null)}let De=R.get(ie.box);De&&(ie.text=`\u25A0 ${De}`)}}let b=await Promise.all(n.boxes.map(R=>aS(R,t)));await qE(n,b.flat());let{agentTurnBoxes:y}=await HE(n,m);for(let R of n.boxes)y.has(R.name)&&R.cwd&&fu(R.cwd);await IA(n),await v_(n,m).catch(()=>{});for(let R of n.boxes)R.agent==="codex"&&R.state==="live"&&await KE(n,R),R.agent==="cursor"&&R.state==="live"&&await XE(n,R);let x=await Promise.all(n.boxes.map(R=>sS(R,t)));await WE(n,x.flat().filter(R=>!qg(R.box,R.ts,u)));let S=await K(n),$=c&&i.viewId?i.viewId:null,M=UA(S),D=Hn(S).filter(R=>!(R.kind==="message"&&(Jg(R)||Yg(R)||zg(R)||kg(R)))&&!(R.kind==="view-card"&&$&&R.viewId===$)),j=HA(D,M),A=D.filter(R=>!(R.kind==="message"&&R.role==="assistant"&&!j.has(R)&&qg(R.box,R.ts,M))),_=th(S,A).map(R=>({kind:"command-batch",id:R.id,ts:R.pending?R.ts:(R.anchorTs??R.ts)-1,anchorTs:R.anchorTs,anchorBox:R.anchorBox,agent:R.agent,boxRole:R.boxRole,commands:R.commands,pending:R.pending})).filter(R=>!qg(R.anchorBox,R.anchorTs??R.ts,u)),L=yv(bv([...A,..._].filter(R=>R.ts!=null).sort((R,ie)=>R.ts-ie.ts))),G=hW(n),O=G.size?L.filter(R=>R.kind==="event"||!G.has(R.box??R.anchorBox)):L,T=n.boxes.find(R=>R.name===n.leadBox)||null,C=T?await Vi(T.cwd,T.sessionId,{fallbackNewest:!1}):null;if(!C&&T){let R=[...S].reverse().find(ie=>ie.type==="context.usage"&&ie.boxId===T.name);R?.payload?.window&&(C={pct:R.payload.pct??(R.payload.occupancy!=null?R.payload.occupancy/R.payload.window:null),occupancy:R.payload.occupancy??null,window:R.payload.window,windowKnown:!0,model:R.payload.model||null})}let P=[...w].reverse().find(R=>R.role==="assistant"&&R.model),W=P?.model||null,X=[...n.events||[]].reverse().find(R=>R.type==="model-change"),ge=C?.model||W;X&&(!P?.ts||X.at>=P.ts)&&(ge=X.to);let Ie=T?await Ne(T.name,{lines:Ve}).catch(()=>""):"",Pe=T&&to(T.agent,Ie)?await bn(T.name).catch(()=>Ie):Ie,ee=T?Wn(T,Pe):null;if(ee&&T){let R=Yu(T,ee,Pe);if(R){let ie=Lv(T.name,ee.promptId);ie.action!=="give-up"&&(ie.action==="send"&&(Ge(T.name,R.keys,{kind:"auto-reject"}).catch(()=>{}),ie.first&&N(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{ts:Date.now(),promptId:ee.promptId,rule:R.id}}).catch(()=>{})),ee=null)}}!ee&&T&&!fa(T.agent,Ie)&&!_s(n.id).length&&(ee=Zm(O,{leadBoxId:T.name,provider:T.agent,isAnswered:R=>S.some(ie=>ie.type==="prompt.answered"&&ie.payload?.promptId===R)})),ee&&$A(n,T,ee);let Ze=O.filter(R=>!(R.kind==="message"&&R.role==="assistant"&&R.ask&&!String(R.text||"").trim()&&!R.images?.length)),le=!!T&&Xu(T.name,T.agent,Ie)&&!ee;if(T&&!T.readyAt&&(le||ee||Ie&&Zr(T.agent,Ie))){T.readyAt=Date.now();let R=fe(await Q(),n.id)?.boxes?.find(ie=>ie.name===T.name);R&&!R.readyAt&&(R.readyAt=T.readyAt,await ae())}let re=d$(T,n.createdAt);if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&T&&Zr(T.agent,Ie)&&!ee){let R=await Q(),ie=fe(R,n.id);ie?.setup&&!ie.setup.connectedAt&&(ie.setup.connectedAt=Date.now(),n.setup.connectedAt=ie.setup.connectedAt,await N(ie,{type:"provider.connected",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{provider:T.agent,ts:ie.setup.connectedAt}}).catch(()=>{}),await ae())}let $e=s?{running:!0,step:o.step,toAgent:o.toAgent||null,wait:o.wait||null}:null,J=dl(n.id),he=J&&!J.finishedAt?{running:!0,step:J.step||null,added:J.added||[],removed:J.removed||[]}:null,Be=null;if(c){let R=Date.now()-i.startedAt;if(i.viewId&&!le){let ie=op(n.id);ie&&(Ae(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:ie.finishedAt}),await ae())}else if(ee||R>m_&&!le&&!i.viewId||R>Yw){let ie=ee?"needs-input":R>Yw?"timeout":"no-view";op(n.id,{error:ie})&&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:ie}}).catch(()=>{})}else Be={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:Ze,context:C?{pct:C.pct,occupancy:C.occupancy,window:C.window,windowKnown:C.windowKnown,model:C.model}:null,models:{current:ge,options:zh(T?.agent)},pendingPrompt:ee,working:le,stopping:f$(n.id),leadStarting:re,handoff:$e,reshape:he,view:Be,queued:_s(n.id).filter(R=>R.kind!=="ship-notify"&&R.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function hW(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=Ms(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=th(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:Ve}).catch(()=>"");u=Xu(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}function wW(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>XI)throw new Error(`custom answer is too long (max ${XI} characters)`);return r})}function gW(e,t){let n=String(e||"").replace(/\s+/g," ").trim(),r=String(t||"").replace(/\s+/g," ").trim();return!n||!r?!1:n===r||r.startsWith(n)||n.startsWith(r.slice(0,24))}async function Mp(e,t={}){let n=Xc.get(e);n&&await n.catch(()=>{});let r=yW(e,t);Xc.set(e,r);try{return await r}finally{Xc.get(e)===r&&Xc.delete(e)}}async function yW(e,{promptId:t,answers:n,text:r,custom:o}={}){let s=fe(await Q(),e);if(!s)throw new Error(`track "${e}" not found`);Qa(e,"prompt-answer");let a=s.boxes.find(b=>b.name===s.leadBox);if(!a)throw new Error(`track "${e}" has no lead box`);if(!await xe(s.leadBox))throw new Error(`lead box "${s.leadBox}" is not running`);let c=await ch(a);if(c||(c=await bW(s,a)),!c)throw new Error("no pending prompt to answer");if(t&&t!==c.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let l=wW(o,c.questions?.length??1);if(l.some(Boolean)&&!c.custom)throw new Error("custom answer isn't available for this prompt \u2014 pick one of its options");let u=(n||[]).some(b=>Array.isArray(b)&&b.length>0);if(c.custom&&!u&&!l.some(Boolean)&&c.answerMode!=="text")throw new Error("no answer selected \u2014 pick an option or type your own");l.some(Boolean)&&(n=(c.questions||[]).map((b,y)=>l[y]&&!b.multiSelect?[]:Array.isArray(n?.[y])?n[y]:[]));let d;if(c.answerMode==="text"){let b=typeof r=="string"?r.trim():"";if(!b)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(b)||b.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await it(s.leadBox,b,{enter:!0,kind:"answer-prompt"}),d={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(c.answerMode==="esc")await Ge(s.leadBox,["Escape"],{kind:"answer-prompt"}),d={mode:"keys",value:"Escape",count:1};else if(c.answerMode==="say"){let b=mv(c,n||[],typeof r=="string"?r:"",l);if(!b)throw new Error("no answer selected \u2014 pick an option or reply in chat");await zt(s.id,b,{kind:"prompt-answer"}),d={mode:"say",value:b,count:1}}else if(l[0]&&c.provider==="claude"){let b=Pv(c,n||[],l[0]);await bm(s.leadBox,[...b.pre,{text:b.text}],{kind:"answer-prompt"});let y=!1;for(let S of[150,300,600]){await Dr(S);let $=await bn(s.leadBox).catch(()=>""),M=Wn(a,$);if(M?.customText&&gW(M.customText,b.text)){y=!0;break}}if(!y)throw new Error("custom answer didn't take \u2014 the widget moved; reload and answer again");await bm(s.leadBox,b.post,{kind:"answer-prompt"});let x=S=>S.map($=>$.key).join(" ");d={mode:"keys+text",value:`${x(b.pre)} \u2039text\u203A ${x(b.post)}`.trim(),count:b.pre.length+1+b.post.length}}else if(c.provider==="claude"||c.answerMode==="keys"){let b=lh(c,n||[]);await Ge(s.leadBox,b,{kind:"answer-prompt"}),d={mode:"keys",value:b.join(" "),count:b.length}}else{let b=Nx(c,n||[]);await it(s.leadBox,b,{enter:!0,kind:"answer-prompt"}),d={mode:"text",value:b,count:1}}if(c.answerMode==="say"){let b=c.questions||[];for(let y=0;y<b.length;y++){let x=b[y],S=new Set(Array.isArray(n?.[y])?n[y]:[]),$=[...S].map(D=>x.options?.[D]).filter(Boolean).map(D=>({label:D.label,description:D.description||""}));l[y]&&$.push({label:l[y],description:"",custom:!0});let M=(x.options||[]).map((D,j)=>S.has(j)?null:D?.label).filter(Boolean);Jh(s,a,c,{...d,groupId:c.groupId||c.promptId,questionIndex:y,questionTotal:c.questionTotal??b.length,header:x.header||"",question:x.question||"",multiSelect:!!x.multiSelect,chosen:$,rejected:M})}return await oe({flow:"prompt",phase:"answered",sessionName:s.leadBox,agent:a.agent,provider:c.provider,promptId:c.promptId,...d}).catch(()=>{}),{ok:!0,promptId:c.promptId,sent:d.count}}let p=c.questions?.[0]||{},f=new Set(Array.isArray(n?.[0])?n[0]:[]),w=[...f].map(b=>p.options?.[b]).filter(Boolean).map(b=>({label:b.label,description:b.description||""}));l[0]&&w.push({label:l[0],description:"",custom:!0});let m=(p.options||[]).map((b,y)=>f.has(y)?null:b?.label).filter(Boolean),g=c.review?"review":/^claude:model-confirm:/.test(c.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(c.promptId||"")?"resume-confirm":void 0;return d={...d,groupId:c.groupId||c.promptId,questionIndex:c.questionIndex??0,questionTotal:c.questionTotal??1,header:p.header||"",question:p.question||"",multiSelect:!!p.multiSelect,chosen:w,rejected:m,...g?{control:g}:{}},await oe({flow:"prompt",phase:"answered",sessionName:s.leadBox,agent:a.agent,provider:c.provider,promptId:c.promptId,...d}).catch(()=>{}),Jh(s,a,c,d),{ok:!0,promptId:c.promptId,sent:d.count}}async function bW(e,t){if(!t)return null;let n=await K(e).catch(()=>[]);return Zm(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 Q(),e);if(!r)throw new Error(`track "${e}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(!await xe(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await ch(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=Dv(a,n);return c.length?(await Ge(r.leadBox,c,{kind:"navigate-prompt"}),await oe({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 Cp(e){let t=Ja.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Ja.set(e,t)),t}function _s(e){return(Ja.get(e)?.items??[]).map(({delivery:t,...n})=>({...n}))}function iy(e=Date.now()){let t=0,n=0;for(let r of Ja.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 xW(e,t,n){let r=t.filter(o=>o.delivery&&!o.delivery.settled);r.length&&(async()=>{let o=fe(await Q(),e)||null;for(let s of r)await En(o,s.delivery,`dropped:${n}`)})().catch(()=>{})}function ul(e,t="cancel"){let n=Ja.get(e);if(!n)return[];let r=n.items.splice(0);return n.idleSince=null,n.timer&&(clearInterval(n.timer),n.timer=null),xW(e,r,t),r}async function cy(e){let t=ul(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=fe(await Q(),e);if(o)return r=Ya(o,n),await ae(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function ly(e){let t=Cp(e);t.timer||(t.timer=setInterval(()=>vW(e).catch(()=>{}),kW))}async function vW(e){let t=Ja.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 Q(),e);if(!n||n.archivedAt){ul(e,"archived");return}let r=async(l,u="")=>{let d=[];for(let g of t.items)s_(g.delivery,l),_I(g,l)&&d.push(g);if(!d.length)return;t.items=t.items.filter(g=>!d.includes(g));let p=Date.now()-Math.min(...d.map(g=>g.heldSince||Date.now())),f=Math.max(1,Math.round(vg()/6e4)),w=String(u||"").split(`
|
|
223
|
-
`).map(
|
|
224
|
-
`),m=l==="noSession"?`The scheduled prompt waited ${f} min for the box's session to come back and was dropped \u2014 open the terminal.`:`The scheduled prompt waited ${f} min for the box to become idle and was dropped \u2014 open the terminal.`;for(let
|
|
221
|
+
(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 Vg(e){return e.role==="user"&&iW.test((e.text||"").trim())}async function ee(){return Lt||(Fg||=(async()=>{try{Lt=JSON.parse(await z.readFile(a$,"utf8"))}catch{Lt={tracks:[]}}finally{Fg=null}return Lt})(),Fg)}async function ae(){await Pt(a$,Lt)}function uW(e,t){let n=JSON.stringify({trackId:e,...t})+`
|
|
222
|
+
`;return Ug=Ug.then(()=>z.mkdir(rr,{recursive:!0})).then(()=>z.appendFile(sW,n)).catch(()=>{}),Ug}async function c$(e,{box:t,agent:n,message:r}){let o=fe(await ee(),e);!o||o.archivedAt||(Ae(o,"startup-gate",{box:t,agent:n??null,message:r}),await ae(),mc(o,{provider:n??null,cls:"startup-gate",note:r}).catch(()=>{}))}async function sr({current:e,latest:t,min:n=null,installKind:r=null,required:o=!1,status:s="available",policy:a=null,error:i=null,reason:c=null}={}){if(!t)return{announced:0};if(s==="available"){let d=null;try{d=JSON.parse(await z.readFile(Hg,"utf8"))}catch{}if(d?.latest===t&&d?.current===e)return{announced:0,deduped:!0}}let l=await ee(),u=0;for(let d of l.tracks)Fe(d)||!d.leadBox||(Ae(d,"node-update",{status:s,current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},...a?{policy:a}:{},...i?{error:String(i).slice(0,400)}:{},...c?{reason:c}:{},required:o===!0}),u++);if(await ae().catch(()=>{}),s==="available")try{await z.mkdir(V.dirname(Hg),{recursive:!0}),await z.writeFile(Hg,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function ny({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await ee(),o=0,s=new Set;for(let a of r.tracks){if(a.archivedAt||a.transferredTo||!a.leadBox)continue;let i=new Map;for(let c of a.events||[])c.type==="node-update"&&c.latest&&i.set(c.latest,c);for(let[c,l]of i)["done","failed","rolled-back"].includes(l.status)||dW(e,c)||(Ae(a,"node-update",{status:"done",current:l.current??null,latest:c,...t?{installKind:t}:{},...n?{policy:n}:{},reason:"version-reconciled",installed:e,required:!1}),o++,s.add(c))}return o&&await ae().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function dW(e,t){let n=String(e).split(".").map(Number),r=String(t).split(".").map(Number);if(n.some(Number.isNaN)||r.some(Number.isNaN))return!0;for(let o=0;o<Math.max(n.length,r.length);o++){let s=(n[o]||0)-(r[o]||0);if(s)return s<0}return!1}function l$(e){if(!e)return null;let t=typeof e=="string"?[e]:Array.isArray(e?.codes)&&e.codes.length?e.codes:e?.code?[e.code]:[],n=[...new Set(t.filter(s=>pW.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 u$(e,t,n,{kind:r}={}){let o=await xe(t);if(!o)throw new Error(`box "${t}" is not running`);let s=$s(o.agent),a=Nc({text:n,producer:Cc({kind:r,orchestration:!0}),kind:r||"orchestration",provider:o.agent}),i=fe(await ee(),e)||null,c=Date.now(),l=null;try{l=await fr({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s})}catch(y){throw Dc(a,{ms:Date.now()-c,outcome:y?.code==="cli-not-running"?"cli-not-running":"blocked"}),await _n(i,a,Da(y)),y}Dc(a,{ms:Date.now()-c,outcome:l||"up"});let u=Hn(o.agent,n);a.paste=!!u.paste;try{await it(t,n,{...u,kind:r||"orchestration"})}catch(y){throw await _n(i,a,Da(y)),y}let d={},p=await ac({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration",stats:d});a.verify=d,await _n(i,a,p?d.verdict==="unverified"?"unverified":"delivered":"unsubmitted"),await oe({flow:"orchestration",phase:"sent",submitted:p,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let f=await ee(),w=fe(f,e);if(!w)return;let m=w.boxes.find(y=>y.name===t);Ae(w,"orchestration-prompt",{box:t,agent:m?.agent||o.agent,boxRole:m?.role,prompt:n,kind:r||"orchestration",deliveryId:a.id}),await ae()}async function fW(e){let t=V.join(Qs(e),`${e.id}.ndjson`),n;try{n=await z.stat(t)}catch{return null}let r=ZI.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 ZI.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function mW(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:pc(n.name,n.agent)}async function zt(e,t=null,n=null){let r=t??new Map((await bn()).map(S=>[S.name,S])),o=!!e.transferredTo,s=e.boxes.map(S=>{let $=o?null:r.get(S.name),M=Dr.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([fW(e),mW(e,s),_g(e),n??Tn().catch(()=>[]),zI({...e,boxes:s})]),p=sl.has(e.id),f=(u||[]).filter(S=>S.trackId===e.id&&!["merged","closed","lost"].includes(S.state)),w=XI(e.id),m=w&&{status:"waiting",since:w.startedAt,summary:w.summary??null,count:w.count??null}||LI(f),y=s.find(S=>S.name===e.leadBox)?.state==="live",k=y?_s(e.id).filter(S=>S.kind!=="ship-notify"&&S.kind!=="prompt-answer"):[],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=UI({archivedAt:e.archivedAt,archiving:p,prompted:c,openActions:l.openActions,openActionSince:l.openActionSince,prDecisions:FI(f),operation:m,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 d$(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-hW,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 p$(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=ix(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=d$(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 Y0(p.box,p.sessionId),n=!0}}return n}async function be(){let e=await ee();await p$(e)&&await ae();let t=new Map((await bn()).map(r=>[r.name,r])),n=await Tn().catch(()=>[]);return Promise.all(e.tracks.map(r=>zt(r,t,n)))}async function Mc(e,t){let n=await ee();if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===e);if(o)return{track:r,box:o}}if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===t);if(o)return{track:r,box:o}}return null}async function ll(e){let t=Fo(e);if(!t)return null;let n=await ee();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Ck(o.hookTokenHash,t))return{track:r,box:o};return null}async function ry(e){return fe(await ee(),e)??null}async function me(e){let t=await ee(),n=fe(t,e);return n?(await p$(t)&&await ae(),zt(n)):null}async function oy(e){let t=await K(e).catch(()=>[]),n;try{n=Wn(t)}catch{return[]}let r=Vu(n).map(s=>({kind:s.kind,id:s.offerId??s.proposalId??s.id,ts:s.ts??null})),o=[];try{o=xv(t)}catch{o=[]}return r.concat(o.map(s=>({kind:"pr-open",id:`${s.repo??""}#${s.number}`,ts:null})))}function zg(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(wW[e.agent]||[]).some(n=>n.test(t))}function Jg(e){let t=(e.text||"").trim();return t?gW.some(n=>n.test(t)):!1}function f$(e,t,n=Date.now()){return!e||e.readyAt||e.state!=="live"?!1:n-(t||0)<yW}async function Op(e,t={}){let n=await me(e);if(!n)return null;let r=new Set([...n.events.filter(R=>R.type==="orchestration-prompt"&&R.prompt).map(R=>String(R.prompt).trim()),...n.events.filter(R=>R.type==="said"&&R.text).map(R=>String(R.text).trim())]),o=ni(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=Yw(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...y_(n),...l],p={windows:[...$S(n),...RS(n,sd),...a,...u]},w=(await Promise.all(n.boxes.map(R=>bh(R,t)))).flat(),m=w.map(R=>{let ie={kind:"message",...R};if(ie.role==="assistant"&&(ie.text||"").trim()&&ie.tools?.length){let De=ro(ie.agent).cliTools,Ot=ie.tools.filter(Q=>!De.has(Q));return{...ie,tools:Ot.length?Ot:void 0}}return ie}).map(R=>{if(R.role!=="user")return R;let ie=lv(R);return ie?ie.kind==="suppress"?null:{...R,note:ie,text:dv(ie)}:R}).filter(Boolean).filter(R=>!(R.role==="user"&&r.has((R.text||"").trim()))).filter(R=>!zg(R)).filter(R=>!Jg(R)).filter(R=>!Vg(R)).filter(R=>!bg(R)).filter(R=>R.role!=="assistant"||!!(R.text||"").trim()||!!R.images?.length).filter(R=>!OS(R,p)),y=m.filter(R=>R.failed&&R.text===Yo);if(y.length){let R=new Map;for(let ie of y){if(!R.has(ie.box)){let Q=[...(await Pe(ie.box,{lines:Ve}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(ke=>ke[1]);R.set(ie.box,Q.length?Q[Q.length-1]:null)}let De=R.get(ie.box);De&&(ie.text=`\u25A0 ${De}`)}}let k=await Promise.all(n.boxes.map(R=>cS(R,t)));await KE(n,k.flat());let{agentTurnBoxes:g}=await qE(n,m);for(let R of n.boxes)g.has(R.name)&&R.cwd&&fu(R.cwd);await RA(n),await A_(n,m).catch(()=>{});for(let R of n.boxes)R.agent==="codex"&&R.state==="live"&&await zE(n,R),R.agent==="cursor"&&R.state==="live"&&await QE(n,R);let x=await Promise.all(n.boxes.map(R=>iS(R,t)));await GE(n,x.flat().filter(R=>!Wg(R.box,R.ts,u)));let S=await K(n),$=c&&i.viewId?i.viewId:null,M=WA(S),D=Wn(S).filter(R=>!(R.kind==="message"&&(zg(R)||Jg(R)||Vg(R)||bg(R)))&&!(R.kind==="view-card"&&$&&R.viewId===$)),j=qA(D,M),A=D.filter(R=>!(R.kind==="message"&&R.role==="assistant"&&!j.has(R)&&Wg(R.box,R.ts,M))),_=th(S,A).map(R=>({kind:"command-batch",id:R.id,ts:R.pending?R.ts:(R.anchorTs??R.ts)-1,anchorTs:R.anchorTs,anchorBox:R.anchorBox,agent:R.agent,boxRole:R.boxRole,commands:R.commands,pending:R.pending})).filter(R=>!Wg(R.anchorBox,R.anchorTs??R.ts,u)),L=bv(kv([...A,..._].filter(R=>R.ts!=null).sort((R,ie)=>R.ts-ie.ts))),G=bW(n),O=G.size?L.filter(R=>R.kind==="event"||!G.has(R.box??R.anchorBox)):L,T=n.boxes.find(R=>R.name===n.leadBox)||null,C=T?await Vi(T.cwd,T.sessionId,{fallbackNewest:!1}):null;if(!C&&T){let R=[...S].reverse().find(ie=>ie.type==="context.usage"&&ie.boxId===T.name);R?.payload?.window&&(C={pct:R.payload.pct??(R.payload.occupancy!=null?R.payload.occupancy/R.payload.window:null),occupancy:R.payload.occupancy??null,window:R.payload.window,windowKnown:!0,model:R.payload.model||null})}let P=[...w].reverse().find(R=>R.role==="assistant"&&R.model),W=P?.model||null,X=[...n.events||[]].reverse().find(R=>R.type==="model-change"),ge=C?.model||W;X&&(!P?.ts||X.at>=P.ts)&&(ge=X.to);let Ie=T?await Pe(T.name,{lines:Ve}).catch(()=>""):"",Ce=T&&to(T.agent,Ie)?await Gt(T.name).catch(()=>Ie):Ie,Z=T?vn(T,Ce):null;if(T&&ay(T.name,Z,Ce)&&Z&&(Z={...Z,answering:!0}),Z&&T){let R=Yu(T,Z,Ce);if(R){let ie=Uv(T.name,Z.promptId);ie.action!=="give-up"&&(ie.action==="send"&&(Ge(T.name,R.keys,{kind:"auto-reject"}).catch(()=>{}),ie.first&&N(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{ts:Date.now(),promptId:Z.promptId,rule:R.id}}).catch(()=>{})),Z=null)}}!Z&&T&&!fa(T.agent,Ie)&&!_s(n.id).length&&(Z=Zm(O,{leadBoxId:T.name,provider:T.agent,isAnswered:R=>S.some(ie=>ie.type==="prompt.answered"&&ie.payload?.promptId===R)})),Z&&OA(n,T,Z);let Ze=O.filter(R=>!(R.kind==="message"&&R.role==="assistant"&&R.ask&&!String(R.text||"").trim()&&!R.images?.length)),le=!!T&&Xu(T.name,T.agent,Ie)&&!Z;if(T&&!T.readyAt&&(le||Z||Ie&&Zr(T.agent,Ie))){T.readyAt=Date.now();let R=fe(await ee(),n.id)?.boxes?.find(ie=>ie.name===T.name);R&&!R.readyAt&&(R.readyAt=T.readyAt,await ae())}let re=f$(T,n.createdAt);if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&T&&Zr(T.agent,Ie)&&!Z){let R=await ee(),ie=fe(R,n.id);ie?.setup&&!ie.setup.connectedAt&&(ie.setup.connectedAt=Date.now(),n.setup.connectedAt=ie.setup.connectedAt,await N(ie,{type:"provider.connected",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{provider:T.agent,ts:ie.setup.connectedAt}}).catch(()=>{}),await ae())}let $e=s?{running:!0,step:o.step,toAgent:o.toAgent||null,wait:o.wait||null}:null,J=dl(n.id),he=J&&!J.finishedAt?{running:!0,step:J.step||null,added:J.added||[],removed:J.removed||[]}:null,Be=null;if(c){let R=Date.now()-i.startedAt;if(i.viewId&&!le){let ie=op(n.id);ie&&(Ae(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:ie.finishedAt}),await ae())}else if(Z||R>w_&&!le&&!i.viewId||R>Jw){let ie=Z?"needs-input":R>Jw?"timeout":"no-view";op(n.id,{error:ie})&&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:ie}}).catch(()=>{})}else Be={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:Ze,context:C?{pct:C.pct,occupancy:C.occupancy,window:C.window,windowKnown:C.windowKnown,model:C.model}:null,models:{current:ge,options:Vh(T?.agent)},pendingPrompt:Z,working:le,stopping:w$(n.id),leadStarting:re,handoff:$e,reshape:he,view:Be,queued:_s(n.id).filter(R=>R.kind!=="ship-notify"&&R.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function bW(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=Ms(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=Wn(s).filter(d=>d.box===o&&!(d.kind==="message"&&d.role==="assistant"&&d.ask&&!String(d.text||"").trim()&&!d.images?.length)),i=th(s.filter(d=>d.boxId===o),a.filter(d=>d.kind==="message")).map(d=>({kind:"command-batch",id:d.id,ts:d.pending?d.ts:(d.anchorTs??d.ts)-1,anchorTs:d.anchorTs,anchorBox:d.anchorBox,agent:d.agent,boxRole:d.boxRole,commands:d.commands,pending:d.pending})),c=[...a,...i].filter(d=>d.ts!=null).sort((d,p)=>d.ts-p.ts),l=n.boxes.find(d=>d.name===o)||null,u=!1;if(l&&l.state==="live"){let d=await Pe(o,{lines:Ve}).catch(()=>"");u=Xu(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}function kW(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>QI)throw new Error(`custom answer is too long (max ${QI} characters)`);return r})}function xW(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 ay(e,t,n,r=Date.now()){let o=Yg.get(e);if(!o)return!1;let s=!!t&&t.promptId===o.promptId&&r-o.at<vW&&(t.setup?!0:m$(n)===o.paneHash);return s||Yg.delete(e),s}async function AW(e,t,n){let r=n;for(let o of SW){await or(o);let s=await Gt(e).catch(()=>null);if(s!=null&&(r=s,s!==n))break}Yg.set(e,{promptId:t.promptId,paneHash:m$(r),setup:t.setup||null,at:Date.now()})}async function Mp(e,t={}){let n=Xc.get(e);n&&await n.catch(()=>{});let r=TW(e,t);Xc.set(e,r);try{return await r}finally{Xc.get(e)===r&&Xc.delete(e)}}async function TW(e,{promptId:t,answers:n,text:r,custom:o}={}){let s=fe(await ee(),e);if(!s)throw new Error(`track "${e}" not found`);Qa(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 xe(s.leadBox))throw new Error(`lead box "${s.leadBox}" is not running`);let c=await Gt(a.name).catch(()=>""),l=vn(a,c);if(l||(l=await EW(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"&&ay(a.name,l,c))throw new Error("prompt already answered \u2014 waiting for the CLI to move on");let u=kW(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 it(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 Ge(s.leadBox,["Escape"],{kind:"answer-prompt"}),p={mode:"keys",value:"Escape",count:1};else if(l.answerMode==="say"){let g=hv(l,n||[],typeof r=="string"?r:"",u);if(!g)throw new Error("no answer selected \u2014 pick an option or reply in chat");await Jt(s.id,g,{kind:"prompt-answer"}),p={mode:"say",value:g,count:1}}else if(u[0]&&l.provider==="claude"){let g=Bv(l,n||[],u[0]);await bm(s.leadBox,[...g.pre,{text:g.text}],{kind:"answer-prompt"});let x=!1;for(let $ of[150,300,600]){await or($);let M=await Gt(s.leadBox).catch(()=>""),D=vn(a,M);if(D?.customText&&xW(D.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 bm(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=ch(l,n||[]);await Ge(s.leadBox,g,{kind:"answer-prompt"}),p={mode:"keys",value:g.join(" "),count:g.length}}else{let g=Px(l,n||[]);await it(s.leadBox,g,{enter:!0,kind:"answer-prompt"}),p={mode:"text",value:g,count:1}}if(l.answerMode!=="say"&&await AW(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 D=(S.options||[]).map((j,A)=>$.has(A)?null:j?.label).filter(Boolean);zh(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:D})}return await oe({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]:[]),m=[...w].map(g=>f.options?.[g]).filter(Boolean).map(g=>({label:g.label,description:g.description||""}));u[0]&&m.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":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:m,rejected:y,...k?{control:k}:{}},await oe({flow:"prompt",phase:"answered",sessionName:s.leadBox,agent:a.agent,provider:l.provider,promptId:l.promptId,...p}).catch(()=>{}),zh(s,a,l,p),{ok:!0,promptId:l.promptId,sent:p.count}}async function EW(e,t){if(!t)return null;let n=await K(e).catch(()=>[]);return Zm(Wn(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function iy(e,{promptId:t,questionIndex:n}={}){let r=fe(await ee(),e);if(!r)throw new Error(`track "${e}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(!await xe(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Dv(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=jv(a,n);return c.length?(await Ge(r.leadBox,c,{kind:"navigate-prompt"}),await oe({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 Cp(e){let t=Ja.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Ja.set(e,t)),t}function _s(e){return(Ja.get(e)?.items??[]).map(({delivery:t,...n})=>({...n}))}function cy(e=Date.now()){let t=0,n=0;for(let r of Ja.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 IW(e,t,n){let r=t.filter(o=>o.delivery&&!o.delivery.settled);r.length&&(async()=>{let o=fe(await ee(),e)||null;for(let s of r)await _n(o,s.delivery,`dropped:${n}`)})().catch(()=>{})}function ul(e,t="cancel"){let n=Ja.get(e);if(!n)return[];let r=n.items.splice(0);return n.idleSince=null,n.timer&&(clearInterval(n.timer),n.timer=null),IW(e,r,t),r}async function ly(e){let t=ul(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=fe(await ee(),e);if(o)return r=Ya(o,n),await ae(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function uy(e){let t=Cp(e);t.timer||(t.timer=setInterval(()=>$W(e).catch(()=>{}),_W))}async function $W(e){let t=Ja.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 ee(),e);if(!n||n.archivedAt){ul(e,"archived");return}let r=async(l,u="")=>{let d=[];for(let y of t.items)i_(y.delivery,l),$I(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(xg()/6e4)),w=String(u||"").split(`
|
|
223
|
+
`).map(y=>y.trimEnd()).filter(y=>y.trim()).slice(-3).join(`
|
|
224
|
+
`),m=l==="noSession"?`The scheduled prompt waited ${f} min for the box's session to come back and was dropped \u2014 open the terminal.`:`The scheduled prompt waited ${f} min for the box to become idle and was dropped \u2014 open the terminal.`;for(let y of d)y.delivery&&await _n(n,y.delivery,l==="noSession"?"failed:not-running":"failed:blocked");Ae(n,"delivery-held",{box:n.leadBox,agent:o?.agent??null,reason:l,heldMs:p,producer:"loop",deliveryId:d[0]?.delivery?.id??null,message:m,...w?{detail:w}:{}}),await ae(),mc(n,{provider:o?.agent??null,cls:"delivery-held",note:m}).catch(()=>{})},o=n.boxes.find(l=>l.name===n.leadBox),s=ni(e);if(s&&!s.finishedAt&&s.step!=="done"&&!s.error){Es(t,!1),await r("handoff");return}let a=await xe(n.leadBox);if(a&&await Ty(a)&&(a=await al(n,o,"queue")?await xe(n.leadBox):null,await ae()),!a){Es(t,!1),await r("noSession");return}if(uo(o?.agent)){Es(t,!1),await r("cap");return}let i=await Pe(n.leadBox,{lines:Ve}).catch(()=>"");if(ec(o?.agent,i)){Es(t,!1),await r("busy",i);return}if(to(o?.agent,i)){let l=await Gt(n.leadBox).catch(()=>i);if(vn(o,l)){Es(t,!1),await r("prompt");return}}if(!Es(t,!0)){await r("grace");return}let c=t.items.shift();t.idleSince=null,c.delivery&&(c.delivery.dequeuedAt=Date.now());try{await Xg(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 _n(n,c.delivery,Da(l)),l;t.items.unshift(c),c.delivery&&(c.delivery.requeued+=1,c.delivery.dequeuedAt=null,await Pc(n,c.delivery,{position:1,reason:"requeue"})),await al(n,o,"queue-gate")}await ae()}finally{t.flushing=!1}}}async function h$(e,t,n,r){let o=uo(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(Ya(e,[n]),await ae()),mA(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 RW(e,t){let n=await Pe(e.leadBox,{lines:Ve}).catch(()=>"");if(!to(t?.agent,n))return!1;let r=await Gt(e.leadBox).catch(()=>n);return!!vn(t,r)}async function Xg(e,t,n,r,o=null,s=null,a=null){a||(a=Nc({text:r,producer:Cc({kind:o}),kind:o,provider:t?.agent??null,attachments:Array.isArray(s)?s.length:0})),a.provider??=t?.agent??null,await h$(e,t,r,o);let i=nl.get(e.id)||0,c=r,l=!1,u=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(c=i$(r,e.pendingHandoffHydrate.artifact||To),delete e.pendingHandoffHydrate,l=!0);let d=Array.isArray(s)?s.filter(m=>m&&m.rel):[];if(d.length){let m=II(d.map(y=>y.rel));c=c.trim()?`${c.trim()}
|
|
225
225
|
|
|
226
|
-
${m}`:m}let p=Ae(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,deliveryId:a.id,...o?{kind:o}:{},...d.length?{attachments:d}:{}});o||za(e,null);try{let m=Date.now(),
|
|
226
|
+
${m}`:m}let p=Ae(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,deliveryId:a.id,...o?{kind:o}:{},...d.length?{attachments:d}:{}});o||za(e,null);try{let m=Date.now(),y=null;try{y=await fr({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:$s(n.agent),bailOnDialog:!0})}catch(g){throw Dc(a,{ms:Date.now()-m,outcome:g?.code==="cli-not-running"?"cli-not-running":"blocked"}),g}if(Dc(a,{ms:Date.now()-m,outcome:y||"up"}),(nl.get(e.id)||0)!==i){!o&&r.trim()&&(Ya(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 RW(e,t)){let g=Cp(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),uy(e.id),await Pc(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=Hn(t?.agent,c);a.paste=!!k.paste,await it(e.leadBox,c,{...k,kind:l?"lead-handoff-first-prompt":"say"})}catch(m){if(u&&!e.pendingHandoffHydrate&&(e.pendingHandoffHydrate=u),Ae(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:p.at,text:r,...m?.interrupted?{reason:"interrupted"}:{},...m?.requeued?{reason:"requeued"}:{}}),m?.requeued)return{queued:!0,...m.requeued};throw m?.code!=="cli-not-running"&&await _n(e,a,Da(m)),m}let f={},w=await KW(e,t,c,f);a.verify=f,w?await _n(e,a,f.verdict==="unverified"?"unverified":"delivered"):(await g$(e.leadBox,t?.agent,c.length+8,c).catch(()=>{}),Ae(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:p.at,text:r,reason:"unsubmitted"}),o||Ya(e,[r]),await _n(e,a,"unsubmitted")),l&&await oe({flow:"lead-handoff",phase:"first-prompt",trackId:e.id,sessionName:e.leadBox,agent:t?.agent,userTextLen:r.length})}function dy(e,{actions:t}={}){let n=Cp(e),r=GA(t),o=Nc({text:r,producer:Cc({kind:"ship-notify"}),kind:"ship-notify"});n.items.push({id:o.id,text:r,ts:Date.now(),kind:"ship-notify",delivery:o}),uy(e),(async()=>{let s=fe(await ee(),e);s&&(o.provider=s.boxes.find(a=>a.name===s.leadBox)?.agent??null,await Pc(s,o,{position:n.items.length,reason:"queue"}))})().catch(()=>{})}async function OW(e,t,n,r){await Ge(e.leadBox,["Escape"],{kind:"dismiss-prompt"});let o="",s=!1;for(let c of[250,400,600,900]){if(await or(c),o=await Pe(e.leadBox,{lines:Ve}).catch(()=>""),!to(t?.agent,o)){s=!0;break}let l=await Gt(e.leadBox).catch(()=>o);if(!vn(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 oe({flow:"prompt",phase:"dismissed",sessionName:e.leadBox,agent:t?.agent,provider:n.provider,promptId:n.promptId,...i}).catch(()=>{}),s&&MA(e,t,n,i),{gone:s,pane:o}}async function Jt(e,t,{kind:n=null,attachments:r=null,producer:o=null,dismissPrompt:s=!1}={}){let a=Array.isArray(r)?r.filter(d=>d&&typeof d.rel=="string"):[];if((typeof t!="string"||!va(t))&&!a.length)throw new Error("text (string) or an attachment required");let i=fe(await ee(),e);if(!i)throw new Error(`track "${e}" not found`);if(i.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before driving it`);Hp(i);let c=i.boxes.find(d=>d.name===i.leadBox),l=va(t),u=Nc({text:l,producer:Cc({kind:n,producer:o}),kind:n,provider:c?.agent??null,attachments:a.length});try{return await MW(e,i,c,l,{kind:n,atts:a,d:u,dismissPrompt:s})}catch(d){throw await _n(i,u,Da(d)),d}}async function MW(e,t,n,r,{kind:o,atts:s,d:a,dismissPrompt:i=!1}){await h$(t,n,r,o),Qa(e,"say");let c=await xe(t.leadBox);if(c&&await Ty(c)?await al(t,n,"say")?(await ae(),c=await xe(t.leadBox)):c=null:!c&&await ti(t)&&(await ae(),c=await xe(t.leadBox)),!c)throw new Error(`lead box "${t.leadBox}" is not running`);let l=Xc.get(e);l&&await l.catch(()=>{});let u=Cp(e),d=await Pe(t.leadBox,{lines:Ve}).catch(()=>""),p=u.items.length>0?"queue":ec(n?.agent,d)?"busy":null,f=p!=null,w=i&&!o;if((!f||p==="queue"&&w)&&to(n?.agent,d)){let y=await Gt(t.leadBox).catch(()=>d),k=vn(n,y);if(f||(f=!!k,f&&(p="prompt")),k&&w&&k.dismissable){let g=await OW(t,n,k,a);g.gone&&p==="prompt"&&(d=g.pane,f=ec(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),uy(e),o||za(t,null),await ae(),await Pc(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 m;try{m=await Xg(t,n,c,r,o,s,a)}catch(y){if(y?.code!=="cli-not-running"||!await al(t,n,"say-gate")||(await ae(),c=await xe(t.leadBox),!c))throw y;m=await Xg(t,n,c,r,o,s,a)}return await ae(),m?.queued?{ok:!0,leadBox:t.leadBox,queued:!0,id:m.id,position:m.position,deliveryId:a.id}:{ok:!0,leadBox:t.leadBox,deliveryId:a.id}}async function py(e,{hint:t}={}){let n=fe(await ee(),e);if(!n)throw new Error(`track "${e}" not found`);if(n.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it first`);let r=Yw(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";g_(n.id,{name:s,box:n.leadBox});let a=String(t||"").trim()?`${e$}
|
|
227
227
|
|
|
228
|
-
What to preview: ${String(t).trim()}`:
|
|
228
|
+
What to preview: ${String(t).trim()}`:e$;try{await Jt(e,a,{kind:"spin-up-view"})}catch(i){throw op(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Np(e){let t=fe(await ee(),e);if(!t)throw new Error(`track "${e}" not found`);if(t.archivedAt)throw new Error(`track "${e}" is archived`);nl.set(e,(nl.get(e)||0)+1);let n=ul(e,"interrupt"),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=Va.get(e);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=Ya(t,r),await ae()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:t.draftRev||0}}let s=jW(e,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,or(CW)]);let a=fe(await ee(),e)||t;return{ok:!0,interrupted:s.result!=="no-session",stopped:s.result==="stopped"||s.result==="stopped-late",stopping:!s.result,dropped:n.length,restored:s.restored,draftRev:a.draftRev||0}}function w$(e){let t=Va.get(e);return!!t&&!t.canceled}function Qa(e,t){let n=Va.get(e);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=t||null)}function fy(e){Qa(e,"track-ended"),nl.delete(e),ul(e,"track-ended")}function jW(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}),Va.set(e,r),(async()=>{let s=fe(await ee(),e),a=s?.boxes.find(y=>y.name===s.leadBox),i=s?await xe(s.leadBox):null,c=!1,l=!1;if(i){e:for(let y=0;y<NW&&!r.canceled;y++){await Ge(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let k of PW){await or(k);let g=await Pe(s.leadBox,{lines:Ve}).catch(()=>"");if(!fa(a?.agent,g)){c=!0;break e}}}if(!c){l=!0;for(let y=0;y<BW&&!r.canceled&&(await or(DW),!r.canceled);y++){let k=await Pe(s.leadBox,{lines:Ve}).catch(()=>"");if(!fa(a?.agent,k)){c=!0;break}await Ge(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=fe(await ee(),e)||s,d=u?.boxes.find(y=>y.name===u.leadBox)||a,p=null,f=null;u&&d&&i&&c&&!r.canceled&&(p=await HW(u,d).catch(()=>null),p||(f=await GW(u,d).catch(()=>null)));let w=u&&i&&!r.canceled?await FW(u).catch(()=>null):null,m=[p||f,...r.queuedTexts].filter(Boolean);u&&m.length&&(r.restored=Ya(u,m),await ae()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await LW(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 oe({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"),Va.get(e)===r&&Va.delete(e),o()}),r}async function LW(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 FW(e){let t=ja(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 HW(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 UW){await or(r);let o=await Pe(e.leadBox,{lines:Ve}).catch(()=>""),s=rc(t.agent,o);if(s&&Fu(n.text,s))return await g$(e.leadBox,t.agent,n.text.length+8),Ae(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:n.at,text:n.text}),n.text}return null}async function GW(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>WW||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!Ue(r.text))return null;for(let s of qW){await or(s);let a;try{a=await bh(t,{})}catch{return null}if(Wx(a,r))return null;let i=await Pe(e.leadBox,{lines:Ve}).catch(()=>"");if(rc(t.agent,i))return null}return Ae(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}async function KW(e,t,n,r=null){return ac({sessionName:e.leadBox,agent:t?.agent,agentCmd:$s(t?.agent),text:n,trackId:e.id,flow:"say",stats:r})}async function g$(e,t,n,r=null){for(let o=0;o<3;o++){await aa(e,n,{kind:"unsend-wipe"}).catch(()=>{}),await or(200);let s=await Pe(e,{lines:Ve}).catch(()=>""),a=rc(t,s);if(!a||r&&!Fu(r,a)&&!(t==="codex"&&Wm(r,a)))return!0;n=a.length+8}return!1}function za(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 Ya(e,t){let n=[],r=o=>n.some(s=>Ue(s).includes(Ue(o)));for(let o of[e.draft?.text,...t])o&&o.trim()&&!r(o)&&n.push(o);return n.length?za(e,n.join(`
|
|
229
229
|
|
|
230
|
-
`),{source:"restore"})?.text??null:null}function
|
|
230
|
+
`),{source:"restore"})?.text??null:null}function zW(e,t){let n=Ue(t);if(!n)return!1;if(_s(e.id).some(o=>Ue(o.text)===n))return!0;let r=e.events||[];for(let o=r.length-1;o>=0;o--){let s=r[o];if(typeof s.at=="number"&&Date.now()-s.at>VW)break;if(!(s.type!=="said"||!s.text||Ue(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function my(e,t,{rev:n=null}={}){if(typeof t!="string")throw new Error("text (string) required");let r=fe(await ee(),e);if(!r)throw new Error(`track "${e}" not found`);let o=t,s=va(t);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&zW(r,s))return{ok:!0,draft:r.draft||null,rev:null,ignored:"already-sent"};if(typeof n=="number"&&n<(r.draftRev||0)&&!o.trim()&&r.draft?.text)return{ok:!0,draft:r.draft,rev:r.draftRev||0,ignored:"stale-clear"};if(typeof n=="number"&&n<(r.draftRev||0)&&r.draft?.source==="restore"){let i=r.draft.text;Ue(o).includes(Ue(i))?za(r,o):za(r,`${i}
|
|
231
231
|
|
|
232
|
-
${o}`,{source:"restore"})}else za(r,o);return await ae(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function my(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=fe(await Q(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await xe(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(d=>d.name===r.leadBox)?.agent||o.agent||"claude",i=vA(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await Vi(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await pr({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:$s(o.agent)});let u=await SA(a,r.leadBox,i);return Ae(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await ae(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function h$(e){for(let t of e){if(!t||!await Sa(t))continue;let n=await tA(t).catch(()=>null);if(!n)continue;if((await z.stat(V.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function Ut(e){let t=e;return t?t.kind==="multi"?Array.isArray(t.repos)?t.repos.filter(Boolean):[]:t.base?[{name:V.basename(t.base),dir:t.dir??null,base:t.base,baseRef:t.baseRef??null,repo:t.repo??null}]:[]:[]}async function vs(e){let t=[];for(let n of Ut(e?.workspace)){if(!n?.dir)continue;let r=await is(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await cs(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function hy(e){let t=e;typeof t=="string"&&!V.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await zs(t)??await su(t));let n=await h$([t]);if(!n){let r=typeof e=="string"&&V.isAbsolute(e)&&!await z.stat(e).catch(()=>null);throw new Error(`tagged repo "${e}" is not a usable base (${r?"that path does not exist on this node \u2014 pick one of this node's repos or use owner/name":"need a primary checkout or a synced owner/name"})`)}return n}async function HW(e,t,n,r){let o=`track/${Gp(t)}-${e}`,s=[];try{await z.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await hy(i);if(s.some(f=>f.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await Oe(c).catch(()=>null),u=V.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let f=2;a.has(u);f++)u=`${V.basename(c)}-${f}`;a.add(u);let d=V.join(n,u),p=await us(c);await ls(c,d,o,p),s.push({name:u,dir:d,base:c,baseRef:p,repo:l})}return await ei(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 z.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function Pp(e,t){let n=await Q(),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 hy(typeof t=="string"?t.trim():t);if((o.repos??[]).some(p=>p?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await Oe(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(p=>p?.name).filter(Boolean)]),c=V.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let p=2;i.has(c);p++)c=`${V.basename(s)}-${p}`;let l=V.join(o.dir,c),u=await us(s);await Lh(s,o.branch)?await wc(s,l,o.branch):await ls(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 ei(o.dir,o.repos,o.branch),Ae(r,"retagged",{action:"add",repo:a??s,name:c,workspace:wy(o)}),await ae(),d}async function Dp(e,t){let n=await Q(),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 Ft.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 is(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await cs(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 z.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await ei(o.dir,o.repos,o.branch),Ae(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:wy(o)}),await ae(),{removed:s.name}}async function vp(e,t){let n=await Q(),r=fe(n,e);r&&(r.reshape={...r.reshape||{},...t},await ae())}async function WW(e){let t=await z.readdir(e).catch(()=>[]);for(let n of t)if(!(n===".git"||n===".bostrat")){if(n==="AGENTS.md"){let r=await z.readFile(V.join(e,n),"utf8").catch(()=>"");if(Qc(r))continue}return!0}try{let{stdout:n}=await _p("git",["-C",e,"rev-list","--count","HEAD"]);if(Number(n.trim())>1)return!0}catch{}return!1}function QI(e,t,n){let r=V.basename(e);n.has(r)&&t&&(r=t.replace("/","-"));for(let o=2;n.has(r);o++)r=`${V.basename(e)}-${o}`;return n.add(r),r}async function w$(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 $n(l)&&!await $n(u)&&await z.rename(l,u)};if(n==="worktree"&&s){let l=V.join(r,s);await $n(l)&&(await c(V.join(r,".bostrat"),V.join(l,".bostrat")),await Sp(a,l,o))}if(n==="repo"&&i){let l=V.join(r,i);await $n(l)&&(await $n(o)&&await z.rm(o,{recursive:!0,force:!0}),await z.rename(l,o))}return await $n(o)?(await $n(r)&&(await c(V.join(r,".bostrat"),V.join(o,".bostrat")),await z.rm(r,{recursive:!0,force:!0})),n==="worktree"?await Sp(a,o,r):await z.rename(o,r),!0):!1}async function gy(e,t,{skipPermissions:n=!0}={}){let r=await Q(),o=fe(r,e);if(!o)throw xt(`track "${e}" not found`,404);if(o.archivedAt)throw xt("track is archived \u2014 unarchive it first");try{Hp(o)}catch(g){throw Object.assign(g,{status:409})}if(o.kind==="setup")throw xt("setup boxes have no repos to reshape");if(String(o.name||"").startsWith("\u21BB"))throw xt("this Track's repos are the loop's \u2014 propose-update the loop instead");let s=Zg.get(e);if(s&&!s.finishedAt)throw xt("a reshape is already running for this Track");let a=Is.get(e);if(a&&!a.finishedAt)throw xt("a lead handoff is running \u2014 retry when it settles");let i=Tp.get(e);if(i&&!i.finishedAt)throw xt("a node transfer is running \u2014 retry when it settles");if(kp.has(e))throw xt("an adopt is running for this Track \u2014 retry when it settles");for(let g of Ft.values())if(g.trackId===e&&!g.finishedAt)throw xt("a subtask is still running on this Track \u2014 wait for it to merge back before reshaping");let c=(Array.isArray(t)?t:[]).map(g=>typeof g=="string"?g.trim():"").filter(Boolean);if(!c.length)throw xt("repos (non-empty list of paths or owner/name slugs) required",400);let l=[];for(let g of c){let b;try{b=await hy(g)}catch(y){throw xt(y?.message||String(y),400)}if(l.some(y=>y.base===b))throw xt(`repo tagged twice: ${g}`,400);l.push({sel:g,base:b,slug:await Oe(b).catch(()=>null)})}let u=o.workspace;if(u?.kind==="multi"){let g=(u.repos??[]).filter(Boolean),b=g.filter(S=>!l.some($=>$.base===S.base)),y=l.filter(S=>!g.some($=>$.base===S.base));if(!b.length&&!y.length)return{trackId:e,step:"done",inPlace:!0,added:[],removed:[],startedAt:Date.now(),finishedAt:Date.now(),error:null};for(let S of b){let $=await is(S.dir,{ignorePlumbing:!0}).catch(()=>!1),M=await cs(S.dir,S.baseRef).catch(()=>0);if($||M>0)throw xt(`"${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 y){let $=await Pp(e,S.base);x.added.push($.repo??$.base)}for(let S of b)await Dp(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 xt(`can't reshape a "${u?.kind??"unknown"}" workspace`);let d=u.kind==="worktree"&&l.some(g=>g.base===u.base);if(u.kind==="worktree"&&!d){let g=await is(u.dir??o.cwd,{ignorePlumbing:!0}).catch(()=>!1),b=await cs(u.dir??o.cwd,u.baseRef).catch(()=>0);if(g||b>0)throw xt(`"${V.basename(u.base)}" has work (${g?"uncommitted changes":`${b} commit${b===1?"":"s"} ahead of ${u.baseRef??"base"}`}) \u2014 ship or discard it before swapping it out`)}Qa(e,"reshape");let p=o.boxes.find(g=>g.name===o.leadBox)||null;if(!p)throw xt("this Track has no lead box");let f=p.agent||"claude",w=await Za(f,n),m={trackId:e,step:"checkpoint",inPlace:!1,startedAt:Date.now(),finishedAt:null,error:null,added:l.filter(g=>!(u.kind==="worktree"&&g.base===u.base)).map(g=>g.slug??g.base),removed:u.kind==="worktree"&&!d?[u.repo??u.base]:[],scratch:!1,views:0,checkpoint:null};return Zg.set(e,m),await vp(e,{step:m.step,startedAt:m.startedAt,finishedAt:null,error:null,added:m.added,removed:m.removed,layout:null}),qW(m,o,{desired:l,keep:d,lead:p,agent:f,agentCmd:w,skipPermissions:n}).catch(async g=>{m.error=g?.message||String(g),m.finishedAt=Date.now(),console.error(`[reshape ${e}] FAILED at step "${m.step}": ${m.error}`),await vp(e,{step:m.step,error:m.error,finishedAt:m.finishedAt}).catch(()=>{})}),m}async function qW(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 vp(c,{step:T,...C})},p=await xe(o.name).catch(()=>null),f=o.sessionId??p?.sessionId??null,w=o.pinned===!0;if(await d("checkpoint"),e.checkpoint="llm",!p)e.checkpoint="skipped-dead";else if(uo(s))e.checkpoint="skipped-capped";else try{await ad(o.name,{timeoutMs:ty,quiesce:{box:o,reason:"reshape",onWait:()=>d("await-turn"),onResume:()=>d("checkpoint")}})}catch{e.checkpoint="reconstructed"}let m=null;e.checkpoint!=="llm"&&(m=await id(c,u,{trackName:t.name||c}).catch(()=>null)),await d("quiesce"),e.views=await rg(c).catch(()=>0),await mu(u).catch(()=>{}),p&&(await Rh(o.name,{fromAgent:s}).catch(()=>{}),await Dt(o.name).catch(()=>{}));let g=`${u}.reshape-${Date.now()}`,b=new Set([".bostrat"]),y={shape:l.kind,dir:u,staging:g,base:l.kind==="worktree"?l.base:null,sub:l.kind==="worktree"&&r?QI(l.base,l.repo??null,b):null,scratch:null},x=l.branch||`track/${Gp(t.name||c)}-${c}`,S=[],$=[],M=null;try{if(await d("move",{layout:y}),l.kind==="worktree")if(await Sp(l.base,u,g),await z.mkdir(u,{recursive:!0}),r){let C=V.join(u,y.sub);await Sp(l.base,g,C),await $n(V.join(C,".bostrat"))&&await z.rename(V.join(C,".bostrat"),V.join(u,".bostrat"));let P=V.join(C,"AGENTS.md");Qc(await z.readFile(P,"utf8").catch(()=>null))&&(await z.rm(P,{force:!0}).catch(()=>{}),await b$(C,"/AGENTS.md").catch(()=>{})),S.push({name:y.sub,dir:C,base:l.base,baseRef:l.baseRef??null,repo:l.repo??null})}else await $n(V.join(g,".bostrat"))&&await z.rename(V.join(g,".bostrat"),V.join(u,".bostrat"));else await z.rename(u,g),await z.mkdir(u,{recursive:!0}),await $n(V.join(g,".bostrat"))&&await z.rename(V.join(g,".bostrat"),V.join(u,".bostrat"));await d("materialize");for(let C of n){if(l.kind==="worktree"&&C.base===l.base)continue;let P=QI(C.base,C.slug,b),W=V.join(u,P),X=await us(C.base);await Lh(C.base,x)?await wc(C.base,W,x):await ls(C.base,W,x,X);let ge={name:P,dir:W,base:C.base,baseRef:X,repo:C.slug};S.push(ge),$.push(ge)}if(l.kind==="repo"&&await WW(g)){let C="scratch";for(let P=2;b.has(C);P++)C=`scratch-${P}`;b.add(C),y.scratch=C,await vp(c,{layout:y}),await z.rename(g,V.join(u,C)),e.scratch=!0}await ei(u,S,x,{scratch:y.scratch}),await d("relaunch"),M=await jn(o.name,u,{agentCmd:a,agent:s}),await pr({cwd:u,sessionName:o.name,agent:s,agentCmd:$s(s)}).catch(C=>{if(C?.code!=="cli-not-running")throw C});let T=await Ne(o.name,{lines:Ve}).catch(()=>"");if(!Zr(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 Dt(o.name).catch(()=>{}),await w$(y,{added:$}).catch(X=>{console.error(`[reshape ${c}] rollback move failed: ${X?.message||X}`)});let C=await Q(),P=fe(C,c),W=P?.boxes.find(X=>X.name===o.name)||null;try{let X=$p(s,f,{skipPermissions:i,cwd:u,peerName:o.name}),ge=await jn(o.name,u,{agentCmd:X,agent:s,resumeSessionId:f,pinned:w});W&&(W.hookTokenHash=Fo(ge.hookToken),W.state="live"),Zo({cwd:u,sessionName:o.name,agent:s,agentCmd:X})}catch(X){console.error(`[reshape ${c}] rollback relaunch failed: ${X?.message||X}`)}throw P&&(Ae(P,"retag-failed",{action:"reshape",box:o.name,agent:s,reason:T?.message||String(T),rolledBack:!0}),P.reshape={...P.reshape||{},step:"error",error:T?.message||String(T),finishedAt:Date.now(),recovered:"rolled-back"},await ae()),e.step="error",e.recovered="rolled-back",e.error=T?.message||String(T),e.finishedAt=Date.now(),T}await d("commit");let D=await Q(),j=fe(D,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=Ao(A),G=L[L.length-1];G.endedAt==null&&(G.endedAt=_),G.endReason==null&&(G.endReason="reshape");let O=await as({agent:s,agentCmd:a});L.push({agent:s,sessionId:M.sessionId,pinned:M.pinned===!0,cliVersion:O,startedAt:_,endedAt:null}),A.cwd=u,A.agent=s,A.cliVersion=O,A.sessionId=M.sessionId,A.pinned=M.pinned===!0,A.hookTokenHash=Fo(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:m||To,at:_},Ae(j,"retagged",{action:"reshape",added:e.added,removed:e.removed,scratch:e.scratch,checkpoint:e.checkpoint,views:e.views,box:o.name,agent:s,fromSessionId:f,toSessionId:M.sessionId,epoch:L.length-1,workspace:wy(j.workspace)}),j.reshape={...j.reshape||{},step:"done",finishedAt:_,error:null,checkpoint:e.checkpoint,scratch:e.scratch,views:e.views,layout:null},await ae(),Zo({cwd:u,sessionName:o.name,agent:s,agentCmd:a,onReady:async()=>{A.readyAt||(A.readyAt=Date.now(),await ae())}}),await $n(g)&&(l.kind==="worktree"&&await _t(l.base,g).catch(()=>{}),await z.rm(g,{recursive:!0,force:!0}).catch(()=>{})),e.step="done",e.finishedAt=_}async function yy(){let e=await Q(),t=0;for(let n of e.tracks||[]){let r=n.reshape;if(!r||r.finishedAt)continue;t++;let o=n.boxes.find(c=>c.name===n.leadBox)||null;o&&await Dt(o.name).catch(()=>{});let s=!1,a=null;if(r.layout)try{let{dir:c,sub:l,scratch:u}=r.layout,d=new Set([".bostrat",l,u].filter(Boolean)),p=[];for(let f of await z.readdir(c).catch(()=>[])){if(d.has(f))continue;let w=V.join(c,f),m=await z.readFile(V.join(w,".git"),"utf8").catch(()=>null),g=m&&/^gitdir:\s*(.+?)\/\.git\/worktrees\//m.exec(m);g&&p.push({base:g[1],dir:w})}s=await w$(r.layout,{added:p})}catch(c){a=c?.message||String(c),console.error(`[reshape ${n.id}] boot rollback failed: ${a}`)}o&&!Pr.has(o.state)&&(o.state="stopped");let i=`interrupted by a node restart during "${r.step}"${s?" \u2014 moved back":""}${a?` (${a})`:""}`;Ae(n,"retag-failed",{action:"reshape",box:n.leadBox,agent:o?.agent??null,reason:i,rolledBack:!a,recovered:!0}),n.reshape={...r,step:"error",error:i,finishedAt:Date.now(),recovered:a?"rollback-failed":"rolled-back",layout:null}}return t&&await ae(),t}async function GW(e,t,{repo:n,repos:r,cwd:o}={}){await z.mkdir(el,{recursive:!0});let s=V.join(el,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return HW(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!V.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await zs(n)??await su(n));let i=await h$([n,o,nW]);if(i){let c=`track/${Gp(t)}-${e}`;try{let l=await us(i);await ls(i,s,c,l),await ol(s);let u=await Oe(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await z.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await z.mkdir(s,{recursive:!0}),await Fh(s),await ol(s),{dir:s,base:null,branch:null,kind:"repo"}}async function ol(e){try{await z.writeFile(V.join(e,"AGENTS.md"),Zc,{flag:"wx"})}catch{return}await JW(e,["/AGENTS.md"])}function zW(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(`
|
|
232
|
+
${o}`,{source:"restore"})}else za(r,o);return await ae(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function hy(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=fe(await ee(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await xe(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(d=>d.name===r.leadBox)?.agent||o.agent||"claude",i=AA(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await Vi(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await fr({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:$s(o.agent)});let u=await TA(a,r.leadBox,i);return Ae(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await ae(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function y$(e){for(let t of e){if(!t||!await Sa(t))continue;let n=await rA(t).catch(()=>null);if(!n)continue;if((await z.stat(V.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function Ut(e){let t=e;return t?t.kind==="multi"?Array.isArray(t.repos)?t.repos.filter(Boolean):[]:t.base?[{name:V.basename(t.base),dir:t.dir??null,base:t.base,baseRef:t.baseRef??null,repo:t.repo??null}]:[]:[]}async function vs(e){let t=[];for(let n of Ut(e?.workspace)){if(!n?.dir)continue;let r=await is(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await cs(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function wy(e){let t=e;typeof t=="string"&&!V.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await zs(t)??await su(t));let n=await y$([t]);if(!n){let r=typeof e=="string"&&V.isAbsolute(e)&&!await z.stat(e).catch(()=>null);throw new Error(`tagged repo "${e}" is not a usable base (${r?"that path does not exist on this node \u2014 pick one of this node's repos or use owner/name":"need a primary checkout or a synced owner/name"})`)}return n}async function JW(e,t,n,r){let o=`track/${Gp(t)}-${e}`,s=[];try{await z.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await wy(i);if(s.some(f=>f.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await Oe(c).catch(()=>null),u=V.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let f=2;a.has(u);f++)u=`${V.basename(c)}-${f}`;a.add(u);let d=V.join(n,u),p=await us(c);await ls(c,d,o,p),s.push({name:u,dir:d,base:c,baseRef:p,repo:l})}return await ei(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 z.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function Pp(e,t){let n=await ee(),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 wy(typeof t=="string"?t.trim():t);if((o.repos??[]).some(p=>p?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await Oe(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(p=>p?.name).filter(Boolean)]),c=V.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let p=2;i.has(c);p++)c=`${V.basename(s)}-${p}`;let l=V.join(o.dir,c),u=await us(s);await jh(s,o.branch)?await wc(s,l,o.branch):await ls(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 ei(o.dir,o.repos,o.branch),Ae(r,"retagged",{action:"add",repo:a??s,name:c,workspace:gy(o)}),await ae(),d}async function Dp(e,t){let n=await ee(),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 Ft.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 is(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await cs(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 z.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await ei(o.dir,o.repos,o.branch),Ae(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:gy(o)}),await ae(),{removed:s.name}}async function vp(e,t){let n=await ee(),r=fe(n,e);r&&(r.reshape={...r.reshape||{},...t},await ae())}async function YW(e){let t=await z.readdir(e).catch(()=>[]);for(let n of t)if(!(n===".git"||n===".bostrat")){if(n==="AGENTS.md"){let r=await z.readFile(V.join(e,n),"utf8").catch(()=>"");if(Qc(r))continue}return!0}try{let{stdout:n}=await _p("git",["-C",e,"rev-list","--count","HEAD"]);if(Number(n.trim())>1)return!0}catch{}return!1}function t$(e,t,n){let r=V.basename(e);n.has(r)&&t&&(r=t.replace("/","-"));for(let o=2;n.has(r);o++)r=`${V.basename(e)}-${o}`;return n.add(r),r}async function b$(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 Rn(l)&&!await Rn(u)&&await z.rename(l,u)};if(n==="worktree"&&s){let l=V.join(r,s);await Rn(l)&&(await c(V.join(r,".bostrat"),V.join(l,".bostrat")),await Sp(a,l,o))}if(n==="repo"&&i){let l=V.join(r,i);await Rn(l)&&(await Rn(o)&&await z.rm(o,{recursive:!0,force:!0}),await z.rename(l,o))}return await Rn(o)?(await Rn(r)&&(await c(V.join(r,".bostrat"),V.join(o,".bostrat")),await z.rm(r,{recursive:!0,force:!0})),n==="worktree"?await Sp(a,o,r):await z.rename(o,r),!0):!1}async function yy(e,t,{skipPermissions:n=!0}={}){let r=await ee(),o=fe(r,e);if(!o)throw xt(`track "${e}" not found`,404);if(o.archivedAt)throw xt("track is archived \u2014 unarchive it first");try{Hp(o)}catch(y){throw Object.assign(y,{status:409})}if(o.kind==="setup")throw xt("setup boxes have no repos to reshape");if(String(o.name||"").startsWith("\u21BB"))throw xt("this Track's repos are the loop's \u2014 propose-update the loop instead");let s=Zg.get(e);if(s&&!s.finishedAt)throw xt("a reshape is already running for this Track");let a=Is.get(e);if(a&&!a.finishedAt)throw xt("a lead handoff is running \u2014 retry when it settles");let i=Tp.get(e);if(i&&!i.finishedAt)throw xt("a node transfer is running \u2014 retry when it settles");if(kp.has(e))throw xt("an adopt is running for this Track \u2014 retry when it settles");for(let y of Ft.values())if(y.trackId===e&&!y.finishedAt)throw xt("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 xt("repos (non-empty list of paths or owner/name slugs) required",400);let l=[];for(let y of c){let k;try{k=await wy(y)}catch(g){throw xt(g?.message||String(g),400)}if(l.some(g=>g.base===k))throw xt(`repo tagged twice: ${y}`,400);l.push({sel:y,base:k,slug:await Oe(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 is(S.dir,{ignorePlumbing:!0}).catch(()=>!1),M=await cs(S.dir,S.baseRef).catch(()=>0);if($||M>0)throw xt(`"${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 Pp(e,S.base);x.added.push($.repo??$.base)}for(let S of k)await Dp(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 xt(`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 is(u.dir??o.cwd,{ignorePlumbing:!0}).catch(()=>!1),k=await cs(u.dir??o.cwd,u.baseRef).catch(()=>0);if(y||k>0)throw xt(`"${V.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`)}Qa(e,"reshape");let p=o.boxes.find(y=>y.name===o.leadBox)||null;if(!p)throw xt("this Track has no lead box");let f=p.agent||"claude",w=await Za(f,n),m={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 Zg.set(e,m),await vp(e,{step:m.step,startedAt:m.startedAt,finishedAt:null,error:null,added:m.added,removed:m.removed,layout:null}),XW(m,o,{desired:l,keep:d,lead:p,agent:f,agentCmd:w,skipPermissions:n}).catch(async y=>{m.error=y?.message||String(y),m.finishedAt=Date.now(),console.error(`[reshape ${e}] FAILED at step "${m.step}": ${m.error}`),await vp(e,{step:m.step,error:m.error,finishedAt:m.finishedAt}).catch(()=>{})}),m}async function XW(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 vp(c,{step:T,...C})},p=await xe(o.name).catch(()=>null),f=o.sessionId??p?.sessionId??null,w=o.pinned===!0;if(await d("checkpoint"),e.checkpoint="llm",!p)e.checkpoint="skipped-dead";else if(uo(s))e.checkpoint="skipped-capped";else try{await ad(o.name,{timeoutMs:ty,quiesce:{box:o,reason:"reshape",onWait:()=>d("await-turn"),onResume:()=>d("checkpoint")}})}catch{e.checkpoint="reconstructed"}let m=null;e.checkpoint!=="llm"&&(m=await id(c,u,{trackName:t.name||c}).catch(()=>null)),await d("quiesce"),e.views=await ng(c).catch(()=>0),await mu(u).catch(()=>{}),p&&(await $h(o.name,{fromAgent:s}).catch(()=>{}),await Dt(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?t$(l.base,l.repo??null,k):null,scratch:null},x=l.branch||`track/${Gp(t.name||c)}-${c}`,S=[],$=[],M=null;try{if(await d("move",{layout:g}),l.kind==="worktree")if(await Sp(l.base,u,y),await z.mkdir(u,{recursive:!0}),r){let C=V.join(u,g.sub);await Sp(l.base,y,C),await Rn(V.join(C,".bostrat"))&&await z.rename(V.join(C,".bostrat"),V.join(u,".bostrat"));let P=V.join(C,"AGENTS.md");Qc(await z.readFile(P,"utf8").catch(()=>null))&&(await z.rm(P,{force:!0}).catch(()=>{}),await v$(C,"/AGENTS.md").catch(()=>{})),S.push({name:g.sub,dir:C,base:l.base,baseRef:l.baseRef??null,repo:l.repo??null})}else await Rn(V.join(y,".bostrat"))&&await z.rename(V.join(y,".bostrat"),V.join(u,".bostrat"));else await z.rename(u,y),await z.mkdir(u,{recursive:!0}),await Rn(V.join(y,".bostrat"))&&await z.rename(V.join(y,".bostrat"),V.join(u,".bostrat"));await d("materialize");for(let C of n){if(l.kind==="worktree"&&C.base===l.base)continue;let P=t$(C.base,C.slug,k),W=V.join(u,P),X=await us(C.base);await jh(C.base,x)?await wc(C.base,W,x):await ls(C.base,W,x,X);let ge={name:P,dir:W,base:C.base,baseRef:X,repo:C.slug};S.push(ge),$.push(ge)}if(l.kind==="repo"&&await YW(y)){let C="scratch";for(let P=2;k.has(C);P++)C=`scratch-${P}`;k.add(C),g.scratch=C,await vp(c,{layout:g}),await z.rename(y,V.join(u,C)),e.scratch=!0}await ei(u,S,x,{scratch:g.scratch}),await d("relaunch"),M=await Ln(o.name,u,{agentCmd:a,agent:s}),await fr({cwd:u,sessionName:o.name,agent:s,agentCmd:$s(s)}).catch(C=>{if(C?.code!=="cli-not-running")throw C});let T=await Pe(o.name,{lines:Ve}).catch(()=>"");if(!Zr(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 Dt(o.name).catch(()=>{}),await b$(g,{added:$}).catch(X=>{console.error(`[reshape ${c}] rollback move failed: ${X?.message||X}`)});let C=await ee(),P=fe(C,c),W=P?.boxes.find(X=>X.name===o.name)||null;try{let X=$p(s,f,{skipPermissions:i,cwd:u,peerName:o.name}),ge=await Ln(o.name,u,{agentCmd:X,agent:s,resumeSessionId:f,pinned:w});W&&(W.hookTokenHash=Fo(ge.hookToken),W.state="live"),Zo({cwd:u,sessionName:o.name,agent:s,agentCmd:X})}catch(X){console.error(`[reshape ${c}] rollback relaunch failed: ${X?.message||X}`)}throw P&&(Ae(P,"retag-failed",{action:"reshape",box:o.name,agent:s,reason:T?.message||String(T),rolledBack:!0}),P.reshape={...P.reshape||{},step:"error",error:T?.message||String(T),finishedAt:Date.now(),recovered:"rolled-back"},await ae()),e.step="error",e.recovered="rolled-back",e.error=T?.message||String(T),e.finishedAt=Date.now(),T}await d("commit");let D=await ee(),j=fe(D,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=Ao(A),G=L[L.length-1];G.endedAt==null&&(G.endedAt=_),G.endReason==null&&(G.endReason="reshape");let O=await as({agent:s,agentCmd:a});L.push({agent:s,sessionId:M.sessionId,pinned:M.pinned===!0,cliVersion:O,startedAt:_,endedAt:null}),A.cwd=u,A.agent=s,A.cliVersion=O,A.sessionId=M.sessionId,A.pinned=M.pinned===!0,A.hookTokenHash=Fo(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:m||To,at:_},Ae(j,"retagged",{action:"reshape",added:e.added,removed:e.removed,scratch:e.scratch,checkpoint:e.checkpoint,views:e.views,box:o.name,agent:s,fromSessionId:f,toSessionId:M.sessionId,epoch:L.length-1,workspace:gy(j.workspace)}),j.reshape={...j.reshape||{},step:"done",finishedAt:_,error:null,checkpoint:e.checkpoint,scratch:e.scratch,views:e.views,layout:null},await ae(),Zo({cwd:u,sessionName:o.name,agent:s,agentCmd:a,onReady:async()=>{A.readyAt||(A.readyAt=Date.now(),await ae())}}),await Rn(y)&&(l.kind==="worktree"&&await _t(l.base,y).catch(()=>{}),await z.rm(y,{recursive:!0,force:!0}).catch(()=>{})),e.step="done",e.finishedAt=_}async function by(){let e=await ee(),t=0;for(let n of e.tracks||[]){let r=n.reshape;if(!r||r.finishedAt)continue;t++;let o=n.boxes.find(c=>c.name===n.leadBox)||null;o&&await Dt(o.name).catch(()=>{});let s=!1,a=null;if(r.layout)try{let{dir:c,sub:l,scratch:u}=r.layout,d=new Set([".bostrat",l,u].filter(Boolean)),p=[];for(let f of await z.readdir(c).catch(()=>[])){if(d.has(f))continue;let w=V.join(c,f),m=await z.readFile(V.join(w,".git"),"utf8").catch(()=>null),y=m&&/^gitdir:\s*(.+?)\/\.git\/worktrees\//m.exec(m);y&&p.push({base:y[1],dir:w})}s=await b$(r.layout,{added:p})}catch(c){a=c?.message||String(c),console.error(`[reshape ${n.id}] boot rollback failed: ${a}`)}o&&!Dr.has(o.state)&&(o.state="stopped");let i=`interrupted by a node restart during "${r.step}"${s?" \u2014 moved back":""}${a?` (${a})`:""}`;Ae(n,"retag-failed",{action:"reshape",box:n.leadBox,agent:o?.agent??null,reason:i,rolledBack:!a,recovered:!0}),n.reshape={...r,step:"error",error:i,finishedAt:Date.now(),recovered:a?"rollback-failed":"rolled-back",layout:null}}return t&&await ae(),t}async function ZW(e,t,{repo:n,repos:r,cwd:o}={}){await z.mkdir(el,{recursive:!0});let s=V.join(el,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return JW(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!V.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await zs(n)??await su(n));let i=await y$([n,o,aW]);if(i){let c=`track/${Gp(t)}-${e}`;try{let l=await us(i);await ls(i,s,c,l),await ol(s);let u=await Oe(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await z.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await z.mkdir(s,{recursive:!0}),await Lh(s),await ol(s),{dir:s,base:null,branch:null,kind:"repo"}}async function ol(e){try{await z.writeFile(V.join(e,"AGENTS.md"),Zc,{flag:"wx"})}catch{return}await nq(e,["/AGENTS.md"])}function tq(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(`
|
|
233
233
|
`),o=n?`
|
|
234
234
|
\`${n}/\` is this Track's pre-repo workspace, carried over when the
|
|
235
235
|
Track was reshaped onto these repos. It is not part of any tagged repo and
|
|
236
236
|
cannot be shipped \u2014 move anything worth keeping into the repo it belongs to.
|
|
237
|
-
`:"";return`${
|
|
237
|
+
`:"";return`${eq}
|
|
238
238
|
|
|
239
239
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
240
240
|
git worktree per tagged repo, ALL on the same branch \`${t}\`:
|
|
@@ -246,20 +246,20 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
246
246
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
247
247
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
248
248
|
or preview an app, run it from that app's own repo directory.
|
|
249
|
-
${o}`}async function ei(e,t,n,r={}){let o=
|
|
249
|
+
${o}`}async function ei(e,t,n,r={}){let o=tq(t,n,r);for(let s of["CLAUDE.md","AGENTS.md"])await z.writeFile(V.join(e,s),o).catch(()=>{})}async function x$(e){let{stdout:t}=await _p("git",["-C",e,"rev-parse","--git-path","info/exclude"]);return V.resolve(e,t.trim())}async function nq(e,t){try{let n=await x$(e);await z.mkdir(V.dirname(n),{recursive:!0});let r=await z.readFile(n,"utf8").catch(()=>""),o=t.filter(s=>!r.split(`
|
|
250
250
|
`).includes(s));o.length&&await z.appendFile(n,(r.endsWith(`
|
|
251
251
|
`)||!r?"":`
|
|
252
252
|
`)+o.join(`
|
|
253
253
|
`)+`
|
|
254
|
-
`)}catch{}}async function
|
|
254
|
+
`)}catch{}}async function v$(e,t){try{let n=await x$(e),r=await z.readFile(n,"utf8"),o=r.split(`
|
|
255
255
|
`).filter(s=>s!==t);o.length!==r.split(`
|
|
256
256
|
`).length&&await z.writeFile(n,o.join(`
|
|
257
|
-
`))}catch{}}async function
|
|
257
|
+
`))}catch{}}async function ky(){let e={refreshed:0,removed:0,codexGlobal:null};try{await z.mkdir(rr,{recursive:!0}),await z.writeFile(V.join(rr,"CLAUDE.md"),QW).catch(()=>{}),await z.writeFile(V.join(rr,"AGENTS.md"),Zc).catch(()=>{});let t=process.env.CODEX_HOME||V.join(process.env.HOME||"/tmp",".codex");if((await z.stat(t).catch(()=>null))?.isDirectory()){let r=V.join(t,"AGENTS.md"),o=await z.readFile(r,"utf8").catch(()=>null);o===null?(await z.writeFile(r,qg,{flag:"wx"}).catch(()=>{}),e.codexGlobal="created"):Qc(o)&&o!==qg&&(await z.writeFile(r,qg).catch(()=>{}),e.codexGlobal="refreshed")}for(let r of[el,Xa]){let o=await z.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=V.join(r,s.name),i=V.join(a,"AGENTS.md"),c=await z.readFile(i,"utf8").catch(()=>null);c!==null&&Qc(c)&&c!==Zc&&(await z.writeFile(i,Zc).catch(()=>{}),e.refreshed++);let l=V.join(a,"CLAUDE.md"),u=await z.readFile(l,"utf8").catch(()=>null);u!==null&&Qc(u)&&(await z.rm(l,{force:!0}).catch(()=>{}),await v$(a,"/CLAUDE.md"),e.removed++)}}}catch{}return e}async function S$(e){let t=e.workspace;if(!(!t||!t.dir))try{if(t.kind==="multi"){for(let n of Ut(t))n.base&&n.dir&&await _t(n.base,n.dir).catch(()=>{});await z.rm(t.dir,{recursive:!0,force:!0})}else t.kind==="worktree"&&t.base?await _t(t.base,t.dir):await z.rm(t.dir,{recursive:!0,force:!0})}catch{}}async function Bp({name:e,cwd:t,repo:n,repos:r,agent:o="claude",model:s=null,skipPermissions:a=!0}){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");let i=await Za(o,a,s),c=as({agent:o,agentCmd:i}),l=await ee(),u=Ep().slice(0,8),d=await ZW(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(_u(f))&&(f=`${e.trim().slice(0,50).trim()} ${u} lead`);let w=await Ln(f,d.dir,{agentCmd:i,agent:o,freshWorkspace:!0}),m={id:u,name:e.trim(),cwd:w.cwd,workspace:d,leadBox:w.name,boxes:[Oy(w,"lead","live")],events:[],createdAt:Date.now()};return m.boxes[0].cliVersion=await c,Ae(m,"opened",{agent:o,...s?{model:s}:{},box:w.name,cliVersion:m.boxes[0].cliVersion,skipPermissions:a,name:m.name,cwd:m.cwd,workspace:{base:d.base??null,branch:d.branch,kind:d.kind,...d.baseRef?{baseRef:d.baseRef}:{},...d.repo?{repo:d.repo}:{},...d.repos?{repos:d.repos.map(({name:y,base:k,baseRef:g,repo:x})=>({name:y,base:k,baseRef:g,repo:x}))}:{}}}),l.tracks.push(m),await ae(),Zo({cwd:w.cwd,sessionName:w.name,agent:o,agentCmd:i,onBlocked:({message:y})=>c$(m.id,{box:w.name,agent:o,message:y}).catch(()=>{}),onReady:async()=>{let y=m.boxes.find(k=>k.name===m.leadBox);!y||y.readyAt||(y.readyAt=Date.now(),await ae())}}),zt(m)}async function xy({provider:e="claude",fresh:t=!1}={}){if(!rq.has(e))throw new Error(`unknown setup provider "${e}" (claude | codex | cursor)`);let r=(await ee()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===e&&!u.archivedAt);if(r){if(!t){let u=await zt(r);if(u.boxes.find(p=>p.name===u.leadBox)?.state==="live")return u}await Rs(r.id).catch(()=>{})}let o=(await Za(e,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=Ep().slice(0,8),a=V.join(rr,"setup",s);await z.mkdir(a,{recursive:!0});let i=await Ln(`Connect ${e} ${s}`,a,{agentCmd:o,agent:e,firstRun:!0});e==="codex"&&Zo({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:[Oy(i,"lead","live")],events:[],createdAt:Date.now()};return Ae(c,"opened",{agent:e,box:i.name,skipPermissions:!1,trackKind:"setup",name:c.name,cwd:c.cwd,workspace:{base:null,branch:null,kind:"scratch"}}),(await ee()).tracks.push(c),await ae(),zt(c)}async function vy(e,t,n){await mu(t.cwd).catch(()=>{}),await ng(e).catch(()=>{});let r=Is.get(e);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),Is.delete(e),t.handoff&&!t.handoff.finishedAt&&(t.handoff.step="error",t.handoff.error=n,t.handoff.finishedAt=Date.now());for(let a of Ms(e))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await Uy(a).catch(()=>{}),Ft.delete(a.id);for(let a of Object.values(t.subtasks||{}))a.finishedAt||(a.step="error",a.error=n,a.canceled=!0,a.finishedAt=Date.now());let o=await ee(),s=new Set((o.tracks||[]).filter(a=>a.id!==e&&!a.archivedAt).flatMap(a=>(a.boxes||[]).map(i=>i.name)));for(let a of t.boxes)s.has(a.name)||await Dt(a.name).catch(()=>{})}async function Sy(e,{cascadeMocks:t=!0,cascadeRunbooks:n=!1}={}){let r=await ee(),o=r.tracks.findIndex(a=>a.id===e);if(o===-1)throw new Error(`track "${e}" not found`);let s=r.tracks[o];return t||await K_(s),fy(e),await vy(e,s,"track deleted"),n?await SI(e).catch(()=>{}):await vI(e).catch(()=>{}),await IA(),await S$(s),t&&await V_(e).catch(()=>{}),t?await uI(e).catch(()=>{}):await lI(s).catch(()=>{}),Ae(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 Rs(e){let t=await ee(),n=fe(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(),fy(e),Ae(n,"archived",{boxes:n.boxes.length}),await ae(),n.kind==="setup"?(await A$(e),zt(n)):(sl.add(e),aq(e).catch(()=>{}).finally(()=>sl.delete(e)),{...await zt(n),archiving:!0})}async function aq(e){try{await iq(e)}catch{}await A$(e).catch(()=>{})}async function A$(e){let t=await ee(),n=fe(t,e);if(!(!n||!n.archivedAt)){await vy(e,n,"track archived");for(let r of n.boxes)Dr.has(r.state)||(r.state="stopped");await ae()}}async function iq(e){let t=await ee(),n=fe(t,e);if(!n||!n.archivedAt)return;let r=null,o=[],s="derived",a=(n.events||[]).some(l=>l.type==="said"),i=n.leadBox?await xe(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await _S(n.leadBox,i.cwd,{timeoutMs:sq})),s="lead-cli"}catch{}if(r||(r=HS(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 Sr(e,t){let n=await pt(e,t).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await Ay(n);if(r.length){for(let o of r)await fs(o.repo,o.number,!0).catch(()=>{});return!1}try{return await Rs(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function Ay(e){return(await Tn().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 Ar(e,t){return jp(e,t,"merged")}async function Os(e,t){let n=await pt(e,t).catch(()=>null);if(!n?.trackId)return null;let r={repo:n.repo??null,number:n.number,title:n.title??null,url:n.url??null},o=await Ay(n);if(o.length){let c=[...n.followupPending||[],r];for(let l of o)await iT(l.repo,l.number,c).catch(()=>{});return null}let s=[...n.followupPending||[],r],a=fe(await ee(),n.trackId);if(!a)return null;let i=s[0];return Rc(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 jp(e,t,n="merged"){let r=await pt(e,t).catch(()=>null);if(!r?.notifyOnMerge||r.archiveOnMerge||!r.trackId)return!1;let o={repo:r.repo??null,number:r.number,title:r.title??null,url:r.url??null,headRef:r.headRef??null,outcome:n},s=await Ay(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await aT(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];if(!await XA(r.repo,r.number).catch(()=>!0))return!1;try{return await Jt(r.trackId,T$(a),{kind:"merge-notify"}),!0}catch{return await ms(r.repo,r.number,!0).catch(()=>{}),!1}}function T$(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 ${n$[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 ${n$[i.outcome]??i.outcome}`).join(`
|
|
258
258
|
`),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}
|
|
259
259
|
${s}
|
|
260
|
-
${a}`}async function Ay(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<Gi()?!1:Ln(e.name)}async function al(e,t,n){return!t||e.archivedAt||Pr.has(t.state)?!1:(await oe({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 Dt(t.name).catch(()=>{}),t.state="stopped",ti(e))}async function ti(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=Kg.get(r.name);if(o)return o;let s=nq(e,r,{freshSession:t,reason:n}).finally(()=>Kg.delete(r.name));return Kg.set(r.name,s),s}async function Ty(e){let n=(await Q()).tracks.find(o=>o.leadBox===e);if(!n||n.archivedAt||n.transferredTo||Date.now()-(Vg.get(e)??0)<tq)return!1;let r=await ti(n,{reason:"attach"}).catch(()=>!1);return r?(Vg.delete(e),await ae()):Vg.set(e,Date.now()),r}async function nq(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||Pr.has(t.state)||await xe(t.name))return!1;let o=n===!0,s=o?Ep():null,a=o?Wi(ey(t.agent),s,t.name):$p(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await Eu(a);if(i===oa)return Ae(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===sa&&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(Ao(t)))}:null,l=Date.now();if(o){let u=Ao(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=as({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=Fo(d.hookToken),t.cliVersion=await u,Ao(t)[t.epochs.length-1].cliVersion=t.cliVersion,Zo({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a,onBlocked:({message:p})=>a$(e.id,{box:t.name,agent:t.agent,message:p}).catch(()=>{})}),t.state="live",o&&Ae(e,"lead-refreshed",{box:t.name,agent:t.agent,fromSessionId:c.sessionId,toSessionId:s,epoch:t.epochs.length-1,reason:r||"fresh-session",cliVersion:t.cliVersion??null,standing:await qa(t.cwd)}),Ae(e,"rehydrated",{box:t.name,agent:t.agent,...o?{fresh:!0}:{}}),!0}catch(u){return o&&(t.sessionId=c.sessionId,t.pinned=c.pinned,t.epochs=c.epochs),Ae(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function Lp(e,{freshSession:t=!1}={}){let n=await Q(),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,sl.delete(e),Ae(r,"unarchived",{}),await ti(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await ae(),Vt(r)}async function Ey(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>Qg)throw new Error(`name too long (max ${Qg} chars)`);let r=await Q(),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 Vt(o);let s=o.name;return o.name=n,Ae(o,"renamed",{from:s,to:n}),await ae(),Vt(o)}async function _y(){let e=await Q(),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 Iy(){let e=await Q(),t=new Set((await yn()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!Fe(s)).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(Fe(s))for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Dt(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):Ae(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!Pr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await ae(),r}async function Fp(e,{freshSession:t=!1}={}){let n=await Q(),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`);Hp(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(Pr.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await xe(r.leadBox)){let i=await Ne(r.leadBox,{lines:Ve}).catch(()=>"");if(ec(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Dt(r.leadBox),o.state="stopped"}let a=await ti(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 Vt(r)}async function $y(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 Fp(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 t$(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return qd({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function Ao(e){return(!Array.isArray(e.epochs)||e.epochs.length===0)&&(e.epochs=[{agent:e.agent??null,sessionId:e.sessionId??null,pinned:e.pinned===!0,cliVersion:e.cliVersion??null,startedAt:e.createdAt??null,endedAt:null}]),e.epochs}async function Ap(e,t){let n=await Q(),r=fe(n,e);r&&(r.handoff={...r.handoff||{},...t},await ae())}function S$(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 Oy(){let e=await Q(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=S$(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await xe(r.fromBox).catch(()=>null),l=n.boxes.find(u=>u.name===r.fromBox);if(c&&l&&c.agent===r.toAgent&&r.toAgent!==l.agent){let u=Date.now(),d=Ao(l),p=d[d.length-1],f=p.agent,w=p.sessionId??null;p.endedAt=u,r.reason&&(p.endReason=r.reason);let m=c.agent==="claude"&&!!c.sessionId,g=await as({agent:r.toAgent});d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:m,cliVersion:g,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.cliVersion=g,l.sessionId=c.sessionId||null,l.pinned=m,l.state="live",n.pendingHandoffHydrate={artifact:To,at:u},Ae(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:f,toAgent:r.toAgent,fromSessionId:w,toSessionId:c.sessionId||null,epoch:d.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0,standing:await qa(l.cwd)}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await oe({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 oe({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Dt(i).catch(()=>{}),a&&await Dt(a).catch(()=>{})}return t&&await ae(),t}async function Up(e,t,n=!0,r=null){let o=Is.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=fe(await Q(),e);if(!s)throw new Error(`track "${e}" not found`);Qa(e,"handoff");let a=await Za(t,n),i=uo(t);if(i){let u=new Error(`can't hand off to ${t} \u2014 its ${i.window} spend limit is reached ($${i.spentUsd.toFixed(2)} of $${i.limitUsd.toFixed(2)})`);throw u.usageCap=i,u}let c=await xe(s.leadBox);if(!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l={trackId:e,step:"checkpoint",fromBox:s.leadBox,toAgent:t,skipPermissions:n,reason:i$(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return Is.set(e,l),await Ap(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),rq(l,s,c,a,t).catch(async u=>{l.error=u?.message||String(u),l.finishedAt=Date.now(),console.error(`[lead-handoff ${e}] FAILED at step "${l.step}": ${l.error}`),await Ap(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),oe({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),oe({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function rq(e,t,n,r,o){let s=async(D,j={})=>{e.step=D,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${D}`),await Ap(e.trackId,{step:D,toBox:e.toBox}),await oe({flow:"lead-handoff",phase:D,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:j.sessionName,...j})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await Q(),i=fe(a,e.trackId)?.boxes.find(D=>D.name===e.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:e.fromBox}),e.checkpoint="llm",e.quiescence=null,uo(c))e.checkpoint="skipped-capped";else try{let D=await ad(e.fromBox,{timeoutMs:ty,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=Sh(D),e.waitedMs=(D.quiescence?.pre?.waitedMs||0)+(D.quiescence?.post?.waitedMs||0)}catch(D){e.checkpoint="reconstructed",e.quiescence=Sh(D),e.waitedMs=D?.quiescence?.pre?.waitedMs||0,e.wait=null,await oe({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:D?.message||String(D)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await id(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=as({agent:o,agentCmd:r}),f=await Oh(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let w=Lx(f.launchCmd);await pr({cwd:n.cwd,sessionId:f.sessionId,sessionName:e.fromBox,agent:o,agentCmd:$s(o),launchEcho:w}).catch(D=>{if(D?.code!=="cli-not-running")throw D});let m=Um(await Ne(e.fromBox,{lines:Ve}).catch(()=>""),w);if(await oe({flow:"lead-handoff",phase:"hydrate-ready",trackId:e.trackId,sessionName:e.fromBox,toAgent:o,up:Zr(o,m),paneTail:!0}).catch(()=>{}),!Zr(o,m)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let D=$p(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await Oh(e.fromBox,{agentCmd:D,agent:c,resumeSessionId:l,pinned:u});let j=await Q(),A=fe(j,e.trackId);if(A){let _=A.boxes.find(L=>L.name===e.fromBox);_&&(_.state="live"),Ae(A,"lead-handoff-failed",{box:e.fromBox,fromAgent:c,toAgent:o,reason:e.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:e.startedAt}),await ae()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let g=await Q(),b=fe(g,e.trackId);if(!b)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let y=b.boxes.find(D=>D.name===e.fromBox);if(!y)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let x=Date.now(),S=Ao(y),$=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}),y.agent=o,y.sessionId=f.sessionId,y.pinned=f.pinned,y.cliVersion=M,y.state="live",y.role="lead",b.pendingHandoffHydrate={artifact:e.statusArtifact||To,at:x},Ae(b,"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 qa(y.cwd)}),await ae(),await s("done",{sessionName:e.fromBox}),await Ap(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function Ny(e){let t=Tp.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await Q(),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=Is.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of Ft.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 Tp.set(e,s),oq(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 oq(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await xe(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&uo(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await ad(t.leadBox,{timeoutMs:ty,quiesce:{box:n,reason:"transfer-out",onWait:()=>{e.step="await-turn"},onResume:()=>{e.step="checkpoint"}}})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await id(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 Ut(t.workspace)){if(!u?.dir)continue;let d={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await Rn(u.dir,["push","origin",o],My);let{stdout:p}=await Rn(u.dir,["rev-parse","HEAD"]);d.baseSha=p.trim(),d.pushed=!0}catch(p){d.error=String(p?.message||p).split(`
|
|
261
|
-
`)[0]}s.push(d)}e.step="wip";let a=await
|
|
262
|
-
`)[0],advanced:null}}}function cq(e){if(typeof e!="string"||!iq.test(e)||e.includes("..")||e.includes("@{")||e.endsWith(".lock"))throw new Error("branch fails the safe-ref check")}async function Dy(e={}){let{trackId:t,name:n,agent:r="claude",skipPermissions:o=!0,mode:s="transfer"}=e;if(!t||typeof t!="string"||!/^[0-9a-f]{8}$/.test(t))throw new Error("trackId (8-hex string) required");if(typeof n!="string"||!n.trim())throw new Error("name (string) required");if(s!=="transfer"&&s!=="recover")throw new Error(`unknown adopt mode "${s}"`);if(e.branch!=null&&cq(e.branch),kp.has(t))throw new Error("an adopt is already running for this Track");kp.set(t,{startedAt:Date.now()});try{return await lq({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{kp.delete(t)}}async function lq({trackId:e,name:t,agent:n,skipPermissions:r,mode:o,fromNode:s=null,toNode:a=null,branch:i=null,repos:c=[],wip:l=null,wips:u=null,brief:d=null,epochs:p=[],createdAt:f=null,leadBox:w=null,reason:m=null,downSince:g=null}){let b=await Za(n,r),y=await Q(),x=fe(y,e);if(x&&!x.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);x&&(await Tg(x).catch(()=>{}),await k$(x),y.tracks.splice(y.tracks.indexOf(x),1),await ae()),await z.mkdir(el,{recursive:!0});let S=V.join(el,e);await z.rm(S,{recursive:!0,force:!0}).catch(()=>{});let $=null,M=!1,D=[];try{let j=[];if(i)for(let le of Array.isArray(c)?c:[]){let re=typeof le=="string"?le:le?.repo;if(!re||!/^[^/\s]+\/[^/\s]+$/.test(re))continue;let $e=await zs(re)??await su(re);await Rn($e,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],My),j.push({slug:re,base:$e,name:typeof le=="object"?le?.name??null:null})}if(i&&j.length===1){let{base:le,slug:re}=j[0];await n$(le,S,i),D.push({base:le,dir:S}),await ol(S);let $e=await us(le);$={dir:S,base:le,branch:i,kind:"worktree",baseRef:$e,repo:re}}else if(i&&j.length>=2){await z.mkdir(S,{recursive:!0});let le=new Set([".bostrat"]),re=[];for(let $e of j){let J=$e.name||V.basename($e.base);le.has(J)&&(J=$e.slug.replace("/","-"));for(let R=2;le.has(J);R++)J=`${V.basename($e.base)}-${R}`;le.add(J);let he=V.join(S,J);await n$($e.base,he,i),D.push({base:$e.base,dir:he});let Be=await us($e.base);re.push({name:J,dir:he,base:$e.base,baseRef:Be,repo:$e.slug})}await ei(S,re,i),$={dir:S,kind:"multi",branch:i,repos:re}}else M=!0,await z.mkdir(S,{recursive:!0}),await Fh(S),await ol(S),$={dir:S,base:null,branch:null,kind:"repo"};let A=[];for(let le of sq($,{wip:l,wips:u})){let re=await aq(le.dir,e);A.push({name:le.name,repo:le.repo,applied:re.applied,skipped:re.skipped,...re.advanced?{advanced:re.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,G=A.length===1?A[0].advanced??null:null;await PI({id:e,cwd:S}).catch(()=>({restored:!1}));let O=V.join(S,To),T=null;(await bu(S)).restored?T="checkpoint":typeof d=="string"&&d.trim()&&(await z.mkdir(V.dirname(O),{recursive:!0}),await z.writeFile(O,d),T="brief");let P=new Set(y.tracks.flatMap(le=>(le.boxes||[]).map(re=>re.name))),W=null;w&&!P.has(w)&&!await xe(w).catch(()=>null)?W=w:(W=`${t.trim()} lead`,P.has(_u(W))&&(W=`${t.trim().slice(0,50).trim()} ${e} lead`));let X=await jn(W,S,{agentCmd:b,agent:n});Zo({cwd:X.cwd,sessionName:X.name,agent:n,agentCmd:b});let ge=Date.now(),Ie=Ry(X,"lead","live"),Pe=(Array.isArray(p)?p:[]).filter(le=>le&&typeof le=="object").map(le=>({...le})),ee=Pe[Pe.length-1];ee&&ee.endedAt==null&&(ee.endedAt=ge,ee.endReason==null&&(ee.endReason=o==="recover"?"node-offline":"node-transfer"));let Ze=await as({agent:n,agentCmd:b});Pe.push({agent:n,sessionId:X.sessionId??null,pinned:X.pinned===!0,cliVersion:Ze,startedAt:ge,endedAt:null}),Ie.epochs=Pe,Ie.cliVersion=Ze;let Ee={id:e,name:t.trim(),cwd:X.cwd,workspace:$,leadBox:X.name,boxes:[Ie],events:[],createdAt:Number.isFinite(f)?f:ge,...T?{pendingHandoffHydrate:{artifact:To,at:ge}}:{}};return Ae(Ee,"transferred-in",{box:X.name,agent:n,fromNode:s,toNode:a,mode:o,reason:m,...o==="recover"?{downSince:Number.isFinite(g)?g:null}:{},branch:$.branch??null,wip:_,...G?{wipAdvanced:G}:{},...$.kind==="multi"&&A.length?{wipRepos:A}:{},epoch:Pe.length-1,name:Ee.name,cwd:Ee.cwd,workspace:{base:$.base??null,branch:$.branch??null,kind:$.kind,...$.baseRef?{baseRef:$.baseRef}:{},...$.repo?{repo:$.repo}:{},...$.repos?{repos:$.repos.map(({name:le,base:re,baseRef:$e,repo:J})=>({name:le,base:re,baseRef:$e,repo:J}))}:{}}}),y.tracks.push(Ee),await ae(),{...await Vt(Ee),adopt:{wipApplied:_,wipSkipped:L,...G?{wipAdvanced:G}:{},...$.kind==="multi"?{wipRepos:A}:{},codeless:M,artifactSource:T}}}catch(j){for(let A of D)await _t(A.base,A.dir).catch(()=>{});throw await z.rm(S,{recursive:!0,force:!0}).catch(()=>{}),j}}async function A$(e,t,n){let r=await Q(),o=fe(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await ae())}async function Ka(e,t){e.step=t,await pl(e)}function T$(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 E$(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function fq(e,t,n){if(!E$(n)||Ft.has(t)||!await xe(n.box).catch(()=>null))return!1;try{await z.access(n.dir)}catch{return!1}let o=await xe(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return Ft.set(t,s),I$(s,o).catch(a=>_$(s,a)),!0}async function By(){let e=await Q(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Ft.has(r))continue;if(await fq(n,r,o)){t=!0,Ae(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await oe({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=T$(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!Pr.has(i.state)&&(i.state="canceled"),Ae(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await oe({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await r$(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||Pr.has(r.state)||Ft.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?V.join(Xa,`${n.id}-${r.subtaskId}`):null;await oe({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await r$(n,r.name,o)}}return t=await mq(e)||t,t&&await ae(),t}async function r$(e,t,n){if(t&&await Dt(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of Ut(e.workspace)){let s=V.join(n,o.name);await _t(o.base,s).then(()=>!0,()=>!1)||await _t(o.dir,s).catch(()=>{})}await z.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await xe(e.leadBox).catch(()=>null);r?.cwd&&await _t(r.cwd,n).catch(()=>{})}async function mq(e){let t;try{t=await z.readdir(Xa)}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 Ft.values())o.dir&&n.add(V.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=V.join(Xa,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of Ut(a.workspace))await _t(i.base,V.join(s,i.name)).catch(()=>{});await z.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await xe(a.leadBox).catch(()=>null):null;i?.cwd?await _t(i.cwd,s).catch(()=>{}):await z.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await oe({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function jy(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!va(n))throw new Error("task (string) required");let s=await Za(t,r),a=uo(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 Q(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await xe(i.leadBox);if(!c)throw new Error(`lead box "${i.leadBox}" is not running`);if(!c.cwd)throw new Error("can't resolve the lead's working directory");let l=i.workspace?.kind==="multi",u=null;if(l){let f=Ut(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 Sa(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 Sa(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=Ep().slice(0,8),p={id:d,trackId:e,trackName:i.name,agent:t,task:va(n),skipPermissions:r,reason:i$(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${Gp(i.name)}-${d}`,base:null,repos:u,dir:V.join(Xa,`${e}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Ft.set(d,p),await pl(p),hq(p,c,s).catch(f=>_$(p,f)),p}async function _$(e,t){e.failedStep=e.step,e.error=t instanceof il?"canceled":t?.message||String(t),e.step=t instanceof il?"canceled":"error",e.finishedAt=Date.now(),await Fy(e).catch(()=>{}),await pl(e).catch(()=>{}),await bq(e).catch(()=>{})}function Kp(e){let t=Ft.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 Ly(e){let t=Ft.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 hq(e,t,n){if(await Ka(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await Aa(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await pl(e)}else e.base=await Aa(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await Ka(e,"spawn"),await z.mkdir(Xa,{recursive:!0}),e.repos){await z.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await ls(a.leadDir,V.join(e.dir,a.name),e.branch,a.base);let s=Ut((fe(await Q(),e.trackId)||{}).workspace);await ei(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 ls(t.cwd,e.dir,e.branch,e.base),await ol(e.dir);await wq(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 gq(e,o),await c$(e.trackId,o.name,oW(e.task,!!e.repos),{kind:"subtask"}),await I$(e,t)}async function I$(e,t){await Ka(e,"working");let n=V.join(e.dir,xp);if(await xq(n,e),e.canceled)throw new il;if(await Ka(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=V.join(e.dir,l.name);await jh(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:t$({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await Hh(l.leadDir,l.base,e.branch,Ga),l.files=await Wh(l.leadDir,l.base,e.branch,Ga);let d=await qh(l.leadDir,l.base,e.branch,Ga);l.applied=d.applied,l.conflicts=d.conflicts??[],s.files+=l.stat.files,s.insertions+=l.stat.insertions,s.deletions+=l.stat.deletions,a.push(...l.files.map(p=>V.join(l.name,p))),i.push(...l.conflicts.map(p=>p.startsWith("patch did not apply cleanly")?`${l.name}: ${p}`:V.join(l.name,p))),d.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await jh(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:t$({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await Hh(t.cwd,e.base,e.branch,Ga),e.files=await Wh(t.cwd,e.base,e.branch,Ga);let s=await qh(t.cwd,e.base,e.branch,Ga);e.applied=s.applied,e.conflicts=s.conflicts}await pl(e);let r=await kq(e,t,n);await Ka(e,"resume"),await c$(e.trackId,e.leadBox,sW(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await Ka(e,"reap"),await Fy(e),e.step="done";let o=Date.now();await A$(e.trackId,e.id,{...qp(e),finishedAt:o}),await yq(e,o),e.finishedAt=o}async function wq(e,t){let n=V.join(e.dir,V.dirname(tl));await z.mkdir(n,{recursive:!0});let r="";try{r=await Sx(t.cwd,t.sessionId)}catch{}if(!r)try{r=await z.readFile(V.join(t.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${e.id}
|
|
260
|
+
${a}`}async function Ty(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<Gi()?!1:Fn(e.name)}async function al(e,t,n){return!t||e.archivedAt||Dr.has(t.state)?!1:(await oe({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 Dt(t.name).catch(()=>{}),t.state="stopped",ti(e))}async function ti(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=Gg.get(r.name);if(o)return o;let s=lq(e,r,{freshSession:t,reason:n}).finally(()=>Gg.delete(r.name));return Gg.set(r.name,s),s}async function Ey(e){let n=(await ee()).tracks.find(o=>o.leadBox===e);if(!n||n.archivedAt||n.transferredTo||Date.now()-(Kg.get(e)??0)<cq)return!1;let r=await ti(n,{reason:"attach"}).catch(()=>!1);return r?(Kg.delete(e),await ae()):Kg.set(e,Date.now()),r}async function lq(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||Dr.has(t.state)||await xe(t.name))return!1;let o=n===!0,s=o?Ep():null,a=o?Wi(ey(t.agent),s,t.name):$p(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await Eu(a);if(i===oa)return Ae(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===sa&&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(Ao(t)))}:null,l=Date.now();if(o){let u=Ao(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=as({agent:t.agent,agentCmd:a}),d=await Ln(t.name,t.cwd,{agentCmd:a,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=Fo(d.hookToken),t.cliVersion=await u,Ao(t)[t.epochs.length-1].cliVersion=t.cliVersion,Zo({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a,onBlocked:({message:p})=>c$(e.id,{box:t.name,agent:t.agent,message:p}).catch(()=>{})}),t.state="live",o&&Ae(e,"lead-refreshed",{box:t.name,agent:t.agent,fromSessionId:c.sessionId,toSessionId:s,epoch:t.epochs.length-1,reason:r||"fresh-session",cliVersion:t.cliVersion??null,standing:await qa(t.cwd)}),Ae(e,"rehydrated",{box:t.name,agent:t.agent,...o?{fresh:!0}:{}}),!0}catch(u){return o&&(t.sessionId=c.sessionId,t.pinned=c.pinned,t.epochs=c.epochs),Ae(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function Lp(e,{freshSession:t=!1}={}){let n=await ee(),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,sl.delete(e),Ae(r,"unarchived",{}),await ti(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await ae(),zt(r)}async function _y(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>Qg)throw new Error(`name too long (max ${Qg} chars)`);let r=await ee(),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,Ae(o,"renamed",{from:s,to:n}),await ae(),zt(o)}async function Iy(){let e=await ee(),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 $y(){let e=await ee(),t=new Set((await bn()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!Fe(s)).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(Fe(s))for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Dt(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):Ae(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!Dr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await ae(),r}async function Fp(e,{freshSession:t=!1}={}){let n=await ee(),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`);Hp(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(Dr.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await xe(r.leadBox)){let i=await Pe(r.leadBox,{lines:Ve}).catch(()=>"");if(ec(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Dt(r.leadBox),o.state="stopped"}let a=await ti(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 Ry(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 Fp(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 r$(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return qd({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function Ao(e){return(!Array.isArray(e.epochs)||e.epochs.length===0)&&(e.epochs=[{agent:e.agent??null,sessionId:e.sessionId??null,pinned:e.pinned===!0,cliVersion:e.cliVersion??null,startedAt:e.createdAt??null,endedAt:null}]),e.epochs}async function Ap(e,t){let n=await ee(),r=fe(n,e);r&&(r.handoff={...r.handoff||{},...t},await ae())}function E$(e,t=Date.now()){let n=e?.handoff;if(!n||n.finishedAt)return null;let r=n.step,o=!!n.toBox&&n.toBox===n.fromBox,s=e.boxes?.find?.(i=>i.name===e.leadBox)||null;return(o?e.leadBox===n.fromBox&&!!n.startedAt&&Array.isArray(s?.epochs)&&s.epochs.some(i=>i.startedAt!=null&&i.startedAt>=n.startedAt):!!n.toBox&&e.leadBox===n.toBox)?{...n,step:"done",finishedAt:t,recovered:`committed before restart (was "${r}")`}:{...n,step:"error",finishedAt:t,error:`interrupted by an agent restart during "${r}"; lead "${e.leadBox}" retained \u2014 retry the handoff`,recovered:"rolled-back"}}async function My(){let e=await ee(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=E$(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await xe(r.fromBox).catch(()=>null),l=n.boxes.find(u=>u.name===r.fromBox);if(c&&l&&c.agent===r.toAgent&&r.toAgent!==l.agent){let u=Date.now(),d=Ao(l),p=d[d.length-1],f=p.agent,w=p.sessionId??null;p.endedAt=u,r.reason&&(p.endReason=r.reason);let m=c.agent==="claude"&&!!c.sessionId,y=await as({agent:r.toAgent});d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:m,cliVersion:y,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.cliVersion=y,l.sessionId=c.sessionId||null,l.pinned=m,l.state="live",n.pendingHandoffHydrate={artifact:To,at:u},Ae(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:f,toAgent:r.toAgent,fromSessionId:w,toSessionId:c.sessionId||null,epoch:d.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0,standing:await qa(l.cwd)}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await oe({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 oe({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Dt(i).catch(()=>{}),a&&await Dt(a).catch(()=>{})}return t&&await ae(),t}async function Up(e,t,n=!0,r=null){let o=Is.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=fe(await ee(),e);if(!s)throw new Error(`track "${e}" not found`);Qa(e,"handoff");let a=await Za(t,n),i=uo(t);if(i){let u=new Error(`can't hand off to ${t} \u2014 its ${i.window} spend limit is reached ($${i.spentUsd.toFixed(2)} of $${i.limitUsd.toFixed(2)})`);throw u.usageCap=i,u}let c=await xe(s.leadBox);if(!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l={trackId:e,step:"checkpoint",fromBox:s.leadBox,toAgent:t,skipPermissions:n,reason:l$(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return Is.set(e,l),await Ap(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),uq(l,s,c,a,t).catch(async u=>{l.error=u?.message||String(u),l.finishedAt=Date.now(),console.error(`[lead-handoff ${e}] FAILED at step "${l.step}": ${l.error}`),await Ap(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),oe({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),oe({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function uq(e,t,n,r,o){let s=async(D,j={})=>{e.step=D,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${D}`),await Ap(e.trackId,{step:D,toBox:e.toBox}),await oe({flow:"lead-handoff",phase:D,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:j.sessionName,...j})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await ee(),i=fe(a,e.trackId)?.boxes.find(D=>D.name===e.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:e.fromBox}),e.checkpoint="llm",e.quiescence=null,uo(c))e.checkpoint="skipped-capped";else try{let D=await ad(e.fromBox,{timeoutMs:ty,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=vh(D),e.waitedMs=(D.quiescence?.pre?.waitedMs||0)+(D.quiescence?.post?.waitedMs||0)}catch(D){e.checkpoint="reconstructed",e.quiescence=vh(D),e.waitedMs=D?.quiescence?.pre?.waitedMs||0,e.wait=null,await oe({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:D?.message||String(D)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await id(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=as({agent:o,agentCmd:r}),f=await Rh(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let w=Fx(f.launchCmd);await fr({cwd:n.cwd,sessionId:f.sessionId,sessionName:e.fromBox,agent:o,agentCmd:$s(o),launchEcho:w}).catch(D=>{if(D?.code!=="cli-not-running")throw D});let m=Um(await Pe(e.fromBox,{lines:Ve}).catch(()=>""),w);if(await oe({flow:"lead-handoff",phase:"hydrate-ready",trackId:e.trackId,sessionName:e.fromBox,toAgent:o,up:Zr(o,m),paneTail:!0}).catch(()=>{}),!Zr(o,m)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let D=$p(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await Rh(e.fromBox,{agentCmd:D,agent:c,resumeSessionId:l,pinned:u});let j=await ee(),A=fe(j,e.trackId);if(A){let _=A.boxes.find(L=>L.name===e.fromBox);_&&(_.state="live"),Ae(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 ee(),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(D=>D.name===e.fromBox);if(!g)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let x=Date.now(),S=Ao(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||To,at:x},Ae(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 qa(g.cwd)}),await ae(),await s("done",{sessionName:e.fromBox}),await Ap(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function Py(e){let t=Tp.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await ee(),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=Is.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of Ft.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 Tp.set(e,s),dq(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 dq(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await xe(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&uo(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await ad(t.leadBox,{timeoutMs:ty,quiesce:{box:n,reason:"transfer-out",onWait:()=>{e.step="await-turn"},onResume:()=>{e.step="checkpoint"}}})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await id(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 Ut(t.workspace)){if(!u?.dir)continue;let d={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await On(u.dir,["push","origin",o],Cy);let{stdout:p}=await On(u.dir,["rev-parse","HEAD"]);d.baseSha=p.trim(),d.pushed=!0}catch(p){d.error=String(p?.message||p).split(`
|
|
261
|
+
`)[0]}s.push(d)}e.step="wip";let a=await Sg(t,{force:!0}),i=await ee(),c=fe(i,t.id),l=c?.boxes.find(u=>u.name===c.leadBox)||null;l&&Ao(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 Hp(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 Wp(e,{toNode:t,mode:n="transfer"}={}){if(!t||typeof t!="string")throw new Error("toNode required");let r=await ee(),o=fe(r,e);if(!o)throw new Error(`track "${e}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};fy(e),await vy(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();md(o,{at:s,reason:n==="recover"?"node-offline":"node-transfer"});let a=await Ag(o,{ts:s}),i=a.pushed[0]??null;return o.transferredTo=t,o.transferredAt=s,Ae(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 Dy(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await Wp(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 o$(e,t,n){try{await wc(e,t,n)}catch{await On(e,["worktree","prune"]).catch(()=>{}),await wc(e,t,n)}return t}function pq(e,{wip:t=null,wips:n=null}={}){let r=Ut(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 fq(e,t){try{await On(e,["fetch","origin",vg(t)],Cy);let{stdout:n}=await On(e,["rev-parse","FETCH_HEAD^"]),{stdout:r}=await On(e,["rev-parse","HEAD"]),o=null,s=n.trim()===r.trim();return s||await On(e,["merge-base","--is-ancestor",r.trim(),n.trim()]).then(()=>!0,()=>!1)&&(await On(e,["reset","--hard",n.trim()]),o={from:r.trim(),to:n.trim()},s=!0),s?(await On(e,["read-tree","--reset","-u","FETCH_HEAD"]),await On(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(`
|
|
262
|
+
`)[0],advanced:null}}}function hq(e){if(typeof e!="string"||!mq.test(e)||e.includes("..")||e.includes("@{")||e.endsWith(".lock"))throw new Error("branch fails the safe-ref check")}async function By(e={}){let{trackId:t,name:n,agent:r="claude",skipPermissions:o=!0,mode:s="transfer"}=e;if(!t||typeof t!="string"||!/^[0-9a-f]{8}$/.test(t))throw new Error("trackId (8-hex string) required");if(typeof n!="string"||!n.trim())throw new Error("name (string) required");if(s!=="transfer"&&s!=="recover")throw new Error(`unknown adopt mode "${s}"`);if(e.branch!=null&&hq(e.branch),kp.has(t))throw new Error("an adopt is already running for this Track");kp.set(t,{startedAt:Date.now()});try{return await wq({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{kp.delete(t)}}async function wq({trackId:e,name:t,agent:n,skipPermissions:r,mode:o,fromNode:s=null,toNode:a=null,branch:i=null,repos:c=[],wip:l=null,wips:u=null,brief:d=null,epochs:p=[],createdAt:f=null,leadBox:w=null,reason:m=null,downSince:y=null}){let k=await Za(n,r),g=await ee(),x=fe(g,e);if(x&&!x.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);x&&(await Ag(x).catch(()=>{}),await S$(x),g.tracks.splice(g.tracks.indexOf(x),1),await ae()),await z.mkdir(el,{recursive:!0});let S=V.join(el,e);await z.rm(S,{recursive:!0,force:!0}).catch(()=>{});let $=null,M=!1,D=[];try{let j=[];if(i)for(let le of Array.isArray(c)?c:[]){let re=typeof le=="string"?le:le?.repo;if(!re||!/^[^/\s]+\/[^/\s]+$/.test(re))continue;let $e=await zs(re)??await su(re);await On($e,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],Cy),j.push({slug:re,base:$e,name:typeof le=="object"?le?.name??null:null})}if(i&&j.length===1){let{base:le,slug:re}=j[0];await o$(le,S,i),D.push({base:le,dir:S}),await ol(S);let $e=await us(le);$={dir:S,base:le,branch:i,kind:"worktree",baseRef:$e,repo:re}}else if(i&&j.length>=2){await z.mkdir(S,{recursive:!0});let le=new Set([".bostrat"]),re=[];for(let $e of j){let J=$e.name||V.basename($e.base);le.has(J)&&(J=$e.slug.replace("/","-"));for(let R=2;le.has(J);R++)J=`${V.basename($e.base)}-${R}`;le.add(J);let he=V.join(S,J);await o$($e.base,he,i),D.push({base:$e.base,dir:he});let Be=await us($e.base);re.push({name:J,dir:he,base:$e.base,baseRef:Be,repo:$e.slug})}await ei(S,re,i),$={dir:S,kind:"multi",branch:i,repos:re}}else M=!0,await z.mkdir(S,{recursive:!0}),await Lh(S),await ol(S),$={dir:S,base:null,branch:null,kind:"repo"};let A=[];for(let le of pq($,{wip:l,wips:u})){let re=await fq(le.dir,e);A.push({name:le.name,repo:le.repo,applied:re.applied,skipped:re.skipped,...re.advanced?{advanced:re.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,G=A.length===1?A[0].advanced??null:null;await BI({id:e,cwd:S}).catch(()=>({restored:!1}));let O=V.join(S,To),T=null;(await bu(S)).restored?T="checkpoint":typeof d=="string"&&d.trim()&&(await z.mkdir(V.dirname(O),{recursive:!0}),await z.writeFile(O,d),T="brief");let P=new Set(g.tracks.flatMap(le=>(le.boxes||[]).map(re=>re.name))),W=null;w&&!P.has(w)&&!await xe(w).catch(()=>null)?W=w:(W=`${t.trim()} lead`,P.has(_u(W))&&(W=`${t.trim().slice(0,50).trim()} ${e} lead`));let X=await Ln(W,S,{agentCmd:k,agent:n});Zo({cwd:X.cwd,sessionName:X.name,agent:n,agentCmd:k});let ge=Date.now(),Ie=Oy(X,"lead","live"),Ce=(Array.isArray(p)?p:[]).filter(le=>le&&typeof le=="object").map(le=>({...le})),Z=Ce[Ce.length-1];Z&&Z.endedAt==null&&(Z.endedAt=ge,Z.endReason==null&&(Z.endReason=o==="recover"?"node-offline":"node-transfer"));let Ze=await as({agent:n,agentCmd:k});Ce.push({agent:n,sessionId:X.sessionId??null,pinned:X.pinned===!0,cliVersion:Ze,startedAt:ge,endedAt:null}),Ie.epochs=Ce,Ie.cliVersion=Ze;let Ee={id:e,name:t.trim(),cwd:X.cwd,workspace:$,leadBox:X.name,boxes:[Ie],events:[],createdAt:Number.isFinite(f)?f:ge,...T?{pendingHandoffHydrate:{artifact:To,at:ge}}:{}};return Ae(Ee,"transferred-in",{box:X.name,agent:n,fromNode:s,toNode:a,mode:o,reason:m,...o==="recover"?{downSince:Number.isFinite(y)?y:null}:{},branch:$.branch??null,wip:_,...G?{wipAdvanced:G}:{},...$.kind==="multi"&&A.length?{wipRepos:A}:{},epoch:Ce.length-1,name:Ee.name,cwd:Ee.cwd,workspace:{base:$.base??null,branch:$.branch??null,kind:$.kind,...$.baseRef?{baseRef:$.baseRef}:{},...$.repo?{repo:$.repo}:{},...$.repos?{repos:$.repos.map(({name:le,base:re,baseRef:$e,repo:J})=>({name:le,base:re,baseRef:$e,repo:J}))}:{}}}),g.tracks.push(Ee),await ae(),{...await zt(Ee),adopt:{wipApplied:_,wipSkipped:L,...G?{wipAdvanced:G}:{},...$.kind==="multi"?{wipRepos:A}:{},codeless:M,artifactSource:T}}}catch(j){for(let A of D)await _t(A.base,A.dir).catch(()=>{});throw await z.rm(S,{recursive:!0,force:!0}).catch(()=>{}),j}}async function _$(e,t,n){let r=await ee(),o=fe(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await ae())}async function Ka(e,t){e.step=t,await pl(e)}function I$(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 $$(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function kq(e,t,n){if(!$$(n)||Ft.has(t)||!await xe(n.box).catch(()=>null))return!1;try{await z.access(n.dir)}catch{return!1}let o=await xe(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return Ft.set(t,s),O$(s,o).catch(a=>R$(s,a)),!0}async function jy(){let e=await ee(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Ft.has(r))continue;if(await kq(n,r,o)){t=!0,Ae(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await oe({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=I$(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!Dr.has(i.state)&&(i.state="canceled"),Ae(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await oe({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await s$(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||Dr.has(r.state)||Ft.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?V.join(Xa,`${n.id}-${r.subtaskId}`):null;await oe({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await s$(n,r.name,o)}}return t=await xq(e)||t,t&&await ae(),t}async function s$(e,t,n){if(t&&await Dt(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of Ut(e.workspace)){let s=V.join(n,o.name);await _t(o.base,s).then(()=>!0,()=>!1)||await _t(o.dir,s).catch(()=>{})}await z.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await xe(e.leadBox).catch(()=>null);r?.cwd&&await _t(r.cwd,n).catch(()=>{})}async function xq(e){let t;try{t=await z.readdir(Xa)}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 Ft.values())o.dir&&n.add(V.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=V.join(Xa,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of Ut(a.workspace))await _t(i.base,V.join(s,i.name)).catch(()=>{});await z.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await xe(a.leadBox).catch(()=>null):null;i?.cwd?await _t(i.cwd,s).catch(()=>{}):await z.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await oe({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function Ly(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!va(n))throw new Error("task (string) required");let s=await Za(t,r),a=uo(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 ee(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await xe(i.leadBox);if(!c)throw new Error(`lead box "${i.leadBox}" is not running`);if(!c.cwd)throw new Error("can't resolve the lead's working directory");let l=i.workspace?.kind==="multi",u=null;if(l){let f=Ut(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 Sa(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 Sa(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=Ep().slice(0,8),p={id:d,trackId:e,trackName:i.name,agent:t,task:va(n),skipPermissions:r,reason:l$(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${Gp(i.name)}-${d}`,base:null,repos:u,dir:V.join(Xa,`${e}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Ft.set(d,p),await pl(p),vq(p,c,s).catch(f=>R$(p,f)),p}async function R$(e,t){e.failedStep=e.step,e.error=t instanceof il?"canceled":t?.message||String(t),e.step=t instanceof il?"canceled":"error",e.finishedAt=Date.now(),await Uy(e).catch(()=>{}),await pl(e).catch(()=>{}),await Eq(e).catch(()=>{})}function Kp(e){let t=Ft.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 Fy(e){let t=Ft.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 vq(e,t,n){if(await Ka(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await Aa(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await pl(e)}else e.base=await Aa(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await Ka(e,"spawn"),await z.mkdir(Xa,{recursive:!0}),e.repos){await z.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await ls(a.leadDir,V.join(e.dir,a.name),e.branch,a.base);let s=Ut((fe(await ee(),e.trackId)||{}).workspace);await ei(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 ls(t.cwd,e.dir,e.branch,e.base),await ol(e.dir);await Sq(e,t);let r=e.trackName.slice(0,38).trim(),o=await Ln(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await Aq(e,o),await u$(e.trackId,o.name,cW(e.task,!!e.repos),{kind:"subtask"}),await O$(e,t)}async function O$(e,t){await Ka(e,"working");let n=V.join(e.dir,xp);if(await Iq(n,e),e.canceled)throw new il;if(await Ka(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=V.join(e.dir,l.name);await Bh(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:r$({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await Uh(l.leadDir,l.base,e.branch,Ga),l.files=await Hh(l.leadDir,l.base,e.branch,Ga);let d=await Wh(l.leadDir,l.base,e.branch,Ga);l.applied=d.applied,l.conflicts=d.conflicts??[],s.files+=l.stat.files,s.insertions+=l.stat.insertions,s.deletions+=l.stat.deletions,a.push(...l.files.map(p=>V.join(l.name,p))),i.push(...l.conflicts.map(p=>p.startsWith("patch did not apply cleanly")?`${l.name}: ${p}`:V.join(l.name,p))),d.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await Bh(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:r$({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await Uh(t.cwd,e.base,e.branch,Ga),e.files=await Hh(t.cwd,e.base,e.branch,Ga);let s=await Wh(t.cwd,e.base,e.branch,Ga);e.applied=s.applied,e.conflicts=s.conflicts}await pl(e);let r=await _q(e,t,n);await Ka(e,"resume"),await u$(e.trackId,e.leadBox,lW(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await Ka(e,"reap"),await Uy(e),e.step="done";let o=Date.now();await _$(e.trackId,e.id,{...qp(e),finishedAt:o}),await Tq(e,o),e.finishedAt=o}async function Sq(e,t){let n=V.join(e.dir,V.dirname(tl));await z.mkdir(n,{recursive:!0});let r="";try{r=await Ax(t.cwd,t.sessionId)}catch{}if(!r)try{r=await z.readFile(V.join(t.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${e.id}
|
|
263
263
|
|
|
264
264
|
**Delegated task:** ${e.task}
|
|
265
265
|
|
|
@@ -270,7 +270,7 @@ ${a}`}async function Ay(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<
|
|
|
270
270
|
## Conversation so far (lead \u2192 you)
|
|
271
271
|
|
|
272
272
|
${r}
|
|
273
|
-
`;await z.writeFile(V.join(e.dir,tl),o)}async function
|
|
273
|
+
`;await z.writeFile(V.join(e.dir,tl),o)}async function Aq(e,t){let n=await ee(),r=fe(n,e.trackId);r&&(r.boxes.push({name:t.name,agent:t.agent,role:"subtask",state:"live",cwd:t.cwd,sessionId:t.sessionId,hookTokenHash:Fo(t.hookToken),createdAt:Date.now(),subtaskId:e.id,branch:e.branch,task:e.task}),Ae(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]||{},...qp(e)},await ae())}async function Tq(e,t=e.finishedAt){let n=await ee(),r=fe(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),Ae(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 Eq(e){let t=await ee(),n=fe(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"),Ae(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]||{},...qp(e)},await ae()}async function _q(e,t,n){let r=`.bostrat/handoff/subtask-${e.id}.md`;try{let o=V.join(t.cwd,r);await z.mkdir(V.dirname(o),{recursive:!0}),await z.copyFile(n,o)}catch{}return r}async function Uy(e){if(e.dir&&await mu(e.dir).catch(()=>{}),e.box&&await Dt(e.box).catch(()=>{}),e.repos&&e.dir){for(let t of e.repos)await _t(t.leadDir,V.join(e.dir,t.name)).catch(()=>{});await z.rm(e.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(e.dir){let n=(await xe(e.leadBox).catch(()=>null))?.cwd;n&&await _t(n,e.dir).catch(()=>{})}}async function Iq(e,t){let n=(t.startedAt||Date.now())+gq;for(;;){if(t.canceled||t.forceMerge)return;try{await z.access(e);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await or(yq)}}function or(e){return new Promise(t=>setTimeout(t,e))}var _p,rr,a$,sW,el,aW,Ip,cl,So,To,ty,iW,xp,tl,cW,lW,Lt,Fg,fe,Ug,Ae,Hg,Wg,pW,Dr,ZI,hW,wW,gW,yW,QI,Xc,vW,SW,Yg,m$,_W,Ja,e$,CW,NW,PW,DW,BW,Va,nl,UW,WW,qW,VW,gy,Zg,dl,xt,Rn,Sp,rl,k$,QW,Zc,Qc,eq,qg,rq,sl,oq,sq,n$,Gg,cq,Kg,Qg,Oy,Is,ni,Cy,On,Tp,Ny,mq,kp,gq,yq,Xa,Ga,Ft,bq,Ms,qp,pl,Gp,il,Tr=q(()=>{Ci();dt();Ah();WS();Qo();Gr();Pu();XS();bt();yn();dd();eA();Gs();Nu();nd();Xo();lh();dh();tA();oc();qm();Du();Qm();xn();fd();Hr();sn();nc();yd();EA();Jh();VE();zu();Ku();JE();jw();ye();Uw();Kd();ap();wa();ps();Wc();Ua();fg();mp();kg();Kn();Qd();RI();Tg();jI();ku();HI();Ig();Pg();Bg();Lg();_p=oW(rW),rr=process.env.BOSTRAT_STATE_DIR||V.join(process.env.HOME||"/tmp",".bostrat"),a$=V.join(rr,"tracks.json"),sW=V.join(rr,"events.jsonl"),el=V.join(rr,"workspaces"),aW=process.env.BOSTRAT_DEFAULT_REPO||null,Ip={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"}},cl=Object.keys(Ip);So={at:0,value:null};To=".bostrat/handoff/latest.md",ty=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;iW=/\(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\.\)$/;xp=".bostrat/handoff/subtask-result.md",tl=".bostrat/handoff/lead-context.md",cW=(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 ${tl} \u2014 read that first for context. Your scoped task:
|
|
274
274
|
|
|
275
275
|
${e}
|
|
276
276
|
|
|
@@ -278,7 +278,7 @@ Work ONLY inside this directory. cd into the repo a change belongs to before run
|
|
|
278
278
|
|
|
279
279
|
${e}
|
|
280
280
|
|
|
281
|
-
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 ${xp} 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.`,
|
|
281
|
+
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 ${xp} 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.`,lW=(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."),Lt=null,Fg=null;fe=(e,t)=>e.tracks.find(n=>n.id===t)||null,Ug=Promise.resolve();Ae=(e,t,n={})=>{let r={at:Date.now(),type:t,...n};return e.events.push(r),uW(e.id,r),$A(e,r),r};Ix(async({sessionName:e,from:t,to:n,version:r,status:o,error:s})=>{let i=(await ee()).tracks.find(c=>c.leadBox===e||(c.boxes||[]).some(l=>l.name===e));i&&(Ae(i,"codex-update",{box:e,from:t,to:r||n,status:o,...s?{error:s}:{}}),await ae().catch(()=>{}))});Hg=V.join(rr,"plane-link","update-notice.json");Wg=(e,t,n)=>t!=null&&n.some(r=>e===r.box&&t>=r.from&&t<=r.to),pW=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);Dr=new Set(["merged","conflict","canceled","failed","handed-off"]),ZI=new Map;hW=5e3;wW={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};gW=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];yW=10*6e4;QI=400;Xc=new Map,vW=Math.max(1e3,Number(process.env.BOSTRAT_ANSWERING_TTL_MS)||15e3),SW=[100,200,300],Yg=new Map,m$=e=>nW("sha1").update(String(e||"")).digest("hex");_W=1500,Ja=new Map;e$="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.";CW=4e3,NW=3,PW=[300,350,400],DW=1e3,BW=12,Va=new Map,nl=new Map;UW=[250,400,500,600];WW=3e4,qW=[350,650,900];VW=12e4;gy=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}))});Zg=new Map,dl=e=>Zg.get(e)||(Lt?fe(Lt,e)?.reshape:null)||null,xt=(e,t=409)=>Object.assign(new Error(e),{status:t});Rn=e=>z.access(e).then(()=>!0,()=>!1),Sp=(e,t,n)=>_p("git",["-C",e,"worktree","move","--end-of-options",t,n],{timeout:6e4});rl="# bostrat workspace",k$=`${rl}
|
|
282
282
|
|
|
283
283
|
TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
|
|
284
284
|
before substantive work \u2014 it is the shared cross-provider memory for this repo
|
|
@@ -678,16 +678,16 @@ for the same follow-up at the same moment twice; run bostrat-followup ls first
|
|
|
678
678
|
specifics (no PR numbers, URLs, SHAs, tags \u2014 the card supplies them), and HOLD
|
|
679
679
|
HERE after proposing until the operator answers the card. Bostrat never runs a
|
|
680
680
|
follow-up on its own; the operator's tap is the gate.
|
|
681
|
-
`,
|
|
682
|
-
`)),
|
|
681
|
+
`,QW=k$,Zc=k$+wv;Qc=e=>typeof e=="string"&&(e===rl||e.startsWith(`${rl}
|
|
682
|
+
`)),eq="# bostrat multi-repo workspace";qg=Zc.replace(`${rl}
|
|
683
683
|
`,`${rl}
|
|
684
684
|
|
|
685
685
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
686
686
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
687
687
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
688
688
|
When working anywhere else, ignore everything below.
|
|
689
|
-
`);YW=new Set(["claude","codex","cursor"]);sl=new Set,XW=e=>sl.has(e);ZW=9e4;e$={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};Kg=new Map;tq=6e4,Vg=new Map;Qg=120;Ry=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:Fo(e.hookToken),createdAt:Date.now()});Is=new Map,ni=e=>Is.get(e)||(Lt?fe(Lt,e)?.handoff:null)||null;My=6e4,Rn=(e,t,n=15e3)=>_p("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),Tp=new Map,Cy=e=>Tp.get(e)||null;iq=/^[A-Za-z0-9][A-Za-z0-9._/-]{0,200}$/;kp=new Map;uq=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,dq=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,Xa=V.join(rr,"worktrees"),Ga=[tl,xp],Ft=new Map,pq=e=>Ft.get(e)||(Lt?Lt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,Ms=e=>{let t=new Map([...Ft.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Lt?fe(Lt,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)},qp=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});pl=e=>A$(e.trackId,e.id,qp(e));Gp=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",il=class extends Error{}});dt();import Re from"node:path";import At from"node:fs";import lz from"node:readline";import{fileURLToPath as uz}from"node:url";import Qt from"express";import yC from"node:http";import dz from"node:https";import ql from"node:os";import{randomUUID as pz}from"node:crypto";import{WebSocketServer as fz}from"ws";dt();import tD from"node-pty";async function tx(e,t,n={}){if(!je(t)){Ou(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await Jr()){Ou(e,`host tmux server not running on ${qt}`),e.close(1011,"tmux server unavailable");return}if(!await xe(t)){let a=!1;if(typeof n.revive=="function"&&(Mu(e,{type:"notice",message:"Resuming the lead box\u2026"}),a=await n.revive().catch(()=>!1)),!a||!await xe(t)){Ou(e,`session "${t}" not found`),e.close(1008,"session not found");return}if(e.readyState!==e.OPEN)return}let r=ex(n.cols,120,nx),o=ex(n.rows,32,rx),s;try{s=tD.spawn(ne,["-S",qt,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){Ou(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&Mu(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(Mu(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"&&sD(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{}oD(t)})}var nD=6,rD=250;async function oD(e,t=nD,n=rD){for(let r=0;r<t;r++){if(await zo(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var nx=20,rx=6;function sD(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=nx&&Number(t)>=rx}function ex(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function Mu(e,t){try{e.send(JSON.stringify(t))}catch{}}function Ou(e,t){Mu(e,{type:"error",message:t})}function ox(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}}xn();Nu();Th();Ar();import{createRequire as mz}from"node:module";var vq=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function Uy({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(vq)&&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}}Dg();ye();Wc();Kn();Jd();on();var Vp=60*6e4,Sq=1440*6e4,Aq=new Set(["merged","closed","lost"]),fl=(e,t,n)=>Number.isFinite(Number(e))&&n-Number(e)<=t;function Tq({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)<=og&&s.push("ship"),r>0&&s.push("create-net");for(let a of n||[]){if(!a||Aq.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&&fl(a.ciFix.delegatedAt,Vp,o)&&s.push(`ci-fix#${l}`),a.reconcile&&fl(a.reconcile.delegatedAt,Vp,o)&&s.push(`reconcile#${l}`),a.ciFixAwait&&fl(a.ciFixAwait.since,Vp,o)&&s.push(`ci-fix-await#${l}`),a.reconcileAwait&&fl(a.reconcileAwait.since,Vp,o)&&s.push(`reconcile-await#${l}`);let u=a.autoMerge?.armedAt??a.armedAt??null;a.trackId===e&&(a.autoMerge||a.armedAt)&&fl(u,Sq,o)&&s.push(`auto-merge#${l}`)}return s}var zp=new Map;Ye.on("event",e=>{e?.trackId&&zp.delete(e.trackId)});async function Eq(e){if(zp.has(e.id))return zp.get(e.id);let t=await K(e).catch(()=>[]),n=ja(t,-1/0);return zp.set(e.id,n),n}async function R$(e,{now:t=Date.now()}={}){let n=new Map,r=await An().catch(()=>[]);for(let o of e||[]){if(!o||Fe(o))continue;let s=Tq({trackId:o.id,shipPending:await Eq(o),links:r,observers:dE(o.id),now:t});s.length&&n.set(o.id,s)}return n}$g();import H$ from"node:fs";import _o from"node:fs/promises";import qq from"node:os";import gt from"node:path";import{spawn as Gq}from"node:child_process";import{createRequire as Kq}from"node:module";ye();yd();import ri from"node:fs";import hl from"node:fs/promises";import $q from"node:http";import Jt from"node:path";import Yp from"node:os";import{createRequire as Rq}from"node:module";import Wy from"ws";import _q from"node:path";import Iq from"node:os";var ml=process.env.BOSTRAT_STATE_DIR||_q.join(process.env.HOME||Iq.tmpdir(),".bostrat");function Hy(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 O$(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=Hy(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function M$(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}Jf();var On=(()=>{try{return Rq(import.meta.url)("../package.json").version||null}catch{return null}})(),Gy=process.env.BOSTRAT_STATE_DIR||Jt.join(process.env.HOME||Yp.tmpdir(),".bostrat"),L$=Jt.join(Gy,"plane-link","removed.json");function C$(e){return typeof e!="string"||e.length<8?"\u2022\u2022\u2022\u2022":`${e.slice(0,6)}\u2026${e.slice(-4)}`}function Oq(e=L$){try{let t=JSON.parse(ri.readFileSync(e,"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function Cs({env:e=process.env,pkgDir:t=Jt.resolve(Jt.dirname(new URL(import.meta.url).pathname),".."),exists:n=ri.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(Jt.join(t,"..",".git"))?"git-checkout":"dev"}function wl(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 Jp(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 Mq({home:e=process.env.HOME||Yp.homedir(),env:t=process.env}={}){let n=r=>{try{return ri.statSync(r),!0}catch{return!1}};return{claude:n(Jt.join(e,".claude",".credentials.json")),codex:n(Jt.join(t.CODEX_HOME||Jt.join(e,".codex"),"auth.json"))}}var ft=1,N$=1e3,P$=3e4,Cq=6e4,Nq=1500,Pq=500,Eo=100,Dq=2e3,Bq=25e3,jq=6e4,F$=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},Lq=F$(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),Fq=3e4,D$=10*6e4,Uq=400,B$=250,Hq=F$(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),qy=4,Wq=6e4,j$=6e4;function U$({url:e,token:t,nodeId:n=Yp.hostname(),name:r=Yp.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,deliverySample:c=null,onHashKey:l=null,onViewBase:u=null,metricsMs:d=Wq,onUpdateAvailable:p=null,onUpdatePolicy:f=null,onHelloRefused:w=null,updatePolicy:m=null,installKind:g=Cs(),selfUrl:b=`http://127.0.0.1:${process.env.PORT||3362}`,apiToken:y=null,removedMarkerFile:x=L$,onRemoved:S=null,onDegraded:$=null,onRecovered:M=null,onConnected:D=null,ingestMode:j=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:A=Lq,unackedRetryMs:_=jq,pingMs:L=Bq,maxInflightEvents:G=Uq,httpTimeoutMs:O=Hq,stallBackoffMinMs:T=Fq,fetchImpl:C=globalThis.fetch,log:P=console}){if(!e||!t)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");e=M$(e);let W=b.replace(/^http/,"ws"),X=new Set,ge=new Map,Ie=new Map,Pe=Jt.join(Gy,"plane-link","cursor.json"),ee=Jt.join(Gy,"plane-link","dead-letter.ndjson"),Ze=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),Ee={},le={},re=new Map,$e=null,J=null,he=0,Be=0,R=0,ie=Promise.resolve(),De="ws",Ot=0,Z=!1,ke=0,nt=!1,Qe=Eo,Tt=new Set,Ht=[],lt=null,Mt=null,yt=!1,mn=!1,jo=N$,mt=null,Pn=null,Lr=null,en=null,hn=null,bi=!1,k=0,h=!1,v=Oq(x);if(v)if(v.tokenPreview===C$(t))h=!0,zf({reason:v.reason??"removed",at:v.at??null,nodeId:v.nodeId??n}),P.error(`[plane-link] this node was REMOVED from the plane (${v.reason??"removed"}${v.at?` at ${new Date(v.at).toISOString()}`:""}) \u2014 not dialing. Re-provision it with a new node token, or delete ${x} to force a dial.`);else try{ri.unlinkSync(x),P.log("[plane-link] stale removed-marker discarded \u2014 the node token changed")}catch{}function B({reason:F="removed",at:H=Date.now(),via:E="ctl"}={}){if(!h){h=!0,zf({reason:F,at:H,nodeId:n,via:E});try{ri.mkdirSync(Jt.dirname(x),{recursive:!0}),ri.writeFileSync(x,JSON.stringify({at:H,reason:F,via:E,nodeId:n,tokenPreview:C$(t)},null,2))}catch(U){P.error(`[plane-link] could not write ${x}: ${U?.message||U}`)}P.error(`[plane-link] the plane REMOVED this node (${F}, via ${E}) \u2014 going dormant: no more dials, ingest or broker calls. Re-provision with a new node token to reconnect; the box and its data are untouched.`);try{S?.({reason:F,at:H,via:E})}catch{}$k({bye:!1})}}async function Y(){try{Ee=JSON.parse(await hl.readFile(Pe,"utf8"))}catch{Ee={}}}function we(){bi=!0,!Lr&&(Lr=setTimeout(async()=>{if(Lr=null,!!bi){bi=!1;try{await hl.mkdir(Jt.dirname(Pe),{recursive:!0}),await hl.writeFile(Pe,JSON.stringify(Ee))}catch(F){P.error("[plane-link] cursor flush failed:",F?.message||F)}}},Dq),Lr.unref?.())}function ce(F){return!Mt||Mt.readyState!==Wy.OPEN?!1:(Mt.send(JSON.stringify(F)),!0)}function ot(F){let H=Date.now();for(let{trackId:E,event:U}of F){let te=re.get(U.id);re.set(U.id,{trackId:E,seq:U.seq,event:U,sentAt:H,firstSentAt:te?.firstSentAt??H,retryAt:H+_,tries:(te?.tries??0)+1,nacks:te?.nacks??0})}return De==="http"?(PC(F),!0):ce({v:ft,ch:"events",type:"append",events:F})}function ht(F){let H=null;for(let E of re.values())E.trackId===F&&(H==null||E.seq<H)&&(H=E.seq);return H}function qs(F){let H=le[F];if(H==null)return;let E=ht(F),U=E==null?H:Math.min(H,E-1);U>(Ee[F]??0)&&(Ee[F]=U,we())}function tn(){if($e=Date.now(),J!=null){let F=Date.now()-J;P.log(`[plane-link] spine sync recovered after ${Math.round(F/1e3)}s degraded (${he} stall redial(s))`);let H=J;J=null,he=0,Be=0,Z=!1,ke=0;try{M?.({since:H,downMs:F})}catch(E){P.error(`[plane-link] onRecovered failed: ${E?.message||E}`)}}}function jf(F){if(!Array.isArray(F)||!F.length)return;let H=new Set;for(let E of F){let U=re.get(E);U&&(re.delete(E),(le[U.trackId]??0)<U.seq&&(le[U.trackId]=U.seq),H.add(U.trackId))}for(let E of H)qs(E);tn()}function xk(F,H){R++;let E={at:Date.now(),trackId:F.trackId,seq:F.seq,id:F.event?.id??null,type:F.event?.type??null,reason:H,event:F.event??null};P.error(`[plane-link] DEAD-LETTER ${F.trackId} seq ${F.seq} (${E.type??"?"}): plane rejected it permanently (${H}) \u2014 kept in ${ee}`),ie=ie.then(async()=>{await hl.mkdir(Jt.dirname(ee),{recursive:!0}),await hl.appendFile(ee,JSON.stringify(E)+`
|
|
690
|
-
`)}).catch(U=>P.error(`[plane-link] dead-letter write failed: ${U?.message||U}`))}function vk(F,H,E,U=null){if(!Array.isArray(F)||!F.length)return;let te=new Set,ve=Date.now();for(let Te of F){let Ke=re.get(Te);Ke&&(E?(re.delete(Te),xk(Ke,H),(le[Ke.trackId]??0)<Ke.seq&&(le[Ke.trackId]=Ke.seq),te.add(Ke.trackId)):(Ke.nacks++,Ke.retryAt=ve+Math.max(1e3,Number(U)||_)))}for(let Te of te)qs(Te);tn()}function Sk(F=Date.now()){let H=1/0;for(let E of re.values())E.firstSentAt<H&&(H=E.firstSentAt);return H===1/0?0:F-H}function Ak(){if(!yt||!re.size)return;let F=Date.now(),H=Sk(F);if(H<A||$e!=null&&F-$e<A||De==="http"&&Z||Be>F)return;he++;let E=Math.min(T*2**(he-1),D$);if(J==null){J=F-H,P.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(H/1e3)}s with ${re.size} event(s) in flight (${De} door) \u2014 the plane is not answering appends`);try{$?.({since:J,unacked:re.size,oldestAgeMs:H,mode:De})}catch(U){P.error(`[plane-link] onDegraded failed: ${U?.message||U}`)}}if(De==="http"){Z=!0,ke=F+E,P.error(`[plane-link] ingest circuit OPEN (stall #${he}); probing /v1/ingest/health in ${Math.round(E/1e3)}s`);return}Be=F+E,P.error(`[plane-link] closing the link and redialing in ${Math.round(E/1e3)}s (stall redial #${he})`);try{Mt?.terminate()}catch{}}function Lf(){if(!yt)return;let F=Date.now();if(De==="http"&&Z)return;let H=[...re.values()].filter(U=>U.event&&U.retryAt<=F&&!(De==="http"&&Tt.has(U.trackId)));if(!H.length)return;H.sort((U,te)=>U.trackId===te.trackId?U.seq-te.seq:U.trackId<te.trackId?-1:1),P.log(`[plane-link] re-sending ${H.length} unacked event(s) the plane never confirmed`);let E=De==="http"?Qe:Eo;if(De==="http"){let U=new Map;for(let te of H)(U.get(te.trackId)??U.set(te.trackId,[]).get(te.trackId)).push(te);for(let[,te]of U){if(Ot>=qy)return;ot(te.slice(0,E).map(ve=>({trackId:ve.trackId,event:ve.event})))}return}for(let U=0;U<H.length;U+=E){let te=H.slice(U,U+E).map(ve=>({trackId:ve.trackId,event:ve.event}));if(!ot(te))return}}async function CC(F=null){for(;;){if(!yt)return!1;let H=re.size<G,E=De!=="http"||Ot<qy&&!Z&&(!F||!Tt.has(F));if(H&&E)return!0;await new Promise(U=>setTimeout(U,100))}}function Ff(){if(lt=null,!yt||!Ht.length)return;let F=Ht;if(Ht=[],De!=="http"){for(let E=0;E<F.length;E+=Eo)ot(F.slice(E,E+Eo));return}let H=new Map;for(let E of F)(H.get(E.trackId)??H.set(E.trackId,[]).get(E.trackId)).push(E);for(let[E,U]of H){if(Tt.has(E)||Ot>=qy||Z){Ht.push(...U);continue}for(let te=0;te<U.length;te+=Qe)ot(U.slice(te,te+Qe))}Ht.length&&!lt&&(lt=setTimeout(Ff,B$),lt.unref?.())}let NC=F=>{let H=new Map;for(let{trackId:E,event:U}of F)(H.get(E)??H.set(E,[]).get(E)).push(U);return[...H].map(([E,U])=>({trackId:E,events:U}))},Lo=(F,H,E,{progress:U})=>{let te=Date.now();for(let{event:ve}of F){let Te=re.get(ve.id);Te&&(Te.nacks++,Te.retryAt=te+Math.max(1e3,E||_))}U&&tn()};async function PC(F){let H=new Set(F.map(E=>E.trackId));for(let E of H)Tt.add(E);Ot++;try{let E=await C(`${Ze}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:NC(F)}),signal:AbortSignal.timeout(O)}),U=(Number(E.headers.get("retry-after"))||0)*1e3;if(E.status===200){let te=await E.json();Qe=Eo;for(let ve of te.results??[]){jf(ve.acked??[]);let Te=(Ke,Ct)=>{let nn=new Map;for(let st of Ke??[])(nn.get(st.reason)??nn.set(st.reason,[]).get(st.reason)).push(st.id);for(let[st,Rk]of nn)vk(Rk,st,Ct,te.retryAfterMs??null)};Te(ve.rejected,!0),Te(ve.deferred,!1)}jf(te.discarded??[]),tn();return}if(E.status===429){Lo(F,"overloaded",U||5e3,{progress:!0});return}if(E.status===413&&F.length>1){Qe=Math.max(1,Math.floor(F.length/2)),P.error(`[plane-link] ingest request too large (${F.length} events) \u2014 halving batches to ${Qe}`),Lo(F,"too_large",1e3,{progress:!0});return}if(E.status===413){let te=re.get(F[0].event.id);te&&(re.delete(te.event.id),xk(te,"too_large"),(le[te.trackId]??0)<te.seq&&(le[te.trackId]=te.seq),qs(te.trackId)),tn();return}if(E.status===410){B({reason:"removed",via:"410"}),Lo(F,"http_410",6e4,{progress:!1});return}if(E.status===401||E.status===403){let{tripped:te,holdMs:ve}=Js();if(te&&P.error(`[plane-link] holding every plane call for ${Math.round(ve/6e4)} min \u2014 the ingest door refused this node's token three times in a row`),Date.now()-k>=j$){k=Date.now();let Te="";try{Te=(await E.json())?.error??""}catch{}P.error(`[plane-link] plane refused ingest (HTTP ${E.status}${Te?`: ${Te}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}Lo(F,`http_${E.status}`,Math.max(6e4,Ei()),{progress:!1});return}if(E.status===400){P.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${F.length} event(s) for retry; this is a bug, not data`),Lo(F,"http_400",6e4,{progress:!1});return}Lo(F,`http_${E.status}`,U||5e3,{progress:!1})}catch(E){let U=E?.name==="TimeoutError"||E?.name==="AbortError";U&&F.length>1&&(Qe=Math.max(1,Math.floor(F.length/2)),P.error(`[plane-link] ingest request of ${F.length} event(s) outlived its ${O} ms deadline \u2014 halving batches to ${Qe}`)),Lo(F,U?"timeout":"network",5e3,{progress:!1})}finally{Ot--;for(let E of H)Tt.delete(E)}}async function DC(){if(De!=="http"||!Z||!yt||nt)return;let F=Date.now();if(!(ke>F||Ys(F))){nt=!0;try{let H=await C(`${Ze}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(O)});if(!H.ok)throw new Error(`HTTP ${H.status}`);P.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),Z=!1,ke=0,Be=Date.now()+O+_+1e3;for(let E of re.values())E.retryAt=Math.min(E.retryAt,Date.now());Lf()}catch(H){he++;let E=Math.min(T*2**(he-1),D$);ke=Date.now()+E,P.error(`[plane-link] ingest door still not answering (${H?.message||H}); next probe in ${Math.round(E/1e3)}s`)}finally{nt=!1}}}function Tk(){let F=Date.now();return{mode:De,unacked:re.size,oldestUnackedAgeMs:Sk(F),lastProgressAt:$e,degradedSince:J,stallRedials:he,deadLettered:R,http:De==="http"?{inFlight:Ot,circuitOpen:Z,nextProbeInMs:Z?Math.max(0,ke-F):0,batchCap:Qe}:null}}async function Ek(){if(!i)return;let F;try{F=await i()}catch(E){P.error("[plane-link] metrics sample failed:",E?.message||E);return}let H=null;try{H=c?.()??null}catch{}F&&ce({v:ft,ch:"ctl",type:"metrics",metrics:{...F,planeLink:Tk(),...H?{delivery:H}:{}}})}function BC(){i&&(hn&&clearInterval(hn),Ek(),hn=setInterval(Ek,d),hn.unref?.())}async function jC(){let F;try{F=await o()}catch(H){P.error("[plane-link] backfill listTracks failed:",H?.message||H);return}ce({v:ft,ch:"ctl",type:"tracks",ids:F.filter(H=>!H.transferredTo).map(H=>H.id).filter(H=>typeof H=="string")});for(let H of F){if(!yt)return;let E;try{E=await K(H)}catch{continue}let U=Ee[H.id]??0,te=E.filter(Te=>Te.seq>U&&typeof Te.id=="string"),ve=De==="http"?Qe:Eo;for(let Te=0;Te<te.length;Te+=ve){if(!await CC(H.id))return;let Ke=te.slice(Te,Te+Math.min(ve,De==="http"?Qe:Eo)).map(Ct=>({trackId:H.id,event:Ct}));if(!ot(Ke))return}}}function _k(F){!yt||!F?.trackId||typeof F.id!="string"||(Ht.push({trackId:F.trackId,event:F}),lt||(lt=setTimeout(Ff,B$),lt.unref?.()))}let ki=new Map;async function LC(F){let{reqId:H,method:E,path:U,headers:te={},body:ve}=F,Te=Object.fromEntries(Object.entries(te).filter(([,Ct])=>Ct!=null)),Ke=new AbortController;ki.set(H,Ke);try{if(F.host){await FC(F,Te,Ke);return}y&&(Te["x-bostrat-token"]=y);let Ct=await fetch(b+U,{method:E,headers:Te,body:ve?Buffer.from(ve,"base64"):void 0,signal:Ke.signal}),nn=Object.fromEntries(Ct.headers);for(let st of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete nn[st];if(nn["content-type"]||(nn["content-type"]="application/octet-stream"),ce({v:ft,ch:"control",type:"http_head",reqId:H,status:Ct.status,headers:nn}),Ct.body){for await(let st of Ct.body)if(!ce({v:ft,ch:"control",type:"http_data",reqId:H,data:Buffer.from(st).toString("base64")}))break}ce({v:ft,ch:"control",type:"http_end",reqId:H,error:null})}finally{ki.delete(H)}}function FC(F,H,E){let{reqId:U,method:te,path:ve,body:Te}=F,Ke=new URL(b);return new Promise(Ct=>{let nn=$q.request({host:Ke.hostname,port:Ke.port,method:te,path:ve,headers:{...H,host:F.host},signal:E.signal},st=>{let{connection:Rk,"keep-alive":Cz,"transfer-encoding":Nz,upgrade:Pz,...UC}=st.headers;ce({v:ft,ch:"control",type:"http_head",reqId:U,status:st.statusCode||502,headers:UC}),st.on("data",HC=>{ce({v:ft,ch:"control",type:"http_data",reqId:U,data:Buffer.from(HC).toString("base64")})||st.destroy()}),st.on("end",()=>{ce({v:ft,ch:"control",type:"http_end",reqId:U,error:null}),Ct()}),st.on("error",()=>{ce({v:ft,ch:"control",type:"http_end",reqId:U,error:"view upstream error"}),Ct()})});nn.on("error",st=>{ce({v:ft,ch:"control",type:"http_end",reqId:U,error:st?.message||"view request failed"}),Ct()}),Te&&nn.write(Buffer.from(Te,"base64")),nn.end()})}function Ik(){if(mn||h)return;Mt=new Wy(e,{headers:{authorization:`Bearer ${t}`}}),Mt.on("open",async()=>{let H=null;try{H=await s?.()||null}catch{H=null}ce({v:ft,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:On,installKind:g,...typeof m=="function"?{updatePolicy:m()}:m?{updatePolicy:m}:{},providers:Mq(),...H?{agents:H}:{}})}),Mt.on("message",H=>{let E;try{E=JSON.parse(H)}catch{return}if(E.ch==="ctl"&&E.type==="hello_ok"){yt=!0,jo=N$,mt=null,jk(),P.log(`[plane-link] connected to plane as ${n}`);try{D?.()}catch{}if(typeof E.hashKey=="string"&&E.hashKey)try{l?.(E.hashKey)}catch{}try{u?.(typeof E.viewBase=="string"&&E.viewBase?E.viewBase:null)}catch{}E.minNodeVersion&&On&&wl(On,E.minNodeVersion)&&P.error(`[plane-link] this node is bostrat-node ${On}; the plane asks for \u2265${E.minNodeVersion} \u2014 update with: ${Jp(g)}`);try{f?.(typeof E.updatePolicy=="string"?E.updatePolicy:null)}catch{}let U=typeof E.latestNodeVersion=="string"?E.latestNodeVersion:null;if(U&&On&&wl(On,U)&&!X.has(U)){X.add(U),P.log(`[plane-link] bostrat-node ${U} is available (this node runs ${On}) \u2014 ${Jp(g)}`);try{p?.({current:On,latest:U,min:E.minNodeVersion??null,installKind:g,required:!!(E.minNodeVersion&&wl(On,E.minNodeVersion))})}catch(ve){P.error(`[plane-link] update notice failed: ${ve?.message||ve}`)}}let te=E.ingest==="http";De=j==="ws"?"ws":j==="http"||te?"http":"ws",j==="http"&&!te&&P.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),De==="http"&&P.log("[plane-link] spine rides the HTTPS ingest door"),Z=!1,ke=0,Qe=Eo,en&&clearInterval(en),en=setInterval(()=>{ce({v:ft,ch:"ctl",type:"ping"}),Ak(),DC().catch(()=>{}),Lf(),Ht.length&&!lt&&Ff()},L),en.unref?.(),BC(),jC().catch(ve=>P.error("[plane-link] backfill failed:",ve?.message||ve));return}if(E.ch==="ctl"&&E.type==="hello_refused"){let U=typeof E.minNodeVersion=="string"?E.minNodeVersion:null,te=typeof E.latestNodeVersion=="string"?E.latestNodeVersion:null;P.error(`[plane-link] plane REFUSED this node: bostrat-node ${On??"?"} is below its minimum ${U??"?"} \u2014 ${E.fix||Jp(g)}`),jo=P$,mt=0;try{w?.({current:On,min:U,latest:te,fix:E.fix||Jp(g),installKind:g})}catch(ve){P.error(`[plane-link] refusal handler failed: ${ve?.message||ve}`)}return}if(E.ch==="ctl"&&E.type==="removed"){ce({v:ft,ch:"ctl",type:"removed_ack",nodeId:n}),B({reason:typeof E.reason=="string"?E.reason:"removed",at:Number(E.at)||Date.now(),via:"ctl"});return}if(E.ch==="ctl"&&E.type==="update_policy"){try{f?.(typeof E.mode=="string"?E.mode:null)}catch{}return}if(E.ch==="ctl"&&E.type==="usage_caps"){gd(E.caps??{},{source:"plane",updatedAt:E.updatedAt??Date.now()}).then(U=>P.log(`[plane-link] spend caps ${Object.keys(U.caps).length?`updated (${Object.keys(U.caps).join(", ")})`:"cleared"} from plane`)).catch(U=>P.error("[plane-link] spend-caps push rejected:",U?.message||U));return}if(E.ch==="ctl"&&E.type==="transferred"&&Array.isArray(E.tracks)){Promise.resolve().then(()=>a?.(E.tracks)).catch(U=>P.error("[plane-link] transfer notice failed:",U?.message||U));return}if(E.ch==="events"&&E.type==="ack"&&Array.isArray(E.ids)){jf(E.ids);return}if(E.ch==="events"&&E.type==="nack"&&Array.isArray(E.ids)){vk(E.ids,E.reason,E.permanent===!0,E.retryAfterMs??null);return}if(E.ch==="control"&&E.type==="http_open"){LC(E).catch(U=>ce({v:ft,ch:"control",type:"http_end",reqId:E.reqId,error:U?.message||"tunnel error"}));return}if(E.ch==="control"&&E.type==="http_cancel"){ki.get(E.reqId)?.abort();return}if(E.ch==="attach"&&E.type==="open"){let U=new Wy(W+E.path,Array.isArray(E.protocols)?E.protocols:[],E.host?{headers:{host:E.host}}:void 0);ge.set(E.streamId,U),Ie.set(E.streamId,[]),U.on("open",()=>{let ve=Ie.get(E.streamId)||[];Ie.delete(E.streamId);for(let{buf:Te,bin:Ke}of ve)U.send(Te,{binary:Ke})}),U.on("message",(ve,Te)=>{let Ke=Te?ve:Buffer.from(String(ve));ce({v:ft,ch:"attach",type:"data",streamId:E.streamId,data:Ke.toString("base64"),...Te?{bin:!0}:{}})});let te=()=>{Ie.delete(E.streamId),ge.delete(E.streamId)&&ce({v:ft,ch:"attach",type:"close",streamId:E.streamId})};U.on("close",te),U.on("error",te);return}if(E.ch==="attach"&&E.type==="data"){let U=ge.get(E.streamId);if(!U)return;let te=Buffer.from(E.data,"base64"),ve=E.bin===!0,Te=Ie.get(E.streamId);Te?Te.push({buf:te,bin:ve}):U.send(te,{binary:ve});return}if(E.ch==="attach"&&E.type==="close"){let U=ge.get(E.streamId);ge.delete(E.streamId),Ie.delete(E.streamId),U?.close();return}});let F=()=>{yt&&P.log("[plane-link] disconnected from plane"),yt=!1,en&&(clearInterval(en),en=null),hn&&(clearInterval(hn),hn=null),re.clear(),le={},Ht=[],lt&&(clearTimeout(lt),lt=null),Tt.clear();for(let U of ge.values())U.close();ge.clear(),Ie.clear();for(let U of ki.values())U.abort();if(ki.clear(),mn||Pn)return;mt==null&&(mt=Date.now());let H=Date.now()-mt<Cq,E=Math.max(H?Nq+Math.random()*Pq:jo,Be-Date.now(),Ei());Pn=setTimeout(()=>{Pn=null,Ik()},E),Pn.unref?.(),H||(jo=Math.min(jo*2,P$))};Mt.on("close",F),Mt.on("error",H=>{let E=/Unexpected server response: (\d+)/.exec(H?.message||"")?.[1];if(E==="410"){B({reason:"removed",via:"410"});return}if(E==="401"||E==="403"){let{tripped:U,holdMs:te}=Js();U?P.error(`[plane-link] holding every plane call for ${Math.round(te/6e4)} min \u2014 three credential refusals in a row (HTTP ${E}) mean the token is revoked or unknown, not that the plane is down; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN. Next dial in ${Math.round(te/6e4)} min.`):Date.now()-k>=j$&&(k=Date.now(),P.error(`[plane-link] plane refused this node's credentials (HTTP ${E}) \u2014 the token was revoked or is unknown to the plane; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN (still retrying)`))}F()})}Ye.on("event",_k),Y().then(Ik);function $k({bye:F=!0}={}){mn=!0,Ye.off("event",_k),Pn&&clearTimeout(Pn),Lr&&clearTimeout(Lr),en&&clearInterval(en),hn&&clearInterval(hn),lt&&clearTimeout(lt),F&&ce({v:ft,ch:"ctl",type:"bye",reason:"shutdown"}),Mt?.close()}return{stop:()=>$k(),status:()=>({connected:yt,nodeId:n,cursor:{...Ee},inflight:re.size,...Tk(),auth:Lk(),dormant:h,removed:ru()}),_tick:()=>{Ak(),Lf()}}}Pu();var Xp=(()=>{try{return Kq(import.meta.url)("../package.json").version||null}catch{return null}})(),Vq=process.env.BOSTRAT_STATE_DIR||gt.join(process.env.HOME||qq.tmpdir(),".bostrat"),W$=gt.join(Vq,"self-update"),Zp=gt.join(W$,"status.json"),zq=1440*6e4,Jq=6e4,gl=new Set(["npm-global","git-checkout"]);function q$({kind:e=Cs(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:gl.has(e)?e==="git-checkout"||Yq(t)?"auto":"prompt":"off"}function Yq(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var Yt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function Xq({kind:e,to:t,from:n=Xp,statusFile:r=Zp,logFile:o=gt.join(W$,"last.log"),port:s=Number(process.env.PORT)||3362,npmPrefix:a=null,serviceName:i=process.platform==="darwin"?"com.bostrat.node":"bostrat-node.service",uid:c=typeof process.getuid=="function"?process.getuid():0,liveDir:l=process.env.BOSTRAT_LIVE_CHECKOUT||gt.resolve(gt.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!gl.has(e))throw new Error(`self-update: install kind "${e}" is not actionable here`);if(!/^\d+\.\d+\.\d+$/.test(String(t)))throw new Error(`self-update: bad target version "${t}"`);if(n&&!/^\d+\.\d+\.\d+$/.test(String(n)))throw new Error(`self-update: bad current version "${n}"`);let d=(g,b="")=>`write_status ${Yt(g)} ${b}`,p=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${Yt(i)}`,f=`
|
|
689
|
+
`);rq=new Set(["claude","codex","cursor"]);sl=new Set,oq=e=>sl.has(e);sq=9e4;n$={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};Gg=new Map;cq=6e4,Kg=new Map;Qg=120;Oy=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:Fo(e.hookToken),createdAt:Date.now()});Is=new Map,ni=e=>Is.get(e)||(Lt?fe(Lt,e)?.handoff:null)||null;Cy=6e4,On=(e,t,n=15e3)=>_p("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),Tp=new Map,Ny=e=>Tp.get(e)||null;mq=/^[A-Za-z0-9][A-Za-z0-9._/-]{0,200}$/;kp=new Map;gq=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,yq=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,Xa=V.join(rr,"worktrees"),Ga=[tl,xp],Ft=new Map,bq=e=>Ft.get(e)||(Lt?Lt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,Ms=e=>{let t=new Map([...Ft.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Lt?fe(Lt,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)},qp=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});pl=e=>_$(e.trackId,e.id,qp(e));Gp=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",il=class extends Error{}});dt();import Re from"node:path";import At from"node:fs";import wz from"node:readline";import{fileURLToPath as gz}from"node:url";import en from"express";import xC from"node:http";import yz from"node:https";import ql from"node:os";import{randomUUID as bz}from"node:crypto";import{WebSocketServer as kz}from"ws";dt();import oD from"node-pty";async function nx(e,t,n={}){if(!je(t)){Ou(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await Jr()){Ou(e,`host tmux server not running on ${qt}`),e.close(1011,"tmux server unavailable");return}if(!await xe(t)){let a=!1;if(typeof n.revive=="function"&&(Mu(e,{type:"notice",message:"Resuming the lead box\u2026"}),a=await n.revive().catch(()=>!1)),!a||!await xe(t)){Ou(e,`session "${t}" not found`),e.close(1008,"session not found");return}if(e.readyState!==e.OPEN)return}let r=tx(n.cols,120,rx),o=tx(n.rows,32,ox),s;try{s=oD.spawn(ne,["-S",qt,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){Ou(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&Mu(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(Mu(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"&&cD(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{}iD(t)})}var sD=6,aD=250;async function iD(e,t=sD,n=aD){for(let r=0;r<t;r++){if(await zo(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var rx=20,ox=6;function cD(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=rx&&Number(t)>=ox}function tx(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function Mu(e,t){try{e.send(JSON.stringify(t))}catch{}}function Ou(e,t){Mu(e,{type:"error",message:t})}function sx(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}}xn();Nu();Ah();Tr();import{createRequire as xz}from"node:module";var $q=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function Hy({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($q)&&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}}Pg();ye();Wc();Kn();Jd();sn();var Vp=60*6e4,Rq=1440*6e4,Oq=new Set(["merged","closed","lost"]),fl=(e,t,n)=>Number.isFinite(Number(e))&&n-Number(e)<=t;function Mq({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)<=rg&&s.push("ship"),r>0&&s.push("create-net");for(let a of n||[]){if(!a||Oq.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&&fl(a.ciFix.delegatedAt,Vp,o)&&s.push(`ci-fix#${l}`),a.reconcile&&fl(a.reconcile.delegatedAt,Vp,o)&&s.push(`reconcile#${l}`),a.ciFixAwait&&fl(a.ciFixAwait.since,Vp,o)&&s.push(`ci-fix-await#${l}`),a.reconcileAwait&&fl(a.reconcileAwait.since,Vp,o)&&s.push(`reconcile-await#${l}`);let u=a.autoMerge?.armedAt??a.armedAt??null;a.trackId===e&&(a.autoMerge||a.armedAt)&&fl(u,Rq,o)&&s.push(`auto-merge#${l}`)}return s}var zp=new Map;Ye.on("event",e=>{e?.trackId&&zp.delete(e.trackId)});async function Cq(e){if(zp.has(e.id))return zp.get(e.id);let t=await K(e).catch(()=>[]),n=ja(t,-1/0);return zp.set(e.id,n),n}async function C$(e,{now:t=Date.now()}={}){let n=new Map,r=await Tn().catch(()=>[]);for(let o of e||[]){if(!o||Fe(o))continue;let s=Mq({trackId:o.id,shipPending:await Cq(o),links:r,observers:fE(o.id),now:t});s.length&&n.set(o.id,s)}return n}Ig();import G$ from"node:fs";import _o from"node:fs/promises";import Xq from"node:os";import gt from"node:path";import{spawn as Zq}from"node:child_process";import{createRequire as Qq}from"node:module";ye();yd();import ri from"node:fs";import hl from"node:fs/promises";import Dq from"node:http";import Yt from"node:path";import Yp from"node:os";import{createRequire as Bq}from"node:module";import qy from"ws";import Nq from"node:path";import Pq from"node:os";var ml=process.env.BOSTRAT_STATE_DIR||Nq.join(process.env.HOME||Pq.tmpdir(),".bostrat");function Wy(e=process.env){let t=(e.BOSTRAT_HOST||"").trim();if(t!=="all")return t||(e.BOSTRAT_PLANE_URL?"127.0.0.1":void 0)}function N$(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=Wy(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function P$(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}Jf();var Mn=(()=>{try{return Bq(import.meta.url)("../package.json").version||null}catch{return null}})(),Ky=process.env.BOSTRAT_STATE_DIR||Yt.join(process.env.HOME||Yp.tmpdir(),".bostrat"),H$=Yt.join(Ky,"plane-link","removed.json");function D$(e){return typeof e!="string"||e.length<8?"\u2022\u2022\u2022\u2022":`${e.slice(0,6)}\u2026${e.slice(-4)}`}function jq(e=H$){try{let t=JSON.parse(ri.readFileSync(e,"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function Cs({env:e=process.env,pkgDir:t=Yt.resolve(Yt.dirname(new URL(import.meta.url).pathname),".."),exists:n=ri.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(Yt.join(t,"..",".git"))?"git-checkout":"dev"}function wl(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 Jp(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 Lq({home:e=process.env.HOME||Yp.homedir(),env:t=process.env}={}){let n=r=>{try{return ri.statSync(r),!0}catch{return!1}};return{claude:n(Yt.join(e,".claude",".credentials.json")),codex:n(Yt.join(t.CODEX_HOME||Yt.join(e,".codex"),"auth.json"))}}var ft=1,B$=1e3,j$=3e4,Fq=6e4,Uq=1500,Hq=500,Eo=100,Wq=2e3,qq=25e3,Gq=6e4,W$=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},Kq=W$(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),Vq=3e4,L$=10*6e4,zq=400,F$=250,Jq=W$(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),Gy=4,Yq=6e4,U$=6e4;function q$({url:e,token:t,nodeId:n=Yp.hostname(),name:r=Yp.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,deliverySample:c=null,onHashKey:l=null,onViewBase:u=null,metricsMs:d=Yq,onUpdateAvailable:p=null,onUpdatePolicy:f=null,onHelloRefused:w=null,updatePolicy:m=null,installKind:y=Cs(),selfUrl:k=`http://127.0.0.1:${process.env.PORT||3362}`,apiToken:g=null,removedMarkerFile:x=H$,onRemoved:S=null,onDegraded:$=null,onRecovered:M=null,onConnected:D=null,ingestMode:j=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:A=Kq,unackedRetryMs:_=Gq,pingMs:L=qq,maxInflightEvents:G=zq,httpTimeoutMs:O=Jq,stallBackoffMinMs:T=Vq,fetchImpl:C=globalThis.fetch,log:P=console}){if(!e||!t)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");e=P$(e);let W=k.replace(/^http/,"ws"),X=new Set,ge=new Map,Ie=new Map,Ce=Yt.join(Ky,"plane-link","cursor.json"),Z=Yt.join(Ky,"plane-link","dead-letter.ndjson"),Ze=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),Ee={},le={},re=new Map,$e=null,J=null,he=0,Be=0,R=0,ie=Promise.resolve(),De="ws",Ot=0,Q=!1,ke=0,nt=!1,Qe=Eo,Tt=new Set,Ht=[],lt=null,Mt=null,yt=!1,hn=!1,jo=B$,mt=null,Dn=null,Lr=null,tn=null,wn=null,bi=!1,b=0,h=!1,v=jq(x);if(v)if(v.tokenPreview===D$(t))h=!0,zf({reason:v.reason??"removed",at:v.at??null,nodeId:v.nodeId??n}),P.error(`[plane-link] this node was REMOVED from the plane (${v.reason??"removed"}${v.at?` at ${new Date(v.at).toISOString()}`:""}) \u2014 not dialing. Re-provision it with a new node token, or delete ${x} to force a dial.`);else try{ri.unlinkSync(x),P.log("[plane-link] stale removed-marker discarded \u2014 the node token changed")}catch{}function B({reason:F="removed",at:H=Date.now(),via:E="ctl"}={}){if(!h){h=!0,zf({reason:F,at:H,nodeId:n,via:E});try{ri.mkdirSync(Yt.dirname(x),{recursive:!0}),ri.writeFileSync(x,JSON.stringify({at:H,reason:F,via:E,nodeId:n,tokenPreview:D$(t)},null,2))}catch(U){P.error(`[plane-link] could not write ${x}: ${U?.message||U}`)}P.error(`[plane-link] the plane REMOVED this node (${F}, via ${E}) \u2014 going dormant: no more dials, ingest or broker calls. Re-provision with a new node token to reconnect; the box and its data are untouched.`);try{S?.({reason:F,at:H,via:E})}catch{}Rk({bye:!1})}}async function Y(){try{Ee=JSON.parse(await hl.readFile(Ce,"utf8"))}catch{Ee={}}}function we(){bi=!0,!Lr&&(Lr=setTimeout(async()=>{if(Lr=null,!!bi){bi=!1;try{await hl.mkdir(Yt.dirname(Ce),{recursive:!0}),await hl.writeFile(Ce,JSON.stringify(Ee))}catch(F){P.error("[plane-link] cursor flush failed:",F?.message||F)}}},Wq),Lr.unref?.())}function ce(F){return!Mt||Mt.readyState!==qy.OPEN?!1:(Mt.send(JSON.stringify(F)),!0)}function ot(F){let H=Date.now();for(let{trackId:E,event:U}of F){let te=re.get(U.id);re.set(U.id,{trackId:E,seq:U.seq,event:U,sentAt:H,firstSentAt:te?.firstSentAt??H,retryAt:H+_,tries:(te?.tries??0)+1,nacks:te?.nacks??0})}return De==="http"?(jC(F),!0):ce({v:ft,ch:"events",type:"append",events:F})}function ht(F){let H=null;for(let E of re.values())E.trackId===F&&(H==null||E.seq<H)&&(H=E.seq);return H}function qs(F){let H=le[F];if(H==null)return;let E=ht(F),U=E==null?H:Math.min(H,E-1);U>(Ee[F]??0)&&(Ee[F]=U,we())}function nn(){if($e=Date.now(),J!=null){let F=Date.now()-J;P.log(`[plane-link] spine sync recovered after ${Math.round(F/1e3)}s degraded (${he} stall redial(s))`);let H=J;J=null,he=0,Be=0,Q=!1,ke=0;try{M?.({since:H,downMs:F})}catch(E){P.error(`[plane-link] onRecovered failed: ${E?.message||E}`)}}}function jf(F){if(!Array.isArray(F)||!F.length)return;let H=new Set;for(let E of F){let U=re.get(E);U&&(re.delete(E),(le[U.trackId]??0)<U.seq&&(le[U.trackId]=U.seq),H.add(U.trackId))}for(let E of H)qs(E);nn()}function vk(F,H){R++;let E={at:Date.now(),trackId:F.trackId,seq:F.seq,id:F.event?.id??null,type:F.event?.type??null,reason:H,event:F.event??null};P.error(`[plane-link] DEAD-LETTER ${F.trackId} seq ${F.seq} (${E.type??"?"}): plane rejected it permanently (${H}) \u2014 kept in ${Z}`),ie=ie.then(async()=>{await hl.mkdir(Yt.dirname(Z),{recursive:!0}),await hl.appendFile(Z,JSON.stringify(E)+`
|
|
690
|
+
`)}).catch(U=>P.error(`[plane-link] dead-letter write failed: ${U?.message||U}`))}function Sk(F,H,E,U=null){if(!Array.isArray(F)||!F.length)return;let te=new Set,ve=Date.now();for(let Te of F){let Ke=re.get(Te);Ke&&(E?(re.delete(Te),vk(Ke,H),(le[Ke.trackId]??0)<Ke.seq&&(le[Ke.trackId]=Ke.seq),te.add(Ke.trackId)):(Ke.nacks++,Ke.retryAt=ve+Math.max(1e3,Number(U)||_)))}for(let Te of te)qs(Te);nn()}function Ak(F=Date.now()){let H=1/0;for(let E of re.values())E.firstSentAt<H&&(H=E.firstSentAt);return H===1/0?0:F-H}function Tk(){if(!yt||!re.size)return;let F=Date.now(),H=Ak(F);if(H<A||$e!=null&&F-$e<A||De==="http"&&Q||Be>F)return;he++;let E=Math.min(T*2**(he-1),L$);if(J==null){J=F-H,P.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(H/1e3)}s with ${re.size} event(s) in flight (${De} door) \u2014 the plane is not answering appends`);try{$?.({since:J,unacked:re.size,oldestAgeMs:H,mode:De})}catch(U){P.error(`[plane-link] onDegraded failed: ${U?.message||U}`)}}if(De==="http"){Q=!0,ke=F+E,P.error(`[plane-link] ingest circuit OPEN (stall #${he}); probing /v1/ingest/health in ${Math.round(E/1e3)}s`);return}Be=F+E,P.error(`[plane-link] closing the link and redialing in ${Math.round(E/1e3)}s (stall redial #${he})`);try{Mt?.terminate()}catch{}}function Lf(){if(!yt)return;let F=Date.now();if(De==="http"&&Q)return;let H=[...re.values()].filter(U=>U.event&&U.retryAt<=F&&!(De==="http"&&Tt.has(U.trackId)));if(!H.length)return;H.sort((U,te)=>U.trackId===te.trackId?U.seq-te.seq:U.trackId<te.trackId?-1:1),P.log(`[plane-link] re-sending ${H.length} unacked event(s) the plane never confirmed`);let E=De==="http"?Qe:Eo;if(De==="http"){let U=new Map;for(let te of H)(U.get(te.trackId)??U.set(te.trackId,[]).get(te.trackId)).push(te);for(let[,te]of U){if(Ot>=Gy)return;ot(te.slice(0,E).map(ve=>({trackId:ve.trackId,event:ve.event})))}return}for(let U=0;U<H.length;U+=E){let te=H.slice(U,U+E).map(ve=>({trackId:ve.trackId,event:ve.event}));if(!ot(te))return}}async function DC(F=null){for(;;){if(!yt)return!1;let H=re.size<G,E=De!=="http"||Ot<Gy&&!Q&&(!F||!Tt.has(F));if(H&&E)return!0;await new Promise(U=>setTimeout(U,100))}}function Ff(){if(lt=null,!yt||!Ht.length)return;let F=Ht;if(Ht=[],De!=="http"){for(let E=0;E<F.length;E+=Eo)ot(F.slice(E,E+Eo));return}let H=new Map;for(let E of F)(H.get(E.trackId)??H.set(E.trackId,[]).get(E.trackId)).push(E);for(let[E,U]of H){if(Tt.has(E)||Ot>=Gy||Q){Ht.push(...U);continue}for(let te=0;te<U.length;te+=Qe)ot(U.slice(te,te+Qe))}Ht.length&&!lt&&(lt=setTimeout(Ff,F$),lt.unref?.())}let BC=F=>{let H=new Map;for(let{trackId:E,event:U}of F)(H.get(E)??H.set(E,[]).get(E)).push(U);return[...H].map(([E,U])=>({trackId:E,events:U}))},Lo=(F,H,E,{progress:U})=>{let te=Date.now();for(let{event:ve}of F){let Te=re.get(ve.id);Te&&(Te.nacks++,Te.retryAt=te+Math.max(1e3,E||_))}U&&nn()};async function jC(F){let H=new Set(F.map(E=>E.trackId));for(let E of H)Tt.add(E);Ot++;try{let E=await C(`${Ze}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:BC(F)}),signal:AbortSignal.timeout(O)}),U=(Number(E.headers.get("retry-after"))||0)*1e3;if(E.status===200){let te=await E.json();Qe=Eo;for(let ve of te.results??[]){jf(ve.acked??[]);let Te=(Ke,Ct)=>{let rn=new Map;for(let st of Ke??[])(rn.get(st.reason)??rn.set(st.reason,[]).get(st.reason)).push(st.id);for(let[st,Ok]of rn)Sk(Ok,st,Ct,te.retryAfterMs??null)};Te(ve.rejected,!0),Te(ve.deferred,!1)}jf(te.discarded??[]),nn();return}if(E.status===429){Lo(F,"overloaded",U||5e3,{progress:!0});return}if(E.status===413&&F.length>1){Qe=Math.max(1,Math.floor(F.length/2)),P.error(`[plane-link] ingest request too large (${F.length} events) \u2014 halving batches to ${Qe}`),Lo(F,"too_large",1e3,{progress:!0});return}if(E.status===413){let te=re.get(F[0].event.id);te&&(re.delete(te.event.id),vk(te,"too_large"),(le[te.trackId]??0)<te.seq&&(le[te.trackId]=te.seq),qs(te.trackId)),nn();return}if(E.status===410){B({reason:"removed",via:"410"}),Lo(F,"http_410",6e4,{progress:!1});return}if(E.status===401||E.status===403){let{tripped:te,holdMs:ve}=Js();if(te&&P.error(`[plane-link] holding every plane call for ${Math.round(ve/6e4)} min \u2014 the ingest door refused this node's token three times in a row`),Date.now()-b>=U$){b=Date.now();let Te="";try{Te=(await E.json())?.error??""}catch{}P.error(`[plane-link] plane refused ingest (HTTP ${E.status}${Te?`: ${Te}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}Lo(F,`http_${E.status}`,Math.max(6e4,Ei()),{progress:!1});return}if(E.status===400){P.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${F.length} event(s) for retry; this is a bug, not data`),Lo(F,"http_400",6e4,{progress:!1});return}Lo(F,`http_${E.status}`,U||5e3,{progress:!1})}catch(E){let U=E?.name==="TimeoutError"||E?.name==="AbortError";U&&F.length>1&&(Qe=Math.max(1,Math.floor(F.length/2)),P.error(`[plane-link] ingest request of ${F.length} event(s) outlived its ${O} ms deadline \u2014 halving batches to ${Qe}`)),Lo(F,U?"timeout":"network",5e3,{progress:!1})}finally{Ot--;for(let E of H)Tt.delete(E)}}async function LC(){if(De!=="http"||!Q||!yt||nt)return;let F=Date.now();if(!(ke>F||Ys(F))){nt=!0;try{let H=await C(`${Ze}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(O)});if(!H.ok)throw new Error(`HTTP ${H.status}`);P.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),Q=!1,ke=0,Be=Date.now()+O+_+1e3;for(let E of re.values())E.retryAt=Math.min(E.retryAt,Date.now());Lf()}catch(H){he++;let E=Math.min(T*2**(he-1),L$);ke=Date.now()+E,P.error(`[plane-link] ingest door still not answering (${H?.message||H}); next probe in ${Math.round(E/1e3)}s`)}finally{nt=!1}}}function Ek(){let F=Date.now();return{mode:De,unacked:re.size,oldestUnackedAgeMs:Ak(F),lastProgressAt:$e,degradedSince:J,stallRedials:he,deadLettered:R,http:De==="http"?{inFlight:Ot,circuitOpen:Q,nextProbeInMs:Q?Math.max(0,ke-F):0,batchCap:Qe}:null}}async function _k(){if(!i)return;let F;try{F=await i()}catch(E){P.error("[plane-link] metrics sample failed:",E?.message||E);return}let H=null;try{H=c?.()??null}catch{}F&&ce({v:ft,ch:"ctl",type:"metrics",metrics:{...F,planeLink:Ek(),...H?{delivery:H}:{}}})}function FC(){i&&(wn&&clearInterval(wn),_k(),wn=setInterval(_k,d),wn.unref?.())}async function UC(){let F;try{F=await o()}catch(H){P.error("[plane-link] backfill listTracks failed:",H?.message||H);return}ce({v:ft,ch:"ctl",type:"tracks",ids:F.filter(H=>!H.transferredTo).map(H=>H.id).filter(H=>typeof H=="string")});for(let H of F){if(!yt)return;let E;try{E=await K(H)}catch{continue}let U=Ee[H.id]??0,te=E.filter(Te=>Te.seq>U&&typeof Te.id=="string"),ve=De==="http"?Qe:Eo;for(let Te=0;Te<te.length;Te+=ve){if(!await DC(H.id))return;let Ke=te.slice(Te,Te+Math.min(ve,De==="http"?Qe:Eo)).map(Ct=>({trackId:H.id,event:Ct}));if(!ot(Ke))return}}}function Ik(F){!yt||!F?.trackId||typeof F.id!="string"||(Ht.push({trackId:F.trackId,event:F}),lt||(lt=setTimeout(Ff,F$),lt.unref?.()))}let ki=new Map;async function HC(F){let{reqId:H,method:E,path:U,headers:te={},body:ve}=F,Te=Object.fromEntries(Object.entries(te).filter(([,Ct])=>Ct!=null)),Ke=new AbortController;ki.set(H,Ke);try{if(F.host){await WC(F,Te,Ke);return}g&&(Te["x-bostrat-token"]=g);let Ct=await fetch(k+U,{method:E,headers:Te,body:ve?Buffer.from(ve,"base64"):void 0,signal:Ke.signal}),rn=Object.fromEntries(Ct.headers);for(let st of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete rn[st];if(rn["content-type"]||(rn["content-type"]="application/octet-stream"),ce({v:ft,ch:"control",type:"http_head",reqId:H,status:Ct.status,headers:rn}),Ct.body){for await(let st of Ct.body)if(!ce({v:ft,ch:"control",type:"http_data",reqId:H,data:Buffer.from(st).toString("base64")}))break}ce({v:ft,ch:"control",type:"http_end",reqId:H,error:null})}finally{ki.delete(H)}}function WC(F,H,E){let{reqId:U,method:te,path:ve,body:Te}=F,Ke=new URL(k);return new Promise(Ct=>{let rn=Dq.request({host:Ke.hostname,port:Ke.port,method:te,path:ve,headers:{...H,host:F.host},signal:E.signal},st=>{let{connection:Ok,"keep-alive":Fz,"transfer-encoding":Uz,upgrade:Hz,...qC}=st.headers;ce({v:ft,ch:"control",type:"http_head",reqId:U,status:st.statusCode||502,headers:qC}),st.on("data",GC=>{ce({v:ft,ch:"control",type:"http_data",reqId:U,data:Buffer.from(GC).toString("base64")})||st.destroy()}),st.on("end",()=>{ce({v:ft,ch:"control",type:"http_end",reqId:U,error:null}),Ct()}),st.on("error",()=>{ce({v:ft,ch:"control",type:"http_end",reqId:U,error:"view upstream error"}),Ct()})});rn.on("error",st=>{ce({v:ft,ch:"control",type:"http_end",reqId:U,error:st?.message||"view request failed"}),Ct()}),Te&&rn.write(Buffer.from(Te,"base64")),rn.end()})}function $k(){if(hn||h)return;Mt=new qy(e,{headers:{authorization:`Bearer ${t}`}}),Mt.on("open",async()=>{let H=null;try{H=await s?.()||null}catch{H=null}ce({v:ft,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:Mn,installKind:y,...typeof m=="function"?{updatePolicy:m()}:m?{updatePolicy:m}:{},providers:Lq(),...H?{agents:H}:{}})}),Mt.on("message",H=>{let E;try{E=JSON.parse(H)}catch{return}if(E.ch==="ctl"&&E.type==="hello_ok"){yt=!0,jo=B$,mt=null,Lk(),P.log(`[plane-link] connected to plane as ${n}`);try{D?.()}catch{}if(typeof E.hashKey=="string"&&E.hashKey)try{l?.(E.hashKey)}catch{}try{u?.(typeof E.viewBase=="string"&&E.viewBase?E.viewBase:null)}catch{}E.minNodeVersion&&Mn&&wl(Mn,E.minNodeVersion)&&P.error(`[plane-link] this node is bostrat-node ${Mn}; the plane asks for \u2265${E.minNodeVersion} \u2014 update with: ${Jp(y)}`);try{f?.(typeof E.updatePolicy=="string"?E.updatePolicy:null)}catch{}let U=typeof E.latestNodeVersion=="string"?E.latestNodeVersion:null;if(U&&Mn&&wl(Mn,U)&&!X.has(U)){X.add(U),P.log(`[plane-link] bostrat-node ${U} is available (this node runs ${Mn}) \u2014 ${Jp(y)}`);try{p?.({current:Mn,latest:U,min:E.minNodeVersion??null,installKind:y,required:!!(E.minNodeVersion&&wl(Mn,E.minNodeVersion))})}catch(ve){P.error(`[plane-link] update notice failed: ${ve?.message||ve}`)}}let te=E.ingest==="http";De=j==="ws"?"ws":j==="http"||te?"http":"ws",j==="http"&&!te&&P.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),De==="http"&&P.log("[plane-link] spine rides the HTTPS ingest door"),Q=!1,ke=0,Qe=Eo,tn&&clearInterval(tn),tn=setInterval(()=>{ce({v:ft,ch:"ctl",type:"ping"}),Tk(),LC().catch(()=>{}),Lf(),Ht.length&&!lt&&Ff()},L),tn.unref?.(),FC(),UC().catch(ve=>P.error("[plane-link] backfill failed:",ve?.message||ve));return}if(E.ch==="ctl"&&E.type==="hello_refused"){let U=typeof E.minNodeVersion=="string"?E.minNodeVersion:null,te=typeof E.latestNodeVersion=="string"?E.latestNodeVersion:null;P.error(`[plane-link] plane REFUSED this node: bostrat-node ${Mn??"?"} is below its minimum ${U??"?"} \u2014 ${E.fix||Jp(y)}`),jo=j$,mt=0;try{w?.({current:Mn,min:U,latest:te,fix:E.fix||Jp(y),installKind:y})}catch(ve){P.error(`[plane-link] refusal handler failed: ${ve?.message||ve}`)}return}if(E.ch==="ctl"&&E.type==="removed"){ce({v:ft,ch:"ctl",type:"removed_ack",nodeId:n}),B({reason:typeof E.reason=="string"?E.reason:"removed",at:Number(E.at)||Date.now(),via:"ctl"});return}if(E.ch==="ctl"&&E.type==="update_policy"){try{f?.(typeof E.mode=="string"?E.mode:null)}catch{}return}if(E.ch==="ctl"&&E.type==="usage_caps"){gd(E.caps??{},{source:"plane",updatedAt:E.updatedAt??Date.now()}).then(U=>P.log(`[plane-link] spend caps ${Object.keys(U.caps).length?`updated (${Object.keys(U.caps).join(", ")})`:"cleared"} from plane`)).catch(U=>P.error("[plane-link] spend-caps push rejected:",U?.message||U));return}if(E.ch==="ctl"&&E.type==="transferred"&&Array.isArray(E.tracks)){Promise.resolve().then(()=>a?.(E.tracks)).catch(U=>P.error("[plane-link] transfer notice failed:",U?.message||U));return}if(E.ch==="events"&&E.type==="ack"&&Array.isArray(E.ids)){jf(E.ids);return}if(E.ch==="events"&&E.type==="nack"&&Array.isArray(E.ids)){Sk(E.ids,E.reason,E.permanent===!0,E.retryAfterMs??null);return}if(E.ch==="control"&&E.type==="http_open"){HC(E).catch(U=>ce({v:ft,ch:"control",type:"http_end",reqId:E.reqId,error:U?.message||"tunnel error"}));return}if(E.ch==="control"&&E.type==="http_cancel"){ki.get(E.reqId)?.abort();return}if(E.ch==="attach"&&E.type==="open"){let U=new qy(W+E.path,Array.isArray(E.protocols)?E.protocols:[],E.host?{headers:{host:E.host}}:void 0);ge.set(E.streamId,U),Ie.set(E.streamId,[]),U.on("open",()=>{let ve=Ie.get(E.streamId)||[];Ie.delete(E.streamId);for(let{buf:Te,bin:Ke}of ve)U.send(Te,{binary:Ke})}),U.on("message",(ve,Te)=>{let Ke=Te?ve:Buffer.from(String(ve));ce({v:ft,ch:"attach",type:"data",streamId:E.streamId,data:Ke.toString("base64"),...Te?{bin:!0}:{}})});let te=()=>{Ie.delete(E.streamId),ge.delete(E.streamId)&&ce({v:ft,ch:"attach",type:"close",streamId:E.streamId})};U.on("close",te),U.on("error",te);return}if(E.ch==="attach"&&E.type==="data"){let U=ge.get(E.streamId);if(!U)return;let te=Buffer.from(E.data,"base64"),ve=E.bin===!0,Te=Ie.get(E.streamId);Te?Te.push({buf:te,bin:ve}):U.send(te,{binary:ve});return}if(E.ch==="attach"&&E.type==="close"){let U=ge.get(E.streamId);ge.delete(E.streamId),Ie.delete(E.streamId),U?.close();return}});let F=()=>{yt&&P.log("[plane-link] disconnected from plane"),yt=!1,tn&&(clearInterval(tn),tn=null),wn&&(clearInterval(wn),wn=null),re.clear(),le={},Ht=[],lt&&(clearTimeout(lt),lt=null),Tt.clear();for(let U of ge.values())U.close();ge.clear(),Ie.clear();for(let U of ki.values())U.abort();if(ki.clear(),hn||Dn)return;mt==null&&(mt=Date.now());let H=Date.now()-mt<Fq,E=Math.max(H?Uq+Math.random()*Hq:jo,Be-Date.now(),Ei());Dn=setTimeout(()=>{Dn=null,$k()},E),Dn.unref?.(),H||(jo=Math.min(jo*2,j$))};Mt.on("close",F),Mt.on("error",H=>{let E=/Unexpected server response: (\d+)/.exec(H?.message||"")?.[1];if(E==="410"){B({reason:"removed",via:"410"});return}if(E==="401"||E==="403"){let{tripped:U,holdMs:te}=Js();U?P.error(`[plane-link] holding every plane call for ${Math.round(te/6e4)} min \u2014 three credential refusals in a row (HTTP ${E}) mean the token is revoked or unknown, not that the plane is down; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN. Next dial in ${Math.round(te/6e4)} min.`):Date.now()-b>=U$&&(b=Date.now(),P.error(`[plane-link] plane refused this node's credentials (HTTP ${E}) \u2014 the token was revoked or is unknown to the plane; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN (still retrying)`))}F()})}Ye.on("event",Ik),Y().then($k);function Rk({bye:F=!0}={}){hn=!0,Ye.off("event",Ik),Dn&&clearTimeout(Dn),Lr&&clearTimeout(Lr),tn&&clearInterval(tn),wn&&clearInterval(wn),lt&&clearTimeout(lt),F&&ce({v:ft,ch:"ctl",type:"bye",reason:"shutdown"}),Mt?.close()}return{stop:()=>Rk(),status:()=>({connected:yt,nodeId:n,cursor:{...Ee},inflight:re.size,...Ek(),auth:Fk(),dormant:h,removed:ru()}),_tick:()=>{Tk(),Lf()}}}Pu();var Xp=(()=>{try{return Qq(import.meta.url)("../package.json").version||null}catch{return null}})(),e5=process.env.BOSTRAT_STATE_DIR||gt.join(process.env.HOME||Xq.tmpdir(),".bostrat"),K$=gt.join(e5,"self-update"),Zp=gt.join(K$,"status.json"),t5=1440*6e4,n5=6e4,gl=new Set(["npm-global","git-checkout"]);function V$({kind:e=Cs(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:gl.has(e)?e==="git-checkout"||r5(t)?"auto":"prompt":"off"}function r5(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var Xt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function o5({kind:e,to:t,from:n=Xp,statusFile:r=Zp,logFile:o=gt.join(K$,"last.log"),port:s=Number(process.env.PORT)||3362,npmPrefix:a=null,serviceName:i=process.platform==="darwin"?"com.bostrat.node":"bostrat-node.service",uid:c=typeof process.getuid=="function"?process.getuid():0,liveDir:l=process.env.BOSTRAT_LIVE_CHECKOUT||gt.resolve(gt.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!gl.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 ${Xt(y)} ${k}`,p=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${Xt(i)}`,f=`
|
|
691
691
|
health() {
|
|
692
692
|
for i in $(seq 1 45); do
|
|
693
693
|
sleep 1
|
|
@@ -698,20 +698,20 @@ health() {
|
|
|
698
698
|
# bostrat-node self-update one-shot \u2014 written by agent/src/self-update.js.
|
|
699
699
|
# ${n??"?"} \u2192 ${t} (${e}). Status: ${r}
|
|
700
700
|
set -u
|
|
701
|
-
FROM=${
|
|
702
|
-
TO=${
|
|
703
|
-
STATUS_FILE=${
|
|
701
|
+
FROM=${Xt(n??"")}
|
|
702
|
+
TO=${Xt(t)}
|
|
703
|
+
STATUS_FILE=${Xt(r)}
|
|
704
704
|
STARTED_AT=${Number(u)}
|
|
705
705
|
write_status() { # <status> [error]
|
|
706
706
|
local err="\${2:-}"
|
|
707
707
|
err="\${err//\\\\/\\\\\\\\}"; err="\${err//\\"/\\\\\\"}"
|
|
708
708
|
printf '{"status":"%s","from":"%s","to":"%s","kind":"%s","startedAt":%s,"updatedAt":%s%s}\\n' \\
|
|
709
|
-
"$1" "$FROM" "$TO" ${
|
|
709
|
+
"$1" "$FROM" "$TO" ${Xt(e)} "$STARTED_AT" "$(date +%s000)" \\
|
|
710
710
|
"\${err:+,\\"error\\":\\"$err\\"}" > "$STATUS_FILE.tmp" && mv "$STATUS_FILE.tmp" "$STATUS_FILE"
|
|
711
711
|
}
|
|
712
712
|
${f}
|
|
713
|
-
`;if(e==="npm-global"){let
|
|
714
|
-
`:"",
|
|
713
|
+
`;if(e==="npm-global"){let y=a?gt.join(a,"bin","npm"):"npm",k=a?`export PATH=${Xt(gt.join(a,"bin"))}:"$PATH"
|
|
714
|
+
`:"",g=a?` --prefix ${Xt(a)}`:"",x=S=>`${Xt(y)} install -g "bostrat-node@${S}" --no-audit --no-fund${g}`;return w+k+`
|
|
715
715
|
${d("updating",'"" ')}
|
|
716
716
|
if ! ${x("$TO")}; then
|
|
717
717
|
${d("failed",'"npm install failed"')}
|
|
@@ -734,8 +734,8 @@ if [ -n "$FROM" ] && ${x("$FROM")} && ${p} && health; then
|
|
|
734
734
|
fi
|
|
735
735
|
${d("failed",'"agent did not answer after the update AND the rollback"')}
|
|
736
736
|
exit 1
|
|
737
|
-
`}let m=
|
|
738
|
-
LIVE=${
|
|
737
|
+
`}let m=y=>`( cd ${Xt(l)} && git fetch -q origin main --tags && git show origin/main:scripts/deploy-pi.sh > "$TMP_SCRIPT" && bash "$TMP_SCRIPT" "v${y}" )`;return w+`
|
|
738
|
+
LIVE=${Xt(l)}
|
|
739
739
|
TMP_SCRIPT="$(mktemp)"
|
|
740
740
|
trap 'rm -f "$TMP_SCRIPT"' EXIT
|
|
741
741
|
# "restarting" is written BEFORE deploy-pi.sh runs (it does fetch \u2192 build \u2192
|
|
@@ -752,68 +752,68 @@ if [ -n "$FROM" ] && ${m("$FROM")}; then
|
|
|
752
752
|
fi
|
|
753
753
|
${d("failed",'"deploy-pi.sh v$TO failed AND the rollback failed"')}
|
|
754
754
|
exit 1
|
|
755
|
-
`}async function yl(e=Zp){try{return JSON.parse(await _o.readFile(e,"utf8"))}catch{return null}}var Zq=new Set(["updating","restarting","rolling-back"]),Qp=e=>!!(e&&Zq.has(e.status));async function ef({to:e,from:t=Xp,kind:n=Cs(),reason:r="manual",statusFile:o=Zp,spawnImpl:s=Gq,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!gl.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!wl(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await yl(o);if(Qp(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 _o.mkdir(gt.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?Dm(await Qq())||gt.resolve(gt.dirname(process.execPath),".."):null,d=Xq({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),p=gt.join(gt.dirname(o),`update-${e}.sh`),f=gt.join(gt.dirname(o),"last.log");await _o.writeFile(p,d,{mode:448}),await _o.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let w=H$.existsSync("/run/systemd/system")&&await e5("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",p]]:["setsid",["-f","bash","-c",`exec bash ${Yt(p)} >> ${Yt(f)} 2>&1`]];try{s(w[0],w[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(m){return await _o.writeFile(o,JSON.stringify({status:"failed",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a(),error:`could not spawn updater: ${m?.message||m}`})),{ok:!1,reason:`could not spawn updater: ${m?.message||m}`}}return c.log?.(`[self-update] ${t??"?"} \u2192 ${e} (${n}, ${r}) started \u2014 status ${o}`),{ok:!0,to:e,from:t,kind:n,scriptFile:p,statusFile:o}}async function Qq(){try{return await _o.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function e5(e){for(let t of String(process.env.PATH||"").split(gt.delimiter))try{return await _o.access(gt.join(t,e),H$.constants.X_OK),!0}catch{}return!1}var t5=45*6e4;async function G$({statusFile:e=Zp,current:t=Xp,now:n=Date.now,staleMs:r=t5}={}){let o=await yl(e);if(!o)return null;let s=a=>_o.writeFile(e,JSON.stringify(a)).catch(()=>{});if(Qp(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 K$({isBusy:e,start:t=ef,onStuck:n=null,onStarted:r=null,tickMs:o=Jq,stuckMs:s=zq,now:a=Date.now,setIntervalImpl:i=setInterval,clearIntervalImpl:c=clearInterval,log:l=console}={}){let u=null,d=()=>{u?.timer&&c(u.timer),u=null},p=async()=>{if(!u)return;let f=!0;try{f=await e()!==!1}catch{f=!0}if(f){if(!u.stuckFired&&a()-u.since>=s){u.stuckFired=!0;try{await n?.({to:u.to,from:u.from,since:u.since})}catch{}}return}let w=u;d();let m=await t({to:w.to,from:w.from,kind:w.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${m.ok?"started":`not started: ${m.reason}`}`);try{await r?.({...w,result:m})}catch{}};return{arm({to:f,from:w=Xp,kind:m=Cs()}){return u?.to===f?!1:(d(),u={to:f,from:w,kind:m,since:a(),stuckFired:!1,timer:i(p,o)},u.timer.unref?.(),!0)},tick:p,cancel:d,get armed(){return u?{to:u.to,from:u.from,kind:u.kind,since:u.since}:null}}}ye();bt();var V$=(...e)=>fetch(...e);function z$(e=process.env){return!!(Ce(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Ky(e=process.env){if(!z$(e))throw new Error("no plane configured \u2014 circuits live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let t=new AbortController,n=setTimeout(()=>t.abort(),1e4);try{let r=await V$(`${Ce(e)}/v1/circuits`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN}`},signal:t.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return Array.isArray(o.circuits)?o.circuits:[]}finally{clearTimeout(n)}}async function Vy(e=process.env){if(!z$(e))throw new Error("no plane configured \u2014 the roadmap lives on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let t=new AbortController,n=setTimeout(()=>t.abort(),1e4);try{let r=await V$(`${Ce(e)}/v1/roadmap`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN}`},signal:t.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return{people:Array.isArray(o.people)?o.people:[],slots:Array.isArray(o.slots)?o.slots:[],reservations:Array.isArray(o.reservations)?o.reservations:[],tray:Array.isArray(o.tray)?o.tray:[],circuits:Array.isArray(o.circuits)?o.circuits:[]}}finally{clearTimeout(n)}}async function oi(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await Ky(t),o=r.find(c=>c.id===n);if(o)return o;let s=n.toLowerCase(),a=r.filter(c=>c.name.toLowerCase()===s);if(a.length===1)return a[0];if(a.length>1)throw new Error(`"${n}" names ${a.length} circuits \u2014 use an id (bostrat-circuit ls)`);let i=r.filter(c=>c.name.toLowerCase().includes(s)).slice(0,5);throw new Error(i.length?`no circuit named "${n}" \u2014 near matches: ${i.map(c=>`${c.name} (${c.id})`).join(", ")}`:`no circuit named "${n}" \u2014 create it on the Circuits page first (bostrat-circuit ls lists yours)`)}async function tf(e,t,{source:n="chat"}={}){return N(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}ye();bt();var n5=(...e)=>fetch(...e);async function nf(e,t,n,r=process.env){if(!(Ce(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 blueprints live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await n5(`${Ce(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}function r5(e,t=process.env){return nf("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function J$({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await r5(t,o):await oi(e,o),a=await nf("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function zy(e,t=process.env){let n=await oi(e,t),r=await nf("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function Jy(e,t=process.env){return(await nf("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function Y$(e,t,n,r=process.env){let{blueprint:o,items:s}=await zy(t,r),a=new Map(s.map(c=>[c.itemId,c])),i=n.filter(c=>!a.get(c)?.bindable);if(i.length){let c=s.filter(l=>l.bindable).map(l=>l.itemId);throw new Error(`unknown/unbindable item(s): ${i.join(", ")} \u2014 bindable ids: ${c.join(", ")||"(none)"}`)}return await N(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}ye();bt();var o5=(...e)=>fetch(...e);async function s5(e,t,n,r=process.env){if(!(Ce(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 the decision ledger lives on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await o5(`${Ce(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}var X$=72,Yy=160;async function Z$(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>X$)throw new Error(`the title is the choice as a LABEL \u2014 ${X$} chars max (yours is ${i.length}); the full story goes in the decision text`);if(!c)throw new Error(`a summary is required \u2014 one sentence (max ${Yy} chars) a reader can act on without opening the record`);if(c.length>Yy)throw new Error(`the summary is one sentence \u2014 ${Yy} chars max (yours is ${c.length}); the full story goes in the decision text`);if(!String(r??"").trim())throw new Error("the decision itself is required (what was decided)");return await 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 Q$(e,t=process.env){return s5("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}xg();ye();import Tl from"node:fs/promises";import ff from"node:path";import t6 from"node:os";Gr();import a5 from"node:fs/promises";import eR from"node:path";import i5 from"node:os";var tR=()=>eR.join(process.env.BOSTRAT_STATE_DIR||eR.join(process.env.HOME||i5.tmpdir(),".bostrat"),"integrations.json");async function Br(){try{return JSON.parse(await a5.readFile(tR(),"utf8"))||{}}catch{return{}}}var rf=["linked","prOpened","prMerged","create"],Xy=["prompt","auto","off"],c5={create:["prompt","off"]},nR=e=>c5[e]??Xy;function of(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of rf){let s=nR(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 Ns(){let e=await Br(),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:of(e),defaultTeam:n}:null}async function sf(){let e=await Br(),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:of(e)}}async function Zy(){return of(await Br())}async function rR(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${rf.join(", ")}`);for(let[r,o]of t){if(!rf.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${rf.join(", ")}`);let s=nR(r);if(!s.includes(o))throw new Error(s.length<Xy.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Xy.join(", ")}`)}let n=await Br();return n.ticketFlow={...of(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Pt(tR(),n,{mode:384}),n.ticketFlow}async function bl(){let e=(await Br()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function l5(){let e=(await Br()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var u5={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function af(){let t=(await Br()).voiceChat||{},n=t.provider||"openai",r=u5[n];if(!r)return null;let o=n==="openai"?await l5():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function oR(){let e=(await Br()).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 Ps(){let e=(await Br()).slack||{},t=process.env.BOSTRAT_SLACK_BOT_TOKEN||e.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||e.appToken;return!t||!n?null:{botToken:t,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||e.channel||null,maxConcurrent:Number.isInteger(e.maxConcurrent)&&e.maxConcurrent>0?e.maxConcurrent:2}}ye();Vr();lo();Ua();import xl from"node:fs/promises";import vR from"node:path";import j5 from"node:os";import{randomUUID as SR}from"node:crypto";var eb={};Uf(eb,{createComment:()=>b5,findIssueUrl:()=>m5,issueStates:()=>v5,mdToAdf:()=>iR,moveIssue:()=>S5,parseIssueRef:()=>f5,resolveIssue:()=>h5,uploadImage:()=>k5,upsertCard:()=>w5,whoami:()=>Qy});var si=e=>String(e.baseUrl||"").replace(/\/$/,""),aR=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function Ds(e,t,n,r){let o=await fetch(`${si(e)}${n}`,{method:t,headers:{authorization:aR(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 d5=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,p5=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function f5(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(d5);return r?r[1].toUpperCase():null}let n=t.match(p5);return n?n[1]:null}function m5(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 h5(e,t){let n;try{n=await Ds(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:`${si(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function w5(e,t,{url:n,title:r,subtitle:o}){await Ds(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 g5=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function y5(e){let t=[],n=0;for(let r of e.matchAll(g5)){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 iR(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
756
|
-
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...
|
|
755
|
+
`}async function yl(e=Zp){try{return JSON.parse(await _o.readFile(e,"utf8"))}catch{return null}}var s5=new Set(["updating","restarting","rolling-back"]),Qp=e=>!!(e&&s5.has(e.status));async function ef({to:e,from:t=Xp,kind:n=Cs(),reason:r="manual",statusFile:o=Zp,spawnImpl:s=Zq,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!gl.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!wl(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await yl(o);if(Qp(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 _o.mkdir(gt.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?Dm(await a5())||gt.resolve(gt.dirname(process.execPath),".."):null,d=o5({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),p=gt.join(gt.dirname(o),`update-${e}.sh`),f=gt.join(gt.dirname(o),"last.log");await _o.writeFile(p,d,{mode:448}),await _o.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let w=G$.existsSync("/run/systemd/system")&&await i5("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",p]]:["setsid",["-f","bash","-c",`exec bash ${Xt(p)} >> ${Xt(f)} 2>&1`]];try{s(w[0],w[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(m){return await _o.writeFile(o,JSON.stringify({status:"failed",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a(),error:`could not spawn updater: ${m?.message||m}`})),{ok:!1,reason:`could not spawn updater: ${m?.message||m}`}}return c.log?.(`[self-update] ${t??"?"} \u2192 ${e} (${n}, ${r}) started \u2014 status ${o}`),{ok:!0,to:e,from:t,kind:n,scriptFile:p,statusFile:o}}async function a5(){try{return await _o.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function i5(e){for(let t of String(process.env.PATH||"").split(gt.delimiter))try{return await _o.access(gt.join(t,e),G$.constants.X_OK),!0}catch{}return!1}var c5=45*6e4;async function z$({statusFile:e=Zp,current:t=Xp,now:n=Date.now,staleMs:r=c5}={}){let o=await yl(e);if(!o)return null;let s=a=>_o.writeFile(e,JSON.stringify(a)).catch(()=>{});if(Qp(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 J$({isBusy:e,start:t=ef,onStuck:n=null,onStarted:r=null,tickMs:o=n5,stuckMs:s=t5,now:a=Date.now,setIntervalImpl:i=setInterval,clearIntervalImpl:c=clearInterval,log:l=console}={}){let u=null,d=()=>{u?.timer&&c(u.timer),u=null},p=async()=>{if(!u)return;let f=!0;try{f=await e()!==!1}catch{f=!0}if(f){if(!u.stuckFired&&a()-u.since>=s){u.stuckFired=!0;try{await n?.({to:u.to,from:u.from,since:u.since})}catch{}}return}let w=u;d();let m=await t({to:w.to,from:w.from,kind:w.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${m.ok?"started":`not started: ${m.reason}`}`);try{await r?.({...w,result:m})}catch{}};return{arm({to:f,from:w=Xp,kind:m=Cs()}){return u?.to===f?!1:(d(),u={to:f,from:w,kind:m,since:a(),stuckFired:!1,timer:i(p,o)},u.timer.unref?.(),!0)},tick:p,cancel:d,get armed(){return u?{to:u.to,from:u.from,kind:u.kind,since:u.since}:null}}}ye();bt();var Y$=(...e)=>fetch(...e);function X$(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Vy(e=process.env){if(!X$(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 Y$(`${Ne(e)}/v1/circuits`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN}`},signal:t.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return Array.isArray(o.circuits)?o.circuits:[]}finally{clearTimeout(n)}}async function zy(e=process.env){if(!X$(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 Y$(`${Ne(e)}/v1/roadmap`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN}`},signal:t.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return{people:Array.isArray(o.people)?o.people:[],slots:Array.isArray(o.slots)?o.slots:[],reservations:Array.isArray(o.reservations)?o.reservations:[],tray:Array.isArray(o.tray)?o.tray:[],circuits:Array.isArray(o.circuits)?o.circuits:[]}}finally{clearTimeout(n)}}async function oi(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await Vy(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 tf(e,t,{source:n="chat"}={}){return N(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}ye();bt();var l5=(...e)=>fetch(...e);async function nf(e,t,n,r=process.env){if(!(Ne(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 blueprints live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await l5(`${Ne(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}function u5(e,t=process.env){return nf("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function Z$({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await u5(t,o):await oi(e,o),a=await nf("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function Jy(e,t=process.env){let n=await oi(e,t),r=await nf("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function Yy(e,t=process.env){return(await nf("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function Q$(e,t,n,r=process.env){let{blueprint:o,items:s}=await Jy(t,r),a=new Map(s.map(c=>[c.itemId,c])),i=n.filter(c=>!a.get(c)?.bindable);if(i.length){let c=s.filter(l=>l.bindable).map(l=>l.itemId);throw new Error(`unknown/unbindable item(s): ${i.join(", ")} \u2014 bindable ids: ${c.join(", ")||"(none)"}`)}return await N(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}ye();bt();var d5=(...e)=>fetch(...e);async function p5(e,t,n,r=process.env){if(!(Ne(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 the decision ledger lives on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await d5(`${Ne(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}var eR=72,Xy=160;async function tR(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>eR)throw new Error(`the title is the choice as a LABEL \u2014 ${eR} 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 ${Xy} chars) a reader can act on without opening the record`);if(c.length>Xy)throw new Error(`the summary is one sentence \u2014 ${Xy} 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 nR(e,t=process.env){return p5("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}kg();ye();import Tl from"node:fs/promises";import ff from"node:path";import c6 from"node:os";Gr();import f5 from"node:fs/promises";import rR from"node:path";import m5 from"node:os";var oR=()=>rR.join(process.env.BOSTRAT_STATE_DIR||rR.join(process.env.HOME||m5.tmpdir(),".bostrat"),"integrations.json");async function Br(){try{return JSON.parse(await f5.readFile(oR(),"utf8"))||{}}catch{return{}}}var rf=["linked","prOpened","prMerged","create"],Zy=["prompt","auto","off"],h5={create:["prompt","off"]},sR=e=>h5[e]??Zy;function of(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of rf){let s=sR(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 Ns(){let e=await Br(),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:of(e),defaultTeam:n}:null}async function sf(){let e=await Br(),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:of(e)}}async function Qy(){return of(await Br())}async function aR(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${rf.join(", ")}`);for(let[r,o]of t){if(!rf.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${rf.join(", ")}`);let s=sR(r);if(!s.includes(o))throw new Error(s.length<Zy.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Zy.join(", ")}`)}let n=await Br();return n.ticketFlow={...of(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Pt(oR(),n,{mode:384}),n.ticketFlow}async function bl(){let e=(await Br()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function w5(){let e=(await Br()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var g5={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function af(){let t=(await Br()).voiceChat||{},n=t.provider||"openai",r=g5[n];if(!r)return null;let o=n==="openai"?await w5():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function iR(){let e=(await Br()).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 Ps(){let e=(await Br()).slack||{},t=process.env.BOSTRAT_SLACK_BOT_TOKEN||e.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||e.appToken;return!t||!n?null:{botToken:t,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||e.channel||null,maxConcurrent:Number.isInteger(e.maxConcurrent)&&e.maxConcurrent>0?e.maxConcurrent:2}}ye();Vr();lo();Ua();import xl from"node:fs/promises";import TR from"node:path";import G5 from"node:os";import{randomUUID as ER}from"node:crypto";var tb={};Uf(tb,{createComment:()=>E5,findIssueUrl:()=>x5,issueStates:()=>$5,mdToAdf:()=>uR,moveIssue:()=>R5,parseIssueRef:()=>k5,resolveIssue:()=>v5,uploadImage:()=>_5,upsertCard:()=>S5,whoami:()=>eb});var si=e=>String(e.baseUrl||"").replace(/\/$/,""),lR=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function Ds(e,t,n,r){let o=await fetch(`${si(e)}${n}`,{method:t,headers:{authorization:lR(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 y5=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,b5=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function k5(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(y5);return r?r[1].toUpperCase():null}let n=t.match(b5);return n?n[1]:null}function x5(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 v5(e,t){let n;try{n=await Ds(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:`${si(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function S5(e,t,{url:n,title:r,subtitle:o}){await Ds(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 A5=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function T5(e){let t=[],n=0;for(let r of e.matchAll(A5)){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 uR(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
756
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...T5(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 E5(e,t,n){let r=await Ds(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/comment`,{body:uR(n)});return r?.id?`${si(e)}/browse/${t.key}?focusedCommentId=${r.id}`:null}async function eb(e){let t=await Ds(e,"GET","/rest/api/3/myself"),n=null;try{n=new URL(si(e)).host}catch{n=e.baseUrl??null}return{user:t?.displayName??t?.emailAddress??null,org:n}}async function _5(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(`${si(e)}/rest/api/3/issue/${encodeURIComponent(t)}/attachments`,{method:"POST",headers:{authorization:lR(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??`${si(e)}/rest/api/3/attachment/content/${c.id}`}var I5={new:"unstarted",indeterminate:"started",done:"completed"},cR=e=>I5[e?.key]??"started";async function $5(e,t){let n=encodeURIComponent(t.key),[r,o]=await Promise.all([Ds(e,"GET",`/rest/api/3/issue/${n}?fields=status`),Ds(e,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:cR(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:cR(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function R5(e,t,n){await Ds(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/transitions`,{transition:{id:String(n)}})}var sb={};Uf(sb,{createComment:()=>j5,createIssue:()=>ob,createProject:()=>rb,findIssueUrl:()=>P5,issueStates:()=>F5,moveIssue:()=>U5,parseIssueRef:()=>N5,resolveIssue:()=>D5,teams:()=>ai,uploadImage:()=>L5,upsertCard:()=>B5,whoami:()=>nb});var O5=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function ar(e,t,n={}){let r=await fetch(O5(),{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 M5=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,C5=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function N5(e){let t=String(e||"").trim(),n=t.match(M5);if(n)return n[1].toUpperCase();let r=t.match(C5);return r?r[1]:null}function P5(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 D5(e,t){let r=(await ar(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 B5(e,t,{url:n,title:r,subtitle:o}){let s=await ar(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 j5(e,t,n){let r=await ar(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 nb(e){let t=await ar(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 L5(e,{filename:t,contentType:n,buffer:r}){let o=await ar(e,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
757
757
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
758
758
|
success
|
|
759
759
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
760
760
|
}
|
|
761
|
-
}`,{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
|
|
762
|
-
`)):cf.rmSync(r,{force:!0})}catch{}}function ab(){return dR().viewBase}function ib(e=process.env.BOSTRAT_PUBLIC_HOST){return pR(e,"https")}function mR(){let e=ab();if(e)return{proto:"https",host:e,source:"learned"};let t=ib();return t?{...t,source:"env"}:null}function lf(e,t){return e?`${e.proto}://${t}.${e.host}/`:null}function B5(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 hR(){let e=String(process.env.BOSTRAT_PUBLIC_URL||"").trim().replace(/\/+$/,"");if(e)return{url:e,source:"env"};let t=B5();return t?{url:t,source:"plane"}:null}var wR="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 AR={linear:{api:ob,config:Ns},jira:{api:eb,config:sf}};async function mb(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(AR)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Bs(e){let t=AR[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function hb(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 TR=()=>process.env.BOSTRAT_STATE_DIR||vR.join(process.env.HOME||j5.tmpdir(),".bostrat"),db=()=>vR.join(TR(),"tickets.json"),Sl=e=>{let t=hR();if(!t)throw new Error(wR);return`${t.url}/track/${e}`},L5=e=>`http://bostrat.globopi/track/${e}`,gR=e=>{let t=0;for(let[n,r]of Object.entries(e))r&&typeof r=="object"&&!r.cardUrl&&(r.cardUrl=L5(n),t++);return t},ar=null,cb=null,lb=Promise.resolve();async function Al(){return ar||(cb??=xl.readFile(db(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{ar=e;let n=gR(ar)>0;if(!t&&qe()){let{records:r,source:o}=await ut("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}gR(ar),n=n||Object.keys(ar).length>0}}return n&&await pb().catch(()=>{}),ar}).finally(()=>cb=null),cb)}async function pb(){let e=JSON.stringify({tickets:ar},null,2),t=`${db()}.${process.pid}.tmp`;await xl.mkdir(TR(),{recursive:!0}),await xl.writeFile(t,e),await xl.rename(t,db())}async function Io(e=null){if(lb=lb.then(pb,pb),await lb.catch(()=>{}),!e)return;let t=ar?.[e];t?Et("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):wn("tickets",e).catch(()=>{})}async function ir(e){return(await Al())[e]??null}async function ER(){return{...await Al()}}async function _R(){return(await mb()).length>0}async function ci(e,t,{source:n="api"}={}){let r=await mb();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:Sl(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 Al())[e.id]=c,await Io(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 vl(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 kl(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await RR(e.id,c,"linked"),c}async function wb(e){let t=await Al();return t[e]?(delete t[e],await Io(e),!0):!1}var fb=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function IR(e,t){if(!e.track?.cwd)return null;let n=co(await K({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function F5(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${fb(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${fb(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function U5(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(fb(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function vl(e,t){let n=await Bs(t);if(!n)return;let r=await IR(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:t.cardUrl??Sl(e),title:`Bostrat Track: ${t.track.name}`,subtitle:U5(t,r)})}function H5(e){return lf(mR(),e)}var uf=e=>H5(e)??"(no public URL on this node \u2014 link it to a plane)",W5={linked:"linked",opened:"prOpened",merged:"prMerged"};function q5(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 $R(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await Io(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 RR(e,t,n){let r=await Bs(t);if(!r)return;let o=r.cfg.ticketFlow?.[W5[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=hb(s,a),c=q5(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 $R(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:SR(),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 OR(e){let t=await ir(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Bs(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=hb(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await Io(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 gb(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 Bs(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=hb(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 Io(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await $R(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function MR(e,t){let n=await ir(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Bs(n);if(!r)throw new Error("no ticketing integration configured");let o=String(t||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await N({id:e,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function CR(e,t,n){if(!t)throw new Error("offerId required");return bo(`tkt:${e.id}:${t}`,()=>G5(e,t,n))}async function G5(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 gb(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 yR=50;async function NR(){let e=await Ns();if(!e)throw new Error("no ticketing integration configured");return ai(e)}function K5(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>yR)throw new Error(`too many issues (${n.length}); cap is ${yR} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function V5(e,t,n,r){let o=r??await ai(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 PR(e,t){let n=await Ns();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=K5(t),o=await ai(n),s=await V5(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=SR();return await N({id:e.id,cwd:e.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function DR(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await 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 Ns();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let w=(await ai(a)).find(m=>m.id===r);if(!w)throw new Error("unknown team");c={id:w.id,key:w.key,name:w.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let w=await nb(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=w.id,l.push({kind:"epic",title:w.name,url:w.url})}for(let w of i.issues||[])try{let m=await rb(a,{teamId:c.id,title:w.title,description:w.description,projectId:d,priority:w.priority});l.push({kind:"issue",key:m.key,title:m.title,url:m.url})}catch(m){u.push({title:w.title,message:m?.message||String(m)})}await N({id:e.id,cwd:e.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,team:{key:c.key,name:c.name},items:l,failed:u}}),await N({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",createdCount:l.length,failedCount:u.length}});let p=l.filter(w=>w.kind==="issue"),f=null;return i.linkCurrentTrack&&p.length===1&&!await ir(e.id)&&(f=await ci(e,p[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:f?f.key:null}}async function kl(e,t){let n=await Bs(e);n&&await n.api.createComment(n.cfg,e,t)}async function z5(e,t,n){let r=await Bs(e);if(!r)return null;let o=await Ir(t);if(!o)return null;let s=_n(o,n);if(!s)return null;let a=await xl.readFile(ln(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 bR=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),ub=new Map;function J5(e,t){if(bR()<=0)return vl(e,t);if(ub.has(e))return;let n=setTimeout(()=>{ub.delete(e),ir(e).then(r=>r?vl(e,r):null).catch(()=>{})},bR());n.unref?.(),ub.set(e,n)}var kR=new Map;async function xR(e,t,n){let r=kR.get(e)??0;Date.now()-r<600*1e3||(kR.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 Y5(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 mb())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 ci(s,o,{source:"chat"})}catch(a){await xR(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 Io(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Io(e.trackId),await vl(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 Io(e.trackId),await vl(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await IR(n,e.trackId).catch(()=>null):null;await kl(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
761
|
+
}`,{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 F5(e,t){let n=await ar(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 U5(e,t,n){if(!(await ar(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 ai(e){return((await ar(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var dR=255;async function rb(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>dR,a=s?`${o.slice(0,dR-1).trimEnd()}\u2026`:o,i=await ar(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 ob(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await ar(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}}$c();import cf from"node:fs";import ab from"node:path";import H5 from"node:os";var W5=()=>process.env.BOSTRAT_STATE_DIR||ab.join(process.env.HOME||H5.tmpdir(),".bostrat"),pR=()=>ab.join(W5(),"plane-link","public-host.json"),fR="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",ii=null;function mR(){if(ii)return ii;try{let e=JSON.parse(cf.readFileSync(pR(),"utf8"));ii={viewBase:typeof e?.viewBase=="string"&&e.viewBase?e.viewBase:null}}catch{ii={viewBase:null}}return ii}function hR(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 wR(e){let t=typeof e=="string"&&e.trim()?hR(e,"https").host:null;if(mR().viewBase===t)return;ii={viewBase:t};let r=pR();try{t?(cf.mkdirSync(ab.dirname(r),{recursive:!0}),cf.writeFileSync(r,JSON.stringify({viewBase:t,learnedAt:new Date().toISOString()})+`
|
|
762
|
+
`)):cf.rmSync(r,{force:!0})}catch{}}function ib(){return mR().viewBase}function cb(e=process.env.BOSTRAT_PUBLIC_HOST){return hR(e,"https")}function gR(){let e=ib();if(e)return{proto:"https",host:e,source:"learned"};let t=cb();return t?{...t,source:"env"}:null}function lf(e,t){return e?`${e.proto}://${t}.${e.host}/`:null}function q5(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 yR(){let e=String(process.env.BOSTRAT_PUBLIC_URL||"").trim().replace(/\/+$/,"");if(e)return{url:e,source:"env"};let t=q5();return t?{url:t,source:"plane"}:null}var bR="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 _R={linear:{api:sb,config:Ns},jira:{api:tb,config:sf}};async function hb(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(_R)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Bs(e){let t=_R[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function wb(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 IR=()=>process.env.BOSTRAT_STATE_DIR||TR.join(process.env.HOME||G5.tmpdir(),".bostrat"),pb=()=>TR.join(IR(),"tickets.json"),Sl=e=>{let t=yR();if(!t)throw new Error(bR);return`${t.url}/track/${e}`},K5=e=>`http://bostrat.globopi/track/${e}`,kR=e=>{let t=0;for(let[n,r]of Object.entries(e))r&&typeof r=="object"&&!r.cardUrl&&(r.cardUrl=K5(n),t++);return t},ir=null,lb=null,ub=Promise.resolve();async function Al(){return ir||(lb??=xl.readFile(pb(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{ir=e;let n=kR(ir)>0;if(!t&&qe()){let{records:r,source:o}=await ut("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;ir[a]=c}kR(ir),n=n||Object.keys(ir).length>0}}return n&&await fb().catch(()=>{}),ir}).finally(()=>lb=null),lb)}async function fb(){let e=JSON.stringify({tickets:ir},null,2),t=`${pb()}.${process.pid}.tmp`;await xl.mkdir(IR(),{recursive:!0}),await xl.writeFile(t,e),await xl.rename(t,pb())}async function Io(e=null){if(ub=ub.then(fb,fb),await ub.catch(()=>{}),!e)return;let t=ir?.[e];t?Et("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):gn("tickets",e).catch(()=>{})}async function cr(e){return(await Al())[e]??null}async function $R(){return{...await Al()}}async function RR(){return(await hb()).length>0}async function ci(e,t,{source:n="api"}={}){let r=await hb();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:Sl(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 Al())[e.id]=c,await Io(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 vl(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 kl(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await CR(e.id,c,"linked"),c}async function gb(e){let t=await Al();return t[e]?(delete t[e],await Io(e),!0):!1}var mb=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function OR(e,t){if(!e.track?.cwd)return null;let n=co(await K({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function V5(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 z5(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 vl(e,t){let n=await Bs(t);if(!n)return;let r=await OR(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:t.cardUrl??Sl(e),title:`Bostrat Track: ${t.track.name}`,subtitle:z5(t,r)})}function J5(e){return lf(gR(),e)}var uf=e=>J5(e)??"(no public URL on this node \u2014 link it to a plane)",Y5={linked:"linked",opened:"prOpened",merged:"prMerged"};function X5(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 MR(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await Io(e),await N({id:e,cwd:t.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:t.key,url:t.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function CR(e,t,n){let r=await Bs(t);if(!r)return;let o=r.cfg.ticketFlow?.[Y5[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=wb(s,a),c=X5(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 MR(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:ER(),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 NR(e){let t=await cr(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Bs(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=wb(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await Io(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 yb(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await cr(e);if(!r)throw new Error("no ticket linked to this Track");let o=await Bs(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=wb(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 Io(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await MR(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function PR(e,t){let n=await cr(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Bs(n);if(!r)throw new Error("no ticketing integration configured");let o=String(t||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await N({id:e,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function DR(e,t,n){if(!t)throw new Error("offerId required");return bo(`tkt:${e.id}:${t}`,()=>Z5(e,t,n))}async function Z5(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 yb(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 xR=50;async function BR(){let e=await Ns();if(!e)throw new Error("no ticketing integration configured");return ai(e)}function Q5(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>xR)throw new Error(`too many issues (${n.length}); cap is ${xR} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function e6(e,t,n,r){let o=r??await ai(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 cr(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 jR(e,t){let n=await Ns();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=Q5(t),o=await ai(n),s=await e6(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=ER();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 LR(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 Ns();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let w=(await ai(a)).find(m=>m.id===r);if(!w)throw new Error("unknown team");c={id:w.id,key:w.key,name:w.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let w=await rb(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=w.id,l.push({kind:"epic",title:w.name,url:w.url})}for(let w of i.issues||[])try{let m=await ob(a,{teamId:c.id,title:w.title,description:w.description,projectId:d,priority:w.priority});l.push({kind:"issue",key:m.key,title:m.title,url:m.url})}catch(m){u.push({title:w.title,message:m?.message||String(m)})}await N({id:e.id,cwd:e.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,team:{key:c.key,name:c.name},items:l,failed:u}}),await N({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",createdCount:l.length,failedCount:u.length}});let p=l.filter(w=>w.kind==="issue"),f=null;return i.linkCurrentTrack&&p.length===1&&!await cr(e.id)&&(f=await ci(e,p[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:f?f.key:null}}async function kl(e,t){let n=await Bs(e);n&&await n.api.createComment(n.cfg,e,t)}async function t6(e,t,n){let r=await Bs(e);if(!r)return null;let o=await $r(t);if(!o)return null;let s=In(o,n);if(!s)return null;let a=await xl.readFile(un(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 vR=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),db=new Map;function n6(e,t){if(vR()<=0)return vl(e,t);if(db.has(e))return;let n=setTimeout(()=>{db.delete(e),cr(e).then(r=>r?vl(e,r):null).catch(()=>{})},vR());n.unref?.(),db.set(e,n)}var SR=new Map;async function AR(e,t,n){let r=SR.get(e)??0;Date.now()-r<600*1e3||(SR.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 r6(e,{resolveTrack:t=null}={}){let n=await cr(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await hb())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 ci(s,o,{source:"chat"})}catch(a){await AR(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 Io(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Io(e.trackId),await vl(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 Io(e.trackId),await vl(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await OR(n,e.trackId).catch(()=>null):null;await kl(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
763
763
|
|
|
764
|
-
Agent cost for this Track: ${
|
|
764
|
+
Agent cost for this Track: ${V5(o)}.`:"")),await CR(e.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await t6(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await kl(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
765
765
|
|
|
766
766
|
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await kl(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 kl(n,`**Bostrat** \u2014 live preview **${r.name}**: ${uf(r.slug)}
|
|
767
|
-
_(runs on the Track's branch; a Bostrat-fronted preview, not a public deployment)_`);break}case"cost.recorded":await
|
|
768
|
-
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${Sl(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function
|
|
767
|
+
_(runs on the Track's branch; a Bostrat-fronted preview, not a public deployment)_`);break}case"cost.recorded":await n6(e.trackId,n);break;default:break}}catch(o){let s=t?await t(e.trackId).catch(()=>null):null;await AR(e.trackId,s?.cwd??null,o)}}function FR({resolveTrack:e}){Ye.on("event",t=>{r6(t,{resolveTrack:e}).catch(()=>{})})}async function UR(){if(!qe())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await ut("tickets");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Al(),r=0;for(let[o,s]of Object.entries(n))await Et("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}ps();Ua();import o6 from"ws";var HR=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function df(e,t,n={}){let r=await fetch(`${HR()}/${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 bb=(e,t)=>df("chat.postMessage",e,t),js=(e,t)=>df("chat.update",e,t);async function $t(e,t){await fetch(e,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(t)}).catch(()=>{})}async function s6(e,t,n={}){let r=await fetch(`${HR()}/${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 WR(e,{filename:t,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await s6("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 df("files.completeUploadExternal",e,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var a6=2200;function qR({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 df("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new o6(l),o.on("message",u=>{let d;try{d=JSON.parse(u)}catch{return}if(d.type==="hello"){a=1e3,n("socket connected");return}if(d.type==="disconnect"){try{o.close()}catch{}return}if(!d.envelope_id)return;let p=m=>{try{o.send(JSON.stringify(m?{envelope_id:d.envelope_id,payload:m}:{envelope_id:d.envelope_id}))}catch{}},f=!1,w=setTimeout(()=>{f=!0,p()},a6);Promise.resolve().then(()=>t(d)).then(m=>{clearTimeout(w),!f&&(f=!0,p(d.accepts_response_payload?m:void 0))},m=>{clearTimeout(w),f||(f=!0,p()),n(`envelope handler failed: ${m?.message||m}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var VR=()=>process.env.BOSTRAT_STATE_DIR||ff.join(process.env.HOME||c6.tmpdir(),".bostrat"),Ab=()=>ff.join(VR(),"slack.json"),pf=null,kb=null,xb=Promise.resolve();async function Eb(){return pf||(kb??=Tl.readFile(Ab(),"utf8").then(e=>JSON.parse(e)?.threads??{}).catch(()=>({})).then(e=>pf=e).finally(()=>kb=null),kb)}async function GR(){let e=JSON.stringify({threads:pf},null,2),t=`${Ab()}.${process.pid}.tmp`;await Tl.mkdir(VR(),{recursive:!0}),await Tl.writeFile(t,e),await Tl.rename(t,Ab())}async function Cn(){xb=xb.then(GR,GR),await xb.catch(()=>{})}async function _b(e){return(await Eb())[e]??null}async function l6(e,t){let n=await Eb();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 El(e,t){let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function u6(e,t=500){let n=String(e??"").trim().split(/\n\s*\n/)[0]??"";return El(n.replace(/\n+/g," "),t)}function zR(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(El(e.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${t}
|
|
768
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${Sl(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Tb(e,t){await js(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:zR(t)})}async function Ro(e,t,n,r=void 0){return bb(e.botToken,{channel:t.channel,thread_ts:t.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var KR=new Set;function $o(e){KR.add(e),e.catch(()=>{}).finally(()=>KR.delete(e))}var We=e=>({response_type:"ephemeral",text:e}),d6="*/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._",p6=["claude","cursor","codex"];function vb(e,t){let n=(t||"").toLowerCase(),r=e.filter(s=>!s.archivedAt),o=r.filter(s=>s.name.toLowerCase().startsWith(n));return o.sort((s,a)=>(a.lastResponseAt??a.createdAt??0)-(s.lastResponseAt??s.createdAt??0)),{match:o[0]??null,live:r}}var Sb=(e,t)=>We(`No live Track matches \u201C${_e(t)}\u201D. Live: ${e.map(n=>`\`${_e(n.name)}\``).join(" \xB7 ")||"none"}`);async function JR(e,t,{name:n,repo:r,responseUrl:o}){let s=e.deps;try{let a=(await s.listTracks()).filter(d=>!d.archivedAt);if(a.length>=t.maxConcurrent){await $t(o,{...We(`At capacity \u2014 ${a.length} live Tracks (limit ${t.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!t.channel){await $t(o,{...We("No designated channel configured \u2014 set `slack.channel` in integrations.json."),replace_original:!0});return}let i=await s.createTrack({name:n,...r?{repo:r}:{}}),c=i.workspace||{},l={trackId:i.id,channel:t.channel,name:i.name,branch:c.branch||null,repoLabel:c.repo||(c.base?ff.basename(c.base):"fresh workspace"),lead:"claude"},u=await bb(t.botToken,{channel:t.channel,text:`Track started: ${l.name}`,blocks:zR(l),unfurl_links:!1});l.ts=u.ts,(await Eb())[i.id]=l,await Cn(),await $t(o,{...We(`\u2713 *${_e(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await $t(o,{...We(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function f6(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.archiveTrack(n.id);let s=await _b(n.id);s&&(s.archivedAt=Date.now(),await Cn(),await Tb(t,s).catch(()=>{})),await $t(r,{...We(`\u2713 *${_e(n.name)}* archived.`),replace_original:!0})}catch(s){await $t(r,{...We(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function m6(e,t,{track:n,agent:r,responseUrl:o}){try{await e.deps.startLeadHandoff(n.id,r),await $t(o,{...We(`\u2713 Handing *${_e(n.name)}* to ${_e(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await $t(o,{...We(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function h6(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.interruptLead(n.id);let s=(o.listSubtaskJobs?.(n.id)??[]).filter(a=>!a.finishedAt);for(let a of s)try{o.cancelSubtask(a.id)}catch{}await $t(r,{...We(`\u2713 Stopped *${_e(n.name)}*${s.length?` (+ ${s.length} subtask${s.length>1?"s":""} canceled)`:""} \u2014 Track parked, resume from the Track page.`),replace_original:!0})}catch(s){await $t(r,{...We(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function w6(e,t){let n=[...t.slice(0,99).map(r=>({text:{type:"plain_text",text:El(`${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 g6(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?ff.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(`
|
|
769
769
|
`)+(o?`
|
|
770
|
-
_${o} archived_`:"")}async function
|
|
771
|
-
\u2713 *${_e(a)}*`}}]}).catch(()=>{})}}catch(c){await $t(n.response_url,We(`Couldn't answer: ${c?.message||c}`))}}async function
|
|
772
|
-
\u2713 _answered_`}}]})}async function
|
|
773
|
-
_runs on the Track's branch; a Bostrat-fronted preview, not a public deployment_`}}]);return}case"mock.state.changed":return
|
|
774
|
-
`)){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
|
|
775
|
-
`)}ye();Yh();import G6 from"node:fs/promises";import K6 from"node:path";Uu();Ku();lo();var hf=e=>e.payload?.ts??e.ts,L6=12e4,dn=e=>Math.round(e*1e4)/1e4,F6=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{},byTrack:new Map});function U6(e,{since:t}){let{track:n,events:r,brief:o}=e,s=x=>hf(x)>=t,a=fr([ic(n?.name),ic(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=fr(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:D=null}={})=>({textCat:x,shape:S,continuation:$,text:M,ts:D,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((x,S)=>hf(x)-hf(S)),p=0,f=0,w=0,m={turns:0};for(let x of d){let S=l(x.boxId),$=S.segments[S.segments.length-1],M=hf(x);switch(S.lastTs!=null&&M>S.lastTs&&s(x)&&($.activeMs+=Math.min(M-S.lastTs,L6)),S.lastTs=M,x.provider&&(S.provider=x.provider),x.type){case"turn.user":{let D=x.payload?.text;if(x.source==="hook"||!String(D||"").trim()||x.payload?.note||uc(D))break;let j=fr(D),A=!!x.payload?.kind,_=A||Kx(D),L=u(j,{shape:A?"injected":_?"follow-up":"novel-ask",continuation:!j&&_,text:eO(D),ts:M});S.segments.push(L),s(x)&&(L.turns+=1);break}case"peer.message":{let D=x.payload?.text;if(!String(D||"").trim())break;let j=fr(D),A=u(j,{shape:"peer",continuation:!j,text:eO(D),ts:M});S.segments.push(A),s(x)&&(A.turns+=1);break}case"tool.called":{if(x.source==="transcript")break;let D=x.payload?.tool;D&&($.tools[D]=($.tools[D]||0)+1),s(x)&&($.toolCalls+=1);break}case"cost.recorded":{if(!s(x))break;let{usd:D,tokens:j,model:A,unpriced:_}=rn(x);_&&(m.turns+=1),$.usd+=D,j&&($.tokens.input+=j.input,$.tokens.output+=j.output,$.tokens.cacheRead+=j.cacheRead,$.tokens.cacheWrite+=j.cacheWrite),$.models[A]=($.models[A]||0)+D,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 g=new Map,b={usd:0,turns:0,byShape:{},samples:[]},y=!1;for(let[x,S]of c){let $=null;for(let M of S.segments){let D=null,j=null;if(M.textCat)D=M.textCat.category,j="text";else if(i.get(x))D=i.get(x).category,j="boxTask";else if(M.continuation&&$)D=$,j="inherited";else{let O=Gx(M.tools);O?(D=O.category,j="toolMix"):a&&(D=a.category,j="hint")}if(D&&($=D),!(M.usd>0||M.activeMs>0||M.turns>0||M.toolCalls>0))continue;let _=D??"uncategorized";g.has(_)||g.set(_,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{}});let L=g.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[O,T]of Object.entries(M.models))L.models[O]=(L.models[O]||0)+T;let G=M.provider||S.provider;G&&(L.providers[G]=(L.providers[G]||0)+M.usd),G==="cursor"&&M.usd===0&&(y=!0),D||(b.usd+=M.usd,b.turns+=M.turns,b.byShape[M.shape]=(b.byShape[M.shape]||0)+M.usd,M.usd>0&&M.text&&b.samples.push({trackId:n?.id,track:n?.name||n?.id,shape:M.shape,usd:dn(M.usd),ts:M.ts,text:M.text}))}}return{perCategory:g,uncategorized:b,handoffs:p,merged:f,failed:w,sawCursorActivity:y,unpricedTurns:m.turns}}function Ib(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 b of e||[]){let{track:y,events:x}=b;if(!x?.length){a+=1;continue}let{perCategory:S,uncategorized:$,handoffs:M,merged:D,failed:j,sawCursorActivity:A,unpricedTurns:_}=U6(b,{since:r});A&&(i=!0),c+=_,l.usd+=$.usd,l.turns+=$.turns;for(let[G,O]of Object.entries($.byShape))l.byShape[G]=(l.byShape[G]||0)+O;l.samples.push(...$.samples);let L={trackId:y?.id,name:y?.name||y?.id,archived:!!y?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:M,subtasksMerged:D,subtasksFailed:j};for(let[G,O]of S){L.usd+=O.usd,L.activeMs+=O.activeMs,L.turns+=O.turns,L.categories[G]={usd:dn(O.usd),activeMs:O.activeMs};for(let[P,W]of Object.entries(O.models))L.models[P]=dn((L.models[P]||0)+W);for(let[P,W]of Object.entries(O.providers))L.providers[P]=dn((L.providers[P]||0)+W);o.has(G)||o.set(G,F6());let T=o.get(G);T.usd+=O.usd,T.activeMs+=O.activeMs,T.turns+=O.turns,T.toolCalls+=O.toolCalls;for(let[P,W]of Object.entries(O.models))T.models[P]=(T.models[P]||0)+W;for(let[P,W]of Object.entries(O.providers))T.providers[P]=(T.providers[P]||0)+W;for(let[P,W]of Object.entries(O.provenance))T.provenance[P]=(T.provenance[P]||0)+W;let C=T.byTrack.get(y?.id)||{usd:0,activeMs:0};C.usd+=O.usd,C.activeMs+=O.activeMs,T.byTrack.set(y?.id,C)}(L.usd>0||L.activeMs>0||L.turns>0)&&(L.usd=dn(L.usd),L.dominantCategory=q6(L.categories),s.push(L))}let u=[...o.values()].reduce((b,y)=>b+y.usd,0),d=[...o.values()].reduce((b,y)=>b+y.activeMs,0),p=u>0?"usd":"time",f=p==="usd"?u:d,w=new Map((e||[]).map(b=>[b.track?.id,b.track?.name||b.track?.id])),m=[...o.entries()].map(([b,y])=>({category:b,sharePct:f>0?Math.round((p==="usd"?y.usd:y.activeMs)/f*100):0,shareBasis:p,usd:dn(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:Il(y.models),providers:Il(y.providers),provenance:Il(y.provenance),topWorkstreams:[...y.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:dn(S.usd),activeMs:S.activeMs}))})).sort((b,y)=>y.usd-b.usd||y.activeMs-b.activeMs);s.sort((b,y)=>y.usd-b.usd||y.activeMs-b.activeMs);let g={};for(let b of o.values())for(let[y,x]of Object.entries(b.provenance))g[y]=(g[y]||0)+x;return{since:r,capturedAt:n,totalUsd:dn(u),activeMs:d,turns:[...o.values()].reduce((b,y)=>b+y.turns,0),bullets:m,workstreams:s,provenance:Il(g),gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c,uncategorized:{usd:dn(l.usd),turns:l.turns,sharePct:u>0?Math.round(l.usd/u*100):0,byShape:Il(l.byShape),top:l.samples.sort((b,y)=>y.usd-b.usd).slice(0,H6)}}}}var H6=25,QR=160,eO=e=>{let t=String(e||"").replace(/\s+/g," ").trim();return t.length>QR?`${t.slice(0,QR).trimEnd()}\u2026`:t},W6=10;function tO(e,{thresholdPct:t=W6}={}){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:dn(o),totalUsd:dn(r),turns:n.turns||0,inheritedPct:r>0?Math.round((e?.provenance?.inherited||0)/r*100):0,problems:i}}var q6=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},Il=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,dn(n)]));var Fs={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function V6(e){let t=String(e||"7d");return t in Fs?{key:t,ms:Fs[t]}:{key:"7d",ms:Fs["7d"]}}function z6(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=vd(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 J6(e){if(!e?.cwd)return null;try{let n=(await G6.readFile(K6.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 $b(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}:V6(t),d=[];for(let w of e||[]){let m=await K(w),g=z6(w,m);i&&c<o&&(g=g.filter(b=>(b.ts??0)<=c)),d.push({track:w,events:g,brief:await J6(w)})}let p=Ib(d,{windowMs:u,now:c}),f=l==="7d"&&c===o?p:Ib(d,{windowMs:Fs["7d"],now:o});return{window:l,...p,categorization:tO(f)}}var $l=e=>`$${Number(e||0).toFixed(2)}`,nO=e=>{let t=Math.round((e||0)/6e4);return t>=60?`${Math.floor(t/60)}h${String(t%60).padStart(2,"0")}`:`${t}m`},Ls=(e,t)=>String(e).padEnd(t);function rO(e){let t=[];t.push(`USAGE ROUNDUP \xB7 ${e.window} \xB7 ${$l(e.totalUsd)} \xB7 ${nO(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(` ${Ls(`${a.shareBasis==="time"?"~":""}${a.sharePct}%`,5)} ${Ls(a.category,15)} ${Ls($l(a.usd),10)} ${Ls(nO(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 ${$l(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} ${$l(i)}`).join(" \xB7 ")||"none")),s.top?.length||t.push(" (nothing unmatched carried cost in this window)");for(let a of s.top||[])t.push(` ${Ls($l(a.usd),8)} ${Ls(a.shape,10)} ${Ls(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(`
|
|
776
|
-
`)}
|
|
777
|
-
`).slice(-3).join(" | ").slice(0,300);d!==
|
|
778
|
-
`).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
|
|
779
|
-
`).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
|
|
780
|
-
`),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 Cl(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=Cl(r);[c.primary,c.secondary].forEach((f,w)=>{let m=
|
|
781
|
-
`,{mode:384}),vf.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function Nl(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
|
|
770
|
+
_${o} archived_`:"")}async function y6(e,t){let n=await Ps();if(!n)return We("Slack integration not configured.");let r=e.deps,o=(t.text||"").trim().split(/\s+/).filter(Boolean),s=(o[0]||"help").toLowerCase(),a=o.slice(1);switch(s){case"start":{if(!a.length)return We("Usage: `/bostrat start <name> [repo]`");let i=await r.listRepos(),c=a[a.length-1]?.toLowerCase(),l=a.length>1?i.find(d=>d.name.toLowerCase()===c):null,u=(l?a.slice(0,-1):a).join(" ");return l?($o(JR(e,n,{name:u,repo:l.path,responseUrl:t.response_url})),We(`Starting *${_e(u)}* on ${_e(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:w6(u,i)}}case"archive":{if(!a.length)return We("Usage: `/bostrat archive <track>`");let{match:i,live:c}=vb(await r.listTracks(),a.join(" "));return i?($o(f6(e,n,{track:i,responseUrl:t.response_url})),We(`Archiving *${_e(i.name)}*\u2026`)):Sb(c,a.join(" "))}case"handoff":{let i=r.knownAgents??p6,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return We(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:d}=vb(await r.listTracks(),l);return u?($o(m6(e,n,{track:u,agent:c,responseUrl:t.response_url})),We(`Handing *${_e(u.name)}* to ${_e(c)}\u2026`)):Sb(d,l)}case"stop":{if(!a.length)return We("Usage: `/bostrat stop <track>`");let{match:i,live:c}=vb(await r.listTracks(),a.join(" "));return i?($o(h6(e,n,{track:i,responseUrl:t.response_url})),We(`Stopping *${_e(i.name)}*\u2026`)):Sb(c,a.join(" "))}case"list":return We(await g6(r));default:return We(d6)}}async function b6(e,t,n,r){let{trackId:o,promptId:s,label:a}=r,i=await _b(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 Cn(),await js(t.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${_e(l||"question")}~
|
|
771
|
+
\u2713 *${_e(a)}*`}}]}).catch(()=>{})}}catch(c){await $t(n.response_url,We(`Couldn't answer: ${c?.message||c}`))}}async function k6(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)?po.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,_a(d,{branch:u.payload.branch,base:u.payload.base}),{kind:d.includes("pr")?"pr-go":"push-go"}),await i.appendEvent({id:c.id,cwd:c.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:u.boxId,payload:{ts:Date.now(),offerId:s,action:d?a:"dismiss",actions:d}})}catch(c){await $t(n.response_url,We(`Couldn't act on the offer: ${c?.message||c}`))}}async function x6(e,t,n,r){let{trackId:o,offerId:s,choice:a}=r;try{let i=await e.deps.resolveTrack(o);if(!i)throw new Error("track not found");await e.deps.answerPrExists(i,{offerId:s,choice:a})}catch(i){await $t(n.response_url,We(`Couldn't act on the offer: ${i?.message||i}`))}}async function v6(e,t){let n=t.actions?.[0];if(!n?.action_id)return;let r=await Ps();if(!r)return;if(n.action_id==="bostrat-start-repo"){let s;try{({name:s}=JSON.parse(n.block_id))}catch{return}let a=n.selected_option?.value;await $t(t.response_url,{...We(`Starting *${_e(s)}*\u2026`),replace_original:!0}),$o(JR(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"?$o(b6(e,r,t,o)):n.action_id==="bostrat-ship"?$o(k6(e,r,t,o)):n.action_id==="bostrat-pr-exists"&&$o(x6(e,r,t,o))}async function S6(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 l6(t.channel,t.thread_ts);if(!n)return;let r=await Ps();if(r)try{await e.deps.sayToLead(n.trackId,t.text.trim())}catch(o){await Ro(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function A6(e){return async t=>{if(t.type==="slash_commands")return y6(e,t.payload);if(t.type==="interactive"){await v6(e,t.payload);return}if(t.type==="events_api"){await S6(e,t.payload?.event);return}}}var T6=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 E6(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:El(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 _6(e,t,n){let r=n.payload||{};if(!r.promptId||t.prompt?.promptId===r.promptId)return;t.prompt&&await js(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 Ro(e,t,r.question||"The lead needs an answer",E6(n.trackId,r));t.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await Cn()}async function I6(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 Cn(),await js(e.botToken,{channel:t.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${_e(s||"question")}~
|
|
772
|
+
\u2713 _answered_`}}]})}async function $6(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 Ro(e,t,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${_e(El(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await Cn()}async function R6(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await Cn();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 js(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function O6(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 Ro(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 Cn()}async function M6(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await Cn();let s=r.choice==="dismiss"?"dismissed":r.choice==="new"?"\u2713 opening a new PR":"\u2713 pushing to the open PR";await js(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function C6(e,t,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??N6)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await WR(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 Ro(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 N6(e,t){let n=await $r(e);if(!n)return null;let r=In(n,t);if(!r)return null;let o=await Tl.readFile(un(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function P6(e,t){if(!T6.has(t.type))return;let n=await _b(t.trackId);if(!n)return;let r=await Ps();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 Cn(),await Tb(r,n);return;case"turn.agent":{if(!o.text)return;let s=u6(o.text);if(!s)return;await Ro(r,n,s,[{type:"section",text:{type:"mrkdwn",text:_e(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${Sl(t.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return _6(r,n,t);case"prompt.answered":return I6(r,n,t);case"pr.offered":return $6(r,n,t);case"pr.offer.answered":return R6(r,n,t);case"pr.exists.offered":return O6(r,n,t);case"pr.exists.answered":return M6(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 Ro(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 Cn(),await Tb(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await Ro(r,n,`Live preview ${o.name}: ${uf(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${_e(o.name)}*: ${uf(o.slug)}
|
|
773
|
+
_runs on the Track's branch; a Bostrat-fronted preview, not a public deployment_`}}]);return}case"mock.state.changed":return C6(r,n,t,e.deps)}}function YR(e){let t={deps:e};Ye.on("event",s=>{P6(t,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await Ps()||(r&&clearInterval(r),r=null,n=qR({getToken:async()=>(await Ps())?.appToken,onEnvelope:A6(t),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}xn();ye();Bg();yn();Aw();hr();import D6 from"node:fs/promises";function B6(e){let t=new Map;for(let n of String(e||"").split(`
|
|
774
|
+
`)){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 L6=new Set(["Write","Edit","NotebookEdit"]),F6=220,U6=160;function H6(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(L6.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,F6)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=Go(o.command.trim().replace(/\s+/g," ")).slice(0,U6));return r}function W6(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?kn(t.cwd,t.sessionId):t?.cwd?vm(t.cwd):null}async function ZR(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 qa(o?.cwd)}});break;case"UserPromptSubmit":{let c=String(e.prompt??e.user_prompt??""),l=Yd(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:H6(e,o)});break;case"Stop":await q6(r,o,e,i);break;default:break}return a}async function q6(e,t,n,r){let o=[],s=W6(n,t);if(s)try{o=B6(await D6.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||""}`,m=p.get(w)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},cacheWriteTtl:null,messageIds:[]};for(let y of Object.keys(m.tokens))m.tokens[y]+=f.tokens[y];f.cacheWriteTtl&&(m.cacheWriteTtl??={m5:0,h1:0},m.cacheWriteTtl.m5+=f.cacheWriteTtl.m5,m.cacheWriteTtl.h1+=f.cacheWriteTtl.h1),m.messageIds.push(f.messageId),m.speed=f.speed||null,p.set(w,m)}for(let[f,w]of p){let m=f.split("\0")[0];await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:m,usd:qn(m,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=XR(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:XR(d),tokens:d.tokens}:null,calls:l.length,turnIndex:G6(i,t)}})}var XR=e=>e.tokens.input+e.tokens.cacheWrite+e.tokens.cacheRead;function G6(e,t){if(!t?.name)return null;let n=$i(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}Gs();mm();zd();ye();ye();lo();ye();lo();hr();function _l(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 QR=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),eO=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},mf=e=>Math.round(e*1e4)/1e4;async function Ib(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=QR(),d=0,p=[];for(let f of e||[]){let w=await K(f),m=co(w,{boxLabels:_l(f)});if(m.turns===0){f.archivedAt?d++:p.push(f.name||f.id);continue}o+=m.totalUsd,s+=m.turns,a+=m.repricedTurns||0,i+=m.asRecordedUsd||0,c+=m.unpricedTurns||0;for(let y of m.unpricedModels||[])l.add(y);eO(u,m.totalTokens);for(let[y,k]of Object.entries(m.byModel))n[y]||={usd:0,events:0,tokens:QR()},n[y].usd=mf(n[y].usd+k.usd),n[y].events+=k.events,eO(n[y].tokens,k.tokens);for(let[y,k]of Object.entries(m.byProvider))r[y]||={usd:0,events:0},r[y].usd=mf(r[y].usd+k.usd),r[y].events+=k.events;t.push({id:f.id,name:f.name,usd:m.totalUsd,rollup:m})}return t.sort((f,w)=>w.usd-f.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:ka(),fleet:{totalUsd:mf(o),asRecordedUsd:mf(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 Oo=e=>`$${e.toFixed(4)}`,li=(e,t)=>String(e).padEnd(t);function tO(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 ${Oo(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 ${Oo(t.asRecordedUsd)} \xB7 current-rates total ${Oo(t.totalUsd)} (${t.totalUsd>=t.asRecordedUsd?"+":""}${Oo(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(` ${li(a,26)} ${li(Oo(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(` ${li(a.name||a.id,30)} ${Oo(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${li(c.label,40)} ${li(Oo(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))}${li(Oo(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
775
|
+
`)}ye();Jh();import Z6 from"node:fs/promises";import Q6 from"node:path";Uu();Ku();lo();var hf=e=>e.payload?.ts??e.ts,K6=12e4,pn=e=>Math.round(e*1e4)/1e4,V6=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{},byTrack:new Map});function z6(e,{since:t}){let{track:n,events:r,brief:o}=e,s=x=>hf(x)>=t,a=mr([ic(n?.name),ic(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=mr(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:D=null}={})=>({textCat:x,shape:S,continuation:$,text:M,ts:D,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((x,S)=>hf(x)-hf(S)),p=0,f=0,w=0,m={turns:0};for(let x of d){let S=l(x.boxId),$=S.segments[S.segments.length-1],M=hf(x);switch(S.lastTs!=null&&M>S.lastTs&&s(x)&&($.activeMs+=Math.min(M-S.lastTs,K6)),S.lastTs=M,x.provider&&(S.provider=x.provider),x.type){case"turn.user":{let D=x.payload?.text;if(x.source==="hook"||!String(D||"").trim()||x.payload?.note||uc(D))break;let j=mr(D),A=!!x.payload?.kind,_=A||Vx(D),L=u(j,{shape:A?"injected":_?"follow-up":"novel-ask",continuation:!j&&_,text:rO(D),ts:M});S.segments.push(L),s(x)&&(L.turns+=1);break}case"peer.message":{let D=x.payload?.text;if(!String(D||"").trim())break;let j=mr(D),A=u(j,{shape:"peer",continuation:!j,text:rO(D),ts:M});S.segments.push(A),s(x)&&(A.turns+=1);break}case"tool.called":{if(x.source==="transcript")break;let D=x.payload?.tool;D&&($.tools[D]=($.tools[D]||0)+1),s(x)&&($.toolCalls+=1);break}case"cost.recorded":{if(!s(x))break;let{usd:D,tokens:j,model:A,unpriced:_}=on(x);_&&(m.turns+=1),$.usd+=D,j&&($.tokens.input+=j.input,$.tokens.output+=j.output,$.tokens.cacheRead+=j.cacheRead,$.tokens.cacheWrite+=j.cacheWrite),$.models[A]=($.models[A]||0)+D,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 D=null,j=null;if(M.textCat)D=M.textCat.category,j="text";else if(i.get(x))D=i.get(x).category,j="boxTask";else if(M.continuation&&$)D=$,j="inherited";else{let O=Kx(M.tools);O?(D=O.category,j="toolMix"):a&&(D=a.category,j="hint")}if(D&&($=D),!(M.usd>0||M.activeMs>0||M.turns>0||M.toolCalls>0))continue;let _=D??"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[O,T]of Object.entries(M.models))L.models[O]=(L.models[O]||0)+T;let G=M.provider||S.provider;G&&(L.providers[G]=(L.providers[G]||0)+M.usd),G==="cursor"&&M.usd===0&&(g=!0),D||(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:pn(M.usd),ts:M.ts,text:M.text}))}}return{perCategory:y,uncategorized:k,handoffs:p,merged:f,failed:w,sawCursorActivity:g,unpricedTurns:m.turns}}function $b(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:D,failed:j,sawCursorActivity:A,unpricedTurns:_}=z6(k,{since:r});A&&(i=!0),c+=_,l.usd+=$.usd,l.turns+=$.turns;for(let[G,O]of Object.entries($.byShape))l.byShape[G]=(l.byShape[G]||0)+O;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:D,subtasksFailed:j};for(let[G,O]of S){L.usd+=O.usd,L.activeMs+=O.activeMs,L.turns+=O.turns,L.categories[G]={usd:pn(O.usd),activeMs:O.activeMs};for(let[P,W]of Object.entries(O.models))L.models[P]=pn((L.models[P]||0)+W);for(let[P,W]of Object.entries(O.providers))L.providers[P]=pn((L.providers[P]||0)+W);o.has(G)||o.set(G,V6());let T=o.get(G);T.usd+=O.usd,T.activeMs+=O.activeMs,T.turns+=O.turns,T.toolCalls+=O.toolCalls;for(let[P,W]of Object.entries(O.models))T.models[P]=(T.models[P]||0)+W;for(let[P,W]of Object.entries(O.providers))T.providers[P]=(T.providers[P]||0)+W;for(let[P,W]of Object.entries(O.provenance))T.provenance[P]=(T.provenance[P]||0)+W;let C=T.byTrack.get(g?.id)||{usd:0,activeMs:0};C.usd+=O.usd,C.activeMs+=O.activeMs,T.byTrack.set(g?.id,C)}(L.usd>0||L.activeMs>0||L.turns>0)&&(L.usd=pn(L.usd),L.dominantCategory=X6(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])),m=[...o.entries()].map(([k,g])=>({category:k,sharePct:f>0?Math.round((p==="usd"?g.usd:g.activeMs)/f*100):0,shareBasis:p,usd:pn(g.usd),activeMs:g.activeMs,turns:g.turns,toolCalls:g.toolCalls,models:Il(g.models),providers:Il(g.providers),provenance:Il(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:pn(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:pn(u),activeMs:d,turns:[...o.values()].reduce((k,g)=>k+g.turns,0),bullets:m,workstreams:s,provenance:Il(y),gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c,uncategorized:{usd:pn(l.usd),turns:l.turns,sharePct:u>0?Math.round(l.usd/u*100):0,byShape:Il(l.byShape),top:l.samples.sort((k,g)=>g.usd-k.usd).slice(0,J6)}}}}var J6=25,nO=160,rO=e=>{let t=String(e||"").replace(/\s+/g," ").trim();return t.length>nO?`${t.slice(0,nO).trimEnd()}\u2026`:t},Y6=10;function oO(e,{thresholdPct:t=Y6}={}){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:pn(o),totalUsd:pn(r),turns:n.turns||0,inheritedPct:r>0?Math.round((e?.provenance?.inherited||0)/r*100):0,problems:i}}var X6=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},Il=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,pn(n)]));var Fs={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function eG(e){let t=String(e||"7d");return t in Fs?{key:t,ms:Fs[t]}:{key:"7d",ms:Fs["7d"]}}function tG(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=vd(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 nG(e){if(!e?.cwd)return null;try{let n=(await Z6.readFile(Q6.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 Rb(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}:eG(t),d=[];for(let w of e||[]){let m=await K(w),y=tG(w,m);i&&c<o&&(y=y.filter(k=>(k.ts??0)<=c)),d.push({track:w,events:y,brief:await nG(w)})}let p=$b(d,{windowMs:u,now:c}),f=l==="7d"&&c===o?p:$b(d,{windowMs:Fs["7d"],now:o});return{window:l,...p,categorization:oO(f)}}var $l=e=>`$${Number(e||0).toFixed(2)}`,sO=e=>{let t=Math.round((e||0)/6e4);return t>=60?`${Math.floor(t/60)}h${String(t%60).padStart(2,"0")}`:`${t}m`},Ls=(e,t)=>String(e).padEnd(t);function aO(e){let t=[];t.push(`USAGE ROUNDUP \xB7 ${e.window} \xB7 ${$l(e.totalUsd)} \xB7 ${sO(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(` ${Ls(`${a.shareBasis==="time"?"~":""}${a.sharePct}%`,5)} ${Ls(a.category,15)} ${Ls($l(a.usd),10)} ${Ls(sO(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 ${$l(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} ${$l(i)}`).join(" \xB7 ")||"none")),s.top?.length||t.push(" (nothing unmatched carried cost in this window)");for(let a of s.top||[])t.push(` ${Ls($l(a.usd),8)} ${Ls(a.shape,10)} ${Ls(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(`
|
|
776
|
+
`)}hr();var Mo=36e5,uO=24*Mo,rG=400,oG=12,Us=e=>Math.round(e*1e4)/1e4;function Ob({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 Fs?String(e||"7d"):"7d",i=Fs[a];return{key:a,since:i==null?null:r-i,until:r}}var sG=(e,t)=>e!=null&&t-e<=48*Mo?Mo:uO;function iO(e,t){if(t===Mo)return Math.floor(e/Mo)*Mo;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function aG(e,t){if(t===Mo)return e+Mo;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var cO=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),lO=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},iG=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Us(n)]));async function dO(e,t={}){let n=t.now??Date.now(),r=Ob({...t,now:n}),o=r.since==null?uO:sG(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=cO(),d=0,p=0,f=0,w=0,m=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 G=[];for(let T of L){let C=T.ts??0;s(C)?G.push(T):i(C)&&(y+=on(T).usd)}if(!G.length)continue;let O=co(G,{boxLabels:_l(A)});d+=O.totalUsd,p+=O.turns,f+=O.repricedTurns||0,w+=O.asRecordedUsd||0,m+=O.unpricedTurns||0;for(let T of O.unpricedModels||[])k.add(T);lO(u,O.totalTokens);for(let[T,C]of Object.entries(O.byModel))c[T]||={usd:0,events:0,tokens:cO()},c[T].usd=Us(c[T].usd+C.usd),c[T].events+=C.events,lO(c[T].tokens,C.tokens);for(let[T,C]of Object.entries(O.byProvider))l[T]||={usd:0,events:0},l[T].usd=Us(l[T].usd+C.usd),l[T].events+=C.events;for(let T of G){let{usd:C}=on(T);if(!C)continue;let P=iO(T.ts??0,o),W=g.get(P)??{t:P,total:0,byProvider:{}};W.total+=C;let X=T.provider||"unknown";W.byProvider[X]=(W.byProvider[X]||0)+C,g.set(P,W),(M==null||P<M)&&(M=P)}x.push({id:A.id,name:A.name||A.id,archived:!!A.archivedAt,usd:O.totalUsd,turns:O.turns,providers:Object.keys(O.byProvider)});for(let[T,C]of Object.entries(O.byBox))S.push({trackId:A.id,trackName:A.name||A.id,boxId:T,...C})}let D=r.since!=null?iO(r.since,o):M,j=[];for(let A=D,_=0;A!=null&&A<r.until&&_<rG;A=aG(A,o),_++){let L=g.get(A);j.push(L?{t:A,total:Us(L.total),byProvider:iG(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:ka(),window:r.key,since:r.since,until:r.until,bucketMs:o,series:j,fleet:{totalUsd:Us(d),asRecordedUsd:Us(w),turns:p,repricedTurns:f,unpricedTurns:m,unpricedModels:[...k],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:x.length,tracksSilentLive:$},prior:a!=null?{totalUsd:Us(y),since:r.since-a,until:r.since}:null,tracks:x.slice(0,50),boxes:S.slice(0,oG)}}Kd();var cG=new Set(["cost.recorded","code.committed","code.rework","rework.cause.offered","rework.cause.answered"]),lG=20,uG=40;async function pO(e,t={}){let n=t.now??Date.now(),r=Ob({...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=_l(l);for(let f of u){if(!cG.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=YT(s,{boxMeta:a});return{generatedAt:new Date().toISOString(),window:r.key,since:r.since,until:r.until,reworkWindowDays:yo,decayEndDays:hw,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,lG),fixes:c.fixes.slice(0,uG)}}import dG from"node:fs";import fO from"node:fs/promises";import Rl from"node:os";import pG from"node:path";sg();dt();var fG=1024*1024,wf=e=>Math.round(e/fG);async function mG(e){try{let t=await fO.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:wf(r),diskTotalMb:wf(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function hG(){try{return(await bn()).filter(t=>t.isBostrat).length}catch{return null}}async function wG(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await fO.readdir(pG.join(ml,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function mO({stateDir:e=ml,now:t=Date.now}={}){let n=Rl.totalmem(),[r,o,s,a]=await Promise.all([lp().catch(()=>null),mG(dG.existsSync(e)?e:Rl.tmpdir()),hG(),wG()]),i=Rl.cpus()?.length||null,c=Rl.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?wf(n-r):null,memTotalMb:wf(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(Rl.uptime())}}Uw();ye();Gm();Ci();hr();ye();hr();var gG=864e5;function yG(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=qn(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 hO(e,{days:t=14,tolerance:n=.005,now:r=Date.now()}={}){let o=r-t*gG,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=yG(p,{tolerance:n});if(!f)continue;a++;let w=f.speed?`${f.model} (${f.speed})`:f.model,m=s[w]||={rows:0,divergent:0,asRecordedUsd:0,expectedUsd:0};m.rows++,m.asRecordedUsd+=f.asRecorded,m.expectedUsd+=f.expected,f.divergent&&(i++,m.divergent++,c.length<10&&c.push({track:u.name||u.id,...f}))}}let l=u=>Math.round(u*1e4)/1e4;for(let u of Object.values(s))u.asRecordedUsd=l(u.asRecordedUsd),u.expectedUsd=l(u.expectedUsd);return{sinceMs:o,days:t,tolerance:n,rows:a,divergent:i,byModel:s,examples:c,ok:i===0}}jw();Lg();yd();bt();sn();import ui from"node:fs/promises";import bG from"node:fs";import vt from"node:path";import kG from"node:os";import{execFile as xG}from"node:child_process";import{promisify as vG}from"node:util";var SG=vG(xG),wO=process.env.BOSTRAT_STATE_DIR||vt.join(process.env.HOME||kG.tmpdir(),".bostrat"),gf=!1,Mb=null;async function Cb(e,t){let n=await ui.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await ui.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await ui.mkdir(vt.dirname(t),{recursive:!0}),await ui.copyFile(e,t),!0)}var AG=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 yf(e,t,{skip:n=null}={}){let r=0,o=await ui.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(AG.has(s.name)||n?.has(s.name))continue;let a=vt.join(e,s.name);s.isDirectory()?r+=await yf(a,vt.join(t,s.name)):s.isFile()&&(r+=await Cb(a,vt.join(t,s.name))?1:0)}return r}async function gO({stateDir:e=wO,dest:t=process.env.BOSTRAT_SYNC_DIR||`${wO}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await ui.mkdir(t,{recursive:!0});let o=0;o+=await Cb(vt.join(e,"tracks.json"),vt.join(t,"tracks.json"))?1:0,o+=await Cb(vt.join(e,"events.jsonl"),vt.join(t,"events.jsonl"))?1:0,o+=await yf(vt.join(e,"events"),vt.join(t,"events")),o+=await yf(vt.join(e,"memory"),vt.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await yf(vt.join(u.cwd,".bostrat"),vt.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>SG("git",["-C",t,...u]);bG.existsSync(vt.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 Zf(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||gf))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,gf&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),gf=!1,Mb=null}catch(u){gf=!0;let d=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
777
|
+
`).slice(-3).join(" | ").slice(0,300);d!==Mb&&(Mb=d,console.warn(`[state-sync] PUSH FAILING (retrying each cycle; off-box copy is STALE): ${d}`))}return{staged:o,committed:i,pushed:l}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}Tg();xn();Xo();dt();vi();import{execFile as TG}from"node:child_process";import{promisify as EG}from"node:util";import{randomUUID as _G}from"node:crypto";import IG from"node:fs";import $G from"node:os";import RG from"node:path";var No=EG(TG),bf=e=>new Promise(t=>setTimeout(t,e)),Po=gm,Nb=process.env.BOSTRAT_USAGE_PROBE_CWD||RG.join($G.homedir(),".bostrat","usage-probe"),OG=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,MG=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,CG=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,yO=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Co=null,Ol=null,Ml=null;async function NG(){try{return await No(ne,se("has-session","-t",Po)),!0}catch{return!1}}function PG(){Co&&clearTimeout(Co),Co=setTimeout(()=>DG().catch(()=>{}),OG),Co.unref&&Co.unref()}async function DG(){Co&&(clearTimeout(Co),Co=null);try{await No(ne,se("kill-session","-t",Po))}catch{}}async function BG(){let e=Date.now()+CG;for(;;){let t=await Pb().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(t)){await bf(500);return}if(Date.now()>e)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await bf(500)}}async function jG(){if(await NG())return;if(!await Jr())throw new Error("host tmux server not running \u2014 start globo-tmux.service");IG.mkdirSync(Nb,{recursive:!0}),await Fr(Nb);let e=_G();await No(ne,se("new-session","-d","-s",Po,"-c",Nb,"-x","200","-y","60",...Hi()));let t=`${wm} --session-id ${e}`;await No(ne,se("send-keys","-t",Po,t,"Enter")),await BG()}async function Pb(){let{stdout:e}=await No(ne,se("capture-pane","-t",Po,"-p"));return e}async function LG(){await jG(),await No(ne,se("send-keys","-t",Po,"-l","/usage")),await No(ne,se("send-keys","-t",Po,"Enter"));let e=Date.now()+MG,t="";for(;await bf(400),t=await Pb(),!(/Current\s+(session|week)/i.test(t)||/\d+%\s*used/i.test(t)||Date.now()>e););return await bf(900),t=await Pb(),await No(ne,se("send-keys","-t",Po,"Escape")).catch(()=>{}),PG(),t}function FG(e){let t=String(e||"").split(`
|
|
778
|
+
`).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 UG(e){let t=String(e||"").split(`
|
|
779
|
+
`).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 bO({fresh:e=!1}={}){return!e&&Ol&&Date.now()-Ol.at<yO?Ol.data:Ml||(Ml=(async()=>{try{let t=await LG(),n=FG(t),r={...n,contributors:UG(t),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Ol={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 Ol={at:Date.now()-(yO-3e3),data:n},n}finally{Ml=null}})(),Ml)}var HG=262144;function WG(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 qG=new Set([300]),GG=new Set([10080]);function KG(e){let t=Number(e);return!Number.isFinite(t)||t<=0?null:qG.has(t)?"session":GG.has(t)?"week":"other"}function VG(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 zG(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?WG((o-n)/1e3):null,resetsAt:o}}var Cl=e=>({session:null,week:null,other:null,planType:null,credits:null,limitReached:null,sawTokenCount:e});function JG(e,t=Date.now()){let n=String(e||"").split(`
|
|
780
|
+
`),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 Cl(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=Cl(r);[c.primary,c.secondary].forEach((f,w)=>{let m=zG(f,l,t);if(!m)return;let y=KG(f.window_minutes);y===null&&(y=w===0?"session":"week"),y==="session"&&!u.session?u.session=m:y==="week"&&!u.week?u.week=m:(u.other??=[]).push({label:VG(f.window_minutes),...m})}),u.planType=typeof c.plan_type=="string"?c.plan_type:null;let p=c.credits;return p&&(p.has_credits||p.unlimited)&&(u.credits={balance:String(p.balance??""),unlimited:!!p.unlimited}),u.limitReached=typeof c.rate_limit_reached_type=="string"&&c.rate_limit_reached_type||(c.spend_control_reached?"spend_control":null),u}if(c.primary_used_percent!=null||c.secondary_used_percent!=null){let l=u=>Number.isFinite(Number(u))?{pct:Math.round(Number(u)),resets:null,resetsAt:null}:null;return{...Cl(r),session:l(c.primary_used_percent),week:l(c.secondary_used_percent)}}return Cl(r)}return Cl(r)}async function Db(e){let t={provider:"codex",supported:!0,contributors:null};try{let n=await Jo(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 Bt(n.file,HG),o=JG(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 kO(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 kf({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 Db(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await Db(null))}return n.has("cursor")&&r.push(await kf({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function kf(e,{fresh:t=!1}={}){let n=e?.agent||"claude";return n==="codex"?Db(e):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await bO({fresh:t})}}Hr();bt();bt();function YG(e=process.env){return!!(Ne(e)&&e.BOSTRAT_PLANE_TOKEN)}var XG=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),xf=(e,t)=>Object.assign(new Error(e),{status:t});async function xO(e,t){if(!YG(t))throw xf("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${Ne(t)}${e}`,{headers:XG(t)})}catch(o){throw xf(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw xf(r?.error||`plane replied ${n.status}`,n.status);return r}async function vO(e,{max:t=10,env:n=process.env}={}){let r=new URLSearchParams({q:e||""});return t&&r.set("max",String(t)),(await xO(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function SO(e,{env:t=process.env}={}){if(!e)throw xf("message id required",400);return(await xO(`/v1/gmail/messages/${encodeURIComponent(e)}`,t)).message}xc();Nd();function Bb(e){let t=e.split(".");if(t.length!==4)return null;let n=0;for(let r of t){let o=Number(r);if(!Number.isInteger(o)||o<0||o>255)return null;n=n*256+o}return n>>>0}function ZG(e){let[t,n]=e.split("/"),r=Bb(t);if(r==null)return null;let o=n==null?32:Number(n);if(!Number.isInteger(o)||o<0||o>32)return null;let s=o===0?0:4294967295<<32-o>>>0;return a=>(a&s)===(r&s)}function AO(e){return(e||"").split(",").map(t=>t.trim()).filter(Boolean).map(ZG).filter(Boolean)}function QG(e){let t=e.headers["fly-client-ip"];return typeof t=="string"&&t.trim()?t.trim():(e.socket&&e.socket.remoteAddress||"").replace(/^::ffff:/,"")}function TO(e){let t=AO(e),n=String(e||"").trim().toLowerCase()==="open";return{open:n,clientIp:QG,allowed(r){if(n)return!0;let o=Bb((r||"").replace(/^::ffff:/,""));return o==null?!1:t.some(s=>s(o))}}}function EO(e,t){let n=AO(e);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=Bb(o);if(s==null||!n.some(i=>i(s)))return!1;let a=t(r);return!!(a&&a.view)}}}import _O from"node:crypto";import vf from"node:fs";import eK from"node:path";function IO(e=process.env,{stateDir:t=ml,log:n=console}={}){let r=String(e.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=eK.join(t,"api-token");try{let a=vf.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=_O.randomBytes(32).toString("base64url");try{vf.mkdirSync(t,{recursive:!0}),vf.writeFileSync(o,s+`
|
|
781
|
+
`,{mode:384}),vf.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function Nl(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 $O(e,t){if(!e||!t)return!1;let n=Buffer.from(String(e)),r=Buffer.from(String(t));return n.length!==r.length?!1:_O.timingSafeEqual(n,r)}function RO(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 tK=[/^\/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/],nK=e=>tK.some(t=>t.test(e));function OO({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=RO(o);return $O(c,e)?a():nK(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||Nl(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 MO(e,t,{enforce:n=!0}={}){return!n||$O(RO(e),t)?!0:Nl(e)}yn();ap();ap();import rK from"node:http";import oK from"node:net";function Pl(e){if(!e)return null;let t=String(e).replace(/:\d+$/,"").toLowerCase();if(/^[\d.]+$/.test(t)||t.includes(":"))return null;let n=t.split(".");if(n.length<3)return null;let r=n[0];return/^[a-z0-9-]+$/.test(r)?r:null}function Sf(e){let t=Pl(e.headers.host);return t?{slug:t,view:eg(t)}:null}var sK=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),aK=e=>{let t=sK(e.name);return`<!doctype html><meta charset="utf-8">
|
|
782
782
|
<meta http-equiv="refresh" content="3">
|
|
783
783
|
<title>${t} \u2014 starting</title>
|
|
784
784
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
785
785
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
786
|
-
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function
|
|
786
|
+
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function CO(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 NO(e,t,n){let r=Sf(e);if(!r)return n();let{view:o,slug:s}=r;if(!o)return t.statusCode=404,t.setHeader("content-type","text/plain"),t.end(`bostrat: no view named "${s}"`);if(o.state==="starting")return t.statusCode=503,t.setHeader("content-type","text/html"),t.end(aK(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=rK.request({host:"127.0.0.1",port:o.port,method:e.method,path:CO(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 PO(e,t,n){let r=Sf(e);if(!r)return!1;let{view:o,slug:s}=r;if(!o||o.state!=="live"&&o.state!=="unhealthy")return t.write(`HTTP/1.1 404 Not Found\r
|
|
787
787
|
\r
|
|
788
788
|
bostrat: no live view "${s}"\r
|
|
789
|
-
`),t.destroy(),!0;let a=
|
|
789
|
+
`),t.destroy(),!0;let a=oK.connect(o.port,"127.0.0.1",()=>{let c=[`${e.method} ${CO(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
|
|
790
790
|
`)+`\r
|
|
791
791
|
\r
|
|
792
|
-
`),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
|
|
792
|
+
`),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 jb(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=ib();if(r)return{proto:"https",host:r,source:"learned"};let o=cb();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:Pl(s)&&eg(a[0])?a.slice(1).join("."):s,source:"request"}}function DO(e,t){return lf(jb(t),e.slug)}function BO({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
|
|
793
793
|
\r
|
|
794
|
-
`),a.destroy();return}t(s,a,i)}}}function PO(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}Ua();mg();mp();dd();ye();import DO from"node:os";import{randomUUID as Lb}from"node:crypto";var eK=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||DO.hostname(),nodeName:e.BOSTRAT_NODE_NAME||DO.hostname()});var BO=32768,jO=120,LO=1e3,FO=new Set(["claude","codex","cursor"]),tK=new Set(["off","transition","nag"]),nK=[[0,59],[0,23],[1,31],[1,12],[0,6]];function rK(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=nK[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 Cn=e=>String(e??"").trim(),Hs={name(e){let t=Cn(e);if(!t)throw new Error("a loop needs a name");if(t.length>jO)throw new Error(`name is too long (${t.length}); cap is ${jO}`);return t},schedule(e){let t=Cn(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!rK(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=Cn(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>BO)throw new Error(`prompt is too long (${t.length} chars); cap is ${BO}`);return t},expectation(e){let t=Cn(e)||null;if(t&&t.length>LO)throw new Error(`expectation is too long (${t.length}); cap is ${LO}`);return t},notify(e){let t=Cn(e)||"off";if(!tK.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=Cn(typeof t=="string"?t:t?.provider)||"claude";if(!FO.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...FO].join(", ")}`);let r=typeof t=="object"&&t&&Cn(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 oK(e,t){let n=e&&typeof e=="object"?e:{},r=Hs.name(n.name),o=Hs.schedule(n.schedule),s=Hs.prompt(n.prompt),a=Hs.expectation(n.expectation),i=Hs.notify(n.notify),c=Hs.assignment(n.assignment),l=Cn(t?.workspace?.repo)||Cn(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}=eK();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:d||null}}async function HO(e,t){let n=oK(t,e),r=Lb();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 WO(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 jb=["name","schedule","prompt","expectation","notify","assignment","enabled"],sK=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function qO(e){let t=Array.isArray(e.runs)?e.runs:[],n=t[t.length-1]??null;return{definitionUpdatedAt:Number(e.definitionUpdatedAt??e.updatedAt)||null,name:e.name??"",schedule:e.schedule??"",prompt:e.prompt??"",expectation:e.expectation??null,notify:e.notify??"off",assignment:e.assignment&&typeof e.assignment=="object"?e.assignment:{provider:String(e.assignment||"claude")},enabled:e.enabled!==!1,repo:e.repo??null,nodeId:e.nodeId??null,nodeName:e.nodeName??null,trackId:e.trackId??null,nextFireAt:Number.isFinite(Number(e.nextFireAt))?Number(e.nextFireAt):null,createdAt:Number(e.createdAt)||null,runCount:t.length,lastRun:n?{firedAt:Number(n.firedAt)||null,outcome:n.outcome??(n.error?"errored":null)}:null}}function GO(e,t){let n=Cn(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 aK(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=>!jb.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: ${jb.join(", ")}`)}let o={};for(let a of jb){if(!(a in n))continue;let i=Hs[a](n[a]);(a==="assignment"?sK(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 KO(e,t,n){let r=qO(t),{patch:o,changed:s}=aK(n,r),a=Lb(),i={ts:Date.now(),proposalId:a,loopId:String(t.id),name:r.name,base:r,patch:o,changed:s,selfEdit:!!(r.trackId&&r.trackId===e.id)};return await N({id:e.id,cwd:e.cwd},{type:"loop.update.offered",actor:"system",source:"orchestrator",payload:i}),i}var UO=600;async function VO(e,t,n){let r=Cn(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>UO)throw new Error(`reason is too long (${r.length}); cap is ${UO}`);let o=qO(t),s=Lb(),a={ts:Date.now(),proposalId:s,loopId:String(t.id),name:o.name,base:o,reason:r,selfEdit:!!(o.trackId&&o.trackId===e.id)};return await N({id:e.id,cwd:e.cwd},{type:"loop.delete.offered",actor:"system",source:"orchestrator",payload:a}),a}var zO=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 JO(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 zO(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 YO(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 zO(e,"loop.delete.offered",t)).payload,s={id:e.id,cwd:e.cwd};return(n==="delete"||n==="pause")&&await N(s,{type:n==="delete"?"loop.deleted":"loop.paused",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:o.loopId,name:o.name,trackId:o.base?.trackId??null}}),await N(s,{type:"loop.delete.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:o.loopId}}),{proposalId:t,action:n,loopId:o.loopId}}ye();import{randomUUID as iK}from"node:crypto";var Hb=20,cK=300,Fb=120,XO=120,ZO=2e3,Af=new Set(["build","ops","debt","spike"]),QO=new Set(["active","done","abandoned"]),Wb=new Set(["circuit.create","circuit.update","circuit.delete"]),lK=new Set(["person.create","slot.create","slot.update","slot.delete","reservation.create","reservation.update","reservation.delete"]),Dl=new Set([...Wb,...lK]),uK=["name","description","status","type"],dK=["person","startDate","endDate","estUsd"],pK=["person","label","startDate","endDate"],fK=/^\d{4}-\d{2}-\d{2}$/,Xt=e=>String(e??"").trim(),qb=e=>typeof e=="string"&&e.startsWith("@"),nM=864e5;function rM(e){let t=Date.parse(`${e}T00:00:00Z`),n=new Date(t).getUTCDay();return new Date(t-(n+6)%7*nM).toISOString().slice(0,10)}function mK(e){return new Date(Date.parse(`${rM(e)}T00:00:00Z`)+6*nM).toISOString().slice(0,10)}var eM=(e,t)=>{let n=Xt(e);if(!fK.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 hK(e,t){let n=eM(e,"startDate"),r=eM(t,"endDate");if(r<n)throw new Error(`endDate ${r} is before startDate ${n}`);let o=rM(n),s=mK(r);return{startDate:o,endDate:s,snapped:o!==n||s!==r}}var tM=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},Do=(e,t,n)=>{let r=Xt(e);if(r.length>t)throw new Error(`${n} is too long (${r.length}); cap is ${t}`);return r};function Ub(e,t){let n=Xt(t);if(!n)throw new Error("a circuit name or id is required");if(qb(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 wK(e,t){let n=Xt(t);if(!n)throw new Error('a person is required \u2014 "me", a person id, or a row label');if(n==="me"||qb(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 gK(e,t,{allow:n=Dl}={}){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>Hb)throw new Error(`too many ops (${o.length}); cap is ${Hb} \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)},m=x=>{x.id&&(u[x.id]=x.label)},g=(x,S)=>{let $=Xt(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,$},b=o.map((x,S)=>{let $=x&&typeof x=="object"?x:{},M=Xt($.op),D=`op ${S+1}`;if(!Dl.has(M))throw new Error(`${D}: unknown op "${M}" \u2014 one of ${[...Dl].join(", ")}`);if(!n.has(M))throw new Error(`${D}: "${M}" isn't a circuit op \u2014 roadmap changes go through \`bostrat-roadmap propose\` (one card, either CLI)`);let j=O=>{throw new Error(`${D} (${M}): ${O}`)},A=O=>{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(P=>!O.includes(P));return C.length&&j(`unknown field(s) ${C.join(", ")} \u2014 settable: ${O.join(", ")}`),Object.keys(T).length||j("`set` names no fields"),T},_=O=>{let{startDate:T,endDate:C,snapped:P}=hK(O.startDate,O.endDate);return P&&(p=!0),{startDate:T,endDate:C,snapped:P}},L=O=>{let T=wK(a,O);return Xt(O)==="me"&&(f=!0),m(T),{key:T.id??Xt(O),label:T.label}},G=(O,T,C,P,W)=>{let X=Xt(O);X||j(`needs a ${P} id`);let ge=T.find(Ie=>Ie[C]===X);return ge||j(`no ${P} "${X}" \u2014 see \`bostrat-roadmap ls\``),W[X]={...ge},ge};switch(M){case"circuit.create":{let O=Do($.name,XO,"name");O||j("a circuit needs a name");let T=Xt($.type)||"build";Af.has(T)||j(`bad type "${T}" (${[...Af].join(" | ")})`);let C=s.find(W=>String(W.name).toLowerCase()===O.toLowerCase());C&&j(`a circuit named "${C.name}" already exists (${C.id}) \u2014 update it instead of making a twin`);let P=g($.ref,O);return{op:M,...P?{ref:P}:{},name:O,description:Do($.description,ZO,"description"),type:T}}case"circuit.update":{let O=Ub(s,$.circuit);O.id||j("can't update a circuit created in the same plan \u2014 set its fields on the create op"),w(O);let T=A(uK),C={};if(T.name!==void 0){let P=Do(T.name,XO,"name");P||j("name can't be blank"),P!==O.name&&(C.name=P)}return T.description!==void 0&&(C.description=Do(T.description,ZO,"description")),T.status!==void 0&&(QO.has(T.status)||j(`bad status "${T.status}" (${[...QO].join(" | ")})`),T.status!==O.status&&(C.status=T.status)),T.type!==void 0&&(Af.has(T.type)||j(`bad type "${T.type}" (${[...Af].join(" | ")})`),T.type!==(O.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:O.id,name:O.name,set:C}}case"circuit.delete":{let O=Ub(s,$.circuit);O.id||j("can't delete a circuit created in the same plan"),w(O);let T=o.some((C,P)=>P<S&&Xt(C?.op)==="circuit.update"&&C?.set?.status&&C.set.status!=="active"&&Xt(C?.circuit)===Xt($.circuit));return O.status==="active"&&!T&&j(`\u201C${O.name}\u201D is active \u2014 an active circuit can't be deleted; propose a circuit.update to done/abandoned first (both can ride one card)`),{op:M,circuit:O.id,name:O.name,status:O.status}}case"person.create":{let O=Do($.label,Fb,"label");O||j("a person row needs a label");let T=a.find(P=>String(P.label).toLowerCase()===O.toLowerCase());T&&j(`a roadmap row labelled "${T.label}" already exists (${T.personId})`);let C=g($.ref,O);return{op:M,...C?{ref:C}:{},label:O}}case"slot.create":{let O=Ub(s,$.circuit);w(O);let T=L($.person),C=_($);return{op:M,circuit:O.id??O.ref,circuitName:O.name,person:T.key,personLabel:T.label,...C,estUsd:tM($.estUsd)}}case"slot.update":{let O=G($.slot,i,"slotId","slot",l.slots),T=A(dK),C={};if(T.person!==void 0){let W=L(T.person);C.person=W.key,C.personLabel=W.label}if(T.startDate!==void 0||T.endDate!==void 0){let W=_({startDate:T.startDate??O.startDate,endDate:T.endDate??O.endDate});W.startDate!==O.startDate&&(C.startDate=W.startDate),W.endDate!==O.endDate&&(C.endDate=W.endDate)}T.estUsd!==void 0&&(C.estUsd=tM(T.estUsd)),Object.keys(C).length||j("nothing would change");let P=s.find(W=>W.id===O.circuitId);return{op:M,slot:O.slotId,circuitName:P?.name??null,set:C}}case"slot.delete":{let O=G($.slot,i,"slotId","slot",l.slots),T=s.find(C=>C.id===O.circuitId);return{op:M,slot:O.slotId,circuitName:T?.name??null,startDate:O.startDate,endDate:O.endDate}}case"reservation.create":{let O=L($.person),T=Do($.label,Fb,"label");return T||j("a reservation needs a label (Leave, Support rotation, \u2026)"),{op:M,person:O.key,personLabel:O.label,label:T,..._($)}}case"reservation.update":{let O=G($.reservation,c,"reservationId","reservation",l.reservations),T=A(pK),C={};if(T.person!==void 0){let P=L(T.person);C.person=P.key,C.personLabel=P.label}if(T.label!==void 0){let P=Do(T.label,Fb,"label");P||j("label can't be blank"),P!==O.label&&(C.label=P)}if(T.startDate!==void 0||T.endDate!==void 0){let P=_({startDate:T.startDate??O.startDate,endDate:T.endDate??O.endDate});P.startDate!==O.startDate&&(C.startDate=P.startDate),P.endDate!==O.endDate&&(C.endDate=P.endDate)}return Object.keys(C).length||j("nothing would change"),{op:M,reservation:O.reservationId,label:O.label,set:C}}case"reservation.delete":{let O=G($.reservation,c,"reservationId","reservation",l.reservations);return{op:M,reservation:O.reservationId,label:O.label,startDate:O.startDate,endDate:O.endDate}}default:return j("unhandled op")}});b.forEach((x,S)=>{for(let $ of[x.circuit,x.person,x.set?.person])if(qb($)&&!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 y=a.some(x=>x.kind==="user"&&x.userId);return{why:Do(r.why,cK,"why"),ops:b,base:l,resolved:u,snapped:p,createsMeRow:f&&!y}}async function oM(e,t,n,r={}){let o=gK(t,n,r),s=iK(),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 yK=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 sM(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 yK(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,Hb+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}}Qd();ye();Vr();ye();Kn();mp();var aM=10,iM=200,bK=120,kK=24,lM=/^\d{4}-\d{2}-\d{2}$/;function Gb(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var xK=(e=Date.now())=>new Date(e).toISOString().slice(0,10),St=(e,t="i")=>new RegExp(e,t),vK=[{re:St("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:St("\\bSPC\\b",""),reason:"business (SPC)"},{re:St("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:St("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:St("\\bReplicas\\b",""),reason:"competitor research"},{re:St("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:St("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:St("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],SK=[St("\\bSPC\\b",""),St("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),St("competitor|cloudcli|\\bkiro\\b"),St("\\bReplicas\\b",""),St("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),St("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),St("(^|[^\\w&])#\\d+\\b"),St("\\btrack/[\\w.-]+")];function AK({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of vK)if(o.test(n)||o.test(r))return s;return null}function cM(e){let t=String(e||"");for(let n of SK){let r=n.exec(t);if(r)return r[0].trim()}return null}function uM(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 TK=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function EK(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Vc(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(`
|
|
795
|
-
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,bK)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${kK}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=TK.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=cM(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>iM)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${iM}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=cM(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>aM)throw new Error(`${r.length} bullets \u2014 over the ${aM}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function _K(e,{repos:t=Gb()}={}){let n=new Set(t);return(await An()).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:AK({title:r.title,headRef:r.headRef})}))}async function dM(e,t){let n=await _K(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function pM(e,{md:t,day:n=null}){let r=EK(t),o=n==null||n===""?xK():String(n);if(!lM.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 fM(e,{day:t}){let n=String(t||"");if(!lM.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}}gs();Kn();ys();ye();gs();bt();Ar();Kn();xc();zd();Nd();wa();on();import{execFile as LK}from"node:child_process";import{promisify as FK}from"node:util";var IK=new Set(["pull_request","check_suite","workflow_run","push"]),$K=/^[^/\s]+\/[^/\s]+$/,RK=500,Kb=new Set,Vb=[],Tf=new Map,pn={hourStart:0,byEvent:{},duplicates:0,rejected:0};function zb(e,t,n){let r=Math.floor(e/36e5)*36e5;r!==pn.hourStart&&(pn.hourStart=r,pn.byEvent={},pn.duplicates=0,pn.rejected=0),t==="accepted"?pn.byEvent[n]=(pn.byEvent[n]||0)+1:pn[t]+=1}function OK(e){if(!e||typeof e!="object"||e.forge!=="github"||typeof e.event!="string"||!IK.has(e.event)||typeof e.repo!="string"||!$K.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 mM(e,{now:t=Date.now(),onAccept:n=null,log:r=console}={}){let o=OK(e);if(!o)return zb(t,"rejected"),{ok:!1,error:"malformed github event envelope"};if(o.deliveryId){if(Kb.has(o.deliveryId))return zb(t,"duplicates"),{ok:!0,duplicate:!0,envelope:o};for(Kb.add(o.deliveryId),Vb.push(o.deliveryId);Vb.length>RK;)Kb.delete(Vb.shift())}let s=o.repo.toLowerCase(),a=Tf.get(s)||{lastDeliveryAt:0,count:0};a.lastDeliveryAt=t,a.count+=1,Tf.set(s,a),zb(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 MK(e,t=Date.now()){let n=Tf.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 hM=1440*6e4;function Jb(e,t=Date.now()){let n=MK(e,t);return n.covered&&n.ageMs<hM}function wM(e=Date.now()){let t={};for(let[n,r]of Tf)t[n]={lastDeliveryAt:r.lastDeliveryAt,count:r.count,covered:e-r.lastDeliveryAt<hM};return{hourStart:pn.hourStart,byEvent:{...pn.byEvent},duplicates:pn.duplicates,rejected:pn.rejected,repos:t}}ye();Kn();Hr();Ar();on();function gM(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function CK(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var NK=new Set(["merged","closed","lost"]);function PK(e,t){if(!e?.blocked)return null;let n=gM(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 DK(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function BK(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 jK(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 Ef(e,t={}){let{now:n=Date.now(),slugOf:r=Oe,lookup:o=pt,note:s=rT,tracks:a=async()=>(await be()).filter(d=>!Fe(d)),asked:i=BK,emitChange:c=jK}=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(!CK(f))continue;let w=await o(p,f.number).catch(()=>null);if(!w?.trackId||NK.has(w.state))continue;let{prev:m}=await s(p,f.number,{blocked:gM(f),sha:f.headSha??null,at:n});if(!m)continue;let g=PK(m,f);if(!g||DK(w,g.change))continue;u??=new Map((await a()).map(S=>[S.id,S]));let b=u.get(w.trackId);if(!b)continue;let y=f.headSha??null;if(await i(b,g.change,f.number,y,m.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:y,delegated:!1,reason:g.reason};g.change==="ci-fixed"?x.passed=f.checks.passed:x.base=f.baseRefName||null,await c(b,g.change,x),l.push({change:g.change,number:f.number,reason:g.reason})}catch{}}return l}var Zb=FK(LK),EM=3e4,UK=30*6e4,HK=EM,WK=10*6e4,qK=10080*6e4,GK=60*6e4,yM=8,_M=1440*6e4,KK=3,IM="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Xb=60*6e4,VK=15*6e4,zK=9e4,JK=1440*6e4,Bl=new Map,jl=new Map,YK=360*6e4,XK=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}),ZK=/could not resolve|not found|no pull requests?/i,QK=e=>ZK.test(String(e?.message||e||""));function eV(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<Xb)}var bM={fast:HK,slow:WK,decay:GK};function kM(e,t,n=Jb){return $M(e,t,{grace:0})||!n(e.repo,t)&&(e.armedAt&&t-e.armedAt<UK||eV(e,t))?"fast":t-(e.firstSeen||0)>qK?"decay":"slow"}function Fl(e,t,n,r){let o=e.lanes?.get(ue(t.repo,t.number))||{};return Cd({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function tV(e,t){br("watch");try{let{stdout:n}=await Zb("gh",["pr","view",String(t),"--repo",e,"--json",IM],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Dn({slug:e})}});return JSON.parse(n||"null")}catch(n){throw ho(n)&&wo(n),n}}async function nV(e){br("watch");try{let{stdout:t}=await Zb("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",IM],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await Dn({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw ho(t)&&wo(t),t}}async function rV(){let e=new Map;for(let t of await be().catch(()=>[]))e.set(t.id,t);return e}async function oV(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 Vd(e,t).catch(()=>{})}async function sV(e,t){let n=await ws(e);return n?Oa(n,t):null}async function aV(e,t,n){let r=await ws(e);if(!r)throw new Error(`no local checkout for ${e}`);return Ma(r,t,{method:n})}async function iV(e,t){let n=await ws(e);if(!n)throw new Error(`no local checkout for ${e}`);return Ac(n,t)}async function cV(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 di(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 lV=10*6e4;async function uV(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>_M){await di(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 di(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await di(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<lV)return;await di(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 di(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 di(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await RM(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function pi(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 dV(e,t){try{return await Zb("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Ll(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 fi=(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 $M(e,t,{grace:n=zK}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=JK}async function pV(e,t,n,r){let o=r.now??Date.now();if(!$M(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||Fe(s))return!1;let a=(s.boxes||[]).find(d=>d.name===s.leadBox);return await(r.midTurn??ns)(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??gr)(e.repo,e.number).catch(()=>null),!1):await(r.claim??gr)(e.repo,e.number).catch(()=>null)?(await pi(s,"opened",{...fi(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 fV(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 Td(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await _d(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<VK;await nT(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await tT(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Ll(a,"pushed",e.number,d=>d.payload?.sha===s)&&await pi(a,"pushed",{...fi(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>Xb)await Ed(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 Ed(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Fl(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await Ll(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await pi(a,"ci-fixed",{...fi(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await Ed(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>Xb)await Id(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Id(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Fl(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await Ll(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await pi(a,"reconciled",{...fi(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await Id(e.repo,e.number).catch(()=>{})}}}}async function mV(e,t,n,r){let o=Fl(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await Ll(s,"closed",e.number)&&await pi(s,"closed",{...fi(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 xM(e,t,n){Fl(n,e,"lost",null);let r=t.get(e.trackId);r&&!await Ll(r,"lost",e.number)&&await pi(r,"lost",fi(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function RM(e,t,n,r,o={}){let s=Fl(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 vM(e={}){let{now:t=Date.now(),auth:n=sw,view:r=tV,list:o=nV,links:s=aT,trackMap:a=rV,emitMerged:i=oV,mark:c=fo,archiveIfArmed:l=vr,notifyIfArmed:u=Sr,notifyIfResolved:d=jp,followupsIfMerged:p=Os,batchThreshold:f=KK,detail:w=sV,merge:m=aV,updateBranch:g=iV,lookup:b=pt,disarm:y=kc,emitDisarmed:x=cV,localHasCommit:S=dV,claim:$=gr,midTurn:M=ns,only:D=null,wantDetail:j=null,covered:A=Jb,verdicts:_=null,tokenFor:L=J=>Xs(J),noteRefusal:G=QA,clearRefusal:O=eT}=e;if(!(await n().catch(()=>null))?.authenticated||Od()>0)return 0;let C=await s().catch(()=>[]),P=RT(C),W=J=>{try{return!!j?.(J)}catch{return!1}},X=D?C.filter(J=>{try{return D(J)}catch{return!1}}):C.filter(J=>t-(Bl.get(ue(J.repo,J.number))||0)>=bM[kM(J,t,A)]);if(!X.length)return 0;let ge=Date.now(),Ie=new Map,Pe=new Map;for(let J of[...X].sort(Tc)){let he=D?"event":kM(J,t,A);Ie.set(ue(J.repo,J.number),{lane:he,intervalMs:bM[he]??null}),Bl.set(ue(J.repo,J.number),t),Pe.has(J.repo)||Pe.set(J.repo,[]),Pe.get(J.repo).push(J)}let ee=await a().catch(()=>new Map),Ze=new Map,Ee=(J,he)=>{let Be=ue(J,he);if(!Ze.has(Be)){let R=Promise.resolve().then(()=>w(J,he));R.catch(()=>{}),Ze.set(Be,R)}return Ze.get(Be)},le=_??((J,he)=>Ef([{path:J,prs:[he]}],{now:t,slugOf:async()=>J,tracks:async()=>[...ee.values()].filter(Be=>!Fe(Be))}).catch(()=>{})),re={now:t,lanes:Ie,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,followupsIfMerged:p,detail:Ee,merge:m,updateBranch:g,lookup:b,disarm:y,emitDisarmed:x,localHasCommit:S,claim:$,midTurn:M};for(let[J,he]of Pe){let Be=XK(he);if(Be.until>t)continue;let R=!1;try{if(await L(J),Be.misses)for(let Z of he)await O(Z.repo,Z.number).catch(()=>{})}catch(Z){R=Gk(Z)}if(R){let Z=Be.misses+1;if(Z>=yM){console.log(`[pr-watch] ${J}: not granted ${Z}\xD7 \u2014 retiring ${he.length} link(s) as lost`);for(let ke of he){await xM(ke,ee,re);let nt=ue(ke.repo,ke.number);jl.delete(nt),Bl.delete(nt)}}else for(let ke of he)await G(ke.repo,ke.number,{misses:Z,until:t+YK}).catch(()=>{});continue}let ie,De=new Set,Ot=Z=>Z.ciFixAwait||Z.reconcileAwait||W(Z)?!0:!Z.autoMerge||Z.trackId&&P.get(Z.trackId)!==ue(Z.repo,Z.number)?!1:t-(Z.autoMerge.armedAt||0)<=_M;if(he.length>f)ie=await o(J).catch(()=>null);else{ie=new Map;for(let Z of he){if(Ot(Z)){let ke=await Ee(J,Z.number).catch(()=>null);if(ke?.state){ie.set(Number(Z.number),{number:Number(Z.number),url:ke.url,title:ke.title,headRefName:ke.headRefName,headRefOid:ke.headSha??null,state:ke.state,mergedAt:ke.mergedAt??null,closedAt:ke.closedAt??null});continue}}try{let ke=await r(J,Z.number);ke?.number!=null&&ie.set(Number(ke.number),ke)}catch(ke){QK(ke)&&De.add(ue(Z.repo,Z.number))}}}if(ie)for(let Z of he){let ke=ue(Z.repo,Z.number),nt=ie.get(Number(Z.number));if(!nt){if(De.has(ke)){let Qe=(jl.get(ke)||0)+1;jl.set(ke,Qe),Qe>=yM&&(await xM(Z,ee,re),jl.delete(ke),Bl.delete(ke))}continue}if(jl.delete(ke),nt.state==="MERGED"||nt.state==="CLOSED")nt.state==="MERGED"?await RM(Z,nt,ee,re):await mV(Z,nt,ee,re),Bl.delete(ke);else{await pV(Z,nt,ee,re).catch(()=>{}),await fV(Z,nt,ee,re).catch(()=>{});let Qe=!Z.trackId||P.get(Z.trackId)===ke;Z.autoMerge&&Qe&&await uV(Z,ee,re).catch(()=>{});try{if(W(Z)&&!Ze.has(ke)&&Ee(Z.repo,Z.number),Ze.has(ke)){let Tt=await Ze.get(ke).catch(()=>null);Tt?.checks&&await le(Z.repo,Tt)}}catch{}}}}let $e={};for(let{lane:J}of Ie.values())$e[J]=($e[J]||0)+1;return console.log(`[pr-watch] probed ${X.length} link(s) (${Object.entries($e).map(([J,he])=>`${J} ${he}`).join(", ")}) in ${Date.now()-ge}ms`),X.length}function SM(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 Yb=!1,_f=!1,If=[],AM=Promise.resolve();async function hV(){for(;_f||If.length;){if(_f){_f=!1;try{await vM()}catch{}continue}let e=If;If=[];let t=e.map(r=>r.spec),n=Object.assign({},...e.map(r=>r.deps||{}));try{await vM({...n,only:r=>t.some(o=>SM(o,r)),wantDetail:r=>t.some(o=>o.detail&&SM(o,r))})}catch{}}}function $f(e,t){return e===null?_f=!0:If.push({spec:e,deps:t}),Yb||(Yb=!0,AM=hV().finally(()=>{Yb=!1})),AM}function OM(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 $f({repo:n,numbers:r,branch:e.branch||null,event:e.event||null,detail:e.detail===!0},t)}var wV=6e4,TM=0;function MM(e=void 0){let t=e?.now??Date.now();return t-TM<wV?Promise.resolve():(TM=t,$f({all:!0},e))}function CM(){$f(null),setInterval(()=>$f(null),EM)}gs();Jd();Wc();Ar();on();var gV=new Set(["opened","reopened","closed","synchronize","ready_for_review","converted_to_draft","edited"]);async function yV({tracks:e=be,reconcile:t=cp}={}){for(let n of(await e().catch(()=>[])).filter(r=>!Fe(r)))await t(n,{bypassThrottle:!0}).catch(()=>{})}function NM(e,t={}){let{invalidate:n=an,probe:r=OM,poke:o=pE,reconcileOpened:s=yV}=t;try{n()}catch{}let a={probe:null,poked:!1,reconciled:!1};if(!e?.repo)return a;if(e.event==="pull_request"&&gV.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();ye();Ar();on();import{randomUUID as bV}from"node:crypto";var kV=2e4,PM=3,mi=new Map,Qb=new Set;async function DM(e,t,n){let r=await n(e).catch(()=>[]),o=null;for(let s of r)s.type==="box.fullscreen.detected"&&s.boxId===t?o=s.payload?.offerId||null:s.type==="box.fullscreen.answered"&&o&&s.payload?.offerId===o&&(o=null);return o}async function xV(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 yn(),i=n??await be()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(Fe(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=(mi.get(d.name)||0)+1;mi.set(d.name,f),f===PM&&(Qb.add(d.name),await DM(u,d.name,o)||await r(u,{type:"box.fullscreen.detected",actor:"system",source:"orchestrator",boxId:d.name,provider:d.agent,payload:{ts:s,offerId:bV(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let f=(mi.get(d.name)||0)>=PM;if(mi.delete(d.name),f||!Qb.has(d.name)){Qb.add(d.name);let w=await DM(u,d.name,o);w&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:d.name,provider:d.agent,payload:{ts:s,offerId:w,action:"cleared"}}).catch(()=>{})}}}for(let u of[...mi.keys()])l.has(u)||mi.delete(u)}var Ul=null,ek=!1;function BM(){return Ul?()=>{}:(Ul=setInterval(()=>{ek||(ek=!0,xV().catch(()=>{}).finally(()=>{ek=!1}))},kV),Ul.unref?.(),()=>{clearInterval(Ul),Ul=null})}ps();Wc();ye();gs();ys();ps();$c();import{randomUUID as vV}from"node:crypto";async function SV(){let e=await Promise.resolve().then(()=>(Ar(),$$));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var tk={listRepoPrs:Vn,readEvents:K,appendEvent:N,sayDeps:SV,currentBranch:go};async function AV(e,t=tk,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??go)(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 nk(e,t,{actions:n,notify:r=!1}={},o=tk){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 AV(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=vV();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 rk(e,{offerId:t,choice:n}={},r=tk){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');return bo(`prx:${e.id}:${t}`,()=>TV(e,{offerId:t,choice:n},r))}async function TV(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=po.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(e.id,_a(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,FA(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}}$c();import jr from"node:fs/promises";import hi from"node:path";import qM from"node:os";import jM from"node:fs/promises";import Rf from"node:path";import{execFile as EV}from"node:child_process";import{promisify as _V}from"node:util";import{fileURLToPath as IV}from"node:url";var $V=_V(EV),FM=Rf.dirname(IV(import.meta.url)),RV=Rf.resolve(FM,"..","..","client","dist"),OV="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",MV=new Map([...OV].map((e,t)=>[e,t]));function CV(e){let t=[],n=0,r=0;for(let o of e){let s=MV.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 NV(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=CV(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 PV(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 LM=new Map;async function DV(e){let t;try{t=await jM.stat(e)}catch{return null}let n=LM.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await jM.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:NV(r.mappings||"")};return LM.set(e,o),o}catch{return null}}var ok=null;function BV(){return ok||(ok=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):$V("git",["rev-parse","--short","HEAD"],{cwd:Rf.resolve(FM,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),ok}var jV=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function LV(e,{distDir:t=RV}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(jV)){let[a,i,c,l]=s,u=await DV(Rf.join(t,"assets",`${i}.map`)),d=u?PV(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 UM(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await BV();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await LV(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var FV=()=>process.env.BOSTRAT_STATE_DIR||hi.join(process.env.HOME||qM.tmpdir(),".bostrat"),Bo=()=>hi.join(FV(),"telemetry"),GM=()=>hi.join(Bo(),".relay-offsets.json"),UV=50,HV=14,WV=3e4,HM=500,Zt=(e,t)=>typeof e=="string"?e.slice(0,t):"";function qV(e,t){if(!e||typeof e!="object")return null;let n=Zt(e.name,64);if(!n)return null;let r=Number.isFinite(e.ts)?e.ts:t,o={name:n,ts:Math.abs(r-t)<168*3600*1e3?r:t,receivedAt:t,anonId:Zt(e.anonId,64),sessionId:Zt(e.sessionId,64),userId:Zt(e.userId,64),release:Zt(e.release,64),appVersion:Zt(e.appVersion,32),route:Zt(e.route,128),deviceClass:e.deviceClass==="phone"||e.deviceClass==="desktop"?e.deviceClass:""};return e.props&&typeof e.props=="object"&&!Array.isArray(e.props)&&(JSON.stringify(e.props).length<=8192?o.props=e.props:o.props={clamped:!0}),e.error&&typeof e.error=="object"&&(o.error={kind:Zt(e.error.kind,256),message:Zt(e.error.message,8192),stack:Zt(e.error.stack,32768),fingerprint:Zt(e.error.fingerprint,64)}),Array.isArray(e.breadcrumbs)&&(o.breadcrumbs=e.breadcrumbs.slice(-50).map(s=>({t:Number.isFinite(s?.t)?s.t:0,n:Zt(s?.n,64),r:Zt(s?.r,128)}))),o}var KM=e=>new Date(e).toISOString().slice(0,10),WM=0;async function VM(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,UV):[]).map(s=>qV(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await UM(s);await jr.mkdir(Bo(),{recursive:!0});let o=hi.join(Bo(),`${KM(t)}.ndjson`);return await jr.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
796
|
-
`).join(""),"utf8"),t-
|
|
797
|
-
`).filter(Boolean),d=[];for(let p of u)try{d.push(JSON.parse(p))}catch{}for(let p=0;p<d.length;p+=HM){let f=d.slice(p,p+HM).map(m=>KV(m)),w=await e(`${t.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.ingestKey}`},body:JSON.stringify({events:f})}).catch(()=>null);if(!w||!w.ok)return{sent:o,disabled:!1};o+=f.length}r[s]=i+l+1,await YM(r)}return{sent:o,disabled:!1}}var sk=null,ak=!1;function XM(){sk||(sk=setInterval(async()=>{if(!ak){ak=!0;try{await VV()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{ak=!1}}},WV),sk.unref?.())}import Cf from"node:fs/promises";import nC from"node:path";import zV from"node:os";var rC=()=>process.env.BOSTRAT_STATE_DIR||nC.join(process.env.HOME||zV.tmpdir(),".bostrat"),oC=()=>nC.join(rC(),"dictation-glossary.json"),JV=100,YV=500,XV=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Nf=null,ik=null,ck=Promise.resolve();function ZM(){let e={},t=Date.now();for(let n of XV)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function sC(){return Nf||(ik??=Cf.readFile(oC(),"utf8").then(e=>JSON.parse(e)?.terms??ZM()).catch(()=>ZM()).then(e=>Nf=e).finally(()=>ik=null),ik)}async function QM(){let e=JSON.stringify({terms:Nf},null,2),t=oC(),n=`${t}.${process.pid}.tmp`;await Cf.mkdir(rC(),{recursive:!0}),await Cf.writeFile(n,e),await Cf.rename(n,t)}function ZV(){return ck=ck.then(QM,QM),ck.catch(()=>{})}async function lk(){return Object.values(await sC()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var QV=e=>Math.max(1,Math.ceil(e.length/4));async function ez(){let e=[],t=0;for(let{term:n}of await lk()){let r=QV(n);if(!(t+r>YV)&&(e.push(n),t+=r,e.length>=JV))break}return e}async function tz(e,t){let n=await sC(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var eC=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Mf=e=>e.toLowerCase();function tC(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function aC(e,t){let n=eC(e),r=eC(t);if(!n.length||!r.length)return[];let o=n.length,s=r.length,a=Array.from({length:o+1},()=>new Array(s+1).fill(0));for(let u=o-1;u>=0;u--)for(let d=s-1;d>=0;d--)a[u][d]=Mf(n[u])===Mf(r[d])?a[u+1][d+1]+1:Math.max(a[u+1][d],a[u][d+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)Mf(n[c])===Mf(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(tC(r[l])&&i.add(r[l]),l++);for(;l<s;l++)tC(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await tz(u,"learned");return await ZV(),[...i]}var Hl=null;function nz(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 iC(){if(!Hl)return null;let e=await bl();if(!e||e.apiKey.slice(-6)!==Hl.keyTail)return null;let{keyTail:t,...n}=Hl;return n}async function cC(e,t){let n=await bl();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 ez())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=nz(o.status,i);c.persistent&&(Hl={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 Hl=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}ye();var uk={"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 rz(e){if(!e)return null;let t=String(e).trim();if(uk[t])return t;let n=null;for(let r of Object.keys(uk))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function oz(e,t={}){let n=uk[rz(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 sz(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 Wl=null;async function az(){if(!Wl)return null;let e=await af();if(!e||e.apiKey.slice(-6)!==Wl.keyTail)return null;let{keyTail:t,...n}=Wl;return n}async function iz(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=sz(t.status,o);s.persistent&&(Wl={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}Wl=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 cz={openai:iz};async function lC(){let e=await af();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await az()}:{configured:!1}}async function uC(){let e=await af();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 cz[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function dC(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=oz(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}}on();zA(async()=>new Set((await be()).map(e=>e.id)));var bC=Re.dirname(uz(import.meta.url)),hk=Number(process.env.PORT)||3362,Pf=Hy(process.env),kC=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,wi=vO(process.env.BOSTRAT_ALLOW_IPS),Df=SO(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Sf),gk=TO(),xC=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",I=Qt();I.disable("x-powered-by");I.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),Pl(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});I.use((e,t,n)=>{if(Nl(e)||wi.allowed(wi.clientIp(e))||Df.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});I.use(OO);I.use(IO({token:gk,enforce:xC}));I.use("/api/mocks",Qt.json({limit:"1mb"}));I.use("/api/mocks",Qt.raw({type:"image/png",limit:"24mb"}));I.use("/api/mocks",Qt.raw({type:["video/mp4","video/webm"],limit:"105mb"}));I.use("/api/runbooks",Qt.json({limit:"1mb"}));I.use("/api/tracks/adopt",Qt.json({limit:"1mb"}));I.use("/api/telemetry",Qt.json({limit:"256kb"}));I.use("/api/hook",Qt.json({limit:"2mb"}));I.use("/api/dictation",Qt.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));I.use("/api/tracks/:id/upload",Qt.raw({type:()=>!0,limit:"25mb"}));I.use(Qt.json({limit:"64kb"}));I.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:qt,hostTmuxAlive:await Jr(),autoTriggerPct:kC,plane:gi?gi.status():null})});I.get("/api/sessions",async(e,t)=>{t.json({sessions:await yn()})});I.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})}});I.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 xe(n))return t.status(404).json({error:"session not found"});try{await Dt(n),t.json({ok:!0})}catch(r){t.status(500).json({error:`failed to kill: ${r?.message||r}`})}});I.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 xe(n);if(!r)return t.status(404).json({error:"session not found"});let o=await Vi(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});I.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 RS(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)})}});I.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 pC=10;async function vC(){let e=await be(),[t,n]=await Promise.all([VI(e),R$(e)]);return{tracks:e,views:await eg(),queuedOf:r=>_s(r),jobsOf:r=>t.get(r)??[],prPendingOf:r=>n.get(r)??[]}}I.get("/api/idle-guard",async(e,t)=>{try{t.json(Uy(await vC()))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var fn=Cs(),hz=q$({kind:fn}),Gl=null,Rt=()=>Gl&&["auto","prompt","off"].includes(Gl)?Gl:hz,wk=null,wz=gl.has(fn),SC=async({to:e,from:t})=>!wz||Qp(await yl())?!1:(Vl.arm({to:e,from:t,kind:fn}),!0);async function gz(e){let t=Rt();Gl=e??null;let n=Rt();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){Vl.cancel();return}wk&&await SC(wk)}}var AC=(()=>{try{return mz(import.meta.url)("../package.json").version||null}catch{return null}})(),Vl=K$({isBusy:async()=>Uy(await vC()).busy,onStuck:({to:e,from:t})=>or({current:t,latest:e,installKind:fn,policy:Rt(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?or({current:t,latest:e,installKind:fn,policy:Rt(),status:"updating",reason:"auto"}):or({current:t,latest:e,installKind:fn,policy:Rt(),status:"failed",error:n?.reason})).catch(()=>{})});I.get("/api/node/self-update",async(e,t)=>{t.json({installKind:fn,policy:Rt(),policySource:Gl?"plane":"node",current:AC,status:await yl(),armed:Vl.armed})});I.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 ef({to:n,kind:fn,reason:"manual"});if(r.ok)return Vl.cancel(),or({current:r.from,latest:n,installKind:fn,policy:Rt(),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 TC(){try{let e=await G$();e&&await or({current:e.from,latest:e.to,installKind:e.kind||fn,policy:Rt(),status:e.status,error:e.error,reason:e.reason}),await ny({current:AC,installKind:fn,policy:Rt()})}catch{}}TC();setInterval(TC,3e4).unref();I.get("/api/tracks",async(e,t)=>{let n=(await be()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>pC){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,pC).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:cl,agentAvailability:await Rp(),tickets:await ER()})});I.get("/api/cost",async(e,t)=>{try{let n=await be();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await _b(n);if(r.cursorUsage=jw(),e.query.format==="text")return t.type("text/plain").send(ZR(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.get("/api/usage/roundup",async(e,t)=>{try{let n=await $b(await be(),{window:e.query.window,from:e.query.from,to:e.query.to});if(e.query.format==="text")return t.type("text/plain").send(rO(n));t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.get("/api/usage/spend",async(e,t)=>{try{let n=await cO(await be(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=jw(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=gc(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.get("/api/usage/caps",(e,t)=>{t.json(gc())});I.post("/api/usage/caps",async(e,t)=>{try{t.json(await gd(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});I.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await gO(await be(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await Ck(e.get(lr),{byToken:ll,legacy:()=>Mc(e.body?.session_id,e.body?.cwd)});if(n==="refused")return xi("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return xi("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&&xi("mismatch",{track:r.track.id,box:r.box.name}),xi(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 JR(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&fu(r.box.cwd),t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});I.get("/api/git/trailers",async(e,t)=>{t.type("text/plain");try{let n=await ll(e.get(lr));if(!n?.track||!n?.box)return t.status(204).end();t.send((await YT(n.track,n.box)).join(`
|
|
794
|
+
`),a.destroy();return}t(s,a,i)}}}function jO(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}Ua();fg();mp();dd();ye();import LO from"node:os";import{randomUUID as Fb}from"node:crypto";var iK=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||LO.hostname(),nodeName:e.BOSTRAT_NODE_NAME||LO.hostname()});var FO=32768,UO=120,HO=1e3,WO=new Set(["claude","codex","cursor"]),cK=new Set(["off","transition","nag"]),lK=[[0,59],[0,23],[1,31],[1,12],[0,6]];function uK(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=lK[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 Nn=e=>String(e??"").trim(),Hs={name(e){let t=Nn(e);if(!t)throw new Error("a loop needs a name");if(t.length>UO)throw new Error(`name is too long (${t.length}); cap is ${UO}`);return t},schedule(e){let t=Nn(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!uK(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=Nn(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>FO)throw new Error(`prompt is too long (${t.length} chars); cap is ${FO}`);return t},expectation(e){let t=Nn(e)||null;if(t&&t.length>HO)throw new Error(`expectation is too long (${t.length}); cap is ${HO}`);return t},notify(e){let t=Nn(e)||"off";if(!cK.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=Nn(typeof t=="string"?t:t?.provider)||"claude";if(!WO.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...WO].join(", ")}`);let r=typeof t=="object"&&t&&Nn(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 dK(e,t){let n=e&&typeof e=="object"?e:{},r=Hs.name(n.name),o=Hs.schedule(n.schedule),s=Hs.prompt(n.prompt),a=Hs.expectation(n.expectation),i=Hs.notify(n.notify),c=Hs.assignment(n.assignment),l=Nn(t?.workspace?.repo)||Nn(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}=iK();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:d||null}}async function GO(e,t){let n=dK(t,e),r=Fb();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 KO(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 Lb=["name","schedule","prompt","expectation","notify","assignment","enabled"],pK=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function VO(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 zO(e,t){let n=Nn(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 fK(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=>!Lb.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: ${Lb.join(", ")}`)}let o={};for(let a of Lb){if(!(a in n))continue;let i=Hs[a](n[a]);(a==="assignment"?pK(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 JO(e,t,n){let r=VO(t),{patch:o,changed:s}=fK(n,r),a=Fb(),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 qO=600;async function YO(e,t,n){let r=Nn(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>qO)throw new Error(`reason is too long (${r.length}); cap is ${qO}`);let o=VO(t),s=Fb(),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 XO=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 ZO(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 XO(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 QO(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 XO(e,"loop.delete.offered",t)).payload,s={id:e.id,cwd:e.cwd};return(n==="delete"||n==="pause")&&await N(s,{type:n==="delete"?"loop.deleted":"loop.paused",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:o.loopId,name:o.name,trackId:o.base?.trackId??null}}),await N(s,{type:"loop.delete.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:o.loopId}}),{proposalId:t,action:n,loopId:o.loopId}}ye();import{randomUUID as mK}from"node:crypto";var Wb=20,hK=300,Ub=120,eM=120,tM=2e3,Af=new Set(["build","ops","debt","spike"]),nM=new Set(["active","done","abandoned"]),qb=new Set(["circuit.create","circuit.update","circuit.delete"]),wK=new Set(["person.create","slot.create","slot.update","slot.delete","reservation.create","reservation.update","reservation.delete"]),Dl=new Set([...qb,...wK]),gK=["name","description","status","type"],yK=["person","startDate","endDate","estUsd"],bK=["person","label","startDate","endDate"],kK=/^\d{4}-\d{2}-\d{2}$/,Zt=e=>String(e??"").trim(),Gb=e=>typeof e=="string"&&e.startsWith("@"),sM=864e5;function aM(e){let t=Date.parse(`${e}T00:00:00Z`),n=new Date(t).getUTCDay();return new Date(t-(n+6)%7*sM).toISOString().slice(0,10)}function xK(e){return new Date(Date.parse(`${aM(e)}T00:00:00Z`)+6*sM).toISOString().slice(0,10)}var rM=(e,t)=>{let n=Zt(e);if(!kK.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 vK(e,t){let n=rM(e,"startDate"),r=rM(t,"endDate");if(r<n)throw new Error(`endDate ${r} is before startDate ${n}`);let o=aM(n),s=xK(r);return{startDate:o,endDate:s,snapped:o!==n||s!==r}}var oM=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},Do=(e,t,n)=>{let r=Zt(e);if(r.length>t)throw new Error(`${n} is too long (${r.length}); cap is ${t}`);return r};function Hb(e,t){let n=Zt(t);if(!n)throw new Error("a circuit name or id is required");if(Gb(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 SK(e,t){let n=Zt(t);if(!n)throw new Error('a person is required \u2014 "me", a person id, or a row label');if(n==="me"||Gb(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 AK(e,t,{allow:n=Dl}={}){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>Wb)throw new Error(`too many ops (${o.length}); cap is ${Wb} \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)},m=x=>{x.id&&(u[x.id]=x.label)},y=(x,S)=>{let $=Zt(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=Zt($.op),D=`op ${S+1}`;if(!Dl.has(M))throw new Error(`${D}: unknown op "${M}" \u2014 one of ${[...Dl].join(", ")}`);if(!n.has(M))throw new Error(`${D}: "${M}" isn't a circuit op \u2014 roadmap changes go through \`bostrat-roadmap propose\` (one card, either CLI)`);let j=O=>{throw new Error(`${D} (${M}): ${O}`)},A=O=>{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(P=>!O.includes(P));return C.length&&j(`unknown field(s) ${C.join(", ")} \u2014 settable: ${O.join(", ")}`),Object.keys(T).length||j("`set` names no fields"),T},_=O=>{let{startDate:T,endDate:C,snapped:P}=vK(O.startDate,O.endDate);return P&&(p=!0),{startDate:T,endDate:C,snapped:P}},L=O=>{let T=SK(a,O);return Zt(O)==="me"&&(f=!0),m(T),{key:T.id??Zt(O),label:T.label}},G=(O,T,C,P,W)=>{let X=Zt(O);X||j(`needs a ${P} id`);let ge=T.find(Ie=>Ie[C]===X);return ge||j(`no ${P} "${X}" \u2014 see \`bostrat-roadmap ls\``),W[X]={...ge},ge};switch(M){case"circuit.create":{let O=Do($.name,eM,"name");O||j("a circuit needs a name");let T=Zt($.type)||"build";Af.has(T)||j(`bad type "${T}" (${[...Af].join(" | ")})`);let C=s.find(W=>String(W.name).toLowerCase()===O.toLowerCase());C&&j(`a circuit named "${C.name}" already exists (${C.id}) \u2014 update it instead of making a twin`);let P=y($.ref,O);return{op:M,...P?{ref:P}:{},name:O,description:Do($.description,tM,"description"),type:T}}case"circuit.update":{let O=Hb(s,$.circuit);O.id||j("can't update a circuit created in the same plan \u2014 set its fields on the create op"),w(O);let T=A(gK),C={};if(T.name!==void 0){let P=Do(T.name,eM,"name");P||j("name can't be blank"),P!==O.name&&(C.name=P)}return T.description!==void 0&&(C.description=Do(T.description,tM,"description")),T.status!==void 0&&(nM.has(T.status)||j(`bad status "${T.status}" (${[...nM].join(" | ")})`),T.status!==O.status&&(C.status=T.status)),T.type!==void 0&&(Af.has(T.type)||j(`bad type "${T.type}" (${[...Af].join(" | ")})`),T.type!==(O.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:O.id,name:O.name,set:C}}case"circuit.delete":{let O=Hb(s,$.circuit);O.id||j("can't delete a circuit created in the same plan"),w(O);let T=o.some((C,P)=>P<S&&Zt(C?.op)==="circuit.update"&&C?.set?.status&&C.set.status!=="active"&&Zt(C?.circuit)===Zt($.circuit));return O.status==="active"&&!T&&j(`\u201C${O.name}\u201D is active \u2014 an active circuit can't be deleted; propose a circuit.update to done/abandoned first (both can ride one card)`),{op:M,circuit:O.id,name:O.name,status:O.status}}case"person.create":{let O=Do($.label,Ub,"label");O||j("a person row needs a label");let T=a.find(P=>String(P.label).toLowerCase()===O.toLowerCase());T&&j(`a roadmap row labelled "${T.label}" already exists (${T.personId})`);let C=y($.ref,O);return{op:M,...C?{ref:C}:{},label:O}}case"slot.create":{let O=Hb(s,$.circuit);w(O);let T=L($.person),C=_($);return{op:M,circuit:O.id??O.ref,circuitName:O.name,person:T.key,personLabel:T.label,...C,estUsd:oM($.estUsd)}}case"slot.update":{let O=G($.slot,i,"slotId","slot",l.slots),T=A(yK),C={};if(T.person!==void 0){let W=L(T.person);C.person=W.key,C.personLabel=W.label}if(T.startDate!==void 0||T.endDate!==void 0){let W=_({startDate:T.startDate??O.startDate,endDate:T.endDate??O.endDate});W.startDate!==O.startDate&&(C.startDate=W.startDate),W.endDate!==O.endDate&&(C.endDate=W.endDate)}T.estUsd!==void 0&&(C.estUsd=oM(T.estUsd)),Object.keys(C).length||j("nothing would change");let P=s.find(W=>W.id===O.circuitId);return{op:M,slot:O.slotId,circuitName:P?.name??null,set:C}}case"slot.delete":{let O=G($.slot,i,"slotId","slot",l.slots),T=s.find(C=>C.id===O.circuitId);return{op:M,slot:O.slotId,circuitName:T?.name??null,startDate:O.startDate,endDate:O.endDate}}case"reservation.create":{let O=L($.person),T=Do($.label,Ub,"label");return T||j("a reservation needs a label (Leave, Support rotation, \u2026)"),{op:M,person:O.key,personLabel:O.label,label:T,..._($)}}case"reservation.update":{let O=G($.reservation,c,"reservationId","reservation",l.reservations),T=A(bK),C={};if(T.person!==void 0){let P=L(T.person);C.person=P.key,C.personLabel=P.label}if(T.label!==void 0){let P=Do(T.label,Ub,"label");P||j("label can't be blank"),P!==O.label&&(C.label=P)}if(T.startDate!==void 0||T.endDate!==void 0){let P=_({startDate:T.startDate??O.startDate,endDate:T.endDate??O.endDate});P.startDate!==O.startDate&&(C.startDate=P.startDate),P.endDate!==O.endDate&&(C.endDate=P.endDate)}return Object.keys(C).length||j("nothing would change"),{op:M,reservation:O.reservationId,label:O.label,set:C}}case"reservation.delete":{let O=G($.reservation,c,"reservationId","reservation",l.reservations);return{op:M,reservation:O.reservationId,label:O.label,startDate:O.startDate,endDate:O.endDate}}default:return j("unhandled op")}});k.forEach((x,S)=>{for(let $ of[x.circuit,x.person,x.set?.person])if(Gb($)&&!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:Do(r.why,hK,"why"),ops:k,base:l,resolved:u,snapped:p,createsMeRow:f&&!g}}async function iM(e,t,n,r={}){let o=AK(t,n,r),s=mK(),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 TK=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 cM(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 TK(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,Wb+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}}Qd();ye();Vr();ye();Kn();mp();var lM=10,uM=200,EK=120,_K=24,pM=/^\d{4}-\d{2}-\d{2}$/;function Kb(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var IK=(e=Date.now())=>new Date(e).toISOString().slice(0,10),St=(e,t="i")=>new RegExp(e,t),$K=[{re:St("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:St("\\bSPC\\b",""),reason:"business (SPC)"},{re:St("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:St("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:St("\\bReplicas\\b",""),reason:"competitor research"},{re:St("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:St("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:St("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],RK=[St("\\bSPC\\b",""),St("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),St("competitor|cloudcli|\\bkiro\\b"),St("\\bReplicas\\b",""),St("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),St("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),St("(^|[^\\w&])#\\d+\\b"),St("\\btrack/[\\w.-]+")];function OK({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of $K)if(o.test(n)||o.test(r))return s;return null}function dM(e){let t=String(e||"");for(let n of RK){let r=n.exec(t);if(r)return r[0].trim()}return null}function fM(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 MK=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function CK(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Vc(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(`
|
|
795
|
+
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,EK)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${_K}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=MK.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=dM(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>uM)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${uM}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=dM(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>lM)throw new Error(`${r.length} bullets \u2014 over the ${lM}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function NK(e,{repos:t=Kb()}={}){let n=new Set(t);return(await Tn()).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:OK({title:r.title,headRef:r.headRef})}))}async function mM(e,t){let n=await NK(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function hM(e,{md:t,day:n=null}){let r=CK(t),o=n==null||n===""?IK():String(n);if(!pM.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 wM(e,{day:t}){let n=String(t||"");if(!pM.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}}gs();Kn();ys();ye();gs();bt();Tr();Kn();xc();zd();Nd();wa();sn();import{execFile as KK}from"node:child_process";import{promisify as VK}from"node:util";var PK=new Set(["pull_request","check_suite","workflow_run","push"]),DK=/^[^/\s]+\/[^/\s]+$/,BK=500,Vb=new Set,zb=[],Tf=new Map,fn={hourStart:0,byEvent:{},duplicates:0,rejected:0};function Jb(e,t,n){let r=Math.floor(e/36e5)*36e5;r!==fn.hourStart&&(fn.hourStart=r,fn.byEvent={},fn.duplicates=0,fn.rejected=0),t==="accepted"?fn.byEvent[n]=(fn.byEvent[n]||0)+1:fn[t]+=1}function jK(e){if(!e||typeof e!="object"||e.forge!=="github"||typeof e.event!="string"||!PK.has(e.event)||typeof e.repo!="string"||!DK.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 gM(e,{now:t=Date.now(),onAccept:n=null,log:r=console}={}){let o=jK(e);if(!o)return Jb(t,"rejected"),{ok:!1,error:"malformed github event envelope"};if(o.deliveryId){if(Vb.has(o.deliveryId))return Jb(t,"duplicates"),{ok:!0,duplicate:!0,envelope:o};for(Vb.add(o.deliveryId),zb.push(o.deliveryId);zb.length>BK;)Vb.delete(zb.shift())}let s=o.repo.toLowerCase(),a=Tf.get(s)||{lastDeliveryAt:0,count:0};a.lastDeliveryAt=t,a.count+=1,Tf.set(s,a),Jb(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 LK(e,t=Date.now()){let n=Tf.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 yM=1440*6e4;function Yb(e,t=Date.now()){let n=LK(e,t);return n.covered&&n.ageMs<yM}function bM(e=Date.now()){let t={};for(let[n,r]of Tf)t[n]={lastDeliveryAt:r.lastDeliveryAt,count:r.count,covered:e-r.lastDeliveryAt<yM};return{hourStart:fn.hourStart,byEvent:{...fn.byEvent},duplicates:fn.duplicates,rejected:fn.rejected,repos:t}}ye();Kn();Hr();Tr();sn();function kM(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function FK(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var UK=new Set(["merged","closed","lost"]);function HK(e,t){if(!e?.blocked)return null;let n=kM(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 WK(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function qK(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 GK(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 Ef(e,t={}){let{now:n=Date.now(),slugOf:r=Oe,lookup:o=pt,note:s=sT,tracks:a=async()=>(await be()).filter(d=>!Fe(d)),asked:i=qK,emitChange:c=GK}=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(!FK(f))continue;let w=await o(p,f.number).catch(()=>null);if(!w?.trackId||UK.has(w.state))continue;let{prev:m}=await s(p,f.number,{blocked:kM(f),sha:f.headSha??null,at:n});if(!m)continue;let y=HK(m,f);if(!y||WK(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,m.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 Qb=VK(KK),$M=3e4,zK=30*6e4,JK=$M,YK=10*6e4,XK=10080*6e4,ZK=60*6e4,xM=8,RM=1440*6e4,QK=3,OM="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Zb=60*6e4,eV=15*6e4,tV=9e4,nV=1440*6e4,Bl=new Map,jl=new Map,rV=360*6e4,oV=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}),sV=/could not resolve|not found|no pull requests?/i,aV=e=>sV.test(String(e?.message||e||""));function iV(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<Zb)}var vM={fast:JK,slow:YK,decay:ZK};function SM(e,t,n=Yb){return MM(e,t,{grace:0})||!n(e.repo,t)&&(e.armedAt&&t-e.armedAt<zK||iV(e,t))?"fast":t-(e.firstSeen||0)>XK?"decay":"slow"}function Fl(e,t,n,r){let o=e.lanes?.get(ue(t.repo,t.number))||{};return Cd({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function cV(e,t){kr("watch");try{let{stdout:n}=await Qb("gh",["pr","view",String(t),"--repo",e,"--json",OM],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Bn({slug:e})}});return JSON.parse(n||"null")}catch(n){throw ho(n)&&wo(n),n}}async function lV(e){kr("watch");try{let{stdout:t}=await Qb("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",OM],{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 ho(t)&&wo(t),t}}async function uV(){let e=new Map;for(let t of await be().catch(()=>[]))e.set(t.id,t);return e}async function dV(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 Vd(e,t).catch(()=>{})}async function pV(e,t){let n=await ws(e);return n?Oa(n,t):null}async function fV(e,t,n){let r=await ws(e);if(!r)throw new Error(`no local checkout for ${e}`);return Ma(r,t,{method:n})}async function mV(e,t){let n=await ws(e);if(!n)throw new Error(`no local checkout for ${e}`);return Ac(n,t)}async function hV(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 di(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 wV=10*6e4;async function gV(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>RM){await di(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 di(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await di(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<wV)return;await di(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 di(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 di(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await CM(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function pi(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 yV(e,t){try{return await Qb("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Ll(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 fi=(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 MM(e,t,{grace:n=tV}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=nV}async function bV(e,t,n,r){let o=r.now??Date.now();if(!MM(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||Fe(s))return!1;let a=(s.boxes||[]).find(d=>d.name===s.leadBox);return await(r.midTurn??ns)(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??yr)(e.repo,e.number).catch(()=>null),!1):await(r.claim??yr)(e.repo,e.number).catch(()=>null)?(await pi(s,"opened",{...fi(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 kV(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 Td(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await _d(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<eV;await oT(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await rT(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Ll(a,"pushed",e.number,d=>d.payload?.sha===s)&&await pi(a,"pushed",{...fi(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>Zb)await Ed(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 Ed(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Fl(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await Ll(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await pi(a,"ci-fixed",{...fi(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await Ed(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>Zb)await Id(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Id(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Fl(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await Ll(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await pi(a,"reconciled",{...fi(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await Id(e.repo,e.number).catch(()=>{})}}}}async function xV(e,t,n,r){let o=Fl(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await Ll(s,"closed",e.number)&&await pi(s,"closed",{...fi(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 AM(e,t,n){Fl(n,e,"lost",null);let r=t.get(e.trackId);r&&!await Ll(r,"lost",e.number)&&await pi(r,"lost",fi(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function CM(e,t,n,r,o={}){let s=Fl(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 TM(e={}){let{now:t=Date.now(),auth:n=ow,view:r=cV,list:o=lV,links:s=cT,trackMap:a=uV,emitMerged:i=dV,mark:c=fo,archiveIfArmed:l=Sr,notifyIfArmed:u=Ar,notifyIfResolved:d=jp,followupsIfMerged:p=Os,batchThreshold:f=QK,detail:w=pV,merge:m=fV,updateBranch:y=mV,lookup:k=pt,disarm:g=kc,emitDisarmed:x=hV,localHasCommit:S=yV,claim:$=yr,midTurn:M=ns,only:D=null,wantDetail:j=null,covered:A=Yb,verdicts:_=null,tokenFor:L=J=>Xs(J),noteRefusal:G=tT,clearRefusal:O=nT}=e;if(!(await n().catch(()=>null))?.authenticated||Od()>0)return 0;let C=await s().catch(()=>[]),P=MT(C),W=J=>{try{return!!j?.(J)}catch{return!1}},X=D?C.filter(J=>{try{return D(J)}catch{return!1}}):C.filter(J=>t-(Bl.get(ue(J.repo,J.number))||0)>=vM[SM(J,t,A)]);if(!X.length)return 0;let ge=Date.now(),Ie=new Map,Ce=new Map;for(let J of[...X].sort(Tc)){let he=D?"event":SM(J,t,A);Ie.set(ue(J.repo,J.number),{lane:he,intervalMs:vM[he]??null}),Bl.set(ue(J.repo,J.number),t),Ce.has(J.repo)||Ce.set(J.repo,[]),Ce.get(J.repo).push(J)}let Z=await a().catch(()=>new Map),Ze=new Map,Ee=(J,he)=>{let Be=ue(J,he);if(!Ze.has(Be)){let R=Promise.resolve().then(()=>w(J,he));R.catch(()=>{}),Ze.set(Be,R)}return Ze.get(Be)},le=_??((J,he)=>Ef([{path:J,prs:[he]}],{now:t,slugOf:async()=>J,tracks:async()=>[...Z.values()].filter(Be=>!Fe(Be))}).catch(()=>{})),re={now:t,lanes:Ie,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,followupsIfMerged:p,detail:Ee,merge:m,updateBranch:y,lookup:k,disarm:g,emitDisarmed:x,localHasCommit:S,claim:$,midTurn:M};for(let[J,he]of Ce){let Be=oV(he);if(Be.until>t)continue;let R=!1;try{if(await L(J),Be.misses)for(let Q of he)await O(Q.repo,Q.number).catch(()=>{})}catch(Q){R=Kk(Q)}if(R){let Q=Be.misses+1;if(Q>=xM){console.log(`[pr-watch] ${J}: not granted ${Q}\xD7 \u2014 retiring ${he.length} link(s) as lost`);for(let ke of he){await AM(ke,Z,re);let nt=ue(ke.repo,ke.number);jl.delete(nt),Bl.delete(nt)}}else for(let ke of he)await G(ke.repo,ke.number,{misses:Q,until:t+rV}).catch(()=>{});continue}let ie,De=new Set,Ot=Q=>Q.ciFixAwait||Q.reconcileAwait||W(Q)?!0:!Q.autoMerge||Q.trackId&&P.get(Q.trackId)!==ue(Q.repo,Q.number)?!1:t-(Q.autoMerge.armedAt||0)<=RM;if(he.length>f)ie=await o(J).catch(()=>null);else{ie=new Map;for(let Q of he){if(Ot(Q)){let ke=await Ee(J,Q.number).catch(()=>null);if(ke?.state){ie.set(Number(Q.number),{number:Number(Q.number),url:ke.url,title:ke.title,headRefName:ke.headRefName,headRefOid:ke.headSha??null,state:ke.state,mergedAt:ke.mergedAt??null,closedAt:ke.closedAt??null});continue}}try{let ke=await r(J,Q.number);ke?.number!=null&&ie.set(Number(ke.number),ke)}catch(ke){aV(ke)&&De.add(ue(Q.repo,Q.number))}}}if(ie)for(let Q of he){let ke=ue(Q.repo,Q.number),nt=ie.get(Number(Q.number));if(!nt){if(De.has(ke)){let Qe=(jl.get(ke)||0)+1;jl.set(ke,Qe),Qe>=xM&&(await AM(Q,Z,re),jl.delete(ke),Bl.delete(ke))}continue}if(jl.delete(ke),nt.state==="MERGED"||nt.state==="CLOSED")nt.state==="MERGED"?await CM(Q,nt,Z,re):await xV(Q,nt,Z,re),Bl.delete(ke);else{await bV(Q,nt,Z,re).catch(()=>{}),await kV(Q,nt,Z,re).catch(()=>{});let Qe=!Q.trackId||P.get(Q.trackId)===ke;Q.autoMerge&&Qe&&await gV(Q,Z,re).catch(()=>{});try{if(W(Q)&&!Ze.has(ke)&&Ee(Q.repo,Q.number),Ze.has(ke)){let Tt=await Ze.get(ke).catch(()=>null);Tt?.checks&&await le(Q.repo,Tt)}}catch{}}}}let $e={};for(let{lane:J}of Ie.values())$e[J]=($e[J]||0)+1;return console.log(`[pr-watch] probed ${X.length} link(s) (${Object.entries($e).map(([J,he])=>`${J} ${he}`).join(", ")}) in ${Date.now()-ge}ms`),X.length}function EM(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 Xb=!1,_f=!1,If=[],_M=Promise.resolve();async function vV(){for(;_f||If.length;){if(_f){_f=!1;try{await TM()}catch{}continue}let e=If;If=[];let t=e.map(r=>r.spec),n=Object.assign({},...e.map(r=>r.deps||{}));try{await TM({...n,only:r=>t.some(o=>EM(o,r)),wantDetail:r=>t.some(o=>o.detail&&EM(o,r))})}catch{}}}function $f(e,t){return e===null?_f=!0:If.push({spec:e,deps:t}),Xb||(Xb=!0,_M=vV().finally(()=>{Xb=!1})),_M}function NM(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 $f({repo:n,numbers:r,branch:e.branch||null,event:e.event||null,detail:e.detail===!0},t)}var SV=6e4,IM=0;function PM(e=void 0){let t=e?.now??Date.now();return t-IM<SV?Promise.resolve():(IM=t,$f({all:!0},e))}function DM(){$f(null),setInterval(()=>$f(null),$M)}gs();Jd();Wc();Tr();sn();var AV=new Set(["opened","reopened","closed","synchronize","ready_for_review","converted_to_draft","edited"]);async function TV({tracks:e=be,reconcile:t=cp}={}){for(let n of(await e().catch(()=>[])).filter(r=>!Fe(r)))await t(n,{bypassThrottle:!0}).catch(()=>{})}function BM(e,t={}){let{invalidate:n=cn,probe:r=NM,poke:o=mE,reconcileOpened:s=TV}=t;try{n()}catch{}let a={probe:null,poked:!1,reconciled:!1};if(!e?.repo)return a;if(e.event==="pull_request"&&AV.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();ye();Tr();sn();import{randomUUID as EV}from"node:crypto";var _V=2e4,jM=3,mi=new Map,ek=new Set;async function LM(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 IV(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 bn(),i=n??await be()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(Fe(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=(mi.get(d.name)||0)+1;mi.set(d.name,f),f===jM&&(ek.add(d.name),await LM(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:EV(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let f=(mi.get(d.name)||0)>=jM;if(mi.delete(d.name),f||!ek.has(d.name)){ek.add(d.name);let w=await LM(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[...mi.keys()])l.has(u)||mi.delete(u)}var Ul=null,tk=!1;function FM(){return Ul?()=>{}:(Ul=setInterval(()=>{tk||(tk=!0,IV().catch(()=>{}).finally(()=>{tk=!1}))},_V),Ul.unref?.(),()=>{clearInterval(Ul),Ul=null})}ps();Wc();ye();gs();ys();ps();$c();import{randomUUID as $V}from"node:crypto";async function RV(){let e=await Promise.resolve().then(()=>(Tr(),M$));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var nk={listRepoPrs:Vn,readEvents:K,appendEvent:N,sayDeps:RV,currentBranch:go};async function OV(e,t=nk,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??go)(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 rk(e,t,{actions:n,notify:r=!1}={},o=nk){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 OV(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=$V();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 ok(e,{offerId:t,choice:n}={},r=nk){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');return bo(`prx:${e.id}:${t}`,()=>MV(e,{offerId:t,choice:n},r))}async function MV(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=po.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(e.id,_a(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,HA(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}}$c();import jr from"node:fs/promises";import hi from"node:path";import VM from"node:os";import UM from"node:fs/promises";import Rf from"node:path";import{execFile as CV}from"node:child_process";import{promisify as NV}from"node:util";import{fileURLToPath as PV}from"node:url";var DV=NV(CV),WM=Rf.dirname(PV(import.meta.url)),BV=Rf.resolve(WM,"..","..","client","dist"),jV="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",LV=new Map([...jV].map((e,t)=>[e,t]));function FV(e){let t=[],n=0,r=0;for(let o of e){let s=LV.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 UV(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=FV(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 HV(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 HM=new Map;async function WV(e){let t;try{t=await UM.stat(e)}catch{return null}let n=HM.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await UM.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:UV(r.mappings||"")};return HM.set(e,o),o}catch{return null}}var sk=null;function qV(){return sk||(sk=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):DV("git",["rev-parse","--short","HEAD"],{cwd:Rf.resolve(WM,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),sk}var GV=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function KV(e,{distDir:t=BV}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(GV)){let[a,i,c,l]=s,u=await WV(Rf.join(t,"assets",`${i}.map`)),d=u?HV(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 qM(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await qV();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await KV(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var VV=()=>process.env.BOSTRAT_STATE_DIR||hi.join(process.env.HOME||VM.tmpdir(),".bostrat"),Bo=()=>hi.join(VV(),"telemetry"),zM=()=>hi.join(Bo(),".relay-offsets.json"),zV=50,JV=14,YV=3e4,GM=500,Qt=(e,t)=>typeof e=="string"?e.slice(0,t):"";function XV(e,t){if(!e||typeof e!="object")return null;let n=Qt(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:Qt(e.anonId,64),sessionId:Qt(e.sessionId,64),userId:Qt(e.userId,64),release:Qt(e.release,64),appVersion:Qt(e.appVersion,32),route:Qt(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:Qt(e.error.kind,256),message:Qt(e.error.message,8192),stack:Qt(e.error.stack,32768),fingerprint:Qt(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:Qt(s?.n,64),r:Qt(s?.r,128)}))),o}var JM=e=>new Date(e).toISOString().slice(0,10),KM=0;async function YM(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,zV):[]).map(s=>XV(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await qM(s);await jr.mkdir(Bo(),{recursive:!0});let o=hi.join(Bo(),`${JM(t)}.ndjson`);return await jr.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
796
|
+
`).join(""),"utf8"),t-KM>6*3600*1e3&&(KM=t,ZV(t).catch(()=>{})),r.length}async function ZV(e=Date.now()){let t=JM(e-JV*24*3600*1e3),n=[];try{n=await jr.readdir(Bo())}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 jr.rm(hi.join(Bo(),s),{force:!0});let o=await ZM();for(let s of r)delete o[s];await QM(o)}var Of=new Map;function XM(e,t=Date.now()){let n=e||"unknown",r=Of.get(n);return r||(Of.size>512&&Of.clear(),r={tokens:40,at:t},Of.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 ZM(){try{return JSON.parse(await jr.readFile(zM(),"utf8"))||{}}catch{return{}}}async function QM(e){await jr.mkdir(Bo(),{recursive:!0}),await jr.writeFile(zM(),JSON.stringify(e),"utf8")}function QV(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||VM.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 ez({fetchImpl:e=fetch}={}){let t=await iR();if(!t)return{sent:0,disabled:!0};let n;try{n=(await jr.readdir(Bo())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await ZM(),o=0;for(let s of n){let a=hi.join(Bo(),s),i=r[s]||0,c;try{let p=await jr.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(`
|
|
797
|
+
`).filter(Boolean),d=[];for(let p of u)try{d.push(JSON.parse(p))}catch{}for(let p=0;p<d.length;p+=GM){let f=d.slice(p,p+GM).map(m=>QV(m)),w=await e(`${t.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.ingestKey}`},body:JSON.stringify({events:f})}).catch(()=>null);if(!w||!w.ok)return{sent:o,disabled:!1};o+=f.length}r[s]=i+l+1,await QM(r)}return{sent:o,disabled:!1}}var ak=null,ik=!1;function eC(){ak||(ak=setInterval(async()=>{if(!ik){ik=!0;try{await ez()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{ik=!1}}},YV),ak.unref?.())}import Cf from"node:fs/promises";import sC from"node:path";import tz from"node:os";var aC=()=>process.env.BOSTRAT_STATE_DIR||sC.join(process.env.HOME||tz.tmpdir(),".bostrat"),iC=()=>sC.join(aC(),"dictation-glossary.json"),nz=100,rz=500,oz=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Nf=null,ck=null,lk=Promise.resolve();function tC(){let e={},t=Date.now();for(let n of oz)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function cC(){return Nf||(ck??=Cf.readFile(iC(),"utf8").then(e=>JSON.parse(e)?.terms??tC()).catch(()=>tC()).then(e=>Nf=e).finally(()=>ck=null),ck)}async function nC(){let e=JSON.stringify({terms:Nf},null,2),t=iC(),n=`${t}.${process.pid}.tmp`;await Cf.mkdir(aC(),{recursive:!0}),await Cf.writeFile(n,e),await Cf.rename(n,t)}function sz(){return lk=lk.then(nC,nC),lk.catch(()=>{})}async function uk(){return Object.values(await cC()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var az=e=>Math.max(1,Math.ceil(e.length/4));async function iz(){let e=[],t=0;for(let{term:n}of await uk()){let r=az(n);if(!(t+r>rz)&&(e.push(n),t+=r,e.length>=nz))break}return e}async function cz(e,t){let n=await cC(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var rC=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Mf=e=>e.toLowerCase();function oC(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function lC(e,t){let n=rC(e),r=rC(t);if(!n.length||!r.length)return[];let o=n.length,s=r.length,a=Array.from({length:o+1},()=>new Array(s+1).fill(0));for(let u=o-1;u>=0;u--)for(let d=s-1;d>=0;d--)a[u][d]=Mf(n[u])===Mf(r[d])?a[u+1][d+1]+1:Math.max(a[u+1][d],a[u][d+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)Mf(n[c])===Mf(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(oC(r[l])&&i.add(r[l]),l++);for(;l<s;l++)oC(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await cz(u,"learned");return await sz(),[...i]}var Hl=null;function lz(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 uC(){if(!Hl)return null;let e=await bl();if(!e||e.apiKey.slice(-6)!==Hl.keyTail)return null;let{keyTail:t,...n}=Hl;return n}async function dC(e,t){let n=await bl();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 iz())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=lz(o.status,i);c.persistent&&(Hl={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 Hl=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}ye();var dk={"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 uz(e){if(!e)return null;let t=String(e).trim();if(dk[t])return t;let n=null;for(let r of Object.keys(dk))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function dz(e,t={}){let n=dk[uz(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 pz(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 Wl=null;async function fz(){if(!Wl)return null;let e=await af();if(!e||e.apiKey.slice(-6)!==Wl.keyTail)return null;let{keyTail:t,...n}=Wl;return n}async function mz(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=pz(t.status,o);s.persistent&&(Wl={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}Wl=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 hz={openai:mz};async function pC(){let e=await af();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await fz()}:{configured:!1}}async function fC(){let e=await af();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 hz[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function mC(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=dz(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}}sn();YA(async()=>new Set((await be()).map(e=>e.id)));var vC=Re.dirname(gz(import.meta.url)),wk=Number(process.env.PORT)||3362,Pf=Wy(process.env),SC=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,wi=TO(process.env.BOSTRAT_ALLOW_IPS),Df=EO(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Sf),yk=IO(),AC=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",I=en();I.disable("x-powered-by");I.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),Pl(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});I.use((e,t,n)=>{if(Nl(e)||wi.allowed(wi.clientIp(e))||Df.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});I.use(NO);I.use(OO({token:yk,enforce:AC}));I.use("/api/mocks",en.json({limit:"1mb"}));I.use("/api/mocks",en.raw({type:"image/png",limit:"24mb"}));I.use("/api/mocks",en.raw({type:["video/mp4","video/webm"],limit:"105mb"}));I.use("/api/runbooks",en.json({limit:"1mb"}));I.use("/api/tracks/adopt",en.json({limit:"1mb"}));I.use("/api/telemetry",en.json({limit:"256kb"}));I.use("/api/hook",en.json({limit:"2mb"}));I.use("/api/dictation",en.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));I.use("/api/tracks/:id/upload",en.raw({type:()=>!0,limit:"25mb"}));I.use(en.json({limit:"64kb"}));I.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:qt,hostTmuxAlive:await Jr(),autoTriggerPct:SC,plane:gi?gi.status():null})});I.get("/api/sessions",async(e,t)=>{t.json({sessions:await bn()})});I.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 Ln(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})}});I.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 xe(n))return t.status(404).json({error:"session not found"});try{await Dt(n),t.json({ok:!0})}catch(r){t.status(500).json({error:`failed to kill: ${r?.message||r}`})}});I.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 xe(n);if(!r)return t.status(404).json({error:"session not found"});let o=await Vi(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});I.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 MS(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)})}});I.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 hC=10;async function TC(){let e=await be(),[t,n]=await Promise.all([JI(e),C$(e)]);return{tracks:e,views:await Qw(),queuedOf:r=>_s(r),jobsOf:r=>t.get(r)??[],prPendingOf:r=>n.get(r)??[]}}I.get("/api/idle-guard",async(e,t)=>{try{t.json(Hy(await TC()))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var mn=Cs(),vz=V$({kind:mn}),Gl=null,Rt=()=>Gl&&["auto","prompt","off"].includes(Gl)?Gl:vz,gk=null,Sz=gl.has(mn),EC=async({to:e,from:t})=>!Sz||Qp(await yl())?!1:(Vl.arm({to:e,from:t,kind:mn}),!0);async function Az(e){let t=Rt();Gl=e??null;let n=Rt();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){Vl.cancel();return}gk&&await EC(gk)}}var _C=(()=>{try{return xz(import.meta.url)("../package.json").version||null}catch{return null}})(),Vl=J$({isBusy:async()=>Hy(await TC()).busy,onStuck:({to:e,from:t})=>sr({current:t,latest:e,installKind:mn,policy:Rt(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?sr({current:t,latest:e,installKind:mn,policy:Rt(),status:"updating",reason:"auto"}):sr({current:t,latest:e,installKind:mn,policy:Rt(),status:"failed",error:n?.reason})).catch(()=>{})});I.get("/api/node/self-update",async(e,t)=>{t.json({installKind:mn,policy:Rt(),policySource:Gl?"plane":"node",current:_C,status:await yl(),armed:Vl.armed})});I.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 ef({to:n,kind:mn,reason:"manual"});if(r.ok)return Vl.cancel(),sr({current:r.from,latest:n,installKind:mn,policy:Rt(),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 IC(){try{let e=await z$();e&&await sr({current:e.from,latest:e.to,installKind:e.kind||mn,policy:Rt(),status:e.status,error:e.error,reason:e.reason}),await ny({current:_C,installKind:mn,policy:Rt()})}catch{}}IC();setInterval(IC,3e4).unref();I.get("/api/tracks",async(e,t)=>{let n=(await be()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>hC){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,hC).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:cl,agentAvailability:await Rp(),tickets:await $R()})});I.get("/api/cost",async(e,t)=>{try{let n=await be();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await Ib(n);if(r.cursorUsage=Bw(),e.query.format==="text")return t.type("text/plain").send(tO(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.get("/api/usage/roundup",async(e,t)=>{try{let n=await Rb(await be(),{window:e.query.window,from:e.query.from,to:e.query.to});if(e.query.format==="text")return t.type("text/plain").send(aO(n));t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.get("/api/usage/spend",async(e,t)=>{try{let n=await dO(await be(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=Bw(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=gc(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.get("/api/usage/caps",(e,t)=>{t.json(gc())});I.post("/api/usage/caps",async(e,t)=>{try{t.json(await gd(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});I.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await kO(await be(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await Nk(e.get(ur),{byToken:ll,legacy:()=>Mc(e.body?.session_id,e.body?.cwd)});if(n==="refused")return xi("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return xi("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&&xi("mismatch",{track:r.track.id,box:r.box.name}),xi(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 ZR(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&fu(r.box.cwd),t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});I.get("/api/git/trailers",async(e,t)=>{t.type("text/plain");try{let n=await ll(e.get(ur));if(!n?.track||!n?.box)return t.status(204).end();t.send((await ZT(n.track,n.box)).join(`
|
|
798
798
|
`)+`
|
|
799
|
-
`)}catch{t.status(204).end()}});I.post("/api/git/committed",async(e,t)=>{try{let n=await ll(e.get(lr));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;XT(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)})}});I.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 QT(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})}});I.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 eE(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})}});I.get("/api/usage/rework",async(e,t)=>{try{t.json(await lO(await be(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.post("/api/telemetry",async(e,t)=>{try{if(!zM(wi.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await VM(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});I.get("/api/repos",async(e,t)=>{try{let n=await ur(),r=new Set((await Promise.all(n.map(a=>Oe(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await Ii()).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)})}});I.get("/api/github/token",async(e,t)=>{try{t.json(await Xs(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});I.get("/api/github/status",(e,t)=>{t.json({configured:ou(),calls:wT(),latency:kT(),webhook:wM()})});I.post("/api/github/event",Qt.json({limit:"64kb"}),(e,t)=>{let n=mM(e.body,{onAccept:r=>NM(r)});if(!n.ok)return t.status(400).json({error:n.error});t.json({ok:!0,duplicate:n.duplicate})});I.post("/api/github/refresh",(e,t)=>{Kk(),t.json({ok:!0})});I.get("/api/gmail/search",async(e,t)=>{try{let n=await bO(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)})}});I.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await kO(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});async function Kl({repo:e=null,number:t=null,branch:n=null}){let r=e?await Oe(e):null;if(r&&t!=null){let s=await pt(r,t),a=s&&await me(s.trackId);if(a)return a}if(!n)return null;let o=await be();return Ld(o,n,s=>Fd(s,{branch:n,repo:e,slug:r}))}async function EC({repo:e=null,number:t=null,branch:n=null}){let r=await Kl({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!Fe(r))return r;if(!n)return null;let o=e?await Oe(e).catch(()=>null):null,s=await be();return Ld(s,n,a=>!Fe(a)&&Fd(a,{branch:n,repo:e,slug:o}))}async function Nn(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Kl({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await Oe(s).catch(()=>null):null);await N({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:e,number:t,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function yz(e){let t=(await be()).filter(n=>!Fe(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await Oe(n.path);if(r)for(let o of n.prs){if(await pt(r,o.number))continue;let s=await Ld(t,o.headRefName,a=>Fd(a,{branch:o.headRefName,repo:n.path,slug:r}));s&&await Gt({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}I.get("/api/git/prs",async(e,t)=>{try{let n=await _T();await yz(n.repos).catch(()=>{}),await Ef(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.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 y=o||"merge";if(!Sc.has(y))throw new Error(`invalid merge method: ${y}`);let x=Number(r);if(!Number.isInteger(x)||x<=0)throw new Error("invalid PR number");let S=await Oe((await kr(n)).path);if(!S)throw new Error("repo has no GitHub remote");let $=await Kl({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 Ac(n,x),await Gt({repo:S,number:x,trackId:$.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await bc(S,x,{merge:{method:y}}))throw new Error("couldn't record the auto-merge arm");return await fs(S,x,i===!0),await ms(S,x,c===!0&&i!==!0),an(),await Nn("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 Kl({repo:n,number:r,branch:l}).catch(()=>null),p&&(f=await bp(p,"merge",{count:1,summary:`merging PR #${r}`}));let m=await Ma(n,r,{method:o||"squash"});await Nn("merged",{number:m.number,url:d,title:u,branch:l,repo:n});let g=await Oe(n).catch(()=>null),b=!1;if(g&&(i===!0&&await fs(g,m.number,!0).catch(()=>{}),c===!0&&i!==!0&&await ms(g,m.number,!0).catch(()=>{}),await Sr(g,m.number).catch(()=>{}),await Os(g,m.number).catch(()=>{}),b=await vr(g,m.number).catch(()=>!1),await fo(g,m.number,"merged").catch(()=>{})),i===!0&&!b){let y=await Kl({repo:n,number:m.number,branch:l}).catch(()=>null);y&&!y.archivedAt&&await Rs(y.id).catch(()=>{})}t.json(m)}catch(m){w=m;let g=m?.message||String(m),b=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(g)?400:502;t.status(b).json({error:g})}finally{p&&f&&await Lg(p,f,w?"failed":"completed",w).catch(()=>{})}});I.post("/api/git/prs/disarm-auto",async(e,t)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=e.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await Oe((await kr(n)).path).catch(()=>null),l=c?await kc(c,i).catch(()=>null):null;try{await cw(n,i)}catch(u){if(!l)throw u}an(),await Nn("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})}});I.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await Oe(n).catch(()=>null),s=await MT(n,{method:r||"merge",onMerged:a=>{Nn("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&Sr(o,a.number).catch(()=>{}).then(()=>Os(o,a.number)).catch(()=>{}).then(()=>vr(o,a.number)).catch(()=>{}).then(()=>fo(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})}});I.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:OT()})});I.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(!Sc.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 An()).filter(m=>m.trackId===d.id&&m.state!=="merged"&&m.state!=="closed").sort(Tc);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 m of p)await fs(m.repo,m.number,o===!0).catch(()=>{}),await ms(m.repo,m.number,s===!0&&o!==!0).catch(()=>{});let f=[],w=[];for(let m of p){let g={repo:m.repo,number:m.number,ok:!0,action:"armed"};f.push(g);try{let b=await ws(m.repo);if(!b)throw new Error(`no local checkout for ${m.repo}`);let y=await Oa(b,m.number);w.push({r:g,l:m,repoPath:b,pr:y,plan:IT(y,{update:r!==!1})})}catch(b){g.ok=!1,g.action="failed",g.reason=b?.message||String(b)}}for(let{r:m,l:g,repoPath:b,pr:y,plan:x}of $T(w))try{if(x.action==="skip")m.action="skipped",m.reason=x.reason;else if(x.action==="merge")await Ma(b,g.number,{method:u}),await Nn("merged",{number:g.number,url:y.url,title:y.title,branch:y.headRefName,repo:b,track:d}),await Sr(g.repo,g.number).catch(()=>{}),await Os(g.repo,g.number).catch(()=>{}),await vr(g.repo,g.number).catch(()=>{}),await fo(g.repo,g.number,"merged").catch(()=>{}),m.action="merged";else{if(x.action==="update-arm"&&(await Ac(b,g.number).catch(()=>{}),m.action="updated-armed"),!await bc(g.repo,g.number,{merge:{method:u}}))throw new Error("couldn't record the auto-merge arm");await Nn("auto-armed",{number:g.number,url:y.url,title:y.title,branch:y.headRefName,repo:b,track:d,...m.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(S){m.ok=!1,m.action="failed",m.reason=S?.message||String(S)}if(an(),!f.some(m=>m.ok)){if(o===!0)for(let m of p)await fs(m.repo,m.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let m of p)await ms(m.repo,m.number,!1).catch(()=>{});return t.status(502).json({error:f.map(m=>`${m.repo}#${m.number}: ${m.reason}`).join("; "),results:f})}t.json({ok:!0,trackId:d.id,results:f})}catch(u){l=u;let d=u?.message||String(u),p=/invalid|required/i.test(d)?400:502;t.status(p).json({error:d})}finally{i&&c&&await Lg(i,c,l?"failed":"completed",l).catch(()=>{})}});I.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 An()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await kc(a.repo,a.number).catch(()=>null),c=await ws(a.repo).catch(()=>null);c&&await cw(c,a.number).catch(()=>{}),i&&(await Nn("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}))}an(),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})}});I.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=Ut(i.workspace),d,p=Array.isArray(a)?a.filter(Boolean):[];if(p.length)d=p.map(m=>{let g=u.find(b=>b.name===m||b.repo===m||b.base===m||b.dir===m);if(!g)throw new Error(`invalid repo "${m}" \u2014 not tagged on this Track`);return g});else if(d=await vs(i),!d.length)return t.status(409).json({error:"no tagged repo has work to ship"});let f=[];for(let m of d)try{let g=await uw(m.dir,{title:r,body:o,base:s}),b=m.repo||await Oe(m.dir).catch(()=>null);b&&g.number&&await Gt({repo:b,number:g.number,trackId:i.id,url:g.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Nn("opened",{number:g.number,url:g.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:b??m.name}}),f.push({repo:m.name,slug:b??null,ok:!0,url:g.url,number:g.number})}catch(g){f.push({repo:m.name,slug:m.repo??null,ok:!1,error:g?.message||String(g)})}let w=f.filter(m=>m.ok);return w.length?t.json({ok:!0,multi:!0,results:f,url:w[0].url,number:w[0].number}):t.status(502).json({error:f.map(m=>`${m.repo}: ${m.error}`).join("; "),results:f})}let c=await uw(i.cwd,{title:r,body:o,base:s}),l=await Oe(i.cwd).catch(()=>null);l&&c.number&&await Gt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Nn("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})}});I.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 vs(n),o=new Map(r.map(a=>[a.base,a])),s=Ut(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)})}});I.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 Pp(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})}});I.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await Dp(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})}});I.post("/api/tracks/:id/reshape",async(e,t)=>{let{repos:n,skipPermissions:r}=e.body||{};try{let o=await gy(e.params.id,n,{skipPermissions:r!==!1});t.status(o.finishedAt?200:202).json({ok:!0,...yk(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})}});I.get("/api/tracks/:id/reshape",(e,t)=>{t.json(yk(dl(e.params.id))??{running:!1})});function yk(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}I.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Oa(n,r),s=await Oe(n).catch(()=>null),a=await EC({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=Ut(a.workspace).find(b=>b.base===n||s&&b.repo===s)??null,c=a.workspace?.kind==="multi",l=await CT(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 At.promises.mkdir(d,{recursive:!0}),await At.promises.writeFile(Re.join(a.cwd,p),l.map(b=>`\u2550\u2550\u2550\u2550 ${b.name}${b.link?` \xB7 ${b.link}`:""} \u2550\u2550\u2550\u2550
|
|
800
|
-
${
|
|
799
|
+
`)}catch{t.status(204).end()}});I.post("/api/git/committed",async(e,t)=>{try{let n=await ll(e.get(ur));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;QT(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)})}});I.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 tE(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})}});I.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 nE(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})}});I.get("/api/usage/rework",async(e,t)=>{try{t.json(await pO(await be(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.post("/api/telemetry",async(e,t)=>{try{if(!XM(wi.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await YM(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});I.get("/api/repos",async(e,t)=>{try{let n=await dr(),r=new Set((await Promise.all(n.map(a=>Oe(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await Ii()).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)})}});I.get("/api/github/token",async(e,t)=>{try{t.json(await Xs(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});I.get("/api/github/status",(e,t)=>{t.json({configured:ou(),calls:yT(),latency:vT(),webhook:bM()})});I.post("/api/github/event",en.json({limit:"64kb"}),(e,t)=>{let n=gM(e.body,{onAccept:r=>BM(r)});if(!n.ok)return t.status(400).json({error:n.error});t.json({ok:!0,duplicate:n.duplicate})});I.post("/api/github/refresh",(e,t)=>{Vk(),t.json({ok:!0})});I.get("/api/gmail/search",async(e,t)=>{try{let n=await vO(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)})}});I.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await SO(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});async function Kl({repo:e=null,number:t=null,branch:n=null}){let r=e?await Oe(e):null;if(r&&t!=null){let s=await pt(r,t),a=s&&await me(s.trackId);if(a)return a}if(!n)return null;let o=await be();return Ld(o,n,s=>Fd(s,{branch:n,repo:e,slug:r}))}async function $C({repo:e=null,number:t=null,branch:n=null}){let r=await Kl({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!Fe(r))return r;if(!n)return null;let o=e?await Oe(e).catch(()=>null):null,s=await be();return Ld(s,n,a=>!Fe(a)&&Fd(a,{branch:n,repo:e,slug:o}))}async function Pn(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Kl({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await Oe(s).catch(()=>null):null);await N({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:e,number:t,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function Tz(e){let t=(await be()).filter(n=>!Fe(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await Oe(n.path);if(r)for(let o of n.prs){if(await pt(r,o.number))continue;let s=await Ld(t,o.headRefName,a=>Fd(a,{branch:o.headRefName,repo:n.path,slug:r}));s&&await Kt({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}I.get("/api/git/prs",async(e,t)=>{try{let n=await $T();await Tz(n.repos).catch(()=>{}),await Ef(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.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(!Sc.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 Oe((await xr(n)).path);if(!S)throw new Error("repo has no GitHub remote");let $=await Kl({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 Ac(n,x),await Kt({repo:S,number:x,trackId:$.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await bc(S,x,{merge:{method:g}}))throw new Error("couldn't record the auto-merge arm");return await fs(S,x,i===!0),await ms(S,x,c===!0&&i!==!0),cn(),await Pn("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 Kl({repo:n,number:r,branch:l}).catch(()=>null),p&&(f=await bp(p,"merge",{count:1,summary:`merging PR #${r}`}));let m=await Ma(n,r,{method:o||"squash"});await Pn("merged",{number:m.number,url:d,title:u,branch:l,repo:n});let y=await Oe(n).catch(()=>null),k=!1;if(y&&(i===!0&&await fs(y,m.number,!0).catch(()=>{}),c===!0&&i!==!0&&await ms(y,m.number,!0).catch(()=>{}),await Ar(y,m.number).catch(()=>{}),await Os(y,m.number).catch(()=>{}),k=await Sr(y,m.number).catch(()=>!1),await fo(y,m.number,"merged").catch(()=>{})),i===!0&&!k){let g=await Kl({repo:n,number:m.number,branch:l}).catch(()=>null);g&&!g.archivedAt&&await Rs(g.id).catch(()=>{})}t.json(m)}catch(m){w=m;let y=m?.message||String(m),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 jg(p,f,w?"failed":"completed",w).catch(()=>{})}});I.post("/api/git/prs/disarm-auto",async(e,t)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=e.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await Oe((await xr(n)).path).catch(()=>null),l=c?await kc(c,i).catch(()=>null):null;try{await iw(n,i)}catch(u){if(!l)throw u}cn(),await Pn("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})}});I.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await Oe(n).catch(()=>null),s=await NT(n,{method:r||"merge",onMerged:a=>{Pn("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&Ar(o,a.number).catch(()=>{}).then(()=>Os(o,a.number)).catch(()=>{}).then(()=>Sr(o,a.number)).catch(()=>{}).then(()=>fo(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})}});I.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:CT()})});I.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(!Sc.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 Tn()).filter(m=>m.trackId===d.id&&m.state!=="merged"&&m.state!=="closed").sort(Tc);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 m of p)await fs(m.repo,m.number,o===!0).catch(()=>{}),await ms(m.repo,m.number,s===!0&&o!==!0).catch(()=>{});let f=[],w=[];for(let m of p){let y={repo:m.repo,number:m.number,ok:!0,action:"armed"};f.push(y);try{let k=await ws(m.repo);if(!k)throw new Error(`no local checkout for ${m.repo}`);let g=await Oa(k,m.number);w.push({r:y,l:m,repoPath:k,pr:g,plan:RT(g,{update:r!==!1})})}catch(k){y.ok=!1,y.action="failed",y.reason=k?.message||String(k)}}for(let{r:m,l:y,repoPath:k,pr:g,plan:x}of OT(w))try{if(x.action==="skip")m.action="skipped",m.reason=x.reason;else if(x.action==="merge")await Ma(k,y.number,{method:u}),await Pn("merged",{number:y.number,url:g.url,title:g.title,branch:g.headRefName,repo:k,track:d}),await Ar(y.repo,y.number).catch(()=>{}),await Os(y.repo,y.number).catch(()=>{}),await Sr(y.repo,y.number).catch(()=>{}),await fo(y.repo,y.number,"merged").catch(()=>{}),m.action="merged";else{if(x.action==="update-arm"&&(await Ac(k,y.number).catch(()=>{}),m.action="updated-armed"),!await bc(y.repo,y.number,{merge:{method:u}}))throw new Error("couldn't record the auto-merge arm");await Pn("auto-armed",{number:y.number,url:g.url,title:g.title,branch:g.headRefName,repo:k,track:d,...m.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(S){m.ok=!1,m.action="failed",m.reason=S?.message||String(S)}if(cn(),!f.some(m=>m.ok)){if(o===!0)for(let m of p)await fs(m.repo,m.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let m of p)await ms(m.repo,m.number,!1).catch(()=>{});return t.status(502).json({error:f.map(m=>`${m.repo}#${m.number}: ${m.reason}`).join("; "),results:f})}t.json({ok:!0,trackId:d.id,results:f})}catch(u){l=u;let d=u?.message||String(u),p=/invalid|required/i.test(d)?400:502;t.status(p).json({error:d})}finally{i&&c&&await jg(i,c,l?"failed":"completed",l).catch(()=>{})}});I.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 Tn()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await kc(a.repo,a.number).catch(()=>null),c=await ws(a.repo).catch(()=>null);c&&await iw(c,a.number).catch(()=>{}),i&&(await Pn("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}))}cn(),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})}});I.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=Ut(i.workspace),d,p=Array.isArray(a)?a.filter(Boolean):[];if(p.length)d=p.map(m=>{let y=u.find(k=>k.name===m||k.repo===m||k.base===m||k.dir===m);if(!y)throw new Error(`invalid repo "${m}" \u2014 not tagged on this Track`);return y});else if(d=await vs(i),!d.length)return t.status(409).json({error:"no tagged repo has work to ship"});let f=[];for(let m of d)try{let y=await lw(m.dir,{title:r,body:o,base:s}),k=m.repo||await Oe(m.dir).catch(()=>null);k&&y.number&&await Kt({repo:k,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Pn("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:k??m.name}}),f.push({repo:m.name,slug:k??null,ok:!0,url:y.url,number:y.number})}catch(y){f.push({repo:m.name,slug:m.repo??null,ok:!1,error:y?.message||String(y)})}let w=f.filter(m=>m.ok);return w.length?t.json({ok:!0,multi:!0,results:f,url:w[0].url,number:w[0].number}):t.status(502).json({error:f.map(m=>`${m.repo}: ${m.error}`).join("; "),results:f})}let c=await lw(i.cwd,{title:r,body:o,base:s}),l=await Oe(i.cwd).catch(()=>null);l&&c.number&&await Kt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Pn("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})}});I.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 vs(n),o=new Map(r.map(a=>[a.base,a])),s=Ut(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)})}});I.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 Pp(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})}});I.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await Dp(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})}});I.post("/api/tracks/:id/reshape",async(e,t)=>{let{repos:n,skipPermissions:r}=e.body||{};try{let o=await yy(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})}});I.get("/api/tracks/:id/reshape",(e,t)=>{t.json(bk(dl(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}I.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Oa(n,r),s=await Oe(n).catch(()=>null),a=await $C({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=Ut(a.workspace).find(k=>k.base===n||s&&k.repo===s)??null,c=a.workspace?.kind==="multi",l=await PT(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?Re.join("ci",i.name):"ci",d=Re.join(a.cwd,".bostrat",u),p=Re.join(".bostrat",u,`pr-${o.number}-fail.log`);await At.promises.mkdir(d,{recursive:!0}),await At.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
|
|
800
|
+
${k.log??"(no fetchable log \u2014 see link)"}
|
|
801
801
|
`).join(`
|
|
802
|
-
`));let f=l.map(b=>b.name).join(", "),w=c&&i?` in the ${i.name}/ checkout`:"",m=await zt(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${w}. Failing check${l.length===1?"":"s"}: ${f}. The failure log is at ${p} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),g=s;g&&(await Gt({repo:g,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await YA(g,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Nn("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(b=>b.name)}}),t.json({ok:!0,leadBox:m.leadBox,queued:!!m.queued,checks:l.map(b=>b.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})}});I.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Oa(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await Oe(n).catch(()=>null),a=await EC({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=Ut(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 zt(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 Gt({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await XA(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Nn("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})}});I.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=po.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 bo(`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 nk(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 zt(a.id,_a(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&&uy(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})}});I.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 rk(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 zl=e=>t=>({...t,url:CO(t,e.headers)});I.get("/api/views",async(e,t)=>{try{let n=await eg({trackId:e.query.track||void 0});t.json({views:n.map(zl(e)),maxLive:xo})}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.get("/api/views/:id",async(e,t)=>{let n=await y_(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:zl(e)(n),session:Ba(n.slug)})});I.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 be()).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(!Bb(e.headers))return t.status(412).json({error:uR});let c=await ng({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:zl(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})}});I.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:zl(e)(await b_(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});I.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:zl(e)(await k_(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})}});I.delete("/api/views/:id",async(e,t)=>{try{t.json(await x_(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var bz=[["linear",Ns,tb],["jira",sf,Qy]],dk=new Map;I.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await Zy(),r={};for(let[o,s,a]of bz){let i=await s();if(!i){dk.delete(o),r[o]={configured:!1};continue}let c=dk.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},dk.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)})}});I.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await Zy()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await rR(o),i=n?await me(n):null;!i&&r&&(i=(await be()).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 Ws=async(e,t)=>{let n=e?await me(e):null;return!n&&t&&(n=(await be()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Re.sep)))||null),n};I.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await Ky()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});I.get("/api/roadmap",async(e,t)=>{try{t.json(await Vy())}catch(n){t.status(502).json({error:n?.message||String(n)})}});I.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await Ws(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 oi(o);await tf(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})}});I.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await Ws(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await tf(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var _C=async(e,t,n)=>{if(e)return{ref:e,track:await Ws(t,n)};let r=await Ws(t,n),o=r?await Jy(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}};I.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 _C(o,n,r),l=await J$({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})}});I.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await _C(e.query.circuit,e.query.track,e.query.cwd);t.json(await zy(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})}});I.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 Ws(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=await Z$(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})}});I.get("/api/decisions",async(e,t)=>{try{let n=await Ws(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await Q$(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})}});I.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 Ws(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await Jy(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 Y$(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})}});I.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 be()).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 ci(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})}});I.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 be()).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 _R(),ticket:await ir(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});I.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await ir(e.params.id)})});I.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 ci(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})}});I.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await wb(e.params.id)})});var Bf=e=>/no ticket linked|unknown offer|unknown state/i.test(e)?404:/already answered/i.test(e)?409:/required|no ticketing/i.test(e)?400:502;I.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await OR(e.params.id))}catch(n){let r=n?.message||String(n);t.status(Bf(r)).json({error:r})}});I.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await gb(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(Bf(r)).json({error:r})}});I.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await MR(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(Bf(r)).json({error:r})}});I.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 CR(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(Bf(s)).json({error:s})}});I.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await $y(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})}});I.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 be()).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 PR(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})}});I.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await NR()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});I.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 DR(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal|unknown team/i.test(a)?404:/already answered/i.test(a)?409:/required|no ticketing|pick one/i.test(a)?400:502).json({error:a})}});I.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await Kc({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.get("/api/mocks/:id",async(e,t)=>{let n=await Ir(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var Jl={dotfiles:"allow"};I.get("/api/mocks/:id/png",async(e,t)=>{let n=await Ir(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=qc(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,Jl,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});I.get("/api/mocks/:id/media",async(e,t)=>{let n=await Ir(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=qc(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,Jl,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});I.get("/api/mocks/:id/html",async(e,t)=>{let n=await Ir(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(ln(n,r.v).html,Jl,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});I.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&up(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 be()).find(m=>m.cwd&&(c===m.cwd||c.startsWith(m.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 j_({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})}});I.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&up(n)?n:null;try{let o=await L_(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})}});I.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await H_(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await zt(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})}});I.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await aI(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await W_(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})}});I.get("/api/flows",async(e,t)=>{try{t.json({flows:await tI({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.get("/api/flows/:id",async(e,t)=>{let n=await nI(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});I.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 be()).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 rI({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})}});I.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await oI(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})}});I.delete("/api/flows/:id",async(e,t)=>{try{t.json(await sI(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});I.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await be(),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 mI({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)})}});I.get("/api/runbooks/:id",async(e,t)=>{let n=await wg(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});I.get("/api/runbooks/:id/md",async(e,t)=>{let n=await wg(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=pI(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 hI(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(Mr(n,r.v),Jl,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});I.get("/api/loops",async(e,t)=>{t.json(await hc())});I.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 be()).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 hc();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 Ch({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})});I.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 be()).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 HO(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var yi=async(e,t)=>{let n=e?await me(e):null;return!n&&t&&(n=(await be()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Re.sep)))||null),n};I.post("/api/followups/offer",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await yi(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 ME(s,o,{ship:{changedRepos:vs}})})}catch(s){t.status(400).json({error:s?.message||String(s)})}});I.post("/api/followups/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await yi(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 NE(s,o)})}catch(s){let a=s?.message||String(s);t.status(/unreachable/i.test(a)?503:400).json({error:a})}});I.get("/api/followups",async(e,t)=>{let n=typeof e.query?.cwd=="string"?e.query.cwd:null,r=await yi(null,n).catch(()=>null);t.json({ok:!0,trackId:r?.id??null,triggers:Rw,...await jE(r)})});var fC=new Set(["reconcile","fixing-ci","rebase","merge"]);I.get("/api/status",async(e,t)=>{let n=await yi(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})});I.post("/api/status",async(e,t)=>{try{let{trackId:n,cwd:r,phase:o,summary:s,scope:a,reason:i}=e.body||{},c=await yi(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&&!fC.has(l))throw new Error(`phase must be one of ${[...fC].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=pz();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)})}});I.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 DE(i,{offerId:n,followupId:r||null,armId:o||null,promoted:s||null,all:a===!0},{sayToLead:zt,unarchiveTrack:Lp})})}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})}});I.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 CE(n,e.body?.armId)})}catch(n){let r=n?.message||String(n);t.status(/unknown/i.test(r)?404:400).json({error:r})}});I.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 PE(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})}});Ye.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 Rc(s,n,o),t==="opened"||t==="pushed")){let a=BE(await K(s).catch(()=>[]));a&&await Rc(s,"ship.done",{...o,shipOfferId:a})}}).catch(()=>{})});var IC=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await me(n):null;if(!s&&r&&(s=(await be()).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 hc();if(a.source!=="plane")return t.status(503).json({error:a.source==="unconfigured"?"this node isn't linked to a plane \u2014 loops live in the plane":`plane unreachable (${a.error||"no detail"})`}),null;let i;try{i=GO(a.loops,o)}catch(c){return t.status(/matches \d+ loops/.test(c.message)?409:404).json({error:c.message}),null}return{track:s,loop:i}};I.post("/api/loops/propose-update",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await IC(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await KO(s.track,s.loop,e.body?.patch)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});I.post("/api/loops/propose-delete",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await IC(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await VO(s.track,s.loop,e.body?.reason)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});I.post("/api/planning/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o,scope:s}=e.body||{},a=await yi(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i;try{i=await Vy()}catch(c){return t.status(503).json({error:c?.message||String(c)})}try{let c=s==="circuit"?Wb:Dl;t.json({ok:!0,trackId:a.id,proposal:await oM(a,o,i,{allow:c})})}catch(c){t.status(400).json({error:c?.message||String(c)})}});var bk=e=>/unknown proposal/i.test(e)?404:/already answered/i.test(e)?409:400;I.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 sM(s,n,r,{results:o||[]})})}catch(s){let a=s?.message||String(s);t.status(bk(a)).json({error:a})}});I.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 JO(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(bk(a)).json({error:a})}});I.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 YO(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(bk(s)).json({error:s})}});I.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 WO(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})}});I.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 be()).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 wI({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})}});I.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await gI(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})}});I.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await yI(e.params.id,(e.body||{}).status)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/must be/i.test(r)?400:500;t.status(o).json({error:r})}});I.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await bI(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});I.get("/api/changelog/draft",async(e,t)=>{try{let n=uM(e.query.since||null);t.json({since:n,repos:Gb(),...await dM(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var $C=async({trackId:e,cwd:t})=>{let n=e?await me(e):null;return!n&&t&&(n=(await be()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+Re.sep)))||null),n};I.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=await $C({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 pM(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})}});I.post("/api/changelog/retract",async(e,t)=>{let{day:n,trackId:r,cwd:o}=e.body||{};try{let s=await $C({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 fM(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var kz=120,pk=400,xz=/^[a-z0-9][a-z0-9_-]{0,23}$/,gi=null,mC=null;I.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&&!xz.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>pk)return t.status(400).json({error:`message is ${c.length} chars \u2014 over the ${pk} cap; a notification is a nudge, put the detail in chat`});let u=Vc(`${n||""}
|
|
803
|
-
${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 be()).find(
|
|
802
|
+
`));let f=l.map(k=>k.name).join(", "),w=c&&i?` in the ${i.name}/ checkout`:"",m=await Jt(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 Kt({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await ZA(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Pn("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:m.leadBox,queued:!!m.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})}});I.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Oa(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await Oe(n).catch(()=>null),a=await $C({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=Ut(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 Jt(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 Kt({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await QA(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Pn("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})}});I.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=po.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 bo(`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 rk(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 Jt(a.id,_a(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&&dy(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})}});I.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 ok(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 zl=e=>t=>({...t,url:DO(t,e.headers)});I.get("/api/views",async(e,t)=>{try{let n=await Qw({trackId:e.query.track||void 0});t.json({views:n.map(zl(e)),maxLive:xo})}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.get("/api/views/:id",async(e,t)=>{let n=await k_(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:zl(e)(n),session:Ba(n.slug)})});I.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 be()).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(!jb(e.headers))return t.status(412).json({error:fR});let c=await tg({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:zl(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})}});I.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:zl(e)(await x_(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});I.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:zl(e)(await v_(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})}});I.delete("/api/views/:id",async(e,t)=>{try{t.json(await S_(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var Ez=[["linear",Ns,nb],["jira",sf,eb]],pk=new Map;I.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await Qy(),r={};for(let[o,s,a]of Ez){let i=await s();if(!i){pk.delete(o),r[o]={configured:!1};continue}let c=pk.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},pk.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)})}});I.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await Qy()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await aR(o),i=n?await me(n):null;!i&&r&&(i=(await be()).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 Ws=async(e,t)=>{let n=e?await me(e):null;return!n&&t&&(n=(await be()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Re.sep)))||null),n};I.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await Vy()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});I.get("/api/roadmap",async(e,t)=>{try{t.json(await zy())}catch(n){t.status(502).json({error:n?.message||String(n)})}});I.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await Ws(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 oi(o);await tf(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})}});I.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await Ws(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await tf(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var RC=async(e,t,n)=>{if(e)return{ref:e,track:await Ws(t,n)};let r=await Ws(t,n),o=r?await Yy(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}};I.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 RC(o,n,r),l=await Z$({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})}});I.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await RC(e.query.circuit,e.query.track,e.query.cwd);t.json(await Jy(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})}});I.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 Ws(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=await tR(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})}});I.get("/api/decisions",async(e,t)=>{try{let n=await Ws(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await nR(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})}});I.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 Ws(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await Yy(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 Q$(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})}});I.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 be()).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 ci(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})}});I.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 be()).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 RR(),ticket:await cr(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});I.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await cr(e.params.id)})});I.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 ci(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})}});I.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await gb(e.params.id)})});var Bf=e=>/no ticket linked|unknown offer|unknown state/i.test(e)?404:/already answered/i.test(e)?409:/required|no ticketing/i.test(e)?400:502;I.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await NR(e.params.id))}catch(n){let r=n?.message||String(n);t.status(Bf(r)).json({error:r})}});I.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await yb(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(Bf(r)).json({error:r})}});I.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await PR(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(Bf(r)).json({error:r})}});I.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 DR(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(Bf(s)).json({error:s})}});I.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await Ry(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})}});I.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 be()).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 jR(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})}});I.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await BR()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});I.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 LR(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})}});I.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await Kc({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.get("/api/mocks/:id",async(e,t)=>{let n=await $r(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var Jl={dotfiles:"allow"};I.get("/api/mocks/:id/png",async(e,t)=>{let n=await $r(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=In(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=qc(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,Jl,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});I.get("/api/mocks/:id/media",async(e,t)=>{let n=await $r(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=In(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=qc(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,Jl,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});I.get("/api/mocks/:id/html",async(e,t)=>{let n=await $r(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=In(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(un(n,r.v).html,Jl,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});I.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&up(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 be()).find(m=>m.cwd&&(c===m.cwd||c.startsWith(m.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 F_({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})}});I.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&up(n)?n:null;try{let o=await U_(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})}});I.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await q_(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await Jt(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})}});I.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await cI(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await G_(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})}});I.get("/api/flows",async(e,t)=>{try{t.json({flows:await rI({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});I.get("/api/flows/:id",async(e,t)=>{let n=await oI(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});I.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 be()).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 sI({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})}});I.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await aI(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})}});I.delete("/api/flows/:id",async(e,t)=>{try{t.json(await iI(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});I.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await be(),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 wI({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)})}});I.get("/api/runbooks/:id",async(e,t)=>{let n=await hg(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});I.get("/api/runbooks/:id/md",async(e,t)=>{let n=await hg(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=mI(n,e.query.v??null);if(!r)return t.status(404).json({error:"no such version"});if(t.set("content-security-policy","sandbox"),t.set("cache-control","private, max-age=31536000, immutable"),t.type("text/markdown; charset=utf-8"),n.remote){let o=await gI(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(Cr(n,r.v),Jl,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});I.get("/api/loops",async(e,t)=>{t.json(await hc())});I.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 be()).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 hc();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 Mh({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})});I.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 be()).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 GO(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var yi=async(e,t)=>{let n=e?await me(e):null;return!n&&t&&(n=(await be()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Re.sep)))||null),n};I.post("/api/followups/offer",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await yi(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 NE(s,o,{ship:{changedRepos:vs}})})}catch(s){t.status(400).json({error:s?.message||String(s)})}});I.post("/api/followups/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await yi(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 DE(s,o)})}catch(s){let a=s?.message||String(s);t.status(/unreachable/i.test(a)?503:400).json({error:a})}});I.get("/api/followups",async(e,t)=>{let n=typeof e.query?.cwd=="string"?e.query.cwd:null,r=await yi(null,n).catch(()=>null);t.json({ok:!0,trackId:r?.id??null,triggers:$w,...await FE(r)})});var wC=new Set(["reconcile","fixing-ci","rebase","merge"]);I.get("/api/status",async(e,t)=>{let n=await yi(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})});I.post("/api/status",async(e,t)=>{try{let{trackId:n,cwd:r,phase:o,summary:s,scope:a,reason:i}=e.body||{},c=await yi(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&&!wC.has(l))throw new Error(`phase must be one of ${[...wC].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=bz();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)})}});I.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 jE(i,{offerId:n,followupId:r||null,armId:o||null,promoted:s||null,all:a===!0},{sayToLead:Jt,unarchiveTrack:Lp})})}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})}});I.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 PE(n,e.body?.armId)})}catch(n){let r=n?.message||String(n);t.status(/unknown/i.test(r)?404:400).json({error:r})}});I.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 BE(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})}});Ye.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 Rc(s,n,o),t==="opened"||t==="pushed")){let a=LE(await K(s).catch(()=>[]));a&&await Rc(s,"ship.done",{...o,shipOfferId:a})}}).catch(()=>{})});var OC=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await me(n):null;if(!s&&r&&(s=(await be()).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 hc();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=zO(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}};I.post("/api/loops/propose-update",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await OC(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await JO(s.track,s.loop,e.body?.patch)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});I.post("/api/loops/propose-delete",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await OC(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await YO(s.track,s.loop,e.body?.reason)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});I.post("/api/planning/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o,scope:s}=e.body||{},a=await yi(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i;try{i=await zy()}catch(c){return t.status(503).json({error:c?.message||String(c)})}try{let c=s==="circuit"?qb:Dl;t.json({ok:!0,trackId:a.id,proposal:await iM(a,o,i,{allow:c})})}catch(c){t.status(400).json({error:c?.message||String(c)})}});var kk=e=>/unknown proposal/i.test(e)?404:/already answered/i.test(e)?409:400;I.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 cM(s,n,r,{results:o||[]})})}catch(s){let a=s?.message||String(s);t.status(kk(a)).json({error:a})}});I.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 ZO(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(kk(a)).json({error:a})}});I.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 QO(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(kk(s)).json({error:s})}});I.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 KO(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})}});I.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 be()).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 yI({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})}});I.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await bI(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})}});I.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await kI(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})}});I.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await xI(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});I.get("/api/changelog/draft",async(e,t)=>{try{let n=fM(e.query.since||null);t.json({since:n,repos:Kb(),...await mM(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var MC=async({trackId:e,cwd:t})=>{let n=e?await me(e):null;return!n&&t&&(n=(await be()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+Re.sep)))||null),n};I.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=await MC({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 hM(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})}});I.post("/api/changelog/retract",async(e,t)=>{let{day:n,trackId:r,cwd:o}=e.body||{};try{let s=await MC({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 wM(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var _z=120,fk=400,Iz=/^[a-z0-9][a-z0-9_-]{0,23}$/,gi=null,gC=null;I.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&&!Iz.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>fk)return t.status(400).json({error:`message is ${c.length} chars \u2014 over the ${fk} cap; a notification is a nudge, put the detail in chat`});let u=Vc(`${n||""}
|
|
803
|
+
${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 be()).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(m=>m.name===d.leadBox),f=(m,y)=>String(m).replace(/\s+/g," ").trim().slice(0,y),w={title:n?f(n,_z):d.name,body:f(c,fk),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:gi?gi.status().connected:null})}catch(c){t.status(500).json({error:c?.message||String(c)})}});I.post("/api/dictation",async(e,t)=>{if(!Buffer.isBuffer(e.body)||e.body.length===0)return t.status(400).json({error:"expected a raw audio body"});try{t.json(await dC(e.body,e.headers["content-type"]))}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});I.get("/api/dictation/glossary",async(e,t)=>{t.json({configured:!!await bl(),terms:await uk(),problem:await uC()})});I.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 lC(n,r)})});I.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 pC())});I.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 fC())}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});I.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 mC(n,{model:r,usage:o,seconds:s})})}catch(n){t.status(n?.status||500).json({error:n?.message||String(n)})}});I.post("/api/tracks",async(e,t)=>{let{name:n,cwd:r,repo:o,repos:s,agent:a,model:i,skipPermissions:c,ticket:l,circuit:u}=e.body||{};try{let d=await Bp({name:n,cwd:r,repo:o,repos:s,agent:a,model:i,skipPermissions:c!==!1}),p=null;if(l)try{await ci(d,l,{source:"create"})}catch(w){p=w?.message||String(w)}let f=null;if(u)try{await tf(d,await oi(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})}});I.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:CC(ni(e.params.id)),reshape:bk(dl(e.params.id)),subtasks:Ms(e.params.id).map(Yl),pendingOffers:await oy(n)})});I.delete("/api/tracks/:id",async(e,t)=>{try{let n=await Sy(e.params.id,{cascadeMocks:e.query.mocks!=="0",cascadeRunbooks:e.query.runbooks==="1"});await gb(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})}});I.post("/api/tracks/:id/archive",async(e,t)=>{try{t.json({ok:!0,track:await Rs(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})}});I.post("/api/tracks/:id/rename",async(e,t)=>{try{t.json({ok:!0,track:await _y(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})}});I.post("/api/tracks/:id/unarchive",async(e,t)=>{try{t.json({ok:!0,track:await Lp(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})}});I.get("/api/tracks/:id/messages",async(e,t)=>{let n=await Op(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json(n)});I.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}
|
|
804
804
|
data: ${JSON.stringify(i)}
|
|
805
805
|
|
|
806
|
-
`),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=
|
|
806
|
+
`),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=r0(n.id,r),a=setInterval(()=>t.write(`: hb
|
|
807
807
|
|
|
808
|
-
`),25e3);e.on("close",()=>{clearInterval(a),s()})});I.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=cc(await K(n)),a=Hu(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}))})});I.get("/api/memory",async(e,t)=>{t.json({stores:await
|
|
809
|
-
`);I.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await
|
|
808
|
+
`),25e3);e.on("close",()=>{clearInterval(a),s()})});I.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=cc(await K(n)),a=Hu(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}))})});I.get("/api/memory",async(e,t)=>{t.json({stores:await k0(),harvest:w0()})});I.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 x0(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 $z=["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(`
|
|
809
|
+
`);I.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await Jt(e.params.id,$z)})}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})}});I.post("/api/tracks/:id/lead/restart",async(e,t)=>{try{t.json({ok:!0,track:await Fp(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}:{}})}});I.post("/api/tracks/:id/say",async(e,t)=>{let{text:n,attachments:r,producer:o}=e.body||{};try{t.json(await Jt(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})}});I.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 _I(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})}});I.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||!yg(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=gg(n.cwd),s,a;try{s=await At.promises.realpath(o),a=await At.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 Rz=/^[0-9a-fA-F-]{8,64}$/;I.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(!Rz.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=kn(a.cwd,a.sessionId);if(!i)return t.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=wz.createInterface({input:At.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?Mm(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)})}});I.post("/api/tracks/:id/interrupt",async(e,t)=>{try{t.json(await Np(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})}});I.delete("/api/tracks/:id/queue",async(e,t)=>{try{let{removed:n,restored:r,draftRev:o}=await ly(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)})}});I.post("/api/tracks/:id/answer-prompt",async(e,t)=>{let{promptId:n,answers:r,text:o,custom:s}=e.body||{};try{t.json(await Mp(e.params.id,{promptId:n,answers:r,text:o,custom:s}))}catch(a){let i=a?.message||String(a),c=/not found/i.test(i)?404:/no pending prompt|changed since|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})}});I.post("/api/setup/:provider/start",async(e,t)=>{try{let n=await xy({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})}});I.post("/api/tracks/:id/navigate-prompt",async(e,t)=>{let{promptId:n,questionIndex:r}=e.body||{};try{t.json(await iy(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})}});I.put("/api/tracks/:id/draft",async(e,t)=>{let{text:n,rev:r}=e.body||{};try{t.json(await my(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})}});I.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 kf(r,{fresh:o}))});I.post("/api/tracks/:id/model",async(e,t)=>{let{model:n}=e.body||{};try{t.json(await hy(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})}});I.post("/api/tracks/:id/subtask",async(e,t)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=e.body||{};try{let a=await Ly(e.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});t.json({ok:!0,subtask:Yl(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})}});I.get("/api/tracks/:id/subtasks",(e,t)=>{t.json({subtasks:Ms(e.params.id).map(Yl)})});I.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)});I.post("/api/tracks/:id/subtask/:sid/cancel",(e,t)=>{try{t.json({ok:!0,subtask:Yl(Kp(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});I.post("/api/tracks/:id/subtask/:sid/merge-now",(e,t)=>{try{t.json({ok:!0,subtask:Yl(Fy(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});function Yl(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}I.post("/api/tracks/:id/handoff-lead",async(e,t)=>{let{toAgent:n,skipPermissions:r,reason:o}=e.body||{};try{let s=await Up(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})}});I.post("/api/tracks/:id/spin-up-view",async(e,t)=>{try{t.json(await py(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})}});I.get("/api/tracks/:id/handoff-lead",(e,t)=>{t.json(CC(ni(e.params.id))??{running:!1})});I.post("/api/tracks/:id/transfer-out",async(e,t)=>{try{let n=await Py(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})}});I.get("/api/tracks/:id/transfer-out",(e,t)=>{let n=Ny(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??{}})});I.post("/api/tracks/:id/transfer-out/commit",async(e,t)=>{try{t.json(await Wp(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})}});I.post("/api/tracks/adopt",async(e,t)=>{let{trackId:n}=e.body||{};try{t.json(await By({...e.body||{},trackId:n}))}catch(r){let o=r?.message||String(r);t.status(/already running|already hosted/i.test(o)?409:/not found/i.test(o)?404:400).json({error:o})}});function CC(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}I.get("/api/debug/handoff",async(e,t)=>{let n=Math.min(Number(e.query.limit)||80,500);t.json({verbose:xu(),paths:j0,trace:await B0({limit:n})})});var mk=Re.resolve(vC,"..","..","client","dist");At.existsSync(mk)&&(I.use(en.static(mk)),I.get(/^(?!\/api\/).*/,(e,t)=>t.sendFile(Re.join(mk,"index.html"),Jl)));var NC=xC.createServer(I),Oz=new kz({noServer:!0}),xk=(e,t,n)=>{if(!(Nl(e)||wi.allowed(wi.clientIp(e))||Df.allows(e))){t.write(`HTTP/1.1 403 Forbidden\r
|
|
810
810
|
\r
|
|
811
|
-
`),t.destroy();return}if(
|
|
811
|
+
`),t.destroy();return}if(PO(e,t,n))return;if(!MO(e,yk,{enforce:AC})){t.write(`HTTP/1.1 401 Unauthorized\r
|
|
812
812
|
\r
|
|
813
|
-
`),t.destroy();return}let r=
|
|
813
|
+
`),t.destroy();return}let r=sx(e.url);if(!r.ok){t.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
814
814
|
\r
|
|
815
815
|
`),t.destroy();return}let o=r.sessionName;if(!je(o)){t.write(`HTTP/1.1 400 Bad Request\r
|
|
816
816
|
\r
|
|
817
|
-
`),t.destroy();return}let{cols:s,rows:a}=r;
|
|
817
|
+
`),t.destroy();return}let{cols:s,rows:a}=r;Oz.handleUpgrade(e,t,n,i=>nx(i,o,{cols:s,rows:a,revive:()=>Ey(o)}))};NC.on("upgrade",xk);var PC=process.env.BOSTRAT_CERT_DIR||Re.join(process.env.HOME||ql.tmpdir(),".bostrat","certs"),yC=Number(process.env.BOSTRAT_TLS_PORT)||3443,bC=Re.join(PC,"bostrat.globopi.key"),kC=Re.join(PC,"bostrat.globopi.crt");if(At.existsSync(bC)&&At.existsSync(kC)){let e=yz.createServer({key:At.readFileSync(bC),cert:At.readFileSync(kC)},I);e.on("upgrade",xk),e.on("error",t=>{console.error(`[bostrat-agent] https listener failed (${t?.code||t?.message}) \u2014 continuing HTTP-only`)}),e.listen(yC,Pf,()=>{console.log(`[bostrat-agent] https listening on ${Pf||"0.0.0.0"}:${yC}`)})}var hk=String(process.env.BOSTRAT_VIEW_BIND||"").trim();if(hk){Df.active||console.warn(`[bostrat-agent] \u26A0 BOSTRAT_VIEW_BIND=${hk} 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=BO({app:I,onUpgrade:xk,extractViewSlug:Pl,log:console}),t=xC.createServer(e.request);t.on("upgrade",e.upgrade),jO(t,{addr:hk,port:wk,log:console})}NC.listen(wk,Pf,()=>{console.log(`[bostrat-agent] listening on ${Pf||"0.0.0.0"}:${wk}`);let e=N$(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: ${qt}`),console.log(`[bostrat-agent] auto-trigger default: ${SC}%`),console.log(`[bostrat-agent] ip allowlist: ${wi.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(Df.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),My().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),by().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)),jy().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Iy().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(()=>$y()).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)),ky().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)),Lz(),YI({listTracks:be}),WI({trackOf:ry}),Nz(),Pz(),jz(),wA({listTracks:be,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)),CI()&&DI({listTracks:be}),F0().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)),DM(),FM(),FR({resolveTrack:me}),YR({listTracks:be,createTrack:Bp,archiveTrack:Rs,listRepos:dr,resolveTrack:me,sayToLead:Jt,answerPrompt:Mp,startLeadHandoff:Up,interruptLead:Np,listSubtaskJobs:Ms,cancelSubtask:Kp,readEvents:K,appendEvent:N,divertShip:rk,answerPrExists:ok,knownAgents:cl}),Mz(),qi().then(t=>{t||console.warn(`[boot] no host tmux server on ${qt} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),eC(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(gi=q$({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,apiToken:yk,nodeId:process.env.BOSTRAT_NODE_ID||ql.hostname(),name:process.env.BOSTRAT_NODE_NAME||ql.hostname(),listTracks:be,detectAgents:Rp,sampleMetrics:mO,deliverySample:()=>a_(cy()),onHashKey:t_,onViewBase:wR,onTracksTransferred:Dy,onDegraded:({since:t,unacked:n,oldestAgeMs:r,mode:o})=>{let s=Math.round(r/6e4),a=`${process.env.BOSTRAT_NODE_NAME||ql.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(()=>{}),gC=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(()=>{}),gC=null},onConnected:()=>{PM()},onRemoved:({reason:t,via:n})=>{let r=`${process.env.BOSTRAT_NODE_NAME||ql.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{gk={latest:t.latest,current:t.current},await sr({...t,policy:Rt()}),Rt()==="auto"&&await EC({to:t.latest,from:t.current})}catch(n){console.error(`[plane-link] update notice failed: ${n?.message||n}`)}},onUpdatePolicy:t=>Az(t).catch(()=>{}),onHelloRefused:async({current:t,min:n,latest:r,installKind:o,fix:s})=>{try{let a=r||n;if(await sr({current:t,latest:a,min:n,installKind:o,required:!0,policy:Rt(),status:"available",reason:"refused"}),Rt()!=="off"&&a){let i=await ef({to:a,kind:mn,reason:"hard-floor"});i.ok?(Vl.cancel(),await sr({current:t,latest:a,min:n,installKind:o,required:!0,policy:Rt(),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:Rt}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),na().catch(()=>{}),AI().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),UR().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),z_().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),dI().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),lT().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{gi?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function Mz(){Cz(),sp().catch(()=>{}),setInterval(()=>sp().catch(()=>{}),1e4)}function Cz(){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(vC,"..","bin",e),n=Re.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!At.existsSync(t))continue;At.mkdirSync(n,{recursive:!0});let r=Re.join(n,e);(At.existsSync(r)?At.readlinkSync(r):null)!==t&&(At.rmSync(r,{force:!0}),At.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 Nz(){let e=!1,t=async()=>{if(!e){e=!0;try{let n=await be(),r=await gO({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 Pz(){let e=null,t=async()=>{try{let n=ka(),r=0,o=[],s=null,a=null;try{let u=await be(),d=await Ib(u);r=d.fleet.unpricedTurns||0,o=d.fleet.unpricedModels||[],s=await hO(u,{days:7}),a=(await Rb(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(`
|
|
818
818
|
`);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(`
|
|
819
|
-
`).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
|
|
819
|
+
`).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 Dz=Number(process.env.BOSTRAT_MEMORY_RECONCILE_MS)||5*6e4,Bz=Number(process.env.BOSTRAT_MEMORY_RECONCILE_FIRST_MS)||5*6e4;function jz(){let e=async()=>{try{let n=await b0();n.ran&&console.log(`[memory] one-time full backfill sweep: ${n.harvested} fact(s) harvested`);let o=(await be()).filter(a=>!Fe(a)&&a.kind!=="setup").flatMap(a=>[a.cwd,...(a.boxes||[]).map(i=>i.cwd)]).filter(Boolean),s=await y0([...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,Dz).unref()},Bz).unref()}function Lz(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{let t=await be();for(let n of t)Fe(n)||!n.boxes?.some(r=>r.state==="live")||(await Op(n.id).catch(()=>{}),await cp(n).catch(()=>{}))}catch{}finally{e=!1}}},4e3)}
|