bostrat-node 1.10.8 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +165 -165
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
var
|
|
1
|
+
var hN=Object.defineProperty;var G=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var Zf=(e,t)=>{for(var n in t)hN(e,n,{get:t[n],enumerable:!0})};import{createHash as wN,randomBytes as gN,timingSafeEqual as yN}from"node:crypto";function Uk(){return gN(32).toString("hex")}function Ho(e){let t=typeof e=="string"?e.trim():"";return t?wN("sha256").update(t).digest("hex"):null}function Hk(e,t){return typeof e!="string"||typeof t!="string"||e.length!==t.length||!e?!1:yN(Buffer.from(e),Buffer.from(t))}function Ei(e,t={},{now:n=Date.now(),log:r=console.warn}={}){if(Qf.set(e,(Qf.get(e)||0)+1),em.has(e)&&n-em.get(e)<bN)return!1;em.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=${Qf.get(e)}${o?` ${o}`:""}`),!0}async function Wk(e,{byToken:t,legacy:n,env:r=process.env}={}){let o=typeof e=="string"?e.trim():"";if(!o)return r.BOSTRAT_HOOK_LEGACY_DENY==="1"?{mode:"legacy-denied",target:null}:{mode:"legacy",target:await n()};let s=await t(o);return s?.track?{mode:"token",target:s}:{mode:"refused",target:null}}var dr,pr,Qf,em,bN,Js=G(()=>{dr="BOSTRAT_HOOK_TOKEN",pr="x-bostrat-hook";Qf=new Map,em=new Map,bN=6e4});import Vt from"node:fs/promises";import qk from"node:os";import Dt from"node:path";import{execFile as kN}from"node:child_process";import{promisify as xN}from"node:util";function ru(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function tm(){return process.env.CLAUDE_CONFIG_DIR?Dt.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Dt.join(qk.homedir(),".claude.json")}function vN(){return process.env.CLAUDE_CONFIG_DIR?Dt.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Dt.join(qk.homedir(),".claude","settings.json")}async function Wr(e){if(!e||typeof e!="string")return;let t=Dt.resolve(e),n=tm(),r=vN(),o={};try{o=JSON.parse(await Vt.readFile(n,"utf8"))}catch{}o.hasCompletedOnboarding=!0,o.theme||(o.theme="dark"),o.projects=o.projects||{},o.projects[t]=o.projects[t]||{};let s=o.projects[t];Array.isArray(s.allowedTools)||(s.allowedTools=[]),s.hasTrustDialogAccepted=!0,s.hasCompletedProjectOnboarding=!0;let a=process.env.ANTHROPIC_API_KEY;if(a){let c=a.slice(-20);o.customApiKeyResponses=o.customApiKeyResponses||{},o.customApiKeyResponses.approved=o.customApiKeyResponses.approved||[],o.customApiKeyResponses.rejected=o.customApiKeyResponses.rejected||[],o.customApiKeyResponses.approved.includes(c)||o.customApiKeyResponses.approved.push(c)}await Vt.mkdir(Dt.dirname(n),{recursive:!0}),await Vt.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await Vt.readFile(r,"utf8"))}catch{}if(i.skipDangerousModePermissionPrompt=!0,i.remoteControlAtStartup=!1,i.tui===void 0&&(i.tui="default"),process.env.BOSTRAT_HOOKS!=="off"){let l=`curl -sS -m 3 -X POST ${process.env.BOSTRAT_HOOK_URL||`http://127.0.0.1:${process.env.PORT||3362}/api/hook`} -H 'content-type: application/json' -H "${pr}: $${dr}" --data-binary @- >/dev/null 2>&1 || true`,u=[{hooks:[{type:"command",command:l}]}];i.hooks=i.hooks||{};for(let d of["SessionStart","UserPromptSubmit","Stop"])i.hooks[d]=u;i.hooks.PostToolUse=[{matcher:"*",hooks:[{type:"command",command:l}]}]}await Vt.mkdir(Dt.dirname(r),{recursive:!0}),await Vt.writeFile(r,JSON.stringify(i,null,2)),await SN(t)}async function SN(e){try{let t=Dt.resolve(e),n=Dt.join(t,".bostrat","memory","claude"),r=Dt.join(t,".claude","settings.local.json"),o={};try{o=JSON.parse(await Vt.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(Dt.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await Vt.mkdir(n,{recursive:!0}),await Vt.mkdir(Dt.dirname(r),{recursive:!0}),await Vt.writeFile(r,JSON.stringify(o,null,2)),await AN(t,["/.claude/settings.local.json"])}catch{}}async function AN(e,t){try{let{stdout:n}=await xN(kN)("git",["-C",e,"rev-parse","--git-path","info/exclude"]),r=Dt.resolve(e,n.trim()),o=await Vt.readFile(r,"utf8").catch(()=>""),s=t.filter(a=>!o.split(`
|
|
2
2
|
`).includes(a));if(!s.length)return;await Vt.mkdir(Dt.dirname(r),{recursive:!0}),await Vt.appendFile(r,(o&&!o.endsWith(`
|
|
3
3
|
`)?`
|
|
4
4
|
`:"")+s.join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`)}catch{}}var _i=
|
|
6
|
+
`)}catch{}}var _i=G(()=>{Js()});import nm from"node:fs/promises";import TN from"node:os";import ou from"node:path";function EN(){let e=process.env.CODEX_HOME||ou.join(TN.homedir(),".codex");return ou.join(e,"config.toml")}function Gk(e){return`[projects."${e}"]`}function _N(e,t){let n=Gk(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 Ys(e){if(!e||typeof e!="string")return;let t=ou.resolve(e),n=EN(),r="";try{r=await nm.readFile(n,"utf8")}catch{}if(_N(r,t))return;let o=Gk(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 nm.mkdir(ou.dirname(n),{recursive:!0}),await nm.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var su=
|
|
14
|
-
`,"utf8")}var au=q(()=>{});import om from"node:fs/promises";import qr from"node:path";import{execFile as IN}from"node:child_process";import{promisify as $N}from"node:util";async function PN(e){try{return(await om.stat(qr.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function DN(e){let t=i=>om.readFile(qr.join(e,".git",i),"utf8").then(c=>c.trim()).catch(()=>null),r=(await t("refs/remotes/origin/HEAD"))?.match(/^ref:\s*refs\/remotes\/origin\/(.+)/);if(r)return r[1].trim();let o;for(let i of["main","master"])if(await t(`refs/heads/${i}`)||(o??=await t("packed-refs")||"",new RegExp(`^[0-9a-f]{40} refs/heads/${i}$`,"m").test(o)))return i;let a=(await t("HEAD"))?.match(/^ref:\s*refs\/heads\/(.+)/);return a?a[1].trim():null}async function fr(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await PN(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:qr.basename(o),branch:await DN(o)});return}if(s>=CN)return;let i;try{i=await om.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||NN.has(c.name)||await t(qr.join(o,c.name),s+1)}for(let o of MN)await t(qr.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=await Me(o.path),a=s?.toLowerCase();if(a){if(n.has(a))continue;n.add(a),o.slug=s}r.push(o)}return r.sort((o,s)=>o.name.localeCompare(s.name))}function BN(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function Me(e){if(!e)return null;if(rm.has(e))return rm.get(e);let t=null;try{let{stdout:n}=await RN("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=BN(n)}catch{t=null}return rm.set(e,t),t}async function Zs(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await fr())if((await Me(n.path))?.toLowerCase()===t)return n.path;return null}var RN,Vk,ON,MN,CN,NN,rm,Gr=q(()=>{RN=$N(IN),Vk=process.env.HOME||"/home/globo",ON=qr.join(process.env.BOSTRAT_STATE_DIR||qr.join(Vk,".bostrat"),"repos"),MN=[...(process.env.BOSTRAT_REPOS_ROOT||qr.join(Vk,"Documents")).split(","),ON].map(e=>e.trim()).filter(Boolean),CN=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,NN=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);rm=new Map});function Qs(e=Date.now()){if(qo)return{tripped:!1,holdMs:1/0};if(Wo>e)return{tripped:!1,holdMs:Wo-e};if((!Ri||e-Ri>=iu.windowMs)&&(Ri=e,$i=0),$i++,$i<iu.trip)return{tripped:!1,holdMs:0};cu++;let t=Math.min(iu.minMs*2**(cu-1),iu.maxMs);return Wo=e+t,$i=0,Ri=0,{tripped:!0,holdMs:t}}function zk(){$i=0,Ri=0,cu=0,Wo=0}function Oi(e=Date.now()){return qo?1/0:Wo>e?Wo-e:0}function ea(e=Date.now()){return Oi(e)>0}function sm(e={}){qo={at:Date.now(),...e},Wo=1/0}function lu(){return qo}function Jk(e=Date.now()){return{held:ea(e),holdMs:qo?null:Oi(e),holds:cu,removed:qo?{...qo}:null}}var LN,iu,$i,Ri,cu,Wo,qo,am=q(()=>{LN={trip:3,windowMs:3e5,minMs:3e5,maxMs:216e5},iu={...LN},$i=0,Ri=0,cu=0,Wo=0,qo=null});import jN from"node:fs";import Yk from"node:fs/promises";import Mi from"node:path";import FN from"node:os";import{fileURLToPath as UN}from"node:url";import{execFile as HN}from"node:child_process";import{promisify as WN}from"node:util";function De(e=process.env){let t=e.BOSTRAT_PLANE_URL||"";if(!t)return null;try{let n=new URL(t);return n.protocol=n.protocol==="ws:"?"http:":"https:",n.origin}catch{return null}}function uu(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}function t0(e){return e?.status===404&&/not granted/i.test(String(e?.message||""))}async function ta(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!Zk.test(n))throw Go("repo must be owner/name",400);let r=im.get(n);if(r&&r.expiresAt-Date.now()>KN)return r;if(!uu(t))throw Go("node is not plane-linked \u2014 GitHub auth unavailable",503);if(ea()){if(r&&r.expiresAt>Date.now())return r;throw Go(lu()?"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(`${De(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:e0(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw Go(`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)&&Qs(),o.status>=500&&r&&r.expiresAt>Date.now())return r;throw Go(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 im.set(n,a),a}async function Ci({env:e=process.env,force:t=!1}={}){if(!uu(e))return{configured:!1,repos:[],username:null};if(!t&&Kr&&Date.now()-Kr.at<VN)return Kr.data;if(ea())return Kr?.data??{configured:!0,repos:[],username:null,error:"plane refused this node's token"};try{let n=await Qk(`${De(e)}/v1/github/repos`,{headers:e0(e)});if((n.status===401||n.status===403||n.status===410)&&Qs(),!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Kr={at:Date.now(),data:r},r}catch(n){return Kr?Kr.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function n0(){im.clear(),Kr=null}async function Bn({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await Me(e):null);if(!r)return{};let{token:o}=await ta(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function cm({env:e=process.env}={}){return(await Ci({env:e})).username??null}function XN(e){let[t,n]=String(e).split("/");return Mi.join(GN,t,n)}async function lm(e,{env:t=process.env}={}){let n=(...r)=>Xk("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${JN()}" ${YN(t)}`),await n("config","credential.useHttpPath","true")}async function du(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!Zk.test(String(e||"")))throw Go(`bad repo slug "${e}"`,400);let r=XN(e);if(jN.existsSync(Mi.join(r,".git")))return r;let{token:o}=await ta(e,{env:t});await Yk.mkdir(Mi.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Xk("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:zN,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Yk.rm(r,{recursive:!0,force:!0}).catch(()=>{}),Go(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await lm(r,{env:t}),r}var Xk,qN,GN,Zk,KN,VN,zN,Qk,im,Kr,e0,Go,JN,YN,xt=q(()=>{Gr();am();Xk=WN(HN),qN=process.env.BOSTRAT_STATE_DIR||Mi.join(process.env.HOME||FN.tmpdir(),".bostrat"),GN=Mi.join(qN,"repos"),Zk=/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?\/[A-Za-z0-9._-]{1,100}$/,KN=5*6e4,VN=6e4,zN=10*6e4,Qk=fetch,im=new Map,Kr=null;e0=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Go=(e,t)=>Object.assign(new Error(e),{status:t});JN=()=>UN(new URL("../bin/bostrat-git-credential",import.meta.url)),YN=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function o0(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}async function a0(e,t,n,r=r0){let o=De(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await QN(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function i0(e,{env:t=process.env,force:n=!1,timeoutMs:r=r0}={}){if(!o0(t))return{facts:[],source:"unconfigured"};let o=um.get(e);if(!n&&o&&Date.now()-o.at<ZN)return{facts:o.facts,source:"cache"};try{let s=await a0(`/v1/memory/${encodeURIComponent(e)}`,{headers:s0(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 um.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 pu(e,t,{env:n=process.env}={}){if(!o0(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await a0(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...s0(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 um.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 ZN,r0,um,QN,s0,c0=q(()=>{xt();ZN=6e4,r0=5e3,um=new Map,QN=(...e)=>fetch(...e);s0=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import dm from"node:fs/promises";import na from"node:path";import eP from"node:os";import{EventEmitter as tP}from"node:events";import{randomUUID as nP}from"node:crypto";function ra(e){return e?.cwd?na.join(e.cwd,".bostrat","events"):na.join(pm,"events")}async function aP(e,t){if(!fu.has(t)){let o=(await K(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);fu.set(t,o)}let n=fu.get(t)+1;return fu.set(t,n),n}function Ni(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||!rP.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!oP.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!sP.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?na.join(pm,"events"):ra(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=Ni(u,Number(t.payload?.ts??t.ts)||Date.now())}let s=await aP(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||nP(),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 u0(c)&&(c=d0(c),l=JSON.parse(c)),await dm.mkdir(r,{recursive:!0}),await dm.appendFile(l0(r,n),c+`
|
|
13
|
+
`:"")}try{await nm.mkdir(ou.dirname(n),{recursive:!0}),await nm.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var su=G(()=>{});import Kk from"node:fs/promises";import IN from"node:os";import Ii from"node:path";function $N(e){let n=Ii.resolve(e).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||Ii.join(IN.homedir(),".cursor");return Ii.join(r,"projects",n)}async function Xs(e){if(!e||typeof e!="string")return;let t=Ii.resolve(e),n=$N(t),r=Ii.join(n,".workspace-trusted");await Kk.mkdir(n,{recursive:!0}),await Kk.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:t},null,2)}
|
|
14
|
+
`,"utf8")}var au=G(()=>{});import om from"node:fs/promises";import qr from"node:path";import{execFile as RN}from"node:child_process";import{promisify as ON}from"node:util";async function BN(e){try{return(await om.stat(qr.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function LN(e){let t=i=>om.readFile(qr.join(e,".git",i),"utf8").then(c=>c.trim()).catch(()=>null),r=(await t("refs/remotes/origin/HEAD"))?.match(/^ref:\s*refs\/remotes\/origin\/(.+)/);if(r)return r[1].trim();let o;for(let i of["main","master"])if(await t(`refs/heads/${i}`)||(o??=await t("packed-refs")||"",new RegExp(`^[0-9a-f]{40} refs/heads/${i}$`,"m").test(o)))return i;let a=(await t("HEAD"))?.match(/^ref:\s*refs\/heads\/(.+)/);return a?a[1].trim():null}async function fr(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await BN(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:qr.basename(o),branch:await LN(o)});return}if(s>=PN)return;let i;try{i=await om.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||DN.has(c.name)||await t(qr.join(o,c.name),s+1)}for(let o of NN)await t(qr.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=await Me(o.path),a=s?.toLowerCase();if(a){if(n.has(a))continue;n.add(a),o.slug=s}r.push(o)}return r.sort((o,s)=>o.name.localeCompare(s.name))}function jN(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function Me(e){if(!e)return null;if(rm.has(e))return rm.get(e);let t=null;try{let{stdout:n}=await MN("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=jN(n)}catch{t=null}return rm.set(e,t),t}async function Zs(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await fr())if((await Me(n.path))?.toLowerCase()===t)return n.path;return null}var MN,Vk,CN,NN,PN,DN,rm,Gr=G(()=>{MN=ON(RN),Vk=process.env.HOME||"/home/globo",CN=qr.join(process.env.BOSTRAT_STATE_DIR||qr.join(Vk,".bostrat"),"repos"),NN=[...(process.env.BOSTRAT_REPOS_ROOT||qr.join(Vk,"Documents")).split(","),CN].map(e=>e.trim()).filter(Boolean),PN=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,DN=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);rm=new Map});function Qs(e=Date.now()){if(qo)return{tripped:!1,holdMs:1/0};if(Wo>e)return{tripped:!1,holdMs:Wo-e};if((!Ri||e-Ri>=iu.windowMs)&&(Ri=e,$i=0),$i++,$i<iu.trip)return{tripped:!1,holdMs:0};cu++;let t=Math.min(iu.minMs*2**(cu-1),iu.maxMs);return Wo=e+t,$i=0,Ri=0,{tripped:!0,holdMs:t}}function zk(){$i=0,Ri=0,cu=0,Wo=0}function Oi(e=Date.now()){return qo?1/0:Wo>e?Wo-e:0}function ea(e=Date.now()){return Oi(e)>0}function sm(e={}){qo={at:Date.now(),...e},Wo=1/0}function lu(){return qo}function Jk(e=Date.now()){return{held:ea(e),holdMs:qo?null:Oi(e),holds:cu,removed:qo?{...qo}:null}}var FN,iu,$i,Ri,cu,Wo,qo,am=G(()=>{FN={trip:3,windowMs:3e5,minMs:3e5,maxMs:216e5},iu={...FN},$i=0,Ri=0,cu=0,Wo=0,qo=null});import UN from"node:fs";import Yk from"node:fs/promises";import Mi from"node:path";import HN from"node:os";import{fileURLToPath as WN}from"node:url";import{execFile as qN}from"node:child_process";import{promisify as GN}from"node:util";function De(e=process.env){let t=e.BOSTRAT_PLANE_URL||"";if(!t)return null;try{let n=new URL(t);return n.protocol=n.protocol==="ws:"?"http:":"https:",n.origin}catch{return null}}function uu(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}function t0(e){return e?.status===404&&/not granted/i.test(String(e?.message||""))}async function ta(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!Zk.test(n))throw Go("repo must be owner/name",400);let r=im.get(n);if(r&&r.expiresAt-Date.now()>zN)return r;if(!uu(t))throw Go("node is not plane-linked \u2014 GitHub auth unavailable",503);if(ea()){if(r&&r.expiresAt>Date.now())return r;throw Go(lu()?"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(`${De(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:e0(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw Go(`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)&&Qs(),o.status>=500&&r&&r.expiresAt>Date.now())return r;throw Go(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 im.set(n,a),a}async function Ci({env:e=process.env,force:t=!1}={}){if(!uu(e))return{configured:!1,repos:[],username:null};if(!t&&Kr&&Date.now()-Kr.at<JN)return Kr.data;if(ea())return Kr?.data??{configured:!0,repos:[],username:null,error:"plane refused this node's token"};try{let n=await Qk(`${De(e)}/v1/github/repos`,{headers:e0(e)});if((n.status===401||n.status===403||n.status===410)&&Qs(),!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Kr={at:Date.now(),data:r},r}catch(n){return Kr?Kr.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function n0(){im.clear(),Kr=null}async function Bn({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await Me(e):null);if(!r)return{};let{token:o}=await ta(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function cm({env:e=process.env}={}){return(await Ci({env:e})).username??null}function QN(e){let[t,n]=String(e).split("/");return Mi.join(VN,t,n)}async function lm(e,{env:t=process.env}={}){let n=(...r)=>Xk("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${XN()}" ${ZN(t)}`),await n("config","credential.useHttpPath","true")}async function du(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!Zk.test(String(e||"")))throw Go(`bad repo slug "${e}"`,400);let r=QN(e);if(UN.existsSync(Mi.join(r,".git")))return r;let{token:o}=await ta(e,{env:t});await Yk.mkdir(Mi.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Xk("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:YN,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Yk.rm(r,{recursive:!0,force:!0}).catch(()=>{}),Go(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await lm(r,{env:t}),r}var Xk,KN,VN,Zk,zN,JN,YN,Qk,im,Kr,e0,Go,XN,ZN,xt=G(()=>{Gr();am();Xk=GN(qN),KN=process.env.BOSTRAT_STATE_DIR||Mi.join(process.env.HOME||HN.tmpdir(),".bostrat"),VN=Mi.join(KN,"repos"),Zk=/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?\/[A-Za-z0-9._-]{1,100}$/,zN=5*6e4,JN=6e4,YN=10*6e4,Qk=fetch,im=new Map,Kr=null;e0=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Go=(e,t)=>Object.assign(new Error(e),{status:t});XN=()=>WN(new URL("../bin/bostrat-git-credential",import.meta.url)),ZN=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function o0(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}async function a0(e,t,n,r=r0){let o=De(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await tP(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function i0(e,{env:t=process.env,force:n=!1,timeoutMs:r=r0}={}){if(!o0(t))return{facts:[],source:"unconfigured"};let o=um.get(e);if(!n&&o&&Date.now()-o.at<eP)return{facts:o.facts,source:"cache"};try{let s=await a0(`/v1/memory/${encodeURIComponent(e)}`,{headers:s0(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 um.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 pu(e,t,{env:n=process.env}={}){if(!o0(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await a0(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...s0(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 um.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 eP,r0,um,tP,s0,c0=G(()=>{xt();eP=6e4,r0=5e3,um=new Map,tP=(...e)=>fetch(...e);s0=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import dm from"node:fs/promises";import na from"node:path";import nP from"node:os";import{EventEmitter as rP}from"node:events";import{randomUUID as oP}from"node:crypto";function ra(e){return e?.cwd?na.join(e.cwd,".bostrat","events"):na.join(pm,"events")}async function cP(e,t){if(!fu.has(t)){let o=(await K(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);fu.set(t,o)}let n=fu.get(t)+1;return fu.set(t,n),n}function Ni(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||!sP.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!aP.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!iP.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?na.join(pm,"events"):ra(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=Ni(u,Number(t.payload?.ts??t.ts)||Date.now())}let s=await cP(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||oP(),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 u0(c)&&(c=d0(c),l=JSON.parse(c)),await dm.mkdir(r,{recursive:!0}),await dm.appendFile(l0(r,n),c+`
|
|
15
15
|
`),Ze.emit("event",l),Ze.emit(`track:${n}`,l),l}async function K(e,t){let n,r;typeof e=="string"&&t?(n=e,r=t):(r=typeof e=="string"?e:e?.id,n=typeof e=="string"?na.join(pm,"events"):ra(e));let o;try{o=await dm.readFile(l0(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
16
|
-
`))if(a.trim())try{s.push(JSON.parse(u0(a)?d0(a):a))}catch{}return s}function p0(e,t){let n=`track:${e}`;return Ze.on(n,t),()=>Ze.off(n,t)}var
|
|
16
|
+
`))if(a.trim())try{s.push(JSON.parse(u0(a)?d0(a):a))}catch{}return s}function p0(e,t){let n=`track:${e}`;return Ze.on(n,t),()=>Ze.off(n,t)}var sP,aP,iP,Ze,pm,l0,fu,lP,u0,d0,be=G(()=>{sP=new Set(["session.ready","turn.user","turn.agent","turn.retracted","peer.message","provider.error","delivery.queued","delivery.settled","turn.interrupted","tool.called","tool.result","prompt.raised","prompt.answered","prompt.dismissed","prompt.autoRejected","box.fullscreen.detected","box.fullscreen.answered","context.usage","cost.recorded","memory.seed","memory.harvested","handoff.artifact","code.pushed","code.committed","code.rework","rework.cause.offered","rework.cause.answered","rework.cause.retracted","handoff.started","handoff.step","handoff.done","handoff.failed","subtask.delegated","subtask.merged","job.started","job.stalled","job.stopped","pr.state.changed","pr.offered","pr.offer.answered","pr.exists.offered","pr.exists.answered","box.state.changed","view.state.changed","view.spinup.failed","mock.state.changed","flow.state.changed","runbook.state.changed","ticket.linked","ticket.sync.failed","ticket.move.offered","ticket.move.answered","ticket.state.changed","ticket.commented","ticket.flow.changed","ticket.create.offered","ticket.create.answered","ticket.created","loop.create.offered","loop.create.answered","loop.created","loop.update.offered","loop.update.answered","loop.updated","loop.delete.offered","loop.delete.answered","loop.deleted","loop.paused","followup.armed","followup.armed.cancelled","followup.offered","followup.answered","followup.create.offered","followup.create.answered","followup.created","planning.change.offered","planning.change.answered","planning.changed","notify.raised","usage.cap.hit","usage.cap.warning","provider.login.started","provider.connected","track.summary.recorded","track.transferred","track.recovered","track.phase.changed","track.operation.changed","track.obligations.changed","track.circuit.tagged","blueprint.item.bound","decision.recorded","changelog.published","stall.detected","error"]),aP=new Set(["user","agent","orchestrator","system","peer"]),iP=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor","cli"]),Ze=new rP;Ze.setMaxListeners(0);pm=process.env.BOSTRAT_STATE_DIR||na.join(process.env.HOME||nP.tmpdir(),".bostrat");l0=(e,t)=>na.join(e,`${t}.ndjson`),fu=new Map;lP=/(?<!\\)((?:\\\\)*)\\u0000/g,u0=e=>e.includes("\\u0000"),d0=e=>e.replace(lP,"$1\\ufffd")});import ue from"node:fs/promises";import uP from"node:os";import Te from"node:path";import{execFile as dP}from"node:child_process";import{promisify as pP}from"node:util";function hu(){return Te.join(wu,"memory","repos")}function Ko(e){return e===mr?Te.join(wu,"memory",mr):Te.join(hu(),e)}async function Di(e){try{return JSON.parse(await ue.readFile(Te.join(e,b0),"utf8"))}catch{return{}}}async function mm(e,t){let n=await Di(e);for(let[r,o]of t)n[r]=Math.max(Number(n[r])||0,o);await ue.mkdir(e,{recursive:!0}),await ue.writeFile(Te.join(e,b0),JSON.stringify(n))}function m0(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 h0(e){try{let{stdout:t}=await fP("git",["-C",e,"config","--get","remote.origin.url"]);return t.trim()||null}catch{return null}}async function hm(e){if(!e)return[];let t=m0(await h0(e));if(t)return[t];let n=[],r=[];try{r=await ue.readdir(e,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=m0(await h0(Te.join(e,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function gu(e){let t=/^---\n([\s\S]*?)\n---\n?/.exec(e);return t?{fm:t[1],body:e.slice(t[0].length)}:{fm:null,body:e}}function Vr(e,t){let n=new RegExp(`^\\s*${t}:\\s*(.+?)\\s*$`,"m").exec(e||"");return n?n[1].replace(/^"|"$/g,""):null}async function Ln(e,t=null){let n=t??await ue.readFile(e,"utf8").catch(()=>""),{fm:r}=gu(n),o=Vr(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await ue.stat(e)).mtimeMs}catch{return 0}}function k0(e,t){let n=Vr(e,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===mr){let o=Vr(e,"trust");if(!o||/^operator/i.test(o))return mr}return t}function wm(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=sa(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=sa(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 x0(e,t){let n=Vr(t,"name")||e.replace(/\.md$/,""),r=Vr(t,"description")||"";return`- [${n}](${e})${r?` \u2014 ${r}`:""}`}async function Bi(e){let t=[];try{t=await ue.readdir(e,{withFileTypes:!0})}catch{return[]}return t.filter(n=>n.isFile()&&n.name.endsWith(".md")&&n.name!=="MEMORY.md").map(n=>n.name)}async function v0(e,t){let n=Te.join(e,".bostrat","memory"),r=await Bi(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=w0.get(e)??0,s=Date.now(),a=new Map,i=new Map,c=0,l=new Map;for(let d of r){let p=Te.join(n,d);if(o)try{if((await ue.stat(p)).mtimeMs<o-
|
|
21
|
+
`)}function x0(e,t){let n=Vr(t,"name")||e.replace(/\.md$/,""),r=Vr(t,"description")||"";return`- [${n}](${e})${r?` \u2014 ${r}`:""}`}async function Bi(e){let t=[];try{t=await ue.readdir(e,{withFileTypes:!0})}catch{return[]}return t.filter(n=>n.isFile()&&n.name.endsWith(".md")&&n.name!=="MEMORY.md").map(n=>n.name)}async function v0(e,t){let n=Te.join(e,".bostrat","memory"),r=await Bi(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=w0.get(e)??0,s=Date.now(),a=new Map,i=new Map,c=0,l=new Map;for(let d of r){let p=Te.join(n,d);if(o)try{if((await ue.stat(p)).mtimeMs<o-hP)continue}catch{continue}let f;try{f=await ue.readFile(p,"utf8")}catch{continue}let{fm:w}=gu(f),h=k0(w,t);if(!h)continue;let y=Ko(h),k=Te.join(y,d),g=await Ln(p,f);if(l.has(y)||l.set(y,await Di(y)),(l.get(y)[d]||0)>=g)continue;let x=await Ln(k).catch(()=>0);if(await ue.access(k).then(()=>!0).catch(()=>!1)&&x>=g)continue;await ue.mkdir(y,{recursive:!0}),await ue.writeFile(k,f),c++;let _=a.get(y);_||a.set(y,_={won:new Set,generated:new Map}),_.won.add(d),_.generated.set(d,x0(d,w)),i.has(h)||i.set(h,new Set),i.get(h).add(d)}let u=await ue.readFile(Te.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[d,p]of a){let f=Te.join(d,"MEMORY.md"),w=await ue.readFile(f,"utf8").catch(()=>"");await ue.writeFile(f,wm(w,u,p.won,p.generated))}return w0.set(e,s),{harvested:c,changedByKey:i}}async function S0(e){let t=0,n=new Map;for(let r of e||[]){if(!r||!await ue.access(Te.join(r,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[s]=await hm(r),a=await v0(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 wP(){let e=Te.join(wu,"workspaces"),t=[];try{t=await ue.readdir(e,{withFileTypes:!0})}catch{return{harvested:0,changedByKey:new Map}}return S0(t.filter(n=>n.isDirectory()).map(n=>Te.join(e,n.name)))}async function bP(e,t){let n=Ko(e),r=await Di(n),o=new Set,s=new Map,a=[],i=0;for(let c of t||[]){let l=String(c?.file||"");if(!gP.test(l)||l==="MEMORY.md")continue;let u=Te.join(n,l),d=await ue.access(u).then(()=>!0).catch(()=>!1),p=d?await Ln(u):0,f=Number(c.modified)||0;if(c.deleted){f>p&&(d&&await ue.rm(u,{force:!0}),await mm(n,[[l,f]]),d&&(a.push(l),i++));continue}d&&p>=f||(r[l]||0)>=f||(await ue.mkdir(n,{recursive:!0}),await ue.writeFile(u,String(c.content??"")),o.add(l),typeof c.indexLine=="string"&&c.indexLine.trim()&&s.set(l,c.indexLine),i++)}if(o.size||a.length){let c=Te.join(n,"MEMORY.md"),l=await ue.readFile(c,"utf8").catch(()=>""),u=wm(l,[...s.values()].join(`
|
|
22
22
|
`),o);if(a.length){let d=new Set(a);u=u.split(`
|
|
23
23
|
`).filter(p=>!d.has(sa(p))).join(`
|
|
24
|
-
`)}await ue.mkdir(n,{recursive:!0}),await ue.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function yu(e,t=null){let n=Ko(e),r=t?.length?t:await Bi(n);if(!r.length)return{pushed:0,source:"empty"};let o=
|
|
24
|
+
`)}await ue.mkdir(n,{recursive:!0}),await ue.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function yu(e,t=null){let n=Ko(e),r=t?.length?t:await Bi(n);if(!r.length)return{pushed:0,source:"empty"};let o=yP(await ue.readFile(Te.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=Te.join(n,a),c;try{c=await ue.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await Ln(i,c),indexLine:o.get(a)??null})}return pu(e,s)}async function kP(e,{timeoutMs:t}={}){let{facts:n,source:r}=await i0(e,t?{timeoutMs:t}:{});n.length?await bP(e,n):r==="plane"&&await yu(e)}function xP(e){let t=e.split(`
|
|
25
25
|
`);t.length>f0&&(t=t.slice(0,f0));let n=t.join(`
|
|
26
|
-
`);for(;Buffer.byteLength(n,"utf8")>
|
|
27
|
-
`);return n}async function
|
|
26
|
+
`);for(;Buffer.byteLength(n,"utf8")>mP&&t.length;)t.pop(),n=t.join(`
|
|
27
|
+
`);return n}async function SP(e,t){if(!e||!t?.length)return{materialized:!1,reason:"no-repo-key"};let n=Te.join(e,".bostrat","memory"),r=0,o=0,s=[];for(let u of t){let d=Ko(u),p=await Di(d);for(let[y,k]of Object.entries(p)){let g=Te.join(n,y);await ue.access(g).then(()=>!0).catch(()=>!1)&&await Ln(g)<=k&&(await ue.rm(g,{force:!0}),o++)}let f=await Bi(d);if(!f.length)continue;await ue.mkdir(n,{recursive:!0});for(let y of f){let k=Te.join(d,y),g=Te.join(n,y),x=await Ln(k),S=await Ln(g).catch(()=>0);await ue.access(g).then(()=>!0).catch(()=>!1)&&S>=x||(await ue.copyFile(k,g),r++)}let w=await ue.readFile(Te.join(d,"MEMORY.md"),"utf8").catch(()=>""),h=u===mr?"## operator memory":`## repo: ${u}`;w.trim()&&s.push(t.length>1?`${h}
|
|
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=xP(i);await ue.mkdir(n,{recursive:!0}),await ue.writeFile(Te.join(n,"MEMORY.md"),c);let l=Te.join(n,"claude");return await ue.mkdir(l,{recursive:!0}),await ue.writeFile(Te.join(l,"MEMORY.md"),vP,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function A0(e){try{let t=await hm(e);if(!t.length)return{materialized:!1,reason:"no-repo-key"};let n=[...t,mr];return await Promise.all(n.map(r=>kP(r,{timeoutMs:AP}))),await SP(e,n)}catch(t){return{materialized:!1,reason:"error",error:t?.message||String(t)}}}function T0(){return{...ct,pending:oa.size,queued:fm.size}}function E0(e){if(mu.has(e))return mu.get(e);fm.add(e);let t=g0.then(async()=>{fm.delete(e),mu.delete(e);let n=await gm(e);return ct.runs++,ct.lastRunAt=Date.now(),n.error?(ct.errors++,ct.lastError=n.error):ct.harvested+=n.harvested||0,n}).catch(n=>({harvested:0,error:n?.message||String(n)}));return mu.set(e,t),g0=t.then(()=>{}),t}function bu(e,{delayMs:t=TP}={}){if(!e||typeof e!="string")return;ct.scheduled++;let n=oa.get(e);n&&clearTimeout(n);let r=setTimeout(()=>{oa.delete(e),E0(e)},t);r.unref?.(),oa.set(e,r)}async function ku(e,{timeoutMs:t=3e3}={}){if(!e)return{harvested:0,skipped:"no-cwd"};if(!await ue.access(Te.join(e,".bostrat","memory")).then(()=>!0,()=>!1))return{harvested:0,skipped:"no-memory-dir"};let r=oa.get(e);r&&(clearTimeout(r),oa.delete(e));let o,s=new Promise(a=>{o=setTimeout(()=>a({harvested:0,timedOut:!0}),t),o.unref?.()});try{return await Promise.race([E0(e),s])}finally{clearTimeout(o)}}async function _0(e){return Pi||(Pi=(async()=>{try{y0||(y0=!0,await _P());let{harvested:t,changedByKey:n}=await S0(e);for(let[r,o]of n)await yu(r,[...o]);return ct.reconciles++,ct.lastReconcileAt=Date.now(),ct.harvested+=t,{harvested:t,keys:[...n.keys()]}}catch(t){return ct.errors++,ct.lastError=t?.message||String(t),{harvested:0,error:ct.lastError}}finally{Pi=null}})(),Pi)}function EP(){return Te.join(wu,"memory",".full-sweep-done")}async function I0(){let e=EP();try{let t=JSON.parse(await ue.readFile(e,"utf8"));return ct.backfill={at:Number(t?.at)||0,harvested:Number(t?.harvested)||0,prior:!0},{ran:!1,...ct.backfill}}catch{}try{let{harvested:t,changedByKey:n}=await wP();for(let[o,s]of n)await yu(o,[...s]);let r={at:Date.now(),harvested:t};return await ue.mkdir(Te.dirname(e),{recursive:!0}),await ue.writeFile(e,JSON.stringify(r)),ct.backfill=r,{ran:!0,...r}}catch(t){return ct.errors++,ct.lastError=t?.message||String(t),{ran:!1,error:ct.lastError}}}async function _P(){let e=0,t=[];try{t=await ue.readdir(hu(),{withFileTypes:!0})}catch{return{moved:e}}for(let n of t){if(!n.isDirectory())continue;let r=Te.join(hu(),n.name),o=Ko(mr),s=new Set,a=new Map;for(let f of await Bi(r)){let w=Te.join(r,f),h;try{h=await ue.readFile(w,"utf8")}catch{continue}let{fm:y}=gu(h);if(k0(y,n.name)!==mr)continue;let k=Te.join(o,f),g=await Ln(k).catch(()=>0),x=await Ln(w,h);await ue.mkdir(o,{recursive:!0}),x>g&&await ue.writeFile(k,h),await ue.rm(w,{force:!0}),s.add(f),a.set(f,x0(f,y)),e++}if(!s.size)continue;let i=Date.now();await mm(r,[...s].map(f=>[f,i])),await pu(n.name,[...s].map(f=>({file:f,deleted:!0,modified:i})));let c=Te.join(r,"MEMORY.md"),l=await ue.readFile(c,"utf8").catch(()=>""),u=l.split(`
|
|
33
33
|
`).filter(f=>!s.has(sa(f))).join(`
|
|
34
34
|
`);await ue.writeFile(c,u).catch(()=>{});let d=Te.join(o,"MEMORY.md"),p=await ue.readFile(d,"utf8").catch(()=>"");await ue.writeFile(d,wm(p,l,s,a))}return{moved:e}}async function $0(){let e=[],t=[mr];try{for(let n of await ue.readdir(hu(),{withFileTypes:!0}))n.isDirectory()&&t.push(n.name)}catch{}for(let n of t){let r=Ko(n),o=await Bi(r),s=await Di(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await ue.readFile(Te.join(r,i),"utf8").catch(()=>""),{fm:l}=gu(c);a.push({file:i,name:Vr(l,"name")||i.replace(/\.md$/,""),description:Vr(l,"description")||"",scope:Vr(l,"scope")||null,modified:await Ln(Te.join(r,i),c)})}e.push({key:n,facts:a,tombstones:Object.keys(s).length})}return e}async function R0(e,t){if(!/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/.test(t)||t==="MEMORY.md")throw new Error("bad fact filename");let n=Ko(e),r=Te.join(n,t);if(!await ue.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${t} not found in ${e}`);let s=Date.now();await ue.rm(r,{force:!0}),await mm(n,[[t,s]]);let a=Te.join(n,"MEMORY.md"),i=await ue.readFile(a,"utf8").catch(()=>"");await ue.writeFile(a,i.split(`
|
|
35
35
|
`).filter(l=>sa(l)!==t).join(`
|
|
36
|
-
`)).catch(()=>{});let c=await pu(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function gm(e){try{let t=await hm(e),{harvested:n,changedByKey:r}=await v0(e,t[0]||null);for(let[o,s]of r)await yu(o,[...s]);return n>0&&await
|
|
37
|
-
`)){let r=sa(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};
|
|
36
|
+
`)).catch(()=>{});let c=await pu(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function gm(e){try{let t=await hm(e),{harvested:n,changedByKey:r}=await v0(e,t[0]||null);for(let[o,s]of r)await yu(o,[...s]);return n>0&&await $P(e,r).catch(()=>{}),{harvested:n}}catch(t){return{harvested:0,error:t?.message||String(t)}}}async function $P(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,IP)}}await N({id:n,cwd:e},{type:"memory.harvested",actor:"orchestrator",payload:{harvested:o,keys:r,ts:Date.now()}})}var fP,wu,f0,mP,mr,b0,sa,w0,hP,gP,yP,vP,AP,TP,oa,fm,g0,ct,mu,y0,Pi,IP,Li=G(()=>{c0();be();fP=pP(dP),wu=process.env.BOSTRAT_STATE_DIR||Te.join(process.env.HOME||uP.tmpdir(),".bostrat"),f0=200,mP=25*1024;mr="operator";b0=".tombstones.json";sa=e=>/\]\(([^)]+\.md)\)/.exec(e)?.[1]||null;w0=new Map,hP=2e3;gP=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,yP=e=>{let t=new Map;for(let n of(e||"").split(`
|
|
37
|
+
`)){let r=sa(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};vP=`# 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 xu.rename(r,e)}catch(o){throw await xu.unlink(r).catch(()=>{}),o}}var
|
|
42
|
+
`;AP=Number(process.env.BOSTRAT_MEMORY_SPAWN_PULL_MS)||1500;TP=Number(process.env.BOSTRAT_MEMORY_HARVEST_DEBOUNCE_MS)||2e3,oa=new Map,fm=new Set,g0=Promise.resolve(),ct={scheduled:0,runs:0,harvested:0,errors:0,lastRunAt:null,lastError:null,reconciles:0,lastReconcileAt:null,backfill:null};mu=new Map;y0=!1,Pi=null;IP=20});import xu from"node:fs/promises";import RP from"node:path";async function Bt(e,t,{mode:n}={}){await xu.mkdir(RP.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${++OP}.tmp`;try{await xu.writeFile(r,JSON.stringify(t,null,2)+`
|
|
43
|
+
`,n!=null?{mode:n}:void 0),await xu.rename(r,e)}catch(o){throw await xu.unlink(r).catch(()=>{}),o}}var OP,zr=G(()=>{OP=0});import P0 from"node:fs/promises";import D0 from"node:os";import B0 from"node:path";function Ge(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Ui(e,t={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),MP);try{return await BP(`${De(n)}${e}`,{...t,signal:r.signal})}finally{clearTimeout(o)}}async function U0(){return Su||(ym||=P0.readFile(L0(),"utf8").then(e=>JSON.parse(e)).catch(()=>({ops:[]})).then(e=>Su={ops:Array.isArray(e?.ops)?e.ops:[],parked:Array.isArray(e?.parked)?e.parked:[]}).finally(()=>ym=null),ym)}async function Au(){await Bt(L0(),Su).catch(()=>{})}function C0(e){return e.op==="blob"?`blob ${e.family} ${e.id} ${e.v} ${e.kind}`:`${e.op} ${e.family} ${e.id}`}async function km(e){let t=await U0(),n=C0(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=>C0(o)===n);r>=0?t.ops[r]=e:t.ops.push(e)}t.ops.length>O0&&(t.ops=t.ops.slice(-O0)),await Au()}function N0(){vu||(vu=setTimeout(()=>{vu=null,aa().catch(()=>{})},LP),vu.unref?.())}function bm(e){let t=new Error(`plane ${e}`);return t.refused=!0,t}async function FP(e,t=process.env){if(e.op==="record"){let n=await Ui(`/v1/content/${encodeURIComponent(e.family)}`,{method:"POST",headers:{...Fi(t),"content-type":"application/json"},body:JSON.stringify({records:[{id:e.id,trackId:e.trackId??null,data:e.data,updatedAt:e.updatedAt,originNode:j0()}]})},t);if(!n.ok)throw bm(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 P0.readFile(e.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await Ui(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}/blob?v=${e.v}&kind=${encodeURIComponent(e.kind)}`,{method:"POST",headers:{...Fi(t),"content-type":"application/octet-stream"},body:n},t);if(!r.ok&&r.status!==400)throw bm(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 Ui(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}?deletedAt=${e.deletedAt}`,{method:"DELETE",headers:Fi(t)},t);if(!n.ok)throw bm(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function aa({env:e=process.env,log:t=console,now:n=Date.now()}={}){return Ge(e)?ji||(ji=(async()=>{let r=await U0();UP(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await FP(a,e)}catch(c){if(!c?.refused)return N0(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<NP)return N0(),await Au(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};t.error?.(`[content-plane] ALARM parking ${a.op} ${a.family}/${a.id} after ${a.refusals} refusals (${c.message}) \u2014 draining the ${r.ops.length-1} ops behind it`),r.ops.shift(),r.parked.push({...a,parkedAt:n,error:c.message}),r.parked.length>M0&&(r.parked=r.parked.slice(-M0)),s++,await Au();continue}i.rejected&&t.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await Au()}return{flushed:o,parked:s,pending:0}})().finally(()=>{ji=null}),ji):{flushed:0,source:"unconfigured"}}function UP(e,t){if(!e.parked.length||e.ops.length)return;let n=e.parked.filter(r=>t-(r.parkedAt||0)>=jP);if(n.length){e.parked=e.parked.filter(r=>!n.includes(r));for(let r of n)e.ops.push({...r,refusals:0,parkedAt:void 0,error:void 0})}}async function It(e,t,{trackId:n=null,env:r=process.env}={}){return Ge(r)?(await km({op:"record",family:e,id:t.id,trackId:n??t.trackId??null,data:t,updatedAt:Number(t.updatedAt)||Date.now()}),aa({env:r})):{queued:!1,source:"unconfigured"}}async function Jr(e,t,n,r,o,{env:s=process.env}={}){return Ge(s)?(await km({op:"blob",family:e,id:t,v:Number(n),kind:r,path:o}),aa({env:s})):{queued:!1,source:"unconfigured"}}async function yn(e,t,{deletedAt:n=Date.now(),env:r=process.env}={}){return Ge(r)?(await km({op:"delete",family:e,id:t,deletedAt:n}),aa({env:r})):{queued:!1,source:"unconfigured"}}async function dt(e,{env:t=process.env}={}){if(!Ge(t))return{records:[],source:"unconfigured"};try{let n=await Ui(`/v1/content/${encodeURIComponent(e)}?tombstones=1`,{headers:Fi(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 Hi(e,t,n,r,{env:o=process.env}={}){if(!Ge(o))return null;try{let s=await Ui(`/v1/content/${encodeURIComponent(e)}/${encodeURIComponent(t)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Fi(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 MP,CP,O0,NP,M0,PP,DP,L0,j0,F0,BP,LP,jP,Fi,Su,ym,vu,ji,Yr=G(()=>{xt();zr();MP=1e4,CP=3e4,O0=2e3,NP=5,M0=100,PP=60*6e4,DP=()=>process.env.BOSTRAT_STATE_DIR||B0.join(process.env.HOME||D0.tmpdir(),".bostrat"),L0=()=>B0.join(DP(),"content-outbox.json"),j0=()=>process.env.BOSTRAT_NODE_ID||D0.hostname(),F0=()=>j0(),BP=(...e)=>fetch(...e),LP=CP,jP=PP;Fi=(e=process.env)=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`});Su=null,ym=null,vu=null,ji=null});import xm from"node:fs/promises";import H0 from"node:path";async function W0(e,t,{box:n=null,agent:r=null}={}){if(!e||!Ge())return{pushed:!1};try{let o=Date.now();return await It("checkpoints",{id:e,trackId:e,v:o,box:n,agent:r,updatedAt:o},{trackId:e}),await Jr("checkpoints",e,o,"md",t),Wi=null,{pushed:!0,v:o}}catch{return{pushed:!1}}}async function KP(){if(Wi&&Date.now()-Wi.at<GP)return{records:Wi.records,source:"plane"};let e=await dt("checkpoints");return e.source==="plane"&&(Wi={at:Date.now(),records:e.records}),e}async function Tu(e){try{let t=qP(e);if(!t||!Ge())return{restored:!1};let n=H0.join(e,HP,WP);if(await xm.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await KP();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 Hi("checkpoints",t,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await xm.mkdir(H0.dirname(n),{recursive:!0}),await xm.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var HP,WP,qP,GP,Wi,Eu=G(()=>{Yr();HP=".bostrat/handoff",WP="latest.md",qP=e=>/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1]??null;GP=6e4,Wi=null});import qi from"node:fs/promises";import VP from"node:os";import G0 from"node:path";function Vo(e){let t=String(e??"");for(let n of zP)t=t.replace(n,"\u2039redacted\u203A");return t}function _u(){let e=process.env.BOSTRAT_HANDOFF_DEBUG;return e==="1"||e==="true"}async function JP(e){try{await qi.mkdir(vm,{recursive:!0}),await qi.appendFile(ia,`${JSON.stringify(e)}
|
|
44
44
|
`,"utf8");let n=(await qi.readFile(ia,"utf8")).split(`
|
|
45
45
|
`).filter(Boolean);n.length>q0&&await qi.writeFile(ia,`${n.slice(-q0).join(`
|
|
46
46
|
`)}
|
|
47
|
-
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${ia}: ${t?.message||t}`)}}async function re(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||_u()))try{n.paneTail=Vo((await Ne(n.sessionName,{lines:t?
|
|
48
|
-
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Vo(o.paneTail)),o}catch{return{raw:Vo(r)}}})}catch{return[]}}var
|
|
49
|
-
`,r=Sm.join(e,t),o=null;try{o=await Iu.readFile(r,"utf8")}catch{}o!==n&&await Iu.writeFile(r,n,{mode:493}),await Iu.chmod(r,493).catch(()=>{})}return e}function Y0({nodeUrl:e,dir:t=z0,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",`${Gi}=${e}`]}function X0(e=process.env){return e.BOSTRAT_NODE_URL||`http://127.0.0.1:${e.PORT||3362}`}var Gi,
|
|
47
|
+
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${ia}: ${t?.message||t}`)}}async function re(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||_u()))try{n.paneTail=Vo((await Ne(n.sessionName,{lines:t?YP:35})).slice(-XP))}catch{n.paneTail=null}(t||_u()||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 JP(n)}async function K0({limit:e=80}={}){try{return(await qi.readFile(ia,"utf8")).split(`
|
|
48
|
+
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Vo(o.paneTail)),o}catch{return{raw:Vo(r)}}})}catch{return[]}}var zP,vm,ia,q0,YP,XP,V0,bn=G(()=>{pt();zP=[/[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];vm=process.env.BOSTRAT_DEBUG_DIR||G0.join(VP.homedir(),".bostrat","debug"),ia=G0.join(vm,"handoff.jsonl"),q0=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;YP=40,XP=4e3;V0={DEBUG_DIR:vm,LOG_FILE:ia}});import Iu from"node:fs/promises";import Sm from"node:path";import ZP from"node:os";async function J0(e=z0){await Iu.mkdir(e,{recursive:!0});for(let t of eD){let n=t==="prepare-commit-msg"?tD:t==="post-commit"?nD:`${Am}exit 0
|
|
49
|
+
`,r=Sm.join(e,t),o=null;try{o=await Iu.readFile(r,"utf8")}catch{}o!==n&&await Iu.writeFile(r,n,{mode:493}),await Iu.chmod(r,493).catch(()=>{})}return e}function Y0({nodeUrl:e,dir:t=z0,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",`${Gi}=${e}`]}function X0(e=process.env){return e.BOSTRAT_NODE_URL||`http://127.0.0.1:${e.PORT||3362}`}var Gi,QP,z0,eD,Am,tD,nD,Tm=G(()=>{Js();Gi="BOSTRAT_NODE_URL",QP=process.env.BOSTRAT_STATE_DIR||Sm.join(process.env.HOME||ZP.tmpdir(),".bostrat"),z0=Sm.join(QP,"git-hooks"),eD=["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"],Am=`#!/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,7 +61,7 @@ esac
|
|
|
61
61
|
if [ -x "$orig/$hook" ] && [ "$orig/$hook" != "$0" ]; then
|
|
62
62
|
"$orig/$hook" "$@" || exit $?
|
|
63
63
|
fi
|
|
64
|
-
`,
|
|
64
|
+
`,tD=`${Am}
|
|
65
65
|
# \u2500\u2500 bostrat: append provenance trailers (Layer 0). Skipped for merge commits
|
|
66
66
|
# (nobody "wrote" a merge) and outside a box (no token / no node url).
|
|
67
67
|
[ -z "$${dr}" ] && exit 0
|
|
@@ -73,7 +73,7 @@ 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
|
+
`,nD=`${Am}
|
|
77
77
|
# \u2500\u2500 bostrat: report the commit (code.committed + rework analysis, node-side).
|
|
78
78
|
[ -z "$${dr}" ] && exit 0
|
|
79
79
|
[ -z "$${Gi}" ] && exit 0
|
|
@@ -82,57 +82,57 @@ cwd=$(git rev-parse --show-toplevel 2>/dev/null)
|
|
|
82
82
|
printf '{"sha":"%s","cwd":"%s"}' "$sha" "$cwd" | curl -sS -m 5 -X POST "$${Gi}/api/git/committed" \\
|
|
83
83
|
-H 'content-type: application/json' -H "${pr}: $${dr}" --data-binary @- >/dev/null 2>&1 || true
|
|
84
84
|
exit 0
|
|
85
|
-
`});import{execFile as Q0}from"node:child_process";import{promisify as
|
|
86
|
-
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,p,f,w]=r.split("|"),h=Number(i),y=Number(w);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(h)?n-h*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:d||null,agent:p||(l==="1"?"claude":null),alternateOn:f==="1",createdMs:Number.isFinite(y)&&y>0?y*1e3:null}}).filter(r=>!
|
|
87
|
-
`).map(n=>n.trim()).includes(e)}catch{return!1}}function
|
|
85
|
+
`});import{execFile as Q0}from"node:child_process";import{promisify as rD}from"node:util";import Ru from"node:fs";import{randomUUID as ex}from"node:crypto";function zo(e){return new Promise(t=>setTimeout(t,e))}function $u(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 tx(e){return`/tmp/tmux-${e}/default`}function oD({explicit:e,legacyExists:t,runtimeDir:n,uid:r}){if(e)return e;let o=tx(r);return t?o:n?`${n}/bostrat/tmux-default`:o}function Vi(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${Ki}=1`]}function iD(){return aD.flatMap(e=>["-e",`${e}=`])}function zi(e,t,n){let r=Jo(e),o=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(e);if(!r||o)return e;let s=`${e} --session-id ${t}`,a=/(^|\s)(--name|-n)(\s|=)/.test(e);return n&&!a&&je(n)&&(s+=` --name ${n}`),s}function Jo(e){return/(^|\/|\s)claude(\s|$)/.test(String(e||""))}function Xr(e){return/(^|\/|\s)claude(\s|$)/.test(e)?"claude":/cursor/.test(e)?"cursor":/codex/.test(e)?"codex":/gemini/.test(e)?"gemini":"agent"}function lD(e){let t=String(e||"").trim();return!t||/fake-|\.sh(\s|$)/.test(t)?!1:Jo(t)||/(^|\/|\s)codex(\s|$)/.test(t)||/cursor-agent/.test(t)||/(^|\/|\s)gemini(\s|$)/.test(t)}async function nx(e,{loginShell:t=!1,failOpen:n=!1}={}){let r=await rx(e,{loginShell:t});return r===Ou?!0:r===ca?!1:n}async function rx(e,{loginShell:t=!1,timeoutMs:n=Em}={}){if(!e)return ca;let r=t?process.env.SHELL||"/bin/sh":"sh",o=t?"-lic":"-c";try{return await Ce(r,[o,'command -v "$1" >/dev/null 2>&1',"_",e],{timeout:n}),Ou}catch(s){return s?.killed||s?.signal?la:s?.code===1||s?.code===127?ca:la}}async function Mu(e,{retries:t=uD,sleep:n=dD,probe:r=rx}={}){let o=String(e||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:Em});for(let a=0;s===la&&a<t.length;a++)await n(t[a]),s=await r(o,{timeoutMs:Em*2});return s}function se(...e){return["-S",zt,...e]}async function Zr(){if(!Ru.existsSync(zt))return!1;try{return await Ce(oe,se("list-sessions")),!0}catch(e){let t=e&&e.stderr?String(e.stderr):"";return!/no server running/i.test(t)}}async function Ji(){if(await Zr())return!0;try{Ru.mkdirSync(zt.replace(/\/[^/]+$/,""),{recursive:!0}),await Ce(oe,se("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await Zr()}function je(e){return typeof e=="string"&&pD.test(e)}async function kn(){if(!Ru.existsSync(zt))return[];try{let e="#{session_name}|#{session_windows}|#{session_attached}|#{session_activity}|#{pane_current_path}|#{@bostrat-session}|#{@bostrat-title}|#{@bostrat-sid}|#{@bostrat-agent}|#{alternate_on}|#{session_created}",{stdout:t}=await Ce(oe,se("list-sessions","-F",e)),n=Date.now();return t.trim().split(`
|
|
86
|
+
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,p,f,w]=r.split("|"),h=Number(i),y=Number(w);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(h)?n-h*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:d||null,agent:p||(l==="1"?"claude":null),alternateOn:f==="1",createdMs:Number.isFinite(y)&&y>0?y*1e3:null}}).filter(r=>!cD.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function Se(e){return(await kn()).find(n=>n.name===e)||null}function hD(e){if(typeof e!="string")return!1;let t=e.trim();return t.length>=1&&t.length<=mD&&fD.test(t)}function Cu(e){return e.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function ox(e){try{let{stdout:t}=await Ce(oe,se("list-sessions","-F","#{session_name}"));return t.split(`
|
|
87
|
+
`).map(n=>n.trim()).includes(e)}catch{return!1}}function yD(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 bD(e,t){let n=Date.now()+Yi(),r=yD(t),o=!1,s=0,a=0;for(;;){if(await zo(wD),s+=1,!await ox(e))return;let i=await Ne(e,{lines:15}),c=!o&&gD.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 re({flow:"spawn",phase:"launch-retry",sessionName:e,agent:Xr(t),promptHead:t.slice(0,120)}).catch(()=>{}),await Ce(oe,se("send-keys","-t",e,"C-u")),await Ce(oe,se("send-keys","-t",e,"-l",t)),await Ce(oe,se("send-keys","-t",e,"Enter"));continue}if(Date.now()>n){await re({flow:"spawn",phase:"launch-failed",sessionName:e,agent:Xr(t),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function jn(e,t=sD,n={}){if(!hD(e))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=e.trim(),o=Cu(r);if(!je(o))throw new Error("resulting tmux session name is invalid");if(!await Ji())throw new Error(`host tmux server not running on ${zt} and auto-start failed \u2014 start one: ${oe} -S ${zt} new-session -d -s keeper "sleep 2147483647"`);if(await ox(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||_m,a=n.agent||Xr(s),i=!!n.resumeSessionId,c=n.resumeSessionId||ex(),l=i?s:zi(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),d=a==="claude"&&Jo(s),p=n.firstRun===!0;p||(d&&await Wr(t),a==="codex"&&await Ys(t),a==="cursor"&&await Xs(t),await A0(t),n.freshWorkspace!==!0&&await Tu(t));let f=d&&!p?ru():null,w=Uk(),h=["-e",`${dr}=${w}`];h.push(...Y0({nodeUrl:X0()})),h.push(...iD()),f&&h.push("-e",`ANTHROPIC_API_KEY=${f}`),d&&h.push(...Vi());try{await Ce(oe,se("new-session","-d","-s",o,"-c",t,...h))}catch(y){throw $u(f?$u(y,f):y,w)}return await Yo(o),await Ce(oe,se("set-option","-t",o,"@bostrat-session","1")),await Ce(oe,se("set-option","-t",o,"@bostrat-title",r)),await Ce(oe,se("set-option","-t",o,"@bostrat-sid",c)),await Ce(oe,se("set-option","-t",o,"@bostrat-agent",a)),await Ce(oe,se("send-keys","-t",o,l,"Enter")),lD(l)&&bD(o,l).catch(()=>{}),{name:o,title:r,cwd:t,sessionId:c,agent:a,pinned:u,hookToken:w}}async function Lt(e){if(!je(e))throw new Error("invalid session name");await Ce(oe,se("kill-session","-t",e))}async function sx(e,t){if(je(e))try{let n=t?se("set-option","-t",e,"@bostrat-sid",t):se("set-option","-u","-t",e,"@bostrat-sid");await Ce(oe,n)}catch{}}async function Ne(e,{lines:t=40}={}){if(!je(e))return"";try{let n=se("capture-pane","-t",e,"-p");Number.isFinite(t)&&t>0&&n.push("-S",`-${Math.floor(t)}`);let{stdout:r}=await Ce(oe,n);return String(r||"")}catch{return""}}async function ax(e){if(!je(e))return[];try{let{stdout:t}=await Ce(oe,se("list-panes","-s","-t",e,"-F","#{pane_pid}"));return String(t||"").trim().split(`
|
|
88
88
|
`).map(n=>Number(n.trim())).filter(n=>Number.isFinite(n)&&n>0)}catch{return[]}}async function ix(e){if(!je(e))return 0;try{let{stdout:t}=await Ce(oe,se("list-clients","-t",e,"-F","1"));return String(t||"").trim().split(`
|
|
89
|
-
`).filter(Boolean).length}catch{return 0}}async function Yo(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 Zi(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=xn(e,t),o=!0,!r)return null}else if(n){if(r=Cm(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 Qi(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 ec(e){let t=e;for(;t.length&&/^[A-Za-z_][A-Za-z0-9_]*=/.test(t[0]);)t=t.slice(1);for(;t.length&&(t[0]==="sudo"||t[0]==="time"||t[0]==="env");)t=t.slice(1);return t}function Fe(e,t=new Set){let n=[];for(let r=1;r<e.length;r++){let o=e[r];if(o==="<<")break;if(bx(o)){t.has(o)&&r++;continue}n.push(o)}return n}function kx(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
|
|
92
|
-
`,n);i!==-1&&(t=t.slice(0,i))}let r=
|
|
89
|
+
`).filter(Boolean).length}catch{return 0}}async function Yo(e,{cols:t=vD,rows:n=SD}={}){if(!je(e))return!0;if(await ix(e))return!1;try{let r=await cx(e);if(r.cols===t&&r.rows===n)return!0;await Ce(oe,se("resize-window","-t",e,"-x",String(t),"-y",String(n))),await Ce(oe,se("set-window-option","-t",e,"window-size","latest")),await zo(80)}catch{}return!0}async function AD(e){if(!je(e))return 0;try{let{stdout:t}=await Ce(oe,se("display-message","-t",e,"-p","#{pane_pid}"));return Number(String(t||"").trim())||0}catch{return 0}}async function Fn(e){let t=await AD(e);if(!t)return!1;try{return await Ce("pgrep",["-P",String(t)]),!1}catch(n){return n?.code===1}}async function cx(e){if(!je(e))return{cols:0,rows:0};try{let{stdout:t}=await Ce(oe,se("display-message","-t",e,"-p","#{window_width} #{window_height}")),[n,r]=String(t||"").trim().split(/\s+/).map(o=>Number(o)||0);return{cols:n,rows:r}}catch{return{cols:0,rows:0}}}async function Nu(e,{cols:t=kD,rows:n=xD}={}){if(je(e)&&!await ix(e))try{let r=await cx(e),o=Math.max(r.cols,t),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await Ce(oe,se("resize-window","-t",e,"-x",String(o),"-y",String(s))),await Ce(oe,se("set-window-option","-t",e,"window-size","latest")),await zo(80))}catch{}}async function Jt(e){return await Nu(e),Ne(e,{lines:ze})}async function Ke(e,t,{settleMs:n=40,kind:r}={}){if(!je(e))throw new Error("invalid session name");let o=(Array.isArray(t)?t:[t]).filter(s=>lx.has(s));for(let s=0;s<o.length;s++)await Ce(oe,se("send-keys","-t",e,o[s])),n>0&&s<o.length-1&&await zo(n);return re({flow:"tmux",phase:"send-keys",sessionName:e,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function Rm(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"&&lx.has(s.key)||typeof s.text=="string"&&s.text.length>0));for(let s=0;s<o.length;s++){let a=o[s];a.text!=null?await Ce(oe,se("send-keys","-t",e,"-l",a.text)):await Ce(oe,se("send-keys","-t",e,a.key)),n>0&&s<o.length-1&&await zo(n)}return re({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 ua(e,t,{kind:n}={}){if(!je(e))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(t)||0));return r>0&&await Ce(oe,se("send-keys","-t",e,"-N",String(r),"BSpace")),re({flow:"tmux",phase:"send-keys",sessionName:e,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function Du(e,t,{perCharDelayMs:n=120,kind:r}={}){if(!je(e))throw new Error("invalid session name");let o=[...String(t)];for(let s of o)await Ce(oe,se("send-keys","-t",e,"-l",s)),n>0&&await zo(n);re({flow:"tmux",phase:"type",sessionName:e,kind:r||"type",textLen:o.length,textHead:String(t).slice(0,120)}).catch(()=>{})}function TD(e,t){return new Promise((n,r)=>{let o=Q0(oe,se(...e),(s,a,i)=>{s?r(s):n({stdout:a,stderr:i})});o.stdin.on("error",()=>{}),o.stdin.end(t)})}async function ED(e,t){let n=`bostrat-${ex().slice(0,8)}`;await TD(["load-buffer","-b",n,"-"],t),await Ce(oe,se("paste-buffer","-p","-d","-b",n,"-t",e))}async function Qe(e,t,{enter:n=!0,submitDelayMs:r=0,kind:o,paste:s=!1}={}){if(!je(e))throw new Error("invalid session name");if(typeof t=="string"&&t.length>0&&(s?await ED(e,t):await Ce(oe,se("send-keys","-t",e,"-l",t))),n){let a=Number(r);Number.isFinite(a)&&a>0&&await zo(a),await Ce(oe,se("send-keys","-t",e,"Enter"))}re({flow:"tmux",phase:"send",sessionName:e,kind:o||"text",textLen:typeof t=="string"?t.length:0,textHead:typeof t=="string"?t.slice(0,120):"",enter:n,submitDelayMs:r,...s?{paste:!0}:{}}).catch(()=>{})}var Ce,oe,Z0,zt,sD,_m,Ki,aD,Im,cD,Ou,ca,la,Em,uD,dD,$m,pD,fD,mD,Yi,wD,gD,kD,xD,ze,vD,SD,lx,Pu,pt=G(()=>{_i();su();au();Li();Eu();bn();Js();Tm();Ce=rD(Q0);oe=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";Z0=typeof process.getuid=="function"?process.getuid():0,zt=oD({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return Ru.existsSync(tx(Z0))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:Z0}),sD=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",_m=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",Ki="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";aD=Object.freeze(["BOSTRAT_PLANE_URL","BOSTRAT_PLANE_TOKEN"]);Im="bostrat-usage-probe",cD=new Set([Im]);Ou="present",ca="absent",la="unknown",Em=5e3,uD=[250,1e3,3e3],dD=e=>new Promise(t=>setTimeout(t,e));$m={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"};pD=/^[A-Za-z0-9_.-]{1,64}$/;fD=/^[A-Za-z0-9 ._↻-]+$/,mD=64;Yi=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,wD=1e3,gD=/not found|Unknown command/i;kD=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,xD=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,ze=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,vD=Number(process.env.BOSTRAT_BOX_COLS)||120,SD=Number(process.env.BOSTRAT_BOX_ROWS)||40;lx=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);Pu=1600});import Xi from"node:fs";import MD from"node:fs/promises";import da from"node:path";import CD from"node:os";function Mm(e){return e.replace(/[^A-Za-z0-9]/g,"-")}function Cm(e){let t=da.join(Om,Mm(e)),n;try{n=Xi.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=da.join(t,o);try{let{mtimeMs:a}=Xi.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function xn(e,t){if(!t)return null;let n=da.join(Om,Mm(e),`${t}.jsonl`);return Xi.existsSync(n)?n:null}function wx(e){if(!e)return[];let t=da.join(Om,Mm(e)),n;try{n=Xi.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=Xi.statSync(da.join(t,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function jt(e,t=131072){let n=await MD.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 Zi(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=xn(e,t),o=!0,!r)return null}else if(n){if(r=Cm(e),o=!1,!r)return null}else return null;let s;try{s=PD(await jt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(hx,s.model),i=hx[s.model]||ND;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 hx,ND,Om,vn=G(()=>{hx={"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},ND=2e5,Om=process.env.BOSTRAT_CLAUDE_PROJECTS||da.join(CD.homedir(),".claude","projects")});function DD(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 Qi(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 ec(e){let t=e;for(;t.length&&/^[A-Za-z_][A-Za-z0-9_]*=/.test(t[0]);)t=t.slice(1);for(;t.length&&(t[0]==="sudo"||t[0]==="time"||t[0]==="env");)t=t.slice(1);return t}function Fe(e,t=new Set){let n=[];for(let r=1;r<e.length;r++){let o=e[r];if(o==="<<")break;if(bx(o)){t.has(o)&&r++;continue}n.push(o)}return n}function kx(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 jD(e){let t=kx(e),n=vt(Fe(e).find(r=>!/^\d+(,\d+)?p$/.test(r)));return t&&n?t.from===t.to?`Read line ${t.from} of ${Je(n)}`:`Read lines ${t.from}\u2013${t.to} of ${Je(n)}`:e.includes("-i")&&n?`Edit ${Je(n)}`:null}function Nm(e){if(e.includes("--files")){let s=vt(Fe(e,gx)[0]);return s&&s!=="."?`List files in ${pa(s)}`:"List files"}let t=Fe(e,gx),n=t[0];if(!n)return null;let r=vt(t.slice(1).find(s=>s!==".")),o=fa(n,40);return r?`Search for \u201C${o}\u201D in ${pa(r)}`:`Search for \u201C${o}\u201D`}function Pm(e){let t=Fe(e,new Set(["--prefix"])),n=t[0];return n?n==="test"||n==="t"?"Run tests":n==="run"||n==="run-script"?t[1]?`Run ${t[1]}`:null:n==="ci"||n==="install"||n==="i"||n==="add"?"Install dependencies":null:null}function Dm(e,t){if(e.includes("-e")||e.includes("--eval")||e.includes("-c")||e[1]==="-"||e.includes("<<"))return`Run an inline ${t} script`;if(e.includes("--test"))return"Run tests";let n=Fe(e)[0];return n?`Run ${Je(n)}`:null}function FD(e){let t=Fe(e),n=t[0],r=t.find(a=>/\.(service|timer|socket|target)$/.test(a))||t[1],o=r?r.replace(/\.service$/,""):"a service";return{status:`Check ${o} status`,show:`Inspect ${o}`,restart:`Restart ${o}`,start:`Start ${o}`,stop:`Stop ${o}`,"daemon-reload":"Reload systemd"}[n]||null}function yx(e){return e.find(t=>/^https?:\/\//.test(t))||null}function WD(e){let t=ec(Qi(e));if(!t.length)return null;if(/^(ba|z)?sh$/.test(Je(t[0]))&&(t[1]==="-lc"||t[1]==="-c")&&t[2])return Bm(t[2]);let n=Je(t[0]);if(HD.has(n))return null;if(t.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=UD[n];return r?r(t.map((o,s)=>s===0?n:o)):null}function Bm(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=DD(t);for(let i=0;i<r.length-1;i++){let c=ec(Qi(r[i])),l=ec(Qi(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(Je(c[0]))&&Je(l[0])==="sed"){let u=kx(l),d=Fe(c)[0];if(u&&d)return fa(u.from===u.to?`Read line ${u.from} of ${Je(d)}`:`Read lines ${u.from}\u2013${u.to} of ${Je(d)}`,88)}}let o=[];for(let i of r){let c=WD(i);c&&o.push({d:c,head:Je(ec(Qi(i))[0]||""),sub:Fe(ec(Qi(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":fa(s?s.d:o[0].d,88)}var fa,vt,Je,pa,bx,gx,BD,LD,UD,HD,xx=G(()=>{fa=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,vt=e=>e&&!/[$`]/.test(e)?e:null,Je=e=>e.replace(/\/+$/,"").split("/").pop()||e,pa=e=>{let t=e.replace(/\/+$/,"").split("/").filter(Boolean);return t.length>3?t.slice(-2).join("/"):e},bx=e=>e.startsWith("-"),gx=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);BD={status:()=>"Check git status",diff:()=>"Review changes",log:()=>"Review commit history",show:()=>"Inspect a commit",add:()=>"Stage files",commit:()=>"Commit changes",push:()=>"Push to remote",pull:()=>"Pull from remote",fetch:()=>"Fetch from remote",merge:()=>"Merge branches",rebase:()=>"Rebase branch",stash:()=>"Stash changes",clone:e=>e[1]?`Clone ${Je(e[1])}`:"Clone repository",checkout:()=>"Switch branches",switch:()=>"Switch branches",restore:()=>"Restore files",reset:()=>"Reset git state",branch:(e,t)=>t.includes("--show-current")?"Check current branch":"List branches",remote:()=>"Check git remotes","rev-parse":()=>"Resolve a git ref",worktree:()=>"Manage git worktrees","merge-base":()=>"Find the merge base","ls-files":()=>"List tracked files"},LD={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"};UD={git:e=>{let t=Fe(e,new Set(["-C","-c"])),n=BD[t[0]];return n?n(t,e):null},gh:e=>{let t=Fe(e),n=LD[t[0]];return n?n(t):null},sed:jD,rg:Nm,grep:Nm,egrep:Nm,nl:e=>{let t=vt(Fe(e)[0]);return t?`Read ${Je(t)}`:null},cat:e=>{let t=vt(Fe(e)[0]);return t?`Read ${Je(t)}`:null},head:e=>{let t=vt(Fe(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the start of ${Je(t)}`:null},tail:e=>{let t=vt(Fe(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the end of ${Je(t)}`:null},wc:e=>{let t=vt(Fe(e)[0]);return t?`Count lines in ${Je(t)}`:"Count input lines"},ls:e=>{let t=vt(Fe(e)[0]);return t&&t!=="."?`List files in ${pa(t)}`:"List files"},find:e=>vt(e[1])&&!bx(e[1])&&e[1]!=="."?`Find files in ${pa(e[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:e=>{let t=vt(Fe(e)[0]);return t?`Create directory ${pa(t)}`:null},touch:e=>{let t=vt(Fe(e)[0]);return t?`Create ${Je(t)}`:null},rm:e=>{let t=vt(Fe(e)[0]);return t?`Delete ${pa(t)}`:null},mv:e=>{let t=vt(Fe(e)[0]);return t?`Move ${Je(t)}`:null},cp:e=>{let t=vt(Fe(e)[0]);return t?`Copy ${Je(t)}`:null},chmod:e=>{let t=vt(Fe(e)[1]);return t?`Change permissions on ${Je(t)}`:null},diff:e=>{let t=Fe(e);return t.length>=2?`Compare ${Je(t[0])} and ${Je(t[1])}`:null},curl:e=>{let t=yx(e);return t?`Fetch ${fa(t,56)}`:"Fetch a URL"},wget:e=>{let t=yx(e);return t?`Fetch ${fa(t,56)}`:"Fetch a URL"},npm:Pm,pnpm:Pm,yarn:Pm,npx:e=>{let t=Fe(e)[0];return t?`Run ${t}`:null},node:e=>Dm(e,"Node"),python:e=>Dm(e,"Python"),python3:e=>Dm(e,"Python"),make:e=>{let t=Fe(e)[0];return t?`Run make ${t}`:"Run make"},systemctl:FD,journalctl:e=>{let t=e.findIndex(r=>r==="-u"||r==="--unit"||r==="--user-unit"),n=t!==-1?e[t+1]:null;return n?`Read ${n.replace(/\.service$/,"")} logs`:"Read system logs"},tmux:e=>{let t=Fe(e,new Set(["-S","-L","-t","-f"]))[0];return{ls:"List tmux sessions","list-sessions":"List tmux sessions","kill-session":"Kill a tmux session","send-keys":"Send keys to a tmux session","capture-pane":"Capture a tmux pane","new-session":"Start a tmux session"}[t]||null},ps:()=>"List processes",which:e=>{let t=Fe(e)[0];return t?`Locate ${t}`:null},command:e=>e[1]==="-v"&&e[2]?`Locate ${e[2]}`:null,fly:e=>{let t=Fe(e)[0];return t==="deploy"?"Deploy to Fly":t?`Run fly ${t}`:null}},HD=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"])});function ma(e){let t=e.split(`
|
|
93
93
|
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function Qr(e,t=""){let n=t.trim();return n||Bm(e)||ma(e)}function Lm(e,t){if(e){if(Array.isArray(e)){for(let n of e)Lm(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&jm.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))Lm(n,t)}}}async function vx(e,{maxBytes:t=1048576,limit:n=400}={}){let r;try{r=await jt(e,t)}catch{return[]}let o=[];for(let s of r.split(`
|
|
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(Lm(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:Qr(d.command,d.description),ts:u})}return o.slice(-n)}function Sx(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(...
|
|
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 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:Zo,sourceId:w.payload?.turn_id||d||void 0,ts:w.timestamp?Date.parse(w.timestamp):null,model:i}));continue}if(w.type!=="response_item")continue;let h=w.payload;if(!h)continue;if((h.type==="function_call"||h.type==="custom_tool_call")&&h.name){u=!0,Hm.has(h.name)||c.push(h.name);continue}if(h.type!=="message"||h.role!=="assistant")continue;u=!0;let y=
|
|
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"&&tc.has(u.name)){let f;try{f=JSON.parse(u.arguments||"{}")}catch{continue}let w=
|
|
99
|
-
`).trim():"";tc=new Set(["exec_command","shell","exec"]),Hm=new Set(["wait","write_stdin"]),Zo="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 nc({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&$x.has(r))return $x.get(r);let o=Number(t)||0;if(o===401)return"auth";if(o===403)return"entitlement";if(o===429)return"rate_limit";if(o===529)return"overloaded";if(o>=500)return"server";let s=String(n||"").toLowerCase();return/overloaded/.test(s)?"overloaded":/rate limit|too many requests|quota|usage limit/.test(s)?"rate_limit":/billing|subscription|not allowed|disabled .* access|upgrade your plan|no credits/.test(s)?"entitlement":/log ?in|authenticat|expired|invalid.*key|unauthorized/.test(s)?"auth":/server|internal error|5\d\d/.test(s)?"server":"other"}function Rx(e){if(!e||e.role!=="assistant")return null;if(e.apiError){let t=e.apiError;return{cls:t.cls||nc({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===Zo?"other":nc({text:t}),code:null,status:null,requestId:null}}return null}function Ox(e,{codexFailedText:t=null}={}){let n=String(e?.text||"").trim();return n?e?.model==="<synthetic>"?/^No response requested\.$/.test(n)?null:{cls:nc({text:n}),code:null,status:
|
|
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 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"&&
|
|
102
|
-
|
|
103
|
-
`):""}var
|
|
104
|
-
`);if(!jx.test(e))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(
|
|
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(Lm(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:Qr(d.command,d.description),ts:u})}return o.slice(-n)}function Sx(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(...qD(i,r.get(a)||[]));return s.sort((a,i)=>(a.ts||0)-(i.ts||0))}function qD(e,t){let n=[...t].sort((l,u)=>(l.ts||0)-(u.ts||0)),r=[...e].sort((l,u)=>(l.ts||0)-(u.ts||0)),o=[],s=0,a=0,i=l=>{let u=l.n??a+1;return u>a&&(a=u),u};for(let l of n){let u=r.findIndex((w,h)=>h>=s&&(w.ts||0)>(l.ts||0)),d=u===-1?r.length:u,p=r.slice(s,d);if(s=d,!p.length)continue;let f=p.map(w=>({...w,n:i(w)}));o.push({id:`batch-${l.ts}-${l.box}`,anchorTs:l.ts,anchorBox:l.box,agent:l.agent,boxRole:l.boxRole,commands:f,ts:Math.max(...f.map(w=>w.ts||0)),pending:!1})}let c=r.slice(s);if(c.length){let l=c.map(u=>({...u,n:i(u)}));o.push({id:`batch-pending-${l[0]?.box||"lead"}`,anchorTs:null,anchorBox:l[l.length-1]?.box??null,agent:l[l.length-1]?.agent??"claude",boxRole:l[l.length-1]?.boxRole??"lead",commands:l,ts:Math.max(...l.map(u=>u.ts||0)),pending:!0})}return o}var jm,ha=G(()=>{vn();xx();jm=new Set(["Bash"])});import Ax from"node:fs";import Um from"node:fs/promises";import Fm from"node:path";import GD from"node:os";async function Tx(e){try{return await Um.realpath(e)}catch{return e}}function VD(e=KD){let t=[],n=r=>{let o;try{o=Ax.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=Fm.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:Ax.statSync(a).mtimeMs})}catch{}}}};return n(e),t.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function zD(e,t=1048576){let n;try{n=await Um.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 JD(e){let t=await zD(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 Xo(e,t){if(!e&&!t)return null;let n=VD();if(t){let o=n.find(s=>s.id===t);return o||null}let r=await Tx(e);for(let o of n){let s=await JD(o.file);if(s&&(s===e||await Tx(s)===r))return o}return null}async function Ex(e,t,{limit:n=80,maxBytes:r=524288}={}){let o=await Xo(e,t);if(!o)return[];let s;try{s=await jt(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,d=null;for(let p of s.split(`
|
|
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:Zo,sourceId:w.payload?.turn_id||d||void 0,ts:w.timestamp?Date.parse(w.timestamp):null,model:i}));continue}if(w.type!=="response_item")continue;let h=w.payload;if(!h)continue;if((h.type==="function_call"||h.type==="custom_tool_call")&&h.name){u=!0,Hm.has(h.name)||c.push(h.name);continue}if(h.type!=="message"||h.role!=="assistant")continue;u=!0;let y=XD(h.content),k=c.length?c.slice():null;c=[],!(!y&&!k)&&a.push({role:"assistant",text:y,tools:k||void 0,ts:w.timestamp?Date.parse(w.timestamp):null,model:i})}if(i)for(let p of a)p.model||(p.model=i);return a.slice(-n)}function ZD(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 QD(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 e1(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=QD(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function _x(e,t,{limit:n=400,maxBytes:r=524288}={}){let o=await Xo(e,t);if(!o)return[];let s;try{s=await jt(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"&&tc.has(u.name)){let f;try{f=JSON.parse(u.arguments||"{}")}catch{continue}let w=ZD(f);w&&(d=[w])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=e1(u.input));if(!d.length)continue;let p=u.call_id||u.id||null;d.forEach((f,w)=>{a.push({id:p?w?`${p}:${w}`:p:null,tool:u.name,command:f,description:"",summary:Qr(f),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function Ix(e,t){let n=await Xo(e,t);if(!n)return null;try{return Math.round((await Um.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var KD,YD,XD,tc,Hm,Zo,Qo=G(()=>{vn();ha();KD=process.env.BOSTRAT_CODEX_SESSIONS||Fm.join(process.env.CODEX_HOME||Fm.join(GD.homedir(),".codex"),"sessions");YD=new Set(["output_text","text"]),XD=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&typeof t.text=="string"&&YD.has(t.type)).map(t=>t.text).join(`
|
|
99
|
+
`).trim():"";tc=new Set(["exec_command","shell","exec"]),Hm=new Set(["wait","write_stdin"]),Zo="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 nc({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&$x.has(r))return $x.get(r);let o=Number(t)||0;if(o===401)return"auth";if(o===403)return"entitlement";if(o===429)return"rate_limit";if(o===529)return"overloaded";if(o>=500)return"server";let s=String(n||"").toLowerCase();return/overloaded/.test(s)?"overloaded":/rate limit|too many requests|quota|usage limit/.test(s)?"rate_limit":/billing|subscription|not allowed|disabled .* access|upgrade your plan|no credits/.test(s)?"entitlement":/log ?in|authenticat|expired|invalid.*key|unauthorized/.test(s)?"auth":/server|internal error|5\d\d/.test(s)?"server":"other"}function Rx(e){if(!e||e.role!=="assistant")return null;if(e.apiError){let t=e.apiError;return{cls:t.cls||nc({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===Zo?"other":nc({text:t}),code:null,status:null,requestId:null}}return null}function Ox(e,{codexFailedText:t=null}={}){let n=String(e?.text||"").trim();return n?e?.model==="<synthetic>"?/^No response requested\.$/.test(n)?null:{cls:nc({text:n}),code:null,status:t1(n),requestId:null,text:n}:t&&n===t?{cls:"other",code:null,status:null,requestId:null,text:n}:null:null}function t1(e){let t=/^API Error:\s*(\d{3})\b/.exec(e);return t?Number(t[1]):null}var xJ,$x,ju=G(()=>{Qo();xJ=Object.freeze(["entitlement","auth","rate_limit","overloaded","server","other"]),$x=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 n1(e,t){return!e||!t?null:xn(e,t)}function r1(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 o1(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 s1(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&t.type==="tool_result")}function Wm(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 Mx(e,t){if(!e.uuid)return null;let n=Wm(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 qm(e,{maxBytes:t=524288,limit:n=80}={}){let r;try{r=await jt(e,t)}catch{return[]}let o=[],s=new Map,a=new Map;for(let i of r.split(`
|
|
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"&&a1.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(s1(u.content)){let k=Mx(l,s);k&&o.push({role:"assistant",text:"",images:k,ts:l.timestamp?Date.parse(l.timestamp):null,model:null});continue}let p=r1(u.content),f=d==="assistant"?o1(u.content):null,w=d==="user"?Mx(l,s):null;if(!p&&!f&&!w)continue;let h=d==="user"&&l.isMeta&&typeof l.sourceToolUseID=="string"&&l.sourceToolUseID?{toolUseId:l.sourceToolUseID,skill:a.get(l.sourceToolUseID)??null}:null,y=d==="assistant"&&l.isApiErrorMessage?{code:typeof l.error=="string"?l.error:null,status:Number(l.apiErrorStatus)||null,requestId:l.requestId||null,cls:nc({error:l.error,status:l.apiErrorStatus,text:p})}:null;o.push({role:d,text:p,tools:f||void 0,images:w||void 0,injected:h||void 0,ts:l.timestamp?Date.parse(l.timestamp):null,model:u.model||null,apiError:y||void 0})}return o.slice(-n)}async function Cx(e,t,{turns:n=16}={}){let r=n1(e,t);if(!r)return"";let o=await qm(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}:** ${i1(s.text||c,700)} ${i}`.trim()}).join(`
|
|
102
|
+
|
|
103
|
+
`):""}var a1,i1,Fu=G(()=>{ju();vn();a1=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;i1=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e});import c1 from"node:fs/promises";import{execFile as l1}from"node:child_process";import{promisify as u1}from"node:util";function Bx(e){Km=typeof e=="function"?e:null}function Vm(e){let t=String(e||""),n=t.indexOf("/lib/node_modules/");return n>0?t.slice(0,n):null}function Lx({sessionName:e,from:t,to:n}={}){let r=String(n||"").trim();!r||Nx.has(r)||(Nx.add(r),Px=Px.then(()=>p1({sessionName:e,from:t,to:r})).catch(()=>{}))}async function p1({sessionName:e,from:t,to:n}){await re({flow:"codex-update",phase:"start",sessionName:e,from:t,to:n}).catch(()=>{});try{let r=(await Gm("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await c1.realpath(r),s=Vm(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await Gm("npm",["install","-g","@openai/codex"],{env:a,timeout:d1});let i=(await Gm(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await re({flow:"codex-update",phase:"done",sessionName:e,from:t,to:n,version:i}).catch(()=>{}),await Dx({sessionName:e,from:t,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await re({flow:"codex-update",phase:"failed",sessionName:e,from:t,to:n,error:o}).catch(()=>{}),await Dx({sessionName:e,from:t,to:n,status:"failed",error:o})}}async function Dx(e){if(Km)try{await Km(e)}catch{}}var Gm,d1,Nx,Px,Km,Uu=G(()=>{bn();Gm=u1(l1),d1=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,Nx=new Set,Px=Promise.resolve(),Km=null});function m1(e,{provider:t,box:n}){let r=String(e).replace(/\r/g,"").split(`
|
|
104
|
+
`);if(!jx.test(e))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(f1);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"&&jx.test(a)?a:"The agent is asking for approval";return{provider:t,box:n,promptId:`${t}:${c}:${i.map(l=>l.label).join("|")}`.slice(0,200),questions:[{question:c,header:"Approval",multiSelect:!1,options:i.map(l=>({label:l.label,description:""}))}]}}function hr(e){let t=String(e??"").replace(/\r/g,""),n=t.split(`
|
|
105
105
|
`);for(let r=n.length-1;r>=0;r--)if(Hx.test(n[r]))return n.slice(r).join(`
|
|
106
|
-
`);return t}function
|
|
107
|
-
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(Fx);if(!u)continue;s<0&&(s=l);let d="";for(let p=l+1;p<Math.min(l+3,r.length);p++){let f=r[p].trim();if(f){if(Fx.test(r[p])||/^Press enter/.test(f))break;d=f;break}}o.push({n:Number(u[2]),cursor:!!u[1],label:u[3].trim(),desc:d})}let a=o.sort((l,u)=>l.n-u.n);if(a.length<2||a.some((l,u)=>l.n!==u+1)||!a.some(l=>/Sign in with ChatGPT/.test(l.label)))return null;let i=[];for(let l=0;l<s;l++){let u=r[l].trim();u&&!Hx.test(r[l])&&
|
|
108
|
-
`).pop()||"";return
|
|
109
|
-
`),o=r.findIndex(i=>Ux.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(
|
|
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
|
|
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
|
|
112
|
-
`).filter(o=>o.trim());return
|
|
106
|
+
`);return t}function h1(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 w1(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 g1(e,t=""){let n=hr(e);if(rc.test(n)||!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n))return null;let r=n.split(`
|
|
107
|
+
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(Fx);if(!u)continue;s<0&&(s=l);let d="";for(let p=l+1;p<Math.min(l+3,r.length);p++){let f=r[p].trim();if(f){if(Fx.test(r[p])||/^Press enter/.test(f))break;d=f;break}}o.push({n:Number(u[2]),cursor:!!u[1],label:u[3].trim(),desc:d})}let a=o.sort((l,u)=>l.n-u.n);if(a.length<2||a.some((l,u)=>l.n!==u+1)||!a.some(l=>/Sign in with ChatGPT/.test(l.label)))return null;let i=[];for(let l=0;l<s;l++){let u=r[l].trim();u&&!Hx.test(r[l])&&h1(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=w1(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function k1(e,t=""){let n=hr(e);if(rc.test(n)||!y1.test(n))return null;let r=n.trimEnd().split(`
|
|
108
|
+
`).pop()||"";return b1.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 v1(e,t=""){let n=hr(e);if(rc.test(n)||!Ux.test(n))return null;let r=n.split(`
|
|
109
|
+
`),o=r.findIndex(i=>Ux.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(x1);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 S1(e,t=""){let n=hr(e);return rc.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 A1(e,t=""){let n=hr(e);return rc.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 T1(e,t=""){let n=String(e);if(!/Press any key to log in/.test(n)||zm.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 _1(e,t=""){let n=String(e);if(!E1.test(n)||zm.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 I1(e,t=""){let n=String(e);return!/Scan this QR code to log in/.test(n)||zm.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 Wx(e,t,n=""){if(e!=="cursor"&&e!=="codex")return null;if(e==="codex"){let r=k1(t,n)||v1(t,n)||S1(t,n)||A1(t,n)||g1(t,n);if(r)return r}if(e==="cursor"){let r=_1(t,n)||I1(t,n)||T1(t,n);if(r)return r}return m1(t,{provider:e,box:n})}function qx(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 f1,jx,rc,Hx,Fx,y1,b1,Ux,x1,zm,E1,Hu=G(()=>{f1=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,jx=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;rc=/(^|\n)\s*›/,Hx=/^\s*Welcome to Codex\b|^\s*(?:│\s*)?>_ OpenAI Codex\b/;Fx=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;y1=/Successfully logged in\./,b1=/^\s*Press Enter to continue\.\s*$/;Ux=/Enter this one-time code/,x1=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;zm=/Run Everything|Add a follow-up|Plan, search, build anything/;E1=/click this link to log in|Signing in with the browser/});function oc(e,t){if(e!=="claude")return!1;let n=String(t||"");if(!Kx.test(n))return!1;let r=n.replace(/\r/g,"").split(`
|
|
112
|
+
`).filter(o=>o.trim());return $1.test(r.slice(-6).join(`
|
|
113
113
|
`))}function Gx(e){let t=String(e||"").replace(/\r/g,"").split(`
|
|
114
|
-
`);for(let n=t.length-1;n>=0;n--)if(t[n].trim())return t[n];return""}function Vx(e,t){let n=String(t||"");return e==="claude"?Kx.test(n)?!1:
|
|
115
|
-
`),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(
|
|
116
|
-
`).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
|
+
`);for(let n=t.length-1;n>=0;n--)if(t[n].trim())return t[n];return""}function Vx(e,t){let n=String(t||"");return e==="claude"?Kx.test(n)?!1:R1.test(Gx(n)):e==="codex"?C1.test(n)||!O1.test(n)?!1:M1.test(Gx(n)):!1}function zx(e){let t=String(e||"").match(N1);if(!t)return null;let n=t[1].replace(/[.,;:]+$/,"");return n.length>1&&n.length<=120?n:null}function Jx(e){return wa.get(e)||null}function Wu(e){let t=wa.get(e);return t||(t={issuedAt:0,issueCount:0,startedReason:null,enterClearedAt:0,account:null,connectedAt:0,reported:{started:!1,connected:!1}},wa.set(e,t)),t}function Yx(e){let t=wa.get(e);return!!t&&!!t.startedReason&&!t.connectedAt}function qu(e,{onDrive:t=!1,now:n=Date.now()}={}){let r=wa.get(e);return!r||!r.issuedAt?!0:t?n-r.issuedAt>=P1:!1}function Gu(e,t=Date.now()){let n=Wu(e);return n.issuedAt=t,n.issueCount+=1,n.connectedAt&&(n.connectedAt=0,n.reported={started:!1,connected:!1},n.startedReason=null),n.startedReason||(n.startedReason="not-logged-in"),n}function Xx(e,t=""){let n=Wu(e);n.startedReason||(n.startedReason="auth-screen"),n.connectedAt&&(n.connectedAt=0,n.reported={started:!1,connected:!1});let r=zx(t);return r&&(n.account=r),n}function Ku(e,t="",n=Date.now()){let r=Wu(e);r.startedReason||(r.startedReason="auth-screen"),r.enterClearedAt=n;let o=zx(t);return o&&(r.account=o),r}function Zx(e,t=Date.now()){let n=Wu(e);return n.connectedAt=t,n}function Qx(e){let t=wa.get(e);if(!t)return{started:!1,connected:!1};let n={started:!1,connected:!1};return t.startedReason&&!t.reported.started&&(t.reported.started=!0,n.started=!0),t.connectedAt&&!t.reported.connected&&(t.reported.connected=!0,n.connected=!0),n}var $1,Kx,R1,O1,M1,C1,N1,wa,P1,Jm=G(()=>{$1=/Not logged in · (?:Run|Please run) \/login/,Kx=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;R1=/Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/,O1=/Successfully logged in\./,M1=/^\s*Press Enter to continue\.\s*$/,C1=/^\s*›/m;N1=/Logged in as\s+(\S+@\S+|\S+)/;wa=new Map;P1=3e4});function j1(e){let t=String(e||"").replace(/\r/g,"").split(`
|
|
115
|
+
`),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(B1.test(r)||L1.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(`
|
|
116
|
+
`).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 q1(e){let t=String(e||"");if(!rt.codex.block.test(t))return null;let n=hr(t);if(!rt.codex.block.test(n))return null;let r=n.match(W1);return{from:r?.[1]||null,to:r?.[2]||null}}function Ft(e){return new Promise(t=>setTimeout(t,e))}function eo(e,t){let n=Date.now(),r=G1();for(let[s,a]of Vu)n-a>=r&&Vu.delete(s);let o=`${e}::${t}`;return Vu.has(o)?!1:(Vu.set(o,n),!0)}function rv(e,t){if(e?.busyScope!=="footer")return t;let n=String(t||"").split(`
|
|
117
117
|
`),r=-1;for(let o=n.length-1;o>=0;o--)if(nv.test(n[o])){r=o;break}return r<0?t:n.slice(r+1).join(`
|
|
118
|
-
`)}function
|
|
119
|
-
`))nv.test(n)&&(t=Math.max(t,n.trimEnd().length));return t}function sc(e,t){let n=rt[e];if(!n?.busy)return{state:"idle",reason:null};if(ga(e,t))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(t))return{state:"idle",reason:null};if(n.busy.test(rv(n,t)))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(t))return{state:"busy",reason:null};let r=
|
|
120
|
-
`).slice(-
|
|
121
|
-
`);return
|
|
118
|
+
`)}function V1(e){let t=0;for(let n of String(e).split(`
|
|
119
|
+
`))nv.test(n)&&(t=Math.max(t,n.trimEnd().length));return t}function sc(e,t){let n=rt[e];if(!n?.busy)return{state:"idle",reason:null};if(ga(e,t))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(t))return{state:"idle",reason:null};if(n.busy.test(rv(n,t)))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(t))return{state:"busy",reason:null};let r=V1(t);return e==="claude"&&r>0&&r<K1?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function Ym(e,t){return sc(e,t).state}function Ju(e,t){if(e!=="claude")return!1;let n=String(t||"").split(`
|
|
120
|
+
`).slice(-J1).join(`
|
|
121
|
+
`);return z1.test(n)}function ya(e,t){return Ym(e,t)==="busy"}function ac(e,t){return Ym(e,t)!=="idle"}function to(e,t){let n=rt[e];return n?.up?n.up.test(t):!0}function Y1(e,t){let n=rt[e];return n?ga(e,t)?!1:n.up.test(t)&&Ym(e,t)==="idle":!0}function ga(e,t){let n=rt[e];if(!n)return!1;let r=e==="codex"?hr(t):t;return n.auth?.test(r)&&!n.up.test(r)?!0:n.block?.test(r)?e==="codex"?!0:!n.up.test(r):!1}function zu(e,t){let n=String(t||"").trim();if(!n)return e;let r=String(e||"").split(`
|
|
122
122
|
`);for(let o=r.length-1;o>=0;o--)if(r[o].includes(n))return r.slice(o+1).join(`
|
|
123
|
-
`);return e}function ov(e){let t=String(e||"").trim();return t?t.slice(0,32):null}function ev(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 tv(e,t,n,{bailOnDialog:r=!1,launchEcho:o=null,onReady:s}={}){let a=Date.now()+n,i=0,c=null;await Yo(e).catch(()=>{});let l=0;for(;;){let u=zu(await Ne(e),o);if(oc(t,u)){if(l<2&&qu(e,{onDrive:!0})&&eo(e,"login-issue")){l+=1,Gu(e),await re({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"login-issue",attempt:l}).catch(()=>{}),await Qe(e,"/login",{enter:!0,submitDelayMs:600,kind:"login-issue"});let d=Date.now()+8e3,p=!1;for(;Date.now()<d;)if(await Ft(es),ga(t,zu(await Ne(e),o))){p=!0;break}if(p&&r)return re({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"login-issued"}).catch(()=>{}),"login-issued";continue}if(Date.now()>a){let d=`${t} isn't signed in on this box \u2014 answer the Sign-in card in chat (or run /login from the Terminal tab), then send again`;throw re({flow:"agent-ready",phase:"blocked",reason:"not-logged-in",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:d}).catch(()=>{}),new Error(d)}await Ft(es);continue}if((t==="claude"||t==="codex")&&i<3){let d=t==="codex"?hr(u):u;if(Vx(t,d)){if(!eo(e,"login-enter")){await Ft(es);continue}i+=1,Ku(e,d),await re({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"login-enter",attempt:i}).catch(()=>{}),await Qe(e,"",{enter:!0,kind:"login-enter"}),await Ft(ts);continue}}if(
|
|
124
|
-
`)?!0:Buffer.byteLength(e,"utf8")>=
|
|
125
|
-
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(av.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(av.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(
|
|
126
|
-
`).map(o=>o.replace(/\s+$/,"")),n=-1;for(let o=t.length-1;o>=0;o--)if(t[o]){(
|
|
127
|
-
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return mc(r)}function
|
|
128
|
-
${o}`.trim()||t.slice(0,2e3)}function
|
|
129
|
-
`):""}async function yv(e,t,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await Qu.readFile(t,"utf8");if(a.includes(gv))return{appended:!1,reason:"already-enriched"};let i=await
|
|
123
|
+
`);return e}function ov(e){let t=String(e||"").trim();return t?t.slice(0,32):null}function ev(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 tv(e,t,n,{bailOnDialog:r=!1,launchEcho:o=null,onReady:s}={}){let a=Date.now()+n,i=0,c=null;await Yo(e).catch(()=>{});let l=0;for(;;){let u=zu(await Ne(e),o);if(oc(t,u)){if(l<2&&qu(e,{onDrive:!0})&&eo(e,"login-issue")){l+=1,Gu(e),await re({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"login-issue",attempt:l}).catch(()=>{}),await Qe(e,"/login",{enter:!0,submitDelayMs:600,kind:"login-issue"});let d=Date.now()+8e3,p=!1;for(;Date.now()<d;)if(await Ft(es),ga(t,zu(await Ne(e),o))){p=!0;break}if(p&&r)return re({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"login-issued"}).catch(()=>{}),"login-issued";continue}if(Date.now()>a){let d=`${t} isn't signed in on this box \u2014 answer the Sign-in card in chat (or run /login from the Terminal tab), then send again`;throw re({flow:"agent-ready",phase:"blocked",reason:"not-logged-in",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:d}).catch(()=>{}),new Error(d)}await Ft(es);continue}if((t==="claude"||t==="codex")&&i<3){let d=t==="codex"?hr(u):u;if(Vx(t,d)){if(!eo(e,"login-enter")){await Ft(es);continue}i+=1,Ku(e,d),await re({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"login-enter",attempt:i}).catch(()=>{}),await Qe(e,"",{enter:!0,kind:"login-enter"}),await Ft(ts);continue}}if(Y1(t,u))return await s?.(),await Ft(ts),"up";if(rt[t]?.up&&!rt[t].up.test(u))if(await Fn(e)){if(c??=Date.now(),Date.now()-c>=Yi()){let d=ev(t);throw re({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,idleMs:Date.now()-c,error:d.message}).catch(()=>{}),d}}else c=null;else c=null;if(r&&t==="claude"&&H1.test(u)&&!rt.claude.up.test(u))return re({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"resume-confirm"}).catch(()=>{}),"dialog-bail";if(t==="codex"&&U1.test(u)&&!rt.codex.up.test(u)&&i<3){if(!eo(e,"codex-trust")){await Ft(es);continue}i+=1,await re({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"codex-trust",attempt:i}).catch(()=>{}),await Qe(e,"",{enter:!0,kind:"codex-trust-accept"}),await Ft(ts);continue}if(t==="cursor"&&F1.test(u)&&!rt.cursor.up.test(u)&&i<3){if(!eo(e,"cursor-trust")){await Ft(es);continue}i+=1,await re({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i}).catch(()=>{}),await Qe(e,"a",{enter:!1,kind:"cursor-trust-accept"}),await Ft(ts);continue}if(t==="codex"&&i<3){let d=q1(u);if(d){if(!eo(e,`codex-update:${d.from}->${d.to}`)){await Ft(es);continue}i+=1,await re({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:i,update:`${d.from} -> ${d.to}`}).catch(()=>{}),await Qe(e,"3",{enter:!1,kind:"codex-update-skip"}),Lx({sessionName:e,from:d.from,to:d.to}),await Ft(ts);continue}}if(Date.now()>a){if(ga(t,u)){let d=`${t} is blocked on an interactive prompt`;throw t==="codex"?d=rt.codex.auth.test(u)?"codex isn't signed in on this box \u2014 answer the Sign in card in chat (Device Code works from any device) or finish signing in from the Terminal tab, then send again":"codex is stuck on its update prompt (auto-skip didn't clear it) \u2014 open the terminal and choose Skip, then send again":t==="cursor"&&rt.cursor.auth.test(u)?d="cursor isn't signed in on this box \u2014 answer the Sign in card in chat (the login link works from any device) or finish signing in from the Terminal tab, then send again":t==="claude"&&rt.claude.auth.test(u)?d="claude isn't signed in on this box \u2014 answer the setup cards in chat (or finish signing in from the Terminal tab), then send again":t==="claude"&&rt.claude.block.test(u)&&(d=`claude is waiting on its "Press Enter to continue" screen (bostrat's Enter didn't clear it) \u2014 answer the Continue card in chat (or press Enter in the Terminal tab), then send again`),re({flow:"agent-ready",phase:"blocked",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:d}).catch(()=>{}),new Error(d)}if(await Fn(e)){let d=ev(t);throw re({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,error:d.message}).catch(()=>{}),d}return console.warn(`[agent-ready] ${t} readiness unconfirmed in ${e} after ${n}ms \u2014 driving the box anyway.`),re({flow:"agent-ready",phase:"timeout",sessionName:e,agent:t,timeoutMs:n,gateAccepts:i,paneBlocked:!!rt[t]?.block?.test(u),paneUp:!!rt[t]?.up?.test(u),paneBusy:!!rt[t]?.busy?.test(rv(rt[t],u))}).catch(()=>{}),await Ft(ts),"timeout"}await Ft(es)}}function ic({agent:e,agentCmd:t}={}){let n=t?Xr(t):Xr(e||"");return{label:n,realClaude:t?Jo(t):n==="claude",realCodex:t?/codex/.test(t)&&!/fake-codex|fake-lead/.test(t):n==="codex",realCursor:t?/cursor/.test(t)&&!/fake-cursor|fake-box/.test(t):n==="cursor"}}function sv({agent:e,agentCmd:t}={}){let{realClaude:n,realCodex:r,realCursor:o}=ic({agent:e,agentCmd:t});return n||r||o||process.env.BOSTRAT_FAKE_POLL_READY==="1"}function ns({cwd:e,sessionName:t,agent:n,agentCmd:r,onReady:o,onBlocked:s}={}){let{realCodex:a,realCursor:i}=ic({agent:n,agentCmd:r});return!t||!a&&!i?null:wr({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 wr({cwd:e,sessionId:t,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=D1(),bailOnDialog:a=!1,launchEcho:i=null,onReady:c}={}){let{label:l,realClaude:u,realCodex:d,realCursor:p}=ic({agent:r,agentCmd:o});if(u&&e&&await Wr(e),d&&e&&await Ys(e),p&&e&&await Xs(e),(u||d||p)&&n)return await tv(n,l,s,{bailOnDialog:a,launchEcho:i,onReady:c});let f=rt[r]?r:rt[l]?l:null;return process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&f?await tv(n,f,s,{launchEcho:i}):(await Ft(ts),"settle")}var D1,es,ts,B1,L1,rt,F1,U1,H1,W1,G1,Vu,K1,nv,z1,J1,rs=G(()=>{pt();_i();su();au();Uu();Hu();bn();Jm();D1=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,es=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,ts=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,B1=/^\s{0,4}[^\s›•■◦╭╰│]\S*(?: \S+){0,3} · \S/,L1=/^\s*tab to queue message\b/;rt={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,busyScope:"footer",busyAlt:/^[·✢✳✶✻✽✺✹✸✷]\s+[A-Z][\w '’-]{1,40}…/m,auth:/Select login method:|Paste code here if prompted|OAuth error:|Choose the text style that looks best|Quick safety check: Is this a project you created/,block:/Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/m},codex:{up:{test:j1},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/}},F1=/Trust this workspace|\[a\]\s*Trust/,U1=/Do you trust the contents of this directory\?[\s\S]*›\s*1\. Yes, continue[\s\S]*Press enter to continue/,H1=/Resume from summary/i,W1=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;G1=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,Vu=new Map;K1=68,nv=/^\s*[─━]{10,}(?: [^─━\n]+ [─━]+)?\s*$/;z1=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,J1=15});function tB(e){return typeof e!="string"||!e?!1:e.includes(`
|
|
124
|
+
`)?!0:Buffer.byteLength(e,"utf8")>=eB}function Un(e,t){return e==="codex"?{enter:!0,submitDelayMs:Z1,...tB(t)?{paste:!0}:{}}:e==="claude"?{enter:!0,submitDelayMs:Q1}:{enter:!0}}var Z1,Q1,eB,cc=G(()=>{Z1=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),Q1=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),eB=(()=>{let e=Number(process.env.BOSTRAT_CODEX_PASTE_MIN_BYTES??512);return Number.isFinite(e)&&e>=0?e:512})()});function lc(e,t){if(e==="codex")return aB(t);if(e!=="claude")return null;let n=String(t).replace(/\r/g,"").split(`
|
|
125
|
+
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(av.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(av.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(nB);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 aB(e){let t=String(e).replace(/\r/g,"").split(`
|
|
126
|
+
`).map(o=>o.replace(/\s+$/,"")),n=-1;for(let o=t.length-1;o>=0;o--)if(t[o]){(rB.test(t[o])||oB.test(t[o]))&&!Xm.test(t[o])?n=o:Xm.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(Xm);if(a){r.unshift(a[1]);let i=r.map(c=>c.trim()).filter(Boolean);return i.length?i.join(" "):null}if(sB.test(s))return null;r.unshift(s)}return null}function Zm(e,t){let n=String(t||"");if(!/\[Pasted Content \d+ chars\]/.test(n))return!1;let r=0,o=He(n.replace(iB,(i,c)=>(r+=Number(c)," "))),s=[...String(e||"")].length;return o&&!He(e).includes(o)?!1:r+o.length<=s+Math.max(4,Math.round(s*.05))}function He(e){return String(e||"").replace(/\s+/g," ").trim()}function iv(e,t,{slackMs:n=2e4}={}){let r=He(t?.text);if(!r||typeof t?.at!="number")return!1;let o=Array.isArray(e)?e:[],s=o.findIndex(a=>a?.role==="user"&&typeof a.ts=="number"&&a.ts>=t.at-n&&He(a.text).includes(r));return s>=0&&s<o.length-1}function cv(e,t,n,{busy:r=!1}={}){let o=lc(e,n);return o&&Yu(t,o)||o&&e==="codex"&&Zm(t,o)?"stuck":r&&e!=="codex"||o?"submitted":"unknown"}function Yu(e,t){let n=He(e),r=He(t).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var av,nB,Xm,rB,oB,sB,iB,uc=G(()=>{av=/^\s*─{8,}(?: [^─\n]+ ─+)?\s*$/,nB=/^❯[ ]?(.*)$/,Xm=/^\s*›[ ]?(.*)$/,rB=/\S+ · \S/,oB=/^\s*(?:\? for shortcuts|tab to queue message)\b/,sB=/^\s*(?:[•■◦✶✻⚠]|╭|╰|│)/;iB=/\[Pasted Content (\d+) chars\](?: #\d+)?/g});async function pc({sessionName:e,agent:t,agentCmd:n=null,text:r,trackId:o=null,flow:s="say",stats:a=null},i={}){let c=(i.now||Date.now)(),l=(P,j,T={})=>(a&&typeof a=="object"&&Object.assign(a,{verdict:P,retries:T.retries??0,ms:(i.now||Date.now)()-c,sawUp:!!T.sawUp,reason:T.reason??null}),j);if(!cB.has(t))return l("skipped",!0);let u=i.waitForUp??sv({agent:t,agentCmd:n}),d=i.capture||(P=>Ne(P,{lines:ze})),p=i.pressEnter||(P=>Ke(P,["Enter"],{kind:"submit-retry"})),f=i.sleep||pB,w=i.now||Date.now,h=i.log||(P=>re(P).catch(()=>{})),y=i.maxMs??dB(),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(dc[0]);;){let P=await d(e).catch(()=>""),j=to(t,P),T=ya(t,P),$=cv(t,r,P,{busy:T});if($==="submitted")return l("submitted",!0,{retries:x,sawUp:!0});if(u&&(!j||$==="stuck"&&T)){if(w()>=k)return $==="stuck"?(h({...g,phase:"submit-failed",reason:"stuck-at-deadline",retries:x}),l("failed",!1,{retries:x,sawUp:M,reason:"stuck-at-deadline"})):(h({...g,phase:"submit-unverified",reason:M?"cli-down":"cli-never-up",retries:x,waitedMs:y}),l("unverified",!0,{retries:x,sawUp:M,reason:M?"cli-down":"cli-never-up"}));await f(lB);continue}if(M=!0,$==="stuck"){if(x>=uB)return h({...g,phase:"submit-failed",retries:x}),l("failed",!1,{retries:x,sawUp:!0,reason:"stuck"});x+=1,S=0,_=0,await p(e),h({...g,phase:"submit-retry",attempt:x}),await f(dc[0]);continue}if(S+=1,S>=dc.length||w()>=k)return l("assumed",!0,{retries:x,sawUp:!0});_+=1,await f(dc[Math.min(_,dc.length-1)])}}var cB,dc,lB,uB,dB,pB,Qm=G(()=>{pt();rs();uc();bn();cB=new Set(["claude","codex"]),dc=[250,450,700],lB=500,uB=2,dB=()=>{let e=Number(process.env.BOSTRAT_SUBMIT_VERIFY_MAX_MS);return Number.isFinite(e)&&e>0?e:3e4},pB=e=>new Promise(t=>setTimeout(t,e))});function lv(e){return String(e||"").replace(/[\u2018\u2019\u201A\u02BC\u0060\u00B4]/g,"'").replace(/[\u201C\u201D\u201E]/g,'"')}function gr(e){let t=lv(e).toLowerCase();if(!t.trim())return null;let n=null;for(let r of fB){let o=0;for(let s of mB[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function uv(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))hB.has(r)&&(t+=o||0),wB.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function fc(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 dv(e){let t=lv(e).trim();return t?yB.test(t)?!0:t.split(/\s+/).length<=gB:!1}var fB,mB,hB,wB,gB,yB,Xu=G(()=>{fB=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],mB={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/]};hB=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),wB=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);gB=12,yB=/^\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 hv(e){let t=fc(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||bB.has(r)||n.push(r);return n}function kB(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(mv(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(mv(n,"assistant",o))}}return t}function mv(e,t,n){let r=gr(n);return{id:e.id,seq:e.seq,ts:e.payload?.ts??e.ts,role:t,boxId:e.boxId??null,provider:e.provider??null,text:n,tags:r?[r.category]:[]}}function mc(e){let t=kB(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=hv(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 Zu(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(hv(t))];if(!i.length)return[];let c=new Map;for(let u of i){let d=e.postings.get(u);if(!d)continue;let p=d.size,f=Math.log(1+(e.N-p+.5)/(p+.5));for(let[w,h]of d){let y=e.lengths[w],k=h+pv*(1-fv+fv*(y/(e.avgdl||1))),g=f*(h*(pv+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 pv,fv,bB,eh=G(()=>{Xu();pv=1.5,fv=.75,bB=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 Qu from"node:fs/promises";import wv from"node:path";async function xB(e){let t=wv.join(e,".bostrat","events"),n;try{n=await Qu.readdir(t)}catch{return mc([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await Qu.readFile(wv.join(t,o),"utf8");for(let a of s.split(`
|
|
127
|
+
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return mc(r)}function vB(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}
|
|
128
|
+
${o}`.trim()||t.slice(0,2e3)}function SB(e,t,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=Zu(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?["",`## ${gv}, 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(`
|
|
129
|
+
`):""}async function yv(e,t,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await Qu.readFile(t,"utf8");if(a.includes(gv))return{appended:!1,reason:"already-enriched"};let i=await xB(e);if(!i.N)return{appended:!1,reason:"empty-index"};let c=SB(i,vB(a),{k:r,maxChars:o,exclude:s});return c?(await Qu.appendFile(t,`
|
|
130
130
|
`+c),{appended:!0,chars:c.length,chunks:c.split(`
|
|
131
|
-
- `).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 gv,bv=
|
|
132
|
-
`).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
|
|
131
|
+
- `).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 gv,bv=G(()=>{eh();gv="Relevant prior discussion (verbatim, retrieved"});function $B(e){return AB.test(String(e||"").trimStart())}function vv(e){let t=String(e||"");if(!$B(t.trimStart()))return null;let n=t.replace(TB,"").trim(),r=RB(n),o=[];for(let u of _B){let d=u.from.map(p=>ba(r.get(p))).find(p=>p&&!kv(p));d&&o.push({h:u.h,text:hc(d,EB)})}let s=ba(r.get("Current Work"))||ba(r.get("Primary Request and Intent"))||MB(n),a=td(s),i=hc(ed(rh(a[0]||"")),IB)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(hc(ed(rh(l)),nh));for(let u of["Pending Tasks","Optional Next Step"]){let d=td(ba(r.get(u)))[0]||"";if(!(!d||kv(d))){c.push(hc(`Left to do: ${ed(rh(d))}`,nh));break}}if(!c.length){let u=td(ba(r.get("Problem Solving"))||"")[0];u&&c.push(hc(ed(u),nh))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function RB(e){let t=new Map,n=[];th.lastIndex=0;for(let r=th.exec(e);r;r=th.exec(e))n.push({name:OB(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 OB(e){return xv.find(n=>n.toLowerCase()===e.toLowerCase())||e}function ba(e){return e?String(e).replace(/```[\s\S]*?(?:```|$)/g," ").split(`
|
|
132
|
+
`).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 MB(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return ba(t.split(/\n{2,}/).slice(0,2).join(" "))}function td(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 rh(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 ed(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function kv(e){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(e||"").trim())}function hc(e,t){let n=String(e||"").trim();if(n.length<=t)return n;let r=td(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 AB,xv,th,TB,EB,_B,IB,nh,Sv=G(()=>{AB=/^This session is being continued from a previous conversation that ran out of context\./,xv=["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"],th=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${xv.join("|")})[:*\t ]*$`,"gim"),TB=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,EB=420,_B=[{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"]}],IB=116,nh=168});function Ev(e){return oh.has(String(e||"").replace(/^\//,"").toLowerCase())}function wc(e){let t=String(e||"").trim();if(!t)return null;let n=vv(t);if(n)return n;let r=t.match(CB);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(NB);if(o)return{kind:"skill",name:o[1]};let s=t.match(PB);if(s)return{kind:"skill",name:s[1].toLowerCase()};let a=t.match(DB);if(a){let c=a[1].toLowerCase();return oh.has(c)?{kind:"suppress"}:{kind:"command",command:`/${c}`}}let i=t.match(KB);if(i&&oh.has(i[1].toLowerCase()))return{kind:"suppress"};if(BB.test(t))return{kind:"suppress"};if(jB.test(t))return{kind:"suppress"};if(LB.test(t))return{kind:"suppress"};if(FB.test(t)){let c=u=>YB(t.match(u)?.[1].trim()||"")||null,l=c(GB);return{kind:"task",summary:c(UB)||"",taskId:c(HB),toolUseId:c(WB),status:c(qB)?.toLowerCase()||null,resultHead:l?l.length>Av?`${l.slice(0,Av).trimEnd()}\u2026`:l:null}}return null}function _v(e){let t=wc(e?.text);return t||(e?.injected?{kind:"skill",name:e.injected.skill||"skill"}:null)}function Iv(e){let t=String(e||""),n=t.match(VB);if(n)return{kind:"shell-input",command:n[1].trim()};let r=t.match(zB);return r?{kind:"shell-output",stdout:Tv(r[1]),stderr:Tv(r[2])}:null}function YB(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function $v(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 Rv(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var CB,NB,PB,DB,BB,LB,jB,FB,UB,HB,WB,qB,GB,Av,oh,KB,VB,zB,JB,Tv,nd=G(()=>{Sv();CB=/^Base directory for this skill:\s*(\S+)/,NB=/^Run the "([^"]+)" workflow\./,PB=/^#\s*\/([a-z0-9:_-]+)\s*[—–:-]/i,DB=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,BB=/^<local-command-(?:stdout|caveat)>/i,LB=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,jB=/^Continue from where you left off\.$/,FB=/^<task-notification>/i,UB=/<summary>([\s\S]*?)<\/summary>/i,HB=/<task-id>([\s\S]*?)<\/task-id>/i,WB=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,qB=/<status>([\s\S]*?)<\/status>/i,GB=/<result>([\s\S]*?)(?:<\/result>|$)/i,Av=600,oh=new Set(["model","clear","compact","login","exit","quit"]);KB=/^\/([a-z0-9:_-]+)$/i;VB=/^\s*<bash-input>([\s\S]*?)<\/bash-input>\s*$/i,zB=/^\s*<bash-stdout>([\s\S]*?)<\/bash-stdout>\s*(?:<bash-stderr>([\s\S]*?)<\/bash-stderr>)?\s*$/i,JB=/\x1b\[[0-9;?]*[ -/]*[@-~]/g,Tv=e=>String(e||"").replace(JB,"").replace(/^\n+/,"").replace(/\s+$/,"")});import XB from"node:crypto";function tL(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=ka(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,ZB)){let c=ka(i?.label);if(!c)return null;let l=ka(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<QB)return null;n.push({question:o,header:ka(r?.header).slice(0,eL),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function sh(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;Ov.lastIndex=0;let t=null;for(let o;o=Ov.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=tL(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function ah(e,t,n){let r=XB.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${e}:ask:${t||"turn"}:${r}`}function ih(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||sh(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=ah(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 Cv(e,t,n,r){let o=[];(e?.questions||[]).forEach((i,c)=>{let l=(t?.[c]||[]).map(d=>i.options?.[d]?.label).filter(Boolean),u=ka(Array.isArray(r)?r[c]:"");u&&l.push(u),l.length&&o.push({name:i.header||i.question,picks:l.join(", ")})});let s=ka(n),a;return o.length?o.length===1?a=`${o[0].name}: ${o[0].picks}`:a=`Answers:
|
|
133
133
|
${o.map(i=>`- ${i.name}: ${i.picks}`).join(`
|
|
134
134
|
`)}`:a=s,o.length&&s&&(a+=`
|
|
135
|
-
${s}`),a||""}var Mv,
|
|
135
|
+
${s}`),a||""}var Mv,ZB,QB,eL,Ov,ka,Nv,ch=G(()=>{Mv=4,ZB=4,QB=2,eL=24,Ov=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,ka=e=>typeof e=="string"?e.trim():"";Nv=`
|
|
136
136
|
When you are BLOCKED on a decision only the operator can make \u2014 an ambiguous
|
|
137
137
|
requirement, an irreversible choice, several valid approaches \u2014 do not bury the
|
|
138
138
|
question in prose. End your message with exactly ONE fenced code block tagged
|
|
@@ -153,91 +153,91 @@ your turn. The operator sees the questions as an interactive card in their chat
|
|
|
153
153
|
They may instead reply in free text \u2014 treat that as the answer. Never use the block for rhetorical questions, progress updates, or
|
|
154
154
|
anything you can decide yourself; keep asking in prose for questions without
|
|
155
155
|
enumerable options.
|
|
156
|
-
`});function eL(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 Dv(e,t,n){let r=lh(n),o=e.has(r)?r:null;if(!o&&n.number!=null)for(let[i,c]of e){if(Number(c.number)!==Number(n.number))continue;let l=!!(n.url&&c.url&&n.url===c.url),u=!c.repo&&!!n.repo||!!c.repo&&!n.repo;if(l||u&&(!c.url||!n.url||c.url===n.url)){o=i;break}}let s=n.repo||!o?r:o,a=o?e.get(o):e.get(s);if(o&&o!==s){e.delete(o);let i=t.get(o);if(t.delete(o),i?.length){let c=t.get(s)??[];t.set(s,c.concat(i))}}return e.set(s,{number:n.number,url:n.url??a?.url??null,title:n.title??a?.title??null,repo:n.repo??a?.repo??null,branch:n.branch??a?.branch??null}),s}function Hn(e,{now:t=Date.now()}={}){let n=[],r=new Set,o=new Set,s=new Map,a=new Map,i=new Set,c=new Map,l=new Map,u=new Map,d=(b,m)=>{m!=null&&(u.has(b)||u.set(b,[]),u.get(b).push(m))},p=(b,m)=>{let v=null;for(let D of[b,null])for(let J of u.get(D)||[])J>m&&(v==null||J<v)&&(v=J);return v},f=new Set,w=new Map,h=new Map,y=(b,m)=>{h.set(b,m),n.push(m)},k=b=>{let m=h.get(b);m&&(m.retired=!0)},g=/^Agent "([\s\S]+)" finished$/,x=new Map;for(let b of e||[]){if(b.type!=="tool.called"||b.source!=="transcript")continue;let m=String(b.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(m))for(let v of m.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let D=v[0].split("/").pop().toLowerCase();x.has(D)||x.set(D,[]),x.get(D).push(b.payload?.ts??b.ts)}}let A=3*6e4,I=20*6e4,M=(b,m)=>!!(b?.name&&x.get(String(b.name).toLowerCase()))?.some(D=>D>=m-A&&D<=m+I);for(let b of e||[])if(b.type==="turn.retracted"){o.add(`${b.boxId}|${b.payload?.saidTs}`);let m=He(b.payload?.text);m&&(s.has(b.boxId)||s.set(b.boxId,[]),s.get(b.boxId).push(m))}else if(b.type==="turn.user"&&b.source==="orchestrator"){let m=He(b.payload?.text);m&&(a.has(b.boxId)||a.set(b.boxId,new Set),a.get(b.boxId).add(m))}else if(b.type==="job.stalled"&&b.payload?.id)c.set(b.payload.id,{ts:pe(b),quietMs:Number(b.payload.quietMs)||null});else if(b.type==="job.stopped"&&b.payload?.id)l.has(b.payload.id)||l.set(b.payload.id,{status:"killed",summary:null,resultHead:null,ts:pe(b)});else if(b.type==="session.ready"){let m=b.payload?.source;m!=="compact"&&m!=="clear"&&d(b.boxId??null,pe(b))}else if(b.type==="handoff.done")d(b.boxId??b.payload?.from??null,pe(b));else if(b.type==="box.state.changed"&&b.payload?.change==="archived")d(null,pe(b));else if(b.type==="job.started"&&b.payload?.id)i.add(b.payload.id),(b.payload.kind||"agent")==="agent"&&b.payload.name&&f.add(b.payload.name);else if(b.type==="turn.user"&&b.payload?.note?.kind==="task"){let m=b.payload.note,v={status:m.status||null,summary:m.summary||null,resultHead:m.resultHead??null,ts:pe(b)};m.toolUseId&&l.set(m.toolUseId,v);let D=String(m.summary||"").match(g);D&&w.set(D[1],v)}let P=new Map,L=new Map,T=null,E=new Map,j=new Map,G=new Map,R=new Map,S=new Map,C=new Map,H=new Map,B=new Map,Z=new Map,xe=new Map,Ae=new Map,_e=new Map,Q=new Map,Xe=new Map,Pe=new Map,le=new Map,ge=(b,m)=>{b.arm.status=m;let v=b.line,D=v.arms.map(J=>J.status);v.status=D.includes("armed")?"armed":D.includes("offered")?"offered":D.includes("replaced")?"replaced":"cancelled"},de=[],ee=new Set,we=new Map,Oe=(b,m)=>{we.delete(b);let v=n.indexOf(b);v>=0&&n.splice(v,1),m!=null&&(b.ts=m),n.push(b)},Ve=b=>{if(de.push(b),!(b.box?ee.has(b.box):ee.size>0))return;let v=n.indexOf(b);v>=0&&n.splice(v,1),we.set(b,"held")},Nt=b=>{if(!de.length)return;let m=pe(b);for(let v of de)v.status!=="open"||we.get(v)==="superseded"||v.box&&v.box!==b.boxId||(v.ts??0)>m||Oe(v,m)},O=b=>{if(!de.length)return;let m=pe(b);for(let v of de)we.get(v)!=="held"||v.status!=="open"||v.box&&b.boxId&&v.box!==b.boxId||Oe(v,Math.max(v.ts??0,m))},X=()=>{for(let[b,m]of[...we])m==="held"&&Oe(b,null);de.length=0,ne.clear()},ne=new Set,he=b=>{for(let m of ne){if(m.status!=="open"||m.box&&b&&m.box!==b)continue;m.status="superseded";let v=n.indexOf(m);v>=0&&n.splice(v,1),we.set(m,"superseded"),E.delete(m.offerId)}ne.clear()},Be=new Map,Pt=new Map,tt=new Map,wn=new Map,Kt=new Map,ht=new Map,wt=new Map,at=new Map,Si=()=>{for(let b of at.values())b.questions.length&&n.push(b);at.clear()},gt=null,cr=new Map,Hr=6e4,lr=new Map,gn=(b,m)=>{let v=s.get(b);if(!v?.length)return!1;let D=He(m),J=a.get(b);return v.some(ie=>{if(!D.startsWith(ie))return!1;let ve=He(D.slice(ie.length));return!!ve&&!!J?.has(ve)})};for(let b of e||[]){let m=b.payload||{};switch(b.type){case"turn.user":{if(b.source==="hook"){b.boxId&&ee.add(b.boxId);break}if(X(),!String(m.text||"").trim()&&!m.images?.length&&!m.attachments?.length)break;{let v=Iv(m.text);if(v){if(v.kind==="shell-input"){let D={id:b.id,kind:"shell",command:v.command,stdout:null,stderr:null,done:!1,ts:pe(b),box:b.boxId,agent:b.provider};n.push(D),cr.set(b.boxId,D)}else{let D=cr.get(b.boxId);D?(D.stdout=v.stdout||null,D.stderr=v.stderr||null,D.done=!0,cr.delete(b.boxId)):n.push({id:b.id,kind:"shell",command:null,stdout:v.stdout||null,stderr:v.stderr||null,done:!0,ts:pe(b),box:b.boxId,agent:b.provider})}break}}{let v=m.note||wc(m.text);if(v){if(v.kind==="task"){if(v.toolUseId&&i.has(v.toolUseId))break;let D=String(v.summary||"").match(g);if(!v.toolUseId&&D&&f.has(D[1]))break}if(v.kind==="command"&&Ev(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:Rv(v),name:v.name??null,command:v.command??null,summary:v.summary??null,status:v.status??null,ts:pe(b),box:b.boxId,agent:b.provider});break}}if(o.has(`${b.boxId}|${pe(b)}`)||b.source==="transcript"&&gn(b.boxId,m.text))break;if(m.kind==="merge-notify"||m.kind==="ship-notify"){n.push({id:b.id,kind:"event",type:m.kind==="ship-notify"?"ship-notified":"merge-notified",text:m.text,ts:pe(b),box:b.boxId,agent:b.provider});break}if(m.kind==="mock-picked"||m.kind==="ci-fix"||m.kind==="reconcile"||m.kind==="pr-go"||m.kind==="push-go"||m.kind==="spin-up-view"||m.kind==="prompt-answer"||m.kind==="followup-go"||m.kind==="login-greet"){m.kind==="prompt-answer"&&(gt=null);break}for(let v of E.values())v.status="stale";E.clear();for(let v of G.values())v.status="stale";G.clear();for(let v of Q.values())v.rows.some(D=>D.rowStatus==="sent")||(v.status="stale",Q.delete(v.offerId));gt&>.box===b.boxId&&(n.push(eL(gt)),gt=null),n.push({id:b.id,kind:"message",role:"user",text:m.text,ts:pe(b),box:b.boxId,agent:b.provider,boxRole:m.boxRole,...b.actor==="orchestrator"?{orchestration:m.kind||!0}:{},...m.sourceId?{sourceId:m.sourceId}:{},...m.attachments?.length?{attachments:m.attachments}:{},...m.images?.length?{images:m.images}:{}});break}case"peer.message":{let v=m.from||{};n.push({id:b.id,kind:"event",type:"peer-message",text:m.text,from:v.trackName||v.boxId||v.name||null,fromTrackId:v.trackId||null,ts:pe(b),box:b.boxId,agent:b.provider});break}case"turn.agent":{if(m.model==="<synthetic>"&&/^No response requested\.$/.test(String(m.text||"").trim()))break;let v=Ox(m,{codexFailedText:Zo});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}Si();let D=(m.images||[]).filter(yt=>!M(yt,pe(b)));if(b.source==="hook"&&!String(m.text||"").trim()&&(O(b),b.boxId&&ee.delete(b.boxId)),!String(m.text||"").trim()&&!D.length)break;if(b.provider==="cursor"){let yt=`${b.boxId}|${m.text}`;if(r.has(yt))break;r.add(yt)}(!gt||gt.box===b.boxId)&&(gt=null);let J=b.provider==="codex"?(m.tools||[]).filter(yt=>!Hm.has(yt)&&!tc.has(yt)):m.tools||[],ie=m.text,ve=null,_t=sh(String(m.text||""));_t&&(ie=_t.text,ve={questions:_t.questions}),n.push({id:b.id,kind:"message",role:"assistant",text:ie,ts:pe(b),box:b.boxId,agent:b.provider,boxRole:m.boxRole,...ve?{ask:ve}:{},...J.length?{tools:J}:{},...m.model?{model:m.model}:{},...m.sourceId?{sourceId:m.sourceId}:{},...D.length?{images:D}:{}}),Nt(b),ve&&(gt={id:b.id,groupId:ah(b.provider,m.sourceId||b.id,ve.questions),provider:b.provider,box:b.boxId,questions:ve.questions,ts:pe(b)});break}case"job.started":{if(!m.id)break;let v=pe(b),D=l.get(m.id);if(!D&&(m.kind||"agent")==="agent"&&m.name){let rn=w.get(m.name);rn&&rn.ts>=v&&(D=rn)}let J=D?null:c.get(m.id)||null,ie=D||J?null:p(b.boxId??null,v),ve={jobId:m.id,jobKind:m.kind||"agent",name:m.name||null,detail:m.detail||null,status:D?D.status==="killed"?"stopped":D.status&&D.status!=="completed"?"failed":"done":J?"stalled":ie!=null?"lost":"running",startedTs:v,finishedTs:D?.ts??J?.ts??ie??null,quietMs:J?.quietMs??null,summary:D?.summary??null,resultHead:D?.resultHead??null},_t=lr.get(b.boxId),yt=_t?.jobs[_t.jobs.length-1]?.startedTs;if(_t&&yt!=null&&v-yt<=Hr)_t.jobs.push(ve);else{let rn={id:b.id,kind:"job-dock",dockId:m.id,ts:v,box:b.boxId,agent:b.provider,jobs:[ve]};lr.set(b.boxId,rn),n.push(rn)}break}case"handoff.done":n.push(bt(b,"lead-handoff"));break;case"subtask.delegated":n.push({...bt(b,"subtask-delegated"),to:b.boxId,toAgent:b.provider});break;case"subtask.merged":n.push(bt(b,"subtask-merged"));break;case"pr.state.changed":{if(!m.change)break;if(m.change==="opened"){for(let D of E.values())D.status="stale";E.clear();for(let D of G.values())D.status="stale";G.clear()}let v=bt(b,`pr-${m.change}`);if(v.box=b.boxId??null,Pv.has(m.change)&&m.number!=null){let D=Dv(P,L,m);if(P.size>=2){for(let ie of L.values())for(let ve of ie)ve.grouped=!0;v.groupPrs=[...P.values()];let J=[v];for(let ie of P.keys())L.set(ie,J)}else{let J=L.get(D)??[];J.push(v),L.set(D,J)}}else if(m.change==="merged"||m.change==="closed"||m.change==="lost"){let D=lh(m),J=T&&!T.complete||P.size>=2;if(P.has(D))P.delete(D),L.delete(D);else if(m.number!=null)for(let ie of[...P.keys()])ie.endsWith(`#${m.number}`)&&(P.delete(ie),L.delete(ie));if(J&&m.number!=null){(!T||T.complete)&&(T={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(T)),T.prs.push({number:m.number,url:m.url??null,title:m.title??null,repo:m.repo??null,branch:m.branch??null,change:m.change,ts:pe(b)}),T.remaining=[...P.values()],T.complete=P.size===0;break}}n.push(v);break}case"pr.offered":{if(!m.offerId)break;he(b.boxId);for(let D of E.values())D.status="stale";E.clear();for(let D of G.values())D.status="stale";G.clear();let v={id:b.id,kind:"pr-offer",offerId:m.offerId,excerpt:m.excerpt||null,branch:m.branch||null,base:m.base||null,offered:Array.isArray(m.offered)&&m.offered.length?m.offered:null,toPr:m.toPr&&typeof m.toPr=="object"?{number:m.toPr.number??null}:null,available:Array.isArray(m.available)?m.available:null,defaults:Array.isArray(m.defaults)?m.defaults:null,prState:m.prState||null,openPr:m.openPr&&typeof m.openPr=="object"?m.openPr:null,repos:Array.isArray(m.repos)&&m.repos.length?m.repos:null,status:"open",source:b.source==="cli"||m.via==="cli"?"cli":"transcript",ts:pe(b),box:b.boxId,agent:b.provider};E.set(m.offerId,v),j.set(m.offerId,v),n.push(v),ne.add(v),v.source==="cli"&&Ve(v);break}case"pr.offer.answered":{let v=j.get(m.offerId);v&&(v.status=m.action||"dismiss",v.actions=Array.isArray(m.actions)?m.actions:null,v.notify=m.notify===!0,m.existingPr&&typeof m.existingPr=="object"&&(v.existingPr={number:m.existingPr.number??null}),E.delete(m.offerId));break}case"pr.exists.offered":{if(!m.offerId)break;for(let D of G.values())D.status="stale";G.clear();let v={id:b.id,kind:"pr-exists-offer",offerId:m.offerId,srcOfferId:m.srcOfferId??null,branch:m.branch||null,base:m.base||null,pr:m.pr&&typeof m.pr=="object"?{number:m.pr.number??null,url:m.pr.url??null,title:m.pr.title??null}:null,actions:Array.isArray(m.actions)&&m.actions.length?m.actions:null,notify:m.notify===!0,status:"open",ts:pe(b),box:b.boxId,agent:b.provider};G.set(m.offerId,v),R.set(m.offerId,v),n.push(v);break}case"pr.exists.answered":{let v=R.get(m.offerId);v&&(v.status=m.choice||"dismiss",G.delete(m.offerId));break}case"box.state.changed":{let v=m.change==="model"?"model-change":m.change;if(!v)break;let D=bt(b,v);if(v==="model-change"&&(D.to=m.model),v==="node-update"&&D.latest){let J=n.findIndex(ie=>ie.kind==="event"&&ie.type==="node-update"&&ie.latest===D.latest);if(J>=0){let ie=n[J];n[J]={...D,ts:ie.ts,at:ie.at,firstAt:ie.firstAt??ie.ts,updatedAt:D.ts};break}}n.push(D);break}case"track.transferred":case"track.recovered":n.push(bt(b,"track-moved"));break;case"view.state.changed":{if(m.change==="deleted"){k(`view:${m.viewId}`);break}if(m.change!=="created"||m.restarted)break;y(`view:${m.viewId}`,{id:b.id,kind:"view-card",viewId:m.viewId,name:m.name,slug:m.slug,port:m.port,origin:m.origin,ts:pe(b),box:b.boxId,agent:b.provider});break}case"view.spinup.failed":{n.push({id:b.id,kind:"view-spinup-failed",name:m.name||null,reason:m.reason||"no-view",ts:pe(b),box:b.boxId,agent:b.provider});break}case"mock.state.changed":{if(m.change==="picked"){n.push({id:b.id,kind:"mock-picked",mockId:m.mockId,name:m.name,group:m.group??null,version:m.version??null,siblings:m.siblings??[],ts:pe(b),box:b.boxId,agent:b.provider});break}if(m.change==="deleted"){k(`mock:${m.mockId}`);break}if(m.change!=="created")break;y(`mock:${m.mockId}`,{id:b.id,kind:"mock-card",mockId:m.mockId,name:m.name,group:m.group??null,ts:pe(b),box:b.boxId,agent:b.provider});break}case"flow.state.changed":{if(m.change==="deleted"){k(`flow:${m.flowId}`);break}if(m.change!=="created")break;y(`flow:${m.flowId}`,{id:b.id,kind:"flow-card",flowId:m.flowId,name:m.name,stepCount:m.stepCount??null,mockIds:m.mockIds??[],ts:pe(b),box:b.boxId,agent:b.provider});break}case"runbook.state.changed":{if(m.change==="deleted"){k(`runbook:${m.runbookId}`);break}if(m.change!=="created")break;y(`runbook:${m.runbookId}`,{id:b.id,kind:"runbook-card",runbookId:m.runbookId,name:m.name,ts:pe(b),box:b.boxId,agent:b.provider});break}case"ticket.linked":{n.push({id:b.id,kind:"ticket-card",provider:m.provider||"linear",key:m.key,url:m.url,title:m.title??null,linkSource:m.linkSource??null,ts:pe(b),box:b.boxId,agent:b.provider});break}case"box.fullscreen.detected":{if(!m.offerId)break;for(let D of H.values())D.box===(m.box||b.boxId)&&(D.status="stale",H.delete(D.offerId));let v={id:b.id,kind:"fullscreen-offer",offerId:m.offerId,status:"open",ts:pe(b),box:m.box||b.boxId,agent:m.agent||b.provider};H.set(m.offerId,v),B.set(m.offerId,v),n.push(v);break}case"box.fullscreen.answered":{let v=B.get(m.offerId);v&&(v.status=m.action==="restarted"?"restarted":m.action==="cleared"?"cleared":"dismiss",H.delete(m.offerId));break}case"ticket.move.offered":{if(!m.offerId)break;for(let D of S.values())D.key===m.key&&(D.status="stale",S.delete(D.offerId));let v={id:b.id,kind:"ticket-offer",offerId:m.offerId,provider:m.provider||"linear",key:m.key,url:m.url??null,phase:m.phase??null,from:m.from??null,to:m.to?.name??null,prNumber:m.prNumber??null,status:"open",ts:pe(b),box:b.boxId,agent:b.provider};S.set(m.offerId,v),C.set(m.offerId,v),n.push(v);break}case"ticket.move.answered":{let v=C.get(m.offerId);v&&(v.status=m.action==="moved"?"moved":"dismiss",S.delete(m.offerId));break}case"ticket.state.changed":{for(let v of S.values())v.key===m.key&&v.to===m.to&&(v.status="stale",S.delete(v.offerId));if(m.mode==="offer")break;n.push(bt(b,"ticket-state"));break}case"ticket.commented":{n.push({id:b.id,kind:"ticket-comment",key:m.key,url:m.url??null,excerpt:m.excerpt??null,ts:pe(b),box:b.boxId,agent:b.provider});break}case"ticket.flow.changed":n.push(bt(b,"ticket-flow"));break;case"ticket.create.offered":{if(!m.proposalId)break;for(let D of Z.values())D.status="stale";Z.clear();let v={id:b.id,kind:"ticket-create-offer",proposalId:m.proposalId,provider:m.provider||"linear",epic:m.epic??null,issues:Array.isArray(m.issues)?m.issues:[],team:m.team??null,needsTeam:!!m.needsTeam,linkCurrentTrack:!!m.linkCurrentTrack,status:"open",ts:pe(b),box:b.boxId,agent:b.provider};Z.set(m.proposalId,v),xe.set(m.proposalId,v),n.push(v),Ve(v);break}case"ticket.create.answered":{let v=xe.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",v.createdCount=m.createdCount??null,v.failedCount=m.failedCount??null,Z.delete(m.proposalId));break}case"loop.create.offered":{if(!m.proposalId)break;for(let D of Ae.values())D.status="stale";Ae.clear();let v={id:b.id,kind:"loop-create-offer",proposalId:m.proposalId,name:m.name??"",schedule:m.schedule??"",prompt:m.prompt??"",expectation:m.expectation??null,notify:m.notify??"off",assignment:m.assignment??{provider:"claude"},repo:m.repo??null,nodeId:m.nodeId??null,nodeName:m.nodeName??null,status:"open",ts:pe(b),box:b.boxId,agent:b.provider};Ae.set(m.proposalId,v),_e.set(m.proposalId,v),n.push(v),Ve(v);break}case"loop.create.answered":{let v=_e.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",Ae.delete(m.proposalId));break}case"followup.armed":{if(!m.armId)break;let v=m.replaces?Pe.get(m.replaces):null;v&&v.arm.status==="armed"&&ge(v,"replaced");let D={armId:m.armId,seq:Number.isInteger(m.seq)?m.seq:0,label:m.label??"",prompt:m.prompt??"",why:m.why??null,standing:m.standing==="never"?"never":"offerable",status:"armed"},J=m.groupId??null,ie=J?le.get(J):null;ie||(ie={id:b.id,kind:"followup-armed",groupId:J,armId:D.armId,trigger:m.trigger??"now",label:D.label,prompt:D.prompt,why:D.why,planWhy:m.planWhy??null,arms:[],status:"armed",ts:pe(b),box:b.boxId,agent:b.provider},J&&le.set(J,ie),ie.trigger!=="now"&&n.push(ie)),ie.arms.push(D),Pe.set(m.armId,{arm:D,line:ie});break}case"followup.armed.cancelled":{let v=Pe.get(m.armId);v&&v.arm.status==="armed"&&ge(v,"cancelled");break}case"followup.offered":{if(!m.offerId)break;for(let J of Q.values())J.trigger===m.trigger&&(J.status="stale",Q.delete(J.offerId));let v=Array.isArray(m.rows)?m.rows:[];for(let J of v){let ie=J.armId?Pe.get(J.armId):null;ie&&ie.arm.status==="armed"&&ge(ie,"offered")}let D={id:b.id,kind:"followup-offer",offerId:m.offerId,trigger:m.trigger??"now",repo:m.repo??null,context:m.context&&typeof m.context=="object"?m.context:{},groupId:m.groupId??null,why:m.why??null,rows:v.map(J=>({followupId:J.followupId??null,armId:J.armId??null,label:J.label??"",prompt:J.prompt??null,promptPreview:J.promptPreview??null,promptHash:J.promptHash??null,origin:J.origin??"operator",why:J.why??null,groupId:J.groupId??null,seq:Number.isInteger(J.seq)?J.seq:0,standing:J.standing==="never"?"never":"offerable",promotable:J.armId?J.promotable!==!1:!1,promotableReason:J.promotableReason??null,rowStatus:"open"})),status:"open",answer:null,answers:[],ts:pe(b),box:b.boxId,agent:b.provider};Q.set(m.offerId,D),Xe.set(m.offerId,D),n.push(D),D.trigger==="now"&&Ve(D);break}case"followup.answered":{let v=Xe.get(m.offerId);if(!v)break;let D=new Set;m.armId&&D.add(`arm:${m.armId}`),m.followupId&&D.add(`fu:${m.followupId}`);for(let ve of Array.isArray(m.armIds)?m.armIds:[])D.add(`arm:${ve}`);for(let ve of Array.isArray(m.followupIds)?m.followupIds:[])D.add(`fu:${ve}`);let J=m.action==="sent"?"sent":m.action==="removed"?"removed":"dismiss";for(let ve of v.rows){if(ve.rowStatus!=="open")continue;let _t=ve.armId?`arm:${ve.armId}`:`fu:${ve.followupId}`;D.size&&!D.has(_t)||(ve.rowStatus=J)}let ie={followupId:m.followupId??null,armId:m.armId??null,label:m.label??null,promoted:m.promoted??null,revived:m.revived===!0,action:J,batchId:m.batchId??null};v.answers.push(ie),v.answer=ie,v.rows.some(ve=>ve.rowStatus==="open")?v.status="open":(v.status=v.rows.some(ve=>ve.rowStatus==="sent")?"sent":v.rows.some(ve=>ve.rowStatus==="removed")?"removed":"dismiss",Q.delete(m.offerId));break}case"followup.create.offered":{if(!m.proposalId)break;for(let D of Be.values())D.status="stale";Be.clear();let v={id:b.id,kind:"followup-create-offer",proposalId:m.proposalId,label:m.label??"",prompt:m.prompt??"",trigger:m.trigger??"pr.merged",repo:m.repo??null,why:m.why??null,status:"open",created:null,ts:pe(b),box:b.boxId,agent:b.provider};Be.set(m.proposalId,v),Pt.set(m.proposalId,v),n.push(v),Ve(v);break}case"followup.create.answered":{let v=Pt.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",Be.delete(m.proposalId));break}case"followup.created":{let v=m.proposalId?Pt.get(m.proposalId):null;v&&(v.created={followupId:m.followupId??null});break}case"loop.created":{let v=m.proposalId?_e.get(m.proposalId):null;v&&(v.created={loopId:m.loopId??null,nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),n.push(bt(b,"loop-created"));break}case"loop.update.offered":case"loop.delete.offered":{if(!m.proposalId||!m.loopId)break;for(let J of tt.values())J.loopId===m.loopId&&(J.status="stale",J.staleReason="superseded",tt.delete(J.proposalId));let v=b.type==="loop.update.offered",D={id:b.id,kind:v?"loop-update-offer":"loop-delete-offer",proposalId:m.proposalId,loopId:String(m.loopId),name:m.name??m.base?.name??"",base:m.base??null,...v?{patch:m.patch??{},changed:Array.isArray(m.changed)?m.changed:Object.keys(m.patch||{})}:{reason:m.reason??""},selfEdit:!!m.selfEdit,status:"open",staleReason:null,ts:pe(b),box:b.boxId,agent:b.provider};tt.set(m.proposalId,D),wn.set(m.proposalId,D),n.push(D),Ve(D);break}case"loop.update.answered":{let v=wn.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="applied"?"applied":"dismiss",tt.delete(m.proposalId));break}case"loop.delete.answered":{let v=wn.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="delete"?"deleted":m.action==="pause"?"paused":"keep",tt.delete(m.proposalId));break}case"loop.updated":{let v=m.proposalId?wn.get(m.proposalId):null;v&&(v.applied={changed:Array.isArray(m.changed)?m.changed:[],nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),n.push(bt(b,"loop-updated"));break}case"loop.deleted":n.push(bt(b,"loop-deleted"));break;case"loop.paused":n.push(bt(b,"loop-paused"));break;case"planning.change.offered":{if(!m.proposalId||!Array.isArray(m.ops)||!m.ops.length)break;for(let D of Kt.values())D.status="stale",D.staleReason="superseded",Kt.delete(D.proposalId);let v={id:b.id,kind:"planning-offer",proposalId:m.proposalId,why:m.why??"",ops:m.ops,base:m.base??{},resolved:m.resolved??{},snapped:!!m.snapped,createsMeRow:!!m.createsMeRow,status:"open",staleReason:null,applied:null,ts:pe(b),box:b.boxId,agent:b.provider};Kt.set(m.proposalId,v),ht.set(m.proposalId,v),n.push(v),Ve(v);break}case"planning.change.answered":{let v=ht.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="applied"?"applied":"dismiss",Kt.delete(m.proposalId));break}case"planning.changed":{let v=m.proposalId?ht.get(m.proposalId):null;v&&(v.applied={results:Array.isArray(m.results)?m.results:[]}),n.push(bt(b,"planning-changed"));break}case"rework.cause.offered":{if(!m.sha||wt.has(m.sha))break;let v={id:b.id,kind:"rework-cause-offer",sha:m.sha,subject:m.subject??"",linesReworked:Number(m.linesReworked)||0,origins:Array.isArray(m.origins)?m.origins:[],heuristic:m.heuristic??null,intent:m.intent??null,shas:Array.isArray(m.shas)?m.shas:null,pr:m.pr??null,retracts:0,status:"open",cause:null,ts:pe(b),box:b.boxId,agent:b.provider};wt.set(m.sha,v),n.push(v);break}case"rework.cause.answered":{let v=m.sha?wt.get(m.sha):null;v&&(v.status="answered",v.cause=m.cause??null);break}case"rework.cause.retracted":{let v=m.sha?wt.get(m.sha):null;v&&(v.status="open",v.cause=null,v.retracts=(v.retracts??0)+1);break}case"ticket.created":{let v={items:Array.isArray(m.items)?m.items:[],failed:Array.isArray(m.failed)?m.failed:[],team:m.team??null},D=m.proposalId?xe.get(m.proposalId):null;if(D){D.created=v;break}n.push({id:b.id,kind:"ticket-created",...v,ts:pe(b),box:b.boxId,agent:b.provider});break}case"error":{if(!m.kind)break;n.push(bt(b,m.kind));break}case"provider.error":n.push({...bt(b,"provider-error"),provider:b.provider,box:b.boxId,agent:b.provider});break;case"usage.cap.hit":n.push(bt(b,"usage-cap-hit"));break;case"usage.cap.warning":n.push(bt(b,"usage-cap-warning"));break;case"provider.connected":{n.push({id:b.id,kind:"event",type:"provider-connected",provider:m.provider||b.provider,account:m.account||null,reason:m.reason||null,ts:pe(b),at:pe(b),box:b.boxId,agent:b.provider});break}case"provider.login.started":break;case"prompt.answered":{if(gt=null,m.control||!m.question)break;let v=m.groupId||m.promptId;if(!v)break;let D=at.get(v);D||(D={id:b.id,kind:"prompt",groupId:v,provider:b.provider,box:b.boxId,agent:b.provider,questionTotal:m.questionTotal??1,questions:[],ts:pe(b)},at.set(v,D));let J=m.questionIndex??0,ie={index:J,header:m.header||"",question:m.question,multiSelect:!!m.multiSelect,chosen:Array.isArray(m.chosen)?m.chosen:[]},ve=D.questions.findIndex(yt=>yt.index===J&&He(yt.question)===He(ie.question));ve>=0?D.questions[ve]=ie:D.questions.push(ie),D.questions.sort((yt,nt)=>yt.index-nt.index),D.questionTotal=Math.max(D.questionTotal,m.questionTotal??1),D.questions.length>=D.questionTotal&&(n.push(D),at.delete(v));break}case"prompt.dismissed":{gt=null;let v=m.groupId||m.promptId;if(!v||!m.question)break;let J=at.get(v)||{id:b.id,kind:"prompt",groupId:v,provider:b.provider,box:b.boxId,agent:b.provider,questionTotal:m.questionTotal??1,questions:[],ts:pe(b)},ie=m.questionIndex??0;J.questions.some(ve=>ve.index===ie&&He(ve.question)===He(m.question))||(J.questions.push({index:ie,header:m.header||"",question:m.question,multiSelect:!!m.multiSelect,chosen:[]}),J.questions.sort((ve,_t)=>ve.index-_t.index)),J.answeredVia="chat",n.push(J),at.delete(v);break}default:break}}for(let[b,m]of[...we]){if(m==="superseded"){b.status!=="superseded"&&Oe(b,null);continue}(b.status!=="open"||t-(b.ts??0)>=tL)&&Oe(b,null)}let ur=h.size?n.filter(b=>!b.retired):n;return rL(ur.sort((b,m)=>(b.ts??0)-(m.ts??0)))}function rL(e){let t=new Map,n=[];for(let r of e){if(r.kind!=="message"||!r.box){n.push(r);continue}let o=r.role==="assistant"&&!String(r.text||"").trim()&&r.images?.length,s=t.get(r.box);if(o&&s&&(r.ts??0)-s.lastTs<=nL){s.item.images.push(...r.images),s.lastTs=r.ts??s.lastTs;continue}if(o){let a={...r,images:[...r.images]};t.set(r.box,{item:a,lastTs:r.ts??0}),n.push(a)}else t.delete(r.box),n.push(r)}return n}function uh(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:oL(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 Sx(n,r)}function oL(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==ma(e.command)?t:Qr(e.command,e.description||"")}function bt(e,t){let{ts:n,change:r,kind:o,...s}=e.payload||{};return{id:e.id,kind:"event",type:t,...s,at:pe(e),ts:pe(e)}}function 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)+sL,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&aL.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function Lv(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!cL.has(r.type)||!r.box)continue;let o=(r.ts??0)+iL,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 rd(e){return(e||[]).filter(t=>lL.has(t?.kind)&&t.status==="open")}function jv(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(Pv.has(o.change)&&o.number!=null)Dv(t,n,o);else if(o.change==="merged"||o.change==="closed"||o.change==="lost"){let s=lh(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,Pv,lh,tL,nL,sL,aL,iL,cL,lL,od=q(()=>{ha();Qo();ju();uc();nd();ch();pe=e=>e.payload?.ts??e.ts;Pv=new Set(["opened","ci-fixed","reconciled","pushed"]),lh=e=>`${e.repo??""}#${e.number}`;tL=3e4;nL=2*6e4;sL=12e4,aL=new Set(["archived","unarchived","opened"]);iL=2500,cL=new Set(["pr-opened","pr-merged","pr-auto-armed"]);lL=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 mL(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 wL(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 gL(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<pL&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<fL&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function Wv(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=mL(e),s=wL(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(-uL);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(Hv[r]??Hv.failover),l.push(""),o.lastPush?.sha){let p=Uv(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=Uv(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=gL(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"}:** ${hL(p.text,dL)}`),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(`
|
|
156
|
+
`});function nL(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 Dv(e,t,n){let r=lh(n),o=e.has(r)?r:null;if(!o&&n.number!=null)for(let[i,c]of e){if(Number(c.number)!==Number(n.number))continue;let l=!!(n.url&&c.url&&n.url===c.url),u=!c.repo&&!!n.repo||!!c.repo&&!n.repo;if(l||u&&(!c.url||!n.url||c.url===n.url)){o=i;break}}let s=n.repo||!o?r:o,a=o?e.get(o):e.get(s);if(o&&o!==s){e.delete(o);let i=t.get(o);if(t.delete(o),i?.length){let c=t.get(s)??[];t.set(s,c.concat(i))}}return e.set(s,{number:n.number,url:n.url??a?.url??null,title:n.title??a?.title??null,repo:n.repo??a?.repo??null,branch:n.branch??a?.branch??null}),s}function Hn(e,{now:t=Date.now()}={}){let n=[],r=new Set,o=new Set,s=new Map,a=new Map,i=new Set,c=new Map,l=new Map,u=new Map,d=(b,m)=>{m!=null&&(u.has(b)||u.set(b,[]),u.get(b).push(m))},p=(b,m)=>{let v=null;for(let D of[b,null])for(let J of u.get(D)||[])J>m&&(v==null||J<v)&&(v=J);return v},f=new Set,w=new Map,h=new Map,y=(b,m)=>{h.set(b,m),n.push(m)},k=b=>{let m=h.get(b);m&&(m.retired=!0)},g=/^Agent "([\s\S]+)" finished$/,x=new Map;for(let b of e||[]){if(b.type!=="tool.called"||b.source!=="transcript")continue;let m=String(b.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(m))for(let v of m.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let D=v[0].split("/").pop().toLowerCase();x.has(D)||x.set(D,[]),x.get(D).push(b.payload?.ts??b.ts)}}let S=3*6e4,_=20*6e4,M=(b,m)=>!!(b?.name&&x.get(String(b.name).toLowerCase()))?.some(D=>D>=m-S&&D<=m+_);for(let b of e||[])if(b.type==="turn.retracted"){o.add(`${b.boxId}|${b.payload?.saidTs}`);let m=He(b.payload?.text);m&&(s.has(b.boxId)||s.set(b.boxId,[]),s.get(b.boxId).push(m))}else if(b.type==="turn.user"&&b.source==="orchestrator"){let m=He(b.payload?.text);m&&(a.has(b.boxId)||a.set(b.boxId,new Set),a.get(b.boxId).add(m))}else if(b.type==="job.stalled"&&b.payload?.id)c.set(b.payload.id,{ts:pe(b),quietMs:Number(b.payload.quietMs)||null});else if(b.type==="job.stopped"&&b.payload?.id)l.has(b.payload.id)||l.set(b.payload.id,{status:"killed",summary:null,resultHead:null,ts:pe(b)});else if(b.type==="session.ready"){let m=b.payload?.source;m!=="compact"&&m!=="clear"&&d(b.boxId??null,pe(b))}else if(b.type==="handoff.done")d(b.boxId??b.payload?.from??null,pe(b));else if(b.type==="box.state.changed"&&b.payload?.change==="archived")d(null,pe(b));else if(b.type==="job.started"&&b.payload?.id)i.add(b.payload.id),(b.payload.kind||"agent")==="agent"&&b.payload.name&&f.add(b.payload.name);else if(b.type==="turn.user"&&b.payload?.note?.kind==="task"){let m=b.payload.note,v={status:m.status||null,summary:m.summary||null,resultHead:m.resultHead??null,ts:pe(b)};m.toolUseId&&l.set(m.toolUseId,v);let D=String(m.summary||"").match(g);D&&w.set(D[1],v)}let P=new Map,j=new Map,T=null,$=new Map,L=new Map,H=new Map,R=new Map,A=new Map,C=new Map,W=new Map,B=new Map,Z=new Map,xe=new Map,Ae=new Map,_e=new Map,Q=new Map,Xe=new Map,Pe=new Map,le=new Map,ge=(b,m)=>{b.arm.status=m;let v=b.line,D=v.arms.map(J=>J.status);v.status=D.includes("armed")?"armed":D.includes("offered")?"offered":D.includes("replaced")?"replaced":"cancelled"},de=[],ee=new Set,we=new Map,Oe=(b,m)=>{we.delete(b);let v=n.indexOf(b);v>=0&&n.splice(v,1),m!=null&&(b.ts=m),n.push(b)},Ve=b=>{if(de.push(b),!(b.box?ee.has(b.box):ee.size>0))return;let v=n.indexOf(b);v>=0&&n.splice(v,1),we.set(b,"held")},Nt=b=>{if(!de.length)return;let m=pe(b);for(let v of de)v.status!=="open"||we.get(v)==="superseded"||v.box&&v.box!==b.boxId||(v.ts??0)>m||Oe(v,m)},O=b=>{if(!de.length)return;let m=pe(b);for(let v of de)we.get(v)!=="held"||v.status!=="open"||v.box&&b.boxId&&v.box!==b.boxId||Oe(v,Math.max(v.ts??0,m))},X=()=>{for(let[b,m]of[...we])m==="held"&&Oe(b,null);de.length=0,ne.clear()},ne=new Set,he=b=>{for(let m of ne){if(m.status!=="open"||m.box&&b&&m.box!==b)continue;m.status="superseded";let v=n.indexOf(m);v>=0&&n.splice(v,1),we.set(m,"superseded"),$.delete(m.offerId)}ne.clear()},Be=new Map,Pt=new Map,tt=new Map,wn=new Map,Kt=new Map,ht=new Map,wt=new Map,at=new Map,Si=()=>{for(let b of at.values())b.questions.length&&n.push(b);at.clear()},gt=null,cr=new Map,Hr=6e4,lr=new Map,gn=(b,m)=>{let v=s.get(b);if(!v?.length)return!1;let D=He(m),J=a.get(b);return v.some(ie=>{if(!D.startsWith(ie))return!1;let ve=He(D.slice(ie.length));return!!ve&&!!J?.has(ve)})};for(let b of e||[]){let m=b.payload||{};switch(b.type){case"turn.user":{if(b.source==="hook"){b.boxId&&ee.add(b.boxId);break}if(X(),!String(m.text||"").trim()&&!m.images?.length&&!m.attachments?.length)break;{let v=Iv(m.text);if(v){if(v.kind==="shell-input"){let D={id:b.id,kind:"shell",command:v.command,stdout:null,stderr:null,done:!1,ts:pe(b),box:b.boxId,agent:b.provider};n.push(D),cr.set(b.boxId,D)}else{let D=cr.get(b.boxId);D?(D.stdout=v.stdout||null,D.stderr=v.stderr||null,D.done=!0,cr.delete(b.boxId)):n.push({id:b.id,kind:"shell",command:null,stdout:v.stdout||null,stderr:v.stderr||null,done:!0,ts:pe(b),box:b.boxId,agent:b.provider})}break}}{let v=m.note||wc(m.text);if(v){if(v.kind==="task"){if(v.toolUseId&&i.has(v.toolUseId))break;let D=String(v.summary||"").match(g);if(!v.toolUseId&&D&&f.has(D[1]))break}if(v.kind==="command"&&Ev(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:Rv(v),name:v.name??null,command:v.command??null,summary:v.summary??null,status:v.status??null,ts:pe(b),box:b.boxId,agent:b.provider});break}}if(o.has(`${b.boxId}|${pe(b)}`)||b.source==="transcript"&&gn(b.boxId,m.text))break;if(m.kind==="merge-notify"||m.kind==="ship-notify"){n.push({id:b.id,kind:"event",type:m.kind==="ship-notify"?"ship-notified":"merge-notified",text:m.text,ts:pe(b),box:b.boxId,agent:b.provider});break}if(m.kind==="mock-picked"||m.kind==="ci-fix"||m.kind==="reconcile"||m.kind==="pr-go"||m.kind==="push-go"||m.kind==="spin-up-view"||m.kind==="prompt-answer"||m.kind==="followup-go"||m.kind==="login-greet"){m.kind==="prompt-answer"&&(gt=null);break}for(let v of $.values())v.status="stale";$.clear();for(let v of H.values())v.status="stale";H.clear();for(let v of Q.values())v.rows.some(D=>D.rowStatus==="sent")||(v.status="stale",Q.delete(v.offerId));gt&>.box===b.boxId&&(n.push(nL(gt)),gt=null),n.push({id:b.id,kind:"message",role:"user",text:m.text,ts:pe(b),box:b.boxId,agent:b.provider,boxRole:m.boxRole,...b.actor==="orchestrator"?{orchestration:m.kind||!0}:{},...m.sourceId?{sourceId:m.sourceId}:{},...m.attachments?.length?{attachments:m.attachments}:{},...m.images?.length?{images:m.images}:{}});break}case"peer.message":{let v=m.from||{};n.push({id:b.id,kind:"event",type:"peer-message",text:m.text,from:v.trackName||v.boxId||v.name||null,fromTrackId:v.trackId||null,ts:pe(b),box:b.boxId,agent:b.provider});break}case"turn.agent":{if(m.model==="<synthetic>"&&/^No response requested\.$/.test(String(m.text||"").trim()))break;let v=Ox(m,{codexFailedText:Zo});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}Si();let D=(m.images||[]).filter(yt=>!M(yt,pe(b)));if(b.source==="hook"&&!String(m.text||"").trim()&&(O(b),b.boxId&&ee.delete(b.boxId)),!String(m.text||"").trim()&&!D.length)break;if(b.provider==="cursor"){let yt=`${b.boxId}|${m.text}`;if(r.has(yt))break;r.add(yt)}(!gt||gt.box===b.boxId)&&(gt=null);let J=b.provider==="codex"?(m.tools||[]).filter(yt=>!Hm.has(yt)&&!tc.has(yt)):m.tools||[],ie=m.text,ve=null,_t=sh(String(m.text||""));_t&&(ie=_t.text,ve={questions:_t.questions}),n.push({id:b.id,kind:"message",role:"assistant",text:ie,ts:pe(b),box:b.boxId,agent:b.provider,boxRole:m.boxRole,...ve?{ask:ve}:{},...J.length?{tools:J}:{},...m.model?{model:m.model}:{},...m.sourceId?{sourceId:m.sourceId}:{},...D.length?{images:D}:{}}),Nt(b),ve&&(gt={id:b.id,groupId:ah(b.provider,m.sourceId||b.id,ve.questions),provider:b.provider,box:b.boxId,questions:ve.questions,ts:pe(b)});break}case"job.started":{if(!m.id)break;let v=pe(b),D=l.get(m.id);if(!D&&(m.kind||"agent")==="agent"&&m.name){let rn=w.get(m.name);rn&&rn.ts>=v&&(D=rn)}let J=D?null:c.get(m.id)||null,ie=D||J?null:p(b.boxId??null,v),ve={jobId:m.id,jobKind:m.kind||"agent",name:m.name||null,detail:m.detail||null,status:D?D.status==="killed"?"stopped":D.status&&D.status!=="completed"?"failed":"done":J?"stalled":ie!=null?"lost":"running",startedTs:v,finishedTs:D?.ts??J?.ts??ie??null,quietMs:J?.quietMs??null,summary:D?.summary??null,resultHead:D?.resultHead??null},_t=lr.get(b.boxId),yt=_t?.jobs[_t.jobs.length-1]?.startedTs;if(_t&&yt!=null&&v-yt<=Hr)_t.jobs.push(ve);else{let rn={id:b.id,kind:"job-dock",dockId:m.id,ts:v,box:b.boxId,agent:b.provider,jobs:[ve]};lr.set(b.boxId,rn),n.push(rn)}break}case"handoff.done":n.push(bt(b,"lead-handoff"));break;case"subtask.delegated":n.push({...bt(b,"subtask-delegated"),to:b.boxId,toAgent:b.provider});break;case"subtask.merged":n.push(bt(b,"subtask-merged"));break;case"pr.state.changed":{if(!m.change)break;if(m.change==="opened"){for(let D of $.values())D.status="stale";$.clear();for(let D of H.values())D.status="stale";H.clear()}let v=bt(b,`pr-${m.change}`);if(v.box=b.boxId??null,Pv.has(m.change)&&m.number!=null){let D=Dv(P,j,m);if(P.size>=2){for(let ie of j.values())for(let ve of ie)ve.grouped=!0;v.groupPrs=[...P.values()];let J=[v];for(let ie of P.keys())j.set(ie,J)}else{let J=j.get(D)??[];J.push(v),j.set(D,J)}}else if(m.change==="merged"||m.change==="closed"||m.change==="lost"){let D=lh(m),J=T&&!T.complete||P.size>=2;if(P.has(D))P.delete(D),j.delete(D);else if(m.number!=null)for(let ie of[...P.keys()])ie.endsWith(`#${m.number}`)&&(P.delete(ie),j.delete(ie));if(J&&m.number!=null){(!T||T.complete)&&(T={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(T)),T.prs.push({number:m.number,url:m.url??null,title:m.title??null,repo:m.repo??null,branch:m.branch??null,change:m.change,ts:pe(b)}),T.remaining=[...P.values()],T.complete=P.size===0;break}}n.push(v);break}case"pr.offered":{if(!m.offerId)break;he(b.boxId);for(let D of $.values())D.status="stale";$.clear();for(let D of H.values())D.status="stale";H.clear();let v={id:b.id,kind:"pr-offer",offerId:m.offerId,excerpt:m.excerpt||null,branch:m.branch||null,base:m.base||null,offered:Array.isArray(m.offered)&&m.offered.length?m.offered:null,toPr:m.toPr&&typeof m.toPr=="object"?{number:m.toPr.number??null}:null,available:Array.isArray(m.available)?m.available:null,defaults:Array.isArray(m.defaults)?m.defaults:null,prState:m.prState||null,openPr:m.openPr&&typeof m.openPr=="object"?m.openPr:null,repos:Array.isArray(m.repos)&&m.repos.length?m.repos:null,status:"open",source:b.source==="cli"||m.via==="cli"?"cli":"transcript",ts:pe(b),box:b.boxId,agent:b.provider};$.set(m.offerId,v),L.set(m.offerId,v),n.push(v),ne.add(v),v.source==="cli"&&Ve(v);break}case"pr.offer.answered":{let v=L.get(m.offerId);v&&(v.status=m.action||"dismiss",v.actions=Array.isArray(m.actions)?m.actions:null,v.notify=m.notify===!0,m.existingPr&&typeof m.existingPr=="object"&&(v.existingPr={number:m.existingPr.number??null}),$.delete(m.offerId));break}case"pr.exists.offered":{if(!m.offerId)break;for(let D of H.values())D.status="stale";H.clear();let v={id:b.id,kind:"pr-exists-offer",offerId:m.offerId,srcOfferId:m.srcOfferId??null,branch:m.branch||null,base:m.base||null,pr:m.pr&&typeof m.pr=="object"?{number:m.pr.number??null,url:m.pr.url??null,title:m.pr.title??null}:null,actions:Array.isArray(m.actions)&&m.actions.length?m.actions:null,notify:m.notify===!0,status:"open",ts:pe(b),box:b.boxId,agent:b.provider};H.set(m.offerId,v),R.set(m.offerId,v),n.push(v);break}case"pr.exists.answered":{let v=R.get(m.offerId);v&&(v.status=m.choice||"dismiss",H.delete(m.offerId));break}case"box.state.changed":{let v=m.change==="model"?"model-change":m.change;if(!v)break;let D=bt(b,v);if(v==="model-change"&&(D.to=m.model),v==="node-update"&&D.latest){let J=n.findIndex(ie=>ie.kind==="event"&&ie.type==="node-update"&&ie.latest===D.latest);if(J>=0){let ie=n[J];n[J]={...D,ts:ie.ts,at:ie.at,firstAt:ie.firstAt??ie.ts,updatedAt:D.ts};break}}n.push(D);break}case"track.transferred":case"track.recovered":n.push(bt(b,"track-moved"));break;case"view.state.changed":{if(m.change==="deleted"){k(`view:${m.viewId}`);break}if(m.change!=="created"||m.restarted)break;y(`view:${m.viewId}`,{id:b.id,kind:"view-card",viewId:m.viewId,name:m.name,slug:m.slug,port:m.port,origin:m.origin,ts:pe(b),box:b.boxId,agent:b.provider});break}case"view.spinup.failed":{n.push({id:b.id,kind:"view-spinup-failed",name:m.name||null,reason:m.reason||"no-view",ts:pe(b),box:b.boxId,agent:b.provider});break}case"mock.state.changed":{if(m.change==="picked"){n.push({id:b.id,kind:"mock-picked",mockId:m.mockId,name:m.name,group:m.group??null,version:m.version??null,siblings:m.siblings??[],ts:pe(b),box:b.boxId,agent:b.provider});break}if(m.change==="deleted"){k(`mock:${m.mockId}`);break}if(m.change!=="created")break;y(`mock:${m.mockId}`,{id:b.id,kind:"mock-card",mockId:m.mockId,name:m.name,group:m.group??null,ts:pe(b),box:b.boxId,agent:b.provider});break}case"flow.state.changed":{if(m.change==="deleted"){k(`flow:${m.flowId}`);break}if(m.change!=="created")break;y(`flow:${m.flowId}`,{id:b.id,kind:"flow-card",flowId:m.flowId,name:m.name,stepCount:m.stepCount??null,mockIds:m.mockIds??[],ts:pe(b),box:b.boxId,agent:b.provider});break}case"runbook.state.changed":{if(m.change==="deleted"){k(`runbook:${m.runbookId}`);break}if(m.change!=="created")break;y(`runbook:${m.runbookId}`,{id:b.id,kind:"runbook-card",runbookId:m.runbookId,name:m.name,ts:pe(b),box:b.boxId,agent:b.provider});break}case"ticket.linked":{n.push({id:b.id,kind:"ticket-card",provider:m.provider||"linear",key:m.key,url:m.url,title:m.title??null,linkSource:m.linkSource??null,ts:pe(b),box:b.boxId,agent:b.provider});break}case"box.fullscreen.detected":{if(!m.offerId)break;for(let D of W.values())D.box===(m.box||b.boxId)&&(D.status="stale",W.delete(D.offerId));let v={id:b.id,kind:"fullscreen-offer",offerId:m.offerId,status:"open",ts:pe(b),box:m.box||b.boxId,agent:m.agent||b.provider};W.set(m.offerId,v),B.set(m.offerId,v),n.push(v);break}case"box.fullscreen.answered":{let v=B.get(m.offerId);v&&(v.status=m.action==="restarted"?"restarted":m.action==="cleared"?"cleared":"dismiss",W.delete(m.offerId));break}case"ticket.move.offered":{if(!m.offerId)break;for(let D of A.values())D.key===m.key&&(D.status="stale",A.delete(D.offerId));let v={id:b.id,kind:"ticket-offer",offerId:m.offerId,provider:m.provider||"linear",key:m.key,url:m.url??null,phase:m.phase??null,from:m.from??null,to:m.to?.name??null,prNumber:m.prNumber??null,status:"open",ts:pe(b),box:b.boxId,agent:b.provider};A.set(m.offerId,v),C.set(m.offerId,v),n.push(v);break}case"ticket.move.answered":{let v=C.get(m.offerId);v&&(v.status=m.action==="moved"?"moved":"dismiss",A.delete(m.offerId));break}case"ticket.state.changed":{for(let v of A.values())v.key===m.key&&v.to===m.to&&(v.status="stale",A.delete(v.offerId));if(m.mode==="offer")break;n.push(bt(b,"ticket-state"));break}case"ticket.commented":{n.push({id:b.id,kind:"ticket-comment",key:m.key,url:m.url??null,excerpt:m.excerpt??null,ts:pe(b),box:b.boxId,agent:b.provider});break}case"ticket.flow.changed":n.push(bt(b,"ticket-flow"));break;case"ticket.create.offered":{if(!m.proposalId)break;for(let D of Z.values())D.status="stale";Z.clear();let v={id:b.id,kind:"ticket-create-offer",proposalId:m.proposalId,provider:m.provider||"linear",epic:m.epic??null,issues:Array.isArray(m.issues)?m.issues:[],team:m.team??null,needsTeam:!!m.needsTeam,linkCurrentTrack:!!m.linkCurrentTrack,status:"open",ts:pe(b),box:b.boxId,agent:b.provider};Z.set(m.proposalId,v),xe.set(m.proposalId,v),n.push(v),Ve(v);break}case"ticket.create.answered":{let v=xe.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",v.createdCount=m.createdCount??null,v.failedCount=m.failedCount??null,Z.delete(m.proposalId));break}case"loop.create.offered":{if(!m.proposalId)break;for(let D of Ae.values())D.status="stale";Ae.clear();let v={id:b.id,kind:"loop-create-offer",proposalId:m.proposalId,name:m.name??"",schedule:m.schedule??"",prompt:m.prompt??"",expectation:m.expectation??null,notify:m.notify??"off",assignment:m.assignment??{provider:"claude"},repo:m.repo??null,nodeId:m.nodeId??null,nodeName:m.nodeName??null,status:"open",ts:pe(b),box:b.boxId,agent:b.provider};Ae.set(m.proposalId,v),_e.set(m.proposalId,v),n.push(v),Ve(v);break}case"loop.create.answered":{let v=_e.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",Ae.delete(m.proposalId));break}case"followup.armed":{if(!m.armId)break;let v=m.replaces?Pe.get(m.replaces):null;v&&v.arm.status==="armed"&&ge(v,"replaced");let D={armId:m.armId,seq:Number.isInteger(m.seq)?m.seq:0,label:m.label??"",prompt:m.prompt??"",why:m.why??null,standing:m.standing==="never"?"never":"offerable",status:"armed"},J=m.groupId??null,ie=J?le.get(J):null;ie||(ie={id:b.id,kind:"followup-armed",groupId:J,armId:D.armId,trigger:m.trigger??"now",label:D.label,prompt:D.prompt,why:D.why,planWhy:m.planWhy??null,arms:[],status:"armed",ts:pe(b),box:b.boxId,agent:b.provider},J&&le.set(J,ie),ie.trigger!=="now"&&n.push(ie)),ie.arms.push(D),Pe.set(m.armId,{arm:D,line:ie});break}case"followup.armed.cancelled":{let v=Pe.get(m.armId);v&&v.arm.status==="armed"&&ge(v,"cancelled");break}case"followup.offered":{if(!m.offerId)break;for(let J of Q.values())J.trigger===m.trigger&&(J.status="stale",Q.delete(J.offerId));let v=Array.isArray(m.rows)?m.rows:[];for(let J of v){let ie=J.armId?Pe.get(J.armId):null;ie&&ie.arm.status==="armed"&&ge(ie,"offered")}let D={id:b.id,kind:"followup-offer",offerId:m.offerId,trigger:m.trigger??"now",repo:m.repo??null,context:m.context&&typeof m.context=="object"?m.context:{},groupId:m.groupId??null,why:m.why??null,rows:v.map(J=>({followupId:J.followupId??null,armId:J.armId??null,label:J.label??"",prompt:J.prompt??null,promptPreview:J.promptPreview??null,promptHash:J.promptHash??null,origin:J.origin??"operator",why:J.why??null,groupId:J.groupId??null,seq:Number.isInteger(J.seq)?J.seq:0,standing:J.standing==="never"?"never":"offerable",promotable:J.armId?J.promotable!==!1:!1,promotableReason:J.promotableReason??null,rowStatus:"open"})),status:"open",answer:null,answers:[],ts:pe(b),box:b.boxId,agent:b.provider};Q.set(m.offerId,D),Xe.set(m.offerId,D),n.push(D),D.trigger==="now"&&Ve(D);break}case"followup.answered":{let v=Xe.get(m.offerId);if(!v)break;let D=new Set;m.armId&&D.add(`arm:${m.armId}`),m.followupId&&D.add(`fu:${m.followupId}`);for(let ve of Array.isArray(m.armIds)?m.armIds:[])D.add(`arm:${ve}`);for(let ve of Array.isArray(m.followupIds)?m.followupIds:[])D.add(`fu:${ve}`);let J=m.action==="sent"?"sent":m.action==="removed"?"removed":"dismiss";for(let ve of v.rows){if(ve.rowStatus!=="open")continue;let _t=ve.armId?`arm:${ve.armId}`:`fu:${ve.followupId}`;D.size&&!D.has(_t)||(ve.rowStatus=J)}let ie={followupId:m.followupId??null,armId:m.armId??null,label:m.label??null,promoted:m.promoted??null,revived:m.revived===!0,action:J,batchId:m.batchId??null};v.answers.push(ie),v.answer=ie,v.rows.some(ve=>ve.rowStatus==="open")?v.status="open":(v.status=v.rows.some(ve=>ve.rowStatus==="sent")?"sent":v.rows.some(ve=>ve.rowStatus==="removed")?"removed":"dismiss",Q.delete(m.offerId));break}case"followup.create.offered":{if(!m.proposalId)break;for(let D of Be.values())D.status="stale";Be.clear();let v={id:b.id,kind:"followup-create-offer",proposalId:m.proposalId,label:m.label??"",prompt:m.prompt??"",trigger:m.trigger??"pr.merged",repo:m.repo??null,why:m.why??null,status:"open",created:null,ts:pe(b),box:b.boxId,agent:b.provider};Be.set(m.proposalId,v),Pt.set(m.proposalId,v),n.push(v),Ve(v);break}case"followup.create.answered":{let v=Pt.get(m.proposalId);v&&(v.status=m.action==="created"?"created":"dismiss",Be.delete(m.proposalId));break}case"followup.created":{let v=m.proposalId?Pt.get(m.proposalId):null;v&&(v.created={followupId:m.followupId??null});break}case"loop.created":{let v=m.proposalId?_e.get(m.proposalId):null;v&&(v.created={loopId:m.loopId??null,nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),n.push(bt(b,"loop-created"));break}case"loop.update.offered":case"loop.delete.offered":{if(!m.proposalId||!m.loopId)break;for(let J of tt.values())J.loopId===m.loopId&&(J.status="stale",J.staleReason="superseded",tt.delete(J.proposalId));let v=b.type==="loop.update.offered",D={id:b.id,kind:v?"loop-update-offer":"loop-delete-offer",proposalId:m.proposalId,loopId:String(m.loopId),name:m.name??m.base?.name??"",base:m.base??null,...v?{patch:m.patch??{},changed:Array.isArray(m.changed)?m.changed:Object.keys(m.patch||{})}:{reason:m.reason??""},selfEdit:!!m.selfEdit,status:"open",staleReason:null,ts:pe(b),box:b.boxId,agent:b.provider};tt.set(m.proposalId,D),wn.set(m.proposalId,D),n.push(D),Ve(D);break}case"loop.update.answered":{let v=wn.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="applied"?"applied":"dismiss",tt.delete(m.proposalId));break}case"loop.delete.answered":{let v=wn.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="delete"?"deleted":m.action==="pause"?"paused":"keep",tt.delete(m.proposalId));break}case"loop.updated":{let v=m.proposalId?wn.get(m.proposalId):null;v&&(v.applied={changed:Array.isArray(m.changed)?m.changed:[],nextFireAt:Number.isFinite(Number(m.nextFireAt))?Number(m.nextFireAt):null}),n.push(bt(b,"loop-updated"));break}case"loop.deleted":n.push(bt(b,"loop-deleted"));break;case"loop.paused":n.push(bt(b,"loop-paused"));break;case"planning.change.offered":{if(!m.proposalId||!Array.isArray(m.ops)||!m.ops.length)break;for(let D of Kt.values())D.status="stale",D.staleReason="superseded",Kt.delete(D.proposalId);let v={id:b.id,kind:"planning-offer",proposalId:m.proposalId,why:m.why??"",ops:m.ops,base:m.base??{},resolved:m.resolved??{},snapped:!!m.snapped,createsMeRow:!!m.createsMeRow,status:"open",staleReason:null,applied:null,ts:pe(b),box:b.boxId,agent:b.provider};Kt.set(m.proposalId,v),ht.set(m.proposalId,v),n.push(v),Ve(v);break}case"planning.change.answered":{let v=ht.get(m.proposalId);v&&(m.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=m.action==="applied"?"applied":"dismiss",Kt.delete(m.proposalId));break}case"planning.changed":{let v=m.proposalId?ht.get(m.proposalId):null;v&&(v.applied={results:Array.isArray(m.results)?m.results:[]}),n.push(bt(b,"planning-changed"));break}case"rework.cause.offered":{if(!m.sha||wt.has(m.sha))break;let v={id:b.id,kind:"rework-cause-offer",sha:m.sha,subject:m.subject??"",linesReworked:Number(m.linesReworked)||0,origins:Array.isArray(m.origins)?m.origins:[],heuristic:m.heuristic??null,intent:m.intent??null,shas:Array.isArray(m.shas)?m.shas:null,pr:m.pr??null,retracts:0,status:"open",cause:null,ts:pe(b),box:b.boxId,agent:b.provider};wt.set(m.sha,v),n.push(v);break}case"rework.cause.answered":{let v=m.sha?wt.get(m.sha):null;v&&(v.status="answered",v.cause=m.cause??null);break}case"rework.cause.retracted":{let v=m.sha?wt.get(m.sha):null;v&&(v.status="open",v.cause=null,v.retracts=(v.retracts??0)+1);break}case"ticket.created":{let v={items:Array.isArray(m.items)?m.items:[],failed:Array.isArray(m.failed)?m.failed:[],team:m.team??null},D=m.proposalId?xe.get(m.proposalId):null;if(D){D.created=v;break}n.push({id:b.id,kind:"ticket-created",...v,ts:pe(b),box:b.boxId,agent:b.provider});break}case"error":{if(!m.kind)break;n.push(bt(b,m.kind));break}case"provider.error":n.push({...bt(b,"provider-error"),provider:b.provider,box:b.boxId,agent:b.provider});break;case"usage.cap.hit":n.push(bt(b,"usage-cap-hit"));break;case"usage.cap.warning":n.push(bt(b,"usage-cap-warning"));break;case"provider.connected":{n.push({id:b.id,kind:"event",type:"provider-connected",provider:m.provider||b.provider,account:m.account||null,reason:m.reason||null,ts:pe(b),at:pe(b),box:b.boxId,agent:b.provider});break}case"provider.login.started":break;case"prompt.answered":{if(gt=null,m.control||!m.question)break;let v=m.groupId||m.promptId;if(!v)break;let D=at.get(v);D||(D={id:b.id,kind:"prompt",groupId:v,provider:b.provider,box:b.boxId,agent:b.provider,questionTotal:m.questionTotal??1,questions:[],ts:pe(b)},at.set(v,D));let J=m.questionIndex??0,ie={index:J,header:m.header||"",question:m.question,multiSelect:!!m.multiSelect,chosen:Array.isArray(m.chosen)?m.chosen:[]},ve=D.questions.findIndex(yt=>yt.index===J&&He(yt.question)===He(ie.question));ve>=0?D.questions[ve]=ie:D.questions.push(ie),D.questions.sort((yt,nt)=>yt.index-nt.index),D.questionTotal=Math.max(D.questionTotal,m.questionTotal??1),D.questions.length>=D.questionTotal&&(n.push(D),at.delete(v));break}case"prompt.dismissed":{gt=null;let v=m.groupId||m.promptId;if(!v||!m.question)break;let J=at.get(v)||{id:b.id,kind:"prompt",groupId:v,provider:b.provider,box:b.boxId,agent:b.provider,questionTotal:m.questionTotal??1,questions:[],ts:pe(b)},ie=m.questionIndex??0;J.questions.some(ve=>ve.index===ie&&He(ve.question)===He(m.question))||(J.questions.push({index:ie,header:m.header||"",question:m.question,multiSelect:!!m.multiSelect,chosen:[]}),J.questions.sort((ve,_t)=>ve.index-_t.index)),J.answeredVia="chat",n.push(J),at.delete(v);break}default:break}}for(let[b,m]of[...we]){if(m==="superseded"){b.status!=="superseded"&&Oe(b,null);continue}(b.status!=="open"||t-(b.ts??0)>=rL)&&Oe(b,null)}let ur=h.size?n.filter(b=>!b.retired):n;return sL(ur.sort((b,m)=>(b.ts??0)-(m.ts??0)))}function sL(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<=oL){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 uh(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:aL(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 Sx(n,r)}function aL(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==ma(e.command)?t:Qr(e.command,e.description||"")}function bt(e,t){let{ts:n,change:r,kind:o,...s}=e.payload||{};return{id:e.id,kind:"event",type:t,...s,at:pe(e),ts:pe(e)}}function 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)+iL,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&cL.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function Lv(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!uL.has(r.type)||!r.box)continue;let o=(r.ts??0)+lL,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 rd(e){return(e||[]).filter(t=>dL.has(t?.kind)&&t.status==="open")}function jv(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(Pv.has(o.change)&&o.number!=null)Dv(t,n,o);else if(o.change==="merged"||o.change==="closed"||o.change==="lost"){let s=lh(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,Pv,lh,rL,oL,iL,cL,lL,uL,dL,od=G(()=>{ha();Qo();ju();uc();nd();ch();pe=e=>e.payload?.ts??e.ts;Pv=new Set(["opened","ci-fixed","reconciled","pushed"]),lh=e=>`${e.repo??""}#${e.number}`;rL=3e4;oL=2*6e4;iL=12e4,cL=new Set(["archived","unarchived","opened"]);lL=2500,uL=new Set(["pr-opened","pr-merged","pr-auto-armed"]);dL=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 wL(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 yL(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 bL(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<mL&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<hL&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function Wv(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=wL(e),s=yL(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(-pL);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(Hv[r]??Hv.failover),l.push(""),o.lastPush?.sha){let p=Uv(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=Uv(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=bL(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"}:** ${gL(p.text,fL)}`),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(`
|
|
157
157
|
`);return d.length<=Fv?d:`${d.slice(0,Fv)}
|
|
158
158
|
|
|
159
|
-
[\u2026 brief truncated]`}var
|
|
160
|
-
`),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(os);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let p=r[u].trim();p&&!no.test(r[u])&&a.push(p)}}if(s.length<2)return null;let i=s.map(u=>({label:ro(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=["Switch model?",...a].join(" ");return{provider:"claude",box:t,multiSelect:!1,cursorIndex:c,promptId:`claude:model-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Model",multiSelect:!1,options:i}]}}function
|
|
161
|
-
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!
|
|
162
|
-
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(os);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
|
|
163
|
-
`),o=-1;for(let u=r.length-1;u>=0;u--)if(
|
|
164
|
-
`),o=hh(r);return o?{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"theme",promptId:`claude:setup:theme:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks: choose the text style that looks best with your terminal. You can change it later with /theme.",header:"Theme",multiSelect:!1,options:o.map(s=>({label:ro(s.label),description:""}))}]}:null}function
|
|
165
|
-
`),o=hh(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:ro(i),description:c.join("\xB7").trim()}});return{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(a=>a.cursor)),setup:"login-method",promptId:`claude:setup:login-method:${s.map(a=>a.label).join("|")}`.slice(0,200),questions:[{question:"How do you want to sign in to Claude? A Claude subscription (Pro/Max/Team) uses your existing plan; a Console account bills API usage. Your login stays on this box \u2014 bostrat never sees it.",header:"Log in",multiSelect:!1,options:s}]}}function
|
|
166
|
-
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=
|
|
167
|
-
`)].reverse().find(a=>ph.test(a))||"OAuth error").trim();return{provider:"claude",box:t,multiSelect:!1,cursorIndex:0,setup:"oauth-error",promptId:`claude:setup:oauth-error:${s}`.slice(0,200),questions:[{question:`${s} \u2014 retry to get a fresh sign-in link.`,header:"Log in",multiSelect:!1,options:[{label:"Retry sign-in",description:"Mints a new sign-in link and code"}]}]}}function
|
|
168
|
-
`),o=r.flatMap(a=>{let i=a.match(/^\s*(❯\s*)?(Yes, I trust this folder|No, exit)\s*$/);return i?[{cursor:!!i[1],label:i[2]}]:[]}),s=o.length===2&&new Set(o.map(a=>a.label)).size===2&&o.filter(a=>a.cursor).length===1?o:hh(r);return s?{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,s.findIndex(a=>a.cursor)),setup:"trust",promptId:`claude:setup:trust:${s.map(a=>a.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks whether it can read, edit, and run files in this workspace. This box's workspace is bostrat-managed scratch space, so trusting it is safe.",header:"Folder trust",multiSelect:!1,options:s.map(a=>({label:ro(a.label),description:""}))}]}:null}function
|
|
169
|
-
`),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(
|
|
170
|
-
`),o=
|
|
171
|
-
`).pop()||"")?!0:ss.test(n)?!1:
|
|
172
|
-
`).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 Sn(e,t){return!e||!t?null:e.agent==="claude"?
|
|
173
|
-
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];
|
|
174
|
-
`)){let f=p.trim();if(!f||f[0]!=="{")continue;let w;try{w=JSON.parse(f)}catch{continue}if(w.isSidechain)continue;let h=
|
|
175
|
-
`).trim():"",
|
|
176
|
-
|
|
177
|
-
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function Ij(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(OS.test(s)){if(r.length>=WS)continue;let a=_j(s.replace(OS,"").replace(/[.;,:]+$/,""),qS,Tj);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>RS&&(o=`${o.slice(0,RS-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function GS(e,t,{timeoutMs:n=9e4}={}){let r=co.join(t,Sj),o=co.join(r,Aj);await io.mkdir(r,{recursive:!0});let s=await fd(o),a=await Se(e);if(!a)throw new Error(`session "${e}" not found`);return await md(e,a,Ej(o)),await ZS(o,s,n),Ij(await io.readFile(o,"utf8"))}function KS(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===jS||n===HS;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(LS)||n===kj||r.startsWith(FS)||r.startsWith(US)}function VS(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-$j;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 zS(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 JS(e,{windows:t=[]}={}){if(KS(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 hd(e){return Rh.get(e)||null}async function YS(e){let t=Rh.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await Se(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);let r={name:e,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:co.join(n.cwd,PS,DS)};return Rh.set(e,r),Mj(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function Mj(e,t){e.step="summarize",await XS(e.name,t,{quiesce:{reason:"console-checkpoint",onWait:r=>{e.step="await-turn",e.wait=r},onResume:()=>{e.step="summarize"}}}),e.step="clear";let n=await Se(e.name);await md(e.name,n,"/clear"),await Mh(Rj),e.step="resume",await md(e.name,n,vj),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 _S({...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||Cj)===Nj)throw i;return await re({flow:"checkpoint",phase:"quiescence-timeout",sessionName:t,gate:e,waitedMs:i.waitedMs,detail:i.detail,still:$h(i.detail),paneTail:!0}),r.onResume&&await r.onResume(e),{outcome:"timeout",waitedMs:i.waitedMs,waited:!0,detail:i.detail}}}async function XS(e,t,{timeoutMs:n=Oj,quiesce:r=null}={}){let o=co.join(t,PS),s=co.join(o,DS);await io.mkdir(o,{recursive:!0});let a=await Se(e),i=await MS("pre-drive",e,a,r),c=await fd(s);await re({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:s,beforeMtime:c,quiescence:i.outcome,waitedMs:i.waitedMs});try{await md(e,a,xj(s)),await ZS(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 re({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:s,afterMtime:await fd(s),quiescence:l.outcome,waitedMs:l.waitedMs});let u=await yv(t,s,{exclude:f=>KS(f.role,f.text)});await re({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:s,...u});let d=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(d){await N({id:d,cwd:t},{type:"memory.seed",actor:"orchestrator",payload:{...u,tokensEst:u.chars?Math.round(u.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let f=await io.readFile(s,"utf8");await N({id:d,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...BS(f),ts:Date.now()}})}catch{}}await gm(t);let p=await Pj(o,s);return d&&await W0(d,p||s,{box:e,agent:a?.agent??null}),{file:s,quiescence:{pre:i,post:l}}}async function wd(e,{timeoutMs:t,quiesce:n=null}={}){let r=await Se(e);if(!r)throw new Error(`session "${e}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${e}"`);return XS(e,r.cwd,{...t?{timeoutMs:t}:{},quiesce:n})}function Oh(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 gd(e,t,{trackName:n="this Track"}={}){let r=await K({id:e,cwd:t}),o=Wv(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=co.join(t,CS);return await io.mkdir(co.dirname(s),{recursive:!0}),await io.writeFile(s,o,"utf8"),await N({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...BS(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),CS}async function fd(e){try{return(await io.stat(e)).mtimeMs}catch{return 0}}async function ZS(e,t,n){let r=Date.now()+n;for(;;){if(await fd(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await Mh(2e3)}}async function Pj(e,t){try{if(!bj.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=co.join(e,`${n}.md`);return await io.copyFile(t,r),r}catch{return null}}function Mh(e){return new Promise(t=>setTimeout(t,e))}function NS(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 md(e,t,n){if(t){let o=NS(t.agent);await wr({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await Se(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await Ke(e,["Escape"],{kind:"drive-wipe"}),await Mh(150),await ua(e,Pu,{kind:"drive-wipe"}),await Qe(e,n,{kind:"orchestration-drive",...Un(r.agent,n)}),await pc({sessionName:e,agent:r.agent,agentCmd:NS(r.agent),text:n,flow:"orchestration-drive"})}var PS,DS,$S,LS,jS,FS,kj,xj,vj,US,HS,Sj,Aj,RS,WS,qS,Tj,Ej,OS,_j,$j,Rj,Oj,Rh,Cj,Nj,CS,Ch=q(()=>{pt();rs();bn();cc();Qm();bv();Li();be();Eu();qv();IS();PS=".bostrat/handoff",DS="latest.md",$S=32*1024;LS="Checkpoint: write a work-in-progress handoff",jS="HANDOFF WRITTEN",FS="Resume from .bostrat/handoff/latest.md",kj="/clear",xj=e=>`${LS} 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 ${jS} and take no further action.`,vj=`${FS}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,US="Beat: write a",HS="BEAT WRITTEN",Sj=".bostrat/arc",Aj="beat.txt",RS=200,WS=4,qS=5,Tj=36,Ej=e=>`${US} 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 ${WS} further lines, each starting with "- " and naming ONE piece in at most ${qS} 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 ${HS} and take no further action.`,OS=/^(?:[-–—*•·]|\d+[.)])\s+/,_j=(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};$j=6e5;Rj=3500,Oj=3e5,Rh=new Map;Cj="proceed",Nj="reject";CS=".bostrat/handoff/status-latest.md"});function uo(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!yd.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 tA(e){let t={};for(let[n,r]of Object.entries(e)){if(!Array.isArray(r)||r.length===0)throw new Error(`pricing.js: ${n} must be a non-empty array of rate windows`);t[n]=r.map(o=>({...o,fromMs:uo(o.from,"from",n),toMs:uo(o.to,"to",n)}))}return t}function Ph(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 Bj(e){if(!e)return null;let t=String(e).trim();if(lo[t])return t;if(QS[t])return QS[t];let n=null;for(let r of Object.keys(lo))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function Lj(e){if(!e||e===lo)return Dj;let t=eA.get(e);return t||(t=tA(e),eA.set(e,t)),t}function nA(e,t,n={}){let{table:r,speed:o}=n,s=Lj(r),a=r&&r!==lo?s[e]?e:null:Bj(e);if(!a)return null;let i=Ph(t);if(i===null)return null;let c=s[a].find(l=>i>=l.fromMs&&i<=l.toMs)||null;return c?o==null||o==="standard"?c:o==="fast"&&c.fast?{...c.fast,fromMs:c.fromMs,toMs:c.toMs,verifiedAt:c.verifiedAt,note:c.note,speed:"fast"}:null:null}function Wn(e,t={},n,r){let o=nA(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 jj(e=lo){let t=[];for(let[n,r]of Object.entries(e)){if(!Array.isArray(r)||r.length===0){t.push(`${n}: must be a non-empty array of windows`);continue}let o;try{o=r.map(a=>({fromMs:uo(a.from,"from",n),toMs:uo(a.to,"to",n)}))}catch(a){t.push(String(a.message));continue}let s=r.filter(a=>a.to==null);s.length!==1?t.push(`${n}: expected exactly 1 open-ended window (to: null), found ${s.length}`):r[r.length-1].to!=null&&t.push(`${n}: the open-ended window must be last`),r[0].from!=null&&t.push(`${n}: the first window must be open at the start (from: null)`);for(let a=0;a<o.length;a++){if(o[a].fromMs>o[a].toMs&&t.push(`${n}: window ${a} ends before it begins (${r[a].from} \u2192 ${r[a].to})`),a===0)continue;let i=o[a-1],c=o[a];c.fromMs<=i.toMs?t.push(`${n}: windows ${a-1} and ${a} overlap (${r[a-1].to} / ${r[a].from})`):c.fromMs-i.toMs>2&&t.push(`${n}: gap between windows ${a-1} and ${a} (${r[a-1].to} \u2192 ${r[a].from})`),r[a].from||t.push(`${n}: window ${a} must declare a from bound`)}for(let a of r){a.note||t.push(`${n}: every window needs a note recording what was verified and when`),yd.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 Uj({now:e=Date.now(),table:t=lo,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...Fj,...n},a=[],i=Ph(e);if(i===null)return[`pricingFreshness: unparseable now ${JSON.stringify(e)}`];for(let[c,l]of Object.entries(t)){if(!Array.isArray(l))continue;let u;try{u=l.map(d=>({...d,fromMs:uo(d.from,"from",c),toMs:uo(d.to,"to",c),verifiedMs:yd.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*Nh;p&&i-d.verifiedMs>r*Nh&&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*Nh&&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 Ta({now:e=Date.now(),table:t=lo,...n}={}){let r=Ph(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=uo(l.from,"from",i),d=uo(l.to,"to",i);r>=u&&r<=d&&yd.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=[...jj(t),...Uj({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var lo,QS,yd,Dj,eA,rA,Fj,Nh,yr=q(()=>{lo={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"GA rates; verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-08-17",note:"$2/$10 is the standard price (intro pricing made permanent; the 2026-09-01 $3/$15 increase was cancelled) \u2014 verified 2026-08-17 against platform.claude.com/docs/en/about-claude/pricing."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-fable-5-1":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:.25,cacheReadX:.025,verifiedAt:"2026-09-07",note:"verified 2026-09-07 against platform.claude.com/docs/en/about-claude/pricing: $10/$50, 1h cache write $20 (2\xD7), cache hits $0.25 (0.025\xD7 \u2014 the 5.1-only multiplier; footnote 1 on the page)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-03, re-verified 2026-08-17 (developers.openai.com pricing); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:"2026-08-23",input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL). Superseded 2026-08-24 by the vendor's promotional cut (window below)."},{from:"2026-08-24",to:null,input:4,output:20,cacheWrite:5,cacheRead:.4,verifiedAt:"2026-08-24",note:"promotional pricing verified 2026-08-24 (developers.openai.com pricing), 'at least through November 21, 2026'; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:"2026-08-16",input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:2,output:12,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:"2026-08-16",input:1,output:6,cacheWrite:1.25,cacheRead:.1,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:.2,output:1.2,cacheWrite:.25,cacheRead:.02,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-6-astra":[{from:null,to:null,input:10,output:50,cacheWrite:12.5,cacheRead:1,fast:{input:20,output:100,cacheWrite:25,cacheRead:2},verifiedAt:"2026-09-07",note:"verified 2026-09-07 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input; fast tier $20/$100 recorded as the fast variant (codex reports no speed)."}]},QS={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"},yd=/^\d{4}-\d{2}-\d{2}$/;Dj=tA(lo);eA=new WeakMap;rA=(e,t,n)=>nA(e,t,n)!==null;Fj=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),Nh=864e5});function Hj(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function sA(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function sn(e){let t=e.payload?.model||"unknown",n=Hj(e.payload?.tokens),r=Number(e.payload?.usd)||0,o=e.payload?.speed||void 0,s=e.source==="vendor",a=r,i=!1;if(n&&!s){let u=Wn(t,n,e.ts,o?{speed:o}:void 0);u>0&&(a=u,i=Math.abs(u-r)>1e-9)}let c=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,l=!a&&c>0&&!rA(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function po(e,{boxLabels:t={}}={}){let n=(e||[]).filter(p=>p.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,d=oA();for(let p of n){let{usd:f,model:w,tokens:h,repriced:y,unpriced:k,asRecorded:g}=sn(p);i+=g;let x=p.provider||"unknown",A=p.boxId||"unattributed";if(y&&c++,k&&(l++,u.add(w)),a+=f,sA(d,h),r[w]||={usd:0,tokens:oA(),events:0},r[w].usd+=f,r[w].events+=1,sA(r[w].tokens,h),o[x]||={usd:0,events:0},o[x].usd+=f,o[x].events+=1,s[A]||={usd:0,events:0,provider:x,models:new Set,label:t[A]||A},s[A].usd+=f,s[A].events+=1,w!=="unknown"&&s[A].models.add(w),p.epoch!=null){let I=(s[A].epochs||={})[p.epoch]||={usd:0,events:0,provider:x,models:new Set};I.usd+=f,I.events+=1,I.provider=x,w!=="unknown"&&I.models.add(w)}}return{totalUsd:Ea(a),asRecordedUsd:Ea(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:bd(r,p=>({...p,usd:Ea(p.usd)})),byProvider:bd(o,p=>({...p,usd:Ea(p.usd)})),byBox:bd(s,p=>({usd:Ea(p.usd),events:p.events,provider:p.provider,label:p.label,models:[...p.models],...p.epochs?{epochs:bd(p.epochs,f=>({usd:Ea(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var oA,Ea,bd,fo=q(()=>{yr();oA=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});Ea=e=>Math.round(e*1e4)/1e4,bd=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function Wj(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 aA(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(Dh(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Dh(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(`${Dh(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=po(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?Wj(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Dh,iA=q(()=>{fo();Dh=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as qj}from"node:child_process";import{promisify as Gj}from"node:util";import{randomUUID as Kj}from"node:crypto";import cA from"node:fs/promises";async function Bh(e,t){let n=Date.now()+t;for(;;){if(await Fn(e))return!0;if(Date.now()>n)return!1;await ls(fA)}}function Zj(e){let t=String(e||"").split(`
|
|
178
|
-
`).map(n=>n.trimEnd()).filter(n=>n.trim());return!t.length||!
|
|
179
|
-
`))}async function
|
|
180
|
-
`).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 ps(e,t){if(!t)return 0;try{let{stdout:n}=await Ye(e,["rev-list","--count",`${t}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function fs(e,t,n,r){return await Ye(e,["worktree","add","--no-track","-b",n,t,r]),t}async function Vh(e,t){try{return await Ye(e,["rev-parse","--verify","--quiet",`refs/heads/${t}`]),!0}catch{return!1}}async function vc(e,t,n){return await Ye(e,["worktree","add","--end-of-options",t,n]),t}async function ms(e){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:t}=await Ye(e,["symbolic-ref","--short","refs/remotes/origin/HEAD"]);if(t.trim())return t.trim()}catch{}for(let[t,n]of[["refs/remotes/origin/main","origin/main"],["refs/remotes/origin/master","origin/master"],["refs/heads/main","main"],["refs/heads/master","master"]])try{return await Ye(e,["rev-parse","--verify","--quiet",t]),n}catch{}return"HEAD"}async function zh(e){await Ye(e,["init","-q"]),await Ye(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function $t(e,t){try{await Ye(e,["worktree","remove","--force",t])}catch{try{await Ye(e,["worktree","prune"])}catch{}}}function
|
|
181
|
-
`).map(s=>s.trim()).filter(Boolean)}async function Zh(e,t,n,r){let{stdout:o}=await Ye(e,Jh(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await qh(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await qh(e,o,["--3way","--whitespace=nowarn"]),a=await
|
|
182
|
-
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var Gh,
|
|
183
|
-
`),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
|
|
184
|
-
`),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
|
|
185
|
-
`);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
|
|
186
|
-
`),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 HA(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&&!FA.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=UA.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!$F.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function Ac(e,t,{timeoutMs:n=_F}={}){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 $d(IF)}}async function MF(e,t){return await Qe(e,`/model ${t.model}`,Un("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function CF(e,t){await Nu(e),await Qe(e,"/model",{enter:!1}),await $d(250),await Ke(e,["Enter"],{kind:"model-switch"});let n=await Ac(e,DA);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await Ke(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${t.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=OF(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await Qe(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await Ac(e,i=>BA(i)||!DA(i)&&o(i)&&{done:o(i)});if(!s)throw await Ke(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?FA.get(t.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await Ke(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await Qe(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Ac(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 NF(){let e=PA.join(process.env.CURSOR_CONFIG_DIR||PA.join(EF.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await TF.readFile(e,"utf8"))?.model||null}catch{return null}}async function PF(e,t){await Nu(e),await Du(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await Ac(e,LA),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 Ac(e,LA,{timeoutMs:1500});if(s=l?.rows.find(u=>u.active)||null,s&&o(s))break;if(!l||!l.rows.some(o)){s=null;break}await Ke(e,["Down"],{kind:"model-switch"}),await $d(250)}if(!n||!s||!o(s)){await Ke(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await Ke(e,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${t.filter}"`:"cursor model picker didn't open")}await Ke(e,["Enter"],{kind:"model-switch"}),await $d(800);let a=await NF();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 WA(e,t,n){return e==="codex"?CF(t,n):e==="cursor"?PF(t,n):MF(t,n)}var $d,_F,IF,jA,$F,RF,FA,UA,qA=q(()=>{pt();cc();$d=e=>new Promise(t=>setTimeout(t,e)),_F=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,IF=300,jA=[{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"}],$F=new Set([...jA.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"]),RF=[{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"}],FA=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),UA=[{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 Md(e){let t=e||{},n=DF(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function DF(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{text:e.text,boxRole:e.boxRole,...e.kind?{kind:e.kind}:{},...e.attachments?.length?{attachments:e.attachments}:{},...e.deliveryId?{deliveryId:e.deliveryId}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{saidTs:e.saidTs,text:e.text,boxRole:e.boxRole}};case"orchestration-prompt":return{type:"turn.user",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{text:e.prompt,kind:e.kind,boxRole:e.boxRole,...e.deliveryId?{deliveryId:e.deliveryId}:{}}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:lt(e.toAgent),payload:{from:e.from,fromAgent:e.fromAgent,to:e.to,toAgent:e.toAgent,reason:e.reason,handoffStartedAt:e.handoffStartedAt,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,singleBox:e.singleBox,forced:e.forced,recovered:e.recovered,checkpoint:e.checkpoint,cliVersion:e.cliVersion??null,...e.standing?{standing:e.standing}:{}}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.toAgent),payload:{box:e.box,fromAgent:e.fromAgent,toAgent:e.toAgent,reason:e.reason,error:e.error,rolledBack:e.rolledBack,handoffStartedAt:e.handoffStartedAt}};case"subtask-delegated":return{type:"subtask.delegated",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:lt(e.toAgent),payload:{task:e.task,subtaskId:e.subtaskId,reason:e.reason}};case"subtask-merged":return{type:"subtask.merged",actor:"orchestrator",source:"orchestrator",boxId:e.box||e.from,provider:lt(e.agent),payload:{...qn(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:lt(e.agent||e.toAgent),payload:{kind:"subtask-failed",...qn(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{change:"subtask-resumed",...qn(e)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"model",model:e.to,from:e.from??null,verified:e.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:"codex",payload:{change:"cli-update",from:e.from??null,to:e.to??null,status:e.status,...e.error?{error:e.error}:{}}};case"node-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",payload:{change:"node-update",...qn(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...qn(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...qn(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{change:"rehydrated",...e.fresh?{fresh:!0}:{}}};case"lead-refreshed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{change:"lead-refreshed",box:e.box,agent:e.agent,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,reason:e.reason,cliVersion:e.cliVersion??null,...e.standing?{standing:e.standing}:{}}};case"transferred-in":return{type:e.mode==="recover"?"track.recovered":"track.transferred",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{...qn(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...qn(e)}};case"retag-failed":return{type:"error",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{kind:"retag-failed",action:e.action??"reshape",reason:e.reason,rolledBack:e.rolledBack!==!1,...e.recovered?{recovered:!0}:{}}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};case"delivery-held":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{kind:"delivery-held",...qn(e)}};case"startup-gate":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{kind:"startup-gate",...qn(e)}};default:return null}}function VA(e,t){let n=Md(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;Cd(()=>N(r,n))}async function zA(e){if(!(!e?.id||GA.has(e.id))){GA.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=Md(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await N(e,o)}}catch{}}}function qn(e){let{type:t,at:n,...r}=e;return r}function JA(e,t,n){!n?.promptId||Od.get(e.id)===n.promptId||(Od.set(e.id,n.promptId),Cd(()=>N(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:lt(n.provider||t?.agent),payload:{promptId:n.promptId,question:n.question||n.title||null,options:(n.options||[]).map(r=>r?.label??r).slice(0,12)}})))}function YA(e,t,n,r={}){Od.delete(e.id),Cd(()=>N(e,{type:"prompt.dismissed",actor:"user",source:"orchestrator",boxId:t?.name,provider:lt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}function rw(e,t,n,r={}){Od.delete(e.id),Cd(()=>N(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:lt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var lt,Rd,Cd,KA,GA,Od,ow=q(()=>{be();lt=e=>e||null;Rd=Promise.resolve(),Cd=e=>(Rd=Rd.then(e).catch(()=>{}),Rd),KA=()=>Rd;GA=new Set;Od=new Map});function UF(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function nT(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of UF(n).reverse()){if(!BF.test(r))continue;let o=r.endsWith("?")&&r.length<=FF&&jF.test(r);if(LF.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function oT(e){let t=String(e||""),n=[];HF.test(t)&&n.push("commit"),rT.test(t)&&n.push("rebase"),WF.test(t)&&n.push("push");let r=GF.test(t);!r&&qF.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 sT(e){return rT.test(String(e||""))}function Ma(e,{branch:t=null,base:n=null,toPr:r=null,repos:o=null,offeredRebase:s=!1}={}){Array.isArray(e)||(e=e==="push"?["commit","push"]:["commit","push","pr"]);let a=new Set(e),i=Array.isArray(o)&&o.length>0,c=i?o.map(f=>`${f.name}/`).join(", "):null,l=t?` on this branch (${t})`:"",u=[];if(a.has("commit")&&u.push(i?`commit the work in each changed checkout (${c})${l} with a clear commit message per repo`:`commit the work${l} with a clear commit message`),a.has("rebase")){let f=a.has("commit")?"the branch":`this branch${t?` (${t})`:""}`;u.push(i?`rebase ${f} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${f} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`)}if(a.has("push")||a.has("pr")){let f=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",w=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${w}${f}`:`push this branch${t?` (${t})`:""} from each changed checkout (${c}) to its remote${f}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${w}${f}`:`push this branch${l} to the remote${f}`)}a.has("pr")&&u.push(i?"open a pull request in each changed repo with a concise title and a description of what changed and why (run `gh pr create` inside each checkout)":`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let d=[];a.has("commit")||d.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&d.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&d.push("don't push"),a.has("pr")||d.push("don't open a pull request");let p=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return d.length&&(p+=` ${d.join(", and ").replace(/^./,f=>f.toUpperCase())}.`),a.has("pr")&&(p+=i?" If a repo's PR for this branch is already open, just push that repo's update instead.":" If a PR for this branch is already open, just push the update instead."),p}function aT(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 iT(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<=KF&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function cT(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+VF||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function lT(e){let t=(Array.isArray(e)?e:[]).filter(r=>XA[r]).map(r=>XA[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 ZA,QA,eT,tT,BF,LF,jF,FF,ho,HF,rT,WF,qF,GF,KF,VF,XA,ws=q(()=>{ZA=/(?<!\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,QA=/(?<!\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,eT=/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,tT=/(?<!\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,BF=new RegExp(`\\b(?:${ZA}|${QA}|${eT}|${tT})\\b`,"i"),LF=/\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,jF=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,FF=90;ho=["commit","rebase","push","pr"],HF=new RegExp(`\\b(?:${ZA})\\b`,"i"),rT=new RegExp(`\\b(?:${tT})\\b`,"i"),WF=new RegExp(`\\b(?:${QA})\\b`,"i"),qF=new RegExp(`\\b(?:${eT})\\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;KF=3e4;VF=2500;XA={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import zF from"node:crypto";import Nd from"node:fs/promises";import uT from"node:path";import JF from"node:os";function mT(e){fT=typeof e=="function"?e:async()=>new Set}function YF(e,t,n){if(e.originNode&&e.originNode===t)return!0;let r=e.data?.trackId;return!!r&&n.has(r)}async function We(){return kr||(sw??=Nd.readFile(iw,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(kr=e,!t&&Ge()){let{records:n,source:r}=await dt("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){let o=F0(),s=await fT().catch(()=>new Set),a=0;for(let i of n){if(i.deletedAt||!i.data?.repo||i.data?.number==null)continue;if(!YF(i,o,s)){a++;continue}let{id:c,updatedAt:l,...u}=i.data;kr[ce(u.repo,u.number)]=u}a&&console.log(`[pr-links] restore: ${Object.keys(kr).length} link(s) re-seeded, ${a} other-node record(s) left on the plane`),Object.keys(kr).length&&await cw().catch(()=>{})}}return kr}).finally(()=>sw=null),sw)}async function cw(){let e=JSON.stringify({links:kr},null,2),t=`${iw}.${process.pid}.tmp`;await Nd.mkdir(dT,{recursive:!0}),await Nd.writeFile(t,e),await Nd.rename(t,iw)}async function et(e){aw=aw.then(cw,cw),await aw.catch(()=>{});let t=kr?.[e];t&&It("prlinks",{id:pT(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function Yt({repo:e,number:t,trackId:n,url:r=null,headRef:o=null,title:s=null,source:a="matched"}){if(!e||!Number.isInteger(Number(t))||!n)return{link:null,created:!1};let i=await We(),c=ce(e,t);return i[c]?{link:i[c],created:!1}:(i[c]={repo:e,number:Number(t),trackId:n,url:r,headRef:o,title:s,source:a,announced:XF.has(a),firstSeen:Date.now()},await et(ce(e,t)),{link:i[c],created:!0})}async function xr(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return!n||n.announced?null:(n.announced=!0,await et(ce(e,t)),n)}async function hT(e,t){if(!e||t==null)return!1;let n=(await We())[ce(e,t)];return n?.notifyOnMerge?(delete n.notifyOnMerge,await et(ce(e,t)),!0):!1}async function ft(e,t){return!e||t==null?null:(await We())[ce(e,t)]??null}async function wo(e,t,n){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,delete r.notifyOnMerge,delete r.notifyPending,delete r.followupPending,await et(ce(e,t)),r):null}async function wT(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await We())[ce(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await et(ce(e,t)),o):null}async function gT(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await We())[ce(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await et(ce(e,t)),o):null}async function Pd(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await et(ce(e,t)),r)}async function Dd(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await et(ce(e,t)),n)}async function Bd(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await et(ce(e,t)),r)}async function Ld(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await et(ce(e,t)),n)}async function yT(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return r?(r.baseFoldAt=n,await et(ce(e,t)),r):null}async function bT(e,t,{misses:n,until:r}){if(!e||t==null)return null;let o=(await We())[ce(e,t)];return o?(o.brokerRefused={misses:Number(n)||0,until:Number(r)||0},await et(ce(e,t)),o):null}async function kT(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return n?.brokerRefused?(delete n.brokerRefused,await et(ce(e,t)),n):null}async function xT(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await et(ce(e,t)),n)}async function vT(e,t,n){if(!e||t==null||!n)return null;let r=(await We())[ce(e,t)];return r?(r.lastHeadSha=n,await et(ce(e,t)),r):null}async function ST(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await We())[ce(e,t)];if(!r)return{prev:null,link:null};let o=r.verdict??null;return o&&o.blocked===(n.blocked??null)&&(o.sha??null)===(n.sha??null)||(r.verdict={blocked:n.blocked??null,sha:n.sha??null,at:n.at??Date.now()},await et(ce(e,t))),{prev:o,link:r}}async function Tc(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await et(ce(e,t)),r):null}async function gs(e,t,n=!0){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await et(ce(e,t)),r):null}async function ys(e,t,n=!0){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await et(ce(e,t)),r):null}async function AT(e,t,n){if(!e||t==null)return null;let r=(await We())[ce(e,t)];if(!r)return null;r.notifyOnMerge=!0;let o=new Map((r.notifyPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.notifyPending=[...o.values()],await et(ce(e,t)),r}async function TT(e,t,n){if(!e||t==null)return null;let r=(await We())[ce(e,t)];if(!r)return null;let o=new Map((r.followupPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.followupPending=[...o.values()],await et(ce(e,t)),r}async function Ec(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,delete n.verdict,await et(ce(e,t)),n)}async function ET(){return Object.values(await We()).filter(e=>!ZF.has(e.state))}async function En(){return Object.values(await We()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function _T(){if(!Ge())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await dt("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await We(),r=0;for(let[o,s]of Object.entries(n))await It("prlinks",{id:pT(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var dT,iw,ce,pT,kr,sw,aw,fT,XF,ZF,Gn=q(()=>{Yr();dT=process.env.BOSTRAT_STATE_DIR||uT.join(process.env.HOME||JF.tmpdir(),".bostrat"),iw=uT.join(dT,"pr-links.json"),ce=(e,t)=>`${e}#${Number(t)}`,pT=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=zF.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},kr=null,sw=null,aw=Promise.resolve(),fT=async()=>new Set;XF=new Set(["api","observed","reconciled"]);ZF=new Set(["merged","closed","lost"])});import QF from"node:fs";import lw from"node:fs/promises";import OT from"node:path";import eU from"node:os";function NT(e,t=!1){if(!t&&e-$T<tU)return;$T=e;let n=JSON.stringify({windowStart:Ca,byCaller:Object.fromEntries(vr),lastHour:go});RT=RT.then(()=>lw.mkdir(MT,{recursive:!0})).then(()=>lw.writeFile(`${jd}.tmp`,n)).then(()=>lw.rename(`${jd}.tmp`,jd)).catch(()=>{})}function PT(e){e-Ca<CT||(go={start:Ca,total:Fd(vr),byCaller:Object.fromEntries(vr)},go.total&&console.log(`[github] ${go.total} calls last hour`,go.byCaller),Ca=e,vr=new Map,NT(e,!0))}function Sr(e="other",t=Date.now()){PT(t),vr.set(e,(vr.get(e)||0)+1),NT(t)}function DT(e=Date.now()){return PT(e),{windowStart:Ca,total:Fd(vr),byCaller:Object.fromEntries(vr),lastHour:go,backoffUntil:bs>e?bs:null}}function yo(e){return nU.test(String(e?.message||e||""))}function bo(e,t=Date.now()){return bs<=t&&console.warn(`[github] rate limited \u2014 backing off ${IT/6e4} min:`,String(e?.message||e||"").slice(0,200)),bs=Math.max(bs,t+IT),bs}function Ud(e=Date.now()){return Math.max(0,bs-e)}var MT,jd,CT,IT,tU,Ca,vr,go,bs,$T,Fd,RT,nU,_c=q(()=>{MT=process.env.BOSTRAT_STATE_DIR||OT.join(process.env.HOME||eU.tmpdir(),".bostrat"),jd=OT.join(MT,"github-metrics.json"),CT=60*6e4,IT=5*6e4,tU=6e4,Ca=Date.now(),vr=new Map,go=null,bs=0,$T=0,Fd=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(QF.readFileSync(jd,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<CT?(Ca=e.windowStart,vr=new Map(Object.entries(e.byCaller||{})),go=e.lastHour||null):Fd(new Map(Object.entries(e.byCaller||{})))&&(go={start:e.windowStart,total:Fd(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}RT=Promise.resolve();nU=/rate limit|too quickly|abuse detection|HTTP 429/i});import BT from"node:fs/promises";import LT from"node:path";import rU from"node:os";function Wd({change:e,repo:t=null,number:n=null,githubAt:r=null,observedAt:o=Date.now(),lane:s=null,intervalMs:a=null,source:i=null}){let c=r?o-r:null,l={change:e,repo:t,number:n,githubAt:r,observedAt:o,latencyMs:c,lane:s,intervalMs:a,source:i};Hd.push(l),Hd.length>sU&&Hd.shift();let u=uw.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)),uw.set(e,u),aU(l),l}async function aU(e){try{await BT.mkdir(jT,{recursive:!0}),await BT.appendFile(oU,JSON.stringify(e)+`
|
|
187
|
-
`)}catch{}}function FT(){let e={};for(let[t,n]of uw)e[t]={count:n.count,measured:n.measured,avgMs:n.measured?Math.round(n.totalMs/n.measured):null,maxMs:n.measured?n.maxMs:null};return{byChange:e,recent:Hd.slice(-20)}}var jT,oU,sU,Hd,uw,qd=q(()=>{jT=process.env.BOSTRAT_STATE_DIR||LT.join(process.env.HOME||rU.tmpdir(),".bostrat"),oU=LT.join(jT,"github-latency.ndjson"),sU=100,Hd=[],uw=new Map});import{execFile as iU}from"node:child_process";import{promisify as cU}from"node:util";async function lU(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Pa("git",["-C",e,"rev-parse","HEAD"]),Pa("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await N({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function cn(e,t,n={}){let{caller:r="prs",...o}=n,s=await Bn({cwd:e});Sr(r);try{let{stdout:a}=await Pa("gh",t,{cwd:e,...uU,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw yo(a)&&bo(a),a}}async function fw(){if(Gd&&Date.now()-Gd.at<6e4)return Gd.data;let e;try{Sr("auth");let t=await Pa("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 Ci();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return Gd={at:Date.now(),data:e},e}async function dU(e){try{let{stdout:t}=await Pa("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function HT(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 dw(e){let t=(e||[]).map(HT),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 hw(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function qT(e,t){if(!t)return null;let n=await Me(e);if(!n)return null;try{let r=await cn(e,["api",`repos/${n}/actions/runs?head_sha=${encodeURIComponent(t)}&per_page=100`,"--jq",".workflow_runs"]),o=new Map;for(let s of JSON.parse(r||"[]"))o.has(s.name)||o.set(s.name,s);return[...o.values()]}catch{return null}}function GT(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 KT(e,t){let n=await qT(e,t);return n===null?{...dw([]),unavailable:!0}:dw(GT(n))}function Kd(e,t=dw(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 fU(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Na.delete(s);continue}r.add(s),Na.has(s)||Na.set(s,n),o.mergeabilityPending=n-Na.get(s)<pU}for(let o of Na.keys())o.startsWith(`${e}#`)&&!r.has(o)&&Na.delete(o);return t}async function Da(e,t){let n=await Ar(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await cn(n.path,["pr","view",String(r),"--json",mw]);return Kd(JSON.parse(o))}catch(o){if(!hw(o))throw o;let s=await cn(n.path,["pr","view",String(r),"--json",WT]),a=JSON.parse(s);return Kd(a,await KT(n.path,a.headRefOid))}}async function Kn(e,{caller:t="prs"}={}){try{let n=await cn(e,["pr","list","--json",mw,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>Kd(r)).sort(UT)}catch(n){if(!hw(n))throw n;let r=await cn(e,["pr","list","--json",WT,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>Kd(o,await KT(e,o.headRefOid))))).sort(UT)}}function ln(){Ic.clear()}async function wU(e,t){if(!e)return t;for(let n of t){let r=await ft(e,n.number).catch(()=>null);if(!r)continue;r.trackId&&(n.trackId=r.trackId),r.autoMerge&&(n.autoMergeArmed=!0),r.archiveOnMerge&&(n.archiveOnMerge=!0),r.notifyOnMerge&&(n.notifyOnMerge=!0);let o=s=>n.headSha&&s.headSha&&n.headSha!==s.headSha;r.ciFix&&(o(r.ciFix)?await Pd(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Bd(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function VT(){let e=await fw();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await fr())await dU(a.path)&&t.push(a);let n=Date.now(),r=Ud(n)>0,o=await Promise.all(t.map(async a=>{let i=Ic.get(a.path),c=i?.pending?hU:mU;if(i&&(r||n-i.at<c))return i.entry;try{let l=await Me(a.path).catch(()=>null),u=fU(a.path,await Kn(a.path,{caller:"tab"}),n);if(await wU(l,u),i?.entry?.prs)for(let f of u)i.entry.prs.find(h=>h.number===f.number)?.checks?.pending>0&&f.checks.total>0&&f.checks.pending===0&&Wd({change:f.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:f.number,githubAt:f.checks.settledAt,source:"tab"});let d={name:a.name,path:a.path,prs:u},p=u.some(f=>f.mergeabilityPending||f.checks.pending>0||f.autoMergeArmed);return Ic.set(a.path,{at:Date.now(),entry:d,pending:p}),d}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:Vn(l)}}})),s=new Set(t.map(a=>a.path));for(let a of Ic.keys())s.has(a)||Ic.delete(a);return{auth:e,repos:o}}async function Ar(e){let t=(await fr()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function ks(e){return Zs(e)}async function Ba(e,t,{method:n="squash"}={}){let r=await Ar(e);if(!$c.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 cm().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await cn(r.path,s)}catch(a){throw new Error(Vn(a))}finally{ln()}return{ok:!0,number:o}}async function Rc(e,t){let n=await Ar(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await cn(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Vn(s))}finally{ln()}let o=await Me(n.path).catch(()=>null);return o&&await yT(o,r).catch(()=>{}),{ok:!0,number:r}}async function ww(e,t){let n=await Ar(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await cn(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Vn(o))}finally{ln()}return{ok:!0,number:r}}function gw(e){return e.green&&!e.isDraft}function kU(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 zT(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:gw(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function JT(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function Oc(e,t){let n=a=>a?.firstSeen!=null&&Number.isFinite(Number(a.firstSeen))?Number(a.firstSeen):Number.MAX_SAFE_INTEGER,r=n(e),o=n(t);if(r!==o)return r-o;let s=String(e?.repo||"").localeCompare(String(t?.repo||""));return s||Number(e?.number||0)-Number(t?.number||0)}function YT(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(Oc))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function XT(){let e={};for(let[t,n]of pw)e[t]={...n};return e}async function ZT(e,{method:t="merge",onMerged:n=null}={}){let r=await Ar(e);if(!$c.has(t))throw new Error(`invalid merge method: ${t}`);if(pw.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Kn(r.path)).filter(gw).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 pw.set(r.path,s),vU(s,o,{listPrs:()=>Kn(r.path),merge:a=>Ba(r.path,a,{method:t}),sleep:bU,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function xU(e,t){let n=Date.now()+(t.settleMaxMs??yU),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(gU)}}async function vU(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 xU(t.slice(o).map(i=>i.number),n)}catch(i){for(let c of t.slice(o))e.failed.push({number:c.number,title:c.title,reason:`couldn't re-check PR state: ${Vn(i)}`});break}let a=r.find(i=>i.number===s.number);if(!a){e.skipped.push({number:s.number,title:s.title,reason:"no longer open"});continue}if(!gw(a)){e.skipped.push({number:a.number,title:a.title,reason:kU(a)});continue}try{await n.merge(a.number),e.merged.push({number:a.number,title:a.title});try{await n.onMerged?.(a)}catch{}}catch(i){e.failed.push({number:a.number,title:a.title,reason:Vn(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),ln()}}async function yw(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Pa("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Vn(l)}`)}await lU(e);let o=n||"";if((await Bn({cwd:e})).GH_TOKEN){let l=await cm().catch(()=>null);l&&(o=`${o?`${o}
|
|
188
|
-
|
|
189
|
-
`:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",t.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await cn(e,s)}catch(l){throw new Error(Vn(l))}finally{ln()}let i=a.match(/https:\/\/\S+\/pull\/\d+/)?.[0]??null,c=i?Number(i.match(/\/pull\/(\d+)/)?.[1]):null;return{ok:!0,url:i,number:c}}async function
|
|
159
|
+
[\u2026 brief truncated]`}var pL,fL,Fv,mL,hL,gL,Uv,Hv,qv=G(()=>{od();pL=12,fL=2e3,Fv=48e3,mL=30,hL=10;gL=(e,t)=>{let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1)}\u2026`},Uv=(e,t)=>e&&t?Math.max(1,Math.round((e-t)/6e4)):null;Hv={failover:"**This is a reconstruction, not a checkpoint.** The node this Track lived on went offline without a clean handoff; bostrat assembled this brief from the durable event log. Treat every claim below as a lead, not a fact \u2014 **verify the repository state first** (git status, git log, the diff against the last pushed ref) before continuing work.","in-place":"**This is a reconstruction, not a checkpoint.** The previous lead ended without writing one (out of credits, capped, or unresponsive); bostrat assembled this status from the Track's event log. You are in the SAME live workspace \u2014 uncommitted work is present locally. **Run git status and git log first** and treat every claim below as a lead, not a fact.",get recover(){return this.failover},transfer:"**This Track was MOVED to a new machine while it was alive.** The previous node checkpointed and pushed its work as part of the move; this workspace was re-created from that push (uncommitted work re-applied when possible). **Run git status and git log to confirm what carried over**, then continue the objective below."}});function ro(e){let t=e.search(mh);return(t>=0?e.slice(0,t):e).trim()}function kL(e){if(!e)return"";let t=e.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!t||/hidden|\d+\s+lines?/i.test(t)?"":t}function xL(e){let t=String(e).trim();return!!(!t||no.test(e)||mh.test(t)||/hidden|\d+\s+lines?/i.test(t)||/notespress|press n to add/i.test(t)||/^\s*notes?\s*$/i.test(t))}function vL(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=SL(n,t);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function SL(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 Gv(e){return e.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function AL(e,t=""){let n=String(e);if(!zv.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
160
|
+
`),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(os);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let p=r[u].trim();p&&!no.test(r[u])&&a.push(p)}}if(s.length<2)return null;let i=s.map(u=>({label:ro(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=["Switch model?",...a].join(" ");return{provider:"claude",box:t,multiSelect:!1,cursorIndex:c,promptId:`claude:model-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Model",multiSelect:!1,options:i}]}}function EL(e,t=""){let n=String(e).replace(/\r/g,"").split(`
|
|
161
|
+
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!Jv.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(os);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&&!(TL.test(n[c])||no.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:ro(c.label),description:""}));return{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,s.findIndex(c=>c.cursor)),promptId:`claude:confirm-gate:${i.map(c=>c.label).join("|")}`.slice(0,240),questions:[{question:a.join(" ")||"Claude Code is asking you to confirm",header:"Confirm",multiSelect:!1,options:i}]}}function _L(e,t=""){let n=String(e);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
162
|
+
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(os);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 $L(e,t=""){let n=String(e);if(!dh.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
163
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(IL.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(os);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let p=r[u].trim();p&&!no.test(r[u])&&a.push(p)}}if(s.length<2||!s.some(u=>dh.test(u.label)))return null;let i=s.map(u=>({label:ro(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=a.join(" ");return{provider:"claude",box:t,multiSelect:!1,cursorIndex:c,promptId:`claude:resume-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Resume",multiSelect:!1,options:i}]}}function hh(e){let t=[];for(let r=0;r<e.length;r++){let o=e[r].match(os);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 RL(e,t=""){let n=String(e);if(!Yv.test(n)||!Xv.test(n)||ss.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
164
|
+
`),o=hh(r);return o?{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"theme",promptId:`claude:setup:theme:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks: choose the text style that looks best with your terminal. You can change it later with /theme.",header:"Theme",multiSelect:!1,options:o.map(s=>({label:ro(s.label),description:""}))}]}:null}function OL(e,t=""){let n=String(e);if(!Zv.test(n)||ss.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
165
|
+
`),o=hh(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:ro(i),description:c.join("\xB7").trim()}});return{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(a=>a.cursor)),setup:"login-method",promptId:`claude:setup:login-method:${s.map(a=>a.label).join("|")}`.slice(0,200),questions:[{question:"How do you want to sign in to Claude? A Claude subscription (Pro/Max/Team) uses your existing plan; a Console account bills API usage. Your login stays on this box \u2014 bostrat never sees it.",header:"Log in",multiSelect:!1,options:s}]}}function CL(e,t){let n=e[t].trim();for(let r=t+1;r<e.length;r++){let o=e[r].trim();if(!o||!ML.test(o)||wh.test(e[r]))break;n+=o}return n}function NL(e,t=""){let n=String(e);if(!wh.test(n)||ss.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
166
|
+
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=CL(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 DL(e,t=""){let n=String(e);if(!ph.test(n)||!PL.test(n)||ss.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
167
|
+
`)].reverse().find(a=>ph.test(a))||"OAuth error").trim();return{provider:"claude",box:t,multiSelect:!1,cursorIndex:0,setup:"oauth-error",promptId:`claude:setup:oauth-error:${s}`.slice(0,200),questions:[{question:`${s} \u2014 retry to get a fresh sign-in link.`,header:"Log in",multiSelect:!1,options:[{label:"Retry sign-in",description:"Mints a new sign-in link and code"}]}]}}function BL(e,t=""){let n=String(e);if(!Qv.test(n)||ss.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
168
|
+
`),o=r.flatMap(a=>{let i=a.match(/^\s*(❯\s*)?(Yes, I trust this folder|No, exit)\s*$/);return i?[{cursor:!!i[1],label:i[2]}]:[]}),s=o.length===2&&new Set(o.map(a=>a.label)).size===2&&o.filter(a=>a.cursor).length===1?o:hh(r);return s?{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,s.findIndex(a=>a.cursor)),setup:"trust",promptId:`claude:setup:trust:${s.map(a=>a.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks whether it can read, edit, and run files in this workspace. This box's workspace is bostrat-managed scratch space, so trusting it is safe.",header:"Folder trust",multiSelect:!1,options:s.map(a=>({label:ro(a.label),description:""}))}]}:null}function LL(e,t=""){let n=String(e);if(ss.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
169
|
+
`),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(eS);if(!s)return null;let a=[];for(let l=o-1;l>=0&&a.length<3;l--){let u=r[l].trim();if(!u){if(a.length)break;continue}if(no.test(r[l])||mh.test(u))break;a.unshift(u)}let i=s[1].trim();i&&a.push(i);let c=a.join(" ")||"Claude Code is waiting for you to continue.";return{provider:"claude",box:t,multiSelect:!1,cursorIndex:0,setup:"continue",promptId:`claude:setup:continue:${c}`.slice(0,200),questions:[{question:c,header:"Continue",multiSelect:!1,options:[{label:"Continue",description:""}]}]}}function jL(e){let t=-1;for(let r=e.length-1;r>=0;r--)if(fh.test(e[r])){t=r;break}if(t<0)return null;let n=-1;for(let r=t-1;r>=0;r--)if(/[☐☒]/.test(e[r])){n=r;break}return n<0?null:{headerLine:n,chatLine:t,optStart:n+1,optEnd:t}}function FL(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(sd.test(r.label))return null;let o=null;for(let s=r.line+1;s<Math.min(t.length,r.line+3);s++){let a=t[s].replace(/^\s*❯/,"").trim();if(a){o=a;break}}return o&&(no.test(o)||Vv.test(o))?r:null}function UL(e,t){if(!e)return null;for(let n=e.line+1;n<Math.min(t.length,e.line+3);n++){let r=t[n],o=r.replace(/^\s*❯/,"").trim();if(o)return Vv.test(o)?{cursor:/^\s*❯/.test(r)}:null}return null}function HL(e,t=""){let n=String(e);if(!Kv.test(n)||!fh.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
170
|
+
`),o=jL(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let L=i;L<c;L++){let H=r[L].match(os);H&&(u<0&&(u=L),l.push({line:L,cursor:!!H[1],checked:H[3]!=null&&H[3]!==" ",hasCheckbox:H[3]!=null,label:H[4].trim(),desc:""}))}if(!l.length)return null;for(let L of l)for(let H=L.line+1;H<Math.min(r.length,L.line+4)&&!(os.test(r[H])||no.test(r[H]));H++){let R=r[H].trim();if(!(!R||xL(r[H])||sd.test(R))){L.desc=kL(R);break}}let d=FL(l,r),p=l.filter(L=>L!==d&&!sd.test(L.label));if(!p.length)return null;let f=p.some(L=>L.hasCheckbox),w=f?UL(d,r):null,h=w?.cursor?p.length+1:d?.cursor?p.length:Math.max(0,p.findIndex(L=>L.cursor)),y=d&&!sd.test(d.label)?ro(d.label):"",k=s>=0?s+1:0,g=[];for(let L=k;L<u;L++){let H=r[L].trim();H&&!no.test(r[L])&&!/[☐☒]/.test(r[L])&&g.push(H)}let x=g.join(" ")||(s>=0?Gv(r[s]):"")||"The agent is asking a question",S=s>=0?vL(r[s],x):null,_=S?.currentTabLabel||(s>=0?Gv(r[s]):""),M=p.map(L=>({label:ro(L.label),description:L.desc,...f?{checked:!!L.checked}:{}})),P=M.map(L=>L.label).join("|"),T=`claude:${S?`q${S.questionIndex+1}/${S.questionTotal}`:"q1/1"}:${_}:${x}:${P}`.slice(0,240),$=S?`claude:ask:${S.tabs.map(L=>L.label).join("|")}`.slice(0,240):T;return{provider:"claude",box:t,multiSelect:f,cursorIndex:h,promptId:T,groupId:$,...d?{custom:!0,customIndex:p.length,customText:y}:{},...w?{commitIndex:p.length+1}:{},dismissable:!0,...S?{questionIndex:S.questionIndex,questionTotal:S.questionTotal,currentTabLabel:S.currentTabLabel,tabs:S.tabs}:{},questions:[{question:x,header:_,multiSelect:f,options:M}]}}function oo(e,t){let n=String(t||"");return n?e==="claude"?Kv.test(n)&&fh.test(n)||zv.test(n)||dh.test(n)||Jv.test(n.trimEnd().split(`
|
|
171
|
+
`).pop()||"")?!0:ss.test(n)?!1:Yv.test(n)&&Xv.test(n)||Zv.test(n)||wh.test(n)||ph.test(n)||Qv.test(n)||eS.test(n.trimEnd().split(`
|
|
172
|
+
`).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 Sn(e,t){return!e||!t?null:e.agent==="claude"?_L(t,e.name)||HL(t,e.name)||AL(t,e.name)||$L(t,e.name)||DL(t,e.name)||NL(t,e.name)||OL(t,e.name)||RL(t,e.name)||BL(t,e.name)||LL(t,e.name)||EL(t,e.name):e.agent==="cursor"||e.agent==="codex"?Wx(e.agent,t,e.name):null}async function tS(e){if(!e)return null;let t=await Jt(e.name).catch(()=>"");return Sn(e,t)}function gh(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 nS(e,n))s.push(...o(a,i),"Enter"),a=i;Number.isInteger(e.commitIndex)?s.push(...o(a,e.commitIndex),"Enter"):s.push("Tab","Enter")}else{let a=n.length?n[0]:0;s.push(...o(r,a),"Enter")}return s}function nS(e,t){let n=e?.questions?.[0]?.options||[],r=new Set(t),o=Math.max(n.length,t.length?Math.max(...t)+1:0),s=[];for(let a=0;a<o;a++)!!n[a]?.checked!==r.has(a)&&s.push(a);return s}function rS(e,t,n=""){let r=typeof n=="string"?n.trim():"";if(!r||!e?.custom)return{pre:gh(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 nS(e,o))c.push(...i(l,d),{key:"Enter"}),l=d;return c.push(...i(l,s)),{pre:c,text:r,post:[{key:"Down"},{key:"Enter"}]}}return c.push(...i(l,s)),{pre:c,text:r,post:[{key:"Enter"}]}}function oS(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 Kv,fh,os,sd,Vv,no,mh,zv,Jv,TL,dh,IL,ss,Yv,Xv,Zv,wh,ML,ph,PL,Qv,eS,yh=G(()=>{pt();Hu();Kv=/to navigate\b.*\bEsc to cancel|Enter to select\b/,fh=/Chat about this/i,os=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,sd=/^(type something|chat about this|submit|next)\b/i,Vv=/^(submit|next)\b/i,no=/^[─—-]{6,}\s*$/,mh=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;zv=/^\s*Switch model\?\s*$/m;Jv=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,TL=/^\s*[─—▔-]{6,}\s*$/;dh=/Resume from summary/i,IL=/^\s*This session is\b/;ss=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;Yv=/Choose the text style that looks best with your terminal/,Xv=/Let's get started\./;Zv=/Select login method:/;wh=/Paste code here if prompted/,ML=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;ph=/OAuth error:/,PL=/Press Enter to retry\./;Qv=/Quick safety check:\s+Is this a project you created or one you trust/;eS=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function ad(e,t,n){if(!e||!t)return null;for(let r of WL)if(r.provider===e.agent)try{if(r.match(t,n))return r}catch{}return null}function GL(e){let t=bh.get(e);return t||(t={sentAt:[],last:0,emitted:new Set},bh.set(e,t)),t}function iS(e,t,n=Date.now()){let r=GL(e);if(r.sentAt=r.sentAt.filter(s=>n-s<aS),r.sentAt.length>=sS)return{action:"give-up"};if(n-r.last<qL)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 cS(e,t=Date.now()){let n=bh.get(e);return n?n.sentAt.filter(r=>t-r<aS).length>=sS:!1}var WL,qL,sS,aS,bh,kh=G(()=>{WL=[{provider:"claude",id:"login-continue",match(e){return/^claude:setup:continue:/.test(e?.promptId||"")&&e?.setup==="continue"},keys:["Enter"],accept:!0,gate:"login-enter"},{provider:"codex",id:"login-continue",match(e){return e?.promptId==="codex:signin-complete"&&e?.setup==="continue"},keys:["Enter"],accept:!0,gate:"login-enter"},{provider:"claude",id:"fullscreen-upsell",match(e,t){let n=e?.questions?.[0],r=(n?.options||[]).map(s=>String(s.label||"").trim());if(r.length!==2||!/^Yes, try it$/.test(r[0])||!/^Not now$/.test(r[1]))return!1;let o=`${n?.question||""}
|
|
173
|
+
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];qL=5e3,sS=5,aS=3600*1e3,bh=new Map});function id(e,t,n){let{state:r,reason:o}=sc(t,n);if(r!=="unknown"){if(e){let a=Date.now();for(let[i,c]of gc)a-c.at>=lS()&&gc.delete(i);gc.set(e,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!e)return!1;let s=gc.get(e);return s?Date.now()-s.at>=lS()?(gc.delete(e),!1):s.busy:!1}async function yc(e,t){let n={working:!1,prompted:!1};if(!e)return n;let r=await Ne(e,{lines:ze}).catch(()=>"");if(!id(e,t,r))return Ju(t,r)?{working:!0,prompted:!1}:n;if(oo(t,r)){let o=await Jt(e).catch(()=>r),s=Sn({name:e,agent:t},o);if(s)return!ad({name:e,agent:t},s,o)||cS(e)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function as(e,t){return(await yc(e,t)).working}var lS,gc,xa=G(()=>{pt();rs();yh();kh();lS=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,gc=new Map});function QL(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 tj(e){if(e.type==="queue-operation"&&e.operation==="enqueue")return uS.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"&&uS.test(t)?t:null}async function dS(e,t={}){return(await fS(e,t)).launches}async function pS(e,t={}){let{launches:n,done:r}=await fS(e,t);return n.filter(o=>!r.has(o.id))}async function fS(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await jt(e,t)}catch{return{launches:[],done:new Set,stopped:new Map}}let o=new Map,s=new Set,a=new Set,i=new Map,c=new Map,l=new Map,u=new Map;for(let p of r.split(`
|
|
174
|
+
`)){let f=p.trim();if(!f||f[0]!=="{")continue;let w;try{w=JSON.parse(f)}catch{continue}if(w.isSidechain)continue;let h=tj(w);if(h){let g=h.match(ej)?.[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?ma(x):null),detail:x?ma(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(XL.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=nj(g.content);if(KL.test(x)){s.add(g.tool_use_id);let P=x.match(YL)?.[1];P&&l.set(P,g.tool_use_id)}let S=x.match(JL)?.[1];S&&l.set(S,g.tool_use_id);let _=x.match(VL);if(_){l.set(_[1].trim(),g.tool_use_id);let P=c.get(g.tool_use_id);P&&(c.delete(g.tool_use_id),P.proven=!0,P.outputFile=x.match(zL)?.[1]?.replace(/\.$/,"")||null,o.set(P.id,P))}let M=u.get(g.tool_use_id);if(M){u.delete(g.tool_use_id);let{message:P,taskId:j}=QL(x);if(!g.is_error&&ZL.test(P)){let T=l.get(M.taskId)??(j?l.get(j):void 0);T&&(a.add(T),i.set(T,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 KL,VL,zL,JL,YL,XL,ZL,uS,ej,nj,mS=G(()=>{vn();ha();KL=/^Async (?:agent|task) launched/i,VL=/^Command did not complete within its [^\n]*?moved to the background \(ID: ([^)]+)\)/i,zL=/Output is being written to:\s*(\S+)/i,JL=/^Command running in background with ID:\s*([^\s.]+)/i,YL=/\bagentId:\s*([A-Za-z0-9_-]+)/,XL=new Set(["TaskStop","KillShell"]),ZL=/^Successfully (?:stopped|killed)\b/i;uS=/^\s*<task-notification>/i,ej=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i;nj=e=>{if(typeof e=="string")return e.trim();if(!Array.isArray(e))return"";let t=e.find(n=>n&&n.type==="text"&&typeof n.text=="string");return t?t.text.trim():""}});function so(e,t){if(!e)return null;let n=String(e);if(t){let o=t.endsWith("/")?t:`${t}/`;if(n===t)return".";if(n.startsWith(o))return n.slice(o.length)||"."}let r=n.split("/").filter(Boolean);return n.startsWith("/")&&r.length>2?r.slice(-2).join("/"):n}function rj(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 hS(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:An(`read ${so(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${va(r)}`:`Read ${va(r)}`}}case"Grep":{let r=t?.pattern;if(!r)return null;let o=t?.path?so(t.path,n):null,s=t?.glob?` --glob ${xh(t.glob)}`:"",a=An(String(r),40);return{command:An(`grep ${xh(r)}${o&&o!=="."?` ${o}`:""}${s}`,140),description:o&&o!=="."?`Search for \u201C${a}\u201D in ${o}`:`Search for \u201C${a}\u201D`}}case"Glob":{let r=t?.glob_pattern;if(!r)return null;let o=t?.target_directory?so(t.target_directory,n):null;return{command:An(`glob ${xh(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${An(String(r),40)}\u201D`}}case"StrReplace":return t?.path?{command:An(`edit ${so(t.path,n)}`,140),description:`Edit ${va(t.path)}`}:null;case"Write":return t?.path?{command:An(`write ${so(t.path,n)}`,140),description:`Write ${va(t.path)}`}:null;case"Delete":return t?.path?{command:An(`delete ${so(t.path,n)}`,140),description:`Delete ${va(t.path)}`}:null;case"ReadLints":{let o=(Array.isArray(t?.paths)?t.paths:[]).map(s=>so(s,n)).filter(Boolean);return{command:An(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=rj(t);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:An(`apply_patch ${so(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${va(r[0])}`}}case"Await":{let r=t?.shell_id??t?.task_id;return{command:An(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var cd,An,va,xh,wS=G(()=>{cd=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),An=(e,t=88)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,va=e=>String(e).replace(/\/+$/,"").split("/").pop()||String(e);xh=e=>`"${String(e).replace(/"/g,'\\"')}"`});import bc from"node:fs";import oj from"node:fs/promises";import is from"node:path";import sj from"node:os";import aj from"node:crypto";import ij from"node:zlib";function bS(e){let t=aj.createHash("md5").update(String(e)).digest("hex");return is.join(cj,t)}function kS(e){try{let t=JSON.parse(bc.readFileSync(is.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 lj(e){let t=bS(e),n;try{n=bc.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=is.join(t,o.name),a=is.join(s,"store.db");if(!bc.existsSync(a))continue;let i=kS(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 ld(e,t){if(!e)return null;if(t){let n=is.join(bS(e),t),r=is.join(n,"store.db");if(bc.existsSync(r))return{id:t,dir:n,store:r,...kS(n)||{createdAtMs:null,updatedAtMs:null}}}return lj(e)}function gS(e,t){let n=e.prepare("SELECT data FROM blobs WHERE id = ?").get(t);return n?Buffer.from(n.data):null}function uj(e){if(!e)return null;let t=e;if(t.length>1&&t[0]===31&&t[1]===139)try{t=ij.gunzipSync(t)}catch{}try{return JSON.parse(t.toString("utf8"))}catch{return null}}function dj(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"||!cd.has(r.toolName))continue;let o=hS(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 vh(e,t){if(!yS)return Sa;let{DatabaseSync:n}=yS,r;try{r=new n(e,{readOnly:!0})}catch{return Sa}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 Sa;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return Sa}let a=s.lastUsedModel||null,i=s.latestRootBlobId?gS(r,s.latestRootBlobId):null;if(!i)return{...Sa,model:a};let c=[],l=[],u=[],d=null,p=[];for(let f of dj(i)){let w=uj(gS(r,f));if(!w)continue;let h=w.providerOptions?.cursor;if(h?.requestId&&p.push(h.requestId),h?.modelProviderMessageId&&p.push(h.modelProviderMessageId),w.role!=="assistant")continue;if(Array.isArray(w.content))for(let g of w.content){let x=g?.providerOptions?.cursor?.modelName;x&&(d=x)}l.push(...pj(w.content,t).map((g,x)=>({...g,id:g.id||`${f}:${x}`}))),u.push(...mj(w.content));let y=fj(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 Sa}finally{try{r.close()}catch{}}}function xS(e,t){let n=e.updatedAtMs;if(!n)try{n=Math.round(bc.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 vS(e,t,{limit:n=80}={}){let r=ld(e,t);if(!r)return[];let{turns:o,model:s}=await vh(r.store,e);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=xS(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 SS(e,t){let n=ld(e,t);if(!n)return null;let{turns:r,requestIds:o,model:s}=await vh(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 AS(e,t,{limit:n=80}={}){let r=ld(e,t);if(!r)return[];let{turns:o,trailingCommands:s}=await vh(r.store,e);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=xS(r,a.length||1),l=[];a.forEach((u,d)=>{for(let p of u.commands)l.push({...p,summary:Qr(p.command,p.description),ts:c(d)})});for(let u of s)l.push({...u,summary:Qr(u.command,u.description),ts:i+1});return l}async function TS(e,t){let n=ld(e,t);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await oj.stat(n.store)).mtimeMs)}catch{return null}}var cj,fj,mj,yS,Sa,Sh=G(()=>{ha();wS();cj=process.env.BOSTRAT_CURSOR_CHATS||is.join(sj.homedir(),".cursor","chats");fj=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(`
|
|
175
|
+
`).trim():"",mj=e=>Array.isArray(e)?e.filter(t=>t&&t.type==="tool-call"&&t.toolName).map(t=>t.toolName):[],yS=process.getBuiltinModule?.("node:sqlite"),Sa={turns:[],trailingCommands:[],model:null,requestIds:[]}});import hj from"node:fs/promises";function _h(e){return ES.set(e.agent,e),e}function ao(e){return ES.get(e)||wj}async function Ih(e,t={}){return(await ao(e.agent).readMessages(e,t)).map(ud(e))}async function _S(e,t={}){return(await ao(e.agent).readCommands(e,t)).map(ud(e))}async function IS(e,t={}){return(await ao(e.agent).readJobs(e,t)).map(ud(e))}async function dd(e,t={}){return(await ao(e.agent).readOpenJobs(e,t)).map(ud(e))}var Aa,Ah,Th,Eh,wj,ES,ud,pd=G(()=>{vn();Fu();ha();mS();Sh();Qo();Aa=class{constructor(t,n=new Set){this.agent=t,this.cliTools=n}async readMessages(){return[]}async readCommands(){return[]}async readJobs(){return[]}async readOpenJobs(){return[]}async updatedAt(){return null}},Ah=class extends Aa{transcript(t){return!t?.cwd||!t?.sessionId?null:xn(t.cwd,t.sessionId)}async readMessages(t,n){let r=this.transcript(t);return r?qm(r,n):[]}async readCommands(t,n){let r=this.transcript(t);return r?vx(r,n):[]}async readJobs(t,n){let r=this.transcript(t);return r?dS(r,n):[]}async readOpenJobs(t,n){let r=this.transcript(t);return r?pS(r,n):[]}async updatedAt(t){let n=this.transcript(t);if(!n)return null;try{return Math.round((await hj.stat(n)).mtimeMs)}catch{return null}}},Th=class extends Aa{async readMessages(t,n){return vS(t.cwd,t.sessionId,n)}async readCommands(t,n){return AS(t.cwd,t.sessionId,n)}async updatedAt(t){return TS(t.cwd,t.sessionId)}},Eh=class extends Aa{sessionId(t){return t?.pinned?t.sessionId:null}async readMessages(t,n){return Ex(t.cwd,this.sessionId(t),n)}async readCommands(t,n){return _x(t.cwd,this.sessionId(t),n)}async updatedAt(t){return Ix(t.cwd,this.sessionId(t))}},wj=new Aa("unknown"),ES=new Map;_h(new Ah("claude",jm));_h(new Th("cursor",cd));_h(new Eh("codex",tc));ud=e=>t=>({...t,box:e.name,agent:e.agent,boxRole:e.role})});function $h(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 $S(e,{timeoutMs:t,reason:n=null,onWait:r=null,deps:o=null}={}){let s={...xj,...o||{}},a=e?.name,i=e?.agent||null;if(!a)throw new Error("awaitQuiescence: box.name is required");let c=Number(t)||gj(),l=yj(),u=s.now(),d=0,p=0,f=!1,w=null;for(;;){p++;let h=await s.capture(a),y=s.busyRead(i,h);if(y.reason==="blocked"){let g=new Error(`${i||"the agent"} in "${a}" is sitting at a login or trust gate \u2014 answer it in the terminal, then retry`);throw g.code="quiescence-blocked",g.detail={blocked:!0},g}if(w={busy:y.state==="busy",unknown:y.state==="unknown",awaitingBackground:s.awaitingBackground(i,h),prompted:!1,jobs:[]},w.busy){let g=await s.turnState(a,i).catch(()=>({prompted:!1}));w.prompted=g?.prompted===!0}if(!w.busy&&!w.unknown&&!w.awaitingBackground){let g=await s.openJobs(e).catch(()=>[]);w.jobs=(Array.isArray(g)?g:[]).map(x=>({id:x.id??null,name:x.name??null,kind:x.kind??null}))}let k=!w.busy&&!w.unknown&&!w.awaitingBackground&&!w.prompted&&w.jobs.length===0;if(d=k?d+1:0,d>=bj){let g=s.now()-u;return f&&re({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,re({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 ${$h(w)} in "${a}" to finish`);throw g.code="quiescence-timeout",g.detail=w,g.waitedMs=s.now()-u,re({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 gj,yj,bj,kj,xj,RS=G(()=>{pt();rs();xa();pd();bn();gj=()=>Number(process.env.BOSTRAT_QUIESCENCE_TIMEOUT_MS)||6e5,yj=()=>Number(process.env.BOSTRAT_QUIESCENCE_POLL_MS)||1500,bj=2,kj=e=>new Promise(t=>setTimeout(t,e)),xj={capture:e=>Ne(e,{lines:ze}),busyRead:sc,awaitingBackground:Ju,turnState:yc,openJobs:dd,now:()=>Date.now(),sleep:kj}});import vj from"node:fs";import io from"node:fs/promises";import co from"node:path";function jS(e){let t=Vo(String(e??"")),n=Buffer.byteLength(t,"utf8");return n<=OS?{content:t,bytes:n,truncated:!1}:{content:`${Buffer.from(t,"utf8").subarray(0,OS).toString("utf8").replace(/�+$/,"")}
|
|
176
|
+
|
|
177
|
+
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function Oj(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(CS.test(s)){if(r.length>=GS)continue;let a=Rj(s.replace(CS,"").replace(/[.;,:]+$/,""),KS,Ij);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>MS&&(o=`${o.slice(0,MS-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function VS(e,t,{timeoutMs:n=9e4}={}){let r=co.join(t,Ej),o=co.join(r,_j);await io.mkdir(r,{recursive:!0});let s=await fd(o),a=await Se(e);if(!a)throw new Error(`session "${e}" not found`);return await md(e,a,$j(o)),await eA(o,s,n),Oj(await io.readFile(o,"utf8"))}function zS(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===US||n===qS;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(FS)||n===Sj||r.startsWith(HS)||r.startsWith(WS)}function JS(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-Mj;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 YS(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 XS(e,{windows:t=[]}={}){if(zS(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 hd(e){return Rh.get(e)||null}async function ZS(e){let t=Rh.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await Se(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);let r={name:e,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:co.join(n.cwd,BS,LS)};return Rh.set(e,r),Pj(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function Pj(e,t){e.step="summarize",await QS(e.name,t,{quiesce:{reason:"console-checkpoint",onWait:r=>{e.step="await-turn",e.wait=r},onResume:()=>{e.step="summarize"}}}),e.step="clear";let n=await Se(e.name);await md(e.name,n,"/clear"),await Mh(Cj),e.step="resume",await md(e.name,n,Tj),e.step="done",e.finishedAt=Date.now()}async function NS(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 $S({...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||Dj)===Bj)throw i;return await re({flow:"checkpoint",phase:"quiescence-timeout",sessionName:t,gate:e,waitedMs:i.waitedMs,detail:i.detail,still:$h(i.detail),paneTail:!0}),r.onResume&&await r.onResume(e),{outcome:"timeout",waitedMs:i.waitedMs,waited:!0,detail:i.detail}}}async function QS(e,t,{timeoutMs:n=Nj,quiesce:r=null}={}){let o=co.join(t,BS),s=co.join(o,LS);await io.mkdir(o,{recursive:!0});let a=await Se(e),i=await NS("pre-drive",e,a,r),c=await fd(s);await re({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:s,beforeMtime:c,quiescence:i.outcome,waitedMs:i.waitedMs});try{await md(e,a,Aj(s)),await eA(s,c,n)}catch(f){throw f&&typeof f=="object"&&(f.quiescence={pre:i,post:{outcome:"skipped"}}),f}let l=await NS("post-write",e,a,r);await re({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:s,afterMtime:await fd(s),quiescence:l.outcome,waitedMs:l.waitedMs});let u=await yv(t,s,{exclude:f=>zS(f.role,f.text)});await re({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:s,...u});let d=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(d){await N({id:d,cwd:t},{type:"memory.seed",actor:"orchestrator",payload:{...u,tokensEst:u.chars?Math.round(u.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let f=await io.readFile(s,"utf8");await N({id:d,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...jS(f),ts:Date.now()}})}catch{}}await gm(t);let p=await Lj(o,s);return d&&await W0(d,p||s,{box:e,agent:a?.agent??null}),{file:s,quiescence:{pre:i,post:l}}}async function wd(e,{timeoutMs:t,quiesce:n=null}={}){let r=await Se(e);if(!r)throw new Error(`session "${e}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${e}"`);return QS(e,r.cwd,{...t?{timeoutMs:t}:{},quiesce:n})}function Oh(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 gd(e,t,{trackName:n="this Track"}={}){let r=await K({id:e,cwd:t}),o=Wv(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=co.join(t,PS);return await io.mkdir(co.dirname(s),{recursive:!0}),await io.writeFile(s,o,"utf8"),await N({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...jS(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),PS}async function fd(e){try{return(await io.stat(e)).mtimeMs}catch{return 0}}async function eA(e,t,n){let r=Date.now()+n;for(;;){if(await fd(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await Mh(2e3)}}async function Lj(e,t){try{if(!vj.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=co.join(e,`${n}.md`);return await io.copyFile(t,r),r}catch{return null}}function Mh(e){return new Promise(t=>setTimeout(t,e))}function DS(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 md(e,t,n){if(t){let o=DS(t.agent);await wr({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await Se(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await Ke(e,["Escape"],{kind:"drive-wipe"}),await Mh(150),await ua(e,Pu,{kind:"drive-wipe"}),await Qe(e,n,{kind:"orchestration-drive",...Un(r.agent,n)}),await pc({sessionName:e,agent:r.agent,agentCmd:DS(r.agent),text:n,flow:"orchestration-drive"})}var BS,LS,OS,FS,US,HS,Sj,Aj,Tj,WS,qS,Ej,_j,MS,GS,KS,Ij,$j,CS,Rj,Mj,Cj,Nj,Rh,Dj,Bj,PS,Ch=G(()=>{pt();rs();bn();cc();Qm();bv();Li();be();Eu();qv();RS();BS=".bostrat/handoff",LS="latest.md",OS=32*1024;FS="Checkpoint: write a work-in-progress handoff",US="HANDOFF WRITTEN",HS="Resume from .bostrat/handoff/latest.md",Sj="/clear",Aj=e=>`${FS} 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 ${US} and take no further action.`,Tj=`${HS}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,WS="Beat: write a",qS="BEAT WRITTEN",Ej=".bostrat/arc",_j="beat.txt",MS=200,GS=4,KS=5,Ij=36,$j=e=>`${WS} 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 ${GS} further lines, each starting with "- " and naming ONE piece in at most ${KS} 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 ${qS} and take no further action.`,CS=/^(?:[-–—*•·]|\d+[.)])\s+/,Rj=(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};Mj=6e5;Cj=3500,Nj=3e5,Rh=new Map;Dj="proceed",Bj="reject";PS=".bostrat/handoff/status-latest.md"});function uo(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!yd.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 rA(e){let t={};for(let[n,r]of Object.entries(e)){if(!Array.isArray(r)||r.length===0)throw new Error(`pricing.js: ${n} must be a non-empty array of rate windows`);t[n]=r.map(o=>({...o,fromMs:uo(o.from,"from",n),toMs:uo(o.to,"to",n)}))}return t}function Ph(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 Fj(e){if(!e)return null;let t=String(e).trim();if(lo[t])return t;if(tA[t])return tA[t];let n=null;for(let r of Object.keys(lo))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function Uj(e){if(!e||e===lo)return jj;let t=nA.get(e);return t||(t=rA(e),nA.set(e,t)),t}function oA(e,t,n={}){let{table:r,speed:o}=n,s=Uj(r),a=r&&r!==lo?s[e]?e:null:Fj(e);if(!a)return null;let i=Ph(t);if(i===null)return null;let c=s[a].find(l=>i>=l.fromMs&&i<=l.toMs)||null;return c?o==null||o==="standard"?c:o==="fast"&&c.fast?{...c.fast,fromMs:c.fromMs,toMs:c.toMs,verifiedAt:c.verifiedAt,note:c.note,speed:"fast"}:null:null}function Wn(e,t={},n,r){let o=oA(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 Hj(e=lo){let t=[];for(let[n,r]of Object.entries(e)){if(!Array.isArray(r)||r.length===0){t.push(`${n}: must be a non-empty array of windows`);continue}let o;try{o=r.map(a=>({fromMs:uo(a.from,"from",n),toMs:uo(a.to,"to",n)}))}catch(a){t.push(String(a.message));continue}let s=r.filter(a=>a.to==null);s.length!==1?t.push(`${n}: expected exactly 1 open-ended window (to: null), found ${s.length}`):r[r.length-1].to!=null&&t.push(`${n}: the open-ended window must be last`),r[0].from!=null&&t.push(`${n}: the first window must be open at the start (from: null)`);for(let a=0;a<o.length;a++){if(o[a].fromMs>o[a].toMs&&t.push(`${n}: window ${a} ends before it begins (${r[a].from} \u2192 ${r[a].to})`),a===0)continue;let i=o[a-1],c=o[a];c.fromMs<=i.toMs?t.push(`${n}: windows ${a-1} and ${a} overlap (${r[a-1].to} / ${r[a].from})`):c.fromMs-i.toMs>2&&t.push(`${n}: gap between windows ${a-1} and ${a} (${r[a-1].to} \u2192 ${r[a].from})`),r[a].from||t.push(`${n}: window ${a} must declare a from bound`)}for(let a of r){a.note||t.push(`${n}: every window needs a note recording what was verified and when`),yd.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 qj({now:e=Date.now(),table:t=lo,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...Wj,...n},a=[],i=Ph(e);if(i===null)return[`pricingFreshness: unparseable now ${JSON.stringify(e)}`];for(let[c,l]of Object.entries(t)){if(!Array.isArray(l))continue;let u;try{u=l.map(d=>({...d,fromMs:uo(d.from,"from",c),toMs:uo(d.to,"to",c),verifiedMs:yd.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*Nh;p&&i-d.verifiedMs>r*Nh&&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*Nh&&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 Ta({now:e=Date.now(),table:t=lo,...n}={}){let r=Ph(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=uo(l.from,"from",i),d=uo(l.to,"to",i);r>=u&&r<=d&&yd.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=[...Hj(t),...qj({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var lo,tA,yd,jj,nA,sA,Wj,Nh,yr=G(()=>{lo={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"GA rates; verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-08-17",note:"$2/$10 is the standard price (intro pricing made permanent; the 2026-09-01 $3/$15 increase was cancelled) \u2014 verified 2026-08-17 against platform.claude.com/docs/en/about-claude/pricing."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-fable-5-1":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:.25,cacheReadX:.025,verifiedAt:"2026-09-07",note:"verified 2026-09-07 against platform.claude.com/docs/en/about-claude/pricing: $10/$50, 1h cache write $20 (2\xD7), cache hits $0.25 (0.025\xD7 \u2014 the 5.1-only multiplier; footnote 1 on the page)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-03, re-verified 2026-08-17 (developers.openai.com pricing); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:"2026-08-23",input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL). Superseded 2026-08-24 by the vendor's promotional cut (window below)."},{from:"2026-08-24",to:null,input:4,output:20,cacheWrite:5,cacheRead:.4,verifiedAt:"2026-08-24",note:"promotional pricing verified 2026-08-24 (developers.openai.com pricing), 'at least through November 21, 2026'; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:"2026-08-16",input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:2,output:12,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:"2026-08-16",input:1,output:6,cacheWrite:1.25,cacheRead:.1,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:.2,output:1.2,cacheWrite:.25,cacheRead:.02,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-6-astra":[{from:null,to:null,input:10,output:50,cacheWrite:12.5,cacheRead:1,fast:{input:20,output:100,cacheWrite:25,cacheRead:2},verifiedAt:"2026-09-07",note:"verified 2026-09-07 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input; fast tier $20/$100 recorded as the fast variant (codex reports no speed)."}]},tA={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"},yd=/^\d{4}-\d{2}-\d{2}$/;jj=rA(lo);nA=new WeakMap;sA=(e,t,n)=>oA(e,t,n)!==null;Wj=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),Nh=864e5});function Gj(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function iA(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function sn(e){let t=e.payload?.model||"unknown",n=Gj(e.payload?.tokens),r=Number(e.payload?.usd)||0,o=e.payload?.speed||void 0,s=e.source==="vendor",a=r,i=!1;if(n&&!s){let u=Wn(t,n,e.ts,o?{speed:o}:void 0);u>0&&(a=u,i=Math.abs(u-r)>1e-9)}let c=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,l=!a&&c>0&&!sA(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function po(e,{boxLabels:t={}}={}){let n=(e||[]).filter(p=>p.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,d=aA();for(let p of n){let{usd:f,model:w,tokens:h,repriced:y,unpriced:k,asRecorded:g}=sn(p);i+=g;let x=p.provider||"unknown",S=p.boxId||"unattributed";if(y&&c++,k&&(l++,u.add(w)),a+=f,iA(d,h),r[w]||={usd:0,tokens:aA(),events:0},r[w].usd+=f,r[w].events+=1,iA(r[w].tokens,h),o[x]||={usd:0,events:0},o[x].usd+=f,o[x].events+=1,s[S]||={usd:0,events:0,provider:x,models:new Set,label:t[S]||S},s[S].usd+=f,s[S].events+=1,w!=="unknown"&&s[S].models.add(w),p.epoch!=null){let _=(s[S].epochs||={})[p.epoch]||={usd:0,events:0,provider:x,models:new Set};_.usd+=f,_.events+=1,_.provider=x,w!=="unknown"&&_.models.add(w)}}return{totalUsd:Ea(a),asRecordedUsd:Ea(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:bd(r,p=>({...p,usd:Ea(p.usd)})),byProvider:bd(o,p=>({...p,usd:Ea(p.usd)})),byBox:bd(s,p=>({usd:Ea(p.usd),events:p.events,provider:p.provider,label:p.label,models:[...p.models],...p.epochs?{epochs:bd(p.epochs,f=>({usd:Ea(f.usd),events:f.events,provider:f.provider,models:[...f.models]}))}:{}}))}}var aA,Ea,bd,fo=G(()=>{yr();aA=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});Ea=e=>Math.round(e*1e4)/1e4,bd=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function Kj(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 cA(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(Dh(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Dh(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(`${Dh(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=po(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?Kj(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Dh,lA=G(()=>{fo();Dh=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as Vj}from"node:child_process";import{promisify as zj}from"node:util";import{randomUUID as Jj}from"node:crypto";import uA from"node:fs/promises";async function Bh(e,t){let n=Date.now()+t;for(;;){if(await Fn(e))return!0;if(Date.now()>n)return!1;await ls(hA)}}function tF(e){let t=String(e||"").split(`
|
|
178
|
+
`).map(n=>n.trimEnd()).filter(n=>n.trim());return!t.length||!Zj.test(t[t.length-1])?!1:Xj.test(t.slice(-Qj).join(`
|
|
179
|
+
`))}async function nF(e,t){if(t!=="claude")return null;let n=Date.now()+eF;for(;;){if(await Fn(e))return null;let r=await Ne(e,{lines:30});if(tF(r))return await re({flow:"swap",phase:"exit-chooser",sessionName:e,from:t,paneTail:!0}),await Ke(e,["Enter"],{kind:"swap-exit"}),"answered";if(Date.now()>n)return null;await ls(hA)}}async function rF(e,t){let n=dA[t]||dA.default;n.escape&&(await Ke(e,["Escape"],{kind:"swap-exit"}),await ls(150)),n.wipe&&await ua(e,Pu,{kind:"swap-exit"}),await Du(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Un(t).submitDelayMs||0;return await ls(Math.max(r,250)),await Ke(e,["Enter"],{kind:"swap-exit"}),{chooser:await nF(e,t)}}async function iF(e,{settleMs:t=oF,timeoutMs:n=sF,parse:r=!0}={}){let o=Date.now(),s=null,a=null;for(;;){let i=null,c=!0;try{let u=await uA.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 uA.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 ls(aF)}}async function pA(e,t){try{let{stdout:n}=await cs(oe,se("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function wA(e){await cs(oe,se("respawn-pane","-k","-t",e))}async function Lh(e,{exitTimeoutMs:t=fA,fromAgent:n=null}={}){if(!je(e))throw new Error("invalid session name");let r=!1;if(await Fn(e))return{forced:r,chooser:null};let o=n||(await Se(e))?.agent||"agent",{chooser:s}=await rF(e,o),a=await Bh(e,t);if(await re({flow:"swap",phase:"exit",sessionName:e,from:o,graceful:a,chooser:s,paneTail:!0}),!a&&(r=!0,await re({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await wA(e),!await Bh(e,mA)))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);return{forced:r,chooser:s}}async function jh(e,t={}){if(!je(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await Se(e);if(!r)throw new Error(`session "${e}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${e}"`);let o=r.agent||"agent",s=t.agent||Xr(n),a=Number(t.exitTimeoutMs)||fA;await re({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let{forced:i,chooser:c}=await Lh(e,{exitTimeoutMs:a,fromAgent:o});if(o==="claude"){let k=await iF(tm());await re({flow:"swap",phase:"config-settled",sessionName:e,from:o,...k})}let l=s==="claude"&&Jo(n),u=l?ru():null,d=await pA(e,"ANTHROPIC_API_KEY"),p=!1;try{u?(await cs(oe,se("set-environment","-t",e,"ANTHROPIC_API_KEY",u)),p=!0):d&&(await cs(oe,se("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),p=!0)}catch(k){throw u?$u(k,u):k}if(p&&!i&&(await wA(e),!await Bh(e,mA)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let f=!!t.resumeSessionId,w=t.resumeSessionId||Jj(),h=f?n:zi(n,w,e),y=f?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(h);return l&&await Wr(r.cwd),s==="codex"&&await Ys(r.cwd),s==="cursor"&&await Xs(r.cwd),await Yo(e),await cs(oe,se("set-option","-t",e,"@bostrat-sid",w)),await cs(oe,se("set-option","-t",e,"@bostrat-agent",s)),await ls(Yj),l&&Vi().length&&(await pA(e,Ki)||await cs(oe,se("set-environment","-t",e,Ki,"1")).catch(()=>{}),await Qe(e,`export ${Ki}=1`,{kind:"swap-env"}),await ls(150)),await Qe(e,h,{kind:"swap-launch"}),await re({flow:"swap",phase:"launched",sessionName:e,from:o,to:s,paneTail:!0}),await re({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:w,forced:i,chooser:c,envRespawned:p}),{name:e,cwd:r.cwd,sessionId:w,agent:s,pinned:y,forced:i,chooser:c,envRespawned:p,launchCmd:h}}var cs,ls,fA,mA,hA,Yj,dA,Xj,Zj,Qj,eF,oF,sF,aF,gA=G(()=>{pt();_i();su();au();cc();bn();cs=zj(Vj),ls=e=>new Promise(t=>setTimeout(t,e)),fA=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,mA=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,hA=250,Yj=400,dA={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"}};Xj=/Background work is running/,Zj=/Enter to confirm\s*·\s*Esc to cancel/,Qj=12,eF=Number(process.env.BOSTRAT_SWAP_CHOOSER_WAIT_MS)||3e3;oF=Number(process.env.BOSTRAT_CLAUDE_CONFIG_SETTLE_MS)||500,sF=Number(process.env.BOSTRAT_CLAUDE_CONFIG_SETTLE_TIMEOUT_MS)||5e3,aF=100});function Fh(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Uh({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!Fh(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),yA);try{let a=await bA(`${De(r)}/v1/loops/${encodeURIComponent(e)}/report`,{method:"POST",headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN||""}`,"content-type":"application/json"},body:JSON.stringify({outcome:t,...n?{note:n}:{}}),signal:o.signal}),i=await a.json().catch(()=>({}));return a.ok?{ok:!0,source:"plane",status:a.status,run:i?.run||null}:{ok:!1,source:"plane",status:a.status,error:i?.error||`plane ${a.status}`}}catch(a){return{ok:!1,source:"unreachable",error:a?.message||String(a)}}finally{clearTimeout(s)}}async function kc(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(!Fh(a))return{ok:!1,source:"unconfigured"};let c=kd.get(e.id)||0;if(i()-c<cF)return{ok:!1,source:"debounced"};kd.set(e.id,i());let l=await xc({env:a}),u=l.loops.find(f=>f.trackId===e.id);if(!u)return kd.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 Uh({loopId:u.id,outcome:"errored",note:d.slice(0,500),env:a});return p.ok||kd.delete(e.id),{...p,loop:{id:u.id,name:u.name}}}async function xc({env:e=process.env}={}){if(!Fh(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),yA);try{let r=await bA(`${De(e)}/v1/loops`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`},signal:t.signal});if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json();return{loops:Array.isArray(o?.loops)?o.loops:[],source:"plane"}}catch(r){return{loops:[],source:"unreachable",error:r?.message||String(r)}}finally{clearTimeout(n)}}var yA,bA,cF,kd,xd=G(()=>{xt();yA=1e4,bA=(...e)=>fetch(...e);cF=10*6e4,kd=new Map});import{execFile as lF}from"node:child_process";import vd from"node:fs/promises";import Hh from"node:path";import{promisify as uF}from"node:util";function gF(e){let n=String(e||"").trim().match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.+-]+)?/);return n?n[0]:null}async function yF(e,{env:t=process.env}={}){let n=String(e||"").trim();if(!n)return null;let r=n.includes("/")?[Hh.resolve(n)]:String(t.PATH||"").split(Hh.delimiter).filter(Boolean).map(o=>Hh.join(o,n));for(let o of r)try{await vd.access(o,vd.constants.X_OK);let s=await vd.realpath(o),a=await vd.stat(s);if(!a.isFile())continue;return{path:s,mtimeMs:a.mtimeMs}}catch{}return null}async function us({agent:e=null,agentCmd:t=null,env:n=process.env}={}){let r=String(t||(e?n[mF[e]]||fF[e]:"")||"").trim();if(!r)return null;let{realClaude:o,realCodex:s,realCursor:a}=ic({agent:e,agentCmd:r});if(!(o||s||a))return null;let i=await yF(r.split(/\s+/)[0],{env:n});if(!i)return null;let c=`${i.path}@${i.mtimeMs}`;return Wh.has(c)||Wh.set(c,Promise.resolve().then(()=>wF(i.path,{agent:e})).then(l=>gF(l)).catch(()=>null)),Wh.get(c)}var dF,pF,fF,mF,Wh,hF,wF,kA=G(()=>{rs();dF=uF(lF),pF=8e3,fF={claude:"claude",codex:"codex",cursor:"cursor-agent"},mF={claude:"BOSTRAT_AGENT_CMD",codex:"BOSTRAT_CODEX_CMD",cursor:"BOSTRAT_CURSOR_CMD"},Wh=new Map,hF=async e=>(await dF(e,["--version"],{timeout:pF})).stdout,wF=hF});function _a(e){return typeof e=="string"?e.trim():""}var xA=G(()=>{});import{execFile as bF}from"node:child_process";import{promisify as kF}from"node:util";async function Ye(e,t,{input:n}={}){let r=Gh("git",["-C",e,...t],{maxBuffer:vA});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Ia(e){if(!e)return!1;try{let{stdout:t}=await Ye(e,["rev-parse","--is-inside-work-tree"]);return t.trim()==="true"}catch{return!1}}async function SA(e){let{stdout:t}=await Ye(e,["rev-parse","--show-toplevel"]);return t.trim()}async function xF(e){try{return await Ye(e,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function $a(e,t="bostrat: subtask snapshot"){let{stdout:n}=await Ye(e,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>Gh("git",["-C",e,...a],{maxBuffer:vA,env:o,...i});try{let a=await xF(e);a&&await s(["read-tree","HEAD"]),await s(["add","-A"]);let{stdout:i}=await s(["write-tree"]),c=i.trim(),l=a?["commit-tree",c,"-p","HEAD","-m",t]:["commit-tree",c,"-m",t],{stdout:u}=await s(l);return u.trim()}finally{try{await Gh("rm",["-f",r])}catch{}}}async function Kh(e,t,{trailers:n=[]}={}){let{stdout:r}=await Ye(e,["status","--porcelain"]);return r.trim()?(await Ye(e,["add","-A"]),await Ye(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",t,...n.flatMap(o=>["--trailer",o])]),!0):!1}async function ds(e,{ignorePlumbing:t=!1}={}){let{stdout:n}=await Ye(e,["status","--porcelain"]),r=n.split(`
|
|
180
|
+
`).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 ps(e,t){if(!t)return 0;try{let{stdout:n}=await Ye(e,["rev-list","--count",`${t}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function fs(e,t,n,r){return await Ye(e,["worktree","add","--no-track","-b",n,t,r]),t}async function Vh(e,t){try{return await Ye(e,["rev-parse","--verify","--quiet",`refs/heads/${t}`]),!0}catch{return!1}}async function vc(e,t,n){return await Ye(e,["worktree","add","--end-of-options",t,n]),t}async function ms(e){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:t}=await Ye(e,["symbolic-ref","--short","refs/remotes/origin/HEAD"]);if(t.trim())return t.trim()}catch{}for(let[t,n]of[["refs/remotes/origin/main","origin/main"],["refs/remotes/origin/master","origin/master"],["refs/heads/main","main"],["refs/heads/master","master"]])try{return await Ye(e,["rev-parse","--verify","--quiet",t]),n}catch{}return"HEAD"}async function zh(e){await Ye(e,["init","-q"]),await Ye(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function $t(e,t){try{await Ye(e,["worktree","remove","--force",t])}catch{try{await Ye(e,["worktree","prune"])}catch{}}}function vF(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 Jh(e,t){return!t||!t.length?e:[...e,"--",".",...t.map(n=>`:(exclude)${n}`)]}async function Yh(e,t,n,r){let{stdout:o}=await Ye(e,Jh(["diff","--shortstat",t,n],r));return vF(o)}async function Xh(e,t,n,r){let{stdout:o}=await Ye(e,Jh(["diff","--name-only",t,n],r));return o.split(`
|
|
181
|
+
`).map(s=>s.trim()).filter(Boolean)}async function Zh(e,t,n,r){let{stdout:o}=await Ye(e,Jh(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await qh(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await qh(e,o,["--3way","--whitespace=nowarn"]),a=await SF(e);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await qh(e,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function qh(e,t,n){try{return await Ye(e,["apply",...n,"-"],{input:t}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function SF(e){try{let{stdout:t}=await Ye(e,["diff","--name-only","--diff-filter=U"]);return t.split(`
|
|
182
|
+
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var Gh,vA,Sd=G(()=>{Gh=kF(bF),vA=64*1024*1024});function Ad(e,{at:t=Date.now(),reason:n="node-transfer"}={}){let r=!1;for(let o of e?.boxes||[]){if(!o||typeof o!="object")continue;!AF.has(o.state)&&o.state!=="stopped"&&(o.state="stopped",r=!0);let s=Array.isArray(o.epochs)?o.epochs:[],a=s[s.length-1];a&&typeof a=="object"&&a.endedAt==null&&(a.endedAt=t,a.endReason==null&&(a.endReason=n),r=!0)}return r}var Ue,AF,an=G(()=>{Ue=e=>!!(e&&(e.archivedAt||e.transferredTo)),AF=new Set(["merged","conflict","canceled","failed","handed-off"])});import Qh from"node:fs/promises";import TA from"node:path";import TF from"node:os";function $A(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function RA(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function OA(e){let t=new Date($A(e));return t.setDate(t.getDate()+7),t.getTime()}function MA(e){let t=new Date(RA(e));return t.setMonth(t.getMonth()+1),t.getTime()}function Ed(e=Date.now()){for(let[t,n]of Object.entries(Ra)){let r=n.startOf(e);if(r!==br[t]){br[t]=r;for(let o of hs.values())o[t]=0;for(let o of Oa)o.includes(`:${t}:`)&&!o.endsWith(`:${r}`)&&Oa.delete(o)}}}function CA(e,t){let n=Tn?.caps?.[e]?.[Ra[t].capKey];return Number.isFinite(n)&&n>0?n:null}function mo(e,t=Date.now()){if(!e||!tw||!Tn?.caps)return null;Ed(t);let n=hs.get(e);for(let r of Object.keys(Ra)){let o=CA(e,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:e,window:r,limitUsd:o,spentUsd:Td(s),resetsAt:Ra[r].nextStart(t)}}return null}function NA(e,t){let n=`hit:${e}:${t.provider}:${t.window}:${br[t.window]}`;return Oa.has(n)?!1:(Oa.add(n),!0)}function AA(e,t,n,r=Date.now()){if(!e||!t)return;Ed(r);let o=EF(e);for(let[s,a]of Object.entries(Ra))n>=br[s]&&n<a.nextStart(r)&&(o[s]+=t)}async function _F(e,t,n=Date.now()){if(!Tn?.caps||!ew)return;let r=hs.get(e);if(r)for(let[o,s]of Object.entries(Ra)){let a=CA(e,o);if(a==null)continue;let i=r[o]||0;if(i<a*IA||i>=a)continue;let c=`warn:${e}:${o}:${br[o]}`;if(!Oa.has(c)){Oa.add(c);try{let l=await ew(t);if(!l)continue;await N(l,{type:"usage.cap.warning",actor:"system",provider:e,payload:{provider:e,window:o,limitUsd:a,spentUsd:Td(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function PA(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 _d(e,{source:t="local",updatedAt:n=Date.now()}={}){Tn={caps:PA(e),updatedAt:n,source:t};try{await Qh.mkdir(EA,{recursive:!0}),await Qh.writeFile(_A(),JSON.stringify(Tn,null,2))}catch{}return Sc()}function Sc(e=Date.now()){Ed(e);let t=new Set([...hs.keys(),...Object.keys(Tn?.caps||{})]),n={};for(let r of t){let o=hs.get(r);n[r]={weeklyUsd:Td(o?.weekly||0),monthlyUsd:Td(o?.monthly||0)}}return{caps:Tn?.caps||{},configured:!!Tn,seeded:tw,updatedAt:Tn?.updatedAt??null,spent:n,windows:{weekly:{start:br.weekly,resetsAt:OA(e)},monthly:{start:br.monthly,resetsAt:MA(e)}},warnPct:IA}}async function DA({listTracks:e,getTrack:t}={}){ew=t||null,Ed();try{let o=JSON.parse(await Qh.readFile(_A(),"utf8"));Tn={caps:PA(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{Tn=null}let n=Date.now();Ze.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=sn(o);AA(o.provider||"unknown",s,o.ts??Date.now()),_F(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(br.weekly,br.monthly);for(let o of await e?.()||[]){let s;try{s=await K(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||AA(a.provider||"unknown",sn(a).usd,i,n)}}return tw=!0,Sc()}var EA,_A,IA,Ra,Tn,tw,br,hs,Oa,ew,EF,Td,Id=G(()=>{be();fo();EA=process.env.BOSTRAT_STATE_DIR||TA.join(process.env.HOME||TF.tmpdir(),".bostrat"),_A=()=>TA.join(EA,"usage-caps.json"),IA=.8;Ra={weekly:{startOf:$A,nextStart:OA,capKey:"weeklyUsd"},monthly:{startOf:RA,nextStart:MA,capKey:"monthlyUsd"}},Tn=null,tw=!1,br={weekly:0,monthly:0},hs=new Map,Oa=new Set,ew=null,EF=e=>{let t=hs.get(e);return t||(t={weekly:0,monthly:0},hs.set(e,t)),t};Td=e=>Math.round(e*1e4)/1e4});import IF from"node:fs/promises";import $F from"node:os";import BA from"node:path";function nw(e){return e==="codex"?CF:e==="cursor"?WA:UA}function LA(e){let t=String(e||"").split(`
|
|
183
|
+
`),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 jA(e){let t=String(e||"").split(`
|
|
184
|
+
`),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 NF(e){let t=String(e||"").split(`
|
|
185
|
+
`);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 FA(e){let t=String(e||"").split(`
|
|
186
|
+
`),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 qA(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&&!HA.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=WA.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!MF.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function Ac(e,t,{timeoutMs:n=RF}={}){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 $d(OF)}}async function PF(e,t){return await Qe(e,`/model ${t.model}`,Un("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function DF(e,t){await Nu(e),await Qe(e,"/model",{enter:!1}),await $d(250),await Ke(e,["Enter"],{kind:"model-switch"});let n=await Ac(e,LA);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await Ke(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${t.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=NF(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await Qe(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await Ac(e,i=>jA(i)||!LA(i)&&o(i)&&{done:o(i)});if(!s)throw await Ke(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?HA.get(t.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await Ke(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await Qe(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Ac(e,i=>!jA(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 BF(){let e=BA.join(process.env.CURSOR_CONFIG_DIR||BA.join($F.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await IF.readFile(e,"utf8"))?.model||null}catch{return null}}async function LF(e,t){await Nu(e),await Du(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await Ac(e,FA),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 Ac(e,FA,{timeoutMs:1500});if(s=l?.rows.find(u=>u.active)||null,s&&o(s))break;if(!l||!l.rows.some(o)){s=null;break}await Ke(e,["Down"],{kind:"model-switch"}),await $d(250)}if(!n||!s||!o(s)){await Ke(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await Ke(e,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${t.filter}"`:"cursor model picker didn't open")}await Ke(e,["Enter"],{kind:"model-switch"}),await $d(800);let a=await BF();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 GA(e,t,n){return e==="codex"?DF(t,n):e==="cursor"?LF(t,n):PF(t,n)}var $d,RF,OF,UA,MF,CF,HA,WA,KA=G(()=>{pt();cc();$d=e=>new Promise(t=>setTimeout(t,e)),RF=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,OF=300,UA=[{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"}],MF=new Set([...UA.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"]),CF=[{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"}],HA=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),WA=[{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 Md(e){let t=e||{},n=jF(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function jF(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{text:e.text,boxRole:e.boxRole,...e.kind?{kind:e.kind}:{},...e.attachments?.length?{attachments:e.attachments}:{},...e.deliveryId?{deliveryId:e.deliveryId}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{saidTs:e.saidTs,text:e.text,boxRole:e.boxRole}};case"orchestration-prompt":return{type:"turn.user",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{text:e.prompt,kind:e.kind,boxRole:e.boxRole,...e.deliveryId?{deliveryId:e.deliveryId}:{}}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:lt(e.toAgent),payload:{from:e.from,fromAgent:e.fromAgent,to:e.to,toAgent:e.toAgent,reason:e.reason,handoffStartedAt:e.handoffStartedAt,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,singleBox:e.singleBox,forced:e.forced,recovered:e.recovered,checkpoint:e.checkpoint,cliVersion:e.cliVersion??null,...e.standing?{standing:e.standing}:{}}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.toAgent),payload:{box:e.box,fromAgent:e.fromAgent,toAgent:e.toAgent,reason:e.reason,error:e.error,rolledBack:e.rolledBack,handoffStartedAt:e.handoffStartedAt}};case"subtask-delegated":return{type:"subtask.delegated",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:lt(e.toAgent),payload:{task:e.task,subtaskId:e.subtaskId,reason:e.reason}};case"subtask-merged":return{type:"subtask.merged",actor:"orchestrator",source:"orchestrator",boxId:e.box||e.from,provider:lt(e.agent),payload:{...qn(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:lt(e.agent||e.toAgent),payload:{kind:"subtask-failed",...qn(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{change:"subtask-resumed",...qn(e)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"model",model:e.to,from:e.from??null,verified:e.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:"codex",payload:{change:"cli-update",from:e.from??null,to:e.to??null,status:e.status,...e.error?{error:e.error}:{}}};case"node-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",payload:{change:"node-update",...qn(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...qn(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...qn(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{change:"rehydrated",...e.fresh?{fresh:!0}:{}}};case"lead-refreshed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{change:"lead-refreshed",box:e.box,agent:e.agent,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,reason:e.reason,cliVersion:e.cliVersion??null,...e.standing?{standing:e.standing}:{}}};case"transferred-in":return{type:e.mode==="recover"?"track.recovered":"track.transferred",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{...qn(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...qn(e)}};case"retag-failed":return{type:"error",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{kind:"retag-failed",action:e.action??"reshape",reason:e.reason,rolledBack:e.rolledBack!==!1,...e.recovered?{recovered:!0}:{}}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};case"delivery-held":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{kind:"delivery-held",...qn(e)}};case"startup-gate":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{kind:"startup-gate",...qn(e)}};default:return null}}function JA(e,t){let n=Md(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;Cd(()=>N(r,n))}async function YA(e){if(!(!e?.id||VA.has(e.id))){VA.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=Md(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await N(e,o)}}catch{}}}function qn(e){let{type:t,at:n,...r}=e;return r}function XA(e,t,n){!n?.promptId||Od.get(e.id)===n.promptId||(Od.set(e.id,n.promptId),Cd(()=>N(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:lt(n.provider||t?.agent),payload:{promptId:n.promptId,question:n.question||n.title||null,options:(n.options||[]).map(r=>r?.label??r).slice(0,12)}})))}function ZA(e,t,n,r={}){Od.delete(e.id),Cd(()=>N(e,{type:"prompt.dismissed",actor:"user",source:"orchestrator",boxId:t?.name,provider:lt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}function rw(e,t,n,r={}){Od.delete(e.id),Cd(()=>N(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:lt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var lt,Rd,Cd,zA,VA,Od,ow=G(()=>{be();lt=e=>e||null;Rd=Promise.resolve(),Cd=e=>(Rd=Rd.then(e).catch(()=>{}),Rd),zA=()=>Rd;VA=new Set;Od=new Map});function qF(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function oT(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of qF(n).reverse()){if(!FF.test(r))continue;let o=r.endsWith("?")&&r.length<=WF&&HF.test(r);if(UF.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function aT(e){let t=String(e||""),n=[];GF.test(t)&&n.push("commit"),sT.test(t)&&n.push("rebase"),KF.test(t)&&n.push("push");let r=zF.test(t);!r&&VF.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 iT(e){return sT.test(String(e||""))}function Ma(e,{branch:t=null,base:n=null,toPr:r=null,repos:o=null,offeredRebase:s=!1}={}){Array.isArray(e)||(e=e==="push"?["commit","push"]:["commit","push","pr"]);let a=new Set(e),i=Array.isArray(o)&&o.length>0,c=i?o.map(f=>`${f.name}/`).join(", "):null,l=t?` on this branch (${t})`:"",u=[];if(a.has("commit")&&u.push(i?`commit the work in each changed checkout (${c})${l} with a clear commit message per repo`:`commit the work${l} with a clear commit message`),a.has("rebase")){let f=a.has("commit")?"the branch":`this branch${t?` (${t})`:""}`;u.push(i?`rebase ${f} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${f} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`)}if(a.has("push")||a.has("pr")){let f=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",w=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${w}${f}`:`push this branch${t?` (${t})`:""} from each changed checkout (${c}) to its remote${f}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${w}${f}`:`push this branch${l} to the remote${f}`)}a.has("pr")&&u.push(i?"open a pull request in each changed repo with a concise title and a description of what changed and why (run `gh pr create` inside each checkout)":`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let d=[];a.has("commit")||d.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&d.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&d.push("don't push"),a.has("pr")||d.push("don't open a pull request");let p=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return d.length&&(p+=` ${d.join(", and ").replace(/^./,f=>f.toUpperCase())}.`),a.has("pr")&&(p+=i?" If a repo's PR for this branch is already open, just push that repo's update instead.":" If a PR for this branch is already open, just push the update instead."),p}function cT(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 lT(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<=JF&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function uT(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+YF||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function dT(e){let t=(Array.isArray(e)?e:[]).filter(r=>QA[r]).map(r=>QA[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 eT,tT,nT,rT,FF,UF,HF,WF,ho,GF,sT,KF,VF,zF,JF,YF,QA,ws=G(()=>{eT=/(?<!\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,tT=/(?<!\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,nT=/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,rT=/(?<!\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,FF=new RegExp(`\\b(?:${eT}|${tT}|${nT}|${rT})\\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,HF=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,WF=90;ho=["commit","rebase","push","pr"],GF=new RegExp(`\\b(?:${eT})\\b`,"i"),sT=new RegExp(`\\b(?:${rT})\\b`,"i"),KF=new RegExp(`\\b(?:${tT})\\b`,"i"),VF=new RegExp(`\\b(?:${nT})\\b`,"i"),zF=/\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;JF=3e4;YF=2500;QA={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import XF from"node:crypto";import Nd from"node:fs/promises";import pT from"node:path";import ZF from"node:os";function wT(e){hT=typeof e=="function"?e:async()=>new Set}function QF(e,t,n){if(e.originNode&&e.originNode===t)return!0;let r=e.data?.trackId;return!!r&&n.has(r)}async function We(){return kr||(sw??=Nd.readFile(iw,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(kr=e,!t&&Ge()){let{records:n,source:r}=await dt("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){let o=F0(),s=await hT().catch(()=>new Set),a=0;for(let i of n){if(i.deletedAt||!i.data?.repo||i.data?.number==null)continue;if(!QF(i,o,s)){a++;continue}let{id:c,updatedAt:l,...u}=i.data;kr[ce(u.repo,u.number)]=u}a&&console.log(`[pr-links] restore: ${Object.keys(kr).length} link(s) re-seeded, ${a} other-node record(s) left on the plane`),Object.keys(kr).length&&await cw().catch(()=>{})}}return kr}).finally(()=>sw=null),sw)}async function cw(){let e=JSON.stringify({links:kr},null,2),t=`${iw}.${process.pid}.tmp`;await Nd.mkdir(fT,{recursive:!0}),await Nd.writeFile(t,e),await Nd.rename(t,iw)}async function et(e){aw=aw.then(cw,cw),await aw.catch(()=>{});let t=kr?.[e];t&&It("prlinks",{id:mT(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function Yt({repo:e,number:t,trackId:n,url:r=null,headRef:o=null,title:s=null,source:a="matched"}){if(!e||!Number.isInteger(Number(t))||!n)return{link:null,created:!1};let i=await We(),c=ce(e,t);return i[c]?{link:i[c],created:!1}:(i[c]={repo:e,number:Number(t),trackId:n,url:r,headRef:o,title:s,source:a,announced:eU.has(a),firstSeen:Date.now()},await et(ce(e,t)),{link:i[c],created:!0})}async function xr(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return!n||n.announced?null:(n.announced=!0,await et(ce(e,t)),n)}async function gT(e,t){if(!e||t==null)return!1;let n=(await We())[ce(e,t)];return n?.notifyOnMerge?(delete n.notifyOnMerge,await et(ce(e,t)),!0):!1}async function ft(e,t){return!e||t==null?null:(await We())[ce(e,t)]??null}async function wo(e,t,n){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,delete r.notifyOnMerge,delete r.notifyPending,delete r.followupPending,await et(ce(e,t)),r):null}async function yT(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await We())[ce(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await et(ce(e,t)),o):null}async function bT(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await We())[ce(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await et(ce(e,t)),o):null}async function Pd(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await et(ce(e,t)),r)}async function Dd(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await et(ce(e,t)),n)}async function Bd(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await et(ce(e,t)),r)}async function Ld(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await et(ce(e,t)),n)}async function kT(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return r?(r.baseFoldAt=n,await et(ce(e,t)),r):null}async function xT(e,t,{misses:n,until:r}){if(!e||t==null)return null;let o=(await We())[ce(e,t)];return o?(o.brokerRefused={misses:Number(n)||0,until:Number(r)||0},await et(ce(e,t)),o):null}async function vT(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return n?.brokerRefused?(delete n.brokerRefused,await et(ce(e,t)),n):null}async function ST(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await et(ce(e,t)),n)}async function AT(e,t,n){if(!e||t==null||!n)return null;let r=(await We())[ce(e,t)];return r?(r.lastHeadSha=n,await et(ce(e,t)),r):null}async function TT(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await We())[ce(e,t)];if(!r)return{prev:null,link:null};let o=r.verdict??null;return o&&o.blocked===(n.blocked??null)&&(o.sha??null)===(n.sha??null)||(r.verdict={blocked:n.blocked??null,sha:n.sha??null,at:n.at??Date.now()},await et(ce(e,t))),{prev:o,link:r}}async function Tc(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await et(ce(e,t)),r):null}async function gs(e,t,n=!0){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await et(ce(e,t)),r):null}async function ys(e,t,n=!0){if(!e||t==null)return null;let r=(await We())[ce(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await et(ce(e,t)),r):null}async function ET(e,t,n){if(!e||t==null)return null;let r=(await We())[ce(e,t)];if(!r)return null;r.notifyOnMerge=!0;let o=new Map((r.notifyPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.notifyPending=[...o.values()],await et(ce(e,t)),r}async function _T(e,t,n){if(!e||t==null)return null;let r=(await We())[ce(e,t)];if(!r)return null;let o=new Map((r.followupPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.followupPending=[...o.values()],await et(ce(e,t)),r}async function Ec(e,t){if(!e||t==null)return null;let n=(await We())[ce(e,t)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,delete n.verdict,await et(ce(e,t)),n)}async function IT(){return Object.values(await We()).filter(e=>!tU.has(e.state))}async function En(){return Object.values(await We()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function $T(){if(!Ge())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await dt("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await We(),r=0;for(let[o,s]of Object.entries(n))await It("prlinks",{id:mT(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var fT,iw,ce,mT,kr,sw,aw,hT,eU,tU,Gn=G(()=>{Yr();fT=process.env.BOSTRAT_STATE_DIR||pT.join(process.env.HOME||ZF.tmpdir(),".bostrat"),iw=pT.join(fT,"pr-links.json"),ce=(e,t)=>`${e}#${Number(t)}`,mT=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=XF.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},kr=null,sw=null,aw=Promise.resolve(),hT=async()=>new Set;eU=new Set(["api","observed","reconciled"]);tU=new Set(["merged","closed","lost"])});import nU from"node:fs";import lw from"node:fs/promises";import CT from"node:path";import rU from"node:os";function DT(e,t=!1){if(!t&&e-OT<oU)return;OT=e;let n=JSON.stringify({windowStart:Ca,byCaller:Object.fromEntries(vr),lastHour:go});MT=MT.then(()=>lw.mkdir(NT,{recursive:!0})).then(()=>lw.writeFile(`${jd}.tmp`,n)).then(()=>lw.rename(`${jd}.tmp`,jd)).catch(()=>{})}function BT(e){e-Ca<PT||(go={start:Ca,total:Fd(vr),byCaller:Object.fromEntries(vr)},go.total&&console.log(`[github] ${go.total} calls last hour`,go.byCaller),Ca=e,vr=new Map,DT(e,!0))}function Sr(e="other",t=Date.now()){BT(t),vr.set(e,(vr.get(e)||0)+1),DT(t)}function LT(e=Date.now()){return BT(e),{windowStart:Ca,total:Fd(vr),byCaller:Object.fromEntries(vr),lastHour:go,backoffUntil:bs>e?bs:null}}function yo(e){return sU.test(String(e?.message||e||""))}function bo(e,t=Date.now()){return bs<=t&&console.warn(`[github] rate limited \u2014 backing off ${RT/6e4} min:`,String(e?.message||e||"").slice(0,200)),bs=Math.max(bs,t+RT),bs}function Ud(e=Date.now()){return Math.max(0,bs-e)}var NT,jd,PT,RT,oU,Ca,vr,go,bs,OT,Fd,MT,sU,_c=G(()=>{NT=process.env.BOSTRAT_STATE_DIR||CT.join(process.env.HOME||rU.tmpdir(),".bostrat"),jd=CT.join(NT,"github-metrics.json"),PT=60*6e4,RT=5*6e4,oU=6e4,Ca=Date.now(),vr=new Map,go=null,bs=0,OT=0,Fd=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(nU.readFileSync(jd,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<PT?(Ca=e.windowStart,vr=new Map(Object.entries(e.byCaller||{})),go=e.lastHour||null):Fd(new Map(Object.entries(e.byCaller||{})))&&(go={start:e.windowStart,total:Fd(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}MT=Promise.resolve();sU=/rate limit|too quickly|abuse detection|HTTP 429/i});import jT from"node:fs/promises";import FT from"node:path";import aU from"node:os";function Wd({change:e,repo:t=null,number:n=null,githubAt:r=null,observedAt:o=Date.now(),lane:s=null,intervalMs:a=null,source:i=null}){let c=r?o-r:null,l={change:e,repo:t,number:n,githubAt:r,observedAt:o,latencyMs:c,lane:s,intervalMs:a,source:i};Hd.push(l),Hd.length>cU&&Hd.shift();let u=uw.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)),uw.set(e,u),lU(l),l}async function lU(e){try{await jT.mkdir(UT,{recursive:!0}),await jT.appendFile(iU,JSON.stringify(e)+`
|
|
187
|
+
`)}catch{}}function HT(){let e={};for(let[t,n]of uw)e[t]={count:n.count,measured:n.measured,avgMs:n.measured?Math.round(n.totalMs/n.measured):null,maxMs:n.measured?n.maxMs:null};return{byChange:e,recent:Hd.slice(-20)}}var UT,iU,cU,Hd,uw,qd=G(()=>{UT=process.env.BOSTRAT_STATE_DIR||FT.join(process.env.HOME||aU.tmpdir(),".bostrat"),iU=FT.join(UT,"github-latency.ndjson"),cU=100,Hd=[],uw=new Map});import{execFile as uU}from"node:child_process";import{promisify as dU}from"node:util";async function pU(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Pa("git",["-C",e,"rev-parse","HEAD"]),Pa("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await N({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function cn(e,t,n={}){let{caller:r="prs",...o}=n,s=await Bn({cwd:e});Sr(r);try{let{stdout:a}=await Pa("gh",t,{cwd:e,...fU,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw yo(a)&&bo(a),a}}async function fw(){if(Gd&&Date.now()-Gd.at<6e4)return Gd.data;let e;try{Sr("auth");let t=await Pa("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 Ci();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return Gd={at:Date.now(),data:e},e}async function mU(e){try{let{stdout:t}=await Pa("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function qT(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 dw(e){let t=(e||[]).map(qT),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 hw(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function KT(e,t){if(!t)return null;let n=await Me(e);if(!n)return null;try{let r=await cn(e,["api",`repos/${n}/actions/runs?head_sha=${encodeURIComponent(t)}&per_page=100`,"--jq",".workflow_runs"]),o=new Map;for(let s of JSON.parse(r||"[]"))o.has(s.name)||o.set(s.name,s);return[...o.values()]}catch{return null}}function VT(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 zT(e,t){let n=await KT(e,t);return n===null?{...dw([]),unavailable:!0}:dw(VT(n))}function Kd(e,t=dw(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 wU(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Na.delete(s);continue}r.add(s),Na.has(s)||Na.set(s,n),o.mergeabilityPending=n-Na.get(s)<hU}for(let o of Na.keys())o.startsWith(`${e}#`)&&!r.has(o)&&Na.delete(o);return t}async function Da(e,t){let n=await Ar(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await cn(n.path,["pr","view",String(r),"--json",mw]);return Kd(JSON.parse(o))}catch(o){if(!hw(o))throw o;let s=await cn(n.path,["pr","view",String(r),"--json",GT]),a=JSON.parse(s);return Kd(a,await zT(n.path,a.headRefOid))}}async function Kn(e,{caller:t="prs"}={}){try{let n=await cn(e,["pr","list","--json",mw,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>Kd(r)).sort(WT)}catch(n){if(!hw(n))throw n;let r=await cn(e,["pr","list","--json",GT,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>Kd(o,await zT(e,o.headRefOid))))).sort(WT)}}function ln(){Ic.clear()}async function bU(e,t){if(!e)return t;for(let n of t){let r=await ft(e,n.number).catch(()=>null);if(!r)continue;r.trackId&&(n.trackId=r.trackId),r.autoMerge&&(n.autoMergeArmed=!0),r.archiveOnMerge&&(n.archiveOnMerge=!0),r.notifyOnMerge&&(n.notifyOnMerge=!0);let o=s=>n.headSha&&s.headSha&&n.headSha!==s.headSha;r.ciFix&&(o(r.ciFix)?await Pd(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Bd(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function JT(){let e=await fw();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await fr())await mU(a.path)&&t.push(a);let n=Date.now(),r=Ud(n)>0,o=await Promise.all(t.map(async a=>{let i=Ic.get(a.path),c=i?.pending?yU:gU;if(i&&(r||n-i.at<c))return i.entry;try{let l=await Me(a.path).catch(()=>null),u=wU(a.path,await Kn(a.path,{caller:"tab"}),n);if(await bU(l,u),i?.entry?.prs)for(let f of u)i.entry.prs.find(h=>h.number===f.number)?.checks?.pending>0&&f.checks.total>0&&f.checks.pending===0&&Wd({change:f.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:f.number,githubAt:f.checks.settledAt,source:"tab"});let d={name:a.name,path:a.path,prs:u},p=u.some(f=>f.mergeabilityPending||f.checks.pending>0||f.autoMergeArmed);return Ic.set(a.path,{at:Date.now(),entry:d,pending:p}),d}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:Vn(l)}}})),s=new Set(t.map(a=>a.path));for(let a of Ic.keys())s.has(a)||Ic.delete(a);return{auth:e,repos:o}}async function Ar(e){let t=(await fr()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function ks(e){return Zs(e)}async function Ba(e,t,{method:n="squash"}={}){let r=await Ar(e);if(!$c.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 cm().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await cn(r.path,s)}catch(a){throw new Error(Vn(a))}finally{ln()}return{ok:!0,number:o}}async function Rc(e,t){let n=await Ar(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await cn(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Vn(s))}finally{ln()}let o=await Me(n.path).catch(()=>null);return o&&await kT(o,r).catch(()=>{}),{ok:!0,number:r}}async function ww(e,t){let n=await Ar(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await cn(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Vn(o))}finally{ln()}return{ok:!0,number:r}}function gw(e){return e.green&&!e.isDraft}function SU(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 YT(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:gw(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function XT(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function Oc(e,t){let n=a=>a?.firstSeen!=null&&Number.isFinite(Number(a.firstSeen))?Number(a.firstSeen):Number.MAX_SAFE_INTEGER,r=n(e),o=n(t);if(r!==o)return r-o;let s=String(e?.repo||"").localeCompare(String(t?.repo||""));return s||Number(e?.number||0)-Number(t?.number||0)}function ZT(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(Oc))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function QT(){let e={};for(let[t,n]of pw)e[t]={...n};return e}async function eE(e,{method:t="merge",onMerged:n=null}={}){let r=await Ar(e);if(!$c.has(t))throw new Error(`invalid merge method: ${t}`);if(pw.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Kn(r.path)).filter(gw).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 pw.set(r.path,s),TU(s,o,{listPrs:()=>Kn(r.path),merge:a=>Ba(r.path,a,{method:t}),sleep:vU,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function AU(e,t){let n=Date.now()+(t.settleMaxMs??xU),r=null;for(;;){let o=null;try{if(o=await t.listPrs(),r=null,!o.some(a=>e.includes(a.number)&&(a.mergeable==="UNKNOWN"||a.mergeStateStatus==="UNKNOWN")))return o}catch(s){r=s}if(Date.now()>=n){if(o)return o;if(r)throw r;return[]}await t.sleep(kU)}}async function TU(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 AU(t.slice(o).map(i=>i.number),n)}catch(i){for(let c of t.slice(o))e.failed.push({number:c.number,title:c.title,reason:`couldn't re-check PR state: ${Vn(i)}`});break}let a=r.find(i=>i.number===s.number);if(!a){e.skipped.push({number:s.number,title:s.title,reason:"no longer open"});continue}if(!gw(a)){e.skipped.push({number:a.number,title:a.title,reason:SU(a)});continue}try{await n.merge(a.number),e.merged.push({number:a.number,title:a.title});try{await n.onMerged?.(a)}catch{}}catch(i){e.failed.push({number:a.number,title:a.title,reason:Vn(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),ln()}}async function yw(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Pa("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Vn(l)}`)}await pU(e);let o=n||"";if((await Bn({cwd:e})).GH_TOKEN){let l=await cm().catch(()=>null);l&&(o=`${o?`${o}
|
|
188
|
+
|
|
189
|
+
`:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",t.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await cn(e,s)}catch(l){throw new Error(Vn(l))}finally{ln()}let i=a.match(/https:\/\/\S+\/pull\/\d+/)?.[0]??null,c=i?Number(i.match(/\/pull\/(\d+)/)?.[1]):null;return{ok:!0,url:i,number:c}}async function tE(e,t){let n=await Ar(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await cn(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(Vn(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!hw(i))throw i;let c=await cn(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await KT(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(Vn(i));o=VT(l).map(qT).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=EU(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await cn(n.path,["run","view",c,"--log-failed"]).then(u=>_U(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function EU(e){return String(e||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function _U(e,{maxLines:t=400,maxBytes:n=64*1024}={}){let r=String(e||""),o=r.split(`
|
|
190
190
|
`);return o.length>t&&(r=`\u2026 (${o.length-t} earlier lines truncated)
|
|
191
191
|
`+o.slice(-t).join(`
|
|
192
192
|
`)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
|
|
193
193
|
`+r.slice(-n)),r}function Vn(e){return String(e?.stderr||e?.message||e||"").trim().split(`
|
|
194
|
-
`).find(n=>n.trim())||"gh failed"}var Pa,
|
|
195
|
-
`)){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===
|
|
196
|
-
`)){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
|
|
197
|
-
`)){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
|
|
194
|
+
`).find(n=>n.trim())||"gh failed"}var Pa,fU,Gd,mw,GT,hU,Na,WT,gU,yU,Ic,$c,kU,xU,vU,pw,xs=G(()=>{Gr();xt();Gn();_c();qd();be();Pa=dU(uU),fU={timeout:3e4,maxBuffer:8*1024*1024};Gd=null;mw="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt,state,mergedAt,closedAt",GT=mw.replace("statusCheckRollup,","");hU=9e4,Na=new Map;WT=(e,t)=>e.number-t.number;gU=3e4,yU=1500,Ic=new Map;$c=new Set(["squash","merge","rebase"]);kU=2500,xU=9e4,vU=e=>new Promise(t=>setTimeout(t,e));pw=new Map});import{execFile as IU}from"node:child_process";import{promisify as $U}from"node:util";async function OU(e,t){try{let{stdout:n}=await RU("git",["-C",e,"rev-list","--count",t]);return Number(n.trim())||0}catch{return null}}async function MU(e,t,n,{rebase:r=!1,listPrs:o=Kn}={}){let s=await ds(e.dir,{ignorePlumbing:!0}).catch(()=>!1),a=await ps(e.dir,n).catch(()=>0),i=t?await OU(e.dir,`refs/remotes/origin/${t}..HEAD`):null,c=i==null?a>0:i>0,l=null,u="unknown";try{l=(await o(e.dir,{caller:"ship-state"})||[]).find(h=>h.headRefName===t)||null,u=l?"open":"none"}catch{}let d=s||a>0,p=[];s&&p.push("commit"),r&&p.push("rebase"),(s||c)&&p.push("push"),d&&(u!=="open"||s||c)&&p.push("pr");let f=p.filter(w=>!(w==="pr"&&u==="open"));return{name:e.name??null,slug:e.repo??null,dirty:s,unpushed:c,aheadBase:a,prState:u,openPr:l?{number:l.number,url:l.url??null,title:l.title??null}:null,available:Vd.filter(w=>p.includes(w)),defaults:Vd.filter(w=>f.includes(w))}}async function zd(e,{branch:t,rebase:n=!1,listPrs:r=Kn}={}){let o=e?.workspace,s=o?.kind==="multi"?(o.repos||[]).filter(p=>p?.dir):o?.base&&(o.dir||e?.cwd)?[{name:o.base?.split("/").pop()??null,dir:o.dir||e.cwd,repo:o.repo??null,baseRef:o.baseRef}]:[];if(!s.length||!await Ia(s[0].dir).catch(()=>!1))return null;let a=[];for(let p of s)a.push(await MU(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=Vd.filter(p=>c.some(f=>f.available.includes(p))),u=Vd.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 RU,Vd,bw=G(()=>{Sd();xs();RU=$U(IU),Vd=["commit","rebase","push","pr"]});import{execFile as CU}from"node:child_process";import{promisify as NU}from"node:util";async function ko(e){if(!e)return null;let t=nE.get(e);if(t&&Date.now()-t.at<DU)return t.branch;let n=null;try{let{stdout:r}=await PU("git",["-C",e,"symbolic-ref","--short","-q","HEAD"],{timeout:5e3});n=r.trim()||null}catch{n=null}return nE.set(e,{at:Date.now(),branch:n}),n}function rE(e){return String(e||"").match(BU)?.[1]??null}function oE(e,t){let n=rE(e);return!!n&&!!t&&n===t}function sE(e,t){let n=rE(e);return!!n&&n!==t}function LU(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 La(e){let t=[],n=r=>{r&&!t.includes(r)&&t.push(r)};n(e?.workspace?.branch??null);for(let r of LU(e))n(await ko(r));return t}async function Jd(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=>oE(t,a.id));if(s)return s;for(let a of r)if(!Ue(a)&&(await La(a)).includes(t))return a;return null}function jU(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 Yd(e,{branch:t=null,repo:n=null,slug:r=null}={}){return oE(t,e?.id)?!0:jU(e,n,r)}var PU,DU,nE,BU,vs=G(()=>{an();PU=NU(CU),DU=5e3,nE=new Map;BU=/^track\/.+-([0-9a-f]{8})$/});import{execFile as FU}from"node:child_process";import{promisify as UU}from"node:util";function ja({trackId:e,boxId:t,epoch:n}){return`${e}/${t}/${Number.isFinite(Number(n))?Number(n):0}`}function Zd(e){let t=/^([^/\s]+)\/([^/\s]+)\/(\d+)$/.exec(String(e||"").trim());return t?{trackId:t[1],boxId:t[2],epoch:Number(t[3])}:null}function ep({trackId:e,boxId:t,epoch:n=0,provider:r=null,model:o=null}){let s=[],a=r?VU[r]:null;return a&&s.push(a),s.push(`${uE}: ${ja({trackId:e,boxId:t,epoch:n})}`),o&&s.push(`${dE}: ${String(o).trim()}`),s}function aE(e){let t={epoch:null,model:null,provider:null,cause:null};for(let n of String(e||"").split(`
|
|
195
|
+
`)){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===uE.toLowerCase()&&!t.epoch)t.epoch=Zd(a);else if(s===dE.toLowerCase()&&!t.model)t.model=a;else if(s===pE.toLowerCase()&&!t.cause){let i=a.toLowerCase();Mc.includes(i)&&(t.cause=i)}else if(s==="co-authored-by"||s==="made-with"){let i=a.toLowerCase();t.provider||(i.startsWith("claude")?t.provider="claude":i.startsWith("codex")?t.provider="codex":i.startsWith("cursor")&&(t.provider="cursor"))}}return t}function zU(e,t=xo){return e<=t?"rework":"drift"}function JU(e,{window:t=xo,end:n=Aw}={}){return!Number.isFinite(e)||e<=t?1:e>=n?0:1-(e-t)/(n-t)}function tp({subject:e="",trailerCause:t=null,wallReason:n=null,fixerCategory:r=null,ciFix:o=!1}={}){let s=String(e||"");return YU.test(s)?{cause:"defect",level:"A",basis:"revert"}:o?{cause:"defect",level:"A",basis:"ci-fix"}:t?{cause:t,level:"E",basis:`${pE} trailer`}:ZU.test(s)&&!iE.test(s)?{cause:"style",level:"C",basis:"subject"}:iE.test(s)?{cause:"defect",level:"C",basis:"subject"}:XU.test(s)?{cause:"design",level:"C",basis:"subject: refactor"}:n==="wrong-approach"?{cause:"design",level:"C",basis:"wall label: wrong-approach"}:r==="bug-fix"?{cause:"defect",level:"C",basis:"fixer prompt: bug-fix"}:null}function Ew({heuristic:e=null,subject:t="",ref:n=null,promptKind:r=null,fixerCategory:o=null,ticketKind:s=null}={}){let a=String(t||""),i=QU.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&&e4.has(c)?{intent:"fix",basis:`subject prefix: ${c}:`}:c&&t4.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"}:cE.test(String(n||""))||cE.test(a)?{intent:"planned",basis:"blueprint item"}:o&&n4.has(o)?{intent:"planned",basis:`prompt: ${o}`}:{intent:"unknown",basis:null}}function a4(e){let t=String(e||"");return r4.some(n=>n.test(t))?"excluded":o4.some(n=>n.test(t))?"docs":s4.some(n=>n.test(t))?"config":"code"}function i4(e){let t=[],n=null,r=null;for(let o of String(e||"").split(`
|
|
196
|
+
`)){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 c4(e,t){let n=Sw(e),r=Sw(t);if(n===r)return 1;if(!n.length||!r.length)return 0;n.length>Xd&&(n=n.slice(0,Xd)),r.length>Xd&&(r=r.slice(0,Xd));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 l4(e,t=fE){let n=new Set,r=e?.del||[],o=e?.add||[];for(let s=0;s<r.length;s++){let a=e.oldStart+s;if(!Sw(r[s]).length){n.add(a);continue}s<o.length&&c4(r[s],o[s])>=t&&n.add(a)}return n}function u4(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 d4(e){let t=new Map,n=null;for(let r of String(e||"").split(`
|
|
197
|
+
`)){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 lE(e,t){let{stdout:n}=await vw(e,["show","-s","--format=%H%n%P%n%ct%n%s%n%B",t]),[r,o,s,a,...i]=n.split(`
|
|
198
198
|
`);return{sha:r.trim(),parents:o.trim()?o.trim().split(" "):[],ts:Number(s)*1e3,subject:a??"",message:i.join(`
|
|
199
|
-
`)}}async function pE(e,t,{fixKey:n=null,window:r=xo,resolveOrigin:o=null,context:s={}}={}){let a=await iE(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=oE(a.message),l=n?Zd(n):c.epoch,u=l?ja(l):null,{stdout:d}=await vw(e,["diff","-U0","--no-color","-M","-C",i,t]),p=o4(d),f=0,w=0,h=0,y=0,k=0,g=0,x=0,A=new Map;for(let E of p){if(E.binary)continue;let j=E.oldPath||E.newPath||"",G=E.deleted.reduce((B,[,Z])=>B+Z,0),R=r4(j);if(R==="excluded"){h+=G;continue}if(R==="docs"){y+=G,k+=E.added;continue}if(R==="config"){g+=G;continue}f+=E.added;let S=new Set;for(let B of E.hunks||[])for(let Z of a4(B))S.add(Z);x+=S.size;let C=[];for(let[B,Z]of E.deleted)for(let xe=B;xe<B+Z;xe++)S.has(xe)||C.push(xe);if(w+=C.length,!E.oldPath||!C.length)continue;let H;try{let B=["blame","-w","-M","-C","--line-porcelain"];for(let[Z,xe]of i4(C))B.push("-L",`${Z},${Z+xe-1}`);B.push(i,"--",E.oldPath),H=c4((await vw(e,B)).stdout)}catch{continue}for(let{sha:B,time:Z}of H.values()){let xe=A.get(B)??{lines:0,time:Z};xe.lines+=1,A.set(B,xe)}}let I=[],M=0,P=new Map;for(let[E,{lines:j,time:G}]of A){let R=P.get(E);if(!R){try{R=await iE(e,E)}catch{R=null}P.set(E,R)}let S=R?oE(R.message):{epoch:null},C=S.epoch,H=S.provider,B=S.model;if(!C&&o){let Q=await o(E,R);Q?.trackId&&Q?.boxId!=null&&(C={trackId:Q.trackId,boxId:Q.boxId,epoch:Q.epoch??0},H=H??Q.provider??null,B=B??Q.model??null)}if(!C){M+=j;continue}let Z=Number.isFinite(G)?G:R?.ts??a.ts,xe=Math.max(0,(a.ts-Z)/UU),Ae=ja(C),_e=Ae===u?"self":GU(xe,r);I.push({sha:E,key:Ae,trackId:C.trackId,boxId:C.boxId,epoch:C.epoch,provider:H??null,model:B??null,lines:j,ageDays:Math.round(xe*100)/100,class:_e,decay:_e==="rework"?1:_e==="drift"?Math.round(KU(xe,{window:r})*1e3)/1e3:0})}I.sort((E,j)=>j.lines-E.lines);let L=tp({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0}),T=Ew({heuristic:L,subject:a.subject,ref:s.ref??null,promptKind:s.promptKind??null,fixerCategory:s.fixerCategory??null,ticketKind:s.ticketKind??null});return{sha:a.sha,parent:i,subject:a.subject.slice(0,200),ts:a.ts,fixKey:u,fixProvider:c.provider??s.provider??null,fixModel:c.model??s.model??null,window:r,added:f,deleted:w,excludedDeleted:h,cosmeticDeleted:x,docsDeleted:y,docsAdded:k,configDeleted:g,codeDelta:f+w+x>0,origins:I,unattributedLines:M,ghost:w===0&&f>0,heuristic:L,intent:T}}function fE(e,{minLines:t=Tw}={}){if(!e)return!1;let n=e.intent?.intent??"unknown";return Fa.has(n)?Cc(e)>=t:!1}function Cc(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function xw(e){let t=e.genUsd;return{...e,genUsd:_n(t),reworkChargedUsd:_n(e.reworkChargedUsd),reworkPendingUsd:_n(e.reworkPendingUsd),trueUsd:_n(t+e.reworkChargedUsd),multiplier:t>0?_n((t+e.reworkChargedUsd)/t):null}}function mE(e,{window:t=xo,boxMeta:n={}}={}){let r=e||[],o=r.filter(T=>T.type==="cost.recorded"),s=r.filter(T=>T.type==="code.committed"),a=r.filter(T=>T.type==="code.rework"),i=T=>Array.isArray(T?.shas)&&T.shas.length?T.shas:T?.sha?[T.sha]:[],c=new Map,l=new Set;for(let T of r)if(T.type==="rework.cause.offered")for(let E of i(T.payload))l.add(E);else if(T.type==="rework.cause.answered"&&T.payload?.cause)for(let E of i(T.payload))c.set(E,T.payload.cause);else if(T.type==="rework.cause.retracted")for(let E of i(T.payload))c.delete(E);let u={},d=(T,E,j)=>ja({trackId:T,boxId:E,epoch:j??0}),p=(T,E={})=>{if(!u[T]){let G=Zd(T)||{};u[T]={key:T,...G,provider:null,model:null,...kw()}}let j=u[T];return E.provider&&!j.provider&&(j.provider=E.provider),E.model&&!j.model&&(j.model=E.model),j};for(let T of o){if(!T.boxId)continue;let E=d(T.trackId,T.boxId,T.epoch),{usd:j,model:G}=sn(T),R=p(E,{provider:T.provider,model:G!=="unknown"?G:null});R.genUsd+=j}let f=new Map;for(let T of s){let E=`${T.trackId}/${T.boxId}`;(f.get(E)??f.set(E,[]).get(E)).push({...T,ts:T.payload?.ts??T.ts})}for(let T of f.values())T.sort((E,j)=>E.ts-j.ts);let w=(T,E,j,G)=>{let R=f.get(`${T}/${E}`)||[],S=-1/0;for(let B of R)B.ts<G&&(S=B.ts);let C=0,H=0;for(let B of o)B.trackId!==T||B.boxId!==E||(B.epoch??0)===(j??0)&&(B.ts<=S||B.ts>G||(C+=sn(B).usd,H++));return{usd:C,turns:H,from:Number.isFinite(S)?S:null}},h={charged:0,pending:0,self:0,decayed:0,unattributed:0,ghost:0,planned:0},y={A:0,B:0,C:0,E:0,none:0},k={fix:0,"likely-fix":0,planned:0,unknown:0},g=0,x=[],A=[],I={},M={},P=new Set;for(let T of a){let E=T.payload||{};if(!E.sha||P.has(E.sha))continue;if(P.add(E.sha),E.codeDelta===!1){g++;continue}let j=Zd(E.fixKey)||{trackId:T.trackId,boxId:T.boxId,epoch:T.epoch??0},{usd:G,turns:R}=w(j.trackId,j.boxId,j.epoch,E.ts??T.ts),S=WU.includes(E.intent?.intent)?E.intent.intent:"unknown";k[S]+=1;let C=null;c.has(E.sha)?C={cause:c.get(E.sha),level:"B",basis:"operator"}:(E.heuristic?.level&&E.heuristic.level!=="C"||E.heuristic)&&(C=E.heuristic);let H=C?.level??"none";y[H]=(y[H]||0)+1;let B=HU.has(H),Z=Number(E.deleted)||0,xe=Array.isArray(E.origins)?E.origins:[],Ae=xe.filter(Q=>Q.class==="rework").reduce((Q,Xe)=>Q+Xe.lines,0),_e={sha:E.sha,subject:E.subject??"",ts:E.ts??T.ts,trackId:T.trackId,fixKey:E.fixKey??null,fixProvider:E.fixProvider??T.provider??null,fixUsd:_n(G),fixTurns:R,linesReworked:Ae,unattributedLines:Number(E.unattributedLines)||0,ghost:E.ghost===!0,intent:{intent:S,basis:E.intent?.basis??null},verdict:C?{cause:C.cause,level:H,basis:C.basis??null}:null,charged:_n(0),origins:[]};if(S==="planned"||C?.cause===Qd){h.planned+=G;for(let Q of xe)Q.class==="rework"&&_e.origins.push({key:Q.key,provider:Q.provider??null,model:Q.model??null,lines:Q.lines,ageDays:Q.ageDays,class:Q.class,usd:0})}else if(E.ghost||Z===0)h.ghost+=G;else{let Q=0;for(let Pe of xe){let le=Pe.lines/Z,ge=G*le;if(Q+=ge,Pe.class==="self"){h.self+=ge;continue}let de=Pe.class==="rework"?1:Number(Pe.decay)||0,ee=ge*de;if(h.decayed+=ge-ee,ee<=0)continue;let we=p(Pe.key,{provider:Pe.provider,model:Pe.model});we.fixes+=1,we.linesReworked+=Pe.class==="rework"?Pe.lines:0;let Oe=C?.cause&&Mc.includes(C.cause)?C.cause:"unclassified";we.causes[Oe]+=1,B?(we.reworkChargedUsd+=ee,h.charged+=ee,_e.charged=_n(_e.charged+ee)):(we.reworkPendingUsd+=ee,h.pending+=ee),_e.origins.push({key:Pe.key,provider:we.provider,model:we.model,lines:Pe.lines,ageDays:Pe.ageDays,class:Pe.class,usd:_n(ee)})}let Xe=Math.max(0,G-Q);h.unattributed+=Xe}x.push(_e),!c.has(E.sha)&&l.has(E.sha)&&A.push(E.sha)}for(let T of Object.values(u)){let E=T.provider||"unknown",j=T.model||"unknown",G=R=>{R.genUsd+=T.genUsd,R.reworkChargedUsd+=T.reworkChargedUsd,R.reworkPendingUsd+=T.reworkPendingUsd,R.linesReworked+=T.linesReworked,R.fixes+=T.fixes;for(let S of Object.keys(R.causes))R.causes[S]+=T.causes[S]};G(I[E]||=kw()),G(M[j]||=kw())}x.sort((T,E)=>E.ts-T.ts);let L=Object.values(u).map(T=>{let E=n[`${T.trackId}/${T.boxId}`]||{};return xw({...T,trackName:E.trackName??null,label:E.label??null})}).sort((T,E)=>E.reworkChargedUsd+E.reworkPendingUsd-(T.reworkChargedUsd+T.reworkPendingUsd)||E.genUsd-T.genUsd);return{window:t,basis:`rework = code lines a fix deletes/modifies that blame to another epoch's commit \u2264${t} d old (docs/config churn and in-place updates \u2265${dE} 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(L.map(T=>[T.key,T])),epochs:L,byProvider:Object.fromEntries(Object.entries(I).map(([T,E])=>[T,xw(E)])),byModel:Object.fromEntries(Object.entries(M).map(([T,E])=>[T,xw(E)])),buckets:Object.fromEntries(Object.entries(h).map(([T,E])=>[T,_n(E)])),fixUsdTotal:_n(Object.values(h).reduce((T,E)=>T+E,0)),fixes:x,verdicts:y,pendingTaps:A,commits:s.length}}var jU,FU,vw,cE,lE,uE,UU,xo,Aw,Tw,Mc,Qd,HU,WU,Fa,dE,qU,VU,sE,zU,JU,YU,XU,ZU,aE,QU,e4,t4,n4,Sw,Xd,_n,kw,np=q(()=>{fo();jU=LU(BU),FU=64*1024*1024,vw=(e,t)=>jU("git",["-C",e,...t],{maxBuffer:FU,timeout:6e4}),cE="Bostrat-Epoch",lE="Bostrat-Model",uE="Bostrat-Fix-Cause",UU=24*36e5,xo=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,Aw=90,Tw=3,Mc=Object.freeze(["defect","design","requirements","style"]),Qd="not-a-fix",HU=new Set(["A","B","E"]),WU=Object.freeze(["fix","likely-fix","planned","unknown"]),Fa=new Set(["fix","likely-fix"]),dE=.8;qU={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};VU=/^revert\b|^revert "/i,sE=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,zU=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,JU=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;YU=/^([a-z]+)(\([^)]*\))?!?:\s/i,XU=new Set(["fix","hotfix","bugfix","revert"]),ZU=new Set(["feat","feature","docs","doc","test","tests","build","ci","chore"]),aE=/\bBP-[A-Z0-9]+(?:-[A-Z0-9]+)*\b/i,QU=new Set(["feature-build","docs-writing","research","design","ops-infra","orchestration"]);e4=[/(^|\/)\.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)\//],t4=[/\.(md|mdx|markdown|rst|txt|adoc|asciidoc)$/i,/(^|\/)(docs?|notes|business|design)\//i,/(^|\/)(README|CHANGELOG|CHANGES|LICENSE|CONTRIBUTING|AUTHORS|NOTICE)(\.[a-z]+)?$/i],n4=[/\.(json|jsonc|json5|ya?ml|toml|ini|cfg|conf|properties|env|example)$/i,/(^|\/)\.[^/]+$/,/(^|\/)\.github\//,/(^|\/)(Dockerfile|Makefile|Procfile|fly\.toml)$/];Sw=e=>String(e??"").replace(/\s+/g," ").trim(),Xd=400;_n=e=>Math.round(e*1e4)/1e4,kw=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import{execFile as l4}from"node:child_process";import{promisify as u4}from"node:util";function hE(e,t=Date.now()){return Ni(e,t)??0}function p4(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 wE(e,t,{now:n=Date.now()}={}){let r=hE(t,n),o=await K(e);return ep({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:p4(o,t.name,r)})}function f4(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 m4(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=f4(n,t.name),c=i?gr(i.text)?.category??null:null;return{wallReason:a,fixerCategory:c,promptKind:i?.kind??null}}async function h4(e,t){try{let{stdout:n}=await _w(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 w4(e){try{let{stdout:t}=await _w(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function gE(e,t,n,{cwd:r=t?.cwd,log:o=console,now:s=Date.now()}={}){if(!e?.id||!t?.name||!n||!r)return{recorded:!1,reason:"incomplete"};let a=await K(e);if(a.some(h=>h.type==="code.committed"&&h.payload?.sha===n))return{recorded:!1,reason:"duplicate"};let i=s,c=null;try{let{stdout:h}=await _w(r,["show","-s","--format=%ct%n%s",n]),[y,...k]=h.split(`
|
|
199
|
+
`)}}async function mE(e,t,{fixKey:n=null,window:r=xo,resolveOrigin:o=null,context:s={}}={}){let a=await lE(e,t);if(a.parents.length!==1)return null;let i=a.parents[0],c=aE(a.message),l=n?Zd(n):c.epoch,u=l?ja(l):null,{stdout:d}=await vw(e,["diff","-U0","--no-color","-M","-C",i,t]),p=i4(d),f=0,w=0,h=0,y=0,k=0,g=0,x=0,S=new Map;for(let $ of p){if($.binary)continue;let L=$.oldPath||$.newPath||"",H=$.deleted.reduce((B,[,Z])=>B+Z,0),R=a4(L);if(R==="excluded"){h+=H;continue}if(R==="docs"){y+=H,k+=$.added;continue}if(R==="config"){g+=H;continue}f+=$.added;let A=new Set;for(let B of $.hunks||[])for(let Z of l4(B))A.add(Z);x+=A.size;let C=[];for(let[B,Z]of $.deleted)for(let xe=B;xe<B+Z;xe++)A.has(xe)||C.push(xe);if(w+=C.length,!$.oldPath||!C.length)continue;let W;try{let B=["blame","-w","-M","-C","--line-porcelain"];for(let[Z,xe]of u4(C))B.push("-L",`${Z},${Z+xe-1}`);B.push(i,"--",$.oldPath),W=d4((await vw(e,B)).stdout)}catch{continue}for(let{sha:B,time:Z}of W.values()){let xe=S.get(B)??{lines:0,time:Z};xe.lines+=1,S.set(B,xe)}}let _=[],M=0,P=new Map;for(let[$,{lines:L,time:H}]of S){let R=P.get($);if(!R){try{R=await lE(e,$)}catch{R=null}P.set($,R)}let A=R?aE(R.message):{epoch:null},C=A.epoch,W=A.provider,B=A.model;if(!C&&o){let Q=await o($,R);Q?.trackId&&Q?.boxId!=null&&(C={trackId:Q.trackId,boxId:Q.boxId,epoch:Q.epoch??0},W=W??Q.provider??null,B=B??Q.model??null)}if(!C){M+=L;continue}let Z=Number.isFinite(H)?H:R?.ts??a.ts,xe=Math.max(0,(a.ts-Z)/qU),Ae=ja(C),_e=Ae===u?"self":zU(xe,r);_.push({sha:$,key:Ae,trackId:C.trackId,boxId:C.boxId,epoch:C.epoch,provider:W??null,model:B??null,lines:L,ageDays:Math.round(xe*100)/100,class:_e,decay:_e==="rework"?1:_e==="drift"?Math.round(JU(xe,{window:r})*1e3)/1e3:0})}_.sort(($,L)=>L.lines-$.lines);let j=tp({subject:a.subject,trailerCause:c.cause,wallReason:s.wallReason??null,fixerCategory:s.fixerCategory??null,ciFix:s.ciFix===!0}),T=Ew({heuristic:j,subject:a.subject,ref:s.ref??null,promptKind:s.promptKind??null,fixerCategory:s.fixerCategory??null,ticketKind:s.ticketKind??null});return{sha:a.sha,parent:i,subject:a.subject.slice(0,200),ts:a.ts,fixKey:u,fixProvider:c.provider??s.provider??null,fixModel:c.model??s.model??null,window:r,added:f,deleted:w,excludedDeleted:h,cosmeticDeleted:x,docsDeleted:y,docsAdded:k,configDeleted:g,codeDelta:f+w+x>0,origins:_,unattributedLines:M,ghost:w===0&&f>0,heuristic:j,intent:T}}function hE(e,{minLines:t=Tw}={}){if(!e)return!1;let n=e.intent?.intent??"unknown";return Fa.has(n)?Cc(e)>=t:!1}function Cc(e){return(e?.origins||[]).filter(t=>t.class==="rework").reduce((t,n)=>t+n.lines,0)}function xw(e){let t=e.genUsd;return{...e,genUsd:_n(t),reworkChargedUsd:_n(e.reworkChargedUsd),reworkPendingUsd:_n(e.reworkPendingUsd),trueUsd:_n(t+e.reworkChargedUsd),multiplier:t>0?_n((t+e.reworkChargedUsd)/t):null}}function wE(e,{window:t=xo,boxMeta:n={}}={}){let r=e||[],o=r.filter(T=>T.type==="cost.recorded"),s=r.filter(T=>T.type==="code.committed"),a=r.filter(T=>T.type==="code.rework"),i=T=>Array.isArray(T?.shas)&&T.shas.length?T.shas:T?.sha?[T.sha]:[],c=new Map,l=new Set;for(let T of r)if(T.type==="rework.cause.offered")for(let $ of i(T.payload))l.add($);else if(T.type==="rework.cause.answered"&&T.payload?.cause)for(let $ of i(T.payload))c.set($,T.payload.cause);else if(T.type==="rework.cause.retracted")for(let $ of i(T.payload))c.delete($);let u={},d=(T,$,L)=>ja({trackId:T,boxId:$,epoch:L??0}),p=(T,$={})=>{if(!u[T]){let H=Zd(T)||{};u[T]={key:T,...H,provider:null,model:null,...kw()}}let L=u[T];return $.provider&&!L.provider&&(L.provider=$.provider),$.model&&!L.model&&(L.model=$.model),L};for(let T of o){if(!T.boxId)continue;let $=d(T.trackId,T.boxId,T.epoch),{usd:L,model:H}=sn(T),R=p($,{provider:T.provider,model:H!=="unknown"?H:null});R.genUsd+=L}let f=new Map;for(let T of s){let $=`${T.trackId}/${T.boxId}`;(f.get($)??f.set($,[]).get($)).push({...T,ts:T.payload?.ts??T.ts})}for(let T of f.values())T.sort(($,L)=>$.ts-L.ts);let w=(T,$,L,H)=>{let R=f.get(`${T}/${$}`)||[],A=-1/0;for(let B of R)B.ts<H&&(A=B.ts);let C=0,W=0;for(let B of o)B.trackId!==T||B.boxId!==$||(B.epoch??0)===(L??0)&&(B.ts<=A||B.ts>H||(C+=sn(B).usd,W++));return{usd:C,turns:W,from:Number.isFinite(A)?A:null}},h={charged:0,pending:0,self:0,decayed:0,unattributed:0,ghost:0,planned:0},y={A:0,B:0,C:0,E:0,none:0},k={fix:0,"likely-fix":0,planned:0,unknown:0},g=0,x=[],S=[],_={},M={},P=new Set;for(let T of a){let $=T.payload||{};if(!$.sha||P.has($.sha))continue;if(P.add($.sha),$.codeDelta===!1){g++;continue}let L=Zd($.fixKey)||{trackId:T.trackId,boxId:T.boxId,epoch:T.epoch??0},{usd:H,turns:R}=w(L.trackId,L.boxId,L.epoch,$.ts??T.ts),A=KU.includes($.intent?.intent)?$.intent.intent:"unknown";k[A]+=1;let C=null;c.has($.sha)?C={cause:c.get($.sha),level:"B",basis:"operator"}:($.heuristic?.level&&$.heuristic.level!=="C"||$.heuristic)&&(C=$.heuristic);let W=C?.level??"none";y[W]=(y[W]||0)+1;let B=GU.has(W),Z=Number($.deleted)||0,xe=Array.isArray($.origins)?$.origins:[],Ae=xe.filter(Q=>Q.class==="rework").reduce((Q,Xe)=>Q+Xe.lines,0),_e={sha:$.sha,subject:$.subject??"",ts:$.ts??T.ts,trackId:T.trackId,fixKey:$.fixKey??null,fixProvider:$.fixProvider??T.provider??null,fixUsd:_n(H),fixTurns:R,linesReworked:Ae,unattributedLines:Number($.unattributedLines)||0,ghost:$.ghost===!0,intent:{intent:A,basis:$.intent?.basis??null},verdict:C?{cause:C.cause,level:W,basis:C.basis??null}:null,charged:_n(0),origins:[]};if(A==="planned"||C?.cause===Qd){h.planned+=H;for(let Q of xe)Q.class==="rework"&&_e.origins.push({key:Q.key,provider:Q.provider??null,model:Q.model??null,lines:Q.lines,ageDays:Q.ageDays,class:Q.class,usd:0})}else if($.ghost||Z===0)h.ghost+=H;else{let Q=0;for(let Pe of xe){let le=Pe.lines/Z,ge=H*le;if(Q+=ge,Pe.class==="self"){h.self+=ge;continue}let de=Pe.class==="rework"?1:Number(Pe.decay)||0,ee=ge*de;if(h.decayed+=ge-ee,ee<=0)continue;let we=p(Pe.key,{provider:Pe.provider,model:Pe.model});we.fixes+=1,we.linesReworked+=Pe.class==="rework"?Pe.lines:0;let Oe=C?.cause&&Mc.includes(C.cause)?C.cause:"unclassified";we.causes[Oe]+=1,B?(we.reworkChargedUsd+=ee,h.charged+=ee,_e.charged=_n(_e.charged+ee)):(we.reworkPendingUsd+=ee,h.pending+=ee),_e.origins.push({key:Pe.key,provider:we.provider,model:we.model,lines:Pe.lines,ageDays:Pe.ageDays,class:Pe.class,usd:_n(ee)})}let Xe=Math.max(0,H-Q);h.unattributed+=Xe}x.push(_e),!c.has($.sha)&&l.has($.sha)&&S.push($.sha)}for(let T of Object.values(u)){let $=T.provider||"unknown",L=T.model||"unknown",H=R=>{R.genUsd+=T.genUsd,R.reworkChargedUsd+=T.reworkChargedUsd,R.reworkPendingUsd+=T.reworkPendingUsd,R.linesReworked+=T.linesReworked,R.fixes+=T.fixes;for(let A of Object.keys(R.causes))R.causes[A]+=T.causes[A]};H(_[$]||=kw()),H(M[L]||=kw())}x.sort((T,$)=>$.ts-T.ts);let j=Object.values(u).map(T=>{let $=n[`${T.trackId}/${T.boxId}`]||{};return xw({...T,trackName:$.trackName??null,label:$.label??null})}).sort((T,$)=>$.reworkChargedUsd+$.reworkPendingUsd-(T.reworkChargedUsd+T.reworkPendingUsd)||$.genUsd-T.genUsd);return{window:t,basis:`rework = code lines a fix deletes/modifies that blame to another epoch's commit \u2264${t} d old (docs/config churn and in-place updates \u2265${fE} 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(T=>[T.key,T])),epochs:j,byProvider:Object.fromEntries(Object.entries(_).map(([T,$])=>[T,xw($)])),byModel:Object.fromEntries(Object.entries(M).map(([T,$])=>[T,xw($)])),buckets:Object.fromEntries(Object.entries(h).map(([T,$])=>[T,_n($)])),fixUsdTotal:_n(Object.values(h).reduce((T,$)=>T+$,0)),fixes:x,verdicts:y,pendingTaps:S,commits:s.length}}var HU,WU,vw,uE,dE,pE,qU,xo,Aw,Tw,Mc,Qd,GU,KU,Fa,fE,VU,YU,iE,XU,ZU,QU,e4,t4,cE,n4,r4,o4,s4,Sw,Xd,_n,kw,np=G(()=>{fo();HU=UU(FU),WU=64*1024*1024,vw=(e,t)=>HU("git",["-C",e,...t],{maxBuffer:WU,timeout:6e4}),uE="Bostrat-Epoch",dE="Bostrat-Model",pE="Bostrat-Fix-Cause",qU=24*36e5,xo=Number(process.env.BOSTRAT_REWORK_WINDOW_DAYS)||21,Aw=90,Tw=3,Mc=Object.freeze(["defect","design","requirements","style"]),Qd="not-a-fix",GU=new Set(["A","B","E"]),KU=Object.freeze(["fix","likely-fix","planned","unknown"]),Fa=new Set(["fix","likely-fix"]),fE=.8;VU={claude:"Co-Authored-By: Claude <noreply@anthropic.com>",codex:"Co-authored-by: Codex <noreply@openai.com>",cursor:"Made-with: Cursor"};YU=/^revert\b|^revert "/i,iE=/\b(fix(es|ed)?|bug|hotfix|regression|crash|broken|npe|null ?pointer|off[- ]by[- ]one|leak)\b/i,XU=/\b(refactor|rearchitect|re-architect|restructure|rework|rewrite|redesign|simplif(y|ied)|clean ?up)\b/i,ZU=/\b(lint|format(ting)?|prettier|typo|whitespace|rename)\b/i;QU=/^([a-z]+)(\([^)]*\))?!?:\s/i,e4=new Set(["fix","hotfix","bugfix","revert"]),t4=new Set(["feat","feature","docs","doc","test","tests","build","ci","chore"]),cE=/\bBP-[A-Z0-9]+(?:-[A-Z0-9]+)*\b/i,n4=new Set(["feature-build","docs-writing","research","design","ops-infra","orchestration"]);r4=[/(^|\/)\.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)\//],o4=[/\.(md|mdx|markdown|rst|txt|adoc|asciidoc)$/i,/(^|\/)(docs?|notes|business|design)\//i,/(^|\/)(README|CHANGELOG|CHANGES|LICENSE|CONTRIBUTING|AUTHORS|NOTICE)(\.[a-z]+)?$/i],s4=[/\.(json|jsonc|json5|ya?ml|toml|ini|cfg|conf|properties|env|example)$/i,/(^|\/)\.[^/]+$/,/(^|\/)\.github\//,/(^|\/)(Dockerfile|Makefile|Procfile|fly\.toml)$/];Sw=e=>String(e??"").replace(/\s+/g," ").trim(),Xd=400;_n=e=>Math.round(e*1e4)/1e4,kw=()=>({genUsd:0,reworkChargedUsd:0,reworkPendingUsd:0,linesReworked:0,fixes:0,causes:{defect:0,design:0,requirements:0,style:0,unclassified:0}})});import{execFile as p4}from"node:child_process";import{promisify as f4}from"node:util";function gE(e,t=Date.now()){return Ni(e,t)??0}function h4(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 yE(e,t,{now:n=Date.now()}={}){let r=gE(t,n),o=await K(e);return ep({trackId:e.id,boxId:t.name,epoch:r,provider:t.agent??null,model:h4(o,t.name,r)})}function w4(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 g4(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=w4(n,t.name),c=i?gr(i.text)?.category??null:null;return{wallReason:a,fixerCategory:c,promptKind:i?.kind??null}}async function y4(e,t){try{let{stdout:n}=await _w(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 b4(e){try{let{stdout:t}=await _w(e,["rev-parse","--abbrev-ref","HEAD"]);return t.trim()||null}catch{return null}}async function bE(e,t,n,{cwd:r=t?.cwd,log:o=console,now:s=Date.now()}={}){if(!e?.id||!t?.name||!n||!r)return{recorded:!1,reason:"incomplete"};let a=await K(e);if(a.some(h=>h.type==="code.committed"&&h.payload?.sha===n))return{recorded:!1,reason:"duplicate"};let i=s,c=null;try{let{stdout:h}=await _w(r,["show","-s","--format=%ct%n%s",n]),[y,...k]=h.split(`
|
|
200
200
|
`),g=Number(y.trim())*1e3;Number.isFinite(g)&&g>0&&(i=g),c=k.join(`
|
|
201
|
-
`).trim().slice(0,300)||null}catch{return{recorded:!1,reason:"unknown-sha"}}let l=
|
|
202
|
-
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+_4(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 $w(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function IE(e){let t=EE(e),n=t.match(A4);if(!n)return null;let r=$w(t,n.index);return E4.test(r)?null:{repo:r.match(_E)?.[1]??null,head:r.match(I4)?.[1]??null}}function $E(e){let t=EE(e),n=t.match(T4);if(!n)return null;let r=$w(t,n.index),o=$w(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("-")){R4.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(_E)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function M4(e,t,{exclude:n=()=>!1}={}){let r=async i=>{Sr("observer");let{stdout:c}=await vE("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",O4],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw yo(l)&&bo(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-x4&&!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 C4(e,t){if(t)return[t];let n=[];for(let r of RE(e)){let o=await Me(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await fr().catch(()=>[])){let o=await Me(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function RE(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 N4(e,t,n){let r=await La(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)||$4(c.headRefName,e.id),i=[];for(let c of await C4(e,t?.repo??null)){let l;try{l=await M4(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await ft(c,u.number);if(s(u.headRefName)){if(d&&d.trackId===e.id&&d.announced)return{settled:!0};if(d&&d.trackId!==e.id)continue;return{slug:c,pr:u}}d&&(d.trackId!==e.id||d.announced)||i.push({slug:c,pr:u})}}return i.length?(i.sort((c,l)=>Date.parse(l.pr.createdAt)-Date.parse(c.pr.createdAt)),i[0]):null}function OE(e){if(!e)return 0;let t=0;for(let n of Tr)n.startsWith(`${e}|`)&&t++;return t}function ME(){for(let e of[...Rw])e()}function CE(e,t,{delays:n=SE,turnEnd:r}={}){let o=IE(t.command);if(!o)return null;let s=!!$E(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(Tr.has(a))return null;Tr.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(h=>{let y=()=>{clearTimeout(k),Rw.delete(y),h()},k=setTimeout(y,l);Rw.add(y)});let u=await N4(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:d,pr:p}=u,{link:f,created:w}=await Yt({repo:d,number:p.number,trackId:e.id,url:p.url,headRef:p.headRefName,title:p.title,source:"observed"});return!w&&!(f?.trackId===e.id&&await xr(d,p.number))?null:(await Iw(t.box,t.agent,r),await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"opened",number:p.number,url:p.url,title:p.title,branch:p.headRefName,repo:d,...s?{selfMerge:!0}:{}}}),p)}return null}finally{Tr.delete(a)}})();return c.catch(()=>Tr.delete(a)),c}function NE(e,t,{delays:n=SE,turnEnd:r,openedWait:o}={}){let s=$E(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(Tr.has(a))return null;Tr.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=RE(e);if(w.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let h of w){let y=await Me(h).catch(()=>null);y&&!d.includes(y)&&d.push(y)}}if(l||=(await La(e).catch(()=>[])).filter(w=>w!==e.workspace?.branch)[0]||e.workspace?.branch,!d.length||!l)return null;c=d[0];let p=async(w,h)=>{let y=await ft(c,w.number);return y&&y.trackId!==e.id?null:(y||await Yt({repo:c,number:w.number,trackId:e.id,url:w.url,headRef:w.headRefName,title:w.title,source:"merge-observed"}),await Tc(c,w.number),h&&((await K(e).catch(()=>[])).some(x=>x.type==="pr.state.changed"&&(x.payload?.change==="auto-armed"||x.payload?.change==="merged")&&x.payload?.number===w.number)||(await Iw(t.box,t.agent,r),await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:w.number,url:w.url,title:w.title,branch:w.headRefName,repo:c}}))),w)},f=null;for(let w of n){await new Promise(M=>setTimeout(M,w));let h=[];for(let M of d)try{Sr("observer");let{stdout:P}=await vE("gh",["pr","view",String(l),"--repo",M,"--json",P4],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await Bn({slug:M})}}),L=JSON.parse(P||"null");L?.number&&h.push({slug:M,pr:L})}catch(P){yo(P)&&bo(P)}let y=h.find(M=>M.pr.state==="MERGED")??(h.length===1?h[0]:null);if(!y)continue;c=y.slug;let k=y.pr;if(!k?.number)continue;if(k.state==="CLOSED")return null;if(k.state!=="MERGED"){if(f=k,s.auto)return p(k,!0);continue}let g=await ft(c,k.number);if(g&&g.trackId!==e.id)return null;g||await Yt({repo:c,number:k.number,trackId:e.id,url:k.url,headRef:k.headRefName,title:k.title,source:"merge-observed"}),await Iw(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 A=M=>M.some(P=>P.type==="pr.state.changed"&&P.payload?.change==="opened"&&P.payload?.number===k.number),I=0;if(!A(x)){if(IE(t.command)&&await xr(c,k.number)){let M=Date.now();I=M+1,await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:M,change:"opened",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c,selfMerge:!0}})}else if((await ft(c,k.number))?.announced){let M=o?.capMs??v4,P=o?.pollMs??S4,L=Date.now()+M;for(;Date.now()<L&&(await new Promise(T=>setTimeout(T,P)),!A(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(),I),change:"merged",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c}}),await rp(e,{repo:c,number:k.number,title:k.title,branch:k.headRefName,url:k.url}).catch(()=>{}),await _r(c,k.number).catch(()=>{}),await Er(c,k.number).catch(()=>{}),await wo(c,k.number,"merged"),k}return f?p(f,!1):null}finally{Tr.delete(a)}})();return i.catch(()=>Tr.delete(a)),i}var vE,SE,b4,k4,x4,v4,S4,AE,TE,A4,T4,E4,xE,_4,_E,I4,$4,R4,O4,Tr,Rw,P4,sp=q(()=>{be();Gr();xt();_c();Gn();Ir();xa();vs();op();vE=y4(g4),SE=[4e3,15e3,45e3],b4=2500,k4=5*6e4;x4=15e3,v4=9e4,S4=2e3,AE=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,TE=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,A4=new RegExp(`${AE}${TE}create\\b`),T4=new RegExp(`${AE}${TE}merge\\b`),E4=/\s--web\b/,xE=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,_4=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");_E=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,I4=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,$4=rE;R4=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);O4="number,url,title,headRefName,createdAt";Tr=new Set;Rw=new Set;P4="number,url,title,headRefName,state"});import D4 from"node:fs/promises";import Mw from"node:path";import B4 from"node:os";function Ow(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function ap(e){let t=L4.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=Ow(n,"from"),s=o?o.replace(/^uds:/,""):null,a=PE(s);return{socket:s,pid:a,name:Ow(n,"from-name"),mode:Ow(n,"from-mode"),text:r.trim()}}function PE(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function j4(){return process.env.CLAUDE_CONFIG_DIR?Mw.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):Mw.join(B4.homedir(),".claude","sessions")}async function F4(e){let t=PE(e);if(!t)return null;try{let n=await D4.readFile(Mw.join(j4(),`${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 DE(e,t){let n=await F4(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 L4,Cw=q(()=>{L4=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function BE(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}async function LE(e,t,n=Nw){let r=new AbortController,o=setTimeout(()=>r.abort(),n);try{return await H4(`${De(t)}${e}`,{headers:W4(t),signal:r.signal})}finally{clearTimeout(o)}}async function Nc({env:e=process.env,force:t=!1,timeoutMs:n=Nw}={}){if(!BE(e))return{followups:[],source:"unconfigured"};if(!t&&Ss&&Date.now()-Ss.at<U4)return{followups:Ss.followups,source:"cache"};try{let r=await LE("/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 Ss={at:Date.now(),followups:s},{followups:s,source:"plane"}}catch(r){return Ss?{followups:Ss.followups,source:"stale",error:r?.message}:{followups:[],source:"unreachable",error:r?.message}}}async function jE(e,{env:t=process.env,timeoutMs:n=Nw}={}){if(!e)return{followup:null,source:"unconfigured"};if(!BE(t))return{followup:null,source:"unconfigured"};try{let r=await LE(`/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=Ss?.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,Nw,Ss,H4,W4,FE=q(()=>{xt();U4=6e4,Nw=5e3,Ss=null,H4=(...e)=>fetch(...e);W4=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});async function vo(e,t){if(Pw.has(e)){let n=new Error("offer already answered (answer in flight)");throw n.code="answer-in-flight",n}Pw.add(e);try{return await t()}finally{Pw.delete(e)}}var Pw,Pc=q(()=>{Pw=new Set});import{randomUUID as As,createHash as q4}from"node:crypto";function ip(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function JE(e){for(let[t,n]of z4)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 qE(e,{at:t="",promptRequired:n=!0,standing:r=!1}={}){let o=e&&typeof e=="object"?e:{},s=Ts(o.label).replace(/\s+/g," ");if(!s)throw new Error(`${t}label required (\u2264${Dw} chars, e.g. "Deploy to Pi")`);if(s.length>Dw)throw new Error(`${t}label must be \u2264${Dw} characters`);let a=Ts(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")>UE)throw new Error(`${t}prompt must be \u2264${UE} bytes`);if(r){let l=JE(a);if(l)throw new Error(`${t}${l}`)}let i=o.standing==null||o.standing===""?"offerable":String(o.standing).trim().toLowerCase();if(!V4.includes(i))throw new Error(`${t}standing must be "offerable" or "never", got "${o.standing}"`);let c=Ts(o.why).slice(0,KE)||null;return{label:s,prompt:a,why:c,standing:i}}function YE(e,{standing:t=!1}={}){let n=e&&typeof e=="object"?e:{},r=Ts(n.trigger)||(n.ship?"now":""),o=t?G4:VE.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"?X4(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>WE)throw new Error(`items must be \u2264${WE} \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)=>qE(f,{at:`items[${w}]: `,standing:t})),c=Ts(n.why).slice(0,KE)||null;let p=new Set;for(let f of i){let w=ip(f.label);if(p.has(w))throw new Error(`items must have distinct labels ("${f.label}" appears twice)`);p.add(w)}}else i=[qE(n,{promptRequired:!s,standing:t})];let l=i[0],u=c??l.why,d=t?Y4(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 J4(e){return e.standing==="never"?{promotable:!1,promotableReason:"one-off"}:JE(e.prompt)?{promotable:!1,promotableReason:"specifics"}:{promotable:!0,promotableReason:null}}function Y4(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 X4(e){let t=e&&typeof e=="object"?e:{},n=Array.isArray(t.actions)?t.actions.map(i=>String(i).trim().toLowerCase()):Bw.slice();for(let i of n)if(!Bw.includes(i))throw new Error(`ship.actions may only contain ${Bw.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=Ts(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=>Ts(i)).filter(Boolean),a.length||(a=null)}return{actions:[...new Set(n)],rebase:r,toPr:o,base:s,repos:a}}function cp(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 XE(e){return{armId:e.armId,label:e.label,prompt:e.prompt,promptHash:zE(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",...J4(e)}}async function ZE(e,t,{pull:n=Nc,ship:r={}}={}){if(jw(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=YE(t);if(o.ship)return{kind:"ship",...await n2(e,o,r)};let s={id:e.id,cwd:e.cwd},a=(e.boxes||[]).find(g=>g.name===e.leadBox)||null,i={boxId:e.leadBox??null,provider:a?.agent??null},c=As(),l=(g,x,A={})=>({ts:Date.now(),armId:As(),trigger:o.trigger,label:g.label,prompt:g.prompt,why:g.why,standing:g.standing,groupId:c,seq:x,...o.planWhy?{planWhy:o.planWhy}:{},...A}),u={groupId:c,trigger:o.trigger,label:o.label,prompt:o.prompt,why:o.why,planWhy:o.planWhy};if(o.trigger==="now"){let g=As(),x=[];for(let[A,I]of o.items.entries()){let M=l(I,A);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(XE)}}),{kind:"now",offerId:g,armId:x[0].armId,items:x.map(A=>({armId:A.armId,label:A.label,standing:A.standing})),...u}}let d=e.workspace?.repo||null,{followups:p}=await n().catch(()=>({followups:[]})),f=await K(s),w=cp(f,{trigger:o.trigger}),h=[],y=0;for(let g of o.items){let x=ip(g.label),A=p.find(L=>L.status==="active"&&L.mode!=="off"&&L.trigger===o.trigger&&(L.repo==null||L.repo===d)&&L.dedupeKey===x);if(A){h.push({label:g.label,standing:g.standing,covered:{id:A.id,label:A.label}});continue}let I=w.filter(L=>ip(L.label)===x),M=I.length?I[I.length-1].armId:null,P=l(g,y++,M?{replaces:M}:{});await N(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:P}),h.push({armId:P.armId,label:g.label,standing:g.standing,replaces:M})}let k=h.filter(g=>g.armId);return k.length?{kind:"armed",armId:k[0].armId,replaces:k[0].replaces,items:h,...u}:{kind:"covered",covered:h[0].covered,items:h,...u}}async function QE(e,t){if(!t)throw new Error("armId required");let n={id:e.id,cwd:e.cwd},r=await K(n);if(!cp(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 e_(e,t,{pull:n=Nc}={}){if(jw(e))throw new Error("propose from a lead Track, not a loop's run Track");let r=YE(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=ip(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}" (${Lw[f.trigger]||f.trigger}) already exists as a standing follow-up \u2014 not re-proposed`);if(p){let y=p.deletedAt?new Date(p.deletedAt).toISOString().slice(0,10):"earlier";throw new Error(`the operator deleted "${p.label}" on ${y} \u2014 do not re-propose it`)}let w=As(),h=(e.boxes||[]).find(y=>y.name===e.leadBox)||null;return await N(s,{type:"followup.create.offered",actor:"agent",source:"cli",boxId:e.leadBox??null,provider:h?.agent??null,payload:{ts:Date.now(),proposalId:w,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why,origin:"agent"}}),{proposalId:w,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why}}async function t_(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 Dc(e,t,n={},{pull:r=Nc}={}){if(!e||jw(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=A=>{let I=(A?.prs||[]).filter(M=>Number.isFinite(Number(M?.number))).map(M=>`${M?.repo??A?.repo??""}#${Number(M.number)}`);return Number.isFinite(Number(A?.prNumber))&&I.push(`${A?.repo??""}#${Number(A.prNumber)}`),I},g=new Set(k(n));if(s.some(A=>A.type==="followup.offered"&&A.payload?.trigger==="pr.merged"&&k(A.payload?.context).some(I=>g.has(I))))return null}let i=cp(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,HE).map(XE),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 A=I=>I.stats?.offered?(I.stats.accepted||0)/I.stats.offered:0;return A(x)-A(g)||(g.origin==="operator"?-1:0)-(x.origin==="operator"?-1:0)||(x.createdAt||0)-(g.createdAt||0)}).slice(0,K4).map(g=>({followupId:g.id,label:g.label,promptPreview:Z4(g.prompt),promptHash:zE(g.prompt),origin:g.origin,repo:g.repo??null}))}let w=[...d,...f].slice(0,HE);if(!w.length)return null;let h=As(),y={ts:Date.now(),offerId:h,trigger:t,repo:a,context:{...n,repo:a},groupId:c,why:p,rows:w};return await N(o,{type:"followup.offered",actor:"system",source:"orchestrator",payload:y}),y}function Q4(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 GE(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 n_(e,{offerId:t,followupId:n=null,armId:r=null,promoted:o=null,all:s=!1}={},a={}){if(!t)throw new Error("offerId required");return vo(`fu:${e.id}:${t}`,()=>t2(e,{offerId:t,followupId:n,armId:r,promoted:o,all:s===!0},a))}async function t2(e,{offerId:t,followupId:n,armId:r,promoted:o,all:s},a){let{sayToLead:i,unarchiveTrack:c,getFollowup:l=jE}=a,u={id:e.id,cwd:e.cwd},d=await K(u),p=d.find(T=>T.type==="followup.offered"&&T.payload?.offerId===t);if(!p)throw new Error("unknown offer");let f=p.payload.rows||[],w=Q4(d,p.payload),h=e2(w,f);if(!h.length)throw new Error("offer already answered");let y=T=>N(u,{type:"followup.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,...T}}),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=T=>{let j=d.find(G=>G.type==="followup.armed"&&G.payload?.armId===T.armId)?.payload?.prompt??T.prompt;if(!j)throw new Error("the arm's prompt is missing from the log");return j};if(s){let T=p.payload.groupId??null,E=h.filter(B=>B.armId&&(!T||B.groupId===T)).sort((B,Z)=>(B.seq??0)-(Z.seq??0));if(!E.length)throw new Error("nothing to run together \u2014 no open items from this plan remain on the card");let j=E.map(B=>({row:B,prompt:g(B)})),G=await k();if(!i)throw new Error("sayToLead unavailable");let R=`${GE(p.payload).replace(/:$/,"")} \u2014 ${j.length} steps, in order:`,S=j.map((B,Z)=>`${Z+1}. ${B.row.label}
|
|
201
|
+
`).trim().slice(0,300)||null}catch{return{recorded:!1,reason:"unknown-sha"}}let l=gE(t,i),u=await y4(r,n),d=await b4(r),p=await N(e,{type:"code.committed",actor:"agent",source:"hook",boxId:t.name,provider:t.agent??null,payload:{sha:n,ref:d,subject:c,...u,ts:i}}),f=null,w=!1;try{let h=await mE(r,n,{fixKey:ja({trackId:e.id,boxId:t.name,epoch:l}),window:xo,context:{...g4(e,t,a,l),ref:d,provider:t.agent??null}});if(h&&(f=await N(e,{type:"code.rework",actor:"system",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:p.id,payload:h}),!(e?.workspace?.kind==="worktree"&&!!e?.workspace?.branch)&&hE(h))){let k=Cc(h),g=h.origins.filter(x=>x.class==="rework").slice(0,3);await N(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:t.name,provider:t.agent??null,causationId:f.id,payload:{sha:n,subject:h.subject,linesReworked:k,origins:g.map(x=>({key:x.key,provider:x.provider,model:x.model,lines:x.lines,ageDays:x.ageDays})),heuristic:h.heuristic,intent:h.intent,ts:i}}),w=!0}}catch(h){o.warn?.(`[rework] ${e.id}: analysis of ${String(n).slice(0,7)} failed: ${h?.message||h}`)}return{recorded:!0,event:p,rework:f,offered:w}}function kE(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 xE(e,t,n){if(!Mc.includes(n)&&n!==Qd)throw new Error(`bad cause "${n}" (${Mc.join("|")}|${Qd})`);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(kE(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 vE(e,t){let n=await K(e);if(!kE(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 rp(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=Ew({heuristic:tp({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";(Fa.has(S)||S==="unknown"&&Fa.has(d.intent))&&Cc(x)!==0&&p.push(x)}let f=p.reduce((g,x)=>g+Cc(x),0);if(f<Tw)return{offered:!1,reason:"below-threshold"};let w=new Map;for(let g of p)for(let x of g.origins??[]){if(x.class!=="rework")continue;let S=w.get(x.key)??{key:x.key,provider:x.provider??null,model:x.model??null,lines:0,ageDays:x.ageDays};S.lines+=x.lines,S.ageDays=Math.min(S.ageDays,x.ageDays),w.set(x.key,S)}let h=p.map(g=>g.sha),y=p.map(g=>g.heuristic).find(g=>g&&g.level!=="C")??tp({subject:r})??p.map(g=>g.heuristic).find(Boolean)??null,k=Fa.has(d.intent)?d:p.find(g=>Fa.has(g.intent?.intent))?.intent??d;return await N(e,{type:"rework.cause.offered",actor:"orchestrator",source:"orchestrator",boxId:null,payload:{sha:h[h.length-1],shas:h,pr:{repo:t,number:n,title:String(r||"").slice(0,200),url:s,branch:o},subject:String(r||"").slice(0,200)||p[p.length-1].subject,linesReworked:f,origins:[...w.values()].sort((g,x)=>x.lines-g.lines).slice(0,3),heuristic:y,intent:k,ts:i}}),{offered:!0,shas:h}}catch(c){return a.warn?.(`[rework] ${e?.id}: PR-level offer for #${n} failed: ${c?.message||c}`),{offered:!1,reason:"error"}}}var m4,_w,op=G(()=>{be();Xu();np();m4=f4(p4),_w=(e,t)=>m4("git",["-C",e,...t],{maxBuffer:16*1024*1024,timeout:3e4})});import{execFile as k4}from"node:child_process";import{promisify as x4}from"node:util";async function Iw(e,t,{midTurn:n=as,pollMs:r=v4,capMs:o=S4}={}){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 IE(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){SE.lastIndex=n;let o=SE.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(`
|
|
202
|
+
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+R4(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 $w(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function RE(e){let t=IE(e),n=t.match(_4);if(!n)return null;let r=$w(t,n.index);return $4.test(r)?null:{repo:r.match($E)?.[1]??null,head:r.match(O4)?.[1]??null}}function OE(e){let t=IE(e),n=t.match(I4);if(!n)return null;let r=$w(t,n.index),o=$w(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("-")){C4.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match($E)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function P4(e,t,{exclude:n=()=>!1}={}){let r=async i=>{Sr("observer");let{stdout:c}=await AE("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",N4],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw yo(l)&&bo(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-A4&&!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 D4(e,t){if(t)return[t];let n=[];for(let r of ME(e)){let o=await Me(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await fr().catch(()=>[])){let o=await Me(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function ME(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 B4(e,t,n){let r=await La(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)||M4(c.headRefName,e.id),i=[];for(let c of await D4(e,t?.repo??null)){let l;try{l=await P4(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await ft(c,u.number);if(s(u.headRefName)){if(d&&d.trackId===e.id&&d.announced)return{settled:!0};if(d&&d.trackId!==e.id)continue;return{slug:c,pr:u}}d&&(d.trackId!==e.id||d.announced)||i.push({slug:c,pr:u})}}return i.length?(i.sort((c,l)=>Date.parse(l.pr.createdAt)-Date.parse(c.pr.createdAt)),i[0]):null}function CE(e){if(!e)return 0;let t=0;for(let n of Tr)n.startsWith(`${e}|`)&&t++;return t}function NE(){for(let e of[...Rw])e()}function PE(e,t,{delays:n=TE,turnEnd:r}={}){let o=RE(t.command);if(!o)return null;let s=!!OE(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(Tr.has(a))return null;Tr.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(h=>{let y=()=>{clearTimeout(k),Rw.delete(y),h()},k=setTimeout(y,l);Rw.add(y)});let u=await B4(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:d,pr:p}=u,{link:f,created:w}=await Yt({repo:d,number:p.number,trackId:e.id,url:p.url,headRef:p.headRefName,title:p.title,source:"observed"});return!w&&!(f?.trackId===e.id&&await xr(d,p.number))?null:(await Iw(t.box,t.agent,r),await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"opened",number:p.number,url:p.url,title:p.title,branch:p.headRefName,repo:d,...s?{selfMerge:!0}:{}}}),p)}return null}finally{Tr.delete(a)}})();return c.catch(()=>Tr.delete(a)),c}function DE(e,t,{delays:n=TE,turnEnd:r,openedWait:o}={}){let s=OE(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(Tr.has(a))return null;Tr.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=ME(e);if(w.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let h of w){let y=await Me(h).catch(()=>null);y&&!d.includes(y)&&d.push(y)}}if(l||=(await La(e).catch(()=>[])).filter(w=>w!==e.workspace?.branch)[0]||e.workspace?.branch,!d.length||!l)return null;c=d[0];let p=async(w,h)=>{let y=await ft(c,w.number);return y&&y.trackId!==e.id?null:(y||await Yt({repo:c,number:w.number,trackId:e.id,url:w.url,headRef:w.headRefName,title:w.title,source:"merge-observed"}),await Tc(c,w.number),h&&((await K(e).catch(()=>[])).some(x=>x.type==="pr.state.changed"&&(x.payload?.change==="auto-armed"||x.payload?.change==="merged")&&x.payload?.number===w.number)||(await Iw(t.box,t.agent,r),await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:w.number,url:w.url,title:w.title,branch:w.headRefName,repo:c}}))),w)},f=null;for(let w of n){await new Promise(M=>setTimeout(M,w));let h=[];for(let M of d)try{Sr("observer");let{stdout:P}=await AE("gh",["pr","view",String(l),"--repo",M,"--json",L4],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await Bn({slug:M})}}),j=JSON.parse(P||"null");j?.number&&h.push({slug:M,pr:j})}catch(P){yo(P)&&bo(P)}let y=h.find(M=>M.pr.state==="MERGED")??(h.length===1?h[0]:null);if(!y)continue;c=y.slug;let k=y.pr;if(!k?.number)continue;if(k.state==="CLOSED")return null;if(k.state!=="MERGED"){if(f=k,s.auto)return p(k,!0);continue}let g=await ft(c,k.number);if(g&&g.trackId!==e.id)return null;g||await Yt({repo:c,number:k.number,trackId:e.id,url:k.url,headRef:k.headRefName,title:k.title,source:"merge-observed"}),await Iw(t.box,t.agent,r);let x=await K(e).catch(()=>[]);if(x.some(M=>M.type==="pr.state.changed"&&M.payload?.change==="merged"&&M.payload?.number===k.number))return null;let S=M=>M.some(P=>P.type==="pr.state.changed"&&P.payload?.change==="opened"&&P.payload?.number===k.number),_=0;if(!S(x)){if(RE(t.command)&&await xr(c,k.number)){let M=Date.now();_=M+1,await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:M,change:"opened",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c,selfMerge:!0}})}else if((await ft(c,k.number))?.announced){let M=o?.capMs??T4,P=o?.pollMs??E4,j=Date.now()+M;for(;Date.now()<j&&(await new Promise(T=>setTimeout(T,P)),!S(await K(e).catch(()=>[]))););}}return await N({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Math.max(Date.now(),_),change:"merged",number:k.number,url:k.url,title:k.title,branch:k.headRefName,repo:c}}),await rp(e,{repo:c,number:k.number,title:k.title,branch:k.headRefName,url:k.url}).catch(()=>{}),await _r(c,k.number).catch(()=>{}),await Er(c,k.number).catch(()=>{}),await wo(c,k.number,"merged"),k}return f?p(f,!1):null}finally{Tr.delete(a)}})();return i.catch(()=>Tr.delete(a)),i}var AE,TE,v4,S4,A4,T4,E4,EE,_E,_4,I4,$4,SE,R4,$E,O4,M4,C4,N4,Tr,Rw,L4,sp=G(()=>{be();Gr();xt();_c();Gn();Ir();xa();vs();op();AE=x4(k4),TE=[4e3,15e3,45e3],v4=2500,S4=5*6e4;A4=15e3,T4=9e4,E4=2e3,EE=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,_E=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,_4=new RegExp(`${EE}${_E}create\\b`),I4=new RegExp(`${EE}${_E}merge\\b`),$4=/\s--web\b/,SE=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,R4=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");$E=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,O4=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,M4=sE;C4=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);N4="number,url,title,headRefName,createdAt";Tr=new Set;Rw=new Set;L4="number,url,title,headRefName,state"});import j4 from"node:fs/promises";import Mw from"node:path";import F4 from"node:os";function Ow(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function ap(e){let t=U4.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=Ow(n,"from"),s=o?o.replace(/^uds:/,""):null,a=BE(s);return{socket:s,pid:a,name:Ow(n,"from-name"),mode:Ow(n,"from-mode"),text:r.trim()}}function BE(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function H4(){return process.env.CLAUDE_CONFIG_DIR?Mw.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):Mw.join(F4.homedir(),".claude","sessions")}async function W4(e){let t=BE(e);if(!t)return null;try{let n=await j4.readFile(Mw.join(H4(),`${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 LE(e,t){let n=await W4(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,Cw=G(()=>{U4=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function jE(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}async function FE(e,t,n=Nw){let r=new AbortController,o=setTimeout(()=>r.abort(),n);try{return await G4(`${De(t)}${e}`,{headers:K4(t),signal:r.signal})}finally{clearTimeout(o)}}async function Nc({env:e=process.env,force:t=!1,timeoutMs:n=Nw}={}){if(!jE(e))return{followups:[],source:"unconfigured"};if(!t&&Ss&&Date.now()-Ss.at<q4)return{followups:Ss.followups,source:"cache"};try{let r=await FE("/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 Ss={at:Date.now(),followups:s},{followups:s,source:"plane"}}catch(r){return Ss?{followups:Ss.followups,source:"stale",error:r?.message}:{followups:[],source:"unreachable",error:r?.message}}}async function UE(e,{env:t=process.env,timeoutMs:n=Nw}={}){if(!e)return{followup:null,source:"unconfigured"};if(!jE(t))return{followup:null,source:"unconfigured"};try{let r=await FE(`/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=Ss?.followups.find(s=>s.id===e)??null;return o?{followup:o,source:"stale",error:r?.message}:{followup:null,source:"unreachable",error:r?.message}}}var q4,Nw,Ss,G4,K4,HE=G(()=>{xt();q4=6e4,Nw=5e3,Ss=null,G4=(...e)=>fetch(...e);K4=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});async function vo(e,t){if(Pw.has(e)){let n=new Error("offer already answered (answer in flight)");throw n.code="answer-in-flight",n}Pw.add(e);try{return await t()}finally{Pw.delete(e)}}var Pw,Pc=G(()=>{Pw=new Set});import{randomUUID as As,createHash as V4}from"node:crypto";function ip(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function XE(e){for(let[t,n]of X4)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 KE(e,{at:t="",promptRequired:n=!0,standing:r=!1}={}){let o=e&&typeof e=="object"?e:{},s=Ts(o.label).replace(/\s+/g," ");if(!s)throw new Error(`${t}label required (\u2264${Dw} chars, e.g. "Deploy to Pi")`);if(s.length>Dw)throw new Error(`${t}label must be \u2264${Dw} characters`);let a=Ts(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")>WE)throw new Error(`${t}prompt must be \u2264${WE} bytes`);if(r){let l=XE(a);if(l)throw new Error(`${t}${l}`)}let i=o.standing==null||o.standing===""?"offerable":String(o.standing).trim().toLowerCase();if(!Y4.includes(i))throw new Error(`${t}standing must be "offerable" or "never", got "${o.standing}"`);let c=Ts(o.why).slice(0,zE)||null;return{label:s,prompt:a,why:c,standing:i}}function ZE(e,{standing:t=!1}={}){let n=e&&typeof e=="object"?e:{},r=Ts(n.trigger)||(n.ship?"now":""),o=t?z4:JE.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"?e2(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>GE)throw new Error(`items must be \u2264${GE} \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)=>KE(f,{at:`items[${w}]: `,standing:t})),c=Ts(n.why).slice(0,zE)||null;let p=new Set;for(let f of i){let w=ip(f.label);if(p.has(w))throw new Error(`items must have distinct labels ("${f.label}" appears twice)`);p.add(w)}}else i=[KE(n,{promptRequired:!s,standing:t})];let l=i[0],u=c??l.why,d=t?Q4(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 Z4(e){return e.standing==="never"?{promotable:!1,promotableReason:"one-off"}:XE(e.prompt)?{promotable:!1,promotableReason:"specifics"}:{promotable:!0,promotableReason:null}}function Q4(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 e2(e){let t=e&&typeof e=="object"?e:{},n=Array.isArray(t.actions)?t.actions.map(i=>String(i).trim().toLowerCase()):Bw.slice();for(let i of n)if(!Bw.includes(i))throw new Error(`ship.actions may only contain ${Bw.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=Ts(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=>Ts(i)).filter(Boolean),a.length||(a=null)}return{actions:[...new Set(n)],rebase:r,toPr:o,base:s,repos:a}}function cp(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 QE(e){return{armId:e.armId,label:e.label,prompt:e.prompt,promptHash:YE(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",...Z4(e)}}async function e_(e,t,{pull:n=Nc,ship:r={}}={}){if(jw(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=ZE(t);if(o.ship)return{kind:"ship",...await s2(e,o,r)};let s={id:e.id,cwd:e.cwd},a=(e.boxes||[]).find(g=>g.name===e.leadBox)||null,i={boxId:e.leadBox??null,provider:a?.agent??null},c=As(),l=(g,x,S={})=>({ts:Date.now(),armId:As(),trigger:o.trigger,label:g.label,prompt:g.prompt,why:g.why,standing:g.standing,groupId:c,seq:x,...o.planWhy?{planWhy:o.planWhy}:{},...S}),u={groupId:c,trigger:o.trigger,label:o.label,prompt:o.prompt,why:o.why,planWhy:o.planWhy};if(o.trigger==="now"){let g=As(),x=[];for(let[S,_]of o.items.entries()){let M=l(_,S);await N(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:M}),x.push(M)}return await N(s,{type:"followup.offered",actor:"system",source:"orchestrator",...i,payload:{ts:Date.now(),offerId:g,trigger:"now",repo:e.workspace?.repo||null,context:{},groupId:c,why:o.planWhy,rows:x.map(QE)}}),{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=cp(f,{trigger:o.trigger}),h=[],y=0;for(let g of o.items){let x=ip(g.label),S=p.find(j=>j.status==="active"&&j.mode!=="off"&&j.trigger===o.trigger&&(j.repo==null||j.repo===d)&&j.dedupeKey===x);if(S){h.push({label:g.label,standing:g.standing,covered:{id:S.id,label:S.label}});continue}let _=w.filter(j=>ip(j.label)===x),M=_.length?_[_.length-1].armId:null,P=l(g,y++,M?{replaces:M}:{});await N(s,{type:"followup.armed",actor:"agent",source:"cli",...i,payload:P}),h.push({armId:P.armId,label:g.label,standing:g.standing,replaces:M})}let k=h.filter(g=>g.armId);return k.length?{kind:"armed",armId:k[0].armId,replaces:k[0].replaces,items:h,...u}:{kind:"covered",covered:h[0].covered,items:h,...u}}async function t_(e,t){if(!t)throw new Error("armId required");let n={id:e.id,cwd:e.cwd},r=await K(n);if(!cp(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 n_(e,t,{pull:n=Nc}={}){if(jw(e))throw new Error("propose from a lead Track, not a loop's run Track");let r=ZE(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=ip(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}" (${Lw[f.trigger]||f.trigger}) already exists as a standing follow-up \u2014 not re-proposed`);if(p){let y=p.deletedAt?new Date(p.deletedAt).toISOString().slice(0,10):"earlier";throw new Error(`the operator deleted "${p.label}" on ${y} \u2014 do not re-propose it`)}let w=As(),h=(e.boxes||[]).find(y=>y.name===e.leadBox)||null;return await N(s,{type:"followup.create.offered",actor:"agent",source:"cli",boxId:e.leadBox??null,provider:h?.agent??null,payload:{ts:Date.now(),proposalId:w,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why,origin:"agent"}}),{proposalId:w,label:r.label,prompt:r.prompt,trigger:r.trigger,repo:o,why:r.why}}async function r_(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 Dc(e,t,n={},{pull:r=Nc}={}){if(!e||jw(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=cp(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,qE).map(QE),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,J4).map(g=>({followupId:g.id,label:g.label,promptPreview:t2(g.prompt),promptHash:YE(g.prompt),origin:g.origin,repo:g.repo??null}))}let w=[...d,...f].slice(0,qE);if(!w.length)return null;let h=As(),y={ts:Date.now(),offerId:h,trigger:t,repo:a,context:{...n,repo:a},groupId:c,why:p,rows:w};return await N(o,{type:"followup.offered",actor:"system",source:"orchestrator",payload:y}),y}function n2(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 VE(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 o_(e,{offerId:t,followupId:n=null,armId:r=null,promoted:o=null,all:s=!1}={},a={}){if(!t)throw new Error("offerId required");return vo(`fu:${e.id}:${t}`,()=>o2(e,{offerId:t,followupId:n,armId:r,promoted:o,all:s===!0},a))}async function o2(e,{offerId:t,followupId:n,armId:r,promoted:o,all:s},a){let{sayToLead:i,unarchiveTrack:c,getFollowup:l=UE}=a,u={id:e.id,cwd:e.cwd},d=await K(u),p=d.find(T=>T.type==="followup.offered"&&T.payload?.offerId===t);if(!p)throw new Error("unknown offer");let f=p.payload.rows||[],w=n2(d,p.payload),h=r2(w,f);if(!h.length)throw new Error("offer already answered");let y=T=>N(u,{type:"followup.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,...T}}),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=T=>{let L=d.find(H=>H.type==="followup.armed"&&H.payload?.armId===T.armId)?.payload?.prompt??T.prompt;if(!L)throw new Error("the arm's prompt is missing from the log");return L};if(s){let T=p.payload.groupId??null,$=h.filter(B=>B.armId&&(!T||B.groupId===T)).sort((B,Z)=>(B.seq??0)-(Z.seq??0));if(!$.length)throw new Error("nothing to run together \u2014 no open items from this plan remain on the card");let L=$.map(B=>({row:B,prompt:g(B)})),H=await k();if(!i)throw new Error("sayToLead unavailable");let R=`${VE(p.payload).replace(/:$/,"")} \u2014 ${L.length} steps, in order:`,A=L.map((B,Z)=>`${Z+1}. ${B.row.label}
|
|
203
203
|
${B.prompt}`).join(`
|
|
204
204
|
|
|
205
205
|
`),C=await i(e.id,`${R}
|
|
206
206
|
|
|
207
|
-
${
|
|
207
|
+
${A}`,{kind:"followup-go"}),W=As();for(let[B,Z]of L.entries())await y({action:"sent",armId:Z.row.armId,label:Z.row.label,batchId:W,...B===0&&H?{revived:!0}:{},source:`arm:${Z.row.armId}`});return{offerId:t,action:"sent",all:!0,batchId:W,armIds:L.map(B=>B.row.armId),revived:H,remaining:h.length-L.length,queued:!!C?.queued}}if(!n&&!r)return await y({action:"dismiss",followupIds:h.map(T=>T.followupId).filter(Boolean),armIds:h.map(T=>T.armId).filter(Boolean)}),{offerId:t,action:"dismiss",remaining:0};let x=null,S=null,_=n?`fu:${n}`:`arm:${r}`;if(n){if(!f.find(H=>H.followupId===n))throw new Error("that follow-up is not on this card");if(w.get(_)!=null)throw new Error("that row is already answered");let{followup:$,source:L}=await l(n);if(L==="unreachable")throw new Error("plane unreachable \u2014 the follow-up's current prompt can't be read; try again shortly");if(!$||$.status!=="active"){await y({action:"removed",followupId:n});let H=new Error("removed in Settings since it was offered");throw H.code="removed",H}x=$.prompt,S=$.label}else{let T=f.find($=>$.armId===r);if(!T)throw new Error("that arm is not on this card");if(w.get(_)!=null)throw new Error("that row is already answered");x=g(T),S=T.label}let M=await k();if(!i)throw new Error("sayToLead unavailable");let P=`${VE(p.payload)}
|
|
208
208
|
|
|
209
|
-
${x}`,
|
|
209
|
+
${x}`,j=await i(e.id,P,{kind:"followup-go"});return await y({action:"sent",...n?{followupId:n}:{armId:r},label:S,...o?{promoted:String(o)}:{},...M?{revived:!0}:{},source:n?`followup:${n}`:`arm:${r}`}),{offerId:t,action:"sent",followupId:n,armId:r,promoted:o,revived:M,remaining:h.length-1,queued:!!j?.queued}}async function s2(e,t,{changedRepos:n=null,probe:r=zd,branchOf:o=ko}={}){let s=t.ship;if(!e.workspace?.branch)throw new Error("this Track has no branch to ship \u2014 a ship offer needs a worktree Track");let a={id:e.id,cwd:e.cwd},i=(e.boxes||[]).find(k=>k.name===e.leadBox)||null,c=e.workspace?.kind==="multi",l;if(c&&n){let k=await n(e).catch(()=>[]),g=s.repos?k.filter(x=>s.repos.includes(x.name)):k;l=g.length?g.map(x=>({name:x.name,slug:x.repo??null})):void 0}let u=(c?null:await o(e.cwd).catch(()=>null))||e.workspace.branch,d=s.rebase==="offered",p=await r(e,{branch:u,rebase:d}).catch(()=>null),f=[...d?["rebase"]:[],...s.actions].filter(k=>ho.includes(k));if(p&&p.available.length===0)return{skipped:!0,reason:"nothing to ship \u2014 the tree is clean, pushed, and its PR is open",shipState:p};let w=As(),h=t.why?`${t.label} \u2014 ${t.why}`:t.label,y=Fw(await K(a).catch(()=>[]));return await N(a,{type:"pr.offered",actor:"agent",source:"cli",boxId:e.leadBox??null,provider:i?.agent??null,payload:{offerId:w,ts:Date.now(),excerpt:h,via:"cli",branch:u,base:s.base||e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:"lead",available:p?.available,defaults:p?p.defaults.filter(k=>f.includes(k)||k==="rebase"):void 0,prState:p?.prState,openPr:p?.openPr||void 0,repos:p?.repos||l,offered:p?f.filter(k=>p.available.includes(k)):f,toPr:s.toPr?{number:s.toPr}:void 0}}),{offerId:w,branch:u,offered:f,available:p?.available??null,prState:p?.prState??null,replaced:y}}function Fw(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 s_(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 a_(e,{pull:t=Nc}={}){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?cp(s):[]}}var Dw,WE,zE,JE,z4,Lw,J4,qE,GE,Y4,Bw,YE,Ts,X4,jw,t2,r2,lp=G(()=>{be();ws();bw();vs();HE();Pc();Dw=60,WE=8*1024,zE=200,JE=["now","pr.merged","ship.done","pr.opened","checks.green","track.opened"],z4=JE.filter(e=>e!=="now"&&e!=="track.opened"),Lw={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"},J4=3,qE=6,GE=5,Y4=["offerable","never"],Bw=["commit","push","pr"];YE=e=>V4("sha256").update(String(e)).digest("hex").slice(0,16),Ts=e=>typeof e=="string"?e.trim():"",X4=[[/(^|[^\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"]];jw=e=>String(e?.name||"").startsWith("\u21BB");t2=e=>String(e||"").split(`
|
|
210
210
|
`).slice(0,3).join(`
|
|
211
|
-
`).slice(0,400);
|
|
212
|
-
`)){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 s2(e,t){let n=`${e.id}:${t.name}`;if(!Ww.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);Ww.set(n,o)}return Ww.get(n)}async function p_(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await Xo(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await s2(e,t),o=o2(await jt(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||t.model||"gpt-5.5";await N(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:Wn(c,i,a.ts),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await N(e,{type:"context.usage",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{occupancy:a.occupancy,first:null,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}var Ww,f_=q(()=>{be();Qo();vn();yr();Ww=new Map});import dp from"node:fs";import Gw from"node:path";import a2 from"node:os";async function l2({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(c2,{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:m_})});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<m_)break}return r}function u2(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 w_(){try{return JSON.parse(dp.readFileSync(pp(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function p2(e){try{dp.mkdirSync(Gw.dirname(pp()),{recursive:!0});let t=pp()+".tmp";dp.writeFileSync(t,JSON.stringify(e)),dp.renameSync(t,pp())}catch{}}async function m2({fetchImpl:e,now:t=Date.now()}={}){if(So.rows&&t-So.at<d2)return So.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=w_(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await l2({sinceMs:o,cookie:n,fetchImpl:e});So={at:t,rows:s.map(u2).filter(Boolean),error:null}}catch(s){So={at:t,rows:null,error:String(s?.message||s)}}return So.rows}async function h2(e,t){let n=`${e.id}:${t.name}`;if(!qw.has(n)){let r=await K(e),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===t.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);qw.set(n,o)}return qw.get(n)}async function g_(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!h_())return;let r=await m2({fetchImpl:n});if(!r||!r.length)return;let o=await xS(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await h2(e,t),a=w_(),i=new Set(a.seen||[]),c=a.sinceMs||0;for(let l of r){if(!l.conversationId||l.conversationId!==o.chatId||s.has(l.id)||i.has(`${e.id}:${t.name}:${l.id}`))continue;s.add(l.id),i.add(`${e.id}:${t.name}:${l.id}`),l.ts&&l.ts>c&&(c=l.ts);let u=l.model||o.models[o.models.length-1]||"cursor-unknown",d=l.tokens||null,p=l.vendorCents!=null?l.vendorCents/100:0;!p&&d&&(p=Wn(u,d,l.ts??void 0)),!p&&l.chargedCents!=null&&(p=l.chargedCents/100),await N(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.name,provider:"cursor",payload:{usd:p,model:u,...d?{tokens:d}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}p2({sinceMs:c,seen:[...i].slice(-f2)})}catch{}}var i2,h_,c2,m_,d2,So,pp,f2,Kw,qw,Vw=q(()=>{be();Sh();yr();i2=process.env.BOSTRAT_STATE_DIR||Gw.join(process.env.HOME||a2.tmpdir(),".bostrat"),h_=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,c2="https://cursor.com/api/dashboard/get-filtered-usage-events",m_=100;d2=5*6e4,So={at:0,rows:null,error:null},pp=()=>Gw.join(i2,"cursor-usage.json");f2=5e3;Kw=()=>({enabled:h_(),lastPollAt:So.at||null,error:So.error}),qw=new Map});import{randomUUID as w2,createHmac as g2}from"node:crypto";function b_(e){Jw=typeof e=="string"&&e?e:null}function y2(e){if(!Jw)return null;let t=He(e);return t?g2("sha256",Buffer.from(Jw,"base64url")).update(t,"utf8").digest("hex").slice(0,16):null}function v_(){let e={};for(let t of x_)e[String(t)]=0;return e.inf=0,{buckets:e,sumMs:0,count:0}}function y_(e,t){let n=Math.max(0,Number(t)||0);for(let r of x_)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 k_)e[t]=0;return e}function k2(){zn={total:new Map,holds:S_(),queueWaitMs:v_(),readyWaitMs:new Map,verify:new Map,retries:new Map}}function A_({queueDepth:e=0,queueOldestAgeMs:t=0}={}){let n=zn;return{total:[...n.total].map(([r,o])=>{let[s,a,i]=r.split("|");return{provider:s,producer:a,outcome:i,n:o}}),holds:{...n.holds},queueWaitMs:{buckets:{...n.queueWaitMs.buckets},sumMs:n.queueWaitMs.sumMs,count:n.queueWaitMs.count},readyWaitMs:[...n.readyWaitMs].map(([r,o])=>{let[s,a]=r.split("|");return{provider:s,outcome:a,buckets:{...o.buckets},sumMs:o.sumMs,count:o.count}}),verify:[...n.verify].map(([r,o])=>{let[s,a,i]=r.split("|");return{provider:s,verdict:a,cliVersion:i||null,n:o}}),retries:[...n.retries].map(([r,o])=>({provider:r,n:o})),queueDepth:Number(e)||0,queueOldestAgeMs:Number(t)||0}}function jc({kind:e=null,producer:t=null,orchestration:n=!1}={}){return n?`orchestration:${e||"orchestration"}`:e?`scripted:${e}`:b2.has(t)?t:"composer"}function Fc({text:e="",producer:t="composer",kind:n=null,provider:r=null,attachments:o=0,now:s=Date.now}={}){let a=s();return{id:w2(),producer:t,kind:n||null,provider:r||null,textBytes:Buffer.byteLength(String(e||""),"utf8"),textHmac16:y2(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 Uc(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 T_(e,t){k_.includes(t)&&(e?.holds&&(e.holds[t]+=1),zn.holds[t]+=1)}function Hc(e,{ms:t,outcome:n}){e&&(e.readyWaitMs=Math.max(0,Number(t)||0),e.readyOutcome=n||null)}function x2(e){let t=e?.boxes?.find?.(n=>n.name===e.leadBox);return typeof t?.cliVersion=="string"&&t.cliVersion?t.cliVersion:null}async function In(e,t,n,{now:r=Date.now}={}){if(!t||t.settled)return null;t.settled=!0,t.outcome=n;let o=r(),s=t.provider||"unknown",a=t.queuedAt!=null,i=a?Math.max(0,(t.dequeuedAt??o)-t.queuedAt):null;if(zw(zn.total,`${s}|${t.producer}|${n}`),a&&y_(zn.queueWaitMs,i),t.readyWaitMs!=null){let u=`${s}|${t.readyOutcome||"unknown"}`;zn.readyWaitMs.has(u)||zn.readyWaitMs.set(u,v_()),y_(zn.readyWaitMs.get(u),t.readyWaitMs)}let c=x2(e);t.verify?.verdict&&(zw(zn.verify,`${s}|${t.verify.verdict}|${c||""}`),t.verify.retries&&zw(zn.retries,s,t.verify.retries));let l={ts:o,deliveryId:t.id,producer:t.producer,...t.kind?{kind:t.kind}:{},cliVersion:c,textBytes:t.textBytes,textHmac16:t.textHmac16,paste:t.paste,attachments:t.attachments,outcome:n,queued:a,queueWaitMs:i,queueHolds:{...t.holds},requeued:t.requeued,readyWaitMs:t.readyWaitMs,readyOutcome:t.readyOutcome,verify:t.verify?{...t.verify}:null,receiptMs:null,totalMs:Math.max(0,o-t.t0)};return e&&await N(e,{type:"delivery.settled",actor:"system",source:"orchestrator",boxId:e.leadBox??null,provider:t.provider,payload:l}).catch(()=>{}),l}function Ua(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 Jw,k_,x_,b2,zn,zw,Yw=q(()=>{be();uc();Jw=null;k_=["busy","prompt","handoff","noSession","cap","grace"],x_=[500,2e3,1e4,6e4,6e5],b2=new Set(["composer","voice","loop"]);zn=null;k2();zw=(e,t,n=1)=>e.set(t,(e.get(t)||0)+n)});import Gc from"node:fs/promises";import Ut from"node:path";import v2 from"node:os";import S2 from"node:net";import{randomUUID as A2}from"node:crypto";import{execFile as T2}from"node:child_process";import{promisify as E2}from"node:util";function C2(e){for(let t of rg(e))if(!O_.test(t))return t;return null}function M_(e){let t=rg(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function N2(e,t){let n=M_(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Ut.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return Ut.resolve(e,n[r+1])}return e}function D2(e){let t=!1,n=!1;for(let r of rg(e)){if(!t){if(O_.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&Xw(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,Xw(r))return`serves the filesystem root ("${r}")`;continue}if(Xw(r))return`serves the filesystem root ("${r}")`;if(P2(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function L2(e,t){if(Ut.resolve(e)===Ut.resolve(t))return!0;let[n,r]=await Promise.all([fp(e),fp(t)]);return n===r}async function j2(e){let n=(await Gc.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 C_(Ut.join(e,o.name,eg))?`./${o.name}`:null))).filter(Boolean)}async function F2(e,t){let[n,r]=await Promise.all([fp(e),fp(t)]),o=Ut.relative(n,r);if(o&&(o.startsWith("..")||Ut.isAbsolute(o)))throw Wc(`serve directory escapes the Track workspace: ${t}`)}async function U2(e,t){let n=D2(t);if(n)throw Wc(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await B2(e))throw Wc(`serve directory does not exist: ${e}`);let r=C2(t);if(!r)return;if(!r.includes("/")&&!await nx(r,{loginShell:!0,failOpen:!0}))throw Wc(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(R2.test(t)||O2.has(r)||!$2.has(r)||!M_(t).includes("run")||M2.test(t))return;let o=N2(e,t);if(await C_(Ut.join(o,eg)))return;let s=await j2(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Wc(`no ${eg} in ${o}${a}`)}async function Or(){return Jn||(Zw||=(async()=>{try{Jn=JSON.parse(await Gc.readFile(R_,"utf8"))}catch{Jn={views:[],adoptWatermarks:{}}}finally{Zw=null}return Jn.views||=[],Jn.adoptWatermarks||={},Jn})(),Zw)}async function Xn(){await Bt(R_,Jn)}function Rr(e,t,n={}){return N({id:e.trackId,cwd:e.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,payload:{ts:Date.now(),change:t,viewId:e.id,name:e.name,slug:e.slug,port:e.port,cmd:e.cmd||null,...n}}).catch(()=>{})}function sg(e){return qc.get(e)||null}function P_(e,{name:t,box:n}={}){let r=qc.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 qc.set(e,o),o}function tg(e,t){let n=qc.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function mp(e,{error:t=null}={}){let n=qc.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function D_(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 ng(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function H2(e,t){let n=ng(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!I_.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!I_.has(s))return s}}function Kc(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=S2.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 ag(e){if(process.platform!=="darwin"){await Yn("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await Yn("lsof",["-t","-sTCP:LISTEN","-i",`tcp:${e}`]))}catch{return}for(let n of t.split(/\s+/).filter(Boolean)){let r=Number(n);if(!(!r||r===process.pid))try{process.kill(r,"SIGKILL")}catch{}}}function B_(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function W2(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=E_;o<=__;o++)if(!(n.has(o)||r.has(o))&&!await Kc(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${E_}-${__}`)}async function q2(e){try{let{stdout:t}=await Yn(oe,se("list-sessions","-F","#{session_name}"));return t.split(`
|
|
213
|
-
`).some(n=>n.trim()===Ha(e))}catch{return!1}}async function Vc(e){try{await Yn(oe,se("kill-session","-t",Ha(e)))}catch{}}async function ig(e){if(!await Ji())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=Ha(e.slug),n=e.serveCwd??e.cwd,r=Ut.dirname(process.execPath);await Yn(oe,se("new-session","-d","-s",t,"-c",n)),await Yn(oe,se("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await Yn(oe,se("send-keys","-t",t,"Enter")),await Yn(oe,se("send-keys","-t",t,"-l",e.cmd)),await Yn(oe,se("send-keys","-t",t,"Enter"))}async function
|
|
211
|
+
`).slice(0,400);r2=(e,t)=>t.filter(n=>e.get(n.armId?`arm:${n.armId}`:`fu:${n.followupId}`)==null)});import{randomUUID as a2}from"node:crypto";function Bc(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function i_(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function c_(e){return`job|${e.box}|id:${e.id}`}function l_(e){return`jobstop|${e.box}|id:${e.id}`}async function Hw(e){return Uw.has(e.id)?Uw.get(e.id):(up.has(e.id)||up.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(Bc({role:o,sourceId:r.payload.sourceId})),n.keys.add(Bc({role:o,ts:r.payload.ts,text:r.payload.text}))}else if(r.type==="peer.message"&&r.source==="transcript")r.payload?.sourceId&&n.keys.add(Bc({role:"user",sourceId:r.payload.sourceId})),n.keys.add(Bc({role:"user",ts:r.payload?.ts,text:r.payload?.keyText??""}));else if(r.type==="tool.called"&&r.source==="transcript"&&r.payload?.command){let o=r.payload;n.keys.add(i_({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(c_({id:r.payload.id,box:r.boxId})):r.type==="job.stopped"&&r.payload?.id&&n.keys.add(l_({id:r.payload.id,box:r.boxId}));return Uw.set(e.id,n),n}).finally(()=>up.delete(e.id))),up.get(e.id))}async function u_(e,t){let n=new Set;try{let r=(await Hw(e)).keys;for(let o of t||[]){if(!o||o.ts==null||!String(o.text||"").trim()&&!o.images?.length)continue;let s=Bc(o);if(r.has(s))continue;r.add(s);let a=o.role==="user"?ap(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 LE(a,Lc),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=Rx(o);if(i){await N(e,{type:"provider.error",actor:"system",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{cls:i.cls,code:i.code||void 0,status:i.status||void 0,requestId:i.requestId||void 0,text:o.text,ts:o.ts,model:o.model||null,boxRole:o.boxRole,sourceId:o.sourceId||void 0}}),kc(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=oT(o.text);if(c&&Fw(await K(e).catch(()=>[])))continue;if(c){let l=iT(c),u;if(e.workspace?.kind==="multi"){let w=await Es(e).catch(()=>[]);u=w.length?w.map(h=>({name:h.name,slug:h.repo??null})):void 0}let d=(e.workspace?.kind==="multi"?null:await ko(e.cwd).catch(()=>null))||e.workspace.branch,p=await zd(e,{branch:d,rebase:l}).catch(()=>null),f=p?null:aT(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:a2(),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 d_(e,t){try{let n=await Hw(e),r=[...t||[]].filter(o=>o&&String(o.command||"").trim()).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=i_(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}}),PE(e,o),DE(e,o)}}catch{}}async function p_(e,t){try{let n=await Hw(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=c_(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=l_(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 Uw,up,f_=G(()=>{be();ws();bw();sp();vs();Cw();Ir();ju();xd();lp();Uw=new Map,up=new Map});function i2(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
212
|
+
`)){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 c2(e,t){let n=`${e.id}:${t.name}`;if(!Ww.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);Ww.set(n,o)}return Ww.get(n)}async function m_(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await Xo(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await c2(e,t),o=i2(await jt(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||t.model||"gpt-5.5";await N(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:Wn(c,i,a.ts),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await N(e,{type:"context.usage",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{occupancy:a.occupancy,first:null,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}var Ww,h_=G(()=>{be();Qo();vn();yr();Ww=new Map});import dp from"node:fs";import Gw from"node:path";import l2 from"node:os";async function p2({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(d2,{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:w_})});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<w_)break}return r}function f2(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 y_(){try{return JSON.parse(dp.readFileSync(pp(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function h2(e){try{dp.mkdirSync(Gw.dirname(pp()),{recursive:!0});let t=pp()+".tmp";dp.writeFileSync(t,JSON.stringify(e)),dp.renameSync(t,pp())}catch{}}async function g2({fetchImpl:e,now:t=Date.now()}={}){if(So.rows&&t-So.at<m2)return So.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=y_(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await p2({sinceMs:o,cookie:n,fetchImpl:e});So={at:t,rows:s.map(f2).filter(Boolean),error:null}}catch(s){So={at:t,rows:null,error:String(s?.message||s)}}return So.rows}async function y2(e,t){let n=`${e.id}:${t.name}`;if(!qw.has(n)){let r=await K(e),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===t.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);qw.set(n,o)}return qw.get(n)}async function b_(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!g_())return;let r=await g2({fetchImpl:n});if(!r||!r.length)return;let o=await SS(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await y2(e,t),a=y_(),i=new Set(a.seen||[]),c=a.sinceMs||0;for(let l of r){if(!l.conversationId||l.conversationId!==o.chatId||s.has(l.id)||i.has(`${e.id}:${t.name}:${l.id}`))continue;s.add(l.id),i.add(`${e.id}:${t.name}:${l.id}`),l.ts&&l.ts>c&&(c=l.ts);let u=l.model||o.models[o.models.length-1]||"cursor-unknown",d=l.tokens||null,p=l.vendorCents!=null?l.vendorCents/100:0;!p&&d&&(p=Wn(u,d,l.ts??void 0)),!p&&l.chargedCents!=null&&(p=l.chargedCents/100),await N(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.name,provider:"cursor",payload:{usd:p,model:u,...d?{tokens:d}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}h2({sinceMs:c,seen:[...i].slice(-w2)})}catch{}}var u2,g_,d2,w_,m2,So,pp,w2,Kw,qw,Vw=G(()=>{be();Sh();yr();u2=process.env.BOSTRAT_STATE_DIR||Gw.join(process.env.HOME||l2.tmpdir(),".bostrat"),g_=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,d2="https://cursor.com/api/dashboard/get-filtered-usage-events",w_=100;m2=5*6e4,So={at:0,rows:null,error:null},pp=()=>Gw.join(u2,"cursor-usage.json");w2=5e3;Kw=()=>({enabled:g_(),lastPollAt:So.at||null,error:So.error}),qw=new Map});import{randomUUID as b2,createHmac as k2}from"node:crypto";function x_(e){Jw=typeof e=="string"&&e?e:null}function x2(e){if(!Jw)return null;let t=He(e);return t?k2("sha256",Buffer.from(Jw,"base64url")).update(t,"utf8").digest("hex").slice(0,16):null}function A_(){let e={};for(let t of S_)e[String(t)]=0;return e.inf=0,{buckets:e,sumMs:0,count:0}}function k_(e,t){let n=Math.max(0,Number(t)||0);for(let r of S_)n<=r&&(e.buckets[String(r)]+=1);e.buckets.inf+=1,e.sumMs+=n,e.count+=1}function T_(){let e={};for(let t of v_)e[t]=0;return e}function S2(){zn={total:new Map,holds:T_(),queueWaitMs:A_(),readyWaitMs:new Map,verify:new Map,retries:new Map}}function E_({queueDepth:e=0,queueOldestAgeMs:t=0}={}){let n=zn;return{total:[...n.total].map(([r,o])=>{let[s,a,i]=r.split("|");return{provider:s,producer:a,outcome:i,n:o}}),holds:{...n.holds},queueWaitMs:{buckets:{...n.queueWaitMs.buckets},sumMs:n.queueWaitMs.sumMs,count:n.queueWaitMs.count},readyWaitMs:[...n.readyWaitMs].map(([r,o])=>{let[s,a]=r.split("|");return{provider:s,outcome:a,buckets:{...o.buckets},sumMs:o.sumMs,count:o.count}}),verify:[...n.verify].map(([r,o])=>{let[s,a,i]=r.split("|");return{provider:s,verdict:a,cliVersion:i||null,n:o}}),retries:[...n.retries].map(([r,o])=>({provider:r,n:o})),queueDepth:Number(e)||0,queueOldestAgeMs:Number(t)||0}}function jc({kind:e=null,producer:t=null,orchestration:n=!1}={}){return n?`orchestration:${e||"orchestration"}`:e?`scripted:${e}`:v2.has(t)?t:"composer"}function Fc({text:e="",producer:t="composer",kind:n=null,provider:r=null,attachments:o=0,now:s=Date.now}={}){let a=s();return{id:b2(),producer:t,kind:n||null,provider:r||null,textBytes:Buffer.byteLength(String(e||""),"utf8"),textHmac16:x2(e),attachments:Number(o)||0,paste:null,t0:a,queuedAt:null,dequeuedAt:null,holds:T_(),requeued:0,readyWaitMs:null,readyOutcome:null,verify:null,settled:!1,outcome:null}}async function Uc(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 __(e,t){v_.includes(t)&&(e?.holds&&(e.holds[t]+=1),zn.holds[t]+=1)}function Hc(e,{ms:t,outcome:n}){e&&(e.readyWaitMs=Math.max(0,Number(t)||0),e.readyOutcome=n||null)}function A2(e){let t=e?.boxes?.find?.(n=>n.name===e.leadBox);return typeof t?.cliVersion=="string"&&t.cliVersion?t.cliVersion:null}async function In(e,t,n,{now:r=Date.now}={}){if(!t||t.settled)return null;t.settled=!0,t.outcome=n;let o=r(),s=t.provider||"unknown",a=t.queuedAt!=null,i=a?Math.max(0,(t.dequeuedAt??o)-t.queuedAt):null;if(zw(zn.total,`${s}|${t.producer}|${n}`),a&&k_(zn.queueWaitMs,i),t.readyWaitMs!=null){let u=`${s}|${t.readyOutcome||"unknown"}`;zn.readyWaitMs.has(u)||zn.readyWaitMs.set(u,A_()),k_(zn.readyWaitMs.get(u),t.readyWaitMs)}let c=A2(e);t.verify?.verdict&&(zw(zn.verify,`${s}|${t.verify.verdict}|${c||""}`),t.verify.retries&&zw(zn.retries,s,t.verify.retries));let l={ts:o,deliveryId:t.id,producer:t.producer,...t.kind?{kind:t.kind}:{},cliVersion:c,textBytes:t.textBytes,textHmac16:t.textHmac16,paste:t.paste,attachments:t.attachments,outcome:n,queued:a,queueWaitMs:i,queueHolds:{...t.holds},requeued:t.requeued,readyWaitMs:t.readyWaitMs,readyOutcome:t.readyOutcome,verify:t.verify?{...t.verify}:null,receiptMs:null,totalMs:Math.max(0,o-t.t0)};return e&&await N(e,{type:"delivery.settled",actor:"system",source:"orchestrator",boxId:e.leadBox??null,provider:t.provider,payload:l}).catch(()=>{}),l}function Ua(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 Jw,v_,S_,v2,zn,zw,Yw=G(()=>{be();uc();Jw=null;v_=["busy","prompt","handoff","noSession","cap","grace"],S_=[500,2e3,1e4,6e4,6e5],v2=new Set(["composer","voice","loop"]);zn=null;S2();zw=(e,t,n=1)=>e.set(t,(e.get(t)||0)+n)});import Gc from"node:fs/promises";import Ut from"node:path";import T2 from"node:os";import E2 from"node:net";import{randomUUID as _2}from"node:crypto";import{execFile as I2}from"node:child_process";import{promisify as $2}from"node:util";function D2(e){for(let t of rg(e))if(!C_.test(t))return t;return null}function N_(e){let t=rg(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function B2(e,t){let n=N_(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Ut.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return Ut.resolve(e,n[r+1])}return e}function j2(e){let t=!1,n=!1;for(let r of rg(e)){if(!t){if(C_.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&Xw(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,Xw(r))return`serves the filesystem root ("${r}")`;continue}if(Xw(r))return`serves the filesystem root ("${r}")`;if(L2(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function U2(e,t){if(Ut.resolve(e)===Ut.resolve(t))return!0;let[n,r]=await Promise.all([fp(e),fp(t)]);return n===r}async function H2(e){let n=(await Gc.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 P_(Ut.join(e,o.name,eg))?`./${o.name}`:null))).filter(Boolean)}async function W2(e,t){let[n,r]=await Promise.all([fp(e),fp(t)]),o=Ut.relative(n,r);if(o&&(o.startsWith("..")||Ut.isAbsolute(o)))throw Wc(`serve directory escapes the Track workspace: ${t}`)}async function q2(e,t){let n=j2(t);if(n)throw Wc(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await F2(e))throw Wc(`serve directory does not exist: ${e}`);let r=D2(t);if(!r)return;if(!r.includes("/")&&!await nx(r,{loginShell:!0,failOpen:!0}))throw Wc(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(C2.test(t)||N2.has(r)||!M2.has(r)||!N_(t).includes("run")||P2.test(t))return;let o=B2(e,t);if(await P_(Ut.join(o,eg)))return;let s=await H2(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Wc(`no ${eg} in ${o}${a}`)}async function Or(){return Jn||(Zw||=(async()=>{try{Jn=JSON.parse(await Gc.readFile(M_,"utf8"))}catch{Jn={views:[],adoptWatermarks:{}}}finally{Zw=null}return Jn.views||=[],Jn.adoptWatermarks||={},Jn})(),Zw)}async function Xn(){await Bt(M_,Jn)}function Rr(e,t,n={}){return N({id:e.trackId,cwd:e.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,payload:{ts:Date.now(),change:t,viewId:e.id,name:e.name,slug:e.slug,port:e.port,cmd:e.cmd||null,...n}}).catch(()=>{})}function sg(e){return qc.get(e)||null}function B_(e,{name:t,box:n}={}){let r=qc.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 qc.set(e,o),o}function tg(e,t){let n=qc.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function mp(e,{error:t=null}={}){let n=qc.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function L_(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 ng(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function G2(e,t){let n=ng(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!R_.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!R_.has(s))return s}}function Kc(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=E2.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 ag(e){if(process.platform!=="darwin"){await Yn("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await Yn("lsof",["-t","-sTCP:LISTEN","-i",`tcp:${e}`]))}catch{return}for(let n of t.split(/\s+/).filter(Boolean)){let r=Number(n);if(!(!r||r===process.pid))try{process.kill(r,"SIGKILL")}catch{}}}function j_(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function K2(e,t){let n=new Set(e.views.map(o=>o.port)),r=j_();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=I_;o<=$_;o++)if(!(n.has(o)||r.has(o))&&!await Kc(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${I_}-${$_}`)}async function V2(e){try{let{stdout:t}=await Yn(oe,se("list-sessions","-F","#{session_name}"));return t.split(`
|
|
213
|
+
`).some(n=>n.trim()===Ha(e))}catch{return!1}}async function Vc(e){try{await Yn(oe,se("kill-session","-t",Ha(e)))}catch{}}async function ig(e){if(!await Ji())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=Ha(e.slug),n=e.serveCwd??e.cwd,r=Ut.dirname(process.execPath);await Yn(oe,se("new-session","-d","-s",t,"-c",n)),await Yn(oe,se("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await Yn(oe,se("send-keys","-t",t,"Enter")),await Yn(oe,se("send-keys","-t",t,"-l",e.cmd)),await Yn(oe,se("send-keys","-t",t,"Enter"))}async function O_(e,t=12){try{let{stdout:n}=await Yn(oe,se("capture-pane","-t",Ha(e),"-p"));return String(n||"").trimEnd().split(`
|
|
214
214
|
`).filter(Boolean).slice(-t).join(`
|
|
215
|
-
`)}catch{return""}}async function K2(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await q2(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await $_(e.slug)||"server exited before listening"),$r.delete(e.id),await Rr(e,e.state,e.error?{error:e.error}:{}),!0}if(await Kc(e.port))return $r.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await Rr(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>G2?(e.state="error",e.error=await $_(e.slug)||"never started listening",await Rr(e,"error",{error:e.error}),!0):!1;let o=($r.get(e.id)||0)+1;return $r.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function hp(){if(!Qw){Qw=!0;try{let e=await Or(),t=!1;for(let n of e.views)await K2(n)&&(t=!0);t&&await Xn()}finally{Qw=!1}}}async function cg({trackId:e}={}){await hp();let t=await Or();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function L_(e){let n=(await Or()).views.find(r=>r.id===e);return n?{...n}:null}function lg(e){return Jn&&Jn.views.find(t=>t.slug===e)||null}async function ug({trackId:e,name:t,cmd:n,cwd:r,serveCwd:o,port:s,box:a,origin:i="agent",branch:c=null}){if(!e)throw new Error("trackId required");if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(i!=="detected"&&(typeof n!="string"||!n.trim()))throw new Error("cmd (string) required");if(!r)throw new Error("cwd required");let l=o!=null,u=Ut.resolve(r,o??"."),d=i==="detected"?null:async()=>{await F2(r,u),await U2(u,n)},p=await Or();if(i!=="detected"){let h=ng(t),y=String(n).trim(),k=p.views.find(g=>g.trackId===e&&(g.slug===h||ng(g.name)===h||g.cmd===y&&Ut.resolve(g.serveCwd??g.cwd)===u));if(k)return V2(p,k,{cmd:y,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),p.views.filter(h=>h.state==="live"||h.state==="starting").length>=Ao)throw new Error(`view cap reached (${Ao} live) \u2014 stop one first`);let w={id:A2().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:H2(p,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await W2(p,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await ig(w),p.views.push(w),await Xn(),await Rr(w,"created",{origin:i}),tg(e,w.id),i==="detected"&&await Kc(w.port)&&(w.state="live",w.lastHealthyAt=Date.now(),await Xn(),await Rr(w,"live")),{...w}}async function V2(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 L2(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Kc(t.port))return await Xn(),tg(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>=Ao)throw new Error(`view cap reached (${Ao} live) \u2014 stop one first`);return t.origin==="detected"?(await ag(t.port),t.origin="agent"):await Vc(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),$r.delete(t.id),await ig(t),await Xn(),await Rr(t,"created",{restarted:!0,reused:!0}),tg(t.trackId,t.id),{...t,reused:!0}}async function j_(e){let n=(await Or()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await ag(n.port):await Vc(n.slug),n.state!=="stopped"&&(n.state="stopped",$r.delete(n.id),await Xn(),await Rr(n,"stopped")),{...n}}async function F_(e){let t=await Or(),n=t.views.find(o=>o.id===e);if(!n)throw new Error("view not found");if(!n.cmd)throw new Error("a detected view has no known command \u2014 recreate it as a managed view");if(t.views.filter(o=>o.id!==e&&(o.state==="live"||o.state==="starting")).length>=Ao)throw new Error(`view cap reached (${Ao} live) \u2014 stop one first`);return await Vc(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),$r.delete(n.id),await ig(n),await Xn(),await Rr(n,"created",{restarted:!0}),{...n}}async function U_(e){let t=await Or(),n=t.views.findIndex(o=>o.id===e);if(n===-1)throw new Error("view not found");let r=t.views[n];return r.origin==="detected"?await ag(r.port):await Vc(r.slug),t.views.splice(n,1),$r.delete(r.id),await Xn(),await Rr(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function dg(e){let t=await Or(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Vc(r.slug),$r.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await Xn()),n.length}async function H_(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await Or(),r=n.adoptWatermarks[e.id]??null,o=t.filter(c=>c.role==="assistant"&&c.ts!=null&&(r==null?!1:c.ts>r)),s=t.reduce((c,l)=>l.ts>c?l.ts:c,r??0);if(n.adoptWatermarks[e.id]!==s&&(n.adoptWatermarks[e.id]=s,await Xn()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(z2)){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 Kc(c))try{i.push(await ug({trackId:e.id,name:`${e.name} preview`,cmd:null,cwd:e.cwd,port:c,origin:"detected",branch:e.workspace?.branch??null}))}catch{}return i}var Yn,_2,R_,E_,__,Ao,I2,Ha,I_,Wc,$2,eg,R2,O2,M2,rg,O_,Xw,P2,B2,C_,fp,Jn,Zw,qc,og,N_,$r,G2,Qw,z2,wp=q(()=>{pt();be();zr();Yn=E2(T2),_2=process.env.BOSTRAT_STATE_DIR||Ut.join(process.env.HOME||v2.tmpdir(),".bostrat"),R_=Ut.join(_2,"views.json"),[E_,__]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),Ao=Number(process.env.BOSTRAT_VIEW_MAX)||3,I2="bostrat-view-",Ha=e=>`${I2}${e}`,I_=new Set(["bostrat","www","api","views","app"]),Wc=e=>Object.assign(new Error(e),{status:400}),$2=new Set(["npm","pnpm","yarn","bun"]),eg="package.json",R2=/[|;&`]|\$\(/,O2=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),M2=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,rg=e=>String(e).trim().split(/\s+/).filter(Boolean),O_=/^[A-Za-z_][A-Za-z0-9_]*=/;Xw=e=>e==="/",P2=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");B2=e=>Gc.stat(e).then(t=>t.isDirectory(),()=>!1),C_=e=>Gc.stat(e).then(t=>t.isFile(),()=>!1),fp=e=>Gc.realpath(e).catch(()=>Ut.resolve(e));Jn=null,Zw=null;qc=new Map,og=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,N_=12e3;$r=new Map,G2=12e4;Qw=!1;z2=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});import{execFile as J2}from"node:child_process";import{promisify as Y2}from"node:util";function Q2(e){return q_(e?.toPr?.number??e?.openPr?.number)}function q_(e){if(e==null||e==="")return null;let t=Number(e);return Number.isFinite(t)&&t>0?t:null}function eH(e){return Array.isArray(e?.actions)?e.actions.includes("push")||e.actions.includes("pr"):e?.action==="push"||e?.action==="pr"}function Wa(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=Q2(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&&eH(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:q_(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>pg?null:n}function tH(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function yp(e,{bypassThrottle:t=!1}={}){let n=e?.workspace?.branch;if(!n)return;let r;try{r=await K(e)}catch{return}let o=Wa(r);if(o==null)return;let s=(e.boxes||[]).find(d=>d.name===e.leadBox);if(await as(e.leadBox,s?.agent).catch(()=>!1))return;let a=Date.now();if(!t&&a<(zc.get(e.id)||0))return;zc.set(e.id,a+W_);let i;try{i=await Kn(e.cwd,{caller:"reconcile"})}catch{return}let c=await La(e).catch(()=>[n]),l=o.toPr!=null&&(i||[]).find(d=>Number(d.number)===o.toPr&&c.includes(d.headRefName))||(i||[]).find(d=>c.includes(d.headRefName));if(!l)return;let u=await Me(e.cwd).catch(()=>null);if(u){let{link:d,created:p}=await Yt({repo:u,number:l.number,trackId:e.id,url:l.url,headRef:l.headRefName||n,title:l.title,source:"reconciled"});if(!p&&!(d?.trackId===e.id&&await xr(u,l.number))){if(!tH(d,o))return;let f=await nH(e);if(f&&l.headSha&&f!==l.headSha){let h=Math.min(Z2,(gp.get(e.id)||W_)*2);gp.set(e.id,h),zc.set(e.id,a+h);return}let w=await K(e).catch(()=>null);if(w&&Wa(w)==null)return;await N(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:rH(o,l,{branch:n,repo:u})}),zc.delete(e.id),gp.delete(e.id);return}}await N(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:l.number,url:l.url,title:l.title,branch:n,...u?{repo:u}:{}}}).catch(()=>{}),zc.delete(e.id),gp.delete(e.id)}async function nH(e){try{let{stdout:t}=await X2("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 rH(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 X2,W_,zc,Z2,gp,pg,Jc=q(()=>{be();xs();Gr();Gn();xa();vs();X2=Y2(J2),W_=6e3,zc=new Map,Z2=5*6e4,gp=new Map,pg=30*6e4});import fg from"node:os";import{execFile as oH}from"node:child_process";import{promisify as sH}from"node:util";async function bp(){if(process.platform!=="darwin")return fg.freemem();try{let{stdout:e}=await aH("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:fg.freemem()}catch{return fg.freemem()}}var aH,mg=q(()=>{aH=sH(oH)});import ot from"node:fs/promises";import st from"node:path";import z_ from"node:os";import{randomUUID as iH}from"node:crypto";import{execFile as cH}from"node:child_process";import{promisify as lH}from"node:util";import{chromium as uH}from"playwright-core";async function er(){return _s||(hg||=(async()=>{try{_s=JSON.parse(await ot.readFile(Y_,"utf8"))}catch{_s={mocks:[]}}finally{hg=null}return _s.mocks||=[],_s})(),hg)}async function un(){await Bt(Y_,_s)}function eI(e,t){Qn(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&Qn(e,t.v,"png")}function Rt(e,t,n={}){return N({id:e.trackId,cwd:e.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,mockId:e.id,name:e.name,...e.group?{group:e.group}:{},...e.kind==="video"?{kind:"video"}:{},...n}}).catch(()=>{})}function dn(e,t){let n=Xc(e.cwd);return{html:st.join(n,`${e.id}.v${t}.html`),png:st.join(n,`${e.id}.v${t}.png`)}}function Yc(e,t,n){if(!t)return null;let r=n==="html"?t.htmlRel:n==="png"?t.pngRel??t.posterRel??null:n==="video"?t.videoRel??null:null;return r?st.join(e.cwd,r):null}function $n(e,t=null){let n=e.versions||[];return t!=null?n.find(r=>r.v===Number(t))||null:n.length?n[n.length-1]:null}function yg(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function kp(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(gH)}function yH(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 bH(e){let t=Buffer.alloc(24),n=await ot.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function kH({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 xH(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((w,h)=>{r.onload=w,r.onerror=()=>h(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,d=s,p=-1,f=-1;for(let w=0;w<s;w++)for(let h=w*o*4,y=0;y<o;y++,h+=4)Math.abs(c[h]-l[0])+Math.abs(c[h+1]-l[1])+Math.abs(c[h+2]-l[2])>24&&(y<u&&(u=y),y>p&&(p=y),w<d&&(d=w),f=w);return p<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:p,bottom:f}}},t.toString("base64"))}async function vH(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 SH({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await uH.launch({executablePath:X_,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Xt("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:qa})).newPage();a.setDefaultTimeout(Ga),await a.goto(`file://${e}`,{waitUntil:"load",timeout:Ga}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Ga});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await xH(a,i),u=l&&kH({...l,pad:fH*qa,align:qa});u&&(i=await vH(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Z_)throw new Xt(`capture produced a ${i.length}-byte image`);let c=yg(i);if(!c)throw new Xt("output is not a PNG");return await ot.writeFile(t,i),{width:Math.round(c.width/qa),height:Math.round(c.height/qa),dpr:qa,renderMs:Date.now()-r}}catch(s){throw s instanceof Xt?s:new Xt(/timeout/i.test(s?.message||"")?`render timed out after ${Ga}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function V_({htmlPath:e,outPath:t,width:n}){let r=await ot.mkdtemp(st.join(z_.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await gg(G_,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Ga,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new Xt(`firefox not found (${G_})`,""):new Xt(c?.killed?`render timed out after ${Ga}ms`:"firefox exited non-zero",s)}let a=await ot.stat(t).catch(()=>null);if(!a||a.size<Z_)throw new Xt(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=yg(await bH(t));if(!i)throw new Xt("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await ot.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function AH(e){if(dH==="firefox")return V_(e);try{return await SH(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await V_(e)}catch(n){throw/not found/.test(n?.message||"")?new Xt(`no browser installed \u2014 chromium missing at ${X_} 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 tI(e){return wg=wg.then(e,e),wg}async function nI(e,t){let n=await er(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await un(),await Rt(r,"rendering",{version:t});try{let o=await bp();if(o<mH)throw new Xt(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=dn(r,t),a=await TH({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await ot.rm(s.png,{force:!0}).catch(()=>{});return}bg(r,{v:t,source:"html",htmlRel:st.relative(r.cwd,s.html),pngRel:st.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await un(),await Rt(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),Zn(r),Qn(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await un(),await Rt(r,"error",{version:t,error:r.error}),Zn(r)}}}function bg(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 EH({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?pH:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Zc({trackId:e,group:t}={}){let n=await er(),r=e?n.mocks.filter(o=>o.trackId===e):n.mocks;return t&&(r=r.filter(o=>o.group===t)),r.map(o=>({...o}))}async function Mr(e){let n=(await er()).mocks.find(r=>r.id===e);return n?{...n}:null}function rI({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&&!kp(t))throw new Error("image must be a PNG");if(n){let o=yH(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>K_)throw new Error(`video must be ${K_/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function oI({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=EH({trackId:e,name:t,cwd:s,width:a}),p=typeof n=="string"&&n.trim(),f=rI({html:n,image:r,video:o}),w=await er(),h={id:iH().slice(0,8),trackId:e,box:c||null,agent:l||null,name:t.trim(),prompt:u?String(u).slice(0,500):null,group:i?String(i).trim():null,cwd:s,width:d,height:null,dpr:null,state:"queued",source:p?"html":o?"video":"image",kind:o?"video":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await ot.mkdir(Xc(s),{recursive:!0}),w.mocks.push(h),p?(await ot.writeFile(dn(h,1).html,n),await un(),await Rt(h,"created"),Zn(h),Qn(h,1,"html"),tI(()=>nI(h.id,1))):o?(await aI(h,1,o,f),await un(),await Rt(h,"created"),await Rt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr,durationS:h.versions[0]?.durationS??null}),Zn(h),eI(h,h.versions[0])):(await iI(h,1,r),await un(),await Rt(h,"created"),await Rt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr}),Zn(h),Qn(h,1,"png")),{...h}}async function sI(e,{html:t,image:n,video:r,width:o}={}){let a=(await er()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=rI({html:t,image:n,video:r});if(o!=null){let u=Number(o);if(!Number.isInteger(u)||u<320||u>4e3)throw new Error("invalid width (320-4000)");a.width=u}let l=a.version+1;return a.version=l,a.updatedAt=Date.now(),a.source=i?"html":r?"video":"image",a.kind=r?"video":"image",await ot.mkdir(Xc(a.cwd),{recursive:!0}),i?(a.state="queued",await ot.writeFile(dn(a,l).html,t),await un(),await Rt(a,"updated",{version:l}),Zn(a),Qn(a,l,"html"),tI(()=>nI(a.id,l))):r?(await aI(a,l,r,c),await un(),await Rt(a,"updated",{version:l}),await Rt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:$n(a)?.durationS??null}),Zn(a),eI(a,a.versions.find(u=>u.v===l))):(await iI(a,l,n),await un(),await Rt(a,"updated",{version:l}),await Rt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Zn(a),Qn(a,l,"png")),{...a}}async function _H(e){try{let{stdout:t}=await gg(hH,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:Q_,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 IH(e,t){try{await gg(wH,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:Q_,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await ot.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function aI(e,t,n,r){let o=Xc(e.cwd),s=st.join(o,`${e.id}.v${t}.${r.ext}`),a=st.join(o,`${e.id}.v${t}.poster.png`);await ot.writeFile(s,n);let i=await _H(s),c=await IH(s,a);bg(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:st.relative(e.cwd,s),posterRel:c?st.relative(e.cwd,a):null,mimeType:r.mime,bytes:n.length,durationS:i?.durationS??null,width:i?.width??null,height:i?.height??null,dpr:1,renderMs:null,createdAt:Date.now()}),i?.width&&(e.width=i.width)}async function iI(e,t,n){let r=yg(n);if(!r)throw new Error("image must be a PNG");let o=dn(e,t);await ot.writeFile(o.png,n),bg(e,{v:t,source:"image",htmlRel:null,pngRel:st.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function cI(e){let t=await er(),n=t.mocks.find(o=>o.id===e);if(!n)throw new Error("mock not found");if(n.readyVersion==null)throw new Error(`mock is ${n.state} \u2014 nothing to pick yet`);let r=n.group?t.mocks.filter(o=>o.trackId===n.trackId&&o.group===n.group&&o.id!==e&&o.readyVersion!=null).map(o=>({mockId:o.id,name:o.name})):[];return await Rt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function lI(e){let t=await er(),n=t.mocks.findIndex(o=>o.id===e);if(n===-1)throw new Error("mock not found");let r=t.mocks[n];t.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=dn(r,o);await ot.rm(s.html,{force:!0}).catch(()=>{}),await ot.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await ot.rm(st.join(r.cwd,s),{force:!0}).catch(()=>{});return await un(),await Rt(r,"deleted"),yn("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function uI(e){let n=(await er()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=st.join(J_,"mock-archive",e.id),o=Xc(e.cwd),s=st.join(r,".bostrat","mocks");await ot.mkdir(s,{recursive:!0});let a=await ot.readdir(o).catch(()=>[]);for(let i of a)await ot.copyFile(st.join(o,i),st.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await ot.stat(st.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=e.name||e.id,i.updatedAt=Date.now(),Zn(i)}return await un(),n.length}async function dI(e){let t=await er(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await un();for(let r of n)yn("mocks",r.id).catch(()=>{})}return n.length}async function pI(){if(!Ge())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await dt("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await er();for(let r of n.mocks){await Zn(r);for(let o of r.versions||[])o.htmlRel&&await Qn(r,o.v,"html"),(o.pngRel||o.posterRel)&&await Qn(r,o.v,"png"),o.videoRel&&await Qn(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var gg,J_,Y_,dH,X_,G_,pH,qa,fH,Z_,Ga,mH,K_,hH,wH,Q_,Xt,_s,hg,Zn,Qn,Xc,gH,TH,wg,Ka=q(()=>{be();zr();mg();Yr();gg=lH(cH),J_=process.env.BOSTRAT_STATE_DIR||st.join(process.env.HOME||z_.tmpdir(),".bostrat"),Y_=st.join(J_,"mocks.json"),dH=process.env.BOSTRAT_MOCK_ENGINE||"chromium",X_=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",G_=process.env.BOSTRAT_FIREFOX_BIN||"firefox",pH=1300,qa=2,fH=32,Z_=1500,Ga=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,mH=700*1024*1024,K_=100*1024*1024,hH=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",wH=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",Q_=2e4,Xt=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
215
|
+
`)}catch{return""}}async function J2(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await V2(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await O_(e.slug)||"server exited before listening"),$r.delete(e.id),await Rr(e,e.state,e.error?{error:e.error}:{}),!0}if(await Kc(e.port))return $r.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await Rr(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>z2?(e.state="error",e.error=await O_(e.slug)||"never started listening",await Rr(e,"error",{error:e.error}),!0):!1;let o=($r.get(e.id)||0)+1;return $r.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function hp(){if(!Qw){Qw=!0;try{let e=await Or(),t=!1;for(let n of e.views)await J2(n)&&(t=!0);t&&await Xn()}finally{Qw=!1}}}async function cg({trackId:e}={}){await hp();let t=await Or();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function F_(e){let n=(await Or()).views.find(r=>r.id===e);return n?{...n}:null}function lg(e){return Jn&&Jn.views.find(t=>t.slug===e)||null}async function ug({trackId:e,name:t,cmd:n,cwd:r,serveCwd:o,port:s,box:a,origin:i="agent",branch:c=null}){if(!e)throw new Error("trackId required");if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(i!=="detected"&&(typeof n!="string"||!n.trim()))throw new Error("cmd (string) required");if(!r)throw new Error("cwd required");let l=o!=null,u=Ut.resolve(r,o??"."),d=i==="detected"?null:async()=>{await W2(r,u),await q2(u,n)},p=await Or();if(i!=="detected"){let h=ng(t),y=String(n).trim(),k=p.views.find(g=>g.trackId===e&&(g.slug===h||ng(g.name)===h||g.cmd===y&&Ut.resolve(g.serveCwd??g.cwd)===u));if(k)return Y2(p,k,{cmd:y,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),p.views.filter(h=>h.state==="live"||h.state==="starting").length>=Ao)throw new Error(`view cap reached (${Ao} live) \u2014 stop one first`);let w={id:_2().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:G2(p,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await K2(p,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await ig(w),p.views.push(w),await Xn(),await Rr(w,"created",{origin:i}),tg(e,w.id),i==="detected"&&await Kc(w.port)&&(w.state="live",w.lastHealthyAt=Date.now(),await Xn(),await Rr(w,"live")),{...w}}async function Y2(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 Kc(t.port))return await Xn(),tg(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>=Ao)throw new Error(`view cap reached (${Ao} live) \u2014 stop one first`);return t.origin==="detected"?(await ag(t.port),t.origin="agent"):await Vc(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),$r.delete(t.id),await ig(t),await Xn(),await Rr(t,"created",{restarted:!0,reused:!0}),tg(t.trackId,t.id),{...t,reused:!0}}async function U_(e){let n=(await Or()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await ag(n.port):await Vc(n.slug),n.state!=="stopped"&&(n.state="stopped",$r.delete(n.id),await Xn(),await Rr(n,"stopped")),{...n}}async function H_(e){let t=await Or(),n=t.views.find(o=>o.id===e);if(!n)throw new Error("view not found");if(!n.cmd)throw new Error("a detected view has no known command \u2014 recreate it as a managed view");if(t.views.filter(o=>o.id!==e&&(o.state==="live"||o.state==="starting")).length>=Ao)throw new Error(`view cap reached (${Ao} live) \u2014 stop one first`);return await Vc(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),$r.delete(n.id),await ig(n),await Xn(),await Rr(n,"created",{restarted:!0}),{...n}}async function W_(e){let t=await Or(),n=t.views.findIndex(o=>o.id===e);if(n===-1)throw new Error("view not found");let r=t.views[n];return r.origin==="detected"?await ag(r.port):await Vc(r.slug),t.views.splice(n,1),$r.delete(r.id),await Xn(),await Rr(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function dg(e){let t=await Or(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Vc(r.slug),$r.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await Xn()),n.length}async function q_(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await Or(),r=n.adoptWatermarks[e.id]??null,o=t.filter(c=>c.role==="assistant"&&c.ts!=null&&(r==null?!1:c.ts>r)),s=t.reduce((c,l)=>l.ts>c?l.ts:c,r??0);if(n.adoptWatermarks[e.id]!==s&&(n.adoptWatermarks[e.id]=s,await Xn()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(X2)){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(!j_().has(c)&&!n.views.some(l=>l.port===c)&&await Kc(c))try{i.push(await ug({trackId:e.id,name:`${e.name} preview`,cmd:null,cwd:e.cwd,port:c,origin:"detected",branch:e.workspace?.branch??null}))}catch{}return i}var Yn,R2,M_,I_,$_,Ao,O2,Ha,R_,Wc,M2,eg,C2,N2,P2,rg,C_,Xw,L2,F2,P_,fp,Jn,Zw,qc,og,D_,$r,z2,Qw,X2,wp=G(()=>{pt();be();zr();Yn=$2(I2),R2=process.env.BOSTRAT_STATE_DIR||Ut.join(process.env.HOME||T2.tmpdir(),".bostrat"),M_=Ut.join(R2,"views.json"),[I_,$_]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),Ao=Number(process.env.BOSTRAT_VIEW_MAX)||3,O2="bostrat-view-",Ha=e=>`${O2}${e}`,R_=new Set(["bostrat","www","api","views","app"]),Wc=e=>Object.assign(new Error(e),{status:400}),M2=new Set(["npm","pnpm","yarn","bun"]),eg="package.json",C2=/[|;&`]|\$\(/,N2=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),P2=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,rg=e=>String(e).trim().split(/\s+/).filter(Boolean),C_=/^[A-Za-z_][A-Za-z0-9_]*=/;Xw=e=>e==="/",L2=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");F2=e=>Gc.stat(e).then(t=>t.isDirectory(),()=>!1),P_=e=>Gc.stat(e).then(t=>t.isFile(),()=>!1),fp=e=>Gc.realpath(e).catch(()=>Ut.resolve(e));Jn=null,Zw=null;qc=new Map,og=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,D_=12e3;$r=new Map,z2=12e4;Qw=!1;X2=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});import{execFile as Z2}from"node:child_process";import{promisify as Q2}from"node:util";function nH(e){return K_(e?.toPr?.number??e?.openPr?.number)}function K_(e){if(e==null||e==="")return null;let t=Number(e);return Number.isFinite(t)&&t>0?t:null}function rH(e){return Array.isArray(e?.actions)?e.actions.includes("push")||e.actions.includes("pr"):e?.action==="push"||e?.action==="pr"}function Wa(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=nH(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&&rH(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:K_(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>pg?null:n}function oH(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function yp(e,{bypassThrottle:t=!1}={}){let n=e?.workspace?.branch;if(!n)return;let r;try{r=await K(e)}catch{return}let o=Wa(r);if(o==null)return;let s=(e.boxes||[]).find(d=>d.name===e.leadBox);if(await as(e.leadBox,s?.agent).catch(()=>!1))return;let a=Date.now();if(!t&&a<(zc.get(e.id)||0))return;zc.set(e.id,a+G_);let i;try{i=await Kn(e.cwd,{caller:"reconcile"})}catch{return}let c=await La(e).catch(()=>[n]),l=o.toPr!=null&&(i||[]).find(d=>Number(d.number)===o.toPr&&c.includes(d.headRefName))||(i||[]).find(d=>c.includes(d.headRefName));if(!l)return;let u=await Me(e.cwd).catch(()=>null);if(u){let{link:d,created:p}=await Yt({repo:u,number:l.number,trackId:e.id,url:l.url,headRef:l.headRefName||n,title:l.title,source:"reconciled"});if(!p&&!(d?.trackId===e.id&&await xr(u,l.number))){if(!oH(d,o))return;let f=await sH(e);if(f&&l.headSha&&f!==l.headSha){let h=Math.min(tH,(gp.get(e.id)||G_)*2);gp.set(e.id,h),zc.set(e.id,a+h);return}let w=await K(e).catch(()=>null);if(w&&Wa(w)==null)return;await N(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:aH(o,l,{branch:n,repo:u})}),zc.delete(e.id),gp.delete(e.id);return}}await N(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:l.number,url:l.url,title:l.title,branch:n,...u?{repo:u}:{}}}).catch(()=>{}),zc.delete(e.id),gp.delete(e.id)}async function sH(e){try{let{stdout:t}=await eH("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 aH(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 eH,G_,zc,tH,gp,pg,Jc=G(()=>{be();xs();Gr();Gn();xa();vs();eH=Q2(Z2),G_=6e3,zc=new Map,tH=5*6e4,gp=new Map,pg=30*6e4});import fg from"node:os";import{execFile as iH}from"node:child_process";import{promisify as cH}from"node:util";async function bp(){if(process.platform!=="darwin")return fg.freemem();try{let{stdout:e}=await lH("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:fg.freemem()}catch{return fg.freemem()}}var lH,mg=G(()=>{lH=cH(iH)});import ot from"node:fs/promises";import st from"node:path";import Y_ from"node:os";import{randomUUID as uH}from"node:crypto";import{execFile as dH}from"node:child_process";import{promisify as pH}from"node:util";import{chromium as fH}from"playwright-core";async function er(){return _s||(hg||=(async()=>{try{_s=JSON.parse(await ot.readFile(Z_,"utf8"))}catch{_s={mocks:[]}}finally{hg=null}return _s.mocks||=[],_s})(),hg)}async function un(){await Bt(Z_,_s)}function nI(e,t){Qn(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&Qn(e,t.v,"png")}function Rt(e,t,n={}){return N({id:e.trackId,cwd:e.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,mockId:e.id,name:e.name,...e.group?{group:e.group}:{},...e.kind==="video"?{kind:"video"}:{},...n}}).catch(()=>{})}function dn(e,t){let n=Xc(e.cwd);return{html:st.join(n,`${e.id}.v${t}.html`),png:st.join(n,`${e.id}.v${t}.png`)}}function Yc(e,t,n){if(!t)return null;let r=n==="html"?t.htmlRel:n==="png"?t.pngRel??t.posterRel??null:n==="video"?t.videoRel??null:null;return r?st.join(e.cwd,r):null}function $n(e,t=null){let n=e.versions||[];return t!=null?n.find(r=>r.v===Number(t))||null:n.length?n[n.length-1]:null}function yg(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function kp(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(kH)}function xH(e){return!Buffer.isBuffer(e)||e.length<32?null:e.subarray(4,8).toString("latin1")==="ftyp"?{ext:"mp4",mime:"video/mp4"}:e.readUInt32BE(0)===440786851?{ext:"webm",mime:"video/webm"}:null}async function vH(e){let t=Buffer.alloc(24),n=await ot.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function SH({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 AH(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((w,h)=>{r.onload=w,r.onerror=()=>h(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,d=s,p=-1,f=-1;for(let w=0;w<s;w++)for(let h=w*o*4,y=0;y<o;y++,h+=4)Math.abs(c[h]-l[0])+Math.abs(c[h+1]-l[1])+Math.abs(c[h+2]-l[2])>24&&(y<u&&(u=y),y>p&&(p=y),w<d&&(d=w),f=w);return p<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:p,bottom:f}}},t.toString("base64"))}async function TH(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 EH({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await fH.launch({executablePath:Q_,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Xt("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:qa})).newPage();a.setDefaultTimeout(Ga),await a.goto(`file://${e}`,{waitUntil:"load",timeout:Ga}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Ga});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await AH(a,i),u=l&&SH({...l,pad:wH*qa,align:qa});u&&(i=await TH(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<eI)throw new Xt(`capture produced a ${i.length}-byte image`);let c=yg(i);if(!c)throw new Xt("output is not a PNG");return await ot.writeFile(t,i),{width:Math.round(c.width/qa),height:Math.round(c.height/qa),dpr:qa,renderMs:Date.now()-r}}catch(s){throw s instanceof Xt?s:new Xt(/timeout/i.test(s?.message||"")?`render timed out after ${Ga}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function J_({htmlPath:e,outPath:t,width:n}){let r=await ot.mkdtemp(st.join(Y_.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await gg(V_,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Ga,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new Xt(`firefox not found (${V_})`,""):new Xt(c?.killed?`render timed out after ${Ga}ms`:"firefox exited non-zero",s)}let a=await ot.stat(t).catch(()=>null);if(!a||a.size<eI)throw new Xt(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=yg(await vH(t));if(!i)throw new Xt("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await ot.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function _H(e){if(mH==="firefox")return J_(e);try{return await EH(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await J_(e)}catch(n){throw/not found/.test(n?.message||"")?new Xt(`no browser installed \u2014 chromium missing at ${Q_} 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 rI(e){return wg=wg.then(e,e),wg}async function oI(e,t){let n=await er(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await un(),await Rt(r,"rendering",{version:t});try{let o=await bp();if(o<gH)throw new Xt(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=dn(r,t),a=await IH({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await ot.rm(s.png,{force:!0}).catch(()=>{});return}bg(r,{v:t,source:"html",htmlRel:st.relative(r.cwd,s.html),pngRel:st.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await un(),await Rt(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),Zn(r),Qn(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await un(),await Rt(r,"error",{version:t,error:r.error}),Zn(r)}}}function bg(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 $H({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?hH:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Zc({trackId:e,group:t}={}){let n=await er(),r=e?n.mocks.filter(o=>o.trackId===e):n.mocks;return t&&(r=r.filter(o=>o.group===t)),r.map(o=>({...o}))}async function Mr(e){let n=(await er()).mocks.find(r=>r.id===e);return n?{...n}:null}function sI({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&&!kp(t))throw new Error("image must be a PNG");if(n){let o=xH(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>z_)throw new Error(`video must be ${z_/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function aI({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=$H({trackId:e,name:t,cwd:s,width:a}),p=typeof n=="string"&&n.trim(),f=sI({html:n,image:r,video:o}),w=await er(),h={id:uH().slice(0,8),trackId:e,box:c||null,agent:l||null,name:t.trim(),prompt:u?String(u).slice(0,500):null,group:i?String(i).trim():null,cwd:s,width:d,height:null,dpr:null,state:"queued",source:p?"html":o?"video":"image",kind:o?"video":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await ot.mkdir(Xc(s),{recursive:!0}),w.mocks.push(h),p?(await ot.writeFile(dn(h,1).html,n),await un(),await Rt(h,"created"),Zn(h),Qn(h,1,"html"),rI(()=>oI(h.id,1))):o?(await cI(h,1,o,f),await un(),await Rt(h,"created"),await Rt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr,durationS:h.versions[0]?.durationS??null}),Zn(h),nI(h,h.versions[0])):(await lI(h,1,r),await un(),await Rt(h,"created"),await Rt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr}),Zn(h),Qn(h,1,"png")),{...h}}async function iI(e,{html:t,image:n,video:r,width:o}={}){let a=(await er()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=sI({html:t,image:n,video:r});if(o!=null){let u=Number(o);if(!Number.isInteger(u)||u<320||u>4e3)throw new Error("invalid width (320-4000)");a.width=u}let l=a.version+1;return a.version=l,a.updatedAt=Date.now(),a.source=i?"html":r?"video":"image",a.kind=r?"video":"image",await ot.mkdir(Xc(a.cwd),{recursive:!0}),i?(a.state="queued",await ot.writeFile(dn(a,l).html,t),await un(),await Rt(a,"updated",{version:l}),Zn(a),Qn(a,l,"html"),rI(()=>oI(a.id,l))):r?(await cI(a,l,r,c),await un(),await Rt(a,"updated",{version:l}),await Rt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:$n(a)?.durationS??null}),Zn(a),nI(a,a.versions.find(u=>u.v===l))):(await lI(a,l,n),await un(),await Rt(a,"updated",{version:l}),await Rt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Zn(a),Qn(a,l,"png")),{...a}}async function RH(e){try{let{stdout:t}=await gg(yH,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:tI,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 OH(e,t){try{await gg(bH,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:tI,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await ot.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function cI(e,t,n,r){let o=Xc(e.cwd),s=st.join(o,`${e.id}.v${t}.${r.ext}`),a=st.join(o,`${e.id}.v${t}.poster.png`);await ot.writeFile(s,n);let i=await RH(s),c=await OH(s,a);bg(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:st.relative(e.cwd,s),posterRel:c?st.relative(e.cwd,a):null,mimeType:r.mime,bytes:n.length,durationS:i?.durationS??null,width:i?.width??null,height:i?.height??null,dpr:1,renderMs:null,createdAt:Date.now()}),i?.width&&(e.width=i.width)}async function lI(e,t,n){let r=yg(n);if(!r)throw new Error("image must be a PNG");let o=dn(e,t);await ot.writeFile(o.png,n),bg(e,{v:t,source:"image",htmlRel:null,pngRel:st.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function uI(e){let t=await er(),n=t.mocks.find(o=>o.id===e);if(!n)throw new Error("mock not found");if(n.readyVersion==null)throw new Error(`mock is ${n.state} \u2014 nothing to pick yet`);let r=n.group?t.mocks.filter(o=>o.trackId===n.trackId&&o.group===n.group&&o.id!==e&&o.readyVersion!=null).map(o=>({mockId:o.id,name:o.name})):[];return await Rt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function dI(e){let t=await er(),n=t.mocks.findIndex(o=>o.id===e);if(n===-1)throw new Error("mock not found");let r=t.mocks[n];t.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=dn(r,o);await ot.rm(s.html,{force:!0}).catch(()=>{}),await ot.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await ot.rm(st.join(r.cwd,s),{force:!0}).catch(()=>{});return await un(),await Rt(r,"deleted"),yn("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function pI(e){let n=(await er()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=st.join(X_,"mock-archive",e.id),o=Xc(e.cwd),s=st.join(r,".bostrat","mocks");await ot.mkdir(s,{recursive:!0});let a=await ot.readdir(o).catch(()=>[]);for(let i of a)await ot.copyFile(st.join(o,i),st.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await ot.stat(st.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=e.name||e.id,i.updatedAt=Date.now(),Zn(i)}return await un(),n.length}async function fI(e){let t=await er(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await un();for(let r of n)yn("mocks",r.id).catch(()=>{})}return n.length}async function mI(){if(!Ge())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await dt("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await er();for(let r of n.mocks){await Zn(r);for(let o of r.versions||[])o.htmlRel&&await Qn(r,o.v,"html"),(o.pngRel||o.posterRel)&&await Qn(r,o.v,"png"),o.videoRel&&await Qn(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var gg,X_,Z_,mH,Q_,V_,hH,qa,wH,eI,Ga,gH,z_,yH,bH,tI,Xt,_s,hg,Zn,Qn,Xc,kH,IH,wg,Ka=G(()=>{be();zr();mg();Yr();gg=pH(dH),X_=process.env.BOSTRAT_STATE_DIR||st.join(process.env.HOME||Y_.tmpdir(),".bostrat"),Z_=st.join(X_,"mocks.json"),mH=process.env.BOSTRAT_MOCK_ENGINE||"chromium",Q_=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",V_=process.env.BOSTRAT_FIREFOX_BIN||"firefox",hH=1300,qa=2,wH=32,eI=1500,Ga=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,gH=700*1024*1024,z_=100*1024*1024,yH=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",bH=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",tI=2e4,Xt=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
216
216
|
${n.trim().split(`
|
|
217
217
|
`).slice(-6).join(`
|
|
218
|
-
`)}`:t),this.name="MockRenderError"}},_s=null,hg=null;Zn=e=>It("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),Qn=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?Yc(e,r,"video"):r&&Yc(e,r,n)||(n==="html"?dn(e,t).html:null);return o?Jr("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Xc=e=>st.join(e,".bostrat","mocks");gH=Buffer.from([137,80,78,71,13,10,26,10]);TH=AH,wg=Promise.resolve()});import $H from"node:fs/promises";import gI from"node:path";import RH from"node:os";import{randomUUID as yI}from"node:crypto";async function Nr(){return Cr||(kg||=(async()=>{let e=!0;try{Cr=JSON.parse(await $H.readFile(bI,"utf8"))}catch{Cr={flows:[]},e=!1}finally{kg=null}if(Cr.flows||=[],!e&&Ge()){let{records:t,source:n}=await dt("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&Cr.flows.push(r.data);Cr.flows.length&&await Va().catch(()=>{})}}return Cr})(),kg)}async function Va(){await Bt(bI,Cr)}function xg(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 kI({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>fI)throw new Error(`steps must be an array of 1-${fI}`);let r=await Zc({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>hI)throw new Error(`${l}: caption over ${hI} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>wI)throw new Error(`${l}: title over ${wI} chars`);let p=String(c.mock??"").trim().replace(/^mock:/,"");if(!p)throw new Error(`${l}: mock (name or id) required`);let f=r.filter(g=>g.id===p);if(f.length||(f=r.filter(g=>g.name.toLowerCase()===p.toLowerCase())),!f.length)throw new Error(`${l}: no mock matching "${p}" in this Track \u2014 see \`bostrat-mock ls\``);if(f.length>1)throw new Error(`${l}: "${p}" is ambiguous (${f.map(g=>`mock:${g.id}`).join(", ")}) \u2014 use the id`);let w=f[0],h=null;if(c.v!=null&&(h=Number(c.v),!Number.isInteger(h)||!(w.versions||[]).some(g=>g.v===h)))throw new Error(`${l}: "${w.name}" has no captured v${c.v} to pin`);let y=o.findIndex(g=>g.mockId===w.id),k=y>=0?o.splice(y,1)[0].id:yI().slice(0,8);s.push({id:k,mockId:w.id,caption:u,title:d,v:h})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function MH(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>mI)throw new Error(`name over ${mI} chars`);return e.trim()}async function xI({trackId:e}={}){let t=await Nr();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function CH(e){let n=(await Nr()).flows.find(r=>r.id===e);return n?{...n}:null}async function vI(e){let t=await CH(e);if(!t)return null;let n=new Map((await Zc({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 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=MH(t),i=await Nr(),c=i.flows.find(f=>f.trackId===e&&f.name.toLowerCase()===a.toLowerCase());if(c)throw new Error(`flow "${c.name}" already exists in this Track (flow:${c.id}) \u2014 revise it with \`bostrat-flow update\``);let{steps:l,resolved:u}=await kI({trackId:e,steps:n}),d=Date.now(),p={id:yI().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 Va(),await xg(p,"created"),xp(p),{...p}}async function AI(e,{steps:t,note:n}={}){let o=(await Nr()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await kI({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 Va(),await xg(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),xp(o),{...o}}async function TI(e){let t=await Nr(),n=t.flows.findIndex(o=>o.id===e);if(n===-1)throw new Error("flow not found");let r=t.flows[n];return t.flows.splice(n,1),await Va(),await xg(r,"deleted"),yn("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function EI(e){return(await Nr()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function _I(e){let n=(await Nr()).flows.filter(r=>r.trackId===e.id);for(let r of n)r.trackDeletedAt=Date.now(),r.trackName=e.name||e.id,r.updatedAt=Date.now(),xp(r);return n.length&&await Va(),n.length}async function II(e){let t=await Nr(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await Va();for(let r of n)yn("flows",r.id).catch(()=>{})}return n.length}async function $I(){if(!Ge())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await dt("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Nr();for(let r of n.flows)await xp(r);return{pushed:n.flows.length,source:"plane"}}var OH,bI,fI,mI,hI,wI,Cr,kg,xp,vg=q(()=>{be();zr();Ka();Yr();OH=process.env.BOSTRAT_STATE_DIR||gI.join(process.env.HOME||RH.tmpdir(),".bostrat"),bI=gI.join(OH,"flows.json"),fI=20,mI=80,hI=500,wI=80,Cr=null,kg=null;xp=e=>It("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import Br from"node:fs/promises";import Lr from"node:path";import NH from"node:os";import{randomUUID as PH}from"node:crypto";function Qc(e){return DH.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function tr(){return Pr||(Sg||=(async()=>{let e=!0;try{Pr=JSON.parse(await Br.readFile(OI,"utf8"))}catch{Pr={runbooks:[]},e=!1}finally{Sg=null}return Pr.runbooks||=[],!e&&Ge()&&await BH().catch(()=>{}),Pr})(),Sg)}async function $s(){await Bt(OI,Pr)}function Sp(e){return el(It("runbooks",{...e},{trackId:e.trackId}))}async function BH(){let{records:e,source:t}=await dt("runbooks");if(!(t!=="plane"||!e.length)){await Br.mkdir(vp,{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 Hi("runbooks",r.id,o.v,"md");typeof s=="string"&&await Br.writeFile(Dr(r,o.v),s).catch(()=>{})}Pr.runbooks.push(r)}Pr.runbooks.length&&await $s()}}function za(e,t,n={}){return N({id:e.trackId,cwd:e.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,runbookId:e.id,name:e.name,status:e.status,...n}}).catch(()=>{})}function Dr(e,t){return Lr.join(vp,`${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 CI(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>RI)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${RI/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Qc(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 LH(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function jH({trackId:e,status:t}={}){let n=await tr(),r=e?n.runbooks.filter(o=>o.trackId===e):n.runbooks;return t&&(r=r.filter(o=>o.status===t)),r.map(o=>({...o}))}async function NI({trackId:e,status:t}={}){let n=await tr(),r=await jH({trackId:e,status:t});if(!Ge())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await dt("runbooks");if(s!=="plane")return{runbooks:r,fleetDegraded:!0};let a=new Set(n.runbooks.map(c=>c.id)),i=o.filter(c=>!c.deletedAt&&c.data?.id&&!a.has(c.data.id)).map(c=>({...c.data,originNode:c.originNode||null,remote:!0})).filter(c=>(!e||c.trackId===e)&&(!t||c.status===t));return{runbooks:[...r,...i],fleetDegraded:!1}}async function Ag(e){let t=await FH(e);if(t)return t;if(!Ge())return null;let{records:n,source:r}=await dt("runbooks");if(r!=="plane")return null;let o=n.find(s=>!s.deletedAt&&s.data?.id===e);return o?{...o.data,originNode:o.originNode||null,remote:!0}:null}async function PI(e,t){let n=await Hi("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function FH(e){let n=(await tr()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function DI({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=CI(r),c=await tr(),l={id:PH().slice(0,8),trackId:e,trackName:t||e,box:s||null,agent:a||null,name:n.trim(),status:"open",version:1,versions:[],trackDeletedAt:null,cwd:o||null,createdAt:Date.now(),updatedAt:Date.now()};return await Br.mkdir(vp,{recursive:!0}),await Br.writeFile(Dr(l,1),r),l.versions.push({v:1,mdRel:Lr.relative(Is,Dr(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await $s(),await za(l,"created",{version:1}),Sp(l),el(Jr("runbooks",l.id,1,"md",Dr(l,1))),{...l}}async function BI(e,{md:t,note:n}={}){let o=(await tr()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");LH(o,"update");let s=CI(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Br.mkdir(vp,{recursive:!0}),await Br.writeFile(Dr(o,a),t),o.versions.push({v:a,mdRel:Lr.relative(Is,Dr(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await $s(),await za(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Sp(o),el(Jr("runbooks",o.id,a,"md",Dr(o,a))),{...o}}async function LI(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await tr()).runbooks.find(o=>o.id===e);if(!r)throw new Error("runbook not found");return r.status===t?{...r}:(r.status=t,r.updatedAt=Date.now(),await $s(),await za(r,t==="done"?"done":"reopened"),Sp(r),{...r})}async function jI(e){let t=await tr(),n=t.runbooks.findIndex(o=>o.id===e);if(n===-1)throw new Error("runbook not found");let r=t.runbooks[n];t.runbooks.splice(n,1);for(let o of r.versions||[])await Br.rm(Lr.join(Is,o.mdRel),{force:!0}).catch(()=>{});return await $s(),r.trackDeletedAt||await za(r,"deleted"),el(yn("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function FI(e){let n=(await tr()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await za(r,"orphaned"),r.cwd=null,Sp(r);return n.length&&await $s(),n.length}async function UI(e){let t=await tr(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await za(r,"deleted");for(let o of r.versions||[])await Br.rm(Lr.join(Is,o.mdRel),{force:!0}).catch(()=>{});el(yn("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await $s()),n.length}async function HI(){if(!Ge())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await dt("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await tr();for(let r of n.runbooks){await It("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await Jr("runbooks",r.id,o.v,"md",Lr.join(Is,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var Is,OI,vp,RI,DH,Pr,Sg,el,Ap=q(()=>{zr();be();Yr();Is=process.env.BOSTRAT_STATE_DIR||Lr.join(process.env.HOME||NH.tmpdir(),".bostrat"),OI=Lr.join(Is,"runbooks.json"),vp=Lr.join(Is,"runbooks"),RI=256*1024,DH=[{label:"Anthropic API key",re:/\bsk-ant-[A-Za-z0-9_-]{20,}/},{label:"OpenAI-style API key",re:/\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{32,}\b/},{label:"Stripe secret key",re:/\bsk_(?:live|test)_[A-Za-z0-9]{16,}/},{label:"GitHub token",re:/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}|\bgithub_pat_[A-Za-z0-9_]{22,}/},{label:"AWS access key id",re:/\bAKIA[0-9A-Z]{16}\b/},{label:"Slack token",re:/\bxox[baprs]-[A-Za-z0-9-]{10,}/},{label:"private key block",re:/-----BEGIN [A-Z ]*PRIVATE KEY-----/},{label:"JWT",re:/\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/}];Pr=null,Sg=null;el=e=>Promise.resolve(e).catch(()=>{})});import Tg from"node:fs/promises";import tl from"node:path";import{randomUUID as UH}from"node:crypto";function _g(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return HH.has(t)?"image":WH.has(t)?"doc":null}function qH(e){let r=tl.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 GI(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>WI)throw new Error(`file too large \u2014 max ${Math.floor(WI/(1024*1024))} MB`);let r=qH(t),o=tl.extname(r).slice(1),s=_g(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=Eg(e);await Tg.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Tg.writeFile(tl.join(i,c),n,{mode:384}),await Tg.chmod(tl.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${qI}/${c}`,kind:s,size:n.length}}function KI(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function Ig(e){return e.role==="user"&&GH.test((e.text||"").trim())}var qI,Eg,WI,HH,WH,GH,$g=q(()=>{qI=".bostrat/uploads",Eg=e=>tl.join(e,qI),WI=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,HH=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),WH=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 Rs(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}function Rg(){let e=Number(process.env.BOSTRAT_LOOP_QUEUE_HOLD_MS);return Number.isFinite(e)&&e>0?e:KH}function zH(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 VI(e,t,{capMs:n=Rg(),now:r=Date.now()}={}){if(!e)return!1;let o=zH(e,t,r);return e.delivery?.producer!=="loop"||!VH.has(t)?!1:o>=n}var KH,VH,zI=q(()=>{KH=5*6e4,VH=new Set(["busy","noSession"])});import{execFile as JH}from"node:child_process";import{promisify as YH}from"node:util";function Og(e){return`refs/bostrat/wip/${e}`}function QH(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function XI(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function eW(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:ZH}function ZI(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 tW(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 To(s,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await To(s,["remote","get-url","origin"])}catch{return JI.has(s)||(JI.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 $a(s,"bostrat: wip snapshot"),{stdout:c}=await To(s,["rev-parse",`${i}^{tree}`]),l=c.trim();if(!n&&Tp.get(s)===l)return{pushed:!1,reason:"unchanged"};let u=Og(o);return await To(s,["push","--force","origin",`${i}:${u}`],YI),Tp.set(s,l),await N(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:u,sha:i,wip:!0,...QI(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(`
|
|
219
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function Mg(e,{log:t=console,force:n=!1}={}){let r=[],o=[];for(let s of
|
|
220
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function Cg(e,{log:t=console,ts:n=Date.now()}={}){let r=[],o=[];for(let s of
|
|
218
|
+
`)}`:t),this.name="MockRenderError"}},_s=null,hg=null;Zn=e=>It("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),Qn=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?Yc(e,r,"video"):r&&Yc(e,r,n)||(n==="html"?dn(e,t).html:null);return o?Jr("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Xc=e=>st.join(e,".bostrat","mocks");kH=Buffer.from([137,80,78,71,13,10,26,10]);IH=_H,wg=Promise.resolve()});import MH from"node:fs/promises";import bI from"node:path";import CH from"node:os";import{randomUUID as kI}from"node:crypto";async function Nr(){return Cr||(kg||=(async()=>{let e=!0;try{Cr=JSON.parse(await MH.readFile(xI,"utf8"))}catch{Cr={flows:[]},e=!1}finally{kg=null}if(Cr.flows||=[],!e&&Ge()){let{records:t,source:n}=await dt("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&Cr.flows.push(r.data);Cr.flows.length&&await Va().catch(()=>{})}}return Cr})(),kg)}async function Va(){await Bt(xI,Cr)}function xg(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 vI({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>hI)throw new Error(`steps must be an array of 1-${hI}`);let r=await Zc({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>gI)throw new Error(`${l}: caption over ${gI} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>yI)throw new Error(`${l}: title over ${yI} chars`);let p=String(c.mock??"").trim().replace(/^mock:/,"");if(!p)throw new Error(`${l}: mock (name or id) required`);let f=r.filter(g=>g.id===p);if(f.length||(f=r.filter(g=>g.name.toLowerCase()===p.toLowerCase())),!f.length)throw new Error(`${l}: no mock matching "${p}" in this Track \u2014 see \`bostrat-mock ls\``);if(f.length>1)throw new Error(`${l}: "${p}" is ambiguous (${f.map(g=>`mock:${g.id}`).join(", ")}) \u2014 use the id`);let w=f[0],h=null;if(c.v!=null&&(h=Number(c.v),!Number.isInteger(h)||!(w.versions||[]).some(g=>g.v===h)))throw new Error(`${l}: "${w.name}" has no captured v${c.v} to pin`);let y=o.findIndex(g=>g.mockId===w.id),k=y>=0?o.splice(y,1)[0].id:kI().slice(0,8);s.push({id:k,mockId:w.id,caption:u,title:d,v:h})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function PH(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>wI)throw new Error(`name over ${wI} chars`);return e.trim()}async function SI({trackId:e}={}){let t=await Nr();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function DH(e){let n=(await Nr()).flows.find(r=>r.id===e);return n?{...n}:null}async function AI(e){let t=await DH(e);if(!t)return null;let n=new Map((await Zc({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 TI({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=PH(t),i=await Nr(),c=i.flows.find(f=>f.trackId===e&&f.name.toLowerCase()===a.toLowerCase());if(c)throw new Error(`flow "${c.name}" already exists in this Track (flow:${c.id}) \u2014 revise it with \`bostrat-flow update\``);let{steps:l,resolved:u}=await vI({trackId:e,steps:n}),d=Date.now(),p={id:kI().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 Va(),await xg(p,"created"),xp(p),{...p}}async function EI(e,{steps:t,note:n}={}){let o=(await Nr()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await vI({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 Va(),await xg(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),xp(o),{...o}}async function _I(e){let t=await Nr(),n=t.flows.findIndex(o=>o.id===e);if(n===-1)throw new Error("flow not found");let r=t.flows[n];return t.flows.splice(n,1),await Va(),await xg(r,"deleted"),yn("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function II(e){return(await Nr()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function $I(e){let n=(await Nr()).flows.filter(r=>r.trackId===e.id);for(let r of n)r.trackDeletedAt=Date.now(),r.trackName=e.name||e.id,r.updatedAt=Date.now(),xp(r);return n.length&&await Va(),n.length}async function RI(e){let t=await Nr(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await Va();for(let r of n)yn("flows",r.id).catch(()=>{})}return n.length}async function OI(){if(!Ge())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await dt("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Nr();for(let r of n.flows)await xp(r);return{pushed:n.flows.length,source:"plane"}}var NH,xI,hI,wI,gI,yI,Cr,kg,xp,vg=G(()=>{be();zr();Ka();Yr();NH=process.env.BOSTRAT_STATE_DIR||bI.join(process.env.HOME||CH.tmpdir(),".bostrat"),xI=bI.join(NH,"flows.json"),hI=20,wI=80,gI=500,yI=80,Cr=null,kg=null;xp=e=>It("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import Br from"node:fs/promises";import Lr from"node:path";import BH from"node:os";import{randomUUID as LH}from"node:crypto";function Qc(e){return jH.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function tr(){return Pr||(Sg||=(async()=>{let e=!0;try{Pr=JSON.parse(await Br.readFile(CI,"utf8"))}catch{Pr={runbooks:[]},e=!1}finally{Sg=null}return Pr.runbooks||=[],!e&&Ge()&&await FH().catch(()=>{}),Pr})(),Sg)}async function $s(){await Bt(CI,Pr)}function Sp(e){return el(It("runbooks",{...e},{trackId:e.trackId}))}async function FH(){let{records:e,source:t}=await dt("runbooks");if(!(t!=="plane"||!e.length)){await Br.mkdir(vp,{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 Hi("runbooks",r.id,o.v,"md");typeof s=="string"&&await Br.writeFile(Dr(r,o.v),s).catch(()=>{})}Pr.runbooks.push(r)}Pr.runbooks.length&&await $s()}}function za(e,t,n={}){return N({id:e.trackId,cwd:e.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,runbookId:e.id,name:e.name,status:e.status,...n}}).catch(()=>{})}function Dr(e,t){return Lr.join(vp,`${e.id}.v${t}.md`)}function NI(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 PI(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>MI)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${MI/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Qc(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 HH({trackId:e,status:t}={}){let n=await tr(),r=e?n.runbooks.filter(o=>o.trackId===e):n.runbooks;return t&&(r=r.filter(o=>o.status===t)),r.map(o=>({...o}))}async function DI({trackId:e,status:t}={}){let n=await tr(),r=await HH({trackId:e,status:t});if(!Ge())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await dt("runbooks");if(s!=="plane")return{runbooks:r,fleetDegraded:!0};let a=new Set(n.runbooks.map(c=>c.id)),i=o.filter(c=>!c.deletedAt&&c.data?.id&&!a.has(c.data.id)).map(c=>({...c.data,originNode:c.originNode||null,remote:!0})).filter(c=>(!e||c.trackId===e)&&(!t||c.status===t));return{runbooks:[...r,...i],fleetDegraded:!1}}async function Ag(e){let t=await WH(e);if(t)return t;if(!Ge())return null;let{records:n,source:r}=await dt("runbooks");if(r!=="plane")return null;let o=n.find(s=>!s.deletedAt&&s.data?.id===e);return o?{...o.data,originNode:o.originNode||null,remote:!0}:null}async function BI(e,t){let n=await Hi("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function WH(e){let n=(await tr()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function LI({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=PI(r),c=await tr(),l={id:LH().slice(0,8),trackId:e,trackName:t||e,box:s||null,agent:a||null,name:n.trim(),status:"open",version:1,versions:[],trackDeletedAt:null,cwd:o||null,createdAt:Date.now(),updatedAt:Date.now()};return await Br.mkdir(vp,{recursive:!0}),await Br.writeFile(Dr(l,1),r),l.versions.push({v:1,mdRel:Lr.relative(Is,Dr(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await $s(),await za(l,"created",{version:1}),Sp(l),el(Jr("runbooks",l.id,1,"md",Dr(l,1))),{...l}}async function jI(e,{md:t,note:n}={}){let o=(await tr()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");UH(o,"update");let s=PI(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Br.mkdir(vp,{recursive:!0}),await Br.writeFile(Dr(o,a),t),o.versions.push({v:a,mdRel:Lr.relative(Is,Dr(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await $s(),await za(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Sp(o),el(Jr("runbooks",o.id,a,"md",Dr(o,a))),{...o}}async function FI(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await tr()).runbooks.find(o=>o.id===e);if(!r)throw new Error("runbook not found");return r.status===t?{...r}:(r.status=t,r.updatedAt=Date.now(),await $s(),await za(r,t==="done"?"done":"reopened"),Sp(r),{...r})}async function UI(e){let t=await tr(),n=t.runbooks.findIndex(o=>o.id===e);if(n===-1)throw new Error("runbook not found");let r=t.runbooks[n];t.runbooks.splice(n,1);for(let o of r.versions||[])await Br.rm(Lr.join(Is,o.mdRel),{force:!0}).catch(()=>{});return await $s(),r.trackDeletedAt||await za(r,"deleted"),el(yn("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function HI(e){let n=(await tr()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await za(r,"orphaned"),r.cwd=null,Sp(r);return n.length&&await $s(),n.length}async function WI(e){let t=await tr(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await za(r,"deleted");for(let o of r.versions||[])await Br.rm(Lr.join(Is,o.mdRel),{force:!0}).catch(()=>{});el(yn("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await $s()),n.length}async function qI(){if(!Ge())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await dt("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await tr();for(let r of n.runbooks){await It("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await Jr("runbooks",r.id,o.v,"md",Lr.join(Is,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var Is,CI,vp,MI,jH,Pr,Sg,el,Ap=G(()=>{zr();be();Yr();Is=process.env.BOSTRAT_STATE_DIR||Lr.join(process.env.HOME||BH.tmpdir(),".bostrat"),CI=Lr.join(Is,"runbooks.json"),vp=Lr.join(Is,"runbooks"),MI=256*1024,jH=[{label:"Anthropic API key",re:/\bsk-ant-[A-Za-z0-9_-]{20,}/},{label:"OpenAI-style API key",re:/\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{32,}\b/},{label:"Stripe secret key",re:/\bsk_(?:live|test)_[A-Za-z0-9]{16,}/},{label:"GitHub token",re:/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}|\bgithub_pat_[A-Za-z0-9_]{22,}/},{label:"AWS access key id",re:/\bAKIA[0-9A-Z]{16}\b/},{label:"Slack token",re:/\bxox[baprs]-[A-Za-z0-9-]{10,}/},{label:"private key block",re:/-----BEGIN [A-Z ]*PRIVATE KEY-----/},{label:"JWT",re:/\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/}];Pr=null,Sg=null;el=e=>Promise.resolve(e).catch(()=>{})});import Tg from"node:fs/promises";import tl from"node:path";import{randomUUID as qH}from"node:crypto";function _g(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return GH.has(t)?"image":KH.has(t)?"doc":null}function VH(e){let r=tl.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 VI(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>GI)throw new Error(`file too large \u2014 max ${Math.floor(GI/(1024*1024))} MB`);let r=VH(t),o=tl.extname(r).slice(1),s=_g(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=qH().slice(0,8),i=Eg(e);await Tg.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Tg.writeFile(tl.join(i,c),n,{mode:384}),await Tg.chmod(tl.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${KI}/${c}`,kind:s,size:n.length}}function zI(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function Ig(e){return e.role==="user"&&zH.test((e.text||"").trim())}var KI,Eg,GI,GH,KH,zH,$g=G(()=>{KI=".bostrat/uploads",Eg=e=>tl.join(e,KI),GI=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,GH=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),KH=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"]);zH=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function Rs(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}function Rg(){let e=Number(process.env.BOSTRAT_LOOP_QUEUE_HOLD_MS);return Number.isFinite(e)&&e>0?e:JH}function XH(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 JI(e,t,{capMs:n=Rg(),now:r=Date.now()}={}){if(!e)return!1;let o=XH(e,t,r);return e.delivery?.producer!=="loop"||!YH.has(t)?!1:o>=n}var JH,YH,YI=G(()=>{JH=5*6e4,YH=new Set(["busy","noSession"])});import{execFile as ZH}from"node:child_process";import{promisify as QH}from"node:util";function Og(e){return`refs/bostrat/wip/${e}`}function nW(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function QI(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function rW(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:tW}function e$(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 oW(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 To(s,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await To(s,["remote","get-url","origin"])}catch{return XI.has(s)||(XI.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 $a(s,"bostrat: wip snapshot"),{stdout:c}=await To(s,["rev-parse",`${i}^{tree}`]),l=c.trim();if(!n&&Tp.get(s)===l)return{pushed:!1,reason:"unchanged"};let u=Og(o);return await To(s,["push","--force","origin",`${i}:${u}`],ZI),Tp.set(s,l),await N(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:u,sha:i,wip:!0,...t$(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(`
|
|
219
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function Mg(e,{log:t=console,force:n=!1}={}){let r=[],o=[];for(let s of e$(e)){let a=await oW(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 sW(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 To(s,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await To(s,["remote","get-url","origin"])}catch{return{pushed:!1,reason:"no-origin"}}try{let i=await $a(s,"bostrat: transfer rescue snapshot"),{stdout:c}=await To(s,["rev-parse",`${i}^{tree}`]),l=c.trim();if(Tp.get(s)===l)return{pushed:!1,reason:"unchanged"};let u=nW(o,r);return await To(s,["push","origin",`${i}:${u}`],ZI),Tp.set(s,l),await N(e,{type:"code.pushed",actor:"orchestrator",boxId:e.leadBox??null,payload:{ref:u,sha:i,rescue:!0,...t$(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(`
|
|
220
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function Cg(e,{log:t=console,ts:n=Date.now()}={}){let r=[],o=[];for(let s of e$(e)){let a=await sW(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 n$({listTracks:e,sweepMs:t=rW(),log:n=console}){let r=!1,o=async()=>{if(!r){r=!0;try{let a=await e()??[];for(let i of a)Ue(i)||i?.kind==="setup"||await Mg(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 eW,tW,ZI,To,Tp,XI,t$,Ng=G(()=>{Sd();be();an();eW=QH(ZH),tW=12e4,ZI=6e4;To=(e,t,n=15e3)=>eW("git",["-C",e,...t],{timeout:n,maxBuffer:4*1024*1024}),Tp=new Map,XI=new Set,t$=e=>e?.name||e?.repo?{name:e.name??null,repo:e.repo??null}:{}});import aW from"node:fs";import Pg from"node:fs/promises";import iW from"node:path";function dW(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}async function r$(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(!dW(t))return{restored:!1,reason:"plane not configured"};let s=ra(e),a=iW.join(s,`${o}.ndjson`);try{if(aW.statSync(a).size>0)return{restored:!1,reason:"log not empty"}}catch{}let i=De(t),c={authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`},l=[],u=0;for(;;){let p=new AbortController,f=setTimeout(()=>p.abort(),uW),w;try{w=await n(`${i}/v1/spine/${encodeURIComponent(o)}?after=${u}&limit=${cW}`,{headers:c,signal:p.signal})}finally{clearTimeout(f)}if(w.status===404)return{restored:!1,reason:"unknown to plane"};if(!w.ok)return{restored:!1,reason:`plane ${w.status}`};let h=await w.json();for(let y of h.events||[])l.push(JSON.stringify(y));if(l.length>lW)return{restored:!1,reason:"event count over safety cap"};if(h.next==null)break;u=h.next}if(!l.length)return{restored:!1,reason:"unknown to plane"};await Pg.mkdir(s,{recursive:!0});let d=`${a}.pull-${process.pid}-${Date.now()}`;return await Pg.writeFile(d,l.join(`
|
|
221
221
|
`)+`
|
|
222
|
-
`),await Pg.rename(d,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var sW,aW,iW,n$=q(()=>{xt();be();sW=1e3,aW=5e5,iW=3e4});function r$(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 o$(e){let t=(e||[]).filter(n=>!lW.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 s$({archivedAt:e=null,archiving:t=!1,prompted:n=!1,openActions:r=0,openActionSince:o=null,loopAttention:s=!1,prDecisions:a=null,operation:i=null,phase:c=null,working:l=!1,workingSince:u=null,pending:d=null,completedTurnAt:p=null,live:f=!1,now:w=Date.now()}={}){return e&&!t?pn("archived","lifecycle",e):n?pn("needs-you","pane",o||w,{count:r||1}):t?pn("archiving","lifecycle",e||w):i?.status==="working"?pn("working","operation",i.since,{summary:i.summary??null,count:i.count??null}):(l||n)&&c?.phase?pn("working","agent-phase",c.at,{summary:c.summary??null}):l?pn("working","pane",u||w):r>0||s?pn("needs-you",s?"loop":"obligations",o||w,{count:r||1}):a?.count?pn("needs-you","pr",a.since,{count:a.count,summary:a.summary??null}):i?.status==="waiting"?pn("waiting","operation",i.since,{summary:i.summary??null,count:i.count??null}):d?.count?pn("waiting",d.source||"pending",d.since,{summary:d.summary??null,count:d.count}):p&&f?pn("settled","obligations",p,{summary:"no open actions"}):pn("idle","fallback",p||w)}var pn,lW,a$=q(()=>{pn=(e,t,n,r={})=>({kind:e,source:t,since:n||0,...r});lW=new Set(["merged","closed","lost"])});function uW(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 dW(e,t,n){let r=uW(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 Dg(e){if(Ep.has(e.id))return Ep.get(e.id);let t=await K(e).catch(()=>[]),n=[];try{n=Hn(t)}catch{}let r=rd(n),o=n.filter(f=>f?.kind==="followup-offer"&&f.status==="open"),s=new Set(t.filter(f=>f.type==="prompt.answered").map(f=>f.payload?.promptId)),a=t.filter(f=>f.type==="prompt.raised"&&f.payload?.promptId&&!s.has(f.payload.promptId)),i=[...r,...o,...a.map(f=>({ts:f.payload?.ts??f.ts}))],c=null;for(let f of t){if(f.type!=="track.phase.changed")continue;let w=f.payload||{};c=w.phase?{phase:w.phase,summary:w.summary??null,scope:w.scope??null,phaseId:w.phaseId??null,at:w.ts??f.ts}:null}let l=n.flatMap(f=>f?.kind==="job-dock"?f.jobs||[]:[]).filter(f=>f?.status==="running"),u=t.reduce((f,w)=>w.type==="turn.agent"?Math.max(f,w.payload?.ts??w.ts??0):f,0)||null,d=t.reduce((f,w)=>w.type==="turn.user"?Math.max(f,w.payload?.ts??w.ts??0):f,0)||null,p={openActions:i.length,openActionSince:i.length?Math.min(...i.map(f=>f.ts||Date.now())):null,phase:c,completedTurnAt:u,currentTurnAt:d,runningJobs:l.length,runningJobsSince:l.length?Math.min(...l.map(f=>f.startedTs||Date.now())):null};return Ep.set(e.id,p),!Ue(e)&&e.kind!=="setup"&&dW(e,t,p),p}function i$({trackOf:e,debounceMs:t=pW,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(nl.get(s));let a=setTimeout(()=>{nl.delete(s),Promise.resolve(e(s)).then(i=>i&&!Ue(i)&&i.kind!=="setup"?Dg(i):null).catch(i=>n.warn?.(`[obligations] fold ${s} failed: ${i?.message||i}`))},t);a.unref?.(),nl.set(s,a)};return Ze.on("event",r),()=>{Ze.off("event",r);for(let o of nl.values())clearTimeout(o);nl.clear()}}var Ep,pW,nl,Bg=q(()=>{be();od();an();Ep=new Map;Ze.on("event",e=>{e?.trackId&&Ep.delete(e.trackId)});pW=750,nl=new Map});import Lg from"node:fs/promises";async function mW(e){let t;try{t=await Lg.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/fW*1e3,ioBytes:await hW(e)}}async function hW(e){let t;try{t=await Lg.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 c$(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 Lg.readdir("/proc")}catch{return null}let r=new Map;await Promise.all(n.map(async l=>{if(!/^\d+$/.test(l))return;let u=await mW(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 fW,l$=q(()=>{fW=100});import wW from"node:fs/promises";function Hg(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 Wg(e,t,n){let r=null;try{r=await n.updatedAt(e)}catch{r=null}let o=u$.get(e.name);if(o&&o.mtime===r&&t-o.at<xW)return o.jobs;let s=[];try{s=await n.readOpenJobs(e)}catch{s=[]}return u$.set(e.name,{at:t,mtime:r,jobs:s}),s}async function vW(e,t,n){let r=await n.panePids(e.name),o=r.length?await n.treeStats(r):null,s=null;try{s=await n.updatedAt(e)}catch{s=null}return{at:t,cpuMs:o?.cpuMs??null,ioBytes:o?.ioBytes??null,procs:o?.procs??null,mtime:s}}async function SW(e,t){return e?.outputFile?await t.statFile(e.outputFile):null}function AW(e,t){if(!e)return!0;let n=Math.max(1,(t.at-e.at)/1e3);return t.mtime!=null&&e.mtime!=null&&t.mtime>e.mtime||t.procs!=null&&e.procs!=null&&t.procs>e.procs||t.cpuMs!=null&&e.cpuMs!=null&&(t.cpuMs-e.cpuMs)/n>=bW||t.ioBytes!=null&&e.ioBytes!=null&&(t.ioBytes-e.ioBytes)/n>=kW}function d$(e,t,n,r){let o=[];for(let s of n){let a=Ug(e.id,t.name,s.id),i=Rn.get(a);i?i.job=s:Rn.set(a,{trackId:e.id,boxName:t.name,agent:t.agent||null,job:s,firstSeenAt:r,quiet:0,stalled:!1}),o.push(a)}return o}async function p$(e,{now:t=Date.now(),deps:n=Fg}={}){let o=(await Promise.all(Hg([e]).map(({box:s})=>Wg(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 f$(e,{now:t=Date.now(),deps:n=Fg}={}){let r=new Map,o=new Set;for(let{track:s,box:a}of Hg(e)){let i=await Wg(a,t,n);for(let l of i)o.add(Ug(s.id,a.name,l.id));let c=d$(s,a,i,t).filter(l=>!Rn.get(l).stalled).map(l=>Rn.get(l).job);c.length&&r.set(s.id,[...r.get(s.id)??[],...c])}for(let s of[...Rn.keys()])o.has(s)||Rn.delete(s);return r}async function TW(e,{now:t=Date.now(),deps:n=Fg}={}){let r={checked:0,progressing:0,quiet:0,stalled:[]},o=Hg(e),s=new Set(o.map(({box:a})=>a.name));for(let[a,i]of[...Rn])s.has(i.boxName)||Rn.delete(a);for(let{track:a,box:i}of o){let c=await Wg(i,t,n),l=new Set(c.map(w=>Ug(a.id,i.name,w.id)));for(let[w,h]of[...Rn])h.boxName===i.name&&!l.has(w)&&Rn.delete(w);if(!c.length){jg.delete(i.name);continue}let u=d$(a,i,c,t),d=await vW(i,t,n),p=jg.get(i.name);jg.set(i.name,d);let f=AW(p,d);for(let w of u){let h=Rn.get(w);r.checked++;let y=await SW(h.job,n),k=!!y&&!!h.file&&(y.size>h.file.size||y.mtimeMs>h.file.mtimeMs),g=!!y&&!h.file;if(h.file=y??h.file,f||k||g){h.quiet=0,h.lastProgressAt=t,h.stalled=!1,r.progressing++;continue}if(h.stalled||(h.quiet++,r.quiet++,h.quiet<yW))continue;h.stalled=!0,h.stalledAt=t,r.stalled.push(w);let A=t-(h.lastProgressAt??h.firstSeenAt);try{await n.appendEvent(a,{type:"job.stalled",actor:"orchestrator",source:"orchestrator",boxId:i.name,provider:i.agent||null,payload:{id:h.job.id,kind:h.job.kind||"agent",name:h.job.name||null,detail:h.job.detail||null,startedTs:h.job.ts??h.firstSeenAt,quietMs:A,checks:h.quiet}})}catch{}console.log(`[job-watch] ${a.id}/${i.name} job ${h.job.id} (${h.job.name||h.job.kind}) quiet for ${Math.round(A/6e4)}m over ${h.quiet} check(s) \u2014 released; the node may sleep`)}}return r}function m$({listTracks:e,intervalMs:t=gW}={}){let n=!1,o=setInterval(async()=>{if(!n){n=!0;try{await TW(await e())}catch(s){console.error(`[job-watch] sweep: ${s?.message||s}`)}finally{n=!1}}},t);return o.unref?.(),o}var gW,yW,bW,kW,xW,Fg,Rn,u$,jg,Ug,qg=q(()=>{be();pd();pt();l$();gW=Number(process.env.BOSTRAT_JOB_CHECK_MS)||15*6e4,yW=Number(process.env.BOSTRAT_JOB_STALL_CHECKS)||3,bW=Number(process.env.BOSTRAT_JOB_CPU_RATE_MIN)||25,kW=Number(process.env.BOSTRAT_JOB_IO_RATE_MIN)||64*1024,xW=5*6e4,Fg={readOpenJobs:dd,statFile:async e=>{try{let t=await wW.stat(e);return{size:t.size,mtimeMs:Math.round(t.mtimeMs)}}catch{return null}},updatedAt:e=>ao(e.agent).updatedAt(e),panePids:ax,treeStats:c$,appendEvent:N},Rn=new Map,u$=new Map,jg=new Map,Ug=(e,t,n)=>`${e}|${t}|${n}`});import EW from"node:fs/promises";import _W from"node:os";import _p from"node:path";async function Gg(e){try{return(await EW.stat(e)).size}catch{return null}}async function Ja(e){let[t,n,r]=await Promise.all([Gg(_p.join(IW,"CLAUDE.md")),e?Gg(_p.join(e,"CLAUDE.md")):null,e?Gg(_p.join(e,".bostrat","memory","MEMORY.md")):null]);return{bostratClaudeMd:t,repoClaudeMd:n,memoryIndex:r}}var IW,Kg=q(()=>{IW=process.env.BOSTRAT_STATE_DIR||_p.join(process.env.HOME||_W.tmpdir(),".bostrat")});import{randomUUID as $W}from"node:crypto";async function $p(e,t,n={}){let r={operationId:$W(),kind:t,startedAt:Date.now(),...n};return Ip.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 Vg(e,t,n="completed",r=null){Ip.get(e.id)?.operationId===t?.operationId&&Ip.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 Ip,h$,zg=q(()=>{be();Ip=new Map;h$=e=>Ip.get(e)||null});var X$={};Zf(X$,{HANDOFF_LATEST_REL:()=>Io,KNOWN_AGENTS:()=>ml,LOGIN_GREETING:()=>C$,TRACK_NAME_MAX:()=>cy,addTrackRepo:()=>Gp,adoptTrack:()=>Ky,agentAvailability:()=>Fp,announceNodeUpdate:()=>or,answerFullscreenOffer:()=>jy,answerPrompt:()=>Hp,answeringFor:()=>hy,archiveTrack:()=>Ps,archiveTrackForMergedPr:()=>Er,augmentFirstPromptAfterHandoff:()=>T$,buildAgentCmd:()=>ly,canResumeSubtask:()=>z$,cancelQueued:()=>wl,cancelQueuedToDraft:()=>yy,cancelStopJob:()=>ri,cancelSubtask:()=>nf,changedRepos:()=>Es,commitTransferOut:()=>Qp,createSetupTrack:()=>Ry,createTrack:()=>Vp,deleteTrack:()=>My,demoteTransferredTracks:()=>Gy,ensureLeadEpochs:()=>_o,forceMergeSubtask:()=>Jy,getHandoffJob:()=>ai,getReshapeJob:()=>gl,getSubtaskJob:()=>zq,getTrack:()=>me,getTransferOutJob:()=>Wy,healDeadLead:()=>pl,interruptLead:()=>qp,isArchiving:()=>Cq,isHandoffNoteEcho:()=>ny,isInterruptEcho:()=>ry,isLeadStarting:()=>O$,isModelSwitchEcho:()=>oy,leadCliDead:()=>Ny,listSubtaskJobs:()=>Bs,listTracks:()=>ke,matchTranscriptsToBoxes:()=>$$,mergeNotifyMessage:()=>q$,navigatePrompt:()=>wy,notifyTrackForMergedPr:()=>_r,notifyTrackForResolvedPr:()=>zp,offerFollowupsForMergedPr:()=>Ds,pendingOffers:()=>fy,queueGauges:()=>gy,queueShipCompleteNotify:()=>ky,queuedMessages:()=>Ms,rawTrack:()=>py,reapArchivedOrphans:()=>Ly,reconcileDemotedTracks:()=>By,reconcileHandoffOutcome:()=>G$,reconcileInterruptedHandoffs:()=>Uy,reconcileInterruptedReshapes:()=>Iy,reconcileInterruptedSubtasks:()=>Vy,reconcileNodeUpdateNotices:()=>dy,reconcileSubtaskOutcome:()=>V$,refreshStandingInstructions:()=>$y,refuseIfTransferred:()=>Zp,rehydrateLead:()=>si,removeTrackRepo:()=>Kp,renameTrack:()=>Dy,reposOf:()=>Gt,reshapeTrack:()=>_y,resolveTrackBox:()=>Lc,resolveTrackBoxByHookToken:()=>hl,restartLead:()=>Yp,resumeAgentCmd:()=>jp,reviveLeadForSession:()=>Py,sayToLead:()=>qt,sendOptionsForAgent:()=>Un,setTrackDraft:()=>Sy,setTrackModel:()=>Ay,startLeadHandoff:()=>Xp,startSpinUpView:()=>xy,startSubtask:()=>zy,startTransferOut:()=>qy,stopJobActive:()=>P$,subtaskMessages:()=>my,trackMessages:()=>Up,unarchiveTrack:()=>Jp});import Y from"node:fs/promises";import z from"node:path";import{randomUUID as Dp,createHash as RW}from"node:crypto";import{execFile as OW}from"node:child_process";import{promisify as MW}from"node:util";function ly(e,{skipPermissions:t=!0,model:n=null}={}){let r=Lp[e];if(!r)throw new Error(`unknown agent "${e}"`);let o=process.env[r.envVar];if(o)return o;let s=t&&r.skipFlag?`${r.base} ${r.skipFlag}`:r.base;if(n&&n!=="default"){let a=nw(e).find(i=>i.alias===n);if(!a)throw new Error(`unknown ${e} launch model "${n}"`);s+=` --model ${a.alias}`}return s}function jp(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=Lp[e];if(!s)throw new Error(`unknown agent "${e}"`);let a=process.env[s.envVar];if(a)return a;let i=n&&s.skipFlag?` ${s.skipFlag}`:"";switch(e){case"claude":{if(!t)return`${s.base}${i}`;if(r&&!xn(r,t)){let c=o&&je(o)?` --name ${o}`:"";return`${s.base} --session-id ${t}${c}${i}`}return`${s.base} --resume ${t}${i}`}case"cursor":return`${s.base} --continue${i}`;case"codex":return`${s.base} resume --last${i}`;default:return`${s.base}${i}`}}function Ns(e){let t=Lp[e];if(t)return process.env[t.envVar]||t.base}async function Fp({maxAgeMs:e=6e4}={}){if(Eo.value&&Date.now()-Eo.at<e)return Eo.value;let t=Eo.value,n=await Promise.all(ml.map(async r=>{let o=await Mu(Ns(r),{retries:[]});return o===la?[r,t?.[r]??!0]:[r,o===Ou]}));return Eo.value=Object.fromEntries(n),Eo.at=Date.now(),Eo.value}async function ni(e,t=!0,n=null){let r=ly(e,{skipPermissions:t,model:n});if(await Mu(r)===ca){Eo.at=0;let o=r.split(/\s+/)[0],s=process.env.FLY_APP_NAME?"This Fly node's image predates CLI parity \u2014 update the node image (Links page \u2192 Fly card \u2192 Update) or re-provision.":$m[o]?`Install it on this node: ${$m[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 T$(e,t=Io){return`${e.trim()}
|
|
222
|
+
`),await Pg.rename(d,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var cW,lW,uW,o$=G(()=>{xt();be();cW=1e3,lW=5e5,uW=3e4});function s$(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 a$(e){let t=(e||[]).filter(n=>!pW.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 i$({archivedAt:e=null,archiving:t=!1,prompted:n=!1,openActions:r=0,openActionSince:o=null,loopAttention:s=!1,prDecisions:a=null,operation:i=null,phase:c=null,working:l=!1,workingSince:u=null,pending:d=null,completedTurnAt:p=null,live:f=!1,now:w=Date.now()}={}){return e&&!t?pn("archived","lifecycle",e):n?pn("needs-you","pane",o||w,{count:r||1}):t?pn("archiving","lifecycle",e||w):i?.status==="working"?pn("working","operation",i.since,{summary:i.summary??null,count:i.count??null}):(l||n)&&c?.phase?pn("working","agent-phase",c.at,{summary:c.summary??null}):l?pn("working","pane",u||w):r>0||s?pn("needs-you",s?"loop":"obligations",o||w,{count:r||1}):a?.count?pn("needs-you","pr",a.since,{count:a.count,summary:a.summary??null}):i?.status==="waiting"?pn("waiting","operation",i.since,{summary:i.summary??null,count:i.count??null}):d?.count?pn("waiting",d.source||"pending",d.since,{summary:d.summary??null,count:d.count}):p&&f?pn("settled","obligations",p,{summary:"no open actions"}):pn("idle","fallback",p||w)}var pn,pW,c$=G(()=>{pn=(e,t,n,r={})=>({kind:e,source:t,since:n||0,...r});pW=new Set(["merged","closed","lost"])});function fW(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 mW(e,t,n){let r=fW(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 Dg(e){if(Ep.has(e.id))return Ep.get(e.id);let t=await K(e).catch(()=>[]),n=[];try{n=Hn(t)}catch{}let r=rd(n),o=n.filter(f=>f?.kind==="followup-offer"&&f.status==="open"),s=new Set(t.filter(f=>f.type==="prompt.answered").map(f=>f.payload?.promptId)),a=t.filter(f=>f.type==="prompt.raised"&&f.payload?.promptId&&!s.has(f.payload.promptId)),i=[...r,...o,...a.map(f=>({ts:f.payload?.ts??f.ts}))],c=null;for(let f of t){if(f.type!=="track.phase.changed")continue;let w=f.payload||{};c=w.phase?{phase:w.phase,summary:w.summary??null,scope:w.scope??null,phaseId:w.phaseId??null,at:w.ts??f.ts}:null}let l=n.flatMap(f=>f?.kind==="job-dock"?f.jobs||[]:[]).filter(f=>f?.status==="running"),u=t.reduce((f,w)=>w.type==="turn.agent"?Math.max(f,w.payload?.ts??w.ts??0):f,0)||null,d=t.reduce((f,w)=>w.type==="turn.user"?Math.max(f,w.payload?.ts??w.ts??0):f,0)||null,p={openActions:i.length,openActionSince:i.length?Math.min(...i.map(f=>f.ts||Date.now())):null,phase:c,completedTurnAt:u,currentTurnAt:d,runningJobs:l.length,runningJobsSince:l.length?Math.min(...l.map(f=>f.startedTs||Date.now())):null};return Ep.set(e.id,p),!Ue(e)&&e.kind!=="setup"&&mW(e,t,p),p}function l$({trackOf:e,debounceMs:t=hW,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(nl.get(s));let a=setTimeout(()=>{nl.delete(s),Promise.resolve(e(s)).then(i=>i&&!Ue(i)&&i.kind!=="setup"?Dg(i):null).catch(i=>n.warn?.(`[obligations] fold ${s} failed: ${i?.message||i}`))},t);a.unref?.(),nl.set(s,a)};return Ze.on("event",r),()=>{Ze.off("event",r);for(let o of nl.values())clearTimeout(o);nl.clear()}}var Ep,hW,nl,Bg=G(()=>{be();od();an();Ep=new Map;Ze.on("event",e=>{e?.trackId&&Ep.delete(e.trackId)});hW=750,nl=new Map});import Lg from"node:fs/promises";async function gW(e){let t;try{t=await Lg.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/wW*1e3,ioBytes:await yW(e)}}async function yW(e){let t;try{t=await Lg.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 u$(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 Lg.readdir("/proc")}catch{return null}let r=new Map;await Promise.all(n.map(async l=>{if(!/^\d+$/.test(l))return;let u=await gW(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 wW,d$=G(()=>{wW=100});import bW from"node:fs/promises";function Hg(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 Wg(e,t,n){let r=null;try{r=await n.updatedAt(e)}catch{r=null}let o=p$.get(e.name);if(o&&o.mtime===r&&t-o.at<AW)return o.jobs;let s=[];try{s=await n.readOpenJobs(e)}catch{s=[]}return p$.set(e.name,{at:t,mtime:r,jobs:s}),s}async function TW(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 EW(e,t){return e?.outputFile?await t.statFile(e.outputFile):null}function _W(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>=vW||t.ioBytes!=null&&e.ioBytes!=null&&(t.ioBytes-e.ioBytes)/n>=SW}function f$(e,t,n,r){let o=[];for(let s of n){let a=Ug(e.id,t.name,s.id),i=Rn.get(a);i?i.job=s:Rn.set(a,{trackId:e.id,boxName:t.name,agent:t.agent||null,job:s,firstSeenAt:r,quiet:0,stalled:!1}),o.push(a)}return o}async function m$(e,{now:t=Date.now(),deps:n=Fg}={}){let o=(await Promise.all(Hg([e]).map(({box:s})=>Wg(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 h$(e,{now:t=Date.now(),deps:n=Fg}={}){let r=new Map,o=new Set;for(let{track:s,box:a}of Hg(e)){let i=await Wg(a,t,n);for(let l of i)o.add(Ug(s.id,a.name,l.id));let c=f$(s,a,i,t).filter(l=>!Rn.get(l).stalled).map(l=>Rn.get(l).job);c.length&&r.set(s.id,[...r.get(s.id)??[],...c])}for(let s of[...Rn.keys()])o.has(s)||Rn.delete(s);return r}async function IW(e,{now:t=Date.now(),deps:n=Fg}={}){let r={checked:0,progressing:0,quiet:0,stalled:[]},o=Hg(e),s=new Set(o.map(({box:a})=>a.name));for(let[a,i]of[...Rn])s.has(i.boxName)||Rn.delete(a);for(let{track:a,box:i}of o){let c=await Wg(i,t,n),l=new Set(c.map(w=>Ug(a.id,i.name,w.id)));for(let[w,h]of[...Rn])h.boxName===i.name&&!l.has(w)&&Rn.delete(w);if(!c.length){jg.delete(i.name);continue}let u=f$(a,i,c,t),d=await TW(i,t,n),p=jg.get(i.name);jg.set(i.name,d);let f=_W(p,d);for(let w of u){let h=Rn.get(w);r.checked++;let y=await EW(h.job,n),k=!!y&&!!h.file&&(y.size>h.file.size||y.mtimeMs>h.file.mtimeMs),g=!!y&&!h.file;if(h.file=y??h.file,f||k||g){h.quiet=0,h.lastProgressAt=t,h.stalled=!1,r.progressing++;continue}if(h.stalled||(h.quiet++,r.quiet++,h.quiet<xW))continue;h.stalled=!0,h.stalledAt=t,r.stalled.push(w);let S=t-(h.lastProgressAt??h.firstSeenAt);try{await n.appendEvent(a,{type:"job.stalled",actor:"orchestrator",source:"orchestrator",boxId:i.name,provider:i.agent||null,payload:{id:h.job.id,kind:h.job.kind||"agent",name:h.job.name||null,detail:h.job.detail||null,startedTs:h.job.ts??h.firstSeenAt,quietMs:S,checks:h.quiet}})}catch{}console.log(`[job-watch] ${a.id}/${i.name} job ${h.job.id} (${h.job.name||h.job.kind}) quiet for ${Math.round(S/6e4)}m over ${h.quiet} check(s) \u2014 released; the node may sleep`)}}return r}function w$({listTracks:e,intervalMs:t=kW}={}){let n=!1,o=setInterval(async()=>{if(!n){n=!0;try{await IW(await e())}catch(s){console.error(`[job-watch] sweep: ${s?.message||s}`)}finally{n=!1}}},t);return o.unref?.(),o}var kW,xW,vW,SW,AW,Fg,Rn,p$,jg,Ug,qg=G(()=>{be();pd();pt();d$();kW=Number(process.env.BOSTRAT_JOB_CHECK_MS)||15*6e4,xW=Number(process.env.BOSTRAT_JOB_STALL_CHECKS)||3,vW=Number(process.env.BOSTRAT_JOB_CPU_RATE_MIN)||25,SW=Number(process.env.BOSTRAT_JOB_IO_RATE_MIN)||64*1024,AW=5*6e4,Fg={readOpenJobs:dd,statFile:async e=>{try{let t=await bW.stat(e);return{size:t.size,mtimeMs:Math.round(t.mtimeMs)}}catch{return null}},updatedAt:e=>ao(e.agent).updatedAt(e),panePids:ax,treeStats:u$,appendEvent:N},Rn=new Map,p$=new Map,jg=new Map,Ug=(e,t,n)=>`${e}|${t}|${n}`});import $W from"node:fs/promises";import RW from"node:os";import _p from"node:path";async function Gg(e){try{return(await $W.stat(e)).size}catch{return null}}async function Ja(e){let[t,n,r]=await Promise.all([Gg(_p.join(OW,"CLAUDE.md")),e?Gg(_p.join(e,"CLAUDE.md")):null,e?Gg(_p.join(e,".bostrat","memory","MEMORY.md")):null]);return{bostratClaudeMd:t,repoClaudeMd:n,memoryIndex:r}}var OW,Kg=G(()=>{OW=process.env.BOSTRAT_STATE_DIR||_p.join(process.env.HOME||RW.tmpdir(),".bostrat")});import{randomUUID as MW}from"node:crypto";async function $p(e,t,n={}){let r={operationId:MW(),kind:t,startedAt:Date.now(),...n};return Ip.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 Vg(e,t,n="completed",r=null){Ip.get(e.id)?.operationId===t?.operationId&&Ip.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 Ip,g$,zg=G(()=>{be();Ip=new Map;g$=e=>Ip.get(e)||null});var Q$={};Zf(Q$,{HANDOFF_LATEST_REL:()=>Io,KNOWN_AGENTS:()=>ml,LOGIN_GREETING:()=>P$,TRACK_NAME_MAX:()=>cy,addTrackRepo:()=>Gp,adoptTrack:()=>Ky,agentAvailability:()=>Fp,announceNodeUpdate:()=>or,answerFullscreenOffer:()=>jy,answerPrompt:()=>Hp,answeringFor:()=>hy,archiveTrack:()=>Ps,archiveTrackForMergedPr:()=>Er,augmentFirstPromptAfterHandoff:()=>_$,buildAgentCmd:()=>ly,canResumeSubtask:()=>Y$,cancelQueued:()=>wl,cancelQueuedToDraft:()=>yy,cancelStopJob:()=>ri,cancelSubtask:()=>nf,changedRepos:()=>Es,commitTransferOut:()=>Qp,createSetupTrack:()=>Ry,createTrack:()=>Vp,deleteTrack:()=>My,demoteTransferredTracks:()=>Gy,ensureLeadEpochs:()=>_o,forceMergeSubtask:()=>Jy,getHandoffJob:()=>ai,getReshapeJob:()=>gl,getSubtaskJob:()=>Xq,getTrack:()=>me,getTransferOutJob:()=>Wy,healDeadLead:()=>pl,interruptLead:()=>qp,isArchiving:()=>Dq,isHandoffNoteEcho:()=>ny,isInterruptEcho:()=>ry,isLeadStarting:()=>C$,isModelSwitchEcho:()=>oy,leadCliDead:()=>Ny,listSubtaskJobs:()=>Bs,listTracks:()=>ke,matchTranscriptsToBoxes:()=>O$,mergeNotifyMessage:()=>K$,navigatePrompt:()=>wy,notifyTrackForMergedPr:()=>_r,notifyTrackForResolvedPr:()=>zp,offerFollowupsForMergedPr:()=>Ds,pendingOffers:()=>fy,queueGauges:()=>gy,queueShipCompleteNotify:()=>ky,queuedMessages:()=>Ms,rawTrack:()=>py,reapArchivedOrphans:()=>Ly,reconcileDemotedTracks:()=>By,reconcileHandoffOutcome:()=>V$,reconcileInterruptedHandoffs:()=>Uy,reconcileInterruptedReshapes:()=>Iy,reconcileInterruptedSubtasks:()=>Vy,reconcileNodeUpdateNotices:()=>dy,reconcileSubtaskOutcome:()=>J$,refreshStandingInstructions:()=>$y,refuseIfTransferred:()=>Zp,rehydrateLead:()=>si,removeTrackRepo:()=>Kp,renameTrack:()=>Dy,reposOf:()=>Gt,reshapeTrack:()=>_y,resolveTrackBox:()=>Lc,resolveTrackBoxByHookToken:()=>hl,restartLead:()=>Yp,resumeAgentCmd:()=>jp,reviveLeadForSession:()=>Py,sayToLead:()=>qt,sendOptionsForAgent:()=>Un,setTrackDraft:()=>Sy,setTrackModel:()=>Ay,startLeadHandoff:()=>Xp,startSpinUpView:()=>xy,startSubtask:()=>zy,startTransferOut:()=>qy,stopJobActive:()=>B$,subtaskMessages:()=>my,trackMessages:()=>Up,unarchiveTrack:()=>Jp});import Y from"node:fs/promises";import z from"node:path";import{randomUUID as Dp,createHash as CW}from"node:crypto";import{execFile as NW}from"node:child_process";import{promisify as PW}from"node:util";function ly(e,{skipPermissions:t=!0,model:n=null}={}){let r=Lp[e];if(!r)throw new Error(`unknown agent "${e}"`);let o=process.env[r.envVar];if(o)return o;let s=t&&r.skipFlag?`${r.base} ${r.skipFlag}`:r.base;if(n&&n!=="default"){let a=nw(e).find(i=>i.alias===n);if(!a)throw new Error(`unknown ${e} launch model "${n}"`);s+=` --model ${a.alias}`}return s}function jp(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=Lp[e];if(!s)throw new Error(`unknown agent "${e}"`);let a=process.env[s.envVar];if(a)return a;let i=n&&s.skipFlag?` ${s.skipFlag}`:"";switch(e){case"claude":{if(!t)return`${s.base}${i}`;if(r&&!xn(r,t)){let c=o&&je(o)?` --name ${o}`:"";return`${s.base} --session-id ${t}${c}${i}`}return`${s.base} --resume ${t}${i}`}case"cursor":return`${s.base} --continue${i}`;case"codex":return`${s.base} resume --last${i}`;default:return`${s.base}${i}`}}function Ns(e){let t=Lp[e];if(t)return process.env[t.envVar]||t.base}async function Fp({maxAgeMs:e=6e4}={}){if(Eo.value&&Date.now()-Eo.at<e)return Eo.value;let t=Eo.value,n=await Promise.all(ml.map(async r=>{let o=await Mu(Ns(r),{retries:[]});return o===la?[r,t?.[r]??!0]:[r,o===Ou]}));return Eo.value=Object.fromEntries(n),Eo.at=Date.now(),Eo.value}async function ni(e,t=!0,n=null){let r=ly(e,{skipPermissions:t,model:n});if(await Mu(r)===ca){Eo.at=0;let o=r.split(/\s+/)[0],s=process.env.FLY_APP_NAME?"This Fly node's image predates CLI parity \u2014 update the node image (Links page \u2192 Fly card \u2192 Update) or re-provision.":$m[o]?`Install it on this node: ${$m[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 _$(e,t=Io){return`${e.trim()}
|
|
223
223
|
|
|
224
|
-
(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 ny(e){return e.role==="user"&&
|
|
225
|
-
`;return Yg=Yg.then(()=>Y.mkdir(nr,{recursive:!0})).then(()=>Y.appendFile(CW,n)).catch(()=>{}),Yg}async function E$(e,{box:t,agent:n,message:r}){let o=fe(await te(),e);!o||o.archivedAt||(Ee(o,"startup-gate",{box:t,agent:n??null,message:r}),await ae(),kc(o,{provider:n??null,cls:"startup-gate",note:r}).catch(()=>{}))}async function or({current:e,latest:t,min:n=null,installKind:r=null,required:o=!1,status:s="available",policy:a=null,error:i=null,reason:c=null}={}){if(!t)return{announced:0};if(s==="available"){let d=null;try{d=JSON.parse(await Y.readFile(Xg,"utf8"))}catch{}if(d?.latest===t&&d?.current===e)return{announced:0,deduped:!0}}let l=await te(),u=0;for(let d of l.tracks)Ue(d)||!d.leadBox||(Ee(d,"node-update",{status:s,current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},...a?{policy:a}:{},...i?{error:String(i).slice(0,400)}:{},...c?{reason:c}:{},required:o===!0}),u++);if(await ae().catch(()=>{}),s==="available")try{await Y.mkdir(z.dirname(Xg),{recursive:!0}),await Y.writeFile(Xg,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function dy({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await te(),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)||jW(e,c)||(Ee(a,"node-update",{status:"done",current:l.current??null,latest:c,...t?{installKind:t}:{},...n?{policy:n}:{},reason:"version-reconciled",installed:e,required:!1}),o++,s.add(c))}return o&&await ae().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function jW(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 _$(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=>FW.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 I$(e,t,n,{kind:r}={}){let o=await Se(t);if(!o)throw new Error(`box "${t}" is not running`);let s=Ns(o.agent),a=Fc({text:n,producer:jc({kind:r,orchestration:!0}),kind:r||"orchestration",provider:o.agent}),i=fe(await te(),e)||null,c=Date.now(),l=null;try{l=await wr({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s})}catch(y){throw Hc(a,{ms:Date.now()-c,outcome:y?.code==="cli-not-running"?"cli-not-running":"blocked"}),await In(i,a,Ua(y)),y}Hc(a,{ms:Date.now()-c,outcome:l||"up"});let u=Un(o.agent,n);a.paste=!!u.paste;try{await Qe(t,n,{...u,kind:r||"orchestration"})}catch(y){throw await In(i,a,Ua(y)),y}let d={},p=await pc({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration",stats:d});a.verify=d,await In(i,a,p?d.verdict==="unverified"?"unverified":"delivered":"unsubmitted"),await re({flow:"orchestration",phase:"sent",submitted:p,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let f=await te(),w=fe(f,e);if(!w)return;let h=w.boxes.find(y=>y.name===t);Ee(w,"orchestration-prompt",{box:t,agent:h?.agent||o.agent,boxRole:h?.role,prompt:n,kind:r||"orchestration",deliveryId:a.id}),await ae()}async function UW(e){let t=z.join(ra(e),`${e.id}.ndjson`),n;try{n=await Y.stat(t)}catch{return null}let r=w$.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 w$.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function HW(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:yc(n.name,n.agent)}async function Zt(e,t=null,n=null){let r=t??new Map((await kn()).map(A=>[A.name,A])),o=!!e.transferredTo,s=e.boxes.map(A=>{let I=o?null:r.get(A.name),M=jr.has(A.state)?A.state:I?A.role==="lead"?"live":A.state:"stopped";return{...A,state:M,cwd:I?.cwd||A.cwd,agent:I?.agent||A.agent,sessionId:I?.sessionId||A.sessionId}}),[a,{working:i,prompted:c},l,u,d]=await Promise.all([UW(e),HW(e,s),Dg(e),n??En().catch(()=>[]),p$({...e,boxes:s})]),p=dl.has(e.id),f=(u||[]).filter(A=>A.trackId===e.id&&!["merged","closed","lost"].includes(A.state)),w=h$(e.id),h=w&&{status:"waiting",since:w.startedAt,summary:w.summary??null,count:w.count??null}||r$(f),y=s.find(A=>A.name===e.leadBox)?.state==="live",k=y?Ms(e.id).filter(A=>A.kind!=="ship-notify"&&A.kind!=="prompt-answer"&&A.kind!=="login-greet"):[],g=d||(y&&l.runningJobs?{source:"jobs",count:l.runningJobs,since:l.runningJobsSince,summary:l.runningJobs===1?"a background job is still running":`${l.runningJobs} background jobs are still running`}:k.length?{source:"queue",count:k.length,since:Math.min(...k.map(A=>A.ts||Date.now())),summary:"a queued message is about to send"}:null),x=s$({archivedAt:e.archivedAt,archiving:p,prompted:c,openActions:l.openActions,openActionSince:l.openActionSince,prDecisions:o$(f),operation:h,phase:l.phase,working:i,workingSince:l.currentTurnAt,pending:g,completedTurnAt:l.completedTurnAt||a,live:y});return{...e,boxes:s,lastResponseAt:a,working:i,prompted:c,archiving:p,status:x}}function $$(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-WW,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 R$(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=wx(r),i=new Set(a.map(p=>p.id)),c=s.every(p=>p&&i.has(p)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(p=>p.box),d=$$(u,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 sx(p.box,p.sessionId),n=!0}}return n}async function ke(){let e=await te();await R$(e)&&await ae();let t=new Map((await kn()).map(r=>[r.name,r])),n=await En().catch(()=>[]);return Promise.all(e.tracks.map(r=>Zt(r,t,n)))}async function Lc(e,t){let n=await te();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 hl(e){let t=Ho(e);if(!t)return null;let n=await te();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Hk(o.hookTokenHash,t))return{track:r,box:o};return null}async function py(e){return fe(await te(),e)??null}async function me(e){let t=await te(),n=fe(t,e);return n?(await R$(t)&&await ae(),Zt(n)):null}async function fy(e){let t=await K(e).catch(()=>[]),n;try{n=Hn(t)}catch{return[]}let r=rd(n).map(s=>({kind:s.kind,id:s.offerId??s.proposalId??s.id,ts:s.ts??null})),o=[];try{o=jv(t)}catch{o=[]}return r.concat(o.map(s=>({kind:"pr-open",id:`${s.repo??""}#${s.number}`,ts:null})))}function ry(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(qW[e.agent]||[]).some(n=>n.test(t))}function oy(e){let t=(e.text||"").trim();return t?GW.some(n=>n.test(t)):!1}function O$(e,t,n=Date.now()){return!e||e.readyAt||e.state!=="live"?!1:n-(t||0)<KW}async function Up(e,t={}){let n=await me(e);if(!n)return null;let r=new Set([...n.events.filter(O=>O.type==="orchestration-prompt"&&O.prompt).map(O=>String(O.prompt).trim()),...n.events.filter(O=>O.type==="said"&&O.text).map(O=>String(O.text).trim())]),o=ai(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=sg(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...D_(n),...l],p={windows:[...VS(n),...zS(n,hd),...a,...u]},w=(await Promise.all(n.boxes.map(O=>Ih(O,t)))).flat(),h=w.map(O=>{let X={kind:"message",...O};if(X.role==="assistant"&&(X.text||"").trim()&&X.tools?.length){let ne=ao(X.agent).cliTools,he=X.tools.filter(Be=>!ne.has(Be));return{...X,tools:he.length?he:void 0}}return X}).map(O=>{if(O.role!=="user")return O;let X=_v(O);return X?X.kind==="suppress"?null:{...O,note:X,text:$v(X)}:O}).filter(Boolean).filter(O=>!(O.role==="user"&&r.has((O.text||"").trim()))).filter(O=>!ry(O)).filter(O=>!oy(O)).filter(O=>!ny(O)).filter(O=>!Ig(O)).filter(O=>O.role!=="assistant"||!!(O.text||"").trim()||!!O.images?.length).filter(O=>!JS(O,p)),y=h.filter(O=>O.failed&&O.text===Zo);if(y.length){let O=new Map;for(let X of y){if(!O.has(X.box)){let Be=[...(await Ne(X.box,{lines:ze}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Pt=>Pt[1]);O.set(X.box,Be.length?Be[Be.length-1]:null)}let ne=O.get(X.box);ne&&(X.text=`\u25A0 ${ne}`)}}let k=await Promise.all(n.boxes.map(O=>ES(O,t)));await u_(n,k.flat());let{agentTurnBoxes:g}=await c_(n,h);for(let O of n.boxes)g.has(O.name)&&O.cwd&&bu(O.cwd);await zA(n),await H_(n,h).catch(()=>{});for(let O of n.boxes)O.agent==="codex"&&O.state==="live"&&await p_(n,O),O.agent==="cursor"&&O.state==="live"&&await g_(n,O);let x=await Promise.all(n.boxes.map(O=>TS(O,t)));await l_(n,x.flat().filter(O=>!Zg(O.box,O.ts,u)));let A=await K(n),I=c&&i.viewId?i.viewId:null,M=iT(A),P=Hn(A).filter(O=>!(O.kind==="message"&&(ry(O)||oy(O)||ny(O)||Ig(O)))&&!(O.kind==="view-card"&&I&&O.viewId===I)),L=cT(P,M),T=P.filter(O=>!(O.kind==="message"&&O.role==="assistant"&&!L.has(O)&&Zg(O.box,O.ts,M))),E=uh(A,T).map(O=>({kind:"command-batch",id:O.id,ts:O.pending?O.ts:(O.anchorTs??O.ts)-1,anchorTs:O.anchorTs,anchorBox:O.anchorBox,agent:O.agent,boxRole:O.boxRole,commands:O.commands,pending:O.pending})).filter(O=>!Zg(O.anchorBox,O.anchorTs??O.ts,u)),j=Bv(Lv([...T,...E].filter(O=>O.ts!=null).sort((O,X)=>O.ts-X.ts))),G=VW(n),R=G.size?j.filter(O=>O.kind==="event"||!G.has(O.box??O.anchorBox)):j,S=n.boxes.find(O=>O.name===n.leadBox)||null,C=S?await Zi(S.cwd,S.sessionId,{fallbackNewest:!1}):null;if(!C&&S){let O=[...A].reverse().find(X=>X.type==="context.usage"&&X.boxId===S.name);O?.payload?.window&&(C={pct:O.payload.pct??(O.payload.occupancy!=null?O.payload.occupancy/O.payload.window:null),occupancy:O.payload.occupancy??null,window:O.payload.window,windowKnown:!0,model:O.payload.model||null})}let H=[...w].reverse().find(O=>O.role==="assistant"&&O.model),B=H?.model||null,Z=[...n.events||[]].reverse().find(O=>O.type==="model-change"),xe=C?.model||B;Z&&(!H?.ts||Z.at>=H.ts)&&(xe=Z.to);let Ae=S?await Ne(S.name,{lines:ze}).catch(()=>""):"",_e=S&&oo(S.agent,Ae)?await Jt(S.name).catch(()=>Ae):Ae,Q=S?Sn(S,_e):null;if(S&&hy(S.name,Q,_e)&&Q&&(Q={...Q,answering:!0}),Q&&S){let O=ad(S,Q,_e);if(O){let X=sS(S.name,Q.promptId);X.action!=="give-up"&&(X.action==="send"&&(O.accept&&Ku(S.name,_e),(!O.gate||eo(S.name,O.gate))&&Ke(S.name,O.keys,{kind:O.accept?"auto-accept":"auto-reject"}).catch(()=>{}),X.first&&N(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:S.name,provider:S.agent,payload:{ts:Date.now(),promptId:Q.promptId,rule:O.id}}).catch(()=>{})),Q=null)}}S&&(Q?.setup&&Xx(S.name,_e),!Q&&oc(S.agent,Ae)&&qu(S.name)&&eo(S.name,"login-issue")&&(Gu(S.name),Qe(S.name,"/login",{enter:!0,submitDelayMs:600,kind:"login-issue"}).catch(()=>{}))),!Q&&S&&!ya(S.agent,Ae)&&!Ms(n.id).length&&(Q=ih(R,{leadBoxId:S.name,provider:S.agent,isAnswered:O=>A.some(X=>X.type==="prompt.answered"&&X.payload?.promptId===O)})),Q&&JA(n,S,Q);let Xe=R.filter(O=>!(O.kind==="message"&&O.role==="assistant"&&O.ask&&!String(O.text||"").trim()&&!O.images?.length)),le=!!S&&id(S.name,S.agent,Ae)&&!Q;if(S&&!S.readyAt&&(le||Q||Ae&&to(S.agent,Ae))){S.readyAt=Date.now();let O=fe(await te(),n.id)?.boxes?.find(X=>X.name===S.name);O&&!O.readyAt&&(O.readyAt=S.readyAt,await ae())}let ge=O$(S,n.createdAt),de=!!S&&!!Ae&&to(S.agent,Ae)&&!Q&&!oc(S.agent,Ae)&&!ga(S.agent,Ae),ee=!1;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&S){if(de){let O=await te(),X=fe(O,n.id);X?.setup&&!X.setup.connectedAt&&(X.setup.connectedAt=Date.now(),n.setup.connectedAt=X.setup.connectedAt,ee=!0,await ae())}}else S&&de&&Yx(S.name)&&(ee=!0);if(ee&&Zx(S.name),S){let O=Qx(S.name),X=Jx(S.name);O.started&&N(n,{type:"provider.login.started",actor:"system",source:"pane",boxId:S.name,provider:S.agent,payload:{provider:S.agent,reason:X?.startedReason||"auth-screen",ts:Date.now()}}).catch(()=>{}),O.connected&&(await N(n,{type:"provider.connected",actor:"system",source:"pane",boxId:S.name,provider:S.agent,payload:{provider:S.agent,ts:X?.connectedAt||Date.now(),...X?.account?{account:X.account}:{},reason:X?.startedReason||"already-signed-in"}}).catch(()=>{}),n.kind!=="setup"&&X?.startedReason&&!nq(n.id)&&qt(n.id,C$,{kind:"login-greet"}).catch(ne=>{console.warn(`[track ${n.id}] login greeting not delivered: ${ne?.message||ne}`)}))}let we=s?{running:!0,step:o.step,toAgent:o.toAgent||null,wait:o.wait||null}:null,Oe=gl(n.id),Ve=Oe&&!Oe.finishedAt?{running:!0,step:Oe.step||null,added:Oe.added||[],removed:Oe.removed||[]}:null,Nt=null;if(c){let O=Date.now()-i.startedAt;if(i.viewId&&!le){let X=mp(n.id);X&&(Ee(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:X.finishedAt}),await ae())}else if(Q||O>N_&&!le&&!i.viewId||O>og){let X=Q?"needs-input":O>og?"timeout":"no-view";mp(n.id,{error:X})&&await N(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:S?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:X}}).catch(()=>{})}else Nt={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:Xe,context:C?{pct:C.pct,occupancy:C.occupancy,window:C.window,windowKnown:C.windowKnown,model:C.model}:null,models:{current:xe,options:nw(S?.agent)},pendingPrompt:Q,working:le,stopping:P$(n.id),leadStarting:ge,handoff:we,reshape:Ve,view:Nt,queued:Ms(n.id).filter(O=>O.kind!=="ship-notify"&&O.kind!=="prompt-answer"&&O.kind!=="login-greet"),draft:n.draft||null,draftRev:n.draftRev||0}}function VW(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 my(e,t){let n=await me(e);if(!n)return null;let r=Bs(e).find(d=>d.id===t)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await 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=uh(s.filter(d=>d.boxId===o),a.filter(d=>d.kind==="message")).map(d=>({kind:"command-batch",id:d.id,ts:d.pending?d.ts:(d.anchorTs??d.ts)-1,anchorTs:d.anchorTs,anchorBox:d.anchorBox,agent:d.agent,boxRole:d.boxRole,commands:d.commands,pending:d.pending})),c=[...a,...i].filter(d=>d.ts!=null).sort((d,p)=>d.ts-p.ts),l=n.boxes.find(d=>d.name===o)||null,u=!1;if(l&&l.state==="live"){let d=await Ne(o,{lines:ze}).catch(()=>"");u=id(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}function zW(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>g$)throw new Error(`custom answer is too long (max ${g$} characters)`);return r})}function JW(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 hy(e,t,n,r=Date.now()){let o=sy.get(e);if(!o)return!1;let s=!!t&&t.promptId===o.promptId&&r-o.at<YW&&(t.setup?!0:M$(n)===o.paneHash);return s||sy.delete(e),s}async function ZW(e,t,n){let r=n;for(let o of XW){await rr(o);let s=await Jt(e).catch(()=>null);if(s!=null&&(r=s,s!==n))break}sy.set(e,{promptId:t.promptId,paneHash:M$(r),setup:t.setup||null,at:Date.now()})}async function Hp(e,t={}){let n=rl.get(e);n&&await n.catch(()=>{});let r=QW(e,t);rl.set(e,r);try{return await r}finally{rl.get(e)===r&&rl.delete(e)}}async function QW(e,{promptId:t,answers:n,text:r,custom:o}={}){let s=fe(await te(),e);if(!s)throw new Error(`track "${e}" not found`);ri(e,"prompt-answer");let a=s.boxes.find(g=>g.name===s.leadBox);if(!a)throw new Error(`track "${e}" has no lead box`);if(!await Se(s.leadBox))throw new Error(`lead box "${s.leadBox}" is not running`);let c=await Jt(a.name).catch(()=>""),l=Sn(a,c);if(l||(l=await eq(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"&&hy(a.name,l,c))throw new Error("prompt already answered \u2014 waiting for the CLI to move on");let u=zW(o,l.questions?.length??1);if(u.some(Boolean)&&!l.custom)throw new Error("custom answer isn't available for this prompt \u2014 pick one of its options");let d=(n||[]).some(g=>Array.isArray(g)&&g.length>0);if(l.custom&&!d&&!u.some(Boolean)&&l.answerMode!=="text")throw new Error("no answer selected \u2014 pick an option or type your own");u.some(Boolean)&&(n=(l.questions||[]).map((g,x)=>u[x]&&!g.multiSelect?[]:Array.isArray(n?.[x])?n[x]:[]));let p;if(l.answerMode==="text"){let g=typeof r=="string"?r.trim():"";if(!g)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(g)||g.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await Qe(s.leadBox,g,{enter:!0,kind:"answer-prompt"}),p={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(l.answerMode==="esc")await Ke(s.leadBox,["Escape"],{kind:"answer-prompt"}),p={mode:"keys",value:"Escape",count:1};else if(l.answerMode==="say"){let g=Cv(l,n||[],typeof r=="string"?r:"",u);if(!g)throw new Error("no answer selected \u2014 pick an option or reply in chat");await qt(s.id,g,{kind:"prompt-answer"}),p={mode:"say",value:g,count:1}}else if(u[0]&&l.provider==="claude"){let g=tS(l,n||[],u[0]);await Rm(s.leadBox,[...g.pre,{text:g.text}],{kind:"answer-prompt"});let x=!1;for(let I of[150,300,600]){await rr(I);let M=await Jt(s.leadBox).catch(()=>""),P=Sn(a,M);if(P?.customText&&JW(P.customText,g.text)){x=!0;break}}if(!x)throw new Error("custom answer didn't take \u2014 the widget moved; reload and answer again");await Rm(s.leadBox,g.post,{kind:"answer-prompt"});let A=I=>I.map(M=>M.key).join(" ");p={mode:"keys+text",value:`${A(g.pre)} \u2039text\u203A ${A(g.post)}`.trim(),count:g.pre.length+1+g.post.length}}else if(l.provider==="claude"||l.answerMode==="keys"){let g=gh(l,n||[]);await Ke(s.leadBox,g,{kind:"answer-prompt"}),p={mode:"keys",value:g.join(" "),count:g.length}}else{let g=qx(l,n||[]);await Qe(s.leadBox,g,{enter:!0,kind:"answer-prompt"}),p={mode:"text",value:g,count:1}}if(l.answerMode!=="say"&&await ZW(a.name,l,c),l.answerMode==="say"){let g=l.questions||[];for(let x=0;x<g.length;x++){let A=g[x],I=new Set(Array.isArray(n?.[x])?n[x]:[]),M=[...I].map(L=>A.options?.[L]).filter(Boolean).map(L=>({label:L.label,description:L.description||""}));u[x]&&M.push({label:u[x],description:"",custom:!0});let P=(A.options||[]).map((L,T)=>I.has(T)?null:L?.label).filter(Boolean);rw(s,a,l,{...p,groupId:l.groupId||l.promptId,questionIndex:x,questionTotal:l.questionTotal??g.length,header:A.header||"",question:A.question||"",multiSelect:!!A.multiSelect,chosen:M,rejected:P})}return await re({flow:"prompt",phase:"answered",sessionName:s.leadBox,agent:a.agent,provider:l.provider,promptId:l.promptId,...p}).catch(()=>{}),{ok:!0,promptId:l.promptId,sent:p.count}}let f=l.questions?.[0]||{},w=new Set(Array.isArray(n?.[0])?n[0]:[]),h=[...w].map(g=>f.options?.[g]).filter(Boolean).map(g=>({label:g.label,description:g.description||""}));u[0]&&h.push({label:u[0],description:"",custom:!0});let y=(f.options||[]).map((g,x)=>w.has(x)?null:g?.label).filter(Boolean),k=l.review?"review":/^claude:model-confirm:/.test(l.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(l.promptId||"")?"resume-confirm":l.setup==="continue"?"login-continue":void 0;return p={...p,groupId:l.groupId||l.promptId,questionIndex:l.questionIndex??0,questionTotal:l.questionTotal??1,header:f.header||"",question:f.question||"",multiSelect:!!f.multiSelect,chosen:h,rejected:y,...k?{control:k}:{}},await re({flow:"prompt",phase:"answered",sessionName:s.leadBox,agent:a.agent,provider:l.provider,promptId:l.promptId,...p}).catch(()=>{}),rw(s,a,l,p),{ok:!0,promptId:l.promptId,sent:p.count}}async function eq(e,t){if(!t)return null;let n=await K(e).catch(()=>[]);return ih(Hn(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function wy(e,{promptId:t,questionIndex:n}={}){let r=fe(await te(),e);if(!r)throw new Error(`track "${e}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(!await Se(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await eS(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=nS(a,n);return c.length?(await Ke(r.leadBox,c,{kind:"navigate-prompt"}),await re({flow:"prompt",phase:"navigated",sessionName:r.leadBox,agent:o.agent,provider:a.provider,promptId:a.promptId,fromIndex:a.questionIndex??0,toIndex:n,mode:"keys",value:c.join(" "),count:c.length}).catch(()=>{}),{ok:!0,promptId:a.promptId,sent:c.length}):{ok:!0,promptId:a.promptId,sent:0}}function Wp(e){let t=Os.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Os.set(e,t)),t}function nq(e){let t=Os.get(e);return!!t&&(t.items.length>0||!!t.flushing)}function Ms(e){return(Os.get(e)?.items??[]).map(({delivery:t,...n})=>({...n}))}function gy(e=Date.now()){let t=0,n=0;for(let r of Os.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 rq(e,t,n){let r=t.filter(o=>o.delivery&&!o.delivery.settled);r.length&&(async()=>{let o=fe(await te(),e)||null;for(let s of r)await In(o,s.delivery,`dropped:${n}`)})().catch(()=>{})}function wl(e,t="cancel"){let n=Os.get(e);if(!n)return[];let r=n.items.splice(0);return n.idleSince=null,n.timer&&(clearInterval(n.timer),n.timer=null),rq(e,r,t),r}async function yy(e){let t=wl(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=fe(await te(),e);if(o)return r=ei(o,n),await ae(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function by(e){let t=Wp(e);t.timer||(t.timer=setInterval(()=>oq(e).catch(()=>{}),tq))}async function oq(e){let t=Os.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 te(),e);if(!n||n.archivedAt){wl(e,"archived");return}let r=async(l,u="")=>{let d=[];for(let y of t.items)T_(y.delivery,l),VI(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(Rg()/6e4)),w=String(u||"").split(`
|
|
224
|
+
(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 ny(e){return e.role==="user"&&LW.test((e.text||"").trim())}async function te(){return Ht||(Jg||=(async()=>{try{Ht=JSON.parse(await Y.readFile(E$,"utf8"))}catch{Ht={tracks:[]}}finally{Jg=null}return Ht})(),Jg)}async function ae(){await Bt(E$,Ht)}function UW(e,t){let n=JSON.stringify({trackId:e,...t})+`
|
|
225
|
+
`;return Yg=Yg.then(()=>Y.mkdir(nr,{recursive:!0})).then(()=>Y.appendFile(DW,n)).catch(()=>{}),Yg}async function I$(e,{box:t,agent:n,message:r}){let o=fe(await te(),e);!o||o.archivedAt||(Ee(o,"startup-gate",{box:t,agent:n??null,message:r}),await ae(),kc(o,{provider:n??null,cls:"startup-gate",note:r}).catch(()=>{}))}async function or({current:e,latest:t,min:n=null,installKind:r=null,required:o=!1,status:s="available",policy:a=null,error:i=null,reason:c=null}={}){if(!t)return{announced:0};if(s==="available"){let d=null;try{d=JSON.parse(await Y.readFile(Xg,"utf8"))}catch{}if(d?.latest===t&&d?.current===e)return{announced:0,deduped:!0}}let l=await te(),u=0;for(let d of l.tracks)Ue(d)||!d.leadBox||(Ee(d,"node-update",{status:s,current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},...a?{policy:a}:{},...i?{error:String(i).slice(0,400)}:{},...c?{reason:c}:{},required:o===!0}),u++);if(await ae().catch(()=>{}),s==="available")try{await Y.mkdir(z.dirname(Xg),{recursive:!0}),await Y.writeFile(Xg,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function dy({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await te(),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)||HW(e,c)||(Ee(a,"node-update",{status:"done",current:l.current??null,latest:c,...t?{installKind:t}:{},...n?{policy:n}:{},reason:"version-reconciled",installed:e,required:!1}),o++,s.add(c))}return o&&await ae().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function HW(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 $$(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=>WW.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 R$(e,t,n,{kind:r}={}){let o=await Se(t);if(!o)throw new Error(`box "${t}" is not running`);let s=Ns(o.agent),a=Fc({text:n,producer:jc({kind:r,orchestration:!0}),kind:r||"orchestration",provider:o.agent}),i=fe(await te(),e)||null,c=Date.now(),l=null;try{l=await wr({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s})}catch(y){throw Hc(a,{ms:Date.now()-c,outcome:y?.code==="cli-not-running"?"cli-not-running":"blocked"}),await In(i,a,Ua(y)),y}Hc(a,{ms:Date.now()-c,outcome:l||"up"});let u=Un(o.agent,n);a.paste=!!u.paste;try{await Qe(t,n,{...u,kind:r||"orchestration"})}catch(y){throw await In(i,a,Ua(y)),y}let d={},p=await pc({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration",stats:d});a.verify=d,await In(i,a,p?d.verdict==="unverified"?"unverified":"delivered":"unsubmitted"),await re({flow:"orchestration",phase:"sent",submitted:p,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let f=await te(),w=fe(f,e);if(!w)return;let h=w.boxes.find(y=>y.name===t);Ee(w,"orchestration-prompt",{box:t,agent:h?.agent||o.agent,boxRole:h?.role,prompt:n,kind:r||"orchestration",deliveryId:a.id}),await ae()}async function qW(e){let t=z.join(ra(e),`${e.id}.ndjson`),n;try{n=await Y.stat(t)}catch{return null}let r=y$.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 y$.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function GW(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:yc(n.name,n.agent)}async function Zt(e,t=null,n=null){let r=t??new Map((await kn()).map(S=>[S.name,S])),o=!!e.transferredTo,s=e.boxes.map(S=>{let _=o?null:r.get(S.name),M=jr.has(S.state)?S.state:_?S.role==="lead"?"live":S.state:"stopped";return{...S,state:M,cwd:_?.cwd||S.cwd,agent:_?.agent||S.agent,sessionId:_?.sessionId||S.sessionId}}),[a,{working:i,prompted:c},l,u,d]=await Promise.all([qW(e),GW(e,s),Dg(e),n??En().catch(()=>[]),m$({...e,boxes:s})]),p=dl.has(e.id),f=(u||[]).filter(S=>S.trackId===e.id&&!["merged","closed","lost"].includes(S.state)),w=g$(e.id),h=w&&{status:"waiting",since:w.startedAt,summary:w.summary??null,count:w.count??null}||s$(f),y=s.find(S=>S.name===e.leadBox)?.state==="live",k=y?Ms(e.id).filter(S=>S.kind!=="ship-notify"&&S.kind!=="prompt-answer"&&S.kind!=="login-greet"):[],g=d||(y&&l.runningJobs?{source:"jobs",count:l.runningJobs,since:l.runningJobsSince,summary:l.runningJobs===1?"a background job is still running":`${l.runningJobs} background jobs are still running`}:k.length?{source:"queue",count:k.length,since:Math.min(...k.map(S=>S.ts||Date.now())),summary:"a queued message is about to send"}:null),x=i$({archivedAt:e.archivedAt,archiving:p,prompted:c,openActions:l.openActions,openActionSince:l.openActionSince,prDecisions:a$(f),operation:h,phase:l.phase,working:i,workingSince:l.currentTurnAt,pending:g,completedTurnAt:l.completedTurnAt||a,live:y});return{...e,boxes:s,lastResponseAt:a,working:i,prompted:c,archiving:p,status:x}}function O$(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-KW,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 M$(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=wx(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=O$(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 sx(p.box,p.sessionId),n=!0}}return n}async function ke(){let e=await te();await M$(e)&&await ae();let t=new Map((await kn()).map(r=>[r.name,r])),n=await En().catch(()=>[]);return Promise.all(e.tracks.map(r=>Zt(r,t,n)))}async function Lc(e,t){let n=await te();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 hl(e){let t=Ho(e);if(!t)return null;let n=await te();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Hk(o.hookTokenHash,t))return{track:r,box:o};return null}async function py(e){return fe(await te(),e)??null}async function me(e){let t=await te(),n=fe(t,e);return n?(await M$(t)&&await ae(),Zt(n)):null}async function fy(e){let t=await K(e).catch(()=>[]),n;try{n=Hn(t)}catch{return[]}let r=rd(n).map(s=>({kind:s.kind,id:s.offerId??s.proposalId??s.id,ts:s.ts??null})),o=[];try{o=jv(t)}catch{o=[]}return r.concat(o.map(s=>({kind:"pr-open",id:`${s.repo??""}#${s.number}`,ts:null})))}function ry(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(VW[e.agent]||[]).some(n=>n.test(t))}function oy(e){let t=(e.text||"").trim();return t?zW.some(n=>n.test(t)):!1}function C$(e,t,n=Date.now()){return!e||e.readyAt||e.state!=="live"?!1:n-(t||0)<JW}async function Up(e,t={}){let n=await me(e);if(!n)return null;let r=new Set([...n.events.filter(O=>O.type==="orchestration-prompt"&&O.prompt).map(O=>String(O.prompt).trim()),...n.events.filter(O=>O.type==="said"&&O.text).map(O=>String(O.text).trim())]),o=ai(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=sg(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...L_(n),...l],p={windows:[...JS(n),...YS(n,hd),...a,...u]},w=(await Promise.all(n.boxes.map(O=>Ih(O,t)))).flat(),h=w.map(O=>{let X={kind:"message",...O};if(X.role==="assistant"&&(X.text||"").trim()&&X.tools?.length){let ne=ao(X.agent).cliTools,he=X.tools.filter(Be=>!ne.has(Be));return{...X,tools:he.length?he:void 0}}return X}).map(O=>{if(O.role!=="user")return O;let X=_v(O);return X?X.kind==="suppress"?null:{...O,note:X,text:$v(X)}:O}).filter(Boolean).filter(O=>!(O.role==="user"&&r.has((O.text||"").trim()))).filter(O=>!ry(O)).filter(O=>!oy(O)).filter(O=>!ny(O)).filter(O=>!Ig(O)).filter(O=>O.role!=="assistant"||!!(O.text||"").trim()||!!O.images?.length).filter(O=>!XS(O,p)),y=h.filter(O=>O.failed&&O.text===Zo);if(y.length){let O=new Map;for(let X of y){if(!O.has(X.box)){let Be=[...(await Ne(X.box,{lines:ze}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Pt=>Pt[1]);O.set(X.box,Be.length?Be[Be.length-1]:null)}let ne=O.get(X.box);ne&&(X.text=`\u25A0 ${ne}`)}}let k=await Promise.all(n.boxes.map(O=>IS(O,t)));await p_(n,k.flat());let{agentTurnBoxes:g}=await u_(n,h);for(let O of n.boxes)g.has(O.name)&&O.cwd&&bu(O.cwd);await YA(n),await q_(n,h).catch(()=>{});for(let O of n.boxes)O.agent==="codex"&&O.state==="live"&&await m_(n,O),O.agent==="cursor"&&O.state==="live"&&await b_(n,O);let x=await Promise.all(n.boxes.map(O=>_S(O,t)));await d_(n,x.flat().filter(O=>!Zg(O.box,O.ts,u)));let S=await K(n),_=c&&i.viewId?i.viewId:null,M=lT(S),P=Hn(S).filter(O=>!(O.kind==="message"&&(ry(O)||oy(O)||ny(O)||Ig(O)))&&!(O.kind==="view-card"&&_&&O.viewId===_)),j=uT(P,M),T=P.filter(O=>!(O.kind==="message"&&O.role==="assistant"&&!j.has(O)&&Zg(O.box,O.ts,M))),$=uh(S,T).map(O=>({kind:"command-batch",id:O.id,ts:O.pending?O.ts:(O.anchorTs??O.ts)-1,anchorTs:O.anchorTs,anchorBox:O.anchorBox,agent:O.agent,boxRole:O.boxRole,commands:O.commands,pending:O.pending})).filter(O=>!Zg(O.anchorBox,O.anchorTs??O.ts,u)),L=Bv(Lv([...T,...$].filter(O=>O.ts!=null).sort((O,X)=>O.ts-X.ts))),H=YW(n),R=H.size?L.filter(O=>O.kind==="event"||!H.has(O.box??O.anchorBox)):L,A=n.boxes.find(O=>O.name===n.leadBox)||null,C=A?await Zi(A.cwd,A.sessionId,{fallbackNewest:!1}):null;if(!C&&A){let O=[...S].reverse().find(X=>X.type==="context.usage"&&X.boxId===A.name);O?.payload?.window&&(C={pct:O.payload.pct??(O.payload.occupancy!=null?O.payload.occupancy/O.payload.window:null),occupancy:O.payload.occupancy??null,window:O.payload.window,windowKnown:!0,model:O.payload.model||null})}let W=[...w].reverse().find(O=>O.role==="assistant"&&O.model),B=W?.model||null,Z=[...n.events||[]].reverse().find(O=>O.type==="model-change"),xe=C?.model||B;Z&&(!W?.ts||Z.at>=W.ts)&&(xe=Z.to);let Ae=A?await Ne(A.name,{lines:ze}).catch(()=>""):"",_e=A&&oo(A.agent,Ae)?await Jt(A.name).catch(()=>Ae):Ae,Q=A?Sn(A,_e):null;if(A&&hy(A.name,Q,_e)&&Q&&(Q={...Q,answering:!0}),Q&&A){let O=ad(A,Q,_e);if(O){let X=iS(A.name,Q.promptId);X.action!=="give-up"&&(X.action==="send"&&(O.accept&&Ku(A.name,_e),(!O.gate||eo(A.name,O.gate))&&Ke(A.name,O.keys,{kind:O.accept?"auto-accept":"auto-reject"}).catch(()=>{}),X.first&&N(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:A.name,provider:A.agent,payload:{ts:Date.now(),promptId:Q.promptId,rule:O.id}}).catch(()=>{})),Q=null)}}A&&(Q?.setup&&Xx(A.name,_e),!Q&&oc(A.agent,Ae)&&qu(A.name)&&eo(A.name,"login-issue")&&(Gu(A.name),Qe(A.name,"/login",{enter:!0,submitDelayMs:600,kind:"login-issue"}).catch(()=>{}))),!Q&&A&&!ya(A.agent,Ae)&&!Ms(n.id).length&&(Q=ih(R,{leadBoxId:A.name,provider:A.agent,isAnswered:O=>S.some(X=>X.type==="prompt.answered"&&X.payload?.promptId===O)})),Q&&XA(n,A,Q);let Xe=R.filter(O=>!(O.kind==="message"&&O.role==="assistant"&&O.ask&&!String(O.text||"").trim()&&!O.images?.length)),le=!!A&&id(A.name,A.agent,Ae)&&!Q;if(A&&!A.readyAt&&(le||Q||Ae&&to(A.agent,Ae))){A.readyAt=Date.now();let O=fe(await te(),n.id)?.boxes?.find(X=>X.name===A.name);O&&!O.readyAt&&(O.readyAt=A.readyAt,await ae())}let ge=C$(A,n.createdAt),de=!!A&&!!Ae&&to(A.agent,Ae)&&!Q&&!oc(A.agent,Ae)&&!ga(A.agent,Ae),ee=!1;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&A){if(de){let O=await te(),X=fe(O,n.id);X?.setup&&!X.setup.connectedAt&&(X.setup.connectedAt=Date.now(),n.setup.connectedAt=X.setup.connectedAt,ee=!0,await ae())}}else A&&de&&Yx(A.name)&&(ee=!0);if(ee&&Zx(A.name),A){let O=Qx(A.name),X=Jx(A.name);O.started&&N(n,{type:"provider.login.started",actor:"system",source:"pane",boxId:A.name,provider:A.agent,payload:{provider:A.agent,reason:X?.startedReason||"auth-screen",ts:Date.now()}}).catch(()=>{}),O.connected&&(await N(n,{type:"provider.connected",actor:"system",source:"pane",boxId:A.name,provider:A.agent,payload:{provider:A.agent,ts:X?.connectedAt||Date.now(),...X?.account?{account:X.account}:{},reason:X?.startedReason||"already-signed-in"}}).catch(()=>{}),n.kind!=="setup"&&X?.startedReason&&!sq(n.id)&&qt(n.id,P$,{kind:"login-greet"}).catch(ne=>{console.warn(`[track ${n.id}] login greeting not delivered: ${ne?.message||ne}`)}))}let we=s?{running:!0,step:o.step,toAgent:o.toAgent||null,wait:o.wait||null}:null,Oe=gl(n.id),Ve=Oe&&!Oe.finishedAt?{running:!0,step:Oe.step||null,added:Oe.added||[],removed:Oe.removed||[]}:null,Nt=null;if(c){let O=Date.now()-i.startedAt;if(i.viewId&&!le){let X=mp(n.id);X&&(Ee(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:X.finishedAt}),await ae())}else if(Q||O>D_&&!le&&!i.viewId||O>og){let X=Q?"needs-input":O>og?"timeout":"no-view";mp(n.id,{error:X})&&await N(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:A?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:X}}).catch(()=>{})}else Nt={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:Xe,context:C?{pct:C.pct,occupancy:C.occupancy,window:C.window,windowKnown:C.windowKnown,model:C.model}:null,models:{current:xe,options:nw(A?.agent)},pendingPrompt:Q,working:le,stopping:B$(n.id),leadStarting:ge,handoff:we,reshape:Ve,view:Nt,queued:Ms(n.id).filter(O=>O.kind!=="ship-notify"&&O.kind!=="prompt-answer"&&O.kind!=="login-greet"),draft:n.draft||null,draftRev:n.draftRev||0}}function YW(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 my(e,t){let n=await me(e);if(!n)return null;let r=Bs(e).find(d=>d.id===t)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await 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=uh(s.filter(d=>d.boxId===o),a.filter(d=>d.kind==="message")).map(d=>({kind:"command-batch",id:d.id,ts:d.pending?d.ts:(d.anchorTs??d.ts)-1,anchorTs:d.anchorTs,anchorBox:d.anchorBox,agent:d.agent,boxRole:d.boxRole,commands:d.commands,pending:d.pending})),c=[...a,...i].filter(d=>d.ts!=null).sort((d,p)=>d.ts-p.ts),l=n.boxes.find(d=>d.name===o)||null,u=!1;if(l&&l.state==="live"){let d=await Ne(o,{lines:ze}).catch(()=>"");u=id(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}function XW(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>b$)throw new Error(`custom answer is too long (max ${b$} characters)`);return r})}function ZW(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 hy(e,t,n,r=Date.now()){let o=sy.get(e);if(!o)return!1;let s=!!t&&t.promptId===o.promptId&&r-o.at<QW&&(t.setup?!0:N$(n)===o.paneHash);return s||sy.delete(e),s}async function tq(e,t,n){let r=n;for(let o of eq){await rr(o);let s=await Jt(e).catch(()=>null);if(s!=null&&(r=s,s!==n))break}sy.set(e,{promptId:t.promptId,paneHash:N$(r),setup:t.setup||null,at:Date.now()})}async function Hp(e,t={}){let n=rl.get(e);n&&await n.catch(()=>{});let r=nq(e,t);rl.set(e,r);try{return await r}finally{rl.get(e)===r&&rl.delete(e)}}async function nq(e,{promptId:t,answers:n,text:r,custom:o}={}){let s=fe(await te(),e);if(!s)throw new Error(`track "${e}" not found`);ri(e,"prompt-answer");let a=s.boxes.find(g=>g.name===s.leadBox);if(!a)throw new Error(`track "${e}" has no lead box`);if(!await Se(s.leadBox))throw new Error(`lead box "${s.leadBox}" is not running`);let c=await Jt(a.name).catch(()=>""),l=Sn(a,c);if(l||(l=await rq(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"&&hy(a.name,l,c))throw new Error("prompt already answered \u2014 waiting for the CLI to move on");let u=XW(o,l.questions?.length??1);if(u.some(Boolean)&&!l.custom)throw new Error("custom answer isn't available for this prompt \u2014 pick one of its options");let d=(n||[]).some(g=>Array.isArray(g)&&g.length>0);if(l.custom&&!d&&!u.some(Boolean)&&l.answerMode!=="text")throw new Error("no answer selected \u2014 pick an option or type your own");u.some(Boolean)&&(n=(l.questions||[]).map((g,x)=>u[x]&&!g.multiSelect?[]:Array.isArray(n?.[x])?n[x]:[]));let p;if(l.answerMode==="text"){let g=typeof r=="string"?r.trim():"";if(!g)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(g)||g.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await Qe(s.leadBox,g,{enter:!0,kind:"answer-prompt"}),p={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(l.answerMode==="esc")await Ke(s.leadBox,["Escape"],{kind:"answer-prompt"}),p={mode:"keys",value:"Escape",count:1};else if(l.answerMode==="say"){let g=Cv(l,n||[],typeof r=="string"?r:"",u);if(!g)throw new Error("no answer selected \u2014 pick an option or reply in chat");await qt(s.id,g,{kind:"prompt-answer"}),p={mode:"say",value:g,count:1}}else if(u[0]&&l.provider==="claude"){let g=rS(l,n||[],u[0]);await Rm(s.leadBox,[...g.pre,{text:g.text}],{kind:"answer-prompt"});let x=!1;for(let _ of[150,300,600]){await rr(_);let M=await Jt(s.leadBox).catch(()=>""),P=Sn(a,M);if(P?.customText&&ZW(P.customText,g.text)){x=!0;break}}if(!x)throw new Error("custom answer didn't take \u2014 the widget moved; reload and answer again");await Rm(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=gh(l,n||[]);await Ke(s.leadBox,g,{kind:"answer-prompt"}),p={mode:"keys",value:g.join(" "),count:g.length}}else{let g=qx(l,n||[]);await Qe(s.leadBox,g,{enter:!0,kind:"answer-prompt"}),p={mode:"text",value:g,count:1}}if(l.answerMode!=="say"&&await tq(a.name,l,c),l.answerMode==="say"){let g=l.questions||[];for(let x=0;x<g.length;x++){let S=g[x],_=new Set(Array.isArray(n?.[x])?n[x]:[]),M=[..._].map(j=>S.options?.[j]).filter(Boolean).map(j=>({label:j.label,description:j.description||""}));u[x]&&M.push({label:u[x],description:"",custom:!0});let P=(S.options||[]).map((j,T)=>_.has(T)?null:j?.label).filter(Boolean);rw(s,a,l,{...p,groupId:l.groupId||l.promptId,questionIndex:x,questionTotal:l.questionTotal??g.length,header:S.header||"",question:S.question||"",multiSelect:!!S.multiSelect,chosen:M,rejected:P})}return await re({flow:"prompt",phase:"answered",sessionName:s.leadBox,agent:a.agent,provider:l.provider,promptId:l.promptId,...p}).catch(()=>{}),{ok:!0,promptId:l.promptId,sent:p.count}}let f=l.questions?.[0]||{},w=new Set(Array.isArray(n?.[0])?n[0]:[]),h=[...w].map(g=>f.options?.[g]).filter(Boolean).map(g=>({label:g.label,description:g.description||""}));u[0]&&h.push({label:u[0],description:"",custom:!0});let y=(f.options||[]).map((g,x)=>w.has(x)?null:g?.label).filter(Boolean),k=l.review?"review":/^claude:model-confirm:/.test(l.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(l.promptId||"")?"resume-confirm":l.setup==="continue"?"login-continue":void 0;return p={...p,groupId:l.groupId||l.promptId,questionIndex:l.questionIndex??0,questionTotal:l.questionTotal??1,header:f.header||"",question:f.question||"",multiSelect:!!f.multiSelect,chosen:h,rejected:y,...k?{control:k}:{}},await re({flow:"prompt",phase:"answered",sessionName:s.leadBox,agent:a.agent,provider:l.provider,promptId:l.promptId,...p}).catch(()=>{}),rw(s,a,l,p),{ok:!0,promptId:l.promptId,sent:p.count}}async function rq(e,t){if(!t)return null;let n=await K(e).catch(()=>[]);return ih(Hn(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function wy(e,{promptId:t,questionIndex:n}={}){let r=fe(await te(),e);if(!r)throw new Error(`track "${e}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(!await Se(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await tS(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=oS(a,n);return c.length?(await Ke(r.leadBox,c,{kind:"navigate-prompt"}),await re({flow:"prompt",phase:"navigated",sessionName:r.leadBox,agent:o.agent,provider:a.provider,promptId:a.promptId,fromIndex:a.questionIndex??0,toIndex:n,mode:"keys",value:c.join(" "),count:c.length}).catch(()=>{}),{ok:!0,promptId:a.promptId,sent:c.length}):{ok:!0,promptId:a.promptId,sent:0}}function Wp(e){let t=Os.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Os.set(e,t)),t}function sq(e){let t=Os.get(e);return!!t&&(t.items.length>0||!!t.flushing)}function Ms(e){return(Os.get(e)?.items??[]).map(({delivery:t,...n})=>({...n}))}function gy(e=Date.now()){let t=0,n=0;for(let r of Os.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 aq(e,t,n){let r=t.filter(o=>o.delivery&&!o.delivery.settled);r.length&&(async()=>{let o=fe(await te(),e)||null;for(let s of r)await In(o,s.delivery,`dropped:${n}`)})().catch(()=>{})}function wl(e,t="cancel"){let n=Os.get(e);if(!n)return[];let r=n.items.splice(0);return n.idleSince=null,n.timer&&(clearInterval(n.timer),n.timer=null),aq(e,r,t),r}async function yy(e){let t=wl(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=fe(await te(),e);if(o)return r=ei(o,n),await ae(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function by(e){let t=Wp(e);t.timer||(t.timer=setInterval(()=>iq(e).catch(()=>{}),oq))}async function iq(e){let t=Os.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 te(),e);if(!n||n.archivedAt){wl(e,"archived");return}let r=async(l,u="")=>{let d=[];for(let y of t.items)__(y.delivery,l),JI(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(Rg()/6e4)),w=String(u||"").split(`
|
|
226
226
|
`).map(y=>y.trimEnd()).filter(y=>y.trim()).slice(-3).join(`
|
|
227
|
-
`),h=l==="noSession"?`The scheduled prompt waited ${f} min for the box's session to come back and was dropped \u2014 open the terminal.`:`The scheduled prompt waited ${f} min for the box to become idle and was dropped \u2014 open the terminal.`;for(let y of d)y.delivery&&await In(n,y.delivery,l==="noSession"?"failed:not-running":"failed:blocked");Ee(n,"delivery-held",{box:n.leadBox,agent:o?.agent??null,reason:l,heldMs:p,producer:"loop",deliveryId:d[0]?.delivery?.id??null,message:h,...w?{detail:w}:{}}),await ae(),kc(n,{provider:o?.agent??null,cls:"delivery-held",note:h}).catch(()=>{})},o=n.boxes.find(l=>l.name===n.leadBox),s=ai(e);if(s&&!s.finishedAt&&s.step!=="done"&&!s.error){Rs(t,!1),await r("handoff");return}let a=await Se(n.leadBox);if(a&&await Ny(a)&&(a=await pl(n,o,"queue")?await Se(n.leadBox):null,await ae()),!a){Rs(t,!1),await r("noSession");return}if(mo(o?.agent)){Rs(t,!1),await r("cap");return}let i=await Ne(n.leadBox,{lines:ze}).catch(()=>"");if(ac(o?.agent,i)){Rs(t,!1),await r("busy",i);return}if(oo(o?.agent,i)){let l=await Jt(n.leadBox).catch(()=>i);if(Sn(o,l)){Rs(t,!1),await r("prompt");return}}if(!Rs(t,!0)){await r("grace");return}let c=t.items.shift();t.idleSince=null,c.delivery&&(c.delivery.dequeuedAt=Date.now());try{await ay(n,o,a,c.text,c.kind??null,c.attachments??null,c.delivery??null)}catch(l){if(l?.code!=="cli-not-running")throw c.delivery&&await In(n,c.delivery,Ua(l)),l;t.items.unshift(c),c.delivery&&(c.delivery.requeued+=1,c.delivery.dequeuedAt=null,await Uc(n,c.delivery,{position:1,reason:"requeue"})),await pl(n,o,"queue-gate")}await ae()}finally{t.flushing=!1}}}async function
|
|
227
|
+
`),h=l==="noSession"?`The scheduled prompt waited ${f} min for the box's session to come back and was dropped \u2014 open the terminal.`:`The scheduled prompt waited ${f} min for the box to become idle and was dropped \u2014 open the terminal.`;for(let y of d)y.delivery&&await In(n,y.delivery,l==="noSession"?"failed:not-running":"failed:blocked");Ee(n,"delivery-held",{box:n.leadBox,agent:o?.agent??null,reason:l,heldMs:p,producer:"loop",deliveryId:d[0]?.delivery?.id??null,message:h,...w?{detail:w}:{}}),await ae(),kc(n,{provider:o?.agent??null,cls:"delivery-held",note:h}).catch(()=>{})},o=n.boxes.find(l=>l.name===n.leadBox),s=ai(e);if(s&&!s.finishedAt&&s.step!=="done"&&!s.error){Rs(t,!1),await r("handoff");return}let a=await Se(n.leadBox);if(a&&await Ny(a)&&(a=await pl(n,o,"queue")?await Se(n.leadBox):null,await ae()),!a){Rs(t,!1),await r("noSession");return}if(mo(o?.agent)){Rs(t,!1),await r("cap");return}let i=await Ne(n.leadBox,{lines:ze}).catch(()=>"");if(ac(o?.agent,i)){Rs(t,!1),await r("busy",i);return}if(oo(o?.agent,i)){let l=await Jt(n.leadBox).catch(()=>i);if(Sn(o,l)){Rs(t,!1),await r("prompt");return}}if(!Rs(t,!0)){await r("grace");return}let c=t.items.shift();t.idleSince=null,c.delivery&&(c.delivery.dequeuedAt=Date.now());try{await ay(n,o,a,c.text,c.kind??null,c.attachments??null,c.delivery??null)}catch(l){if(l?.code!=="cli-not-running")throw c.delivery&&await In(n,c.delivery,Ua(l)),l;t.items.unshift(c),c.delivery&&(c.delivery.requeued+=1,c.delivery.dequeuedAt=null,await Uc(n,c.delivery,{position:1,reason:"requeue"})),await pl(n,o,"queue-gate")}await ae()}finally{t.flushing=!1}}}async function D$(e,t,n,r){let o=mo(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(ei(e,[n]),await ae()),NA(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 cq(e,t){let n=await Ne(e.leadBox,{lines:ze}).catch(()=>"");if(!oo(t?.agent,n))return!1;let r=await Jt(e.leadBox).catch(()=>n);return!!Sn(t,r)}async function ay(e,t,n,r,o=null,s=null,a=null){a||(a=Fc({text:r,producer:jc({kind:o}),kind:o,provider:t?.agent??null,attachments:Array.isArray(s)?s.length:0})),a.provider??=t?.agent??null,await D$(e,t,r,o);let i=cl.get(e.id)||0,c=r,l=!1,u=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(c=_$(r,e.pendingHandoffHydrate.artifact||Io),delete e.pendingHandoffHydrate,l=!0);let d=Array.isArray(s)?s.filter(h=>h&&h.rel):[];if(d.length){let h=zI(d.map(y=>y.rel));c=c.trim()?`${c.trim()}
|
|
228
228
|
|
|
229
|
-
${h}`:h}let p=Ee(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,deliveryId:a.id,...o?{kind:o}:{},...d.length?{attachments:d}:{}});o||Qa(e,null);try{let h=Date.now(),y=null;try{y=await wr({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:Ns(n.agent),bailOnDialog:!0})}catch(g){throw Hc(a,{ms:Date.now()-h,outcome:g?.code==="cli-not-running"?"cli-not-running":"blocked"}),g}if(Hc(a,{ms:Date.now()-h,outcome:y||"up"}),(cl.get(e.id)||0)!==i){!o&&r.trim()&&(ei(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
|
|
229
|
+
${h}`:h}let p=Ee(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,deliveryId:a.id,...o?{kind:o}:{},...d.length?{attachments:d}:{}});o||Qa(e,null);try{let h=Date.now(),y=null;try{y=await wr({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:Ns(n.agent),bailOnDialog:!0})}catch(g){throw Hc(a,{ms:Date.now()-h,outcome:g?.code==="cli-not-running"?"cli-not-running":"blocked"}),g}if(Hc(a,{ms:Date.now()-h,outcome:y||"up"}),(cl.get(e.id)||0)!==i){!o&&r.trim()&&(ei(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 cq(e,t)){let g=Wp(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),by(e.id),await Uc(e,a,{position:1,reason:"prompt"});let S=new Error("lead is blocked on a prompt \u2014 message queued");throw S.requeued={id:x.id,position:g.items.length},S}let k=Un(t?.agent,c);a.paste=!!k.paste,await Qe(e.leadBox,c,{...k,kind:l?"lead-handoff-first-prompt":"say"})}catch(h){if(u&&!e.pendingHandoffHydrate&&(e.pendingHandoffHydrate=u),Ee(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:p.at,text:r,...h?.interrupted?{reason:"interrupted"}:{},...h?.requeued?{reason:"requeued"}:{}}),h?.requeued)return{queued:!0,...h.requeued};throw h?.code!=="cli-not-running"&&await In(e,a,Ua(h)),h}let f={},w=await Aq(e,t,c,f);a.verify=f,w?await In(e,a,f.verdict==="unverified"?"unverified":"delivered"):(await L$(e.leadBox,t?.agent,c.length+8,c).catch(()=>{}),Ee(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:p.at,text:r,reason:"unsubmitted"}),o||ei(e,[r]),await In(e,a,"unsubmitted")),l&&await re({flow:"lead-handoff",phase:"first-prompt",trackId:e.id,sessionName:e.leadBox,agent:t?.agent,userTextLen:r.length})}function ky(e,{actions:t}={}){let n=Wp(e),r=dT(t),o=Fc({text:r,producer:jc({kind:"ship-notify"}),kind:"ship-notify"});n.items.push({id:o.id,text:r,ts:Date.now(),kind:"ship-notify",delivery:o}),by(e),(async()=>{let s=fe(await te(),e);s&&(o.provider=s.boxes.find(a=>a.name===s.leadBox)?.agent??null,await Uc(s,o,{position:n.items.length,reason:"queue"}))})().catch(()=>{})}async function lq(e,t,n,r){await Ke(e.leadBox,["Escape"],{kind:"dismiss-prompt"});let o="",s=!1;for(let c of[250,400,600,900]){if(await rr(c),o=await Ne(e.leadBox,{lines:ze}).catch(()=>""),!oo(t?.agent,o)){s=!0;break}let l=await Jt(e.leadBox).catch(()=>o);if(!Sn(t,l)){s=!0;break}}let a=n.questions?.[0]||{},i={groupId:n.groupId||n.promptId,questionIndex:n.questionIndex??0,questionTotal:n.questionTotal??1,header:a.header||"",question:a.question||"",multiSelect:!!a.multiSelect,options:(a.options||[]).map(c=>c?.label).filter(Boolean),via:"send",deliveryId:r?.id??null,gone:s};return re({flow:"prompt",phase:"dismissed",sessionName:e.leadBox,agent:t?.agent,provider:n.provider,promptId:n.promptId,...i}).catch(()=>{}),s&&ZA(e,t,n,i),{gone:s,pane:o}}async function qt(e,t,{kind:n=null,attachments:r=null,producer:o=null,dismissPrompt:s=!1}={}){let a=Array.isArray(r)?r.filter(d=>d&&typeof d.rel=="string"):[];if((typeof t!="string"||!_a(t))&&!a.length)throw new Error("text (string) or an attachment required");let i=fe(await te(),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`);Zp(i);let c=i.boxes.find(d=>d.name===i.leadBox),l=_a(t),u=Fc({text:l,producer:jc({kind:n,producer:o}),kind:n,provider:c?.agent??null,attachments:a.length});try{return await uq(e,i,c,l,{kind:n,atts:a,d:u,dismissPrompt:s})}catch(d){throw await In(i,u,Ua(d)),d}}async function uq(e,t,n,r,{kind:o,atts:s,d:a,dismissPrompt:i=!1}){await D$(t,n,r,o),ri(e,"say");let c=await Se(t.leadBox);if(c&&await Ny(c)?await pl(t,n,"say")?(await ae(),c=await Se(t.leadBox)):c=null:!c&&await si(t)&&(await ae(),c=await Se(t.leadBox)),!c)throw new Error(`lead box "${t.leadBox}" is not running`);let l=rl.get(e);l&&await l.catch(()=>{});let u=Wp(e),d=await Ne(t.leadBox,{lines:ze}).catch(()=>""),p=u.items.length>0?"queue":ac(n?.agent,d)?"busy":null,f=p!=null,w=i&&!o;if((!f||p==="queue"&&w)&&oo(n?.agent,d)){let y=await Jt(t.leadBox).catch(()=>d),k=Sn(n,y);if(f||(f=!!k,f&&(p="prompt")),k&&w&&k.dismissable){let g=await lq(t,n,k,a);g.gone&&p==="prompt"&&(d=g.pane,f=ac(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),by(e),o||Qa(t,null),await ae(),await Uc(t,a,{position:u.items.length,reason:p}),{ok:!0,leadBox:t.leadBox,queued:!0,id:y.id,position:u.items.length,deliveryId:a.id}}let h;try{h=await ay(t,n,c,r,o,s,a)}catch(y){if(y?.code!=="cli-not-running"||!await pl(t,n,"say-gate")||(await ae(),c=await Se(t.leadBox),!c))throw y;h=await ay(t,n,c,r,o,s,a)}return await ae(),h?.queued?{ok:!0,leadBox:t.leadBox,queued:!0,id:h.id,position:h.position,deliveryId:a.id}:{ok:!0,leadBox:t.leadBox,deliveryId:a.id}}async function xy(e,{hint:t}={}){let n=fe(await te(),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=sg(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";B_(n.id,{name:s,box:n.leadBox});let a=String(t||"").trim()?`${k$}
|
|
230
230
|
|
|
231
|
-
What to preview: ${String(t).trim()}`:
|
|
231
|
+
What to preview: ${String(t).trim()}`:k$;try{await qt(e,a,{kind:"spin-up-view"})}catch(i){throw mp(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function qp(e){let t=fe(await te(),e);if(!t)throw new Error(`track "${e}" not found`);if(t.archivedAt)throw new Error(`track "${e}" is archived`);cl.set(e,(cl.get(e)||0)+1);let n=wl(e,"interrupt"),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=Za.get(e);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=ei(t,r),await ae()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:t.draftRev||0}}let s=wq(e,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,rr(dq)]);let a=fe(await te(),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 B$(e){let t=Za.get(e);return!!t&&!t.canceled}function ri(e,t){let n=Za.get(e);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=t||null)}function vy(e){ri(e,"track-ended"),cl.delete(e),wl(e,"track-ended")}function wq(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}),Za.set(e,r),(async()=>{let s=fe(await te(),e),a=s?.boxes.find(y=>y.name===s.leadBox),i=s?await Se(s.leadBox):null,c=!1,l=!1;if(i){e:for(let y=0;y<pq&&!r.canceled;y++){await Ke(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let k of fq){await rr(k);let g=await Ne(s.leadBox,{lines:ze}).catch(()=>"");if(!ya(a?.agent,g)){c=!0;break e}}}if(!c){l=!0;for(let y=0;y<hq&&!r.canceled&&(await rr(mq),!r.canceled);y++){let k=await Ne(s.leadBox,{lines:ze}).catch(()=>"");if(!ya(a?.agent,k)){c=!0;break}await Ke(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=fe(await te(),e)||s,d=u?.boxes.find(y=>y.name===u.leadBox)||a,p=null,f=null;u&&d&&i&&c&&!r.canceled&&(p=await kq(u,d).catch(()=>null),p||(f=await Sq(u,d).catch(()=>null)));let w=u&&i&&!r.canceled?await yq(u).catch(()=>null):null,h=[p||f,...r.queuedTexts].filter(Boolean);u&&h.length&&(r.restored=ei(u,h),await ae()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await gq(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 re({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"),Za.get(e)===r&&Za.delete(e),o()}),r}async function gq(e,t,n){await N(e,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:e.leadBox,provider:t?.agent||null,payload:n}).catch(()=>{})}async function yq(e){let t=Wa(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 kq(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 bq){await rr(r);let o=await Ne(e.leadBox,{lines:ze}).catch(()=>""),s=lc(t.agent,o);if(s&&Yu(n.text,s))return await L$(e.leadBox,t.agent,n.text.length+8),Ee(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:n.at,text:n.text}),n.text}return null}async function Sq(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>xq||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!He(r.text))return null;for(let s of vq){await rr(s);let a;try{a=await Ih(t,{})}catch{return null}if(iv(a,r))return null;let i=await Ne(e.leadBox,{lines:ze}).catch(()=>"");if(lc(t.agent,i))return null}return Ee(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}async function Aq(e,t,n,r=null){return pc({sessionName:e.leadBox,agent:t?.agent,agentCmd:Ns(t?.agent),text:n,trackId:e.id,flow:"say",stats:r})}async function L$(e,t,n,r=null){for(let o=0;o<3;o++){await ua(e,n,{kind:"unsend-wipe"}).catch(()=>{}),await rr(200);let s=await Ne(e,{lines:ze}).catch(()=>""),a=lc(t,s);if(!a||r&&!Yu(r,a)&&!(t==="codex"&&Zm(r,a)))return!0;n=a.length+8}return!1}function Qa(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 ei(e,t){let n=[],r=o=>n.some(s=>He(s).includes(He(o)));for(let o of[e.draft?.text,...t])o&&o.trim()&&!r(o)&&n.push(o);return n.length?Qa(e,n.join(`
|
|
232
232
|
|
|
233
|
-
`),{source:"restore"})?.text??null:null}function
|
|
233
|
+
`),{source:"restore"})?.text??null:null}function Eq(e,t){let n=He(t);if(!n)return!1;if(Ms(e.id).some(o=>He(o.text)===n))return!0;let r=e.events||[];for(let o=r.length-1;o>=0;o--){let s=r[o];if(typeof s.at=="number"&&Date.now()-s.at>Tq)break;if(!(s.type!=="said"||!s.text||He(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function Sy(e,t,{rev:n=null}={}){if(typeof t!="string")throw new Error("text (string) required");let r=fe(await te(),e);if(!r)throw new Error(`track "${e}" not found`);let o=t,s=_a(t);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&Eq(r,s))return{ok:!0,draft:r.draft||null,rev:null,ignored:"already-sent"};if(typeof n=="number"&&n<(r.draftRev||0)&&!o.trim()&&r.draft?.text)return{ok:!0,draft:r.draft,rev:r.draftRev||0,ignored:"stale-clear"};if(typeof n=="number"&&n<(r.draftRev||0)&&r.draft?.source==="restore"){let i=r.draft.text;He(o).includes(He(i))?Qa(r,o):Qa(r,`${i}
|
|
234
234
|
|
|
235
|
-
${o}`,{source:"restore"})}else Qa(r,o);return await ae(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Ay(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=fe(await te(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await Se(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(d=>d.name===r.leadBox)?.agent||o.agent||"claude",i=HA(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await Zi(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await wr({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:Ns(o.agent)});let u=await WA(a,r.leadBox,i);return Ee(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await ae(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function B$(e){for(let t of e){if(!t||!await Ia(t))continue;let n=await xA(t).catch(()=>null);if(!n)continue;if((await Y.stat(z.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function Gt(e){let t=e;return t?t.kind==="multi"?Array.isArray(t.repos)?t.repos.filter(Boolean):[]:t.base?[{name:z.basename(t.base),dir:t.dir??null,base:t.base,baseRef:t.baseRef??null,repo:t.repo??null}]:[]:[]}async function Es(e){let t=[];for(let n of Gt(e?.workspace)){if(!n?.dir)continue;let r=await ds(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await ps(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function Ty(e){let t=e;typeof t=="string"&&!z.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await Zs(t)??await du(t));let n=await B$([t]);if(!n){let r=typeof e=="string"&&z.isAbsolute(e)&&!await Y.stat(e).catch(()=>null);throw new Error(`tagged repo "${e}" is not a usable base (${r?"that path does not exist on this node \u2014 pick one of this node's repos or use owner/name":"need a primary checkout or a synced owner/name"})`)}return n}async function Aq(e,t,n,r){let o=`track/${tf(t)}-${e}`,s=[];try{await Y.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await Ty(i);if(s.some(f=>f.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await Me(c).catch(()=>null),u=z.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let f=2;a.has(u);f++)u=`${z.basename(c)}-${f}`;a.add(u);let d=z.join(n,u),p=await ms(c);await fs(c,d,o,p),s.push({name:u,dir:d,base:c,baseRef:p,repo:l})}return await oi(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await $t(i.base,i.dir).catch(()=>{});throw await Y.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function Gp(e,t){let n=await te(),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 Ty(typeof t=="string"?t.trim():t);if((o.repos??[]).some(p=>p?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await Me(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(p=>p?.name).filter(Boolean)]),c=z.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let p=2;i.has(c);p++)c=`${z.basename(s)}-${p}`;let l=z.join(o.dir,c),u=await ms(s);await Vh(s,o.branch)?await vc(s,l,o.branch):await fs(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 oi(o.dir,o.repos,o.branch),Ee(r,"retagged",{action:"add",repo:a??s,name:c,workspace:Ey(o)}),await ae(),d}async function Kp(e,t){let n=await te(),r=fe(n,e);if(!r)throw new Error(`track "${e}" not found`);let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track");let s=(o.repos??[]).find(c=>c&&(c.name===t||c.repo===t||c.base===t));if(!s)throw new Error(`repo "${t}" is not tagged on this Track`);if((o.repos??[]).filter(Boolean).length<=1)throw new Error("can't untag the last repo \u2014 archive or delete the Track instead");for(let c of Wt.values())if(c.trackId===e&&!c.finishedAt)throw new Error("a subtask is still running on this Track \u2014 wait for it to merge back before untagging");let a=await ds(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await ps(s.dir,s.baseRef).catch(()=>0);if(a||i>0)throw new Error(`"${s.name}" has work (${a?"uncommitted changes":`${i} commit${i===1?"":"s"} ahead of ${s.baseRef??"base"}`}) \u2014 ship or discard it first`);return await $t(s.base,s.dir).catch(()=>{}),await Y.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await oi(o.dir,o.repos,o.branch),Ee(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:Ey(o)}),await ae(),{removed:s.name}}async function Mp(e,t){let n=await te(),r=fe(n,e);r&&(r.reshape={...r.reshape||{},...t},await ae())}async function Tq(e){let t=await Y.readdir(e).catch(()=>[]);for(let n of t)if(!(n===".git"||n===".bostrat")){if(n==="AGENTS.md"){let r=await Y.readFile(z.join(e,n),"utf8").catch(()=>"");if(sl(r))continue}return!0}try{let{stdout:n}=await Bp("git",["-C",e,"rev-list","--count","HEAD"]);if(Number(n.trim())>1)return!0}catch{}return!1}function b$(e,t,n){let r=z.basename(e);n.has(r)&&t&&(r=t.replace("/","-"));for(let o=2;n.has(r);o++)r=`${z.basename(e)}-${o}`;return n.add(r),r}async function L$(e,{added:t=[]}={}){let{shape:n,dir:r,staging:o,sub:s,base:a,scratch:i}=e;for(let l of t)await $t(l.base,l.dir).catch(()=>{});let c=async(l,u)=>{await On(l)&&!await On(u)&&await Y.rename(l,u)};if(n==="worktree"&&s){let l=z.join(r,s);await On(l)&&(await c(z.join(r,".bostrat"),z.join(l,".bostrat")),await Cp(a,l,o))}if(n==="repo"&&i){let l=z.join(r,i);await On(l)&&(await On(o)&&await Y.rm(o,{recursive:!0,force:!0}),await Y.rename(l,o))}return await On(o)?(await On(r)&&(await c(z.join(r,".bostrat"),z.join(o,".bostrat")),await Y.rm(r,{recursive:!0,force:!0})),n==="worktree"?await Cp(a,o,r):await Y.rename(o,r),!0):!1}async function _y(e,t,{skipPermissions:n=!0}={}){let r=await te(),o=fe(r,e);if(!o)throw St(`track "${e}" not found`,404);if(o.archivedAt)throw St("track is archived \u2014 unarchive it first");try{Zp(o)}catch(y){throw Object.assign(y,{status:409})}if(o.kind==="setup")throw St("setup boxes have no repos to reshape");if(String(o.name||"").startsWith("\u21BB"))throw St("this Track's repos are the loop's \u2014 propose-update the loop instead");let s=iy.get(e);if(s&&!s.finishedAt)throw St("a reshape is already running for this Track");let a=Cs.get(e);if(a&&!a.finishedAt)throw St("a lead handoff is running \u2014 retry when it settles");let i=Pp.get(e);if(i&&!i.finishedAt)throw St("a node transfer is running \u2014 retry when it settles");if(Rp.has(e))throw St("an adopt is running for this Track \u2014 retry when it settles");for(let y of Wt.values())if(y.trackId===e&&!y.finishedAt)throw St("a subtask is still running on this Track \u2014 wait for it to merge back before reshaping");let c=(Array.isArray(t)?t:[]).map(y=>typeof y=="string"?y.trim():"").filter(Boolean);if(!c.length)throw St("repos (non-empty list of paths or owner/name slugs) required",400);let l=[];for(let y of c){let k;try{k=await Ty(y)}catch(g){throw St(g?.message||String(g),400)}if(l.some(g=>g.base===k))throw St(`repo tagged twice: ${y}`,400);l.push({sel:y,base:k,slug:await Me(k).catch(()=>null)})}let u=o.workspace;if(u?.kind==="multi"){let y=(u.repos??[]).filter(Boolean),k=y.filter(A=>!l.some(I=>I.base===A.base)),g=l.filter(A=>!y.some(I=>I.base===A.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 A of k){let I=await ds(A.dir,{ignorePlumbing:!0}).catch(()=>!1),M=await ps(A.dir,A.baseRef).catch(()=>0);if(I||M>0)throw St(`"${A.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 A of g){let I=await Gp(e,A.base);x.added.push(I.repo??I.base)}for(let A of k)await Kp(e,A.name),x.removed.push(A.repo??A.base);return x.step="done",x.finishedAt=Date.now(),x}if(u?.kind!=="worktree"&&u?.kind!=="repo")throw St(`can't reshape a "${u?.kind??"unknown"}" workspace`);let d=u.kind==="worktree"&&l.some(y=>y.base===u.base);if(u.kind==="worktree"&&!d){let y=await ds(u.dir??o.cwd,{ignorePlumbing:!0}).catch(()=>!1),k=await ps(u.dir??o.cwd,u.baseRef).catch(()=>0);if(y||k>0)throw St(`"${z.basename(u.base)}" has work (${y?"uncommitted changes":`${k} commit${k===1?"":"s"} ahead of ${u.baseRef??"base"}`}) \u2014 ship or discard it before swapping it out`)}ri(e,"reshape");let p=o.boxes.find(y=>y.name===o.leadBox)||null;if(!p)throw St("this Track has no lead box");let f=p.agent||"claude",w=await ni(f,n),h={trackId:e,step:"checkpoint",inPlace:!1,startedAt:Date.now(),finishedAt:null,error:null,added:l.filter(y=>!(u.kind==="worktree"&&y.base===u.base)).map(y=>y.slug??y.base),removed:u.kind==="worktree"&&!d?[u.repo??u.base]:[],scratch:!1,views:0,checkpoint:null};return iy.set(e,h),await Mp(e,{step:h.step,startedAt:h.startedAt,finishedAt:null,error:null,added:h.added,removed:h.removed,layout:null}),Eq(h,o,{desired:l,keep:d,lead:p,agent:f,agentCmd:w,skipPermissions:n}).catch(async y=>{h.error=y?.message||String(y),h.finishedAt=Date.now(),console.error(`[reshape ${e}] FAILED at step "${h.step}": ${h.error}`),await Mp(e,{step:h.step,error:h.error,finishedAt:h.finishedAt}).catch(()=>{})}),h}async function Eq(e,t,{desired:n,keep:r,lead:o,agent:s,agentCmd:a,skipPermissions:i}){let c=e.trackId,l=t.workspace,u=t.cwd,d=async(S,C={})=>{e.step=S,console.log(`[reshape ${c}] step \u2192 ${S}`),await Mp(c,{step:S,...C})},p=await Se(o.name).catch(()=>null),f=o.sessionId??p?.sessionId??null,w=o.pinned===!0;if(await d("checkpoint"),e.checkpoint="llm",!p)e.checkpoint="skipped-dead";else if(mo(s))e.checkpoint="skipped-capped";else try{await wd(o.name,{timeoutMs:uy,quiesce:{box:o,reason:"reshape",onWait:()=>d("await-turn"),onResume:()=>d("checkpoint")}})}catch{e.checkpoint="reconstructed"}let h=null;e.checkpoint!=="llm"&&(h=await gd(c,u,{trackName:t.name||c}).catch(()=>null)),await d("quiesce"),e.views=await dg(c).catch(()=>0),await ku(u).catch(()=>{}),p&&(await Lh(o.name,{fromAgent:s}).catch(()=>{}),await Lt(o.name).catch(()=>{}));let y=`${u}.reshape-${Date.now()}`,k=new Set([".bostrat"]),g={shape:l.kind,dir:u,staging:y,base:l.kind==="worktree"?l.base:null,sub:l.kind==="worktree"&&r?b$(l.base,l.repo??null,k):null,scratch:null},x=l.branch||`track/${tf(t.name||c)}-${c}`,A=[],I=[],M=null;try{if(await d("move",{layout:g}),l.kind==="worktree")if(await Cp(l.base,u,y),await Y.mkdir(u,{recursive:!0}),r){let C=z.join(u,g.sub);await Cp(l.base,y,C),await On(z.join(C,".bostrat"))&&await Y.rename(z.join(C,".bostrat"),z.join(u,".bostrat"));let H=z.join(C,"AGENTS.md");sl(await Y.readFile(H,"utf8").catch(()=>null))&&(await Y.rm(H,{force:!0}).catch(()=>{}),await U$(C,"/AGENTS.md").catch(()=>{})),A.push({name:g.sub,dir:C,base:l.base,baseRef:l.baseRef??null,repo:l.repo??null})}else await On(z.join(y,".bostrat"))&&await Y.rename(z.join(y,".bostrat"),z.join(u,".bostrat"));else await Y.rename(u,y),await Y.mkdir(u,{recursive:!0}),await On(z.join(y,".bostrat"))&&await Y.rename(z.join(y,".bostrat"),z.join(u,".bostrat"));await d("materialize");for(let C of n){if(l.kind==="worktree"&&C.base===l.base)continue;let H=b$(C.base,C.slug,k),B=z.join(u,H),Z=await ms(C.base);await Vh(C.base,x)?await vc(C.base,B,x):await fs(C.base,B,x,Z);let xe={name:H,dir:B,base:C.base,baseRef:Z,repo:C.slug};A.push(xe),I.push(xe)}if(l.kind==="repo"&&await Tq(y)){let C="scratch";for(let H=2;k.has(C);H++)C=`scratch-${H}`;k.add(C),g.scratch=C,await Mp(c,{layout:g}),await Y.rename(y,z.join(u,C)),e.scratch=!0}await oi(u,A,x,{scratch:g.scratch}),await d("relaunch"),M=await jn(o.name,u,{agentCmd:a,agent:s}),await wr({cwd:u,sessionName:o.name,agent:s,agentCmd:Ns(s)}).catch(C=>{if(C?.code!=="cli-not-running")throw C});let S=await Ne(o.name,{lines:ze}).catch(()=>"");if(!to(s,S))throw new Error(`relaunched ${s} CLI never drew its composer in the hub`)}catch(S){await d("rollback",{error:S?.message||String(S)}),await Lt(o.name).catch(()=>{}),await L$(g,{added:I}).catch(Z=>{console.error(`[reshape ${c}] rollback move failed: ${Z?.message||Z}`)});let C=await te(),H=fe(C,c),B=H?.boxes.find(Z=>Z.name===o.name)||null;try{let Z=jp(s,f,{skipPermissions:i,cwd:u,peerName:o.name}),xe=await jn(o.name,u,{agentCmd:Z,agent:s,resumeSessionId:f,pinned:w});B&&(B.hookTokenHash=Ho(xe.hookToken),B.state="live"),ns({cwd:u,sessionName:o.name,agent:s,agentCmd:Z})}catch(Z){console.error(`[reshape ${c}] rollback relaunch failed: ${Z?.message||Z}`)}throw H&&(Ee(H,"retag-failed",{action:"reshape",box:o.name,agent:s,reason:S?.message||String(S),rolledBack:!0}),H.reshape={...H.reshape||{},step:"error",error:S?.message||String(S),finishedAt:Date.now(),recovered:"rolled-back"},await ae()),e.step="error",e.recovered="rolled-back",e.error=S?.message||String(S),e.finishedAt=Date.now(),S}await d("commit");let P=await te(),L=fe(P,c);if(!L)throw new Error(`track "${c}" vanished mid-reshape`);let T=L.boxes.find(S=>S.name===o.name);if(!T)throw new Error(`lead box row "${o.name}" missing during reshape commit`);let E=Date.now(),j=_o(T),G=j[j.length-1];G.endedAt==null&&(G.endedAt=E),G.endReason==null&&(G.endReason="reshape");let R=await us({agent:s,agentCmd:a});j.push({agent:s,sessionId:M.sessionId,pinned:M.pinned===!0,cliVersion:R,startedAt:E,endedAt:null}),T.cwd=u,T.agent=s,T.cliVersion=R,T.sessionId=M.sessionId,T.pinned=M.pinned===!0,T.hookTokenHash=Ho(M.hookToken),T.state="live",T.role="lead",delete T.readyAt,L.cwd=u,L.workspace={dir:u,kind:"multi",branch:x,repos:A},L.pendingHandoffHydrate={artifact:h||Io,at:E},Ee(L,"retagged",{action:"reshape",added:e.added,removed:e.removed,scratch:e.scratch,checkpoint:e.checkpoint,views:e.views,box:o.name,agent:s,fromSessionId:f,toSessionId:M.sessionId,epoch:j.length-1,workspace:Ey(L.workspace)}),L.reshape={...L.reshape||{},step:"done",finishedAt:E,error:null,checkpoint:e.checkpoint,scratch:e.scratch,views:e.views,layout:null},await ae(),ns({cwd:u,sessionName:o.name,agent:s,agentCmd:a,onReady:async()=>{T.readyAt||(T.readyAt=Date.now(),await ae())}}),await On(y)&&(l.kind==="worktree"&&await $t(l.base,y).catch(()=>{}),await Y.rm(y,{recursive:!0,force:!0}).catch(()=>{})),e.step="done",e.finishedAt=E}async function Iy(){let e=await te(),t=0;for(let n of e.tracks||[]){let r=n.reshape;if(!r||r.finishedAt)continue;t++;let o=n.boxes.find(c=>c.name===n.leadBox)||null;o&&await Lt(o.name).catch(()=>{});let s=!1,a=null;if(r.layout)try{let{dir:c,sub:l,scratch:u}=r.layout,d=new Set([".bostrat",l,u].filter(Boolean)),p=[];for(let f of await Y.readdir(c).catch(()=>[])){if(d.has(f))continue;let w=z.join(c,f),h=await Y.readFile(z.join(w,".git"),"utf8").catch(()=>null),y=h&&/^gitdir:\s*(.+?)\/\.git\/worktrees\//m.exec(h);y&&p.push({base:y[1],dir:w})}s=await L$(r.layout,{added:p})}catch(c){a=c?.message||String(c),console.error(`[reshape ${n.id}] boot rollback failed: ${a}`)}o&&!jr.has(o.state)&&(o.state="stopped");let i=`interrupted by a node restart during "${r.step}"${s?" \u2014 moved back":""}${a?` (${a})`:""}`;Ee(n,"retag-failed",{action:"reshape",box:n.leadBox,agent:o?.agent??null,reason:i,rolledBack:!a,recovered:!0}),n.reshape={...r,step:"error",error:i,finishedAt:Date.now(),recovered:a?"rollback-failed":"rolled-back",layout:null}}return t&&await ae(),t}async function _q(e,t,{repo:n,repos:r,cwd:o}={}){await Y.mkdir(al,{recursive:!0});let s=z.join(al,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return Aq(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!z.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await Zs(n)??await du(n));let i=await B$([n,o,NW]);if(i){let c=`track/${tf(t)}-${e}`;try{let l=await ms(i);await fs(i,s,c,l),await ul(s);let u=await Me(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await Y.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await Y.mkdir(s,{recursive:!0}),await zh(s),await ul(s),{dir:s,base:null,branch:null,kind:"repo"}}async function ul(e){try{await Y.writeFile(z.join(e,"AGENTS.md"),ol,{flag:"wx"})}catch{return}await Oq(e,["/AGENTS.md"])}function Rq(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(`
|
|
235
|
+
${o}`,{source:"restore"})}else Qa(r,o);return await ae(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Ay(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=fe(await te(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await Se(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(d=>d.name===r.leadBox)?.agent||o.agent||"claude",i=qA(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await Zi(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await wr({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:Ns(o.agent)});let u=await GA(a,r.leadBox,i);return Ee(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await ae(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function j$(e){for(let t of e){if(!t||!await Ia(t))continue;let n=await SA(t).catch(()=>null);if(!n)continue;if((await Y.stat(z.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function Gt(e){let t=e;return t?t.kind==="multi"?Array.isArray(t.repos)?t.repos.filter(Boolean):[]:t.base?[{name:z.basename(t.base),dir:t.dir??null,base:t.base,baseRef:t.baseRef??null,repo:t.repo??null}]:[]:[]}async function Es(e){let t=[];for(let n of Gt(e?.workspace)){if(!n?.dir)continue;let r=await ds(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await ps(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function Ty(e){let t=e;typeof t=="string"&&!z.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await Zs(t)??await du(t));let n=await j$([t]);if(!n){let r=typeof e=="string"&&z.isAbsolute(e)&&!await Y.stat(e).catch(()=>null);throw new Error(`tagged repo "${e}" is not a usable base (${r?"that path does not exist on this node \u2014 pick one of this node's repos or use owner/name":"need a primary checkout or a synced owner/name"})`)}return n}async function _q(e,t,n,r){let o=`track/${tf(t)}-${e}`,s=[];try{await Y.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await Ty(i);if(s.some(f=>f.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await Me(c).catch(()=>null),u=z.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let f=2;a.has(u);f++)u=`${z.basename(c)}-${f}`;a.add(u);let d=z.join(n,u),p=await ms(c);await fs(c,d,o,p),s.push({name:u,dir:d,base:c,baseRef:p,repo:l})}return await oi(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await $t(i.base,i.dir).catch(()=>{});throw await Y.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function Gp(e,t){let n=await te(),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 Ty(typeof t=="string"?t.trim():t);if((o.repos??[]).some(p=>p?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await Me(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(p=>p?.name).filter(Boolean)]),c=z.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let p=2;i.has(c);p++)c=`${z.basename(s)}-${p}`;let l=z.join(o.dir,c),u=await ms(s);await Vh(s,o.branch)?await vc(s,l,o.branch):await fs(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 oi(o.dir,o.repos,o.branch),Ee(r,"retagged",{action:"add",repo:a??s,name:c,workspace:Ey(o)}),await ae(),d}async function Kp(e,t){let n=await te(),r=fe(n,e);if(!r)throw new Error(`track "${e}" not found`);let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track");let s=(o.repos??[]).find(c=>c&&(c.name===t||c.repo===t||c.base===t));if(!s)throw new Error(`repo "${t}" is not tagged on this Track`);if((o.repos??[]).filter(Boolean).length<=1)throw new Error("can't untag the last repo \u2014 archive or delete the Track instead");for(let c of Wt.values())if(c.trackId===e&&!c.finishedAt)throw new Error("a subtask is still running on this Track \u2014 wait for it to merge back before untagging");let a=await ds(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await ps(s.dir,s.baseRef).catch(()=>0);if(a||i>0)throw new Error(`"${s.name}" has work (${a?"uncommitted changes":`${i} commit${i===1?"":"s"} ahead of ${s.baseRef??"base"}`}) \u2014 ship or discard it first`);return await $t(s.base,s.dir).catch(()=>{}),await Y.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await oi(o.dir,o.repos,o.branch),Ee(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:Ey(o)}),await ae(),{removed:s.name}}async function Mp(e,t){let n=await te(),r=fe(n,e);r&&(r.reshape={...r.reshape||{},...t},await ae())}async function Iq(e){let t=await Y.readdir(e).catch(()=>[]);for(let n of t)if(!(n===".git"||n===".bostrat")){if(n==="AGENTS.md"){let r=await Y.readFile(z.join(e,n),"utf8").catch(()=>"");if(sl(r))continue}return!0}try{let{stdout:n}=await Bp("git",["-C",e,"rev-list","--count","HEAD"]);if(Number(n.trim())>1)return!0}catch{}return!1}function x$(e,t,n){let r=z.basename(e);n.has(r)&&t&&(r=t.replace("/","-"));for(let o=2;n.has(r);o++)r=`${z.basename(e)}-${o}`;return n.add(r),r}async function F$(e,{added:t=[]}={}){let{shape:n,dir:r,staging:o,sub:s,base:a,scratch:i}=e;for(let l of t)await $t(l.base,l.dir).catch(()=>{});let c=async(l,u)=>{await On(l)&&!await On(u)&&await Y.rename(l,u)};if(n==="worktree"&&s){let l=z.join(r,s);await On(l)&&(await c(z.join(r,".bostrat"),z.join(l,".bostrat")),await Cp(a,l,o))}if(n==="repo"&&i){let l=z.join(r,i);await On(l)&&(await On(o)&&await Y.rm(o,{recursive:!0,force:!0}),await Y.rename(l,o))}return await On(o)?(await On(r)&&(await c(z.join(r,".bostrat"),z.join(o,".bostrat")),await Y.rm(r,{recursive:!0,force:!0})),n==="worktree"?await Cp(a,o,r):await Y.rename(o,r),!0):!1}async function _y(e,t,{skipPermissions:n=!0}={}){let r=await te(),o=fe(r,e);if(!o)throw St(`track "${e}" not found`,404);if(o.archivedAt)throw St("track is archived \u2014 unarchive it first");try{Zp(o)}catch(y){throw Object.assign(y,{status:409})}if(o.kind==="setup")throw St("setup boxes have no repos to reshape");if(String(o.name||"").startsWith("\u21BB"))throw St("this Track's repos are the loop's \u2014 propose-update the loop instead");let s=iy.get(e);if(s&&!s.finishedAt)throw St("a reshape is already running for this Track");let a=Cs.get(e);if(a&&!a.finishedAt)throw St("a lead handoff is running \u2014 retry when it settles");let i=Pp.get(e);if(i&&!i.finishedAt)throw St("a node transfer is running \u2014 retry when it settles");if(Rp.has(e))throw St("an adopt is running for this Track \u2014 retry when it settles");for(let y of Wt.values())if(y.trackId===e&&!y.finishedAt)throw St("a subtask is still running on this Track \u2014 wait for it to merge back before reshaping");let c=(Array.isArray(t)?t:[]).map(y=>typeof y=="string"?y.trim():"").filter(Boolean);if(!c.length)throw St("repos (non-empty list of paths or owner/name slugs) required",400);let l=[];for(let y of c){let k;try{k=await Ty(y)}catch(g){throw St(g?.message||String(g),400)}if(l.some(g=>g.base===k))throw St(`repo tagged twice: ${y}`,400);l.push({sel:y,base:k,slug:await Me(k).catch(()=>null)})}let u=o.workspace;if(u?.kind==="multi"){let y=(u.repos??[]).filter(Boolean),k=y.filter(S=>!l.some(_=>_.base===S.base)),g=l.filter(S=>!y.some(_=>_.base===S.base));if(!k.length&&!g.length)return{trackId:e,step:"done",inPlace:!0,added:[],removed:[],startedAt:Date.now(),finishedAt:Date.now(),error:null};for(let S of k){let _=await ds(S.dir,{ignorePlumbing:!0}).catch(()=>!1),M=await ps(S.dir,S.baseRef).catch(()=>0);if(_||M>0)throw St(`"${S.name}" has work \u2014 ship or discard it before dropping it`)}let x={trackId:e,step:"retag",inPlace:!0,added:[],removed:[],startedAt:Date.now(),finishedAt:null,error:null};for(let S of g){let _=await Gp(e,S.base);x.added.push(_.repo??_.base)}for(let S of k)await Kp(e,S.name),x.removed.push(S.repo??S.base);return x.step="done",x.finishedAt=Date.now(),x}if(u?.kind!=="worktree"&&u?.kind!=="repo")throw St(`can't reshape a "${u?.kind??"unknown"}" workspace`);let d=u.kind==="worktree"&&l.some(y=>y.base===u.base);if(u.kind==="worktree"&&!d){let y=await ds(u.dir??o.cwd,{ignorePlumbing:!0}).catch(()=>!1),k=await ps(u.dir??o.cwd,u.baseRef).catch(()=>0);if(y||k>0)throw St(`"${z.basename(u.base)}" has work (${y?"uncommitted changes":`${k} commit${k===1?"":"s"} ahead of ${u.baseRef??"base"}`}) \u2014 ship or discard it before swapping it out`)}ri(e,"reshape");let p=o.boxes.find(y=>y.name===o.leadBox)||null;if(!p)throw St("this Track has no lead box");let f=p.agent||"claude",w=await ni(f,n),h={trackId:e,step:"checkpoint",inPlace:!1,startedAt:Date.now(),finishedAt:null,error:null,added:l.filter(y=>!(u.kind==="worktree"&&y.base===u.base)).map(y=>y.slug??y.base),removed:u.kind==="worktree"&&!d?[u.repo??u.base]:[],scratch:!1,views:0,checkpoint:null};return iy.set(e,h),await Mp(e,{step:h.step,startedAt:h.startedAt,finishedAt:null,error:null,added:h.added,removed:h.removed,layout:null}),$q(h,o,{desired:l,keep:d,lead:p,agent:f,agentCmd:w,skipPermissions:n}).catch(async y=>{h.error=y?.message||String(y),h.finishedAt=Date.now(),console.error(`[reshape ${e}] FAILED at step "${h.step}": ${h.error}`),await Mp(e,{step:h.step,error:h.error,finishedAt:h.finishedAt}).catch(()=>{})}),h}async function $q(e,t,{desired:n,keep:r,lead:o,agent:s,agentCmd:a,skipPermissions:i}){let c=e.trackId,l=t.workspace,u=t.cwd,d=async(A,C={})=>{e.step=A,console.log(`[reshape ${c}] step \u2192 ${A}`),await Mp(c,{step:A,...C})},p=await Se(o.name).catch(()=>null),f=o.sessionId??p?.sessionId??null,w=o.pinned===!0;if(await d("checkpoint"),e.checkpoint="llm",!p)e.checkpoint="skipped-dead";else if(mo(s))e.checkpoint="skipped-capped";else try{await wd(o.name,{timeoutMs:uy,quiesce:{box:o,reason:"reshape",onWait:()=>d("await-turn"),onResume:()=>d("checkpoint")}})}catch{e.checkpoint="reconstructed"}let h=null;e.checkpoint!=="llm"&&(h=await gd(c,u,{trackName:t.name||c}).catch(()=>null)),await d("quiesce"),e.views=await dg(c).catch(()=>0),await ku(u).catch(()=>{}),p&&(await Lh(o.name,{fromAgent:s}).catch(()=>{}),await Lt(o.name).catch(()=>{}));let y=`${u}.reshape-${Date.now()}`,k=new Set([".bostrat"]),g={shape:l.kind,dir:u,staging:y,base:l.kind==="worktree"?l.base:null,sub:l.kind==="worktree"&&r?x$(l.base,l.repo??null,k):null,scratch:null},x=l.branch||`track/${tf(t.name||c)}-${c}`,S=[],_=[],M=null;try{if(await d("move",{layout:g}),l.kind==="worktree")if(await Cp(l.base,u,y),await Y.mkdir(u,{recursive:!0}),r){let C=z.join(u,g.sub);await Cp(l.base,y,C),await On(z.join(C,".bostrat"))&&await Y.rename(z.join(C,".bostrat"),z.join(u,".bostrat"));let W=z.join(C,"AGENTS.md");sl(await Y.readFile(W,"utf8").catch(()=>null))&&(await Y.rm(W,{force:!0}).catch(()=>{}),await W$(C,"/AGENTS.md").catch(()=>{})),S.push({name:g.sub,dir:C,base:l.base,baseRef:l.baseRef??null,repo:l.repo??null})}else await On(z.join(y,".bostrat"))&&await Y.rename(z.join(y,".bostrat"),z.join(u,".bostrat"));else await Y.rename(u,y),await Y.mkdir(u,{recursive:!0}),await On(z.join(y,".bostrat"))&&await Y.rename(z.join(y,".bostrat"),z.join(u,".bostrat"));await d("materialize");for(let C of n){if(l.kind==="worktree"&&C.base===l.base)continue;let W=x$(C.base,C.slug,k),B=z.join(u,W),Z=await ms(C.base);await Vh(C.base,x)?await vc(C.base,B,x):await fs(C.base,B,x,Z);let xe={name:W,dir:B,base:C.base,baseRef:Z,repo:C.slug};S.push(xe),_.push(xe)}if(l.kind==="repo"&&await Iq(y)){let C="scratch";for(let W=2;k.has(C);W++)C=`scratch-${W}`;k.add(C),g.scratch=C,await Mp(c,{layout:g}),await Y.rename(y,z.join(u,C)),e.scratch=!0}await oi(u,S,x,{scratch:g.scratch}),await d("relaunch"),M=await jn(o.name,u,{agentCmd:a,agent:s}),await wr({cwd:u,sessionName:o.name,agent:s,agentCmd:Ns(s)}).catch(C=>{if(C?.code!=="cli-not-running")throw C});let A=await Ne(o.name,{lines:ze}).catch(()=>"");if(!to(s,A))throw new Error(`relaunched ${s} CLI never drew its composer in the hub`)}catch(A){await d("rollback",{error:A?.message||String(A)}),await Lt(o.name).catch(()=>{}),await F$(g,{added:_}).catch(Z=>{console.error(`[reshape ${c}] rollback move failed: ${Z?.message||Z}`)});let C=await te(),W=fe(C,c),B=W?.boxes.find(Z=>Z.name===o.name)||null;try{let Z=jp(s,f,{skipPermissions:i,cwd:u,peerName:o.name}),xe=await jn(o.name,u,{agentCmd:Z,agent:s,resumeSessionId:f,pinned:w});B&&(B.hookTokenHash=Ho(xe.hookToken),B.state="live"),ns({cwd:u,sessionName:o.name,agent:s,agentCmd:Z})}catch(Z){console.error(`[reshape ${c}] rollback relaunch failed: ${Z?.message||Z}`)}throw W&&(Ee(W,"retag-failed",{action:"reshape",box:o.name,agent:s,reason:A?.message||String(A),rolledBack:!0}),W.reshape={...W.reshape||{},step:"error",error:A?.message||String(A),finishedAt:Date.now(),recovered:"rolled-back"},await ae()),e.step="error",e.recovered="rolled-back",e.error=A?.message||String(A),e.finishedAt=Date.now(),A}await d("commit");let P=await te(),j=fe(P,c);if(!j)throw new Error(`track "${c}" vanished mid-reshape`);let T=j.boxes.find(A=>A.name===o.name);if(!T)throw new Error(`lead box row "${o.name}" missing during reshape commit`);let $=Date.now(),L=_o(T),H=L[L.length-1];H.endedAt==null&&(H.endedAt=$),H.endReason==null&&(H.endReason="reshape");let R=await us({agent:s,agentCmd:a});L.push({agent:s,sessionId:M.sessionId,pinned:M.pinned===!0,cliVersion:R,startedAt:$,endedAt:null}),T.cwd=u,T.agent=s,T.cliVersion=R,T.sessionId=M.sessionId,T.pinned=M.pinned===!0,T.hookTokenHash=Ho(M.hookToken),T.state="live",T.role="lead",delete T.readyAt,j.cwd=u,j.workspace={dir:u,kind:"multi",branch:x,repos:S},j.pendingHandoffHydrate={artifact:h||Io,at:$},Ee(j,"retagged",{action:"reshape",added:e.added,removed:e.removed,scratch:e.scratch,checkpoint:e.checkpoint,views:e.views,box:o.name,agent:s,fromSessionId:f,toSessionId:M.sessionId,epoch:L.length-1,workspace:Ey(j.workspace)}),j.reshape={...j.reshape||{},step:"done",finishedAt:$,error:null,checkpoint:e.checkpoint,scratch:e.scratch,views:e.views,layout:null},await ae(),ns({cwd:u,sessionName:o.name,agent:s,agentCmd:a,onReady:async()=>{T.readyAt||(T.readyAt=Date.now(),await ae())}}),await On(y)&&(l.kind==="worktree"&&await $t(l.base,y).catch(()=>{}),await Y.rm(y,{recursive:!0,force:!0}).catch(()=>{})),e.step="done",e.finishedAt=$}async function Iy(){let e=await te(),t=0;for(let n of e.tracks||[]){let r=n.reshape;if(!r||r.finishedAt)continue;t++;let o=n.boxes.find(c=>c.name===n.leadBox)||null;o&&await Lt(o.name).catch(()=>{});let s=!1,a=null;if(r.layout)try{let{dir:c,sub:l,scratch:u}=r.layout,d=new Set([".bostrat",l,u].filter(Boolean)),p=[];for(let f of await Y.readdir(c).catch(()=>[])){if(d.has(f))continue;let w=z.join(c,f),h=await Y.readFile(z.join(w,".git"),"utf8").catch(()=>null),y=h&&/^gitdir:\s*(.+?)\/\.git\/worktrees\//m.exec(h);y&&p.push({base:y[1],dir:w})}s=await F$(r.layout,{added:p})}catch(c){a=c?.message||String(c),console.error(`[reshape ${n.id}] boot rollback failed: ${a}`)}o&&!jr.has(o.state)&&(o.state="stopped");let i=`interrupted by a node restart during "${r.step}"${s?" \u2014 moved back":""}${a?` (${a})`:""}`;Ee(n,"retag-failed",{action:"reshape",box:n.leadBox,agent:o?.agent??null,reason:i,rolledBack:!a,recovered:!0}),n.reshape={...r,step:"error",error:i,finishedAt:Date.now(),recovered:a?"rollback-failed":"rolled-back",layout:null}}return t&&await ae(),t}async function Rq(e,t,{repo:n,repos:r,cwd:o}={}){await Y.mkdir(al,{recursive:!0});let s=z.join(al,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return _q(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!z.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await Zs(n)??await du(n));let i=await j$([n,o,BW]);if(i){let c=`track/${tf(t)}-${e}`;try{let l=await ms(i);await fs(i,s,c,l),await ul(s);let u=await Me(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await Y.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await Y.mkdir(s,{recursive:!0}),await zh(s),await ul(s),{dir:s,base:null,branch:null,kind:"repo"}}async function ul(e){try{await Y.writeFile(z.join(e,"AGENTS.md"),ol,{flag:"wx"})}catch{return}await Nq(e,["/AGENTS.md"])}function Cq(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(`
|
|
236
236
|
`),o=n?`
|
|
237
237
|
\`${n}/\` is this Track's pre-repo workspace, carried over when the
|
|
238
238
|
Track was reshaped onto these repos. It is not part of any tagged repo and
|
|
239
239
|
cannot be shipped \u2014 move anything worth keeping into the repo it belongs to.
|
|
240
|
-
`:"";return`${
|
|
240
|
+
`:"";return`${Mq}
|
|
241
241
|
|
|
242
242
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
243
243
|
git worktree per tagged repo, ALL on the same branch \`${t}\`:
|
|
@@ -249,20 +249,20 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
249
249
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
250
250
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
251
251
|
or preview an app, run it from that app's own repo directory.
|
|
252
|
-
${o}`}async function oi(e,t,n,r={}){let o=
|
|
252
|
+
${o}`}async function oi(e,t,n,r={}){let o=Cq(t,n,r);for(let s of["CLAUDE.md","AGENTS.md"])await Y.writeFile(z.join(e,s),o).catch(()=>{})}async function H$(e){let{stdout:t}=await Bp("git",["-C",e,"rev-parse","--git-path","info/exclude"]);return z.resolve(e,t.trim())}async function Nq(e,t){try{let n=await H$(e);await Y.mkdir(z.dirname(n),{recursive:!0});let r=await Y.readFile(n,"utf8").catch(()=>""),o=t.filter(s=>!r.split(`
|
|
253
253
|
`).includes(s));o.length&&await Y.appendFile(n,(r.endsWith(`
|
|
254
254
|
`)||!r?"":`
|
|
255
255
|
`)+o.join(`
|
|
256
256
|
`)+`
|
|
257
|
-
`)}catch{}}async function
|
|
257
|
+
`)}catch{}}async function W$(e,t){try{let n=await H$(e),r=await Y.readFile(n,"utf8"),o=r.split(`
|
|
258
258
|
`).filter(s=>s!==t);o.length!==r.split(`
|
|
259
259
|
`).length&&await Y.writeFile(n,o.join(`
|
|
260
|
-
`))}catch{}}async function $y(){let e={refreshed:0,removed:0,codexGlobal:null};try{await Y.mkdir(nr,{recursive:!0}),await Y.writeFile(z.join(nr,"CLAUDE.md"),
|
|
260
|
+
`))}catch{}}async function $y(){let e={refreshed:0,removed:0,codexGlobal:null};try{await Y.mkdir(nr,{recursive:!0}),await Y.writeFile(z.join(nr,"CLAUDE.md"),Oq).catch(()=>{}),await Y.writeFile(z.join(nr,"AGENTS.md"),ol).catch(()=>{});let t=process.env.CODEX_HOME||z.join(process.env.HOME||"/tmp",".codex");if((await Y.stat(t).catch(()=>null))?.isDirectory()){let r=z.join(t,"AGENTS.md"),o=await Y.readFile(r,"utf8").catch(()=>null);o===null?(await Y.writeFile(r,Qg,{flag:"wx"}).catch(()=>{}),e.codexGlobal="created"):sl(o)&&o!==Qg&&(await Y.writeFile(r,Qg).catch(()=>{}),e.codexGlobal="refreshed")}for(let r of[al,ti]){let o=await Y.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=z.join(r,s.name),i=z.join(a,"AGENTS.md"),c=await Y.readFile(i,"utf8").catch(()=>null);c!==null&&sl(c)&&c!==ol&&(await Y.writeFile(i,ol).catch(()=>{}),e.refreshed++);let l=z.join(a,"CLAUDE.md"),u=await Y.readFile(l,"utf8").catch(()=>null);u!==null&&sl(u)&&(await Y.rm(l,{force:!0}).catch(()=>{}),await W$(a,"/CLAUDE.md"),e.removed++)}}}catch{}return e}async function q$(e){let t=e.workspace;if(!(!t||!t.dir))try{if(t.kind==="multi"){for(let n of Gt(t))n.base&&n.dir&&await $t(n.base,n.dir).catch(()=>{});await Y.rm(t.dir,{recursive:!0,force:!0})}else t.kind==="worktree"&&t.base?await $t(t.base,t.dir):await Y.rm(t.dir,{recursive:!0,force:!0})}catch{}}async function Vp({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 ni(o,a,s),c=us({agent:o,agentCmd:i}),l=await te(),u=Dp().slice(0,8),d=await Rq(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(Cu(f))&&(f=`${e.trim().slice(0,50).trim()} ${u} lead`);let w=await jn(f,d.dir,{agentCmd:i,agent:o,freshWorkspace:!0}),h={id:u,name:e.trim(),cwd:w.cwd,workspace:d,leadBox:w.name,boxes:[Fy(w,"lead","live")],events:[],createdAt:Date.now()};return h.boxes[0].cliVersion=await c,Ee(h,"opened",{agent:o,...s?{model:s}:{},box:w.name,cliVersion:h.boxes[0].cliVersion,skipPermissions:a,name:h.name,cwd:h.cwd,workspace:{base:d.base??null,branch:d.branch,kind:d.kind,...d.baseRef?{baseRef:d.baseRef}:{},...d.repo?{repo:d.repo}:{},...d.repos?{repos:d.repos.map(({name:y,base:k,baseRef:g,repo:x})=>({name:y,base:k,baseRef:g,repo:x}))}:{}}}),l.tracks.push(h),await ae(),ns({cwd:w.cwd,sessionName:w.name,agent:o,agentCmd:i,onBlocked:({message:y})=>I$(h.id,{box:w.name,agent:o,message:y}).catch(()=>{}),onReady:async()=>{let y=h.boxes.find(k=>k.name===h.leadBox);!y||y.readyAt||(y.readyAt=Date.now(),await ae())}}),Zt(h)}async function Ry({provider:e="claude",fresh:t=!1}={}){if(!Pq.has(e))throw new Error(`unknown setup provider "${e}" (claude | codex | cursor)`);let r=(await te()).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 Ps(r.id).catch(()=>{})}let o=(await ni(e,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=Dp().slice(0,8),a=z.join(nr,"setup",s);await Y.mkdir(a,{recursive:!0});let i=await jn(`Connect ${e} ${s}`,a,{agentCmd:o,agent:e,firstRun:!0});e==="codex"&&ns({cwd:i.cwd,sessionName:i.name,agent:e,agentCmd:o});let c={id:s,name:`Connect ${e}`,kind:"setup",setup:{provider:e,startedAt:Date.now(),connectedAt:null},cwd:i.cwd,workspace:{dir:a,base:null,branch:null,kind:"scratch"},leadBox:i.name,boxes:[Fy(i,"lead","live")],events:[],createdAt:Date.now()};return Ee(c,"opened",{agent:e,box:i.name,skipPermissions:!1,trackKind:"setup",name:c.name,cwd:c.cwd,workspace:{base:null,branch:null,kind:"scratch"}}),(await te()).tracks.push(c),await ae(),Zt(c)}async function Oy(e,t,n){await ku(t.cwd).catch(()=>{}),await dg(e).catch(()=>{});let r=Cs.get(e);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),Cs.delete(e),t.handoff&&!t.handoff.finishedAt&&(t.handoff.step="error",t.handoff.error=n,t.handoff.finishedAt=Date.now());for(let a of Bs(e))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await Yy(a).catch(()=>{}),Wt.delete(a.id);for(let a of Object.values(t.subtasks||{}))a.finishedAt||(a.step="error",a.error=n,a.canceled=!0,a.finishedAt=Date.now());let o=await te(),s=new Set((o.tracks||[]).filter(a=>a.id!==e&&!a.archivedAt).flatMap(a=>(a.boxes||[]).map(i=>i.name)));for(let a of t.boxes)s.has(a.name)||await Lt(a.name).catch(()=>{})}async function My(e,{cascadeMocks:t=!0,cascadeRunbooks:n=!1}={}){let r=await te(),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 pI(s),vy(e),await Oy(e,s,"track deleted"),n?await WI(e).catch(()=>{}):await HI(e).catch(()=>{}),await zA(),await q$(s),t&&await fI(e).catch(()=>{}),t?await RI(e).catch(()=>{}):await $I(s).catch(()=>{}),Ee(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await ae(),{ok:!0,id:e,name:s.name,boxes:s.boxes.length}}async function Ps(e){let t=await te(),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(),vy(e),Ee(n,"archived",{boxes:n.boxes.length}),await ae(),n.kind==="setup"?(await G$(e),Zt(n)):(dl.add(e),Lq(e).catch(()=>{}).finally(()=>dl.delete(e)),{...await Zt(n),archiving:!0})}async function Lq(e){try{await jq(e)}catch{}await G$(e).catch(()=>{})}async function G$(e){let t=await te(),n=fe(t,e);if(!(!n||!n.archivedAt)){await Oy(e,n,"track archived");for(let r of n.boxes)jr.has(r.state)||(r.state="stopped");await ae()}}async function jq(e){let t=await te(),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 Se(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await VS(n.leadBox,i.cwd,{timeoutMs:Bq})),s="lead-cli"}catch{}if(r||(r=cA(n,await K(n).catch(()=>[]))),!r)return;let c={text:r,source:s,at:Date.now()};o.length&&(c.items=o),n.beat=c,await N(n,{type:"track.summary.recorded",actor:"orchestrator",source:"orchestrator",boxId:n.leadBox||null,provider:i?.agent||null,payload:o.length?{text:r,items:o,source:s}:{text:r,source:s}}).catch(()=>{}),await ae()}async function Er(e,t){let n=await ft(e,t).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await Cy(n);if(r.length){for(let o of r)await gs(o.repo,o.number,!0).catch(()=>{});return!1}try{return await Ps(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function Cy(e){return(await En().catch(()=>[])).filter(n=>n.trackId===e.trackId&&n.state!=="merged"&&n.state!=="closed"&&n.state!=="lost"&&!(n.repo===e.repo&&n.number===e.number))}async function _r(e,t){return zp(e,t,"merged")}async function Ds(e,t){let n=await ft(e,t).catch(()=>null);if(!n?.trackId)return null;let r={repo:n.repo??null,number:n.number,title:n.title??null,url:n.url??null},o=await Cy(n);if(o.length){let c=[...n.followupPending||[],r];for(let l of o)await _T(l.repo,l.number,c).catch(()=>{});return null}let s=[...n.followupPending||[],r],a=fe(await te(),n.trackId);if(!a)return null;let i=s[0];return Dc(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 zp(e,t,n="merged"){let r=await ft(e,t).catch(()=>null);if(!r?.notifyOnMerge||r.archiveOnMerge||!r.trackId)return!1;let o={repo:r.repo??null,number:r.number,title:r.title??null,url:r.url??null,headRef:r.headRef??null,outcome:n},s=await Cy(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await ET(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];if(!await gT(r.repo,r.number).catch(()=>!0))return!1;try{return await qt(r.trackId,K$(a),{kind:"merge-notify"}),!0}catch{return await ys(r.repo,r.number,!0).catch(()=>{}),!1}}function K$(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 ${v$[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 ${v$[i.outcome]??i.outcome}`).join(`
|
|
261
261
|
`),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}
|
|
262
262
|
${s}
|
|
263
|
-
${a}`}async function Ny(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<Yi()?!1:Fn(e.name)}async function pl(e,t,n){return!t||e.archivedAt||jr.has(t.state)?!1:(await re({flow:"rehydrate",phase:"resume-dead-cli",trackId:e.id,sessionName:t.name,agent:t.agent,via:n}).catch(()=>{}),console.warn(`[track] ${t.name}: CLI is not running (bare shell) \u2014 relaunching (${n})`),await Lt(t.name).catch(()=>{}),t.state="stopped",si(e))}async function si(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=ey.get(r.name);if(o)return o;let s=Lq(e,r,{freshSession:t,reason:n}).finally(()=>ey.delete(r.name));return ey.set(r.name,s),s}async function Py(e){let n=(await te()).tracks.find(o=>o.leadBox===e);if(!n||n.archivedAt||n.transferredTo||Date.now()-(ty.get(e)??0)<Bq)return!1;let r=await si(n,{reason:"attach"}).catch(()=>!1);return r?(ty.delete(e),await ae()):ty.set(e,Date.now()),r}async function Lq(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||jr.has(t.state)||await Se(t.name))return!1;let o=n===!0,s=o?Dp():null,a=o?zi(ly(t.agent),s,t.name):jp(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await Mu(a);if(i===ca)return Ee(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===la&&console.warn(`[track] ${t.name}: could not confirm "${t.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);let c=o?{sessionId:t.sessionId??null,pinned:t.pinned===!0,epochs:JSON.parse(JSON.stringify(_o(t)))}:null,l=Date.now();if(o){let u=_o(t),d=u[u.length-1];d.endedAt==null&&(d.endedAt=l),d.endReason==null&&(d.endReason=r||"fresh-session"),u.push({agent:t.agent,sessionId:s,pinned:t.agent==="claude",startedAt:l,endedAt:null}),t.sessionId=s,t.pinned=t.agent==="claude"}try{let u=us({agent:t.agent,agentCmd:a}),d=await jn(t.name,t.cwd,{agentCmd:a,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=Ho(d.hookToken),t.cliVersion=await u,_o(t)[t.epochs.length-1].cliVersion=t.cliVersion,ns({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a,onBlocked:({message:p})=>E$(e.id,{box:t.name,agent:t.agent,message:p}).catch(()=>{})}),t.state="live",o&&Ee(e,"lead-refreshed",{box:t.name,agent:t.agent,fromSessionId:c.sessionId,toSessionId:s,epoch:t.epochs.length-1,reason:r||"fresh-session",cliVersion:t.cliVersion??null,standing:await Ja(t.cwd)}),Ee(e,"rehydrated",{box:t.name,agent:t.agent,...o?{fresh:!0}:{}}),!0}catch(u){return o&&(t.sessionId=c.sessionId,t.pinned=c.pinned,t.epochs=c.epochs),Ee(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function Jp(e,{freshSession:t=!1}={}){let n=await te(),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,dl.delete(e),Ee(r,"unarchived",{}),await si(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await ae(),Zt(r)}async function Dy(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>cy)throw new Error(`name too long (max ${cy} chars)`);let r=await te(),o=fe(r,e);if(!o)throw new Error(`track "${e}" not found`);if(String(o.name||"").startsWith("\u21BB"))throw new Error("loop run Tracks are named by their loop \u2014 rename the loop on the Loops tab");if(n===o.name)return Zt(o);let s=o.name;return o.name=n,Ee(o,"renamed",{from:s,to:n}),await ae(),Zt(o)}async function By(){let e=await te(),t=0;for(let n of e.tracks||[])n.transferredTo&&Ad(n,{at:n.transferredAt??Date.now()})&&t++;return t&&await ae(),t}async function Ly(){let e=await te(),t=new Set((await kn()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!Ue(s)).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(Ue(s))for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Lt(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):Ee(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!jr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await ae(),r}async function Yp(e,{freshSession:t=!1}={}){let n=await te(),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`);Zp(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(jr.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await Se(r.leadBox)){let i=await Ne(r.leadBox,{lines:ze}).catch(()=>"");if(ac(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Lt(r.leadBox),o.state="stopped"}let a=await si(r,t?{freshSession:!0,reason:"loop-fire"}:{});if(await ae(),!a)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Zt(r)}async function jy(e,t,n){let r=await me(e);if(!r)throw new Error(`track "${e}" not found`);if(!t)throw new Error("unknown offer");let o=await K(r).catch(()=>[]);if(!o.some(i=>i.type==="box.fullscreen.detected"&&i.payload?.offerId===t))throw new Error("unknown offer");if(o.some(i=>i.type==="box.fullscreen.answered"&&i.payload?.offerId===t))throw new Error("offer already answered");return n&&await Yp(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 x$(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return ep({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function _o(e){return(!Array.isArray(e.epochs)||e.epochs.length===0)&&(e.epochs=[{agent:e.agent??null,sessionId:e.sessionId??null,pinned:e.pinned===!0,cliVersion:e.cliVersion??null,startedAt:e.createdAt??null,endedAt:null}]),e.epochs}async function Np(e,t){let n=await te(),r=fe(n,e);r&&(r.handoff={...r.handoff||{},...t},await ae())}function G$(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 Uy(){let e=await te(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=G$(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await Se(r.fromBox).catch(()=>null),l=n.boxes.find(u=>u.name===r.fromBox);if(c&&l&&c.agent===r.toAgent&&r.toAgent!==l.agent){let u=Date.now(),d=_o(l),p=d[d.length-1],f=p.agent,w=p.sessionId??null;p.endedAt=u,r.reason&&(p.endReason=r.reason);let h=c.agent==="claude"&&!!c.sessionId,y=await us({agent:r.toAgent});d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:h,cliVersion:y,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.cliVersion=y,l.sessionId=c.sessionId||null,l.pinned=h,l.state="live",n.pendingHandoffHydrate={artifact:Io,at:u},Ee(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:f,toAgent:r.toAgent,fromSessionId:w,toSessionId:c.sessionId||null,epoch:d.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0,standing:await Ja(l.cwd)}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await re({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 re({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Lt(i).catch(()=>{}),a&&await Lt(a).catch(()=>{})}return t&&await ae(),t}async function Xp(e,t,n=!0,r=null){let o=Cs.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=fe(await te(),e);if(!s)throw new Error(`track "${e}" not found`);ri(e,"handoff");let a=await ni(t,n),i=mo(t);if(i){let u=new Error(`can't hand off to ${t} \u2014 its ${i.window} spend limit is reached ($${i.spentUsd.toFixed(2)} of $${i.limitUsd.toFixed(2)})`);throw u.usageCap=i,u}let c=await Se(s.leadBox);if(!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l={trackId:e,step:"checkpoint",fromBox:s.leadBox,toAgent:t,skipPermissions:n,reason:_$(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return Cs.set(e,l),await Np(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),jq(l,s,c,a,t).catch(async u=>{l.error=u?.message||String(u),l.finishedAt=Date.now(),console.error(`[lead-handoff ${e}] FAILED at step "${l.step}": ${l.error}`),await Np(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),re({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),re({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function jq(e,t,n,r,o){let s=async(P,L={})=>{e.step=P,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${P}`),await Np(e.trackId,{step:P,toBox:e.toBox}),await re({flow:"lead-handoff",phase:P,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:L.sessionName,...L})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await te(),i=fe(a,e.trackId)?.boxes.find(P=>P.name===e.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:e.fromBox}),e.checkpoint="llm",e.quiescence=null,mo(c))e.checkpoint="skipped-capped";else try{let P=await wd(e.fromBox,{timeoutMs:uy,quiesce:{box:i?{...i,name:e.fromBox}:n,reason:e.reason?.code||"lead-handoff",onWait:async L=>{e.wait=L,await s("await-turn",{sessionName:e.fromBox,wait:L})},onResume:async()=>{e.wait=null,await s("checkpoint",{sessionName:e.fromBox})}}});e.quiescence=Oh(P),e.waitedMs=(P.quiescence?.pre?.waitedMs||0)+(P.quiescence?.post?.waitedMs||0)}catch(P){e.checkpoint="reconstructed",e.quiescence=Oh(P),e.waitedMs=P?.quiescence?.pre?.waitedMs||0,e.wait=null,await re({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:P?.message||String(P)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await gd(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=us({agent:o,agentCmd:r}),f=await jh(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let w=ov(f.launchCmd);await wr({cwd:n.cwd,sessionId:f.sessionId,sessionName:e.fromBox,agent:o,agentCmd:Ns(o),launchEcho:w}).catch(P=>{if(P?.code!=="cli-not-running")throw P});let h=zu(await Ne(e.fromBox,{lines:ze}).catch(()=>""),w);if(await re({flow:"lead-handoff",phase:"hydrate-ready",trackId:e.trackId,sessionName:e.fromBox,toAgent:o,up:to(o,h),paneTail:!0}).catch(()=>{}),!to(o,h)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let P=jp(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await jh(e.fromBox,{agentCmd:P,agent:c,resumeSessionId:l,pinned:u});let L=await te(),T=fe(L,e.trackId);if(T){let E=T.boxes.find(j=>j.name===e.fromBox);E&&(E.state="live"),Ee(T,"lead-handoff-failed",{box:e.fromBox,fromAgent:c,toAgent:o,reason:e.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:e.startedAt}),await ae()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let y=await te(),k=fe(y,e.trackId);if(!k)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let g=k.boxes.find(P=>P.name===e.fromBox);if(!g)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let x=Date.now(),A=_o(g),I=A[A.length-1];I.endedAt=x,e.reason&&(I.endReason=e.reason);let M=await p;A.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,cliVersion:M,startedAt:x,endedAt:null}),g.agent=o,g.sessionId=f.sessionId,g.pinned=f.pinned,g.cliVersion=M,g.state="live",g.role="lead",k.pendingHandoffHydrate={artifact:e.statusArtifact||Io,at:x},Ee(k,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:A.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 Ja(g.cwd)}),await ae(),await s("done",{sessionName:e.fromBox}),await Np(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function qy(e){let t=Pp.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await te(),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=Cs.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of Wt.values())if(a.trackId===e&&!a.finishedAt)throw new Error("a subtask is still running on this Track \u2014 wait for it to merge back before moving");let s={trackId:e,step:"checkpoint",startedAt:Date.now(),finishedAt:null,error:null};return Pp.set(e,s),Fq(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 Fq(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await Se(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&mo(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await wd(t.leadBox,{timeoutMs:uy,quiesce:{box:n,reason:"transfer-out",onWait:()=>{e.step="await-turn"},onResume:()=>{e.step="checkpoint"}}})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await gd(t.id,t.cwd,{trackName:t.name||t.id}).catch(()=>null),e.step="push";let o=t.workspace?.branch??null,s=[];if(o)for(let u of Gt(t.workspace)){if(!u?.dir)continue;let d={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await Mn(u.dir,["push","origin",o],Hy);let{stdout:p}=await Mn(u.dir,["rev-parse","HEAD"]);d.baseSha=p.trim(),d.pushed=!0}catch(p){d.error=String(p?.message||p).split(`
|
|
264
|
-
`)[0]}s.push(d)}e.step="wip";let a=await Mg(t,{force:!0}),i=await te(),c=fe(i,t.id),l=c?.boxes.find(u=>u.name===c.leadBox)||null;l&&_o(l),await ae(),e.result={name:c?.name??t.name,agent:l?.agent??null,leadBox:c?.leadBox??t.leadBox,createdAt:c?.createdAt??null,branch:o,repos:s.filter(u=>u.pushed).map(({name:u,repo:d,baseSha:p})=>({name:u,repo:d,baseSha:p})),unpushed:s.filter(u=>!u.pushed).map(({name:u,repo:d,error:p})=>({name:u,repo:d,error:p})),wip:a.pushed[0]?{ref:a.pushed[0].ref,sha:a.pushed[0].sha}:null,wips:a.pushed,wipSkipped:a.skipped,checkpoint:e.checkpoint,epochs:l?.epochs??[],codeless:!o||!s.some(u=>u.pushed)},e.step="ready",e.finishedAt=Date.now()}function Zp(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 Qp(e,{toNode:t,mode:n="transfer"}={}){if(!t||typeof t!="string")throw new Error("toNode required");let r=await te(),o=fe(r,e);if(!o)throw new Error(`track "${e}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};vy(e),await Oy(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();Ad(o,{at:s,reason:n==="recover"?"node-offline":"node-transfer"});let a=await Cg(o,{ts:s}),i=a.pushed[0]??null;return o.transferredTo=t,o.transferredAt=s,Ee(o,"transferred-out",{toNode:t,mode:n,box:o.leadBox,...i?{rescueRef:i.ref,rescueSha:i.sha}:{},...a.pushed.length?{rescues:a.pushed}:{}}),await ae(),{ok:!0,toNode:t,rescue:i?{ref:i.ref,sha:i.sha}:null,rescues:a.pushed}}async function Gy(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await Qp(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
|
|
265
|
-
`)[0],advanced:null}}}function qq(e){if(typeof e!="string"||!Wq.test(e)||e.includes("..")||e.includes("@{")||e.endsWith(".lock"))throw new Error("branch fails the safe-ref check")}async function Ky(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&&qq(e.branch),Rp.has(t))throw new Error("an adopt is already running for this Track");Rp.set(t,{startedAt:Date.now()});try{return await Gq({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{Rp.delete(t)}}async function Gq({trackId:e,name:t,agent:n,skipPermissions:r,mode:o,fromNode:s=null,toNode:a=null,branch:i=null,repos:c=[],wip:l=null,wips:u=null,brief:d=null,epochs:p=[],createdAt:f=null,leadBox:w=null,reason:h=null,downSince:y=null}){let k=await ni(n,r),g=await te(),x=fe(g,e);if(x&&!x.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);x&&(await Cg(x).catch(()=>{}),await H$(x),g.tracks.splice(g.tracks.indexOf(x),1),await ae()),await Y.mkdir(al,{recursive:!0});let A=z.join(al,e);await Y.rm(A,{recursive:!0,force:!0}).catch(()=>{});let I=null,M=!1,P=[];try{let L=[];if(i)for(let le of Array.isArray(c)?c:[]){let ge=typeof le=="string"?le:le?.repo;if(!ge||!/^[^/\s]+\/[^/\s]+$/.test(ge))continue;let de=await Zs(ge)??await du(ge);await Mn(de,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],Hy),L.push({slug:ge,base:de,name:typeof le=="object"?le?.name??null:null})}if(i&&L.length===1){let{base:le,slug:ge}=L[0];await v$(le,A,i),P.push({base:le,dir:A}),await ul(A);let de=await ms(le);I={dir:A,base:le,branch:i,kind:"worktree",baseRef:de,repo:ge}}else if(i&&L.length>=2){await Y.mkdir(A,{recursive:!0});let le=new Set([".bostrat"]),ge=[];for(let de of L){let ee=de.name||z.basename(de.base);le.has(ee)&&(ee=de.slug.replace("/","-"));for(let Ve=2;le.has(ee);Ve++)ee=`${z.basename(de.base)}-${Ve}`;le.add(ee);let we=z.join(A,ee);await v$(de.base,we,i),P.push({base:de.base,dir:we});let Oe=await ms(de.base);ge.push({name:ee,dir:we,base:de.base,baseRef:Oe,repo:de.slug})}await oi(A,ge,i),I={dir:A,kind:"multi",branch:i,repos:ge}}else M=!0,await Y.mkdir(A,{recursive:!0}),await zh(A),await ul(A),I={dir:A,base:null,branch:null,kind:"repo"};let T=[];for(let le of Uq(I,{wip:l,wips:u})){let ge=await Hq(le.dir,e);T.push({name:le.name,repo:le.repo,applied:ge.applied,skipped:ge.skipped,...ge.advanced?{advanced:ge.advanced}:{}})}let E=T.some(le=>le.applied),j=T.length===1?T[0].skipped:T.filter(le=>le.skipped).map(le=>`${le.name??le.repo}: ${le.skipped}`).join("; ")||null,G=T.length===1?T[0].advanced??null:null;await t$({id:e,cwd:A}).catch(()=>({restored:!1}));let R=z.join(A,Io),S=null;(await Tu(A)).restored?S="checkpoint":typeof d=="string"&&d.trim()&&(await Y.mkdir(z.dirname(R),{recursive:!0}),await Y.writeFile(R,d),S="brief");let H=new Set(g.tracks.flatMap(le=>(le.boxes||[]).map(ge=>ge.name))),B=null;w&&!H.has(w)&&!await Se(w).catch(()=>null)?B=w:(B=`${t.trim()} lead`,H.has(Cu(B))&&(B=`${t.trim().slice(0,50).trim()} ${e} lead`));let Z=await jn(B,A,{agentCmd:k,agent:n});ns({cwd:Z.cwd,sessionName:Z.name,agent:n,agentCmd:k});let xe=Date.now(),Ae=Fy(Z,"lead","live"),_e=(Array.isArray(p)?p:[]).filter(le=>le&&typeof le=="object").map(le=>({...le})),Q=_e[_e.length-1];Q&&Q.endedAt==null&&(Q.endedAt=xe,Q.endReason==null&&(Q.endReason=o==="recover"?"node-offline":"node-transfer"));let Xe=await us({agent:n,agentCmd:k});_e.push({agent:n,sessionId:Z.sessionId??null,pinned:Z.pinned===!0,cliVersion:Xe,startedAt:xe,endedAt:null}),Ae.epochs=_e,Ae.cliVersion=Xe;let Pe={id:e,name:t.trim(),cwd:Z.cwd,workspace:I,leadBox:Z.name,boxes:[Ae],events:[],createdAt:Number.isFinite(f)?f:xe,...S?{pendingHandoffHydrate:{artifact:Io,at:xe}}:{}};return Ee(Pe,"transferred-in",{box:Z.name,agent:n,fromNode:s,toNode:a,mode:o,reason:h,...o==="recover"?{downSince:Number.isFinite(y)?y:null}:{},branch:I.branch??null,wip:E,...G?{wipAdvanced:G}:{},...I.kind==="multi"&&T.length?{wipRepos:T}:{},epoch:_e.length-1,name:Pe.name,cwd:Pe.cwd,workspace:{base:I.base??null,branch:I.branch??null,kind:I.kind,...I.baseRef?{baseRef:I.baseRef}:{},...I.repo?{repo:I.repo}:{},...I.repos?{repos:I.repos.map(({name:le,base:ge,baseRef:de,repo:ee})=>({name:le,base:ge,baseRef:de,repo:ee}))}:{}}}),g.tracks.push(Pe),await ae(),{...await Zt(Pe),adopt:{wipApplied:E,wipSkipped:j,...G?{wipAdvanced:G}:{},...I.kind==="multi"?{wipRepos:T}:{},codeless:M,artifactSource:S}}}catch(L){for(let T of P)await $t(T.base,T.dir).catch(()=>{});throw await Y.rm(A,{recursive:!0,force:!0}).catch(()=>{}),L}}async function K$(e,t,n){let r=await te(),o=fe(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await ae())}async function Xa(e,t){e.step=t,await yl(e)}function V$(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 z$(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function Jq(e,t,n){if(!z$(n)||Wt.has(t)||!await Se(n.box).catch(()=>null))return!1;try{await Y.access(n.dir)}catch{return!1}let o=await Se(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return Wt.set(t,s),Y$(s,o).catch(a=>J$(s,a)),!0}async function Vy(){let e=await te(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Wt.has(r))continue;if(await Jq(n,r,o)){t=!0,Ee(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await re({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=V$(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!jr.has(i.state)&&(i.state="canceled"),Ee(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await re({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"||jr.has(r.state)||Wt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?z.join(ti,`${n.id}-${r.subtaskId}`):null;await re({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 Yq(e)||t,t&&await ae(),t}async function S$(e,t,n){if(t&&await Lt(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of Gt(e.workspace)){let s=z.join(n,o.name);await $t(o.base,s).then(()=>!0,()=>!1)||await $t(o.dir,s).catch(()=>{})}await Y.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await Se(e.leadBox).catch(()=>null);r?.cwd&&await $t(r.cwd,n).catch(()=>{})}async function Yq(e){let t;try{t=await Y.readdir(ti)}catch{return!1}let n=new Set;for(let o of e.tracks||[]){for(let s of Object.keys(o.subtasks||{}))n.add(`${o.id}-${s}`);for(let s of o.boxes||[])s.subtaskId&&n.add(`${o.id}-${s.subtaskId}`)}for(let o of Wt.values())o.dir&&n.add(z.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=z.join(ti,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of Gt(a.workspace))await $t(i.base,z.join(s,i.name)).catch(()=>{});await Y.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await Se(a.leadBox).catch(()=>null):null;i?.cwd?await $t(i.cwd,s).catch(()=>{}):await Y.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await re({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function zy(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!_a(n))throw new Error("task (string) required");let s=await ni(t,r),a=mo(t);if(a){let f=new Error(`can't delegate to ${t} \u2014 its ${a.window} spend limit is reached ($${a.spentUsd.toFixed(2)} of $${a.limitUsd.toFixed(2)})`);throw f.usageCap=a,f}let i=fe(await te(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await Se(i.leadBox);if(!c)throw new Error(`lead box "${i.leadBox}" is not running`);if(!c.cwd)throw new Error("can't resolve the lead's working directory");let l=i.workspace?.kind==="multi",u=null;if(l){let f=Gt(i.workspace);if(!f.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let w of f)if(!w?.dir||!await Ia(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 Ia(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=Dp().slice(0,8),p={id:d,trackId:e,trackName:i.name,agent:t,task:_a(n),skipPermissions:r,reason:_$(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${tf(i.name)}-${d}`,base:null,repos:u,dir:z.join(ti,`${e}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Wt.set(d,p),await yl(p),Xq(p,c,s).catch(f=>J$(p,f)),p}async function J$(e,t){e.failedStep=e.step,e.error=t instanceof fl?"canceled":t?.message||String(t),e.step=t instanceof fl?"canceled":"error",e.finishedAt=Date.now(),await Yy(e).catch(()=>{}),await yl(e).catch(()=>{}),await t5(e).catch(()=>{})}function nf(e){let t=Wt.get(e);if(!t)throw new Error(`subtask "${e}" not found`);if(t.finishedAt)throw new Error("subtask already finished");return t.canceled=!0,t}function Jy(e){let t=Wt.get(e);if(!t)throw new Error(`subtask "${e}" not found`);if(t.finishedAt)throw new Error("subtask already finished");if(t.step!=="working")throw new Error(`subtask is not waiting to merge (step: ${t.step})`);return t.forceMerge=!0,t}async function Xq(e,t,n){if(await Xa(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await $a(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await yl(e)}else e.base=await $a(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await Xa(e,"spawn"),await Y.mkdir(ti,{recursive:!0}),e.repos){await Y.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await fs(a.leadDir,z.join(e.dir,a.name),e.branch,a.base);let s=Gt((fe(await te(),e.trackId)||{}).workspace);await oi(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 fs(t.cwd,e.dir,e.branch,e.base),await ul(e.dir);await Zq(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 Qq(e,o),await I$(e.trackId,o.name,DW(e.task,!!e.repos),{kind:"subtask"}),await Y$(e,t)}async function Y$(e,t){await Xa(e,"working");let n=z.join(e.dir,Op);if(await r5(n,e),e.canceled)throw new fl;if(await Xa(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=z.join(e.dir,l.name);await Kh(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:x$({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await Yh(l.leadDir,l.base,e.branch,Ya),l.files=await Xh(l.leadDir,l.base,e.branch,Ya);let d=await Zh(l.leadDir,l.base,e.branch,Ya);l.applied=d.applied,l.conflicts=d.conflicts??[],s.files+=l.stat.files,s.insertions+=l.stat.insertions,s.deletions+=l.stat.deletions,a.push(...l.files.map(p=>z.join(l.name,p))),i.push(...l.conflicts.map(p=>p.startsWith("patch did not apply cleanly")?`${l.name}: ${p}`:z.join(l.name,p))),d.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await Kh(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:x$({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await Yh(t.cwd,e.base,e.branch,Ya),e.files=await Xh(t.cwd,e.base,e.branch,Ya);let s=await Zh(t.cwd,e.base,e.branch,Ya);e.applied=s.applied,e.conflicts=s.conflicts}await yl(e);let r=await n5(e,t,n);await Xa(e,"resume"),await I$(e.trackId,e.leadBox,BW(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await Xa(e,"reap"),await Yy(e),e.step="done";let o=Date.now();await K$(e.trackId,e.id,{...ef(e),finishedAt:o}),await e5(e,o),e.finishedAt=o}async function Zq(e,t){let n=z.join(e.dir,z.dirname(il));await Y.mkdir(n,{recursive:!0});let r="";try{r=await Cx(t.cwd,t.sessionId)}catch{}if(!r)try{r=await Y.readFile(z.join(t.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${e.id}
|
|
263
|
+
${a}`}async function Ny(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<Yi()?!1:Fn(e.name)}async function pl(e,t,n){return!t||e.archivedAt||jr.has(t.state)?!1:(await re({flow:"rehydrate",phase:"resume-dead-cli",trackId:e.id,sessionName:t.name,agent:t.agent,via:n}).catch(()=>{}),console.warn(`[track] ${t.name}: CLI is not running (bare shell) \u2014 relaunching (${n})`),await Lt(t.name).catch(()=>{}),t.state="stopped",si(e))}async function si(e,{freshSession:t=!1,reason:n=null}={}){let r=e.boxes.find(a=>a.name===e.leadBox);if(!r)return!1;let o=ey.get(r.name);if(o)return o;let s=Uq(e,r,{freshSession:t,reason:n}).finally(()=>ey.delete(r.name));return ey.set(r.name,s),s}async function Py(e){let n=(await te()).tracks.find(o=>o.leadBox===e);if(!n||n.archivedAt||n.transferredTo||Date.now()-(ty.get(e)??0)<Fq)return!1;let r=await si(n,{reason:"attach"}).catch(()=>!1);return r?(ty.delete(e),await ae()):ty.set(e,Date.now()),r}async function Uq(e,t,{freshSession:n=!1,reason:r=null}={}){if(e.archivedAt||e.transferredTo||jr.has(t.state)||await Se(t.name))return!1;let o=n===!0,s=o?Dp():null,a=o?zi(ly(t.agent),s,t.name):jp(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),i=await Mu(a);if(i===ca)return Ee(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;i===la&&console.warn(`[track] ${t.name}: could not confirm "${t.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);let c=o?{sessionId:t.sessionId??null,pinned:t.pinned===!0,epochs:JSON.parse(JSON.stringify(_o(t)))}:null,l=Date.now();if(o){let u=_o(t),d=u[u.length-1];d.endedAt==null&&(d.endedAt=l),d.endReason==null&&(d.endReason=r||"fresh-session"),u.push({agent:t.agent,sessionId:s,pinned:t.agent==="claude",startedAt:l,endedAt:null}),t.sessionId=s,t.pinned=t.agent==="claude"}try{let u=us({agent:t.agent,agentCmd:a}),d=await jn(t.name,t.cwd,{agentCmd:a,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=Ho(d.hookToken),t.cliVersion=await u,_o(t)[t.epochs.length-1].cliVersion=t.cliVersion,ns({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:a,onBlocked:({message:p})=>I$(e.id,{box:t.name,agent:t.agent,message:p}).catch(()=>{})}),t.state="live",o&&Ee(e,"lead-refreshed",{box:t.name,agent:t.agent,fromSessionId:c.sessionId,toSessionId:s,epoch:t.epochs.length-1,reason:r||"fresh-session",cliVersion:t.cliVersion??null,standing:await Ja(t.cwd)}),Ee(e,"rehydrated",{box:t.name,agent:t.agent,...o?{fresh:!0}:{}}),!0}catch(u){return o&&(t.sessionId=c.sessionId,t.pinned=c.pinned,t.epochs=c.epochs),Ee(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(u?.message||u)}),!1}}async function Jp(e,{freshSession:t=!1}={}){let n=await te(),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,dl.delete(e),Ee(r,"unarchived",{}),await si(r,t?{freshSession:!0,reason:"loop-fire"}:{}),await ae(),Zt(r)}async function Dy(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>cy)throw new Error(`name too long (max ${cy} chars)`);let r=await te(),o=fe(r,e);if(!o)throw new Error(`track "${e}" not found`);if(String(o.name||"").startsWith("\u21BB"))throw new Error("loop run Tracks are named by their loop \u2014 rename the loop on the Loops tab");if(n===o.name)return Zt(o);let s=o.name;return o.name=n,Ee(o,"renamed",{from:s,to:n}),await ae(),Zt(o)}async function By(){let e=await te(),t=0;for(let n of e.tracks||[])n.transferredTo&&Ad(n,{at:n.transferredAt??Date.now()})&&t++;return t&&await ae(),t}async function Ly(){let e=await te(),t=new Set((await kn()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!Ue(s)).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(Ue(s))for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Lt(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):Ee(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!jr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await ae(),r}async function Yp(e,{freshSession:t=!1}={}){let n=await te(),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`);Zp(r);let o=r.boxes.find(i=>i.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(jr.has(o.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await Se(r.leadBox)){let i=await Ne(r.leadBox,{lines:ze}).catch(()=>"");if(ac(o.agent,i))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Lt(r.leadBox),o.state="stopped"}let a=await si(r,t?{freshSession:!0,reason:"loop-fire"}:{});if(await ae(),!a)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Zt(r)}async function jy(e,t,n){let r=await me(e);if(!r)throw new Error(`track "${e}" not found`);if(!t)throw new Error("unknown offer");let o=await K(r).catch(()=>[]);if(!o.some(i=>i.type==="box.fullscreen.detected"&&i.payload?.offerId===t))throw new Error("unknown offer");if(o.some(i=>i.type==="box.fullscreen.answered"&&i.payload?.offerId===t))throw new Error("offer already answered");return n&&await Yp(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 S$(e,t){if(!e?.id||!t?.box)return[];let n=(e.boxes||[]).find(r=>r.name===t.box);return ep({trackId:e.id,boxId:t.box,epoch:0,provider:n?.agent??t.agent??null})}function _o(e){return(!Array.isArray(e.epochs)||e.epochs.length===0)&&(e.epochs=[{agent:e.agent??null,sessionId:e.sessionId??null,pinned:e.pinned===!0,cliVersion:e.cliVersion??null,startedAt:e.createdAt??null,endedAt:null}]),e.epochs}async function Np(e,t){let n=await te(),r=fe(n,e);r&&(r.handoff={...r.handoff||{},...t},await ae())}function V$(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 Uy(){let e=await te(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=V$(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await Se(r.fromBox).catch(()=>null),l=n.boxes.find(u=>u.name===r.fromBox);if(c&&l&&c.agent===r.toAgent&&r.toAgent!==l.agent){let u=Date.now(),d=_o(l),p=d[d.length-1],f=p.agent,w=p.sessionId??null;p.endedAt=u,r.reason&&(p.endReason=r.reason);let h=c.agent==="claude"&&!!c.sessionId,y=await us({agent:r.toAgent});d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:h,cliVersion:y,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.cliVersion=y,l.sessionId=c.sessionId||null,l.pinned=h,l.state="live",n.pendingHandoffHydrate={artifact:Io,at:u},Ee(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:f,toAgent:r.toAgent,fromSessionId:w,toSessionId:c.sessionId||null,epoch:d.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0,standing:await Ja(l.cwd)}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await re({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 re({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Lt(i).catch(()=>{}),a&&await Lt(a).catch(()=>{})}return t&&await ae(),t}async function Xp(e,t,n=!0,r=null){let o=Cs.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=fe(await te(),e);if(!s)throw new Error(`track "${e}" not found`);ri(e,"handoff");let a=await ni(t,n),i=mo(t);if(i){let u=new Error(`can't hand off to ${t} \u2014 its ${i.window} spend limit is reached ($${i.spentUsd.toFixed(2)} of $${i.limitUsd.toFixed(2)})`);throw u.usageCap=i,u}let c=await Se(s.leadBox);if(!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l={trackId:e,step:"checkpoint",fromBox:s.leadBox,toAgent:t,skipPermissions:n,reason:$$(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return Cs.set(e,l),await Np(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),Hq(l,s,c,a,t).catch(async u=>{l.error=u?.message||String(u),l.finishedAt=Date.now(),console.error(`[lead-handoff ${e}] FAILED at step "${l.step}": ${l.error}`),await Np(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),re({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),re({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function Hq(e,t,n,r,o){let s=async(P,j={})=>{e.step=P,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${P}`),await Np(e.trackId,{step:P,toBox:e.toBox}),await re({flow:"lead-handoff",phase:P,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:j.sessionName,...j})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await te(),i=fe(a,e.trackId)?.boxes.find(P=>P.name===e.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:e.fromBox}),e.checkpoint="llm",e.quiescence=null,mo(c))e.checkpoint="skipped-capped";else try{let P=await wd(e.fromBox,{timeoutMs:uy,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=Oh(P),e.waitedMs=(P.quiescence?.pre?.waitedMs||0)+(P.quiescence?.post?.waitedMs||0)}catch(P){e.checkpoint="reconstructed",e.quiescence=Oh(P),e.waitedMs=P?.quiescence?.pre?.waitedMs||0,e.wait=null,await re({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:P?.message||String(P)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await gd(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=us({agent:o,agentCmd:r}),f=await jh(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let w=ov(f.launchCmd);await wr({cwd:n.cwd,sessionId:f.sessionId,sessionName:e.fromBox,agent:o,agentCmd:Ns(o),launchEcho:w}).catch(P=>{if(P?.code!=="cli-not-running")throw P});let h=zu(await Ne(e.fromBox,{lines:ze}).catch(()=>""),w);if(await re({flow:"lead-handoff",phase:"hydrate-ready",trackId:e.trackId,sessionName:e.fromBox,toAgent:o,up:to(o,h),paneTail:!0}).catch(()=>{}),!to(o,h)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let P=jp(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await jh(e.fromBox,{agentCmd:P,agent:c,resumeSessionId:l,pinned:u});let j=await te(),T=fe(j,e.trackId);if(T){let $=T.boxes.find(L=>L.name===e.fromBox);$&&($.state="live"),Ee(T,"lead-handoff-failed",{box:e.fromBox,fromAgent:c,toAgent:o,reason:e.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:e.startedAt}),await ae()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let y=await te(),k=fe(y,e.trackId);if(!k)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let g=k.boxes.find(P=>P.name===e.fromBox);if(!g)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let x=Date.now(),S=_o(g),_=S[S.length-1];_.endedAt=x,e.reason&&(_.endReason=e.reason);let M=await p;S.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,cliVersion:M,startedAt:x,endedAt:null}),g.agent=o,g.sessionId=f.sessionId,g.pinned=f.pinned,g.cliVersion=M,g.state="live",g.role="lead",k.pendingHandoffHydrate={artifact:e.statusArtifact||Io,at:x},Ee(k,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:S.length-1,singleBox:!0,forced:f.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,cliVersion:M,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint,quiescence:e.quiescence||void 0,waitedMs:e.waitedMs||void 0,standing:await Ja(g.cwd)}),await ae(),await s("done",{sessionName:e.fromBox}),await Np(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function qy(e){let t=Pp.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await te(),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=Cs.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of Wt.values())if(a.trackId===e&&!a.finishedAt)throw new Error("a subtask is still running on this Track \u2014 wait for it to merge back before moving");let s={trackId:e,step:"checkpoint",startedAt:Date.now(),finishedAt:null,error:null};return Pp.set(e,s),Wq(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 Wq(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await Se(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&mo(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await wd(t.leadBox,{timeoutMs:uy,quiesce:{box:n,reason:"transfer-out",onWait:()=>{e.step="await-turn"},onResume:()=>{e.step="checkpoint"}}})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await gd(t.id,t.cwd,{trackName:t.name||t.id}).catch(()=>null),e.step="push";let o=t.workspace?.branch??null,s=[];if(o)for(let u of Gt(t.workspace)){if(!u?.dir)continue;let d={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await Mn(u.dir,["push","origin",o],Hy);let{stdout:p}=await Mn(u.dir,["rev-parse","HEAD"]);d.baseSha=p.trim(),d.pushed=!0}catch(p){d.error=String(p?.message||p).split(`
|
|
264
|
+
`)[0]}s.push(d)}e.step="wip";let a=await Mg(t,{force:!0}),i=await te(),c=fe(i,t.id),l=c?.boxes.find(u=>u.name===c.leadBox)||null;l&&_o(l),await ae(),e.result={name:c?.name??t.name,agent:l?.agent??null,leadBox:c?.leadBox??t.leadBox,createdAt:c?.createdAt??null,branch:o,repos:s.filter(u=>u.pushed).map(({name:u,repo:d,baseSha:p})=>({name:u,repo:d,baseSha:p})),unpushed:s.filter(u=>!u.pushed).map(({name:u,repo:d,error:p})=>({name:u,repo:d,error:p})),wip:a.pushed[0]?{ref:a.pushed[0].ref,sha:a.pushed[0].sha}:null,wips:a.pushed,wipSkipped:a.skipped,checkpoint:e.checkpoint,epochs:l?.epochs??[],codeless:!o||!s.some(u=>u.pushed)},e.step="ready",e.finishedAt=Date.now()}function Zp(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 Qp(e,{toNode:t,mode:n="transfer"}={}){if(!t||typeof t!="string")throw new Error("toNode required");let r=await te(),o=fe(r,e);if(!o)throw new Error(`track "${e}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};vy(e),await Oy(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();Ad(o,{at:s,reason:n==="recover"?"node-offline":"node-transfer"});let a=await Cg(o,{ts:s}),i=a.pushed[0]??null;return o.transferredTo=t,o.transferredAt=s,Ee(o,"transferred-out",{toNode:t,mode:n,box:o.leadBox,...i?{rescueRef:i.ref,rescueSha:i.sha}:{},...a.pushed.length?{rescues:a.pushed}:{}}),await ae(),{ok:!0,toNode:t,rescue:i?{ref:i.ref,sha:i.sha}:null,rescues:a.pushed}}async function Gy(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await Qp(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 A$(e,t,n){try{await vc(e,t,n)}catch{await Mn(e,["worktree","prune"]).catch(()=>{}),await vc(e,t,n)}return t}function qq(e,{wip:t=null,wips:n=null}={}){let r=Gt(e).filter(a=>a?.dir),o=Array.isArray(n)?n.filter(a=>a?.sha):[];if(!o.length)return t?.sha&&r.length===1?r:[];let s=r.filter(a=>o.some(i=>i.repo?i.repo===a.repo:i.name!=null&&i.name===a.name));return!s.length&&r.length===1&&o.length===1?r:s}async function Gq(e,t){try{await Mn(e,["fetch","origin",Og(t)],Hy);let{stdout:n}=await Mn(e,["rev-parse","FETCH_HEAD^"]),{stdout:r}=await Mn(e,["rev-parse","HEAD"]),o=null,s=n.trim()===r.trim();return s||await Mn(e,["merge-base","--is-ancestor",r.trim(),n.trim()]).then(()=>!0,()=>!1)&&(await Mn(e,["reset","--hard",n.trim()]),o={from:r.trim(),to:n.trim()},s=!0),s?(await Mn(e,["read-tree","--reset","-u","FETCH_HEAD"]),await Mn(e,["reset","-q"]),{applied:!0,skipped:null,advanced:o}):{applied:!1,skipped:"snapshot does not parent the fetched branch head",advanced:null}}catch(n){return{applied:!1,skipped:String(n?.message||n).split(`
|
|
265
|
+
`)[0],advanced:null}}}function Vq(e){if(typeof e!="string"||!Kq.test(e)||e.includes("..")||e.includes("@{")||e.endsWith(".lock"))throw new Error("branch fails the safe-ref check")}async function Ky(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&&Vq(e.branch),Rp.has(t))throw new Error("an adopt is already running for this Track");Rp.set(t,{startedAt:Date.now()});try{return await zq({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{Rp.delete(t)}}async function zq({trackId:e,name:t,agent:n,skipPermissions:r,mode:o,fromNode:s=null,toNode:a=null,branch:i=null,repos:c=[],wip:l=null,wips:u=null,brief:d=null,epochs:p=[],createdAt:f=null,leadBox:w=null,reason:h=null,downSince:y=null}){let k=await ni(n,r),g=await te(),x=fe(g,e);if(x&&!x.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);x&&(await Cg(x).catch(()=>{}),await q$(x),g.tracks.splice(g.tracks.indexOf(x),1),await ae()),await Y.mkdir(al,{recursive:!0});let S=z.join(al,e);await Y.rm(S,{recursive:!0,force:!0}).catch(()=>{});let _=null,M=!1,P=[];try{let j=[];if(i)for(let le of Array.isArray(c)?c:[]){let ge=typeof le=="string"?le:le?.repo;if(!ge||!/^[^/\s]+\/[^/\s]+$/.test(ge))continue;let de=await Zs(ge)??await du(ge);await Mn(de,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],Hy),j.push({slug:ge,base:de,name:typeof le=="object"?le?.name??null:null})}if(i&&j.length===1){let{base:le,slug:ge}=j[0];await A$(le,S,i),P.push({base:le,dir:S}),await ul(S);let de=await ms(le);_={dir:S,base:le,branch:i,kind:"worktree",baseRef:de,repo:ge}}else if(i&&j.length>=2){await Y.mkdir(S,{recursive:!0});let le=new Set([".bostrat"]),ge=[];for(let de of j){let ee=de.name||z.basename(de.base);le.has(ee)&&(ee=de.slug.replace("/","-"));for(let Ve=2;le.has(ee);Ve++)ee=`${z.basename(de.base)}-${Ve}`;le.add(ee);let we=z.join(S,ee);await A$(de.base,we,i),P.push({base:de.base,dir:we});let Oe=await ms(de.base);ge.push({name:ee,dir:we,base:de.base,baseRef:Oe,repo:de.slug})}await oi(S,ge,i),_={dir:S,kind:"multi",branch:i,repos:ge}}else M=!0,await Y.mkdir(S,{recursive:!0}),await zh(S),await ul(S),_={dir:S,base:null,branch:null,kind:"repo"};let T=[];for(let le of qq(_,{wip:l,wips:u})){let ge=await Gq(le.dir,e);T.push({name:le.name,repo:le.repo,applied:ge.applied,skipped:ge.skipped,...ge.advanced?{advanced:ge.advanced}:{}})}let $=T.some(le=>le.applied),L=T.length===1?T[0].skipped:T.filter(le=>le.skipped).map(le=>`${le.name??le.repo}: ${le.skipped}`).join("; ")||null,H=T.length===1?T[0].advanced??null:null;await r$({id:e,cwd:S}).catch(()=>({restored:!1}));let R=z.join(S,Io),A=null;(await Tu(S)).restored?A="checkpoint":typeof d=="string"&&d.trim()&&(await Y.mkdir(z.dirname(R),{recursive:!0}),await Y.writeFile(R,d),A="brief");let W=new Set(g.tracks.flatMap(le=>(le.boxes||[]).map(ge=>ge.name))),B=null;w&&!W.has(w)&&!await Se(w).catch(()=>null)?B=w:(B=`${t.trim()} lead`,W.has(Cu(B))&&(B=`${t.trim().slice(0,50).trim()} ${e} lead`));let Z=await jn(B,S,{agentCmd:k,agent:n});ns({cwd:Z.cwd,sessionName:Z.name,agent:n,agentCmd:k});let xe=Date.now(),Ae=Fy(Z,"lead","live"),_e=(Array.isArray(p)?p:[]).filter(le=>le&&typeof le=="object").map(le=>({...le})),Q=_e[_e.length-1];Q&&Q.endedAt==null&&(Q.endedAt=xe,Q.endReason==null&&(Q.endReason=o==="recover"?"node-offline":"node-transfer"));let Xe=await us({agent:n,agentCmd:k});_e.push({agent:n,sessionId:Z.sessionId??null,pinned:Z.pinned===!0,cliVersion:Xe,startedAt:xe,endedAt:null}),Ae.epochs=_e,Ae.cliVersion=Xe;let Pe={id:e,name:t.trim(),cwd:Z.cwd,workspace:_,leadBox:Z.name,boxes:[Ae],events:[],createdAt:Number.isFinite(f)?f:xe,...A?{pendingHandoffHydrate:{artifact:Io,at:xe}}:{}};return Ee(Pe,"transferred-in",{box:Z.name,agent:n,fromNode:s,toNode:a,mode:o,reason:h,...o==="recover"?{downSince:Number.isFinite(y)?y:null}:{},branch:_.branch??null,wip:$,...H?{wipAdvanced:H}:{},..._.kind==="multi"&&T.length?{wipRepos:T}:{},epoch:_e.length-1,name:Pe.name,cwd:Pe.cwd,workspace:{base:_.base??null,branch:_.branch??null,kind:_.kind,..._.baseRef?{baseRef:_.baseRef}:{},..._.repo?{repo:_.repo}:{},..._.repos?{repos:_.repos.map(({name:le,base:ge,baseRef:de,repo:ee})=>({name:le,base:ge,baseRef:de,repo:ee}))}:{}}}),g.tracks.push(Pe),await ae(),{...await Zt(Pe),adopt:{wipApplied:$,wipSkipped:L,...H?{wipAdvanced:H}:{},..._.kind==="multi"?{wipRepos:T}:{},codeless:M,artifactSource:A}}}catch(j){for(let T of P)await $t(T.base,T.dir).catch(()=>{});throw await Y.rm(S,{recursive:!0,force:!0}).catch(()=>{}),j}}async function z$(e,t,n){let r=await te(),o=fe(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await ae())}async function Xa(e,t){e.step=t,await yl(e)}function J$(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 Y$(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function Zq(e,t,n){if(!Y$(n)||Wt.has(t)||!await Se(n.box).catch(()=>null))return!1;try{await Y.access(n.dir)}catch{return!1}let o=await Se(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return Wt.set(t,s),Z$(s,o).catch(a=>X$(s,a)),!0}async function Vy(){let e=await te(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(Wt.has(r))continue;if(await Zq(n,r,o)){t=!0,Ee(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await re({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=J$(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!jr.has(i.state)&&(i.state="canceled"),Ee(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await re({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await T$(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||jr.has(r.state)||Wt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?z.join(ti,`${n.id}-${r.subtaskId}`):null;await re({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await T$(n,r.name,o)}}return t=await Qq(e)||t,t&&await ae(),t}async function T$(e,t,n){if(t&&await Lt(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of Gt(e.workspace)){let s=z.join(n,o.name);await $t(o.base,s).then(()=>!0,()=>!1)||await $t(o.dir,s).catch(()=>{})}await Y.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await Se(e.leadBox).catch(()=>null);r?.cwd&&await $t(r.cwd,n).catch(()=>{})}async function Qq(e){let t;try{t=await Y.readdir(ti)}catch{return!1}let n=new Set;for(let o of e.tracks||[]){for(let s of Object.keys(o.subtasks||{}))n.add(`${o.id}-${s}`);for(let s of o.boxes||[])s.subtaskId&&n.add(`${o.id}-${s.subtaskId}`)}for(let o of Wt.values())o.dir&&n.add(z.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=z.join(ti,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of Gt(a.workspace))await $t(i.base,z.join(s,i.name)).catch(()=>{});await Y.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await Se(a.leadBox).catch(()=>null):null;i?.cwd?await $t(i.cwd,s).catch(()=>{}):await Y.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await re({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function zy(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!_a(n))throw new Error("task (string) required");let s=await ni(t,r),a=mo(t);if(a){let f=new Error(`can't delegate to ${t} \u2014 its ${a.window} spend limit is reached ($${a.spentUsd.toFixed(2)} of $${a.limitUsd.toFixed(2)})`);throw f.usageCap=a,f}let i=fe(await te(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await Se(i.leadBox);if(!c)throw new Error(`lead box "${i.leadBox}" is not running`);if(!c.cwd)throw new Error("can't resolve the lead's working directory");let l=i.workspace?.kind==="multi",u=null;if(l){let f=Gt(i.workspace);if(!f.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let w of f)if(!w?.dir||!await Ia(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 Ia(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=Dp().slice(0,8),p={id:d,trackId:e,trackName:i.name,agent:t,task:_a(n),skipPermissions:r,reason:$$(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${tf(i.name)}-${d}`,base:null,repos:u,dir:z.join(ti,`${e}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return Wt.set(d,p),await yl(p),e5(p,c,s).catch(f=>X$(p,f)),p}async function X$(e,t){e.failedStep=e.step,e.error=t instanceof fl?"canceled":t?.message||String(t),e.step=t instanceof fl?"canceled":"error",e.finishedAt=Date.now(),await Yy(e).catch(()=>{}),await yl(e).catch(()=>{}),await o5(e).catch(()=>{})}function nf(e){let t=Wt.get(e);if(!t)throw new Error(`subtask "${e}" not found`);if(t.finishedAt)throw new Error("subtask already finished");return t.canceled=!0,t}function Jy(e){let t=Wt.get(e);if(!t)throw new Error(`subtask "${e}" not found`);if(t.finishedAt)throw new Error("subtask already finished");if(t.step!=="working")throw new Error(`subtask is not waiting to merge (step: ${t.step})`);return t.forceMerge=!0,t}async function e5(e,t,n){if(await Xa(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await $a(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await yl(e)}else e.base=await $a(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await Xa(e,"spawn"),await Y.mkdir(ti,{recursive:!0}),e.repos){await Y.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await fs(a.leadDir,z.join(e.dir,a.name),e.branch,a.base);let s=Gt((fe(await te(),e.trackId)||{}).workspace);await oi(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 fs(t.cwd,e.dir,e.branch,e.base),await ul(e.dir);await t5(e,t);let r=e.trackName.slice(0,38).trim(),o=await jn(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await n5(e,o),await R$(e.trackId,o.name,jW(e.task,!!e.repos),{kind:"subtask"}),await Z$(e,t)}async function Z$(e,t){await Xa(e,"working");let n=z.join(e.dir,Op);if(await a5(n,e),e.canceled)throw new fl;if(await Xa(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=z.join(e.dir,l.name);await Kh(u,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:S$({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),l.stat=await Yh(l.leadDir,l.base,e.branch,Ya),l.files=await Xh(l.leadDir,l.base,e.branch,Ya);let d=await Zh(l.leadDir,l.base,e.branch,Ya);l.applied=d.applied,l.conflicts=d.conflicts??[],s.files+=l.stat.files,s.insertions+=l.stat.insertions,s.deletions+=l.stat.deletions,a.push(...l.files.map(p=>z.join(l.name,p))),i.push(...l.conflicts.map(p=>p.startsWith("patch did not apply cleanly")?`${l.name}: ${p}`:z.join(l.name,p))),d.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await Kh(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`,{trailers:S$({id:e.trackId,boxes:[{name:e.box,agent:e.agent}]},e)}),e.stat=await Yh(t.cwd,e.base,e.branch,Ya),e.files=await Xh(t.cwd,e.base,e.branch,Ya);let s=await Zh(t.cwd,e.base,e.branch,Ya);e.applied=s.applied,e.conflicts=s.conflicts}await yl(e);let r=await s5(e,t,n);await Xa(e,"resume"),await R$(e.trackId,e.leadBox,FW(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await Xa(e,"reap"),await Yy(e),e.step="done";let o=Date.now();await z$(e.trackId,e.id,{...ef(e),finishedAt:o}),await r5(e,o),e.finishedAt=o}async function t5(e,t){let n=z.join(e.dir,z.dirname(il));await Y.mkdir(n,{recursive:!0});let r="";try{r=await Cx(t.cwd,t.sessionId)}catch{}if(!r)try{r=await Y.readFile(z.join(t.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${e.id}
|
|
266
266
|
|
|
267
267
|
**Delegated task:** ${e.task}
|
|
268
268
|
|
|
@@ -273,7 +273,7 @@ ${a}`}async function Ny(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<
|
|
|
273
273
|
## Conversation so far (lead \u2192 you)
|
|
274
274
|
|
|
275
275
|
${r}
|
|
276
|
-
`;await Y.writeFile(z.join(e.dir,il),o)}async function
|
|
276
|
+
`;await Y.writeFile(z.join(e.dir,il),o)}async function n5(e,t){let n=await te(),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:Ho(t.hookToken),createdAt:Date.now(),subtaskId:e.id,branch:e.branch,task:e.task}),Ee(r,"subtask-delegated",{to:t.name,toAgent:e.agent,task:e.task,subtaskId:e.id,skipPermissions:e.skipPermissions,reason:e.reason}),r.subtasks=r.subtasks||{},r.subtasks[e.id]={...r.subtasks[e.id]||{},...ef(e)},await ae())}async function r5(e,t=e.finishedAt){let n=await te(),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),Ee(r,"subtask-merged",{from:e.box,fromAgent:e.agent,task:e.task,subtaskId:e.id,stat:e.stat,conflicts:e.conflicts.length,files:e.files,durationMs:t?t-e.startedAt:null}),await ae()}async function o5(e){let t=await te(),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"),Ee(n,r?"subtask-canceled":"subtask-failed",{from:e.box,fromAgent:e.agent,task:e.task,subtaskId:e.id,failedStep:e.failedStep||e.step,error:e.error,durationMs:e.finishedAt?e.finishedAt-e.startedAt:null}),n.subtasks=n.subtasks||{},n.subtasks[e.id]={...n.subtasks[e.id]||{},...ef(e)},await ae()}async function s5(e,t,n){let r=`.bostrat/handoff/subtask-${e.id}.md`;try{let o=z.join(t.cwd,r);await Y.mkdir(z.dirname(o),{recursive:!0}),await Y.copyFile(n,o)}catch{}return r}async function Yy(e){if(e.dir&&await ku(e.dir).catch(()=>{}),e.box&&await Lt(e.box).catch(()=>{}),e.repos&&e.dir){for(let t of e.repos)await $t(t.leadDir,z.join(e.dir,t.name)).catch(()=>{});await Y.rm(e.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(e.dir){let n=(await Se(e.leadBox).catch(()=>null))?.cwd;n&&await $t(n,e.dir).catch(()=>{})}}async function a5(e,t){let n=(t.startedAt||Date.now())+Jq;for(;;){if(t.canceled||t.forceMerge)return;try{await Y.access(e);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await rr(Yq)}}function rr(e){return new Promise(t=>setTimeout(t,e))}var Bp,nr,E$,DW,al,BW,Lp,ml,Eo,Io,uy,LW,Op,il,jW,FW,Ht,Jg,fe,Yg,Ee,Xg,Zg,WW,jr,y$,KW,VW,zW,JW,b$,rl,QW,eq,sy,N$,oq,Os,P$,k$,dq,pq,fq,mq,hq,Za,cl,bq,xq,vq,Tq,Ey,iy,gl,St,On,Cp,ll,U$,Oq,ol,sl,Mq,Qg,Pq,dl,Dq,Bq,v$,ey,Fq,ty,cy,Fy,Cs,ai,Hy,Mn,Pp,Wy,Kq,Rp,Jq,Yq,ti,Ya,Wt,Xq,Bs,ef,yl,tf,fl,Ir=G(()=>{Li();pt();Ch();lA();rs();zr();Uu();gA();xt();bn();xd();kA();Js();Fu();pd();Qo();yh();kh();Jm();xA();uc();Qm();Hu();ch();vn();Sd();Gr();an();cc();Id();KA();ow();f_();od();nd();h_();Vw();be();Yw();np();wp();xa();ws();Jc();Ka();vg();Ap();$g();Gn();lp();YI();Ng();o$();Eu();c$();Bg();qg();Kg();zg();Bp=PW(NW),nr=process.env.BOSTRAT_STATE_DIR||z.join(process.env.HOME||"/tmp",".bostrat"),E$=z.join(nr,"tracks.json"),DW=z.join(nr,"events.jsonl"),al=z.join(nr,"workspaces"),BW=process.env.BOSTRAT_DEFAULT_REPO||null,Lp={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"}},ml=Object.keys(Lp);Eo={at:0,value:null};Io=".bostrat/handoff/latest.md",uy=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;LW=/\(Read \S{1,300} for handoff context from the prior lead — you are now the lead; none of the prior chat is in your context\. Then respond to the user above\.\)$/;Op=".bostrat/handoff/subtask-result.md",il=".bostrat/handoff/lead-context.md",jW=(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 ${il} \u2014 read that first for context. Your scoped task:
|
|
277
277
|
|
|
278
278
|
${e}
|
|
279
279
|
|
|
@@ -281,7 +281,7 @@ Work ONLY inside this directory. cd into the repo a change belongs to before run
|
|
|
281
281
|
|
|
282
282
|
${e}
|
|
283
283
|
|
|
284
|
-
Work ONLY inside this directory. When you are finished: (1) make your code changes, (2) commit them with git and a clear message, (3) LAST, write a short markdown digest to ${Op} with sections \u2014 Result (what you did), Files (paths you changed), Notes (anything the lead must know). Writing that file is your DONE signal: do it last, then stop and take no further action.`,
|
|
284
|
+
Work ONLY inside this directory. When you are finished: (1) make your code changes, (2) commit them with git and a clear message, (3) LAST, write a short markdown digest to ${Op} with sections \u2014 Result (what you did), Files (paths you changed), Notes (anything the lead must know). Writing that file is your DONE signal: do it last, then stop and take no further action.`,FW=(e,t,n,r)=>`A subtask box you delegated has finished: "${e}". Its changes are now merged into your working tree (${t.files} file${t.files===1?"":"s"}, +${t.insertions} \u2212${t.deletions}). Read ${r} for exactly what it did. `+(n.length?`IMPORTANT: ${n.length} merge conflict${n.length===1?"":"s"} need resolving \u2014 fix the conflict markers in: ${n.join(", ")}, then continue.`:"Review the merged changes and continue the work."),Ht=null,Jg=null;fe=(e,t)=>e.tracks.find(n=>n.id===t)||null,Yg=Promise.resolve();Ee=(e,t,n={})=>{let r={at:Date.now(),type:t,...n};return e.events.push(r),UW(e.id,r),JA(e,r),r};Bx(async({sessionName:e,from:t,to:n,version:r,status:o,error:s})=>{let i=(await te()).tracks.find(c=>c.leadBox===e||(c.boxes||[]).some(l=>l.name===e));i&&(Ee(i,"codex-update",{box:e,from:t,to:r||n,status:o,...s?{error:s}:{}}),await ae().catch(()=>{}))});Xg=z.join(nr,"plane-link","update-notice.json");Zg=(e,t,n)=>t!=null&&n.some(r=>e===r.box&&t>=r.from&&t<=r.to),WW=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);jr=new Set(["merged","conflict","canceled","failed","handed-off"]),y$=new Map;KW=5e3;VW={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};zW=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];JW=10*6e4;b$=400;rl=new Map,QW=Math.max(1e3,Number(process.env.BOSTRAT_ANSWERING_TTL_MS)||15e3),eq=[100,200,300],sy=new Map,N$=e=>CW("sha1").update(String(e||"")).digest("hex");oq=1500,Os=new Map;P$="Hello. I've just logged in.";k$="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.";dq=4e3,pq=3,fq=[300,350,400],mq=1e3,hq=12,Za=new Map,cl=new Map;bq=[250,400,500,600];xq=3e4,vq=[350,650,900];Tq=12e4;Ey=e=>({branch:e.branch,kind:e.kind,repos:(e.repos??[]).filter(Boolean).map(({name:t,base:n,baseRef:r,repo:o})=>({name:t,base:n,baseRef:r,repo:o}))});iy=new Map,gl=e=>iy.get(e)||(Ht?fe(Ht,e)?.reshape:null)||null,St=(e,t=409)=>Object.assign(new Error(e),{status:t});On=e=>Y.access(e).then(()=>!0,()=>!1),Cp=(e,t,n)=>Bp("git",["-C",e,"worktree","move","--end-of-options",t,n],{timeout:6e4});ll="# bostrat workspace",U$=`${ll}
|
|
285
285
|
|
|
286
286
|
TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
|
|
287
287
|
before substantive work \u2014 it is the shared cross-provider memory for this repo
|
|
@@ -681,16 +681,16 @@ for the same follow-up at the same moment twice; run bostrat-followup ls first
|
|
|
681
681
|
specifics (no PR numbers, URLs, SHAs, tags \u2014 the card supplies them), and HOLD
|
|
682
682
|
HERE after proposing until the operator answers the card. Bostrat never runs a
|
|
683
683
|
follow-up on its own; the operator's tap is the gate.
|
|
684
|
-
`,
|
|
685
|
-
`))
|
|
684
|
+
`,Oq=U$,ol=U$+Nv;sl=e=>typeof e=="string"&&(e===ll||e.startsWith(`${ll}
|
|
685
|
+
`)),Mq="# bostrat multi-repo workspace";Qg=ol.replace(`${ll}
|
|
686
686
|
`,`${ll}
|
|
687
687
|
|
|
688
688
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
689
689
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
690
690
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
691
691
|
When working anywhere else, ignore everything below.
|
|
692
|
-
`);Mq=new Set(["claude","codex","cursor"]);dl=new Set,Cq=e=>dl.has(e);Nq=9e4;k$={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};ey=new Map;Bq=6e4,ty=new Map;cy=120;Fy=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:Ho(e.hookToken),createdAt:Date.now()});Cs=new Map,ai=e=>Cs.get(e)||(Ht?fe(Ht,e)?.handoff:null)||null;Hy=6e4,Mn=(e,t,n=15e3)=>Bp("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),Pp=new Map,Wy=e=>Pp.get(e)||null;Wq=/^[A-Za-z0-9][A-Za-z0-9._/-]{0,200}$/;Rp=new Map;Kq=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,Vq=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,ti=z.join(nr,"worktrees"),Ya=[il,Op],Wt=new Map,zq=e=>Wt.get(e)||(Ht?Ht.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,Bs=e=>{let t=new Map([...Wt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Ht?fe(Ht,e):null;if(n?.subtasks)for(let[r,o]of Object.entries(n.subtasks))t.has(r)||t.set(r,o);return[...t.values()].sort((r,o)=>r.startedAt-o.startedAt)},ef=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});yl=e=>K$(e.trackId,e.id,ef(e));tf=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",fl=class extends Error{}});pt();import Re from"node:path";import Et from"node:fs";import Kz from"node:readline";import{fileURLToPath as Vz}from"node:url";import nn from"express";import FC from"node:http";import zz from"node:https";import Yl from"node:os";import{randomUUID as Jz}from"node:crypto";import{WebSocketServer as Yz}from"ws";pt();import TD from"node-pty";async function dx(e,t,n={}){if(!je(t)){Bu(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await Zr()){Bu(e,`host tmux server not running on ${zt}`),e.close(1011,"tmux server unavailable");return}if(!await Se(t)){let a=!1;if(typeof n.revive=="function"&&(Lu(e,{type:"notice",message:"Resuming the lead box\u2026"}),a=await n.revive().catch(()=>!1)),!a||!await Se(t)){Bu(e,`session "${t}" not found`),e.close(1008,"session not found");return}if(e.readyState!==e.OPEN)return}let r=ux(n.cols,120,px),o=ux(n.rows,32,fx),s;try{s=TD.spawn(oe,["-S",zt,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){Bu(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&Lu(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(Lu(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"&&$D(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 ED=6,_D=250;async function ID(e,t=ED,n=_D){for(let r=0;r<t;r++){if(await Yo(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var px=20,fx=6;function $D(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=px&&Number(t)>=fx}function ux(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function Lu(e,t){try{e.send(JSON.stringify(t))}catch{}}function Bu(e,t){Lu(e,{type:"error",message:t})}function mx(e){let t=typeof e=="string"&&e?e:"/",n=t.indexOf("?"),r=n===-1?t:t.slice(0,n),o=n===-1?"":t.slice(n+1),s=r.match(/^\/api\/attach\/([^/]+)$/);if(!s)return{ok:!1,status:404};let a;try{a=decodeURIComponent(s[1])}catch{return{ok:!1,status:400}}let i=new URLSearchParams(o);return{ok:!0,sessionName:a,cols:Number(i.get("cols"))||void 0,rows:Number(i.get("rows"))||void 0}}vn();Fu();Ch();Ir();import{createRequire as Xz}from"node:module";var o5=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function Xy({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(o5)&&s.push(`merge-back:${a.id}`),(o(a.id)??[]).length&&s.push(`pr:${a.id}`));return t.some(a=>a?.state==="live"||a?.state==="starting")&&s.push("views"),{busy:s.length>0,reasons:s}}qg();be();Jc();Gn();sp();an();var rf=60*6e4,s5=1440*6e4,a5=new Set(["merged","closed","lost"]),bl=(e,t,n)=>Number.isFinite(Number(e))&&n-Number(e)<=t;function i5({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)<=pg&&s.push("ship"),r>0&&s.push("create-net");for(let a of n||[]){if(!a||a5.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&&bl(a.ciFix.delegatedAt,rf,o)&&s.push(`ci-fix#${l}`),a.reconcile&&bl(a.reconcile.delegatedAt,rf,o)&&s.push(`reconcile#${l}`),a.ciFixAwait&&bl(a.ciFixAwait.since,rf,o)&&s.push(`ci-fix-await#${l}`),a.reconcileAwait&&bl(a.reconcileAwait.since,rf,o)&&s.push(`reconcile-await#${l}`);let u=a.autoMerge?.armedAt??a.armedAt??null;a.trackId===e&&(a.autoMerge||a.armedAt)&&bl(u,s5,o)&&s.push(`auto-merge#${l}`)}return s}var of=new Map;Ze.on("event",e=>{e?.trackId&&of.delete(e.trackId)});async function c5(e){if(of.has(e.id))return of.get(e.id);let t=await K(e).catch(()=>[]),n=Wa(t,-1/0);return of.set(e.id,n),n}async function Z$(e,{now:t=Date.now()}={}){let n=new Map,r=await En().catch(()=>[]);for(let o of e||[]){if(!o||Ue(o))continue;let s=i5({trackId:o.id,shipPending:await c5(o),links:r,observers:OE(o.id),now:t});s.length&&n.set(o.id,s)}return n}Bg();import uR from"node:fs";import Ro from"node:fs/promises";import _5 from"node:os";import kt from"node:path";import{spawn as I5}from"node:child_process";import{createRequire as $5}from"node:module";be();Id();import ii from"node:fs";import xl from"node:fs/promises";import d5 from"node:http";import Ot from"node:path";import af from"node:os";import{createRequire as p5}from"node:module";import Qy from"ws";import l5 from"node:path";import u5 from"node:os";var kl=process.env.BOSTRAT_STATE_DIR||l5.join(process.env.HOME||u5.tmpdir(),".bostrat");function Zy(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 Q$(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=Zy(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function eR(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}am();var Cn=(()=>{try{return p5(import.meta.url)("../package.json").version||null}catch{return null}})(),tb=process.env.BOSTRAT_STATE_DIR||Ot.join(process.env.HOME||af.tmpdir(),".bostrat"),iR=Ot.join(tb,"plane-link","removed.json");function tR(e){return typeof e!="string"||e.length<8?"\u2022\u2022\u2022\u2022":`${e.slice(0,6)}\u2026${e.slice(-4)}`}function f5(e=iR){try{let t=JSON.parse(ii.readFileSync(e,"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function Ls({env:e=process.env,pkgDir:t=Ot.resolve(Ot.dirname(new URL(import.meta.url).pathname),".."),exists:n=ii.existsSync}={}){let r=String(e.BOSTRAT_INSTALL_KIND||"").trim();return["container","npm-global","git-checkout","dev"].includes(r)?r:/\/lib\/node_modules\/bostrat-node(?:\/|$)/.test(t.replace(/\\/g,"/"))?"npm-global":n(Ot.join(t,"..",".git"))?"git-checkout":"dev"}function vl(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 sf(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 m5({home:e=process.env.HOME||af.homedir(),env:t=process.env}={}){let n=r=>{try{return ii.statSync(r),!0}catch{return!1}};return{claude:n(Ot.join(e,".claude",".credentials.json")),codex:n(Ot.join(t.CODEX_HOME||Ot.join(e,".codex"),"auth.json")),cursor:n(Ot.join(t.XDG_CONFIG_HOME||Ot.join(e,".config"),"cursor","auth.json"))}}var mt=1,nR=1e3,rR=3e4,h5=6e4,w5=1500,g5=500,$o=100,y5=2e3,b5=25e3,k5=2.5,x5=6e4,cR=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},v5=cR(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),S5=3e4,oR=10*6e4,A5=400,sR=250,T5=cR(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),eb=4,E5=6e4,aR=6e4;function lR({url:e,token:t,nodeId:n=af.hostname(),name:r=af.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,deliverySample:c=null,onHashKey:l=null,onViewBase:u=null,metricsMs:d=E5,onUpdateAvailable:p=null,onUpdatePolicy:f=null,onHelloRefused:w=null,updatePolicy:h=null,installKind:y=Ls(),selfUrl:k=`http://127.0.0.1:${process.env.PORT||3362}`,apiToken:g=null,removedMarkerFile:x=iR,onRemoved:A=null,onDegraded:I=null,onRecovered:M=null,onConnected:P=null,ingestMode:L=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:T=v5,unackedRetryMs:E=x5,pingMs:j=b5,deadLinkMs:G=Math.round(j*k5),maxInflightEvents:R=A5,httpTimeoutMs:S=T5,stallBackoffMinMs:C=S5,fetchImpl:H=globalThis.fetch,log:B=console}){if(!e||!t)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");e=eR(e);let Z=k.replace(/^http/,"ws"),xe=new Set,Ae=new Map,_e=new Map,Q=Ot.join(tb,"plane-link","cursor.json"),Xe=Ot.join(tb,"plane-link","dead-letter.ndjson"),Pe=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),le={},ge={},de=new Map,ee=null,we=null,Oe=0,Ve=0,Nt=0,O=Promise.resolve(),X="ws",ne=0,he=!1,Be=0,Pt=!1,tt=$o,wn=new Set,Kt=[],ht=null,wt=null,at=!1,Si=!1,gt=nR,cr=null,Hr=null,lr=null,gn=null,ur=0,b=0,m=null,v=!1,D=0,J=!1,ie=f5(x);if(ie)if(ie.tokenPreview===tR(t))J=!0,sm({reason:ie.reason??"removed",at:ie.at??null,nodeId:ie.nodeId??n}),B.error(`[plane-link] this node was REMOVED from the plane (${ie.reason??"removed"}${ie.at?` at ${new Date(ie.at).toISOString()}`:""}) \u2014 not dialing. Re-provision it with a new node token, or delete ${x} to force a dial.`);else try{ii.unlinkSync(x),B.log("[plane-link] stale removed-marker discarded \u2014 the node token changed")}catch{}function ve({reason:F="removed",at:W=Date.now(),via:U="ctl"}={}){if(!J){J=!0,sm({reason:F,at:W,nodeId:n,via:U});try{ii.mkdirSync(Ot.dirname(x),{recursive:!0}),ii.writeFileSync(x,JSON.stringify({at:W,reason:F,via:U,nodeId:n,tokenPreview:tR(t)},null,2))}catch($){B.error(`[plane-link] could not write ${x}: ${$?.message||$}`)}B.error(`[plane-link] the plane REMOVED this node (${F}, via ${U}) \u2014 going dormant: no more dials, ingest or broker calls. Re-provision with a new node token to reconnect; the box and its data are untouched.`);try{A?.({reason:F,at:W,via:U})}catch{}jk({bye:!1})}}async function _t(){try{le=JSON.parse(await xl.readFile(Q,"utf8"))}catch{le={}}}function yt(){v=!0,!lr&&(lr=setTimeout(async()=>{if(lr=null,!!v){v=!1;try{await xl.mkdir(Ot.dirname(Q),{recursive:!0}),await xl.writeFile(Q,JSON.stringify(le))}catch(F){B.error("[plane-link] cursor flush failed:",F?.message||F)}}},y5),lr.unref?.())}function nt(F){return!wt||wt.readyState!==Qy.OPEN?!1:(wt.send(JSON.stringify(F)),!0)}function rn(F){let W=Date.now();for(let{trackId:U,event:$}of F){let V=de.get($.id);de.set($.id,{trackId:U,seq:$.seq,event:$,sentAt:W,firstSentAt:V?.firstSentAt??W,retryAt:W+E,tries:(V?.tries??0)+1,nacks:V?.nacks??0})}return X==="http"?(sN(F),!0):nt({v:mt,ch:"events",type:"append",events:F})}function tN(F){let W=null;for(let U of de.values())U.trackId===F&&(W==null||U.seq<W)&&(W=U.seq);return W}function zf(F){let W=ge[F];if(W==null)return;let U=tN(F),$=U==null?W:Math.min(W,U-1);$>(le[F]??0)&&(le[F]=$,yt())}function Ai(){if(ee=Date.now(),we!=null){let F=Date.now()-we;B.log(`[plane-link] spine sync recovered after ${Math.round(F/1e3)}s degraded (${Oe} stall redial(s))`);let W=we;we=null,Oe=0,Ve=0,he=!1,Be=0;try{M?.({since:W,downMs:F})}catch(U){B.error(`[plane-link] onRecovered failed: ${U?.message||U}`)}}}function Jf(F){if(!Array.isArray(F)||!F.length)return;let W=new Set;for(let U of F){let $=de.get(U);$&&(de.delete(U),(ge[$.trackId]??0)<$.seq&&(ge[$.trackId]=$.seq),W.add($.trackId))}for(let U of W)zf(U);Ai()}function Ok(F,W){Nt++;let U={at:Date.now(),trackId:F.trackId,seq:F.seq,id:F.event?.id??null,type:F.event?.type??null,reason:W,event:F.event??null};B.error(`[plane-link] DEAD-LETTER ${F.trackId} seq ${F.seq} (${U.type??"?"}): plane rejected it permanently (${W}) \u2014 kept in ${Xe}`),O=O.then(async()=>{await xl.mkdir(Ot.dirname(Xe),{recursive:!0}),await xl.appendFile(Xe,JSON.stringify(U)+`
|
|
693
|
-
`)}).catch($=>B.error(`[plane-link] dead-letter write failed: ${$?.message||$}`))}function Mk(F,W,U,$=null){if(!Array.isArray(F)||!F.length)return;let V=new Set,Ie=Date.now();for(let ye of F){let Le=de.get(ye);Le&&(U?(de.delete(ye),Ok(Le,W),(ge[Le.trackId]??0)<Le.seq&&(ge[Le.trackId]=Le.seq),V.add(Le.trackId)):(Le.nacks++,Le.retryAt=Ie+Math.max(1e3,Number($)||E)))}for(let ye of V)zf(ye);Ai()}function Ck(F=Date.now()){let W=1/0;for(let U of de.values())U.firstSentAt<W&&(W=U.firstSentAt);return W===1/0?0:F-W}function nN(F){if(!G||!F||F!==wt||!ur)return!1;let W=Date.now()-ur;if(W<=G)return!1;b++,B.error(`[plane-link] no frame from the plane for ${Math.round(W/1e3)}s \u2014 link is half-open; terminating and redialing`);try{F.terminate()}catch{}return!0}function Nk(){if(!at||!de.size)return;let F=Date.now(),W=Ck(F);if(W<T||ee!=null&&F-ee<T||X==="http"&&he||Ve>F)return;Oe++;let U=Math.min(C*2**(Oe-1),oR);if(we==null){we=F-W,B.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(W/1e3)}s with ${de.size} event(s) in flight (${X} door) \u2014 the plane is not answering appends`);try{I?.({since:we,unacked:de.size,oldestAgeMs:W,mode:X})}catch($){B.error(`[plane-link] onDegraded failed: ${$?.message||$}`)}}if(X==="http"){he=!0,Be=F+U,B.error(`[plane-link] ingest circuit OPEN (stall #${Oe}); probing /v1/ingest/health in ${Math.round(U/1e3)}s`);return}Ve=F+U,B.error(`[plane-link] closing the link and redialing in ${Math.round(U/1e3)}s (stall redial #${Oe})`);try{wt?.terminate()}catch{}}function Yf(){if(!at)return;let F=Date.now();if(X==="http"&&he)return;let W=[...de.values()].filter($=>$.event&&$.retryAt<=F&&!(X==="http"&&wn.has($.trackId)));if(!W.length)return;W.sort(($,V)=>$.trackId===V.trackId?$.seq-V.seq:$.trackId<V.trackId?-1:1),B.log(`[plane-link] re-sending ${W.length} unacked event(s) the plane never confirmed`);let U=X==="http"?tt:$o;if(X==="http"){let $=new Map;for(let V of W)($.get(V.trackId)??$.set(V.trackId,[]).get(V.trackId)).push(V);for(let[,V]of $){if(ne>=eb)return;rn(V.slice(0,U).map(Ie=>({trackId:Ie.trackId,event:Ie.event})))}return}for(let $=0;$<W.length;$+=U){let V=W.slice($,$+U).map(Ie=>({trackId:Ie.trackId,event:Ie.event}));if(!rn(V))return}}async function rN(F=null){for(;;){if(!at)return!1;let W=de.size<R,U=X!=="http"||ne<eb&&!he&&(!F||!wn.has(F));if(W&&U)return!0;await new Promise($=>setTimeout($,100))}}function Xf(){if(ht=null,!at||!Kt.length)return;let F=Kt;if(Kt=[],X!=="http"){for(let U=0;U<F.length;U+=$o)rn(F.slice(U,U+$o));return}let W=new Map;for(let U of F)(W.get(U.trackId)??W.set(U.trackId,[]).get(U.trackId)).push(U);for(let[U,$]of W){if(wn.has(U)||ne>=eb||he){Kt.push(...$);continue}for(let V=0;V<$.length;V+=tt)rn($.slice(V,V+tt))}Kt.length&&!ht&&(ht=setTimeout(Xf,sR),ht.unref?.())}let oN=F=>{let W=new Map;for(let{trackId:U,event:$}of F)(W.get(U)??W.set(U,[]).get(U)).push($);return[...W].map(([U,$])=>({trackId:U,events:$}))},Uo=(F,W,U,{progress:$})=>{let V=Date.now();for(let{event:Ie}of F){let ye=de.get(Ie.id);ye&&(ye.nacks++,ye.retryAt=V+Math.max(1e3,U||E))}$&&Ai()};async function sN(F){let W=new Set(F.map(U=>U.trackId));for(let U of W)wn.add(U);ne++;try{let U=await H(`${Pe}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:oN(F)}),signal:AbortSignal.timeout(S)}),$=(Number(U.headers.get("retry-after"))||0)*1e3;if(U.status===200){let V=await U.json();tt=$o;for(let Ie of V.results??[]){Jf(Ie.acked??[]);let ye=(Le,ut)=>{let on=new Map;for(let it of Le??[])(on.get(it.reason)??on.set(it.reason,[]).get(it.reason)).push(it.id);for(let[it,Fk]of on)Mk(Fk,it,ut,V.retryAfterMs??null)};ye(Ie.rejected,!0),ye(Ie.deferred,!1)}Jf(V.discarded??[]),Ai();return}if(U.status===429){Uo(F,"overloaded",$||5e3,{progress:!0});return}if(U.status===413&&F.length>1){tt=Math.max(1,Math.floor(F.length/2)),B.error(`[plane-link] ingest request too large (${F.length} events) \u2014 halving batches to ${tt}`),Uo(F,"too_large",1e3,{progress:!0});return}if(U.status===413){let V=de.get(F[0].event.id);V&&(de.delete(V.event.id),Ok(V,"too_large"),(ge[V.trackId]??0)<V.seq&&(ge[V.trackId]=V.seq),zf(V.trackId)),Ai();return}if(U.status===410){ve({reason:"removed",via:"410"}),Uo(F,"http_410",6e4,{progress:!1});return}if(U.status===401||U.status===403){let{tripped:V,holdMs:Ie}=Qs();if(V&&B.error(`[plane-link] holding every plane call for ${Math.round(Ie/6e4)} min \u2014 the ingest door refused this node's token three times in a row`),Date.now()-D>=aR){D=Date.now();let ye="";try{ye=(await U.json())?.error??""}catch{}B.error(`[plane-link] plane refused ingest (HTTP ${U.status}${ye?`: ${ye}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}Uo(F,`http_${U.status}`,Math.max(6e4,Oi()),{progress:!1});return}if(U.status===400){B.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${F.length} event(s) for retry; this is a bug, not data`),Uo(F,"http_400",6e4,{progress:!1});return}Uo(F,`http_${U.status}`,$||5e3,{progress:!1})}catch(U){let $=U?.name==="TimeoutError"||U?.name==="AbortError";$&&F.length>1&&(tt=Math.max(1,Math.floor(F.length/2)),B.error(`[plane-link] ingest request of ${F.length} event(s) outlived its ${S} ms deadline \u2014 halving batches to ${tt}`)),Uo(F,$?"timeout":"network",5e3,{progress:!1})}finally{ne--;for(let U of W)wn.delete(U)}}async function aN(){if(X!=="http"||!he||!at||Pt)return;let F=Date.now();if(!(Be>F||ea(F))){Pt=!0;try{let W=await H(`${Pe}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(S)});if(!W.ok)throw new Error(`HTTP ${W.status}`);B.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),he=!1,Be=0,Ve=Date.now()+S+E+1e3;for(let U of de.values())U.retryAt=Math.min(U.retryAt,Date.now());Yf()}catch(W){Oe++;let U=Math.min(C*2**(Oe-1),oR);Be=Date.now()+U,B.error(`[plane-link] ingest door still not answering (${W?.message||W}); next probe in ${Math.round(U/1e3)}s`)}finally{Pt=!1}}}function Pk(){let F=Date.now();return{mode:X,unacked:de.size,oldestUnackedAgeMs:Ck(F),lastProgressAt:ee,degradedSince:we,stallRedials:Oe,deadLettered:Nt,http:X==="http"?{inFlight:ne,circuitOpen:he,nextProbeInMs:he?Math.max(0,Be-F):0,batchCap:tt}:null}}async function Dk(){if(!i)return;let F;try{F=await i()}catch(U){B.error("[plane-link] metrics sample failed:",U?.message||U);return}let W=null;try{W=c?.()??null}catch{}F&&nt({v:mt,ch:"ctl",type:"metrics",metrics:{...F,planeLink:Pk(),...W?{delivery:W}:{}}})}function iN(){i&&(m&&clearInterval(m),Dk(),m=setInterval(Dk,d),m.unref?.())}async function cN(){let F;try{F=await o()}catch(W){B.error("[plane-link] backfill listTracks failed:",W?.message||W);return}nt({v:mt,ch:"ctl",type:"tracks",ids:F.filter(W=>!W.transferredTo).map(W=>W.id).filter(W=>typeof W=="string")});for(let W of F){if(!at)return;let U;try{U=await K(W)}catch{continue}let $=le[W.id]??0,V=U.filter(ye=>ye.seq>$&&typeof ye.id=="string"),Ie=X==="http"?tt:$o;for(let ye=0;ye<V.length;ye+=Ie){if(!await rN(W.id))return;let Le=V.slice(ye,ye+Math.min(Ie,X==="http"?tt:$o)).map(ut=>({trackId:W.id,event:ut}));if(!rn(Le))return}}}function Bk(F){!at||!F?.trackId||typeof F.id!="string"||(Kt.push({trackId:F.trackId,event:F}),ht||(ht=setTimeout(Xf,sR),ht.unref?.()))}let Ti=new Map;async function lN(F){let{reqId:W,method:U,path:$,headers:V={},body:Ie}=F,ye=Object.fromEntries(Object.entries(V).filter(([,ut])=>ut!=null)),Le=new AbortController;Ti.set(W,Le);try{if(F.host){await uN(F,ye,Le);return}g&&(ye["x-bostrat-token"]=g);let ut=await fetch(k+$,{method:U,headers:ye,body:Ie?Buffer.from(Ie,"base64"):void 0,signal:Le.signal}),on=Object.fromEntries(ut.headers);for(let it of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete on[it];if(on["content-type"]||(on["content-type"]="application/octet-stream"),nt({v:mt,ch:"control",type:"http_head",reqId:W,status:ut.status,headers:on}),ut.body){for await(let it of ut.body)if(!nt({v:mt,ch:"control",type:"http_data",reqId:W,data:Buffer.from(it).toString("base64")}))break}nt({v:mt,ch:"control",type:"http_end",reqId:W,error:null})}finally{Ti.delete(W)}}function uN(F,W,U){let{reqId:$,method:V,path:Ie,body:ye}=F,Le=new URL(k);return new Promise(ut=>{let on=d5.request({host:Le.hostname,port:Le.port,method:V,path:Ie,headers:{...W,host:F.host},signal:U.signal},it=>{let{connection:Fk,"keep-alive":w3,"transfer-encoding":g3,upgrade:y3,...dN}=it.headers;nt({v:mt,ch:"control",type:"http_head",reqId:$,status:it.statusCode||502,headers:dN}),it.on("data",pN=>{nt({v:mt,ch:"control",type:"http_data",reqId:$,data:Buffer.from(pN).toString("base64")})||it.destroy()}),it.on("end",()=>{nt({v:mt,ch:"control",type:"http_end",reqId:$,error:null}),ut()}),it.on("error",()=>{nt({v:mt,ch:"control",type:"http_end",reqId:$,error:"view upstream error"}),ut()})});on.on("error",it=>{nt({v:mt,ch:"control",type:"http_end",reqId:$,error:it?.message||"view request failed"}),ut()}),ye&&on.write(Buffer.from(ye,"base64")),on.end()})}function Lk(){if(Si||J)return;wt=new Qy(e,{headers:{authorization:`Bearer ${t}`}});let F=wt;ur=0,F.on("ping",()=>{ur=Date.now()}),F.on("pong",()=>{ur=Date.now()}),wt.on("open",async()=>{let U=null;try{U=await s?.()||null}catch{U=null}nt({v:mt,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:Cn,installKind:y,...typeof h=="function"?{updatePolicy:h()}:h?{updatePolicy:h}:{},providers:m5(),...U?{agents:U}:{}})}),wt.on("message",U=>{ur=Date.now();let $;try{$=JSON.parse(U)}catch{return}if($.ch==="ctl"&&$.type==="hello_ok"){at=!0,gt=nR,cr=null,zk(),B.log(`[plane-link] connected to plane as ${n}`);try{P?.()}catch{}if(typeof $.hashKey=="string"&&$.hashKey)try{l?.($.hashKey)}catch{}try{u?.(typeof $.viewBase=="string"&&$.viewBase?$.viewBase:null)}catch{}$.minNodeVersion&&Cn&&vl(Cn,$.minNodeVersion)&&B.error(`[plane-link] this node is bostrat-node ${Cn}; the plane asks for \u2265${$.minNodeVersion} \u2014 update with: ${sf(y)}`);try{f?.(typeof $.updatePolicy=="string"?$.updatePolicy:null)}catch{}let V=typeof $.latestNodeVersion=="string"?$.latestNodeVersion:null;if(V&&Cn&&vl(Cn,V)&&!xe.has(V)){xe.add(V),B.log(`[plane-link] bostrat-node ${V} is available (this node runs ${Cn}) \u2014 ${sf(y)}`);try{p?.({current:Cn,latest:V,min:$.minNodeVersion??null,installKind:y,required:!!($.minNodeVersion&&vl(Cn,$.minNodeVersion))})}catch(ye){B.error(`[plane-link] update notice failed: ${ye?.message||ye}`)}}let Ie=$.ingest==="http";X=L==="ws"?"ws":L==="http"||Ie?"http":"ws",L==="http"&&!Ie&&B.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),X==="http"&&B.log("[plane-link] spine rides the HTTPS ingest door"),he=!1,Be=0,tt=$o,gn&&clearInterval(gn),ur=Date.now(),gn=setInterval(()=>{nN(F)||(nt({v:mt,ch:"ctl",type:"ping"}),Nk(),aN().catch(()=>{}),Yf(),Kt.length&&!ht&&Xf())},j),gn.unref?.(),iN(),cN().catch(ye=>B.error("[plane-link] backfill failed:",ye?.message||ye));return}if($.ch==="ctl"&&$.type==="hello_refused"){let V=typeof $.minNodeVersion=="string"?$.minNodeVersion:null,Ie=typeof $.latestNodeVersion=="string"?$.latestNodeVersion:null;B.error(`[plane-link] plane REFUSED this node: bostrat-node ${Cn??"?"} is below its minimum ${V??"?"} \u2014 ${$.fix||sf(y)}`),gt=rR,cr=0;try{w?.({current:Cn,min:V,latest:Ie,fix:$.fix||sf(y),installKind:y})}catch(ye){B.error(`[plane-link] refusal handler failed: ${ye?.message||ye}`)}return}if($.ch==="ctl"&&$.type==="removed"){nt({v:mt,ch:"ctl",type:"removed_ack",nodeId:n}),ve({reason:typeof $.reason=="string"?$.reason:"removed",at:Number($.at)||Date.now(),via:"ctl"});return}if($.ch==="ctl"&&$.type==="update_policy"){try{f?.(typeof $.mode=="string"?$.mode:null)}catch{}return}if($.ch==="ctl"&&$.type==="usage_caps"){_d($.caps??{},{source:"plane",updatedAt:$.updatedAt??Date.now()}).then(V=>B.log(`[plane-link] spend caps ${Object.keys(V.caps).length?`updated (${Object.keys(V.caps).join(", ")})`:"cleared"} from plane`)).catch(V=>B.error("[plane-link] spend-caps push rejected:",V?.message||V));return}if($.ch==="ctl"&&$.type==="transferred"&&Array.isArray($.tracks)){Promise.resolve().then(()=>a?.($.tracks)).catch(V=>B.error("[plane-link] transfer notice failed:",V?.message||V));return}if($.ch==="events"&&$.type==="ack"&&Array.isArray($.ids)){Jf($.ids);return}if($.ch==="events"&&$.type==="nack"&&Array.isArray($.ids)){Mk($.ids,$.reason,$.permanent===!0,$.retryAfterMs??null);return}if($.ch==="control"&&$.type==="http_open"){lN($).catch(V=>nt({v:mt,ch:"control",type:"http_end",reqId:$.reqId,error:V?.message||"tunnel error"}));return}if($.ch==="control"&&$.type==="http_cancel"){Ti.get($.reqId)?.abort();return}if($.ch==="attach"&&$.type==="open"){let V=new Qy(Z+$.path,Array.isArray($.protocols)?$.protocols:[],$.host?{headers:{host:$.host}}:void 0);Ae.set($.streamId,V),_e.set($.streamId,[]),V.on("open",()=>{let ye=_e.get($.streamId)||[];_e.delete($.streamId);for(let{buf:Le,bin:ut}of ye)V.send(Le,{binary:ut})}),V.on("message",(ye,Le)=>{let ut=Le?ye:Buffer.from(String(ye));nt({v:mt,ch:"attach",type:"data",streamId:$.streamId,data:ut.toString("base64"),...Le?{bin:!0}:{}})});let Ie=()=>{_e.delete($.streamId),Ae.delete($.streamId)&&nt({v:mt,ch:"attach",type:"close",streamId:$.streamId})};V.on("close",Ie),V.on("error",Ie);return}if($.ch==="attach"&&$.type==="data"){let V=Ae.get($.streamId);if(!V)return;let Ie=Buffer.from($.data,"base64"),ye=$.bin===!0,Le=_e.get($.streamId);Le?Le.push({buf:Ie,bin:ye}):V.send(Ie,{binary:ye});return}if($.ch==="attach"&&$.type==="close"){let V=Ae.get($.streamId);Ae.delete($.streamId),_e.delete($.streamId),V?.close();return}});let W=()=>{at&&B.log("[plane-link] disconnected from plane"),at=!1,gn&&(clearInterval(gn),gn=null),m&&(clearInterval(m),m=null),de.clear(),ge={},Kt=[],ht&&(clearTimeout(ht),ht=null),wn.clear();for(let V of Ae.values())V.close();Ae.clear(),_e.clear();for(let V of Ti.values())V.abort();if(Ti.clear(),Si||Hr)return;cr==null&&(cr=Date.now());let U=Date.now()-cr<h5,$=Math.max(U?w5+Math.random()*g5:gt,Ve-Date.now(),Oi());Hr=setTimeout(()=>{Hr=null,Lk()},$),Hr.unref?.(),U||(gt=Math.min(gt*2,rR))};wt.on("close",W),wt.on("error",U=>{let $=/Unexpected server response: (\d+)/.exec(U?.message||"")?.[1];if($==="410"){ve({reason:"removed",via:"410"});return}if($==="401"||$==="403"){let{tripped:V,holdMs:Ie}=Qs();V?B.error(`[plane-link] holding every plane call for ${Math.round(Ie/6e4)} min \u2014 three credential refusals in a row (HTTP ${$}) mean the token is revoked or unknown, not that the plane is down; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN. Next dial in ${Math.round(Ie/6e4)} min.`):Date.now()-D>=aR&&(D=Date.now(),B.error(`[plane-link] plane refused this node's credentials (HTTP ${$}) \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)`))}W()})}Ze.on("event",Bk),_t().then(Lk);function jk({bye:F=!0}={}){Si=!0,Ze.off("event",Bk),Hr&&clearTimeout(Hr),lr&&clearTimeout(lr),gn&&clearInterval(gn),m&&clearInterval(m),ht&&clearTimeout(ht),F&&nt({v:mt,ch:"ctl",type:"bye",reason:"shutdown"}),wt?.close()}return{stop:()=>jk(),status:()=>({connected:at,nodeId:n,cursor:{...le},inflight:de.size,...Pk(),auth:Jk(),dormant:J,removed:lu(),deadLinkMs:G,deadLinkRedials:b}),_tick:()=>{Nk(),Yf()}}}Uu();var cf=(()=>{try{return $5(import.meta.url)("../package.json").version||null}catch{return null}})(),R5=process.env.BOSTRAT_STATE_DIR||kt.join(process.env.HOME||_5.tmpdir(),".bostrat"),dR=kt.join(R5,"self-update"),lf=kt.join(dR,"status.json"),O5=1440*6e4,M5=6e4,Sl=new Set(["npm-global","git-checkout"]);function pR({kind:e=Ls(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:Sl.has(e)?e==="git-checkout"||C5(t)?"auto":"prompt":"off"}function C5(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var Qt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function N5({kind:e,to:t,from:n=cf,statusFile:r=lf,logFile:o=kt.join(dR,"last.log"),port:s=Number(process.env.PORT)||3362,npmPrefix:a=null,serviceName:i=process.platform==="darwin"?"com.bostrat.node":"bostrat-node.service",uid:c=typeof process.getuid=="function"?process.getuid():0,liveDir:l=process.env.BOSTRAT_LIVE_CHECKOUT||kt.resolve(kt.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!Sl.has(e))throw new Error(`self-update: install kind "${e}" is not actionable here`);if(!/^\d+\.\d+\.\d+$/.test(String(t)))throw new Error(`self-update: bad target version "${t}"`);if(n&&!/^\d+\.\d+\.\d+$/.test(String(n)))throw new Error(`self-update: bad current version "${n}"`);let d=(y,k="")=>`write_status ${Qt(y)} ${k}`,p=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${Qt(i)}`,f=`
|
|
692
|
+
`);Pq=new Set(["claude","codex","cursor"]);dl=new Set,Dq=e=>dl.has(e);Bq=9e4;v$={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};ey=new Map;Fq=6e4,ty=new Map;cy=120;Fy=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:Ho(e.hookToken),createdAt:Date.now()});Cs=new Map,ai=e=>Cs.get(e)||(Ht?fe(Ht,e)?.handoff:null)||null;Hy=6e4,Mn=(e,t,n=15e3)=>Bp("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),Pp=new Map,Wy=e=>Pp.get(e)||null;Kq=/^[A-Za-z0-9][A-Za-z0-9._/-]{0,200}$/;Rp=new Map;Jq=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,Yq=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,ti=z.join(nr,"worktrees"),Ya=[il,Op],Wt=new Map,Xq=e=>Wt.get(e)||(Ht?Ht.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,Bs=e=>{let t=new Map([...Wt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Ht?fe(Ht,e):null;if(n?.subtasks)for(let[r,o]of Object.entries(n.subtasks))t.has(r)||t.set(r,o);return[...t.values()].sort((r,o)=>r.startedAt-o.startedAt)},ef=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});yl=e=>z$(e.trackId,e.id,ef(e));tf=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",fl=class extends Error{}});pt();import Re from"node:path";import Et from"node:fs";import Jz from"node:readline";import{fileURLToPath as Yz}from"node:url";import nn from"express";import HC from"node:http";import Xz from"node:https";import Yl from"node:os";import{randomUUID as Zz}from"node:crypto";import{WebSocketServer as Qz}from"ws";pt();import _D from"node-pty";async function dx(e,t,n={}){if(!je(t)){Bu(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await Zr()){Bu(e,`host tmux server not running on ${zt}`),e.close(1011,"tmux server unavailable");return}if(!await Se(t)){let a=!1;if(typeof n.revive=="function"&&(Lu(e,{type:"notice",message:"Resuming the lead box\u2026"}),a=await n.revive().catch(()=>!1)),!a||!await Se(t)){Bu(e,`session "${t}" not found`),e.close(1008,"session not found");return}if(e.readyState!==e.OPEN)return}let r=ux(n.cols,120,px),o=ux(n.rows,32,fx),s;try{s=_D.spawn(oe,["-S",zt,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){Bu(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&Lu(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(Lu(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"&&OD(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{}RD(t)})}var ID=6,$D=250;async function RD(e,t=ID,n=$D){for(let r=0;r<t;r++){if(await Yo(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var px=20,fx=6;function OD(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=px&&Number(t)>=fx}function ux(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function Lu(e,t){try{e.send(JSON.stringify(t))}catch{}}function Bu(e,t){Lu(e,{type:"error",message:t})}function mx(e){let t=typeof e=="string"&&e?e:"/",n=t.indexOf("?"),r=n===-1?t:t.slice(0,n),o=n===-1?"":t.slice(n+1),s=r.match(/^\/api\/attach\/([^/]+)$/);if(!s)return{ok:!1,status:404};let a;try{a=decodeURIComponent(s[1])}catch{return{ok:!1,status:400}}let i=new URLSearchParams(o);return{ok:!0,sessionName:a,cols:Number(i.get("cols"))||void 0,rows:Number(i.get("rows"))||void 0}}vn();Fu();Ch();Ir();import{createRequire as e3}from"node:module";var i5=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function Xy({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(i5)&&s.push(`merge-back:${a.id}`),(o(a.id)??[]).length&&s.push(`pr:${a.id}`));return t.some(a=>a?.state==="live"||a?.state==="starting")&&s.push("views"),{busy:s.length>0,reasons:s}}qg();be();Jc();Gn();sp();an();var rf=60*6e4,c5=1440*6e4,l5=new Set(["merged","closed","lost"]),bl=(e,t,n)=>Number.isFinite(Number(e))&&n-Number(e)<=t;function u5({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)<=pg&&s.push("ship"),r>0&&s.push("create-net");for(let a of n||[]){if(!a||l5.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&&bl(a.ciFix.delegatedAt,rf,o)&&s.push(`ci-fix#${l}`),a.reconcile&&bl(a.reconcile.delegatedAt,rf,o)&&s.push(`reconcile#${l}`),a.ciFixAwait&&bl(a.ciFixAwait.since,rf,o)&&s.push(`ci-fix-await#${l}`),a.reconcileAwait&&bl(a.reconcileAwait.since,rf,o)&&s.push(`reconcile-await#${l}`);let u=a.autoMerge?.armedAt??a.armedAt??null;a.trackId===e&&(a.autoMerge||a.armedAt)&&bl(u,c5,o)&&s.push(`auto-merge#${l}`)}return s}var of=new Map;Ze.on("event",e=>{e?.trackId&&of.delete(e.trackId)});async function d5(e){if(of.has(e.id))return of.get(e.id);let t=await K(e).catch(()=>[]),n=Wa(t,-1/0);return of.set(e.id,n),n}async function eR(e,{now:t=Date.now()}={}){let n=new Map,r=await En().catch(()=>[]);for(let o of e||[]){if(!o||Ue(o))continue;let s=u5({trackId:o.id,shipPending:await d5(o),links:r,observers:CE(o.id),now:t});s.length&&n.set(o.id,s)}return n}Bg();import pR from"node:fs";import Ro from"node:fs/promises";import R5 from"node:os";import kt from"node:path";import{spawn as O5}from"node:child_process";import{createRequire as M5}from"node:module";be();Id();import ii from"node:fs";import xl from"node:fs/promises";import m5 from"node:http";import Ot from"node:path";import af from"node:os";import{createRequire as h5}from"node:module";import Qy from"ws";import p5 from"node:path";import f5 from"node:os";var kl=process.env.BOSTRAT_STATE_DIR||p5.join(process.env.HOME||f5.tmpdir(),".bostrat");function Zy(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 tR(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=Zy(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function nR(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}am();var Cn=(()=>{try{return h5(import.meta.url)("../package.json").version||null}catch{return null}})(),tb=process.env.BOSTRAT_STATE_DIR||Ot.join(process.env.HOME||af.tmpdir(),".bostrat"),lR=Ot.join(tb,"plane-link","removed.json");function rR(e){return typeof e!="string"||e.length<8?"\u2022\u2022\u2022\u2022":`${e.slice(0,6)}\u2026${e.slice(-4)}`}function w5(e=lR){try{let t=JSON.parse(ii.readFileSync(e,"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function Ls({env:e=process.env,pkgDir:t=Ot.resolve(Ot.dirname(new URL(import.meta.url).pathname),".."),exists:n=ii.existsSync}={}){let r=String(e.BOSTRAT_INSTALL_KIND||"").trim();return["container","npm-global","git-checkout","dev"].includes(r)?r:/\/lib\/node_modules\/bostrat-node(?:\/|$)/.test(t.replace(/\\/g,"/"))?"npm-global":n(Ot.join(t,"..",".git"))?"git-checkout":"dev"}function vl(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 sf(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 g5({home:e=process.env.HOME||af.homedir(),env:t=process.env}={}){let n=r=>{try{return ii.statSync(r),!0}catch{return!1}};return{claude:n(Ot.join(e,".claude",".credentials.json")),codex:n(Ot.join(t.CODEX_HOME||Ot.join(e,".codex"),"auth.json")),cursor:n(Ot.join(t.XDG_CONFIG_HOME||Ot.join(e,".config"),"cursor","auth.json"))}}var mt=1,oR=1e3,sR=3e4,y5=6e4,b5=1500,k5=500,$o=100,x5=2e3,v5=25e3,S5=2.5,A5=6e4,uR=(e,t)=>{let n=Number(e);return Number.isFinite(n)&&n>0?n:t},T5=uR(process.env.BOSTRAT_PLANE_DELIVERY_TIMEOUT_MS,18e4),E5=3e4,aR=10*6e4,_5=400,iR=250,I5=uR(process.env.BOSTRAT_PLANE_INGEST_TIMEOUT_MS,1e4),eb=4,$5=6e4,cR=6e4;function dR({url:e,token:t,nodeId:n=af.hostname(),name:r=af.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,deliverySample:c=null,onHashKey:l=null,onViewBase:u=null,metricsMs:d=$5,onUpdateAvailable:p=null,onUpdatePolicy:f=null,onHelloRefused:w=null,updatePolicy:h=null,installKind:y=Ls(),selfUrl:k=`http://127.0.0.1:${process.env.PORT||3362}`,apiToken:g=null,removedMarkerFile:x=lR,onRemoved:S=null,onDegraded:_=null,onRecovered:M=null,onConnected:P=null,ingestMode:j=["ws","http","auto"].includes(process.env.BOSTRAT_PLANE_INGEST)?process.env.BOSTRAT_PLANE_INGEST:"auto",deliveryTimeoutMs:T=T5,unackedRetryMs:$=A5,pingMs:L=v5,deadLinkMs:H=Math.round(L*S5),maxInflightEvents:R=_5,httpTimeoutMs:A=I5,stallBackoffMinMs:C=E5,fetchImpl:W=globalThis.fetch,log:B=console}){if(!e||!t)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");e=nR(e);let Z=k.replace(/^http/,"ws"),xe=new Set,Ae=new Map,_e=new Map,Q=Ot.join(tb,"plane-link","cursor.json"),Xe=Ot.join(tb,"plane-link","dead-letter.ndjson"),Pe=e.replace(/^ws(s?):\/\//,"http$1://").replace(/\/v1\/node\/?$/,""),le={},ge={},de=new Map,ee=null,we=null,Oe=0,Ve=0,Nt=0,O=Promise.resolve(),X="ws",ne=0,he=!1,Be=0,Pt=!1,tt=$o,wn=new Set,Kt=[],ht=null,wt=null,at=!1,Si=!1,gt=oR,cr=null,Hr=null,lr=null,gn=null,ur=0,b=0,m=null,v=!1,D=0,J=!1,ie=w5(x);if(ie)if(ie.tokenPreview===rR(t))J=!0,sm({reason:ie.reason??"removed",at:ie.at??null,nodeId:ie.nodeId??n}),B.error(`[plane-link] this node was REMOVED from the plane (${ie.reason??"removed"}${ie.at?` at ${new Date(ie.at).toISOString()}`:""}) \u2014 not dialing. Re-provision it with a new node token, or delete ${x} to force a dial.`);else try{ii.unlinkSync(x),B.log("[plane-link] stale removed-marker discarded \u2014 the node token changed")}catch{}function ve({reason:F="removed",at:q=Date.now(),via:U="ctl"}={}){if(!J){J=!0,sm({reason:F,at:q,nodeId:n,via:U});try{ii.mkdirSync(Ot.dirname(x),{recursive:!0}),ii.writeFileSync(x,JSON.stringify({at:q,reason:F,via:U,nodeId:n,tokenPreview:rR(t)},null,2))}catch(I){B.error(`[plane-link] could not write ${x}: ${I?.message||I}`)}B.error(`[plane-link] the plane REMOVED this node (${F}, via ${U}) \u2014 going dormant: no more dials, ingest or broker calls. Re-provision with a new node token to reconnect; the box and its data are untouched.`);try{S?.({reason:F,at:q,via:U})}catch{}jk({bye:!1})}}async function _t(){try{le=JSON.parse(await xl.readFile(Q,"utf8"))}catch{le={}}}function yt(){v=!0,!lr&&(lr=setTimeout(async()=>{if(lr=null,!!v){v=!1;try{await xl.mkdir(Ot.dirname(Q),{recursive:!0}),await xl.writeFile(Q,JSON.stringify(le))}catch(F){B.error("[plane-link] cursor flush failed:",F?.message||F)}}},x5),lr.unref?.())}function nt(F){return!wt||wt.readyState!==Qy.OPEN?!1:(wt.send(JSON.stringify(F)),!0)}function rn(F){let q=Date.now();for(let{trackId:U,event:I}of F){let V=de.get(I.id);de.set(I.id,{trackId:U,seq:I.seq,event:I,sentAt:q,firstSentAt:V?.firstSentAt??q,retryAt:q+$,tries:(V?.tries??0)+1,nacks:V?.nacks??0})}return X==="http"?(iN(F),!0):nt({v:mt,ch:"events",type:"append",events:F})}function rN(F){let q=null;for(let U of de.values())U.trackId===F&&(q==null||U.seq<q)&&(q=U.seq);return q}function zf(F){let q=ge[F];if(q==null)return;let U=rN(F),I=U==null?q:Math.min(q,U-1);I>(le[F]??0)&&(le[F]=I,yt())}function Ai(){if(ee=Date.now(),we!=null){let F=Date.now()-we;B.log(`[plane-link] spine sync recovered after ${Math.round(F/1e3)}s degraded (${Oe} stall redial(s))`);let q=we;we=null,Oe=0,Ve=0,he=!1,Be=0;try{M?.({since:q,downMs:F})}catch(U){B.error(`[plane-link] onRecovered failed: ${U?.message||U}`)}}}function Jf(F){if(!Array.isArray(F)||!F.length)return;let q=new Set;for(let U of F){let I=de.get(U);I&&(de.delete(U),(ge[I.trackId]??0)<I.seq&&(ge[I.trackId]=I.seq),q.add(I.trackId))}for(let U of q)zf(U);Ai()}function Ok(F,q){Nt++;let U={at:Date.now(),trackId:F.trackId,seq:F.seq,id:F.event?.id??null,type:F.event?.type??null,reason:q,event:F.event??null};B.error(`[plane-link] DEAD-LETTER ${F.trackId} seq ${F.seq} (${U.type??"?"}): plane rejected it permanently (${q}) \u2014 kept in ${Xe}`),O=O.then(async()=>{await xl.mkdir(Ot.dirname(Xe),{recursive:!0}),await xl.appendFile(Xe,JSON.stringify(U)+`
|
|
693
|
+
`)}).catch(I=>B.error(`[plane-link] dead-letter write failed: ${I?.message||I}`))}function Mk(F,q,U,I=null){if(!Array.isArray(F)||!F.length)return;let V=new Set,Ie=Date.now();for(let ye of F){let Le=de.get(ye);Le&&(U?(de.delete(ye),Ok(Le,q),(ge[Le.trackId]??0)<Le.seq&&(ge[Le.trackId]=Le.seq),V.add(Le.trackId)):(Le.nacks++,Le.retryAt=Ie+Math.max(1e3,Number(I)||$)))}for(let ye of V)zf(ye);Ai()}function Ck(F=Date.now()){let q=1/0;for(let U of de.values())U.firstSentAt<q&&(q=U.firstSentAt);return q===1/0?0:F-q}function oN(F){if(!H||!F||F!==wt||!ur)return!1;let q=Date.now()-ur;if(q<=H)return!1;b++,B.error(`[plane-link] no frame from the plane for ${Math.round(q/1e3)}s \u2014 link is half-open; terminating and redialing`);try{F.terminate()}catch{}return!0}function Nk(){if(!at||!de.size)return;let F=Date.now(),q=Ck(F);if(q<T||ee!=null&&F-ee<T||X==="http"&&he||Ve>F)return;Oe++;let U=Math.min(C*2**(Oe-1),aR);if(we==null){we=F-q,B.error(`[plane-link] DEGRADED: no ack or nack for ${Math.round(q/1e3)}s with ${de.size} event(s) in flight (${X} door) \u2014 the plane is not answering appends`);try{_?.({since:we,unacked:de.size,oldestAgeMs:q,mode:X})}catch(I){B.error(`[plane-link] onDegraded failed: ${I?.message||I}`)}}if(X==="http"){he=!0,Be=F+U,B.error(`[plane-link] ingest circuit OPEN (stall #${Oe}); probing /v1/ingest/health in ${Math.round(U/1e3)}s`);return}Ve=F+U,B.error(`[plane-link] closing the link and redialing in ${Math.round(U/1e3)}s (stall redial #${Oe})`);try{wt?.terminate()}catch{}}function Yf(){if(!at)return;let F=Date.now();if(X==="http"&&he)return;let q=[...de.values()].filter(I=>I.event&&I.retryAt<=F&&!(X==="http"&&wn.has(I.trackId)));if(!q.length)return;q.sort((I,V)=>I.trackId===V.trackId?I.seq-V.seq:I.trackId<V.trackId?-1:1),B.log(`[plane-link] re-sending ${q.length} unacked event(s) the plane never confirmed`);let U=X==="http"?tt:$o;if(X==="http"){let I=new Map;for(let V of q)(I.get(V.trackId)??I.set(V.trackId,[]).get(V.trackId)).push(V);for(let[,V]of I){if(ne>=eb)return;rn(V.slice(0,U).map(Ie=>({trackId:Ie.trackId,event:Ie.event})))}return}for(let I=0;I<q.length;I+=U){let V=q.slice(I,I+U).map(Ie=>({trackId:Ie.trackId,event:Ie.event}));if(!rn(V))return}}async function sN(F=null){for(;;){if(!at)return!1;let q=de.size<R,U=X!=="http"||ne<eb&&!he&&(!F||!wn.has(F));if(q&&U)return!0;await new Promise(I=>setTimeout(I,100))}}function Xf(){if(ht=null,!at||!Kt.length)return;let F=Kt;if(Kt=[],X!=="http"){for(let U=0;U<F.length;U+=$o)rn(F.slice(U,U+$o));return}let q=new Map;for(let U of F)(q.get(U.trackId)??q.set(U.trackId,[]).get(U.trackId)).push(U);for(let[U,I]of q){if(wn.has(U)||ne>=eb||he){Kt.push(...I);continue}for(let V=0;V<I.length;V+=tt)rn(I.slice(V,V+tt))}Kt.length&&!ht&&(ht=setTimeout(Xf,iR),ht.unref?.())}let aN=F=>{let q=new Map;for(let{trackId:U,event:I}of F)(q.get(U)??q.set(U,[]).get(U)).push(I);return[...q].map(([U,I])=>({trackId:U,events:I}))},Uo=(F,q,U,{progress:I})=>{let V=Date.now();for(let{event:Ie}of F){let ye=de.get(Ie.id);ye&&(ye.nacks++,ye.retryAt=V+Math.max(1e3,U||$))}I&&Ai()};async function iN(F){let q=new Set(F.map(U=>U.trackId));for(let U of q)wn.add(U);ne++;try{let U=await W(`${Pe}/v1/ingest`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify({node:n,batches:aN(F)}),signal:AbortSignal.timeout(A)}),I=(Number(U.headers.get("retry-after"))||0)*1e3;if(U.status===200){let V=await U.json();tt=$o;for(let Ie of V.results??[]){Jf(Ie.acked??[]);let ye=(Le,ut)=>{let on=new Map;for(let it of Le??[])(on.get(it.reason)??on.set(it.reason,[]).get(it.reason)).push(it.id);for(let[it,Fk]of on)Mk(Fk,it,ut,V.retryAfterMs??null)};ye(Ie.rejected,!0),ye(Ie.deferred,!1)}Jf(V.discarded??[]),Ai();return}if(U.status===429){Uo(F,"overloaded",I||5e3,{progress:!0});return}if(U.status===413&&F.length>1){tt=Math.max(1,Math.floor(F.length/2)),B.error(`[plane-link] ingest request too large (${F.length} events) \u2014 halving batches to ${tt}`),Uo(F,"too_large",1e3,{progress:!0});return}if(U.status===413){let V=de.get(F[0].event.id);V&&(de.delete(V.event.id),Ok(V,"too_large"),(ge[V.trackId]??0)<V.seq&&(ge[V.trackId]=V.seq),zf(V.trackId)),Ai();return}if(U.status===410){ve({reason:"removed",via:"410"}),Uo(F,"http_410",6e4,{progress:!1});return}if(U.status===401||U.status===403){let{tripped:V,holdMs:Ie}=Qs();if(V&&B.error(`[plane-link] holding every plane call for ${Math.round(Ie/6e4)} min \u2014 the ingest door refused this node's token three times in a row`),Date.now()-D>=cR){D=Date.now();let ye="";try{ye=(await U.json())?.error??""}catch{}B.error(`[plane-link] plane refused ingest (HTTP ${U.status}${ye?`: ${ye}`:""}) \u2014 events are held locally and retried; check this node's token/binding`)}Uo(F,`http_${U.status}`,Math.max(6e4,Oi()),{progress:!1});return}if(U.status===400){B.error(`[plane-link] plane rejected an ingest request as malformed (400) \u2014 holding ${F.length} event(s) for retry; this is a bug, not data`),Uo(F,"http_400",6e4,{progress:!1});return}Uo(F,`http_${U.status}`,I||5e3,{progress:!1})}catch(U){let I=U?.name==="TimeoutError"||U?.name==="AbortError";I&&F.length>1&&(tt=Math.max(1,Math.floor(F.length/2)),B.error(`[plane-link] ingest request of ${F.length} event(s) outlived its ${A} ms deadline \u2014 halving batches to ${tt}`)),Uo(F,I?"timeout":"network",5e3,{progress:!1})}finally{ne--;for(let U of q)wn.delete(U)}}async function cN(){if(X!=="http"||!he||!at||Pt)return;let F=Date.now();if(!(Be>F||ea(F))){Pt=!0;try{let q=await W(`${Pe}/v1/ingest/health`,{headers:{authorization:`Bearer ${t}`},signal:AbortSignal.timeout(A)});if(!q.ok)throw new Error(`HTTP ${q.status}`);B.log("[plane-link] ingest door answering \u2014 closing the circuit and re-sending"),he=!1,Be=0,Ve=Date.now()+A+$+1e3;for(let U of de.values())U.retryAt=Math.min(U.retryAt,Date.now());Yf()}catch(q){Oe++;let U=Math.min(C*2**(Oe-1),aR);Be=Date.now()+U,B.error(`[plane-link] ingest door still not answering (${q?.message||q}); next probe in ${Math.round(U/1e3)}s`)}finally{Pt=!1}}}function Pk(){let F=Date.now();return{mode:X,unacked:de.size,oldestUnackedAgeMs:Ck(F),lastProgressAt:ee,degradedSince:we,stallRedials:Oe,deadLettered:Nt,http:X==="http"?{inFlight:ne,circuitOpen:he,nextProbeInMs:he?Math.max(0,Be-F):0,batchCap:tt}:null}}async function Dk(){if(!i)return;let F;try{F=await i()}catch(U){B.error("[plane-link] metrics sample failed:",U?.message||U);return}let q=null;try{q=c?.()??null}catch{}F&&nt({v:mt,ch:"ctl",type:"metrics",metrics:{...F,planeLink:Pk(),...q?{delivery:q}:{}}})}function lN(){i&&(m&&clearInterval(m),Dk(),m=setInterval(Dk,d),m.unref?.())}async function uN(){let F;try{F=await o()}catch(q){B.error("[plane-link] backfill listTracks failed:",q?.message||q);return}nt({v:mt,ch:"ctl",type:"tracks",ids:F.filter(q=>!q.transferredTo).map(q=>q.id).filter(q=>typeof q=="string")});for(let q of F){if(!at)return;let U;try{U=await K(q)}catch{continue}let I=le[q.id]??0,V=U.filter(ye=>ye.seq>I&&typeof ye.id=="string"),Ie=X==="http"?tt:$o;for(let ye=0;ye<V.length;ye+=Ie){if(!await sN(q.id))return;let Le=V.slice(ye,ye+Math.min(Ie,X==="http"?tt:$o)).map(ut=>({trackId:q.id,event:ut}));if(!rn(Le))return}}}function Bk(F){!at||!F?.trackId||typeof F.id!="string"||(Kt.push({trackId:F.trackId,event:F}),ht||(ht=setTimeout(Xf,iR),ht.unref?.()))}let Ti=new Map;async function dN(F){let{reqId:q,method:U,path:I,headers:V={},body:Ie}=F,ye=Object.fromEntries(Object.entries(V).filter(([,ut])=>ut!=null)),Le=new AbortController;Ti.set(q,Le);try{if(F.host){await pN(F,ye,Le);return}g&&(ye["x-bostrat-token"]=g);let ut=await fetch(k+I,{method:U,headers:ye,body:Ie?Buffer.from(Ie,"base64"):void 0,signal:Le.signal}),on=Object.fromEntries(ut.headers);for(let it of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete on[it];if(on["content-type"]||(on["content-type"]="application/octet-stream"),nt({v:mt,ch:"control",type:"http_head",reqId:q,status:ut.status,headers:on}),ut.body){for await(let it of ut.body)if(!nt({v:mt,ch:"control",type:"http_data",reqId:q,data:Buffer.from(it).toString("base64")}))break}nt({v:mt,ch:"control",type:"http_end",reqId:q,error:null})}finally{Ti.delete(q)}}function pN(F,q,U){let{reqId:I,method:V,path:Ie,body:ye}=F,Le=new URL(k);return new Promise(ut=>{let on=m5.request({host:Le.hostname,port:Le.port,method:V,path:Ie,headers:{...q,host:F.host},signal:U.signal},it=>{let{connection:Fk,"keep-alive":b3,"transfer-encoding":k3,upgrade:x3,...fN}=it.headers;nt({v:mt,ch:"control",type:"http_head",reqId:I,status:it.statusCode||502,headers:fN}),it.on("data",mN=>{nt({v:mt,ch:"control",type:"http_data",reqId:I,data:Buffer.from(mN).toString("base64")})||it.destroy()}),it.on("end",()=>{nt({v:mt,ch:"control",type:"http_end",reqId:I,error:null}),ut()}),it.on("error",()=>{nt({v:mt,ch:"control",type:"http_end",reqId:I,error:"view upstream error"}),ut()})});on.on("error",it=>{nt({v:mt,ch:"control",type:"http_end",reqId:I,error:it?.message||"view request failed"}),ut()}),ye&&on.write(Buffer.from(ye,"base64")),on.end()})}function Lk(){if(Si||J)return;wt=new Qy(e,{headers:{authorization:`Bearer ${t}`}});let F=wt;ur=0,F.on("ping",()=>{ur=Date.now()}),F.on("pong",()=>{ur=Date.now()}),wt.on("open",async()=>{let U=null;try{U=await s?.()||null}catch{U=null}nt({v:mt,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:Cn,installKind:y,...typeof h=="function"?{updatePolicy:h()}:h?{updatePolicy:h}:{},providers:g5(),...U?{agents:U}:{}})}),wt.on("message",U=>{ur=Date.now();let I;try{I=JSON.parse(U)}catch{return}if(I.ch==="ctl"&&I.type==="hello_ok"){at=!0,gt=oR,cr=null,zk(),B.log(`[plane-link] connected to plane as ${n}`);try{P?.()}catch{}if(typeof I.hashKey=="string"&&I.hashKey)try{l?.(I.hashKey)}catch{}try{u?.(typeof I.viewBase=="string"&&I.viewBase?I.viewBase:null)}catch{}I.minNodeVersion&&Cn&&vl(Cn,I.minNodeVersion)&&B.error(`[plane-link] this node is bostrat-node ${Cn}; the plane asks for \u2265${I.minNodeVersion} \u2014 update with: ${sf(y)}`);try{f?.(typeof I.updatePolicy=="string"?I.updatePolicy:null)}catch{}let V=typeof I.latestNodeVersion=="string"?I.latestNodeVersion:null;if(V&&Cn&&vl(Cn,V)&&!xe.has(V)){xe.add(V),B.log(`[plane-link] bostrat-node ${V} is available (this node runs ${Cn}) \u2014 ${sf(y)}`);try{p?.({current:Cn,latest:V,min:I.minNodeVersion??null,installKind:y,required:!!(I.minNodeVersion&&vl(Cn,I.minNodeVersion))})}catch(ye){B.error(`[plane-link] update notice failed: ${ye?.message||ye}`)}}let Ie=I.ingest==="http";X=j==="ws"?"ws":j==="http"||Ie?"http":"ws",j==="http"&&!Ie&&B.error("[plane-link] BOSTRAT_PLANE_INGEST=http but this plane offers no HTTPS ingest door \u2014 events will fail until it does"),X==="http"&&B.log("[plane-link] spine rides the HTTPS ingest door"),he=!1,Be=0,tt=$o,gn&&clearInterval(gn),ur=Date.now(),gn=setInterval(()=>{oN(F)||(nt({v:mt,ch:"ctl",type:"ping"}),Nk(),cN().catch(()=>{}),Yf(),Kt.length&&!ht&&Xf())},L),gn.unref?.(),lN(),uN().catch(ye=>B.error("[plane-link] backfill failed:",ye?.message||ye));return}if(I.ch==="ctl"&&I.type==="hello_refused"){let V=typeof I.minNodeVersion=="string"?I.minNodeVersion:null,Ie=typeof I.latestNodeVersion=="string"?I.latestNodeVersion:null;B.error(`[plane-link] plane REFUSED this node: bostrat-node ${Cn??"?"} is below its minimum ${V??"?"} \u2014 ${I.fix||sf(y)}`),gt=sR,cr=0;try{w?.({current:Cn,min:V,latest:Ie,fix:I.fix||sf(y),installKind:y})}catch(ye){B.error(`[plane-link] refusal handler failed: ${ye?.message||ye}`)}return}if(I.ch==="ctl"&&I.type==="removed"){nt({v:mt,ch:"ctl",type:"removed_ack",nodeId:n}),ve({reason:typeof I.reason=="string"?I.reason:"removed",at:Number(I.at)||Date.now(),via:"ctl"});return}if(I.ch==="ctl"&&I.type==="update_policy"){try{f?.(typeof I.mode=="string"?I.mode:null)}catch{}return}if(I.ch==="ctl"&&I.type==="usage_caps"){_d(I.caps??{},{source:"plane",updatedAt:I.updatedAt??Date.now()}).then(V=>B.log(`[plane-link] spend caps ${Object.keys(V.caps).length?`updated (${Object.keys(V.caps).join(", ")})`:"cleared"} from plane`)).catch(V=>B.error("[plane-link] spend-caps push rejected:",V?.message||V));return}if(I.ch==="ctl"&&I.type==="transferred"&&Array.isArray(I.tracks)){Promise.resolve().then(()=>a?.(I.tracks)).catch(V=>B.error("[plane-link] transfer notice failed:",V?.message||V));return}if(I.ch==="events"&&I.type==="ack"&&Array.isArray(I.ids)){Jf(I.ids);return}if(I.ch==="events"&&I.type==="nack"&&Array.isArray(I.ids)){Mk(I.ids,I.reason,I.permanent===!0,I.retryAfterMs??null);return}if(I.ch==="control"&&I.type==="http_open"){dN(I).catch(V=>nt({v:mt,ch:"control",type:"http_end",reqId:I.reqId,error:V?.message||"tunnel error"}));return}if(I.ch==="control"&&I.type==="http_cancel"){Ti.get(I.reqId)?.abort();return}if(I.ch==="attach"&&I.type==="open"){let V=new Qy(Z+I.path,Array.isArray(I.protocols)?I.protocols:[],I.host?{headers:{host:I.host}}:void 0);Ae.set(I.streamId,V),_e.set(I.streamId,[]),V.on("open",()=>{let ye=_e.get(I.streamId)||[];_e.delete(I.streamId);for(let{buf:Le,bin:ut}of ye)V.send(Le,{binary:ut})}),V.on("message",(ye,Le)=>{let ut=Le?ye:Buffer.from(String(ye));nt({v:mt,ch:"attach",type:"data",streamId:I.streamId,data:ut.toString("base64"),...Le?{bin:!0}:{}})});let Ie=()=>{_e.delete(I.streamId),Ae.delete(I.streamId)&&nt({v:mt,ch:"attach",type:"close",streamId:I.streamId})};V.on("close",Ie),V.on("error",Ie);return}if(I.ch==="attach"&&I.type==="data"){let V=Ae.get(I.streamId);if(!V)return;let Ie=Buffer.from(I.data,"base64"),ye=I.bin===!0,Le=_e.get(I.streamId);Le?Le.push({buf:Ie,bin:ye}):V.send(Ie,{binary:ye});return}if(I.ch==="attach"&&I.type==="close"){let V=Ae.get(I.streamId);Ae.delete(I.streamId),_e.delete(I.streamId),V?.close();return}});let q=()=>{at&&B.log("[plane-link] disconnected from plane"),at=!1,gn&&(clearInterval(gn),gn=null),m&&(clearInterval(m),m=null),de.clear(),ge={},Kt=[],ht&&(clearTimeout(ht),ht=null),wn.clear();for(let V of Ae.values())V.close();Ae.clear(),_e.clear();for(let V of Ti.values())V.abort();if(Ti.clear(),Si||Hr)return;cr==null&&(cr=Date.now());let U=Date.now()-cr<y5,I=Math.max(U?b5+Math.random()*k5:gt,Ve-Date.now(),Oi());Hr=setTimeout(()=>{Hr=null,Lk()},I),Hr.unref?.(),U||(gt=Math.min(gt*2,sR))};wt.on("close",q),wt.on("error",U=>{let I=/Unexpected server response: (\d+)/.exec(U?.message||"")?.[1];if(I==="410"){ve({reason:"removed",via:"410"});return}if(I==="401"||I==="403"){let{tripped:V,holdMs:Ie}=Qs();V?B.error(`[plane-link] holding every plane call for ${Math.round(Ie/6e4)} min \u2014 three credential refusals in a row (HTTP ${I}) mean the token is revoked or unknown, not that the plane is down; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN. Next dial in ${Math.round(Ie/6e4)} min.`):Date.now()-D>=cR&&(D=Date.now(),B.error(`[plane-link] plane refused this node's credentials (HTTP ${I}) \u2014 the token was revoked or is unknown to the plane; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN (still retrying)`))}q()})}Ze.on("event",Bk),_t().then(Lk);function jk({bye:F=!0}={}){Si=!0,Ze.off("event",Bk),Hr&&clearTimeout(Hr),lr&&clearTimeout(lr),gn&&clearInterval(gn),m&&clearInterval(m),ht&&clearTimeout(ht),F&&nt({v:mt,ch:"ctl",type:"bye",reason:"shutdown"}),wt?.close()}return{stop:()=>jk(),status:()=>({connected:at,nodeId:n,cursor:{...le},inflight:de.size,...Pk(),auth:Jk(),dormant:J,removed:lu(),deadLinkMs:H,deadLinkRedials:b}),_tick:()=>{Nk(),Yf()}}}Uu();var cf=(()=>{try{return M5(import.meta.url)("../package.json").version||null}catch{return null}})(),C5=process.env.BOSTRAT_STATE_DIR||kt.join(process.env.HOME||R5.tmpdir(),".bostrat"),fR=kt.join(C5,"self-update"),lf=kt.join(fR,"status.json"),N5=1440*6e4,P5=6e4,Sl=new Set(["npm-global","git-checkout"]);function mR({kind:e=Ls(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:Sl.has(e)?e==="git-checkout"||D5(t)?"auto":"prompt":"off"}function D5(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var Qt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function B5({kind:e,to:t,from:n=cf,statusFile:r=lf,logFile:o=kt.join(fR,"last.log"),port:s=Number(process.env.PORT)||3362,npmPrefix:a=null,serviceName:i=process.platform==="darwin"?"com.bostrat.node":"bostrat-node.service",uid:c=typeof process.getuid=="function"?process.getuid():0,liveDir:l=process.env.BOSTRAT_LIVE_CHECKOUT||kt.resolve(kt.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!Sl.has(e))throw new Error(`self-update: install kind "${e}" is not actionable here`);if(!/^\d+\.\d+\.\d+$/.test(String(t)))throw new Error(`self-update: bad target version "${t}"`);if(n&&!/^\d+\.\d+\.\d+$/.test(String(n)))throw new Error(`self-update: bad current version "${n}"`);let d=(y,k="")=>`write_status ${Qt(y)} ${k}`,p=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${Qt(i)}`,f=`
|
|
694
694
|
health() {
|
|
695
695
|
for i in $(seq 1 45); do
|
|
696
696
|
sleep 1
|
|
@@ -714,7 +714,7 @@ write_status() { # <status> [error]
|
|
|
714
714
|
}
|
|
715
715
|
${f}
|
|
716
716
|
`;if(e==="npm-global"){let y=a?kt.join(a,"bin","npm"):"npm",k=a?`export PATH=${Qt(kt.join(a,"bin"))}:"$PATH"
|
|
717
|
-
`:"",g=a?` --prefix ${Qt(a)}`:"",x=
|
|
717
|
+
`:"",g=a?` --prefix ${Qt(a)}`:"",x=S=>`${Qt(y)} install -g "bostrat-node@${S}" --no-audit --no-fund${g}`;return w+k+`
|
|
718
718
|
${d("updating",'"" ')}
|
|
719
719
|
if ! ${x("$TO")}; then
|
|
720
720
|
${d("failed",'"npm install failed"')}
|
|
@@ -755,68 +755,68 @@ if [ -n "$FROM" ] && ${h("$FROM")}; then
|
|
|
755
755
|
fi
|
|
756
756
|
${d("failed",'"deploy-pi.sh v$TO failed AND the rollback failed"')}
|
|
757
757
|
exit 1
|
|
758
|
-
`}async function Al(e=lf){try{return JSON.parse(await Ro.readFile(e,"utf8"))}catch{return null}}var P5=new Set(["updating","restarting","rolling-back"]),uf=e=>!!(e&&P5.has(e.status));async function df({to:e,from:t=cf,kind:n=Ls(),reason:r="manual",statusFile:o=lf,spawnImpl:s=I5,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!Sl.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!vl(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await Al(o);if(uf(l))return{ok:!1,reason:`an update to ${l.to} is already in flight`,status:l};if(l&&l.to===e&&(l.status==="failed"||l.status==="rolled-back"))return{ok:!1,reason:`update to ${e} already ${l.status} (${l.error||"no detail"}) \u2014 retried when a newer release exists`,status:l};await Ro.mkdir(kt.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?Vm(await D5())||kt.resolve(kt.dirname(process.execPath),".."):null,d=N5({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),p=kt.join(kt.dirname(o),`update-${e}.sh`),f=kt.join(kt.dirname(o),"last.log");await Ro.writeFile(p,d,{mode:448}),await Ro.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let w=uR.existsSync("/run/systemd/system")&&await B5("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",p]]:["setsid",["-f","bash","-c",`exec bash ${Qt(p)} >> ${Qt(f)} 2>&1`]];try{s(w[0],w[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(h){return await Ro.writeFile(o,JSON.stringify({status:"failed",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a(),error:`could not spawn updater: ${h?.message||h}`})),{ok:!1,reason:`could not spawn updater: ${h?.message||h}`}}return c.log?.(`[self-update] ${t??"?"} \u2192 ${e} (${n}, ${r}) started \u2014 status ${o}`),{ok:!0,to:e,from:t,kind:n,scriptFile:p,statusFile:o}}async function D5(){try{return await Ro.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function B5(e){for(let t of String(process.env.PATH||"").split(kt.delimiter))try{return await Ro.access(kt.join(t,e),uR.constants.X_OK),!0}catch{}return!1}var L5=45*6e4;async function fR({statusFile:e=lf,current:t=cf,now:n=Date.now,staleMs:r=L5}={}){let o=await Al(e);if(!o)return null;let s=a=>Ro.writeFile(e,JSON.stringify(a)).catch(()=>{});if(uf(o)){if(t&&o.to===t){let a={...o,status:"done",updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}if(n()-Number(o.updatedAt||o.startedAt||0)>r){let a={...o,status:"failed",error:`updater went silent while ${o.status} (no status change for ${Math.round(r/6e4)} min)`,updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}return null}if(["failed","rolled-back","done"].includes(o.status)&&!o.announced){let a={...o,announced:!0};return await s(a),a}return null}function mR({isBusy:e,start:t=df,onStuck:n=null,onStarted:r=null,tickMs:o=M5,stuckMs:s=O5,now:a=Date.now,setIntervalImpl:i=setInterval,clearIntervalImpl:c=clearInterval,log:l=console}={}){let u=null,d=()=>{u?.timer&&c(u.timer),u=null},p=async()=>{if(!u)return;let f=!0;try{f=await e()!==!1}catch{f=!0}if(f){if(!u.stuckFired&&a()-u.since>=s){u.stuckFired=!0;try{await n?.({to:u.to,from:u.from,since:u.since})}catch{}}return}let w=u;d();let h=await t({to:w.to,from:w.from,kind:w.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${h.ok?"started":`not started: ${h.reason}`}`);try{await r?.({...w,result:h})}catch{}};return{arm({to:f,from:w=cf,kind:h=Ls()}){return u?.to===f?!1:(d(),u={to:f,from:w,kind:h,since:a(),stuckFired:!1,timer:i(p,o)},u.timer.unref?.(),!0)},tick:p,cancel:d,get armed(){return u?{to:u.to,from:u.from,kind:u.kind,since:u.since}:null}}}be();xt();var hR=(...e)=>fetch(...e);function wR(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}async function nb(e=process.env){if(!wR(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 hR(`${De(e)}/v1/circuits`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN}`},signal:t.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return Array.isArray(o.circuits)?o.circuits:[]}finally{clearTimeout(n)}}async function rb(e=process.env){if(!wR(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 hR(`${De(e)}/v1/roadmap`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN}`},signal:t.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return{people:Array.isArray(o.people)?o.people:[],slots:Array.isArray(o.slots)?o.slots:[],reservations:Array.isArray(o.reservations)?o.reservations:[],tray:Array.isArray(o.tray)?o.tray:[],circuits:Array.isArray(o.circuits)?o.circuits:[]}}finally{clearTimeout(n)}}async function ci(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await nb(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 pf(e,t,{source:n="chat"}={}){return N(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}be();xt();var j5=(...e)=>fetch(...e);async function ff(e,t,n,r=process.env){if(!(De(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 blueprints live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await j5(`${De(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}function F5(e,t=process.env){return ff("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function gR({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await F5(t,o):await ci(e,o),a=await ff("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function ob(e,t=process.env){let n=await ci(e,t),r=await ff("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function sb(e,t=process.env){return(await ff("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function yR(e,t,n,r=process.env){let{blueprint:o,items:s}=await ob(t,r),a=new Map(s.map(c=>[c.itemId,c])),i=n.filter(c=>!a.get(c)?.bindable);if(i.length){let c=s.filter(l=>l.bindable).map(l=>l.itemId);throw new Error(`unknown/unbindable item(s): ${i.join(", ")} \u2014 bindable ids: ${c.join(", ")||"(none)"}`)}return await N(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}be();xt();var U5=(...e)=>fetch(...e);async function H5(e,t,n,r=process.env){if(!(De(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 the decision ledger lives on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await U5(`${De(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}var bR=72,ab=160;async function kR(e,{title:t,context:n="",decision:r,summary:o,alternatives:s=[],supersedesId:a=null}){let i=String(t??"").trim(),c=String(o??"").trim();if(!i)throw new Error("a title is required");if(i.length>bR)throw new Error(`the title is the choice as a LABEL \u2014 ${bR} chars max (yours is ${i.length}); the full story goes in the decision text`);if(!c)throw new Error(`a summary is required \u2014 one sentence (max ${ab} chars) a reader can act on without opening the record`);if(c.length>ab)throw new Error(`the summary is one sentence \u2014 ${ab} 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 xR(e,t=process.env){return H5("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}$g();be();import Ol from"node:fs/promises";import Sf from"node:path";import LG from"node:os";zr();import W5 from"node:fs/promises";import vR from"node:path";import q5 from"node:os";var SR=()=>vR.join(process.env.BOSTRAT_STATE_DIR||vR.join(process.env.HOME||q5.tmpdir(),".bostrat"),"integrations.json");async function Fr(){try{return JSON.parse(await W5.readFile(SR(),"utf8"))||{}}catch{return{}}}var mf=["linked","prOpened","prMerged","create"],ib=["prompt","auto","off"],G5={create:["prompt","off"]},AR=e=>G5[e]??ib;function hf(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of mf){let s=AR(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 js(){let e=await Fr(),t=process.env.BOSTRAT_LINEAR_API_KEY||e.linear?.apiKey,n=process.env.BOSTRAT_LINEAR_DEFAULT_TEAM||e.linear?.defaultTeam||null;return t?{apiKey:t,ticketFlow:hf(e),defaultTeam:n}:null}async function wf(){let e=await Fr(),t=e.jira||{},n=process.env.BOSTRAT_JIRA_BASE_URL||t.baseUrl,r=process.env.BOSTRAT_JIRA_EMAIL||t.email,o=process.env.BOSTRAT_JIRA_API_TOKEN||t.apiToken;return!n||!r||!o?null:{baseUrl:String(n).replace(/\/$/,""),email:r,apiToken:o,tokenExpiresAt:t.tokenExpiresAt??null,ticketFlow:hf(e)}}async function cb(){return hf(await Fr())}async function TR(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${mf.join(", ")}`);for(let[r,o]of t){if(!mf.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${mf.join(", ")}`);let s=AR(r);if(!s.includes(o))throw new Error(s.length<ib.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${ib.join(", ")}`)}let n=await Fr();return n.ticketFlow={...hf(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Bt(SR(),n,{mode:384}),n.ticketFlow}async function Tl(){let e=(await Fr()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function K5(){let e=(await Fr()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var V5={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function gf(){let t=(await Fr()).voiceChat||{},n=t.provider||"openai",r=V5[n];if(!r)return null;let o=n==="openai"?await K5():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function ER(){let e=(await Fr()).cedar||{},t=process.env.BOSTRAT_CEDAR_INGEST_KEY||e.ingestKey,n=process.env.BOSTRAT_CEDAR_URL||e.url||"https://cedar-api.fly.dev";return t?{url:n,ingestKey:t}:null}async function Fs(){let e=(await Fr()).slack||{},t=process.env.BOSTRAT_SLACK_BOT_TOKEN||e.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||e.appToken;return!t||!n?null:{botToken:t,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||e.channel||null,maxConcurrent:Number.isInteger(e.maxConcurrent)&&e.maxConcurrent>0?e.maxConcurrent:2}}be();Yr();fo();Ka();import _l from"node:fs/promises";import qR from"node:path";import xG from"node:os";import{randomUUID as GR}from"node:crypto";var ub={};Zf(ub,{createComment:()=>nG,findIssueUrl:()=>X5,issueStates:()=>sG,mdToAdf:()=>$R,moveIssue:()=>aG,parseIssueRef:()=>Y5,resolveIssue:()=>Z5,uploadImage:()=>rG,upsertCard:()=>Q5,whoami:()=>lb});var li=e=>String(e.baseUrl||"").replace(/\/$/,""),IR=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function Us(e,t,n,r){let o=await fetch(`${li(e)}${n}`,{method:t,headers:{authorization:IR(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 z5=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,J5=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function Y5(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(z5);return r?r[1].toUpperCase():null}let n=t.match(J5);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 Z5(e,t){let n;try{n=await Us(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:`${li(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function Q5(e,t,{url:n,title:r,subtitle:o}){await Us(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 eG=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function tG(e){let t=[],n=0;for(let r of e.matchAll(eG)){r.index>n&&t.push({type:"text",text:e.slice(n,r.index)});let o=r[0];if(o.startsWith("![")){let[,s,a]=o.match(/^!\[([^\]]*)\]\(([^)\s]+)\)$/);t.push({type:"text",text:s||a,marks:[{type:"link",attrs:{href:a}}]})}else o.startsWith("**")?t.push({type:"text",text:o.slice(2,-2),marks:[{type:"strong"}]}):o.startsWith("`")?t.push({type:"text",text:o.slice(1,-1),marks:[{type:"code"}]}):o.startsWith("_")?t.push({type:"text",text:o.slice(1,-1),marks:[{type:"em"}]}):t.push({type:"text",text:o,marks:[{type:"link",attrs:{href:o}}]});n=r.index+o.length}return n<e.length&&t.push({type:"text",text:e.slice(n)}),t}function $R(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
759
|
-
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...
|
|
758
|
+
`}async function Al(e=lf){try{return JSON.parse(await Ro.readFile(e,"utf8"))}catch{return null}}var L5=new Set(["updating","restarting","rolling-back"]),uf=e=>!!(e&&L5.has(e.status));async function df({to:e,from:t=cf,kind:n=Ls(),reason:r="manual",statusFile:o=lf,spawnImpl:s=O5,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!Sl.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!vl(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await Al(o);if(uf(l))return{ok:!1,reason:`an update to ${l.to} is already in flight`,status:l};if(l&&l.to===e&&(l.status==="failed"||l.status==="rolled-back"))return{ok:!1,reason:`update to ${e} already ${l.status} (${l.error||"no detail"}) \u2014 retried when a newer release exists`,status:l};await Ro.mkdir(kt.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?Vm(await j5())||kt.resolve(kt.dirname(process.execPath),".."):null,d=B5({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),p=kt.join(kt.dirname(o),`update-${e}.sh`),f=kt.join(kt.dirname(o),"last.log");await Ro.writeFile(p,d,{mode:448}),await Ro.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let w=pR.existsSync("/run/systemd/system")&&await F5("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",p]]:["setsid",["-f","bash","-c",`exec bash ${Qt(p)} >> ${Qt(f)} 2>&1`]];try{s(w[0],w[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(h){return await Ro.writeFile(o,JSON.stringify({status:"failed",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a(),error:`could not spawn updater: ${h?.message||h}`})),{ok:!1,reason:`could not spawn updater: ${h?.message||h}`}}return c.log?.(`[self-update] ${t??"?"} \u2192 ${e} (${n}, ${r}) started \u2014 status ${o}`),{ok:!0,to:e,from:t,kind:n,scriptFile:p,statusFile:o}}async function j5(){try{return await Ro.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function F5(e){for(let t of String(process.env.PATH||"").split(kt.delimiter))try{return await Ro.access(kt.join(t,e),pR.constants.X_OK),!0}catch{}return!1}var U5=45*6e4;async function hR({statusFile:e=lf,current:t=cf,now:n=Date.now,staleMs:r=U5}={}){let o=await Al(e);if(!o)return null;let s=a=>Ro.writeFile(e,JSON.stringify(a)).catch(()=>{});if(uf(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 wR({isBusy:e,start:t=df,onStuck:n=null,onStarted:r=null,tickMs:o=P5,stuckMs:s=N5,now:a=Date.now,setIntervalImpl:i=setInterval,clearIntervalImpl:c=clearInterval,log:l=console}={}){let u=null,d=()=>{u?.timer&&c(u.timer),u=null},p=async()=>{if(!u)return;let f=!0;try{f=await e()!==!1}catch{f=!0}if(f){if(!u.stuckFired&&a()-u.since>=s){u.stuckFired=!0;try{await n?.({to:u.to,from:u.from,since:u.since})}catch{}}return}let w=u;d();let h=await t({to:w.to,from:w.from,kind:w.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${h.ok?"started":`not started: ${h.reason}`}`);try{await r?.({...w,result:h})}catch{}};return{arm({to:f,from:w=cf,kind:h=Ls()}){return u?.to===f?!1:(d(),u={to:f,from:w,kind:h,since:a(),stuckFired:!1,timer:i(p,o)},u.timer.unref?.(),!0)},tick:p,cancel:d,get armed(){return u?{to:u.to,from:u.from,kind:u.kind,since:u.since}:null}}}be();xt();var gR=(...e)=>fetch(...e);function yR(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}async function nb(e=process.env){if(!yR(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 gR(`${De(e)}/v1/circuits`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN}`},signal:t.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return Array.isArray(o.circuits)?o.circuits:[]}finally{clearTimeout(n)}}async function rb(e=process.env){if(!yR(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 gR(`${De(e)}/v1/roadmap`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN}`},signal:t.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return{people:Array.isArray(o.people)?o.people:[],slots:Array.isArray(o.slots)?o.slots:[],reservations:Array.isArray(o.reservations)?o.reservations:[],tray:Array.isArray(o.tray)?o.tray:[],circuits:Array.isArray(o.circuits)?o.circuits:[]}}finally{clearTimeout(n)}}async function ci(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await nb(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 pf(e,t,{source:n="chat"}={}){return N(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}be();xt();var H5=(...e)=>fetch(...e);async function ff(e,t,n,r=process.env){if(!(De(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 blueprints live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await H5(`${De(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}function W5(e,t=process.env){return ff("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function bR({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await W5(t,o):await ci(e,o),a=await ff("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function ob(e,t=process.env){let n=await ci(e,t),r=await ff("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function sb(e,t=process.env){return(await ff("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function kR(e,t,n,r=process.env){let{blueprint:o,items:s}=await ob(t,r),a=new Map(s.map(c=>[c.itemId,c])),i=n.filter(c=>!a.get(c)?.bindable);if(i.length){let c=s.filter(l=>l.bindable).map(l=>l.itemId);throw new Error(`unknown/unbindable item(s): ${i.join(", ")} \u2014 bindable ids: ${c.join(", ")||"(none)"}`)}return await N(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}be();xt();var q5=(...e)=>fetch(...e);async function G5(e,t,n,r=process.env){if(!(De(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 the decision ledger lives on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await q5(`${De(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}var xR=72,ab=160;async function vR(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>xR)throw new Error(`the title is the choice as a LABEL \u2014 ${xR} 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 ${ab} chars) a reader can act on without opening the record`);if(c.length>ab)throw new Error(`the summary is one sentence \u2014 ${ab} 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 SR(e,t=process.env){return G5("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}$g();be();import Ol from"node:fs/promises";import Sf from"node:path";import UG from"node:os";zr();import K5 from"node:fs/promises";import AR from"node:path";import V5 from"node:os";var TR=()=>AR.join(process.env.BOSTRAT_STATE_DIR||AR.join(process.env.HOME||V5.tmpdir(),".bostrat"),"integrations.json");async function Fr(){try{return JSON.parse(await K5.readFile(TR(),"utf8"))||{}}catch{return{}}}var mf=["linked","prOpened","prMerged","create"],ib=["prompt","auto","off"],z5={create:["prompt","off"]},ER=e=>z5[e]??ib;function hf(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of mf){let s=ER(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 js(){let e=await Fr(),t=process.env.BOSTRAT_LINEAR_API_KEY||e.linear?.apiKey,n=process.env.BOSTRAT_LINEAR_DEFAULT_TEAM||e.linear?.defaultTeam||null;return t?{apiKey:t,ticketFlow:hf(e),defaultTeam:n}:null}async function wf(){let e=await Fr(),t=e.jira||{},n=process.env.BOSTRAT_JIRA_BASE_URL||t.baseUrl,r=process.env.BOSTRAT_JIRA_EMAIL||t.email,o=process.env.BOSTRAT_JIRA_API_TOKEN||t.apiToken;return!n||!r||!o?null:{baseUrl:String(n).replace(/\/$/,""),email:r,apiToken:o,tokenExpiresAt:t.tokenExpiresAt??null,ticketFlow:hf(e)}}async function cb(){return hf(await Fr())}async function _R(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${mf.join(", ")}`);for(let[r,o]of t){if(!mf.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${mf.join(", ")}`);let s=ER(r);if(!s.includes(o))throw new Error(s.length<ib.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${ib.join(", ")}`)}let n=await Fr();return n.ticketFlow={...hf(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Bt(TR(),n,{mode:384}),n.ticketFlow}async function Tl(){let e=(await Fr()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function J5(){let e=(await Fr()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var Y5={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function gf(){let t=(await Fr()).voiceChat||{},n=t.provider||"openai",r=Y5[n];if(!r)return null;let o=n==="openai"?await J5():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 Fr()).cedar||{},t=process.env.BOSTRAT_CEDAR_INGEST_KEY||e.ingestKey,n=process.env.BOSTRAT_CEDAR_URL||e.url||"https://cedar-api.fly.dev";return t?{url:n,ingestKey:t}:null}async function Fs(){let e=(await Fr()).slack||{},t=process.env.BOSTRAT_SLACK_BOT_TOKEN||e.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||e.appToken;return!t||!n?null:{botToken:t,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||e.channel||null,maxConcurrent:Number.isInteger(e.maxConcurrent)&&e.maxConcurrent>0?e.maxConcurrent:2}}be();Yr();fo();Ka();import _l from"node:fs/promises";import KR from"node:path";import AG from"node:os";import{randomUUID as VR}from"node:crypto";var ub={};Zf(ub,{createComment:()=>sG,findIssueUrl:()=>eG,issueStates:()=>cG,mdToAdf:()=>OR,moveIssue:()=>lG,parseIssueRef:()=>Q5,resolveIssue:()=>tG,uploadImage:()=>aG,upsertCard:()=>nG,whoami:()=>lb});var li=e=>String(e.baseUrl||"").replace(/\/$/,""),RR=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function Us(e,t,n,r){let o=await fetch(`${li(e)}${n}`,{method:t,headers:{authorization:RR(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 X5=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,Z5=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function Q5(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(X5);return r?r[1].toUpperCase():null}let n=t.match(Z5);return n?n[1]:null}function eG(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 tG(e,t){let n;try{n=await Us(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:`${li(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function nG(e,t,{url:n,title:r,subtitle:o}){await Us(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 rG=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function oG(e){let t=[],n=0;for(let r of e.matchAll(rG)){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 OR(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
759
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...oG(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 sG(e,t,n){let r=await Us(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/comment`,{body:OR(n)});return r?.id?`${li(e)}/browse/${t.key}?focusedCommentId=${r.id}`:null}async function lb(e){let t=await Us(e,"GET","/rest/api/3/myself"),n=null;try{n=new URL(li(e)).host}catch{n=e.baseUrl??null}return{user:t?.displayName??t?.emailAddress??null,org:n}}async function aG(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(`${li(e)}/rest/api/3/issue/${encodeURIComponent(t)}/attachments`,{method:"POST",headers:{authorization:RR(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??`${li(e)}/rest/api/3/attachment/content/${c.id}`}var iG={new:"unstarted",indeterminate:"started",done:"completed"},$R=e=>iG[e?.key]??"started";async function cG(e,t){let n=encodeURIComponent(t.key),[r,o]=await Promise.all([Us(e,"GET",`/rest/api/3/issue/${n}?fields=status`),Us(e,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:$R(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:$R(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function lG(e,t,n){await Us(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/transitions`,{transition:{id:String(n)}})}var mb={};Zf(mb,{createComment:()=>gG,createIssue:()=>fb,createProject:()=>pb,findIssueUrl:()=>mG,issueStates:()=>bG,moveIssue:()=>kG,parseIssueRef:()=>fG,resolveIssue:()=>hG,teams:()=>ui,uploadImage:()=>yG,upsertCard:()=>wG,whoami:()=>db});var uG=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function sr(e,t,n={}){let r=await fetch(uG(),{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 dG=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,pG=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function fG(e){let t=String(e||"").trim(),n=t.match(dG);if(n)return n[1].toUpperCase();let r=t.match(pG);return r?r[1]:null}function mG(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 hG(e,t){let r=(await sr(e,"query($id: String!) { issue(id: $id) { id identifier title url state { name } team { key } } }",{id:t})).issue;if(!r)throw new Error(`linear: issue "${t}" not found`);return{issueId:r.id,key:r.identifier,title:r.title,url:r.url,state:r.state?.name??null,team:r.team?.key??null}}async function wG(e,t,{url:n,title:r,subtitle:o}){let s=await sr(e,"mutation($input: AttachmentCreateInput!) { attachmentCreate(input: $input) { success attachment { id } } }",{input:{issueId:t.issueId,url:n,title:r,subtitle:o}});if(!s.attachmentCreate?.success)throw new Error("linear: attachmentCreate did not succeed");return s.attachmentCreate.attachment?.id??null}async function gG(e,t,n){let r=await sr(e,"mutation($input: CommentCreateInput!) { commentCreate(input: $input) { success comment { url } } }",{input:{issueId:t.issueId,body:n}});if(!r.commentCreate?.success)throw new Error("linear: commentCreate did not succeed");return r.commentCreate.comment?.url??null}async function db(e){let t=await sr(e,"query { viewer { name email } organization { name urlKey } }");return{user:t.viewer?.name??t.viewer?.email??null,org:t.organization?.name??t.organization?.urlKey??null}}async function yG(e,{filename:t,contentType:n,buffer:r}){let o=await sr(e,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
760
760
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
761
761
|
success
|
|
762
762
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
763
763
|
}
|
|
764
|
-
}`,{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
|
|
765
|
-
`)):yf.rmSync(r,{force:!0})}catch{}}function wb(){return CR().viewBase}function gb(e=process.env.BOSTRAT_PUBLIC_HOST){return NR(e,"https")}function DR(){let e=wb();if(e)return{proto:"https",host:e,source:"learned"};let t=gb();return t?{...t,source:"env"}:null}function bf(e,t){return e?`${e.proto}://${t}.${e.host}/`:null}function kG(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 BR(){let e=String(process.env.BOSTRAT_PUBLIC_URL||"").trim().replace(/\/+$/,"");if(e)return{url:e,source:"env"};let t=kG();return t?{url:t,source:"plane"}:null}var LR="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 KR={linear:{api:mb,config:js},jira:{api:ub,config:wf}};async function Ab(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(KR)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Hs(e){let t=KR[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function Tb(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 VR=()=>process.env.BOSTRAT_STATE_DIR||qR.join(process.env.HOME||xG.tmpdir(),".bostrat"),xb=()=>qR.join(VR(),"tickets.json"),$l=e=>{let t=BR();if(!t)throw new Error(LR);return`${t.url}/track/${e}`},vG=e=>`http://bostrat.globopi/track/${e}`,jR=e=>{let t=0;for(let[n,r]of Object.entries(e))r&&typeof r=="object"&&!r.cardUrl&&(r.cardUrl=vG(n),t++);return t},ar=null,yb=null,bb=Promise.resolve();async function Rl(){return ar||(yb??=_l.readFile(xb(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{ar=e;let n=jR(ar)>0;if(!t&&Ge()){let{records:r,source:o}=await dt("tickets").catch(()=>({records:[],source:"error"}));if(o==="plane"){for(let s of r){if(s.deletedAt||!s.data?.id)continue;let{id:a,updatedAt:i,...c}=s.data;ar[a]=c}jR(ar),n=n||Object.keys(ar).length>0}}return n&&await vb().catch(()=>{}),ar}).finally(()=>yb=null),yb)}async function vb(){let e=JSON.stringify({tickets:ar},null,2),t=`${xb()}.${process.pid}.tmp`;await _l.mkdir(VR(),{recursive:!0}),await _l.writeFile(t,e),await _l.rename(t,xb())}async function Oo(e=null){if(bb=bb.then(vb,vb),await bb.catch(()=>{}),!e)return;let t=ar?.[e];t?It("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):yn("tickets",e).catch(()=>{})}async function ir(e){return(await Rl())[e]??null}async function zR(){return{...await Rl()}}async function JR(){return(await Ab()).length>0}async function pi(e,t,{source:n="api"}={}){let r=await Ab();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:$l(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 Rl())[e.id]=c,await Oo(e.id),await N({id:e.id,cwd:e.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await Il(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 El(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await ZR(e.id,c,"linked"),c}async function Eb(e){let t=await Rl();return t[e]?(delete t[e],await Oo(e),!0):!1}var Sb=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function YR(e,t){if(!e.track?.cwd)return null;let n=po(await K({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function SG(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Sb(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${Sb(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function AG(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(Sb(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function Il(e,t){let n=await Hs(t);if(!n)return;let r=await YR(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:t.cardUrl??$l(e),title:`Bostrat Track: ${t.track.name}`,subtitle:AG(t,r)})}function TG(e){return bf(DR(),e)}var kf=e=>TG(e)??"(no public URL on this node \u2014 link it to a plane)",EG={linked:"linked",opened:"prOpened",merged:"prMerged"};function _G(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 XR(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await Oo(e),await N({id:e,cwd:t.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:t.key,url:t.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function ZR(e,t,n){let r=await Hs(t);if(!r)return;let o=r.cfg.ticketFlow?.[EG[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=Tb(s,a),c=_G(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 XR(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:GR(),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 QR(e){let t=await ir(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Hs(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=Tb(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await Oo(e)),{key:t.key,url:t.url,current:s?{id:s.id,name:s.name??null,type:s.type??null}:null,states:[...o].sort((a,i)=>a.position-i.position).map(({id:a,name:i,type:c,position:l})=>({id:a,name:i,type:c,position:l}))}}async function _b(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 Hs(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Tb(s,a),c=a.find(u=>u.id===t);if(!c)throw new Error("unknown state for this ticket's team");let l=i?.name??r.state??null;return i?.id===c.id?(r.state!==c.name&&(r.state=c.name,await Oo(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await XR(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function eO(e,t){let n=await ir(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Hs(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 tO(e,t,n){if(!t)throw new Error("offerId required");return vo(`tkt:${e.id}:${t}`,()=>IG(e,t,n))}async function IG(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 _b(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 FR=50;async function nO(){let e=await js();if(!e)throw new Error("no ticketing integration configured");return ui(e)}function $G(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>FR)throw new Error(`too many issues (${n.length}); cap is ${FR} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function RG(e,t,n,r){let o=r??await ui(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 rO(e,t){let n=await js();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=$G(t),o=await ui(n),s=await RG(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=GR();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 oO(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 js();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let w=(await ui(a)).find(h=>h.id===r);if(!w)throw new Error("unknown team");c={id:w.id,key:w.key,name:w.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let w=await pb(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=w.id,l.push({kind:"epic",title:w.name,url:w.url})}for(let w of i.issues||[])try{let h=await fb(a,{teamId:c.id,title:w.title,description:w.description,projectId:d,priority:w.priority});l.push({kind:"issue",key:h.key,title:h.title,url:h.url})}catch(h){u.push({title:w.title,message:h?.message||String(h)})}await N({id:e.id,cwd:e.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,team:{key:c.key,name:c.name},items:l,failed:u}}),await N({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",createdCount:l.length,failedCount:u.length}});let p=l.filter(w=>w.kind==="issue"),f=null;return i.linkCurrentTrack&&p.length===1&&!await ir(e.id)&&(f=await pi(e,p[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:f?f.key:null}}async function El(e,t){let n=await Hs(e);n&&await n.api.createComment(n.cfg,e,t)}async function OG(e,t,n){let r=await Hs(e);if(!r)return null;let o=await Mr(t);if(!o)return null;let s=$n(o,n);if(!s)return null;let a=await _l.readFile(dn(o,s.v).png).catch(()=>null);return a?r.api.uploadImage(r.cfg,{issue:e.key,filename:`${o.name.replace(/[^\w.-]+/g,"-")}.v${s.v}.png`,contentType:"image/png",buffer:a}):null}var UR=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),kb=new Map;function MG(e,t){if(UR()<=0)return Il(e,t);if(kb.has(e))return;let n=setTimeout(()=>{kb.delete(e),ir(e).then(r=>r?Il(e,r):null).catch(()=>{})},UR());n.unref?.(),kb.set(e,n)}var HR=new Map;async function WR(e,t,n){let r=HR.get(e)??0;Date.now()-r<600*1e3||(HR.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 CG(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 Ab())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 pi(s,o,{source:"chat"})}catch(a){await WR(e.trackId,s.cwd,a)}return}if(!n.track.cwd&&t){let o=await t(e.trackId).catch(()=>null);o?.cwd&&(n.track.cwd=o.cwd,await Oo(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Oo(e.trackId),await Il(e.trackId,n);break;case"pr.state.changed":{if(!r.change)break;if(r.number&&(n.pr={number:r.number,url:r.url??n.pr?.url??null,change:r.change}),await Oo(e.trackId),await Il(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await YR(n,e.trackId).catch(()=>null):null;await El(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
764
|
+
}`,{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 bG(e,t){let n=await sr(e,"query($id: String!) { issue(id: $id) { state { id name type } team { states { nodes { id name type position } } } } }",{id:t.key});if(!n.issue)throw new Error(`linear: issue "${t.key}" not found`);return{current:n.issue.state??null,states:n.issue.team?.states?.nodes??[]}}async function kG(e,t,n){if(!(await sr(e,"mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success } }",{id:t.issueId,input:{stateId:n}})).issueUpdate?.success)throw new Error("linear: issueUpdate did not succeed")}async function ui(e){return((await sr(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var MR=255;async function pb(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>MR,a=s?`${o.slice(0,MR-1).trimEnd()}\u2026`:o,i=await sr(e,"mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id url name } } }",{input:{teamIds:[t],name:n,...a?{description:a}:{},...s?{content:o}:{}}}),c=i.projectCreate?.project;if(!i.projectCreate?.success||!c)throw new Error("linear: projectCreate did not succeed");return{id:c.id,url:c.url,name:c.name}}async function fb(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await sr(e,"mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier url title } } }",{input:{teamId:t,title:n,...r?{description:r}:{},...o?{projectId:o}:{},...Number.isInteger(s)?{priority:s}:{}}}),i=a.issueCreate?.issue;if(!a.issueCreate?.success||!i)throw new Error("linear: issueCreate did not succeed");return{id:i.id,key:i.identifier,url:i.url,title:i.title}}Pc();import yf from"node:fs";import hb from"node:path";import xG from"node:os";var vG=()=>process.env.BOSTRAT_STATE_DIR||hb.join(process.env.HOME||xG.tmpdir(),".bostrat"),CR=()=>hb.join(vG(),"plane-link","public-host.json"),NR="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",di=null;function PR(){if(di)return di;try{let e=JSON.parse(yf.readFileSync(CR(),"utf8"));di={viewBase:typeof e?.viewBase=="string"&&e.viewBase?e.viewBase:null}}catch{di={viewBase:null}}return di}function DR(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 BR(e){let t=typeof e=="string"&&e.trim()?DR(e,"https").host:null;if(PR().viewBase===t)return;di={viewBase:t};let r=CR();try{t?(yf.mkdirSync(hb.dirname(r),{recursive:!0}),yf.writeFileSync(r,JSON.stringify({viewBase:t,learnedAt:new Date().toISOString()})+`
|
|
765
|
+
`)):yf.rmSync(r,{force:!0})}catch{}}function wb(){return PR().viewBase}function gb(e=process.env.BOSTRAT_PUBLIC_HOST){return DR(e,"https")}function LR(){let e=wb();if(e)return{proto:"https",host:e,source:"learned"};let t=gb();return t?{...t,source:"env"}:null}function bf(e,t){return e?`${e.proto}://${t}.${e.host}/`:null}function SG(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 jR(){let e=String(process.env.BOSTRAT_PUBLIC_URL||"").trim().replace(/\/+$/,"");if(e)return{url:e,source:"env"};let t=SG();return t?{url:t,source:"plane"}:null}var FR="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 zR={linear:{api:mb,config:js},jira:{api:ub,config:wf}};async function Ab(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(zR)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Hs(e){let t=zR[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function Tb(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 JR=()=>process.env.BOSTRAT_STATE_DIR||KR.join(process.env.HOME||AG.tmpdir(),".bostrat"),xb=()=>KR.join(JR(),"tickets.json"),$l=e=>{let t=jR();if(!t)throw new Error(FR);return`${t.url}/track/${e}`},TG=e=>`http://bostrat.globopi/track/${e}`,UR=e=>{let t=0;for(let[n,r]of Object.entries(e))r&&typeof r=="object"&&!r.cardUrl&&(r.cardUrl=TG(n),t++);return t},ar=null,yb=null,bb=Promise.resolve();async function Rl(){return ar||(yb??=_l.readFile(xb(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{ar=e;let n=UR(ar)>0;if(!t&&Ge()){let{records:r,source:o}=await dt("tickets").catch(()=>({records:[],source:"error"}));if(o==="plane"){for(let s of r){if(s.deletedAt||!s.data?.id)continue;let{id:a,updatedAt:i,...c}=s.data;ar[a]=c}UR(ar),n=n||Object.keys(ar).length>0}}return n&&await vb().catch(()=>{}),ar}).finally(()=>yb=null),yb)}async function vb(){let e=JSON.stringify({tickets:ar},null,2),t=`${xb()}.${process.pid}.tmp`;await _l.mkdir(JR(),{recursive:!0}),await _l.writeFile(t,e),await _l.rename(t,xb())}async function Oo(e=null){if(bb=bb.then(vb,vb),await bb.catch(()=>{}),!e)return;let t=ar?.[e];t?It("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):yn("tickets",e).catch(()=>{})}async function ir(e){return(await Rl())[e]??null}async function YR(){return{...await Rl()}}async function XR(){return(await Ab()).length>0}async function pi(e,t,{source:n="api"}={}){let r=await Ab();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:$l(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 Rl())[e.id]=c,await Oo(e.id),await N({id:e.id,cwd:e.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await Il(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 El(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await eO(e.id,c,"linked"),c}async function Eb(e){let t=await Rl();return t[e]?(delete t[e],await Oo(e),!0):!1}var Sb=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function ZR(e,t){if(!e.track?.cwd)return null;let n=po(await K({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function EG(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Sb(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${Sb(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function _G(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(Sb(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function Il(e,t){let n=await Hs(t);if(!n)return;let r=await ZR(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:t.cardUrl??$l(e),title:`Bostrat Track: ${t.track.name}`,subtitle:_G(t,r)})}function IG(e){return bf(LR(),e)}var kf=e=>IG(e)??"(no public URL on this node \u2014 link it to a plane)",$G={linked:"linked",opened:"prOpened",merged:"prMerged"};function RG(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 QR(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await Oo(e),await N({id:e,cwd:t.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:t.key,url:t.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function eO(e,t,n){let r=await Hs(t);if(!r)return;let o=r.cfg.ticketFlow?.[$G[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=Tb(s,a),c=RG(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 QR(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:VR(),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 tO(e){let t=await ir(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Hs(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=Tb(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await Oo(e)),{key:t.key,url:t.url,current:s?{id:s.id,name:s.name??null,type:s.type??null}:null,states:[...o].sort((a,i)=>a.position-i.position).map(({id:a,name:i,type:c,position:l})=>({id:a,name:i,type:c,position:l}))}}async function _b(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 Hs(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Tb(s,a),c=a.find(u=>u.id===t);if(!c)throw new Error("unknown state for this ticket's team");let l=i?.name??r.state??null;return i?.id===c.id?(r.state!==c.name&&(r.state=c.name,await Oo(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await QR(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function nO(e,t){let n=await ir(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Hs(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 rO(e,t,n){if(!t)throw new Error("offerId required");return vo(`tkt:${e.id}:${t}`,()=>OG(e,t,n))}async function OG(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 _b(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 HR=50;async function oO(){let e=await js();if(!e)throw new Error("no ticketing integration configured");return ui(e)}function MG(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>HR)throw new Error(`too many issues (${n.length}); cap is ${HR} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function CG(e,t,n,r){let o=r??await ui(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 sO(e,t){let n=await js();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=MG(t),o=await ui(n),s=await CG(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=VR();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 aO(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 js();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let w=(await ui(a)).find(h=>h.id===r);if(!w)throw new Error("unknown team");c={id:w.id,key:w.key,name:w.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let w=await pb(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=w.id,l.push({kind:"epic",title:w.name,url:w.url})}for(let w of i.issues||[])try{let h=await fb(a,{teamId:c.id,title:w.title,description:w.description,projectId:d,priority:w.priority});l.push({kind:"issue",key:h.key,title:h.title,url:h.url})}catch(h){u.push({title:w.title,message:h?.message||String(h)})}await N({id:e.id,cwd:e.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,team:{key:c.key,name:c.name},items:l,failed:u}}),await N({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",createdCount:l.length,failedCount:u.length}});let p=l.filter(w=>w.kind==="issue"),f=null;return i.linkCurrentTrack&&p.length===1&&!await ir(e.id)&&(f=await pi(e,p[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:f?f.key:null}}async function El(e,t){let n=await Hs(e);n&&await n.api.createComment(n.cfg,e,t)}async function NG(e,t,n){let r=await Hs(e);if(!r)return null;let o=await Mr(t);if(!o)return null;let s=$n(o,n);if(!s)return null;let a=await _l.readFile(dn(o,s.v).png).catch(()=>null);return a?r.api.uploadImage(r.cfg,{issue:e.key,filename:`${o.name.replace(/[^\w.-]+/g,"-")}.v${s.v}.png`,contentType:"image/png",buffer:a}):null}var WR=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),kb=new Map;function PG(e,t){if(WR()<=0)return Il(e,t);if(kb.has(e))return;let n=setTimeout(()=>{kb.delete(e),ir(e).then(r=>r?Il(e,r):null).catch(()=>{})},WR());n.unref?.(),kb.set(e,n)}var qR=new Map;async function GR(e,t,n){let r=qR.get(e)??0;Date.now()-r<600*1e3||(qR.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 DG(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 Ab())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 pi(s,o,{source:"chat"})}catch(a){await GR(e.trackId,s.cwd,a)}return}if(!n.track.cwd&&t){let o=await t(e.trackId).catch(()=>null);o?.cwd&&(n.track.cwd=o.cwd,await Oo(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Oo(e.trackId),await Il(e.trackId,n);break;case"pr.state.changed":{if(!r.change)break;if(r.number&&(n.pr={number:r.number,url:r.url??n.pr?.url??null,change:r.change}),await Oo(e.trackId),await Il(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await ZR(n,e.trackId).catch(()=>null):null;await El(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
766
766
|
|
|
767
|
-
Agent cost for this Track: ${
|
|
767
|
+
Agent cost for this Track: ${EG(o)}.`:"")),await eO(e.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await NG(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await El(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
768
768
|
|
|
769
769
|
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await El(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 El(n,`**Bostrat** \u2014 live preview **${r.name}**: ${kf(r.slug)}
|
|
770
|
-
_(runs on the Track's branch; a Bostrat-fronted preview, not a public deployment)_`);break}case"cost.recorded":await
|
|
771
|
-
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${$l(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Nb(e,t){await Ws(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:
|
|
770
|
+
_(runs on the Track's branch; a Bostrat-fronted preview, not a public deployment)_`);break}case"cost.recorded":await PG(e.trackId,n);break;default:break}}catch(o){let s=t?await t(e.trackId).catch(()=>null):null;await GR(e.trackId,s?.cwd??null,o)}}function iO({resolveTrack:e}){Ze.on("event",t=>{DG(t,{resolveTrack:e}).catch(()=>{})})}async function cO(){if(!Ge())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await dt("tickets");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Rl(),r=0;for(let[o,s]of Object.entries(n))await It("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}ws();Ka();import BG from"ws";var lO=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function xf(e,t,n={}){let r=await fetch(`${lO()}/${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 Ib=(e,t)=>xf("chat.postMessage",e,t),Ws=(e,t)=>xf("chat.update",e,t);async function Mt(e,t){await fetch(e,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(t)}).catch(()=>{})}async function LG(e,t,n={}){let r=await fetch(`${lO()}/${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 uO(e,{filename:t,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await LG("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 xf("files.completeUploadExternal",e,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var jG=2200;function dO({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 xf("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new BG(l),o.on("message",u=>{let d;try{d=JSON.parse(u)}catch{return}if(d.type==="hello"){a=1e3,n("socket connected");return}if(d.type==="disconnect"){try{o.close()}catch{}return}if(!d.envelope_id)return;let p=h=>{try{o.send(JSON.stringify(h?{envelope_id:d.envelope_id,payload:h}:{envelope_id:d.envelope_id}))}catch{}},f=!1,w=setTimeout(()=>{f=!0,p()},jG);Promise.resolve().then(()=>t(d)).then(h=>{clearTimeout(w),!f&&(f=!0,p(d.accepts_response_payload?h:void 0))},h=>{clearTimeout(w),f||(f=!0,p()),n(`envelope handler failed: ${h?.message||h}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var mO=()=>process.env.BOSTRAT_STATE_DIR||Sf.join(process.env.HOME||UG.tmpdir(),".bostrat"),Cb=()=>Sf.join(mO(),"slack.json"),vf=null,$b=null,Rb=Promise.resolve();async function Pb(){return vf||($b??=Ol.readFile(Cb(),"utf8").then(e=>JSON.parse(e)?.threads??{}).catch(()=>({})).then(e=>vf=e).finally(()=>$b=null),$b)}async function pO(){let e=JSON.stringify({threads:vf},null,2),t=`${Cb()}.${process.pid}.tmp`;await Ol.mkdir(mO(),{recursive:!0}),await Ol.writeFile(t,e),await Ol.rename(t,Cb())}async function Nn(){Rb=Rb.then(pO,pO),await Rb.catch(()=>{})}async function Db(e){return(await Pb())[e]??null}async function HG(e,t){let n=await Pb();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 Ml(e,t){let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function WG(e,t=500){let n=String(e??"").trim().split(/\n\s*\n/)[0]??"";return Ml(n.replace(/\n+/g," "),t)}function hO(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(Ml(e.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${t}
|
|
771
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${$l(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Nb(e,t){await Ws(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:hO(t)})}async function Co(e,t,n,r=void 0){return Ib(e.botToken,{channel:t.channel,thread_ts:t.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var fO=new Set;function Mo(e){fO.add(e),e.catch(()=>{}).finally(()=>fO.delete(e))}var qe=e=>({response_type:"ephemeral",text:e}),qG="*/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._",GG=["claude","cursor","codex"];function Ob(e,t){let n=(t||"").toLowerCase(),r=e.filter(s=>!s.archivedAt),o=r.filter(s=>s.name.toLowerCase().startsWith(n));return o.sort((s,a)=>(a.lastResponseAt??a.createdAt??0)-(s.lastResponseAt??s.createdAt??0)),{match:o[0]??null,live:r}}var Mb=(e,t)=>qe(`No live Track matches \u201C${$e(t)}\u201D. Live: ${e.map(n=>`\`${$e(n.name)}\``).join(" \xB7 ")||"none"}`);async function wO(e,t,{name:n,repo:r,responseUrl:o}){let s=e.deps;try{let a=(await s.listTracks()).filter(d=>!d.archivedAt);if(a.length>=t.maxConcurrent){await Mt(o,{...qe(`At capacity \u2014 ${a.length} live Tracks (limit ${t.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!t.channel){await Mt(o,{...qe("No designated channel configured \u2014 set `slack.channel` in integrations.json."),replace_original:!0});return}let i=await s.createTrack({name:n,...r?{repo:r}:{}}),c=i.workspace||{},l={trackId:i.id,channel:t.channel,name:i.name,branch:c.branch||null,repoLabel:c.repo||(c.base?Sf.basename(c.base):"fresh workspace"),lead:"claude"},u=await Ib(t.botToken,{channel:t.channel,text:`Track started: ${l.name}`,blocks:hO(l),unfurl_links:!1});l.ts=u.ts,(await Pb())[i.id]=l,await Nn(),await Mt(o,{...qe(`\u2713 *${$e(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await Mt(o,{...qe(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function KG(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.archiveTrack(n.id);let s=await Db(n.id);s&&(s.archivedAt=Date.now(),await Nn(),await Nb(t,s).catch(()=>{})),await Mt(r,{...qe(`\u2713 *${$e(n.name)}* archived.`),replace_original:!0})}catch(s){await Mt(r,{...qe(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function VG(e,t,{track:n,agent:r,responseUrl:o}){try{await e.deps.startLeadHandoff(n.id,r),await Mt(o,{...qe(`\u2713 Handing *${$e(n.name)}* to ${$e(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await Mt(o,{...qe(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function zG(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.interruptLead(n.id);let s=(o.listSubtaskJobs?.(n.id)??[]).filter(a=>!a.finishedAt);for(let a of s)try{o.cancelSubtask(a.id)}catch{}await Mt(r,{...qe(`\u2713 Stopped *${$e(n.name)}*${s.length?` (+ ${s.length} subtask${s.length>1?"s":""} canceled)`:""} \u2014 Track parked, resume from the Track page.`),replace_original:!0})}catch(s){await Mt(r,{...qe(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function JG(e,t){let n=[...t.slice(0,99).map(r=>({text:{type:"plain_text",text:Ml(`${r.name} (${r.branch})`,75)},value:r.path})),{text:{type:"plain_text",text:"fresh workspace"},value:"__fresh__"}];return[{type:"section",block_id:JSON.stringify({name:e}),text:{type:"mrkdwn",text:`Repo for *${$e(e)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function YG(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?Sf.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(`
|
|
772
772
|
`)+(o?`
|
|
773
|
-
_${o} archived_`:"")}async function
|
|
774
|
-
\u2713 *${$e(a)}*`}}]}).catch(()=>{})}}catch(c){await Mt(n.response_url,qe(`Couldn't answer: ${c?.message||c}`))}}async function
|
|
775
|
-
\u2713 _answered_`}}]})}async function
|
|
776
|
-
_runs on the Track's branch; a Bostrat-fronted preview, not a public deployment_`}}]);return}case"mock.state.changed":return
|
|
777
|
-
`)){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
|
|
778
|
-
`)}be();ow();import I6 from"node:fs/promises";import $6 from"node:path";Xu();nd();fo();var Tf=e=>e.payload?.ts??e.ts,v6=12e4,fn=e=>Math.round(e*1e4)/1e4,S6=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{},byTrack:new Map});function A6(e,{since:t}){let{track:n,events:r,brief:o}=e,s=x=>Tf(x)>=t,a=gr([fc(n?.name),fc(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 A=gr(x.payload.task);A&&i.set(x.boxId,A)}let c=new Map,l=x=>{let A=x||"unattributed";return c.has(A)||c.set(A,{segments:[u(null)],lastTs:null,provider:null}),c.get(A)},u=(x,{shape:A="initial",continuation:I=!1,text:M=null,ts:P=null}={})=>({textCat:x,shape:A,continuation:I,text:M,ts:P,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((x,A)=>Tf(x)-Tf(A)),p=0,f=0,w=0,h={turns:0};for(let x of d){let A=l(x.boxId),I=A.segments[A.segments.length-1],M=Tf(x);switch(A.lastTs!=null&&M>A.lastTs&&s(x)&&(I.activeMs+=Math.min(M-A.lastTs,v6)),A.lastTs=M,x.provider&&(A.provider=x.provider),x.type){case"turn.user":{let P=x.payload?.text;if(x.source==="hook"||!String(P||"").trim()||x.payload?.note||wc(P))break;let L=gr(P),T=!!x.payload?.kind,E=T||dv(P),j=u(L,{shape:T?"injected":E?"follow-up":"novel-ask",continuation:!L&&E,text:vO(P),ts:M});A.segments.push(j),s(x)&&(j.turns+=1);break}case"peer.message":{let P=x.payload?.text;if(!String(P||"").trim())break;let L=gr(P),T=u(L,{shape:"peer",continuation:!L,text:vO(P),ts:M});A.segments.push(T),s(x)&&(T.turns+=1);break}case"tool.called":{if(x.source==="transcript")break;let P=x.payload?.tool;P&&(I.tools[P]=(I.tools[P]||0)+1),s(x)&&(I.toolCalls+=1);break}case"cost.recorded":{if(!s(x))break;let{usd:P,tokens:L,model:T,unpriced:E}=sn(x);E&&(h.turns+=1),I.usd+=P,L&&(I.tokens.input+=L.input,I.tokens.output+=L.output,I.tokens.cacheRead+=L.cacheRead,I.tokens.cacheWrite+=L.cacheWrite),I.models[T]=(I.models[T]||0)+P,x.provider&&(I.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,A]of c){let I=null;for(let M of A.segments){let P=null,L=null;if(M.textCat)P=M.textCat.category,L="text";else if(i.get(x))P=i.get(x).category,L="boxTask";else if(M.continuation&&I)P=I,L="inherited";else{let R=uv(M.tools);R?(P=R.category,L="toolMix"):a&&(P=a.category,L="hint")}if(P&&(I=P),!(M.usd>0||M.activeMs>0||M.turns>0||M.toolCalls>0))continue;let E=P??"uncategorized";y.has(E)||y.set(E,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{}});let j=y.get(E);j.usd+=M.usd,j.activeMs+=M.activeMs,j.turns+=M.turns,j.toolCalls+=M.toolCalls,L&&(j.provenance[L]=(j.provenance[L]||0)+M.usd);for(let[R,S]of Object.entries(M.models))j.models[R]=(j.models[R]||0)+S;let G=M.provider||A.provider;G&&(j.providers[G]=(j.providers[G]||0)+M.usd),G==="cursor"&&M.usd===0&&(g=!0),P||(k.usd+=M.usd,k.turns+=M.turns,k.byShape[M.shape]=(k.byShape[M.shape]||0)+M.usd,M.usd>0&&M.text&&k.samples.push({trackId:n?.id,track:n?.name||n?.id,shape:M.shape,usd:fn(M.usd),ts:M.ts,text:M.text}))}}return{perCategory:y,uncategorized:k,handoffs:p,merged:f,failed:w,sawCursorActivity:g,unpricedTurns:h.turns}}function Lb(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:A,uncategorized:I,handoffs:M,merged:P,failed:L,sawCursorActivity:T,unpricedTurns:E}=A6(k,{since:r});T&&(i=!0),c+=E,l.usd+=I.usd,l.turns+=I.turns;for(let[G,R]of Object.entries(I.byShape))l.byShape[G]=(l.byShape[G]||0)+R;l.samples.push(...I.samples);let j={trackId:g?.id,name:g?.name||g?.id,archived:!!g?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:M,subtasksMerged:P,subtasksFailed:L};for(let[G,R]of A){j.usd+=R.usd,j.activeMs+=R.activeMs,j.turns+=R.turns,j.categories[G]={usd:fn(R.usd),activeMs:R.activeMs};for(let[H,B]of Object.entries(R.models))j.models[H]=fn((j.models[H]||0)+B);for(let[H,B]of Object.entries(R.providers))j.providers[H]=fn((j.providers[H]||0)+B);o.has(G)||o.set(G,S6());let S=o.get(G);S.usd+=R.usd,S.activeMs+=R.activeMs,S.turns+=R.turns,S.toolCalls+=R.toolCalls;for(let[H,B]of Object.entries(R.models))S.models[H]=(S.models[H]||0)+B;for(let[H,B]of Object.entries(R.providers))S.providers[H]=(S.providers[H]||0)+B;for(let[H,B]of Object.entries(R.provenance))S.provenance[H]=(S.provenance[H]||0)+B;let C=S.byTrack.get(g?.id)||{usd:0,activeMs:0};C.usd+=R.usd,C.activeMs+=R.activeMs,S.byTrack.set(g?.id,C)}(j.usd>0||j.activeMs>0||j.turns>0)&&(j.usd=fn(j.usd),j.dominantCategory=_6(j.categories),s.push(j))}let u=[...o.values()].reduce((k,g)=>k+g.usd,0),d=[...o.values()].reduce((k,g)=>k+g.activeMs,0),p=u>0?"usd":"time",f=p==="usd"?u:d,w=new Map((e||[]).map(k=>[k.track?.id,k.track?.name||k.track?.id])),h=[...o.entries()].map(([k,g])=>({category:k,sharePct:f>0?Math.round((p==="usd"?g.usd:g.activeMs)/f*100):0,shareBasis:p,usd:fn(g.usd),activeMs:g.activeMs,turns:g.turns,toolCalls:g.toolCalls,models:Nl(g.models),providers:Nl(g.providers),provenance:Nl(g.provenance),topWorkstreams:[...g.byTrack.entries()].sort((x,A)=>A[1].usd-x[1].usd||A[1].activeMs-x[1].activeMs).slice(0,2).map(([x,A])=>({trackId:x,name:w.get(x),usd:fn(A.usd),activeMs:A.activeMs}))})).sort((k,g)=>g.usd-k.usd||g.activeMs-k.activeMs);s.sort((k,g)=>g.usd-k.usd||g.activeMs-k.activeMs);let y={};for(let k of o.values())for(let[g,x]of Object.entries(k.provenance))y[g]=(y[g]||0)+x;return{since:r,capturedAt:n,totalUsd:fn(u),activeMs:d,turns:[...o.values()].reduce((k,g)=>k+g.turns,0),bullets:h,workstreams:s,provenance:Nl(y),gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c,uncategorized:{usd:fn(l.usd),turns:l.turns,sharePct:u>0?Math.round(l.usd/u*100):0,byShape:Nl(l.byShape),top:l.samples.sort((k,g)=>g.usd-k.usd).slice(0,T6)}}}}var T6=25,xO=160,vO=e=>{let t=String(e||"").replace(/\s+/g," ").trim();return t.length>xO?`${t.slice(0,xO).trimEnd()}\u2026`:t},E6=10;function SO(e,{thresholdPct:t=E6}={}){let n=e?.gaps?.uncategorized||{usd:0,turns:0},r=e?.totalUsd||0,o=n.usd||0,s=r>0?o/r*100:0,a=s<=t,i=a?[]:[`${s.toFixed(1)}% of the last 7d ($${o.toFixed(2)} of $${r.toFixed(2)}) is uncategorized (threshold ${t}%) \u2014 run \`npm run cost -- --uncategorized\` and add classifier table lines for the top asks`];return{ok:a,sharePct:Math.round(s*10)/10,thresholdPct:t,usd:fn(o),totalUsd:fn(r),turns:n.turns||0,inheritedPct:r>0?Math.round((e?.provenance?.inherited||0)/r*100):0,problems:i}}var _6=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},Nl=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,fn(n)]));var Gs={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function R6(e){let t=String(e||"7d");return t in Gs?{key:t,ms:Gs[t]}:{key:"7d",ms:Gs["7d"]}}function O6(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=Md(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 M6(e){if(!e?.cwd)return null;try{let n=(await I6.readFile($6.join(e.cwd,".bostrat","handoff","latest.md"),"utf8")).match(/^##\s*Objective\s*\n([\s\S]*?)(?:\n##\s|\n*$)/m);return n?n[1].trim().slice(0,1e3):null}catch{return null}}async function jb(e,{window:t="7d",from:n,to:r,now:o=Date.now()}={}){let s=Number(n),a=Number(r),i=Number.isFinite(s)&&s>0,c=i&&Number.isFinite(a)&&a>s?Math.min(a,o):o,{key:l,ms:u}=i?{key:"custom",ms:c-s}:R6(t),d=[];for(let w of e||[]){let h=await K(w),y=O6(w,h);i&&c<o&&(y=y.filter(k=>(k.ts??0)<=c)),d.push({track:w,events:y,brief:await M6(w)})}let p=Lb(d,{windowMs:u,now:c}),f=l==="7d"&&c===o?p:Lb(d,{windowMs:Gs["7d"],now:o});return{window:l,...p,categorization:SO(f)}}var Pl=e=>`$${Number(e||0).toFixed(2)}`,AO=e=>{let t=Math.round((e||0)/6e4);return t>=60?`${Math.floor(t/60)}h${String(t%60).padStart(2,"0")}`:`${t}m`},qs=(e,t)=>String(e).padEnd(t);function TO(e){let t=[];t.push(`USAGE ROUNDUP \xB7 ${e.window} \xB7 ${Pl(e.totalUsd)} \xB7 ${AO(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(` ${qs(`${a.shareBasis==="time"?"~":""}${a.sharePct}%`,5)} ${qs(a.category,15)} ${qs(Pl(a.usd),10)} ${qs(AO(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 ${Pl(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} ${Pl(i)}`).join(" \xB7 ")||"none")),s.top?.length||t.push(" (nothing unmatched carried cost in this window)");for(let a of s.top||[])t.push(` ${qs(Pl(a.usd),8)} ${qs(a.shape,10)} ${qs(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(`
|
|
779
|
-
`)}yr();var Po=36e5
|
|
780
|
-
`).slice(-3).join(" | ").slice(0,300);d!==Ub&&(Ub=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)}}}Ng();vn();Qo();pt();_i();import{execFile as
|
|
781
|
-
`).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
|
|
782
|
-
`).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
|
|
783
|
-
`),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 jl(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=jl(r);[c.primary,c.secondary].forEach((f,w)=>{let h=
|
|
784
|
-
`,{mode:384}),Mf.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function Fl(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
|
|
773
|
+
_${o} archived_`:"")}async function XG(e,t){let n=await Fs();if(!n)return qe("Slack integration not configured.");let r=e.deps,o=(t.text||"").trim().split(/\s+/).filter(Boolean),s=(o[0]||"help").toLowerCase(),a=o.slice(1);switch(s){case"start":{if(!a.length)return qe("Usage: `/bostrat start <name> [repo]`");let i=await r.listRepos(),c=a[a.length-1]?.toLowerCase(),l=a.length>1?i.find(d=>d.name.toLowerCase()===c):null,u=(l?a.slice(0,-1):a).join(" ");return l?(Mo(wO(e,n,{name:u,repo:l.path,responseUrl:t.response_url})),qe(`Starting *${$e(u)}* on ${$e(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:JG(u,i)}}case"archive":{if(!a.length)return qe("Usage: `/bostrat archive <track>`");let{match:i,live:c}=Ob(await r.listTracks(),a.join(" "));return i?(Mo(KG(e,n,{track:i,responseUrl:t.response_url})),qe(`Archiving *${$e(i.name)}*\u2026`)):Mb(c,a.join(" "))}case"handoff":{let i=r.knownAgents??GG,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return qe(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:d}=Ob(await r.listTracks(),l);return u?(Mo(VG(e,n,{track:u,agent:c,responseUrl:t.response_url})),qe(`Handing *${$e(u.name)}* to ${$e(c)}\u2026`)):Mb(d,l)}case"stop":{if(!a.length)return qe("Usage: `/bostrat stop <track>`");let{match:i,live:c}=Ob(await r.listTracks(),a.join(" "));return i?(Mo(zG(e,n,{track:i,responseUrl:t.response_url})),qe(`Stopping *${$e(i.name)}*\u2026`)):Mb(c,a.join(" "))}case"list":return qe(await YG(r));default:return qe(qG)}}async function ZG(e,t,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Db(o);try{if(await e.deps.answerPrompt(o,{promptId:s,answers:[a]}),i?.prompt?.promptId===s){let{ts:c,question:l}=i.prompt;delete i.prompt,await Nn(),await Ws(t.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${$e(l||"question")}~
|
|
774
|
+
\u2713 *${$e(a)}*`}}]}).catch(()=>{})}}catch(c){await Mt(n.response_url,qe(`Couldn't answer: ${c?.message||c}`))}}async function QG(e,t,n,r){let{trackId:o,offerId:s,action:a}=r,i=e.deps;try{let c=await i.resolveTrack(o);if(!c)throw new Error("track not found");let l=await i.readEvents(c),u=l.find(p=>p.type==="pr.offered"&&p.payload?.offerId===s);if(!u)throw new Error("unknown offer");if(l.some(p=>p.type==="pr.offer.answered"&&p.payload?.offerId===s))throw new Error("offer already answered");let d=Array.isArray(r.actions)?ho.filter(p=>r.actions.includes(p)):a==="pr"?["commit","push","pr"]:a==="push"?["commit","push"]:null;if(d?.includes("pr")&&i.divertShip&&await i.divertShip(c,u,{actions:d}))return;d&&await i.sayToLead(c.id,Ma(d,{branch:u.payload.branch,base:u.payload.base}),{kind:d.includes("pr")?"pr-go":"push-go"}),await i.appendEvent({id:c.id,cwd:c.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:u.boxId,payload:{ts:Date.now(),offerId:s,action:d?a:"dismiss",actions:d}})}catch(c){await Mt(n.response_url,qe(`Couldn't act on the offer: ${c?.message||c}`))}}async function e6(e,t,n,r){let{trackId:o,offerId:s,choice:a}=r;try{let i=await e.deps.resolveTrack(o);if(!i)throw new Error("track not found");await e.deps.answerPrExists(i,{offerId:s,choice:a})}catch(i){await Mt(n.response_url,qe(`Couldn't act on the offer: ${i?.message||i}`))}}async function t6(e,t){let n=t.actions?.[0];if(!n?.action_id)return;let r=await Fs();if(!r)return;if(n.action_id==="bostrat-start-repo"){let s;try{({name:s}=JSON.parse(n.block_id))}catch{return}let a=n.selected_option?.value;await Mt(t.response_url,{...qe(`Starting *${$e(s)}*\u2026`),replace_original:!0}),Mo(wO(e,r,{name:s,repo:a==="__fresh__"?null:a,responseUrl:t.response_url}));return}let o;try{o=JSON.parse(n.value)}catch{return}n.action_id==="bostrat-prompt"?Mo(ZG(e,r,t,o)):n.action_id==="bostrat-ship"?Mo(QG(e,r,t,o)):n.action_id==="bostrat-pr-exists"&&Mo(e6(e,r,t,o))}async function n6(e,t){if(!t||t.type!=="message"||t.bot_id||t.subtype||!t.thread_ts||t.thread_ts===t.ts||!t.text?.trim())return;let n=await HG(t.channel,t.thread_ts);if(!n)return;let r=await Fs();if(r)try{await e.deps.sayToLead(n.trackId,t.text.trim())}catch(o){await Co(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function r6(e){return async t=>{if(t.type==="slash_commands")return XG(e,t.payload);if(t.type==="interactive"){await t6(e,t.payload);return}if(t.type==="events_api"){await n6(e,t.payload?.event);return}}}var o6=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 s6(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:Ml(String(o),75)},value:JSON.stringify({trackId:e,promptId:t.promptId,label:String(o)})}))}):n.push({type:"context",elements:[{type:"mrkdwn",text:"_reply in this thread to answer_"}]}),n}async function a6(e,t,n){let r=n.payload||{};if(!r.promptId||t.prompt?.promptId===r.promptId)return;t.prompt&&await Ws(e.botToken,{channel:t.channel,ts:t.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${$e(t.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await Co(e,t,r.question||"The lead needs an answer",s6(n.trackId,r));t.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await Nn()}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 Nn(),await Ws(e.botToken,{channel:t.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${$e(s||"question")}~
|
|
775
|
+
\u2713 _answered_`}}]})}async function c6(e,t,n){let r=n.payload||{};if(!r.offerId||t.offers?.[r.offerId])return;let o=(i,c,l,u)=>({type:"button",action_id:"bostrat-ship",text:{type:"plain_text",text:c},value:JSON.stringify({trackId:n.trackId,offerId:r.offerId,action:i,...u?{actions:u}:{}}),...l?{style:l}:{}}),s;if(!Array.isArray(r.available))s=r.toPr?[o("push",`Commit & push${r.toPr.number?` to #${r.toPr.number}`:""}`,"primary"),o("dismiss","Not now")]:[o("pr","Open PR","primary"),o("push","Just push"),o("dismiss","Not now")];else{let i=r.available,c=Array.isArray(r.defaults)&&r.defaults.length?r.defaults:i,l={commit:"Commit",rebase:"rebase",push:"push",pr:r.prState==="open"?"open new PR":"open PR"},u=c.map(d=>l[d]).filter(Boolean).join(" & ");s=[o(c.includes("pr")?"pr":"push",u||"Ship","primary",c)],i.includes("pr")&&!c.includes("pr")&&s.push(o("pr","Open new PR",void 0,["pr"])),s.push(o("dismiss","Not now"))}let a=await Co(e,t,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${$e(Ml(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await Nn()}async function l6(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await Nn();let s=r.action==="dismiss"?"dismissed":r.action==="pr-exists"?`PR${r.existingPr?.number?` #${r.existingPr.number}`:""} already open \u2014 pick how to ship`:r.action==="pr"?"\u2713 opening a PR":"\u2713 pushing";await Ws(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function u6(e,t,n){let r=n.payload||{};if(!r.offerId||t.offers?.[r.offerId])return;let o=r.pr?.number?`#${r.pr.number}`:"a PR",s=(i,c,l)=>({type:"button",action_id:"bostrat-pr-exists",text:{type:"plain_text",text:c},value:JSON.stringify({trackId:n.trackId,offerId:r.offerId,choice:i}),...l?{style:l}:{}}),a=await Co(e,t,`PR ${o} already open \u2014 push or new PR?`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${o}${r.branch?` (${$e(r.branch)})`:""} is already open. Push the update to it, or open a new PR?`}},{type:"actions",elements:[s("push",`Push to ${o}`,"primary"),s("new","Open a new PR"),s("dismiss","Not now")]}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await Nn()}async function d6(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await Nn();let s=r.choice==="dismiss"?"dismissed":r.choice==="new"?"\u2713 opening a new PR":"\u2713 pushing to the open PR";await Ws(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function p6(e,t,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??f6)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await uO(e.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:t.channel,threadTs:t.ts,initialComment:`\u{1F3A8} mock *${$e(o.name)}*${a}${o.group?` (group \`${$e(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${$e(a.name)}*`).join(", ");await Co(e,t,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${$e(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function f6(e,t){let n=await Mr(e);if(!n)return null;let r=$n(n,t);if(!r)return null;let o=await Ol.readFile(dn(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function m6(e,t){if(!o6.has(t.type))return;let n=await Db(t.trackId);if(!n)return;let r=await Fs();if(!r)return;let o=t.payload||{};switch(t.type){case"turn.user":if(n.kickoff||t.actor!=="user"||!o.text)return;n.kickoff=o.text,await Nn(),await Nb(r,n);return;case"turn.agent":{if(!o.text)return;let s=WG(o.text);if(!s)return;await Co(r,n,s,[{type:"section",text:{type:"mrkdwn",text:$e(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${$l(t.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return a6(r,n,t);case"prompt.answered":return i6(r,n,t);case"pr.offered":return c6(r,n,t);case"pr.offer.answered":return l6(r,n,t);case"pr.exists.offered":return u6(r,n,t);case"pr.exists.answered":return d6(r,n,t);case"pr.state.changed":{if(o.change!=="opened"&&o.change!=="merged")return;let s=o.change==="opened"?"PR opened":"PR merged";await Co(r,n,`${s}${o.url?`: ${o.url}`:o.number?` (#${o.number})`:""}`);return}case"handoff.done":if(!o.toAgent||o.toAgent===n.lead)return;n.lead=o.toAgent,await Nn(),await Nb(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await Co(r,n,`Live preview ${o.name}: ${kf(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${$e(o.name)}*: ${kf(o.slug)}
|
|
776
|
+
_runs on the Track's branch; a Bostrat-fronted preview, not a public deployment_`}}]);return}case"mock.state.changed":return p6(r,n,t,e.deps)}}function gO(e){let t={deps:e};Ze.on("event",s=>{m6(t,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await Fs()||(r&&clearInterval(r),r=null,n=dO({getToken:async()=>(await Fs())?.appToken,onEnvelope:r6(t),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}vn();be();Kg();bn();Cw();yr();import h6 from"node:fs/promises";function w6(e){let t=new Map;for(let n of String(e||"").split(`
|
|
777
|
+
`)){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 g6(e){let t=new Set;for(let n of e)if(n.type==="cost.recorded"){n.payload?.messageId&&t.add(n.payload.messageId);for(let r of n.payload?.messageIds||[])t.add(r)}return t}var y6=new Set(["Write","Edit","NotebookEdit"]),b6=220,k6=160;function x6(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(y6.has(n)&&typeof o.file_path=="string"&&o.file_path){let s=o.file_path,a=t?.cwd;a&&s.startsWith(`${a}/`)&&(s=s.slice(a.length+1)),r.path=s.slice(0,b6)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=Vo(o.command.trim().replace(/\s+/g," ")).slice(0,k6));return r}function v6(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?xn(t.cwd,t.sessionId):t?.cwd?Cm(t.cwd):null}async function bO(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 Ja(o?.cwd)}});break;case"UserPromptSubmit":{let c=String(e.prompt??e.user_prompt??""),l=ap(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:x6(e,o)});break;case"Stop":await S6(r,o,e,i);break;default:break}return a}async function S6(e,t,n,r){let o=[],s=v6(n,t);if(s)try{o=w6(await h6.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=g6(i),l=o.filter(p=>!c.has(p.messageId));if(l.length){let p=new Map;for(let f of l){let w=`${f.model||"unknown"}\0${f.speed||""}`,h=p.get(w)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},cacheWriteTtl:null,messageIds:[]};for(let y of Object.keys(h.tokens))h.tokens[y]+=f.tokens[y];f.cacheWriteTtl&&(h.cacheWriteTtl??={m5:0,h1:0},h.cacheWriteTtl.m5+=f.cacheWriteTtl.m5,h.cacheWriteTtl.h1+=f.cacheWriteTtl.h1),h.messageIds.push(f.messageId),h.speed=f.speed||null,p.set(w,h)}for(let[f,w]of p){let h=f.split("\0")[0];await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:h,usd:Wn(h,w.tokens,void 0,w.speed?{speed:w.speed}:void 0),tokens:w.tokens,...w.speed?{speed:w.speed}:{},cacheWriteTtl:w.cacheWriteTtl,messageId:w.messageIds[w.messageIds.length-1],messageIds:w.messageIds,apiCalls:w.messageIds.length}})}}let u=yO(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:yO(d),tokens:d.tokens}:null,calls:l.length,turnIndex:A6(i,t)}})}var yO=e=>e.tokens.input+e.tokens.cacheWrite+e.tokens.cacheRead;function A6(e,t){if(!t?.name)return null;let n=Ni(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}Js();Tm();op();be();be();fo();be();fo();yr();function Cl(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 kO=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),xO=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},Af=e=>Math.round(e*1e4)/1e4;async function Bb(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=kO(),d=0,p=[];for(let f of e||[]){let w=await K(f),h=po(w,{boxLabels:Cl(f)});if(h.turns===0){f.archivedAt?d++:p.push(f.name||f.id);continue}o+=h.totalUsd,s+=h.turns,a+=h.repricedTurns||0,i+=h.asRecordedUsd||0,c+=h.unpricedTurns||0;for(let y of h.unpricedModels||[])l.add(y);xO(u,h.totalTokens);for(let[y,k]of Object.entries(h.byModel))n[y]||={usd:0,events:0,tokens:kO()},n[y].usd=Af(n[y].usd+k.usd),n[y].events+=k.events,xO(n[y].tokens,k.tokens);for(let[y,k]of Object.entries(h.byProvider))r[y]||={usd:0,events:0},r[y].usd=Af(r[y].usd+k.usd),r[y].events+=k.events;t.push({id:f.id,name:f.name,usd:h.totalUsd,rollup:h})}return t.sort((f,w)=>w.usd-f.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:Ta(),fleet:{totalUsd:Af(o),asRecordedUsd:Af(i),turns:s,repricedTurns:a,unpricedTurns:c,unpricedModels:[...l],totalTokens:u,byModel:n,byProvider:r,tracksWithCost:t.length,tracksSilent:d+p.length,tracksSilentArchived:d,tracksSilentLive:p},tracks:t}}var No=e=>`$${e.toFixed(4)}`,fi=(e,t)=>String(e).padEnd(t);function vO(e){let{fleet:t,tracks:n}=e,r=[];r.push(`BOSTRAT COST READ-OUT \xB7 ${e.generatedAt}`),e.pricingBasis&&r.push(`basis: ${e.pricingBasis}`);let o=t.tracksSilentLive||[];if(r.push(`fleet total ${No(t.totalUsd)} over ${t.turns} turn(s) \xB7 ${t.tracksWithCost} track(s) with cost, ${t.tracksSilentArchived??t.tracksSilent} silent (archived)`+(o.length?`, ${o.length} LIVE with no cost data: ${o.join(", ")}`:"")),t.repricedTurns&&r.push(`${t.repricedTurns} turn(s) priced at current table rates (differs from as-recorded)`),t.asRecordedUsd!=null&&Math.abs(t.asRecordedUsd-t.totalUsd)>=.005&&r.push(`as-recorded total ${No(t.asRecordedUsd)} \xB7 current-rates total ${No(t.totalUsd)} (${t.totalUsd>=t.asRecordedUsd?"+":""}${No(t.totalUsd-t.asRecordedUsd)})`),t.unpricedTurns&&r.push(`\u26A0 ${t.unpricedTurns} turn(s) UNPRICED \u2014 total under-reports. Add PER_MTOK rate(s) for: ${(t.unpricedModels||[]).join(", ")}`),e.pricing){let a=e.pricing;r.push(`rates verified against vendor pages: ${a.tableVerifiedAt??"never"}`+(a.upcomingChanges?.length?` \xB7 scheduled: ${a.upcomingChanges.map(i=>`${i.model} ${i.from}`).join(", ")}`:""));for(let i of a.problems||[])r.push(`\u26A0 RATE TABLE: ${i}`)}r.push(""),r.push("BY MODEL");let s=Object.entries(t.byModel).sort((a,i)=>i[1].usd-a[1].usd);s.length||r.push(" (no cost events yet \u2014 hooks emit on each turn of a newly started box)");for(let[a,i]of s){let c=i.tokens;r.push(` ${fi(a,26)} ${fi(No(i.usd),10)} ${i.events} turn(s) \xB7 in ${c.input} out ${c.output} cacheR ${c.cacheRead} cacheW ${c.cacheCreation}`)}r.push(""),r.push("BY TRACK / TASK");for(let a of n){r.push(` ${fi(a.name||a.id,30)} ${No(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${fi(c.label,40)} ${fi(No(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((d,p)=>Number(d[0])-Number(p[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${fi(No(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
778
|
+
`)}be();ow();import O6 from"node:fs/promises";import M6 from"node:path";Xu();nd();fo();var Tf=e=>e.payload?.ts??e.ts,T6=12e4,fn=e=>Math.round(e*1e4)/1e4,E6=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{},byTrack:new Map});function _6(e,{since:t}){let{track:n,events:r,brief:o}=e,s=x=>Tf(x)>=t,a=gr([fc(n?.name),fc(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let x of r)if(x.type==="subtask.delegated"&&x.boxId&&x.payload?.task){let S=gr(x.payload.task);S&&i.set(x.boxId,S)}let c=new Map,l=x=>{let S=x||"unattributed";return c.has(S)||c.set(S,{segments:[u(null)],lastTs:null,provider:null}),c.get(S)},u=(x,{shape:S="initial",continuation:_=!1,text:M=null,ts:P=null}={})=>({textCat:x,shape:S,continuation:_,text:M,ts:P,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((x,S)=>Tf(x)-Tf(S)),p=0,f=0,w=0,h={turns:0};for(let x of d){let S=l(x.boxId),_=S.segments[S.segments.length-1],M=Tf(x);switch(S.lastTs!=null&&M>S.lastTs&&s(x)&&(_.activeMs+=Math.min(M-S.lastTs,T6)),S.lastTs=M,x.provider&&(S.provider=x.provider),x.type){case"turn.user":{let P=x.payload?.text;if(x.source==="hook"||!String(P||"").trim()||x.payload?.note||wc(P))break;let j=gr(P),T=!!x.payload?.kind,$=T||dv(P),L=u(j,{shape:T?"injected":$?"follow-up":"novel-ask",continuation:!j&&$,text:AO(P),ts:M});S.segments.push(L),s(x)&&(L.turns+=1);break}case"peer.message":{let P=x.payload?.text;if(!String(P||"").trim())break;let j=gr(P),T=u(j,{shape:"peer",continuation:!j,text:AO(P),ts:M});S.segments.push(T),s(x)&&(T.turns+=1);break}case"tool.called":{if(x.source==="transcript")break;let P=x.payload?.tool;P&&(_.tools[P]=(_.tools[P]||0)+1),s(x)&&(_.toolCalls+=1);break}case"cost.recorded":{if(!s(x))break;let{usd:P,tokens:j,model:T,unpriced:$}=sn(x);$&&(h.turns+=1),_.usd+=P,j&&(_.tokens.input+=j.input,_.tokens.output+=j.output,_.tokens.cacheRead+=j.cacheRead,_.tokens.cacheWrite+=j.cacheWrite),_.models[T]=(_.models[T]||0)+P,x.provider&&(_.provider=x.provider);break}case"handoff.done":s(x)&&(p+=1);break;case"subtask.merged":s(x)&&(f+=1);break;case"error":s(x)&&x.payload?.kind==="subtask-failed"&&(w+=1);break;default:break}}let y=new Map,k={usd:0,turns:0,byShape:{},samples:[]},g=!1;for(let[x,S]of c){let _=null;for(let M of S.segments){let P=null,j=null;if(M.textCat)P=M.textCat.category,j="text";else if(i.get(x))P=i.get(x).category,j="boxTask";else if(M.continuation&&_)P=_,j="inherited";else{let R=uv(M.tools);R?(P=R.category,j="toolMix"):a&&(P=a.category,j="hint")}if(P&&(_=P),!(M.usd>0||M.activeMs>0||M.turns>0||M.toolCalls>0))continue;let $=P??"uncategorized";y.has($)||y.set($,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},provenance:{}});let L=y.get($);L.usd+=M.usd,L.activeMs+=M.activeMs,L.turns+=M.turns,L.toolCalls+=M.toolCalls,j&&(L.provenance[j]=(L.provenance[j]||0)+M.usd);for(let[R,A]of Object.entries(M.models))L.models[R]=(L.models[R]||0)+A;let H=M.provider||S.provider;H&&(L.providers[H]=(L.providers[H]||0)+M.usd),H==="cursor"&&M.usd===0&&(g=!0),P||(k.usd+=M.usd,k.turns+=M.turns,k.byShape[M.shape]=(k.byShape[M.shape]||0)+M.usd,M.usd>0&&M.text&&k.samples.push({trackId:n?.id,track:n?.name||n?.id,shape:M.shape,usd:fn(M.usd),ts:M.ts,text:M.text}))}}return{perCategory:y,uncategorized:k,handoffs:p,merged:f,failed:w,sawCursorActivity:g,unpricedTurns:h.turns}}function Lb(e,{windowMs:t=null,now:n=Date.now()}={}){let r=t==null?0:n-t,o=new Map,s=[],a=0,i=!1,c=0,l={usd:0,turns:0,byShape:{},samples:[]};for(let k of e||[]){let{track:g,events:x}=k;if(!x?.length){a+=1;continue}let{perCategory:S,uncategorized:_,handoffs:M,merged:P,failed:j,sawCursorActivity:T,unpricedTurns:$}=_6(k,{since:r});T&&(i=!0),c+=$,l.usd+=_.usd,l.turns+=_.turns;for(let[H,R]of Object.entries(_.byShape))l.byShape[H]=(l.byShape[H]||0)+R;l.samples.push(..._.samples);let L={trackId:g?.id,name:g?.name||g?.id,archived:!!g?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:M,subtasksMerged:P,subtasksFailed:j};for(let[H,R]of S){L.usd+=R.usd,L.activeMs+=R.activeMs,L.turns+=R.turns,L.categories[H]={usd:fn(R.usd),activeMs:R.activeMs};for(let[W,B]of Object.entries(R.models))L.models[W]=fn((L.models[W]||0)+B);for(let[W,B]of Object.entries(R.providers))L.providers[W]=fn((L.providers[W]||0)+B);o.has(H)||o.set(H,E6());let A=o.get(H);A.usd+=R.usd,A.activeMs+=R.activeMs,A.turns+=R.turns,A.toolCalls+=R.toolCalls;for(let[W,B]of Object.entries(R.models))A.models[W]=(A.models[W]||0)+B;for(let[W,B]of Object.entries(R.providers))A.providers[W]=(A.providers[W]||0)+B;for(let[W,B]of Object.entries(R.provenance))A.provenance[W]=(A.provenance[W]||0)+B;let C=A.byTrack.get(g?.id)||{usd:0,activeMs:0};C.usd+=R.usd,C.activeMs+=R.activeMs,A.byTrack.set(g?.id,C)}(L.usd>0||L.activeMs>0||L.turns>0)&&(L.usd=fn(L.usd),L.dominantCategory=R6(L.categories),s.push(L))}let u=[...o.values()].reduce((k,g)=>k+g.usd,0),d=[...o.values()].reduce((k,g)=>k+g.activeMs,0),p=u>0?"usd":"time",f=p==="usd"?u:d,w=new Map((e||[]).map(k=>[k.track?.id,k.track?.name||k.track?.id])),h=[...o.entries()].map(([k,g])=>({category:k,sharePct:f>0?Math.round((p==="usd"?g.usd:g.activeMs)/f*100):0,shareBasis:p,usd:fn(g.usd),activeMs:g.activeMs,turns:g.turns,toolCalls:g.toolCalls,models:Nl(g.models),providers:Nl(g.providers),provenance:Nl(g.provenance),topWorkstreams:[...g.byTrack.entries()].sort((x,S)=>S[1].usd-x[1].usd||S[1].activeMs-x[1].activeMs).slice(0,2).map(([x,S])=>({trackId:x,name:w.get(x),usd:fn(S.usd),activeMs:S.activeMs}))})).sort((k,g)=>g.usd-k.usd||g.activeMs-k.activeMs);s.sort((k,g)=>g.usd-k.usd||g.activeMs-k.activeMs);let y={};for(let k of o.values())for(let[g,x]of Object.entries(k.provenance))y[g]=(y[g]||0)+x;return{since:r,capturedAt:n,totalUsd:fn(u),activeMs:d,turns:[...o.values()].reduce((k,g)=>k+g.turns,0),bullets:h,workstreams:s,provenance:Nl(y),gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c,uncategorized:{usd:fn(l.usd),turns:l.turns,sharePct:u>0?Math.round(l.usd/u*100):0,byShape:Nl(l.byShape),top:l.samples.sort((k,g)=>g.usd-k.usd).slice(0,I6)}}}}var I6=25,SO=160,AO=e=>{let t=String(e||"").replace(/\s+/g," ").trim();return t.length>SO?`${t.slice(0,SO).trimEnd()}\u2026`:t},$6=10;function TO(e,{thresholdPct:t=$6}={}){let n=e?.gaps?.uncategorized||{usd:0,turns:0},r=e?.totalUsd||0,o=n.usd||0,s=r>0?o/r*100:0,a=s<=t,i=a?[]:[`${s.toFixed(1)}% of the last 7d ($${o.toFixed(2)} of $${r.toFixed(2)}) is uncategorized (threshold ${t}%) \u2014 run \`npm run cost -- --uncategorized\` and add classifier table lines for the top asks`];return{ok:a,sharePct:Math.round(s*10)/10,thresholdPct:t,usd:fn(o),totalUsd:fn(r),turns:n.turns||0,inheritedPct:r>0?Math.round((e?.provenance?.inherited||0)/r*100):0,problems:i}}var R6=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},Nl=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,fn(n)]));var Gs={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function C6(e){let t=String(e||"7d");return t in Gs?{key:t,ms:Gs[t]}:{key:"7d",ms:Gs["7d"]}}function N6(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=Md(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 P6(e){if(!e?.cwd)return null;try{let n=(await O6.readFile(M6.join(e.cwd,".bostrat","handoff","latest.md"),"utf8")).match(/^##\s*Objective\s*\n([\s\S]*?)(?:\n##\s|\n*$)/m);return n?n[1].trim().slice(0,1e3):null}catch{return null}}async function jb(e,{window:t="7d",from:n,to:r,now:o=Date.now()}={}){let s=Number(n),a=Number(r),i=Number.isFinite(s)&&s>0,c=i&&Number.isFinite(a)&&a>s?Math.min(a,o):o,{key:l,ms:u}=i?{key:"custom",ms:c-s}:C6(t),d=[];for(let w of e||[]){let h=await K(w),y=N6(w,h);i&&c<o&&(y=y.filter(k=>(k.ts??0)<=c)),d.push({track:w,events:y,brief:await P6(w)})}let p=Lb(d,{windowMs:u,now:c}),f=l==="7d"&&c===o?p:Lb(d,{windowMs:Gs["7d"],now:o});return{window:l,...p,categorization:TO(f)}}var Pl=e=>`$${Number(e||0).toFixed(2)}`,EO=e=>{let t=Math.round((e||0)/6e4);return t>=60?`${Math.floor(t/60)}h${String(t%60).padStart(2,"0")}`:`${t}m`},qs=(e,t)=>String(e).padEnd(t);function _O(e){let t=[];t.push(`USAGE ROUNDUP \xB7 ${e.window} \xB7 ${Pl(e.totalUsd)} \xB7 ${EO(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(` ${qs(`${a.shareBasis==="time"?"~":""}${a.sharePct}%`,5)} ${qs(a.category,15)} ${qs(Pl(a.usd),10)} ${qs(EO(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 ${Pl(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} ${Pl(i)}`).join(" \xB7 ")||"none")),s.top?.length||t.push(" (nothing unmatched carried cost in this window)");for(let a of s.top||[])t.push(` ${qs(Pl(a.usd),8)} ${qs(a.shape,10)} ${qs(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(`
|
|
779
|
+
`)}yr();var Po=36e5,OO=24*Po,D6=400,B6=12,Ks=e=>Math.round(e*1e4)/1e4;function Fb({window:e,from:t,to:n,now:r=Date.now()}={}){let o=Number(t),s=Number(n);if(Number.isFinite(o)&&o>0){let c=Number.isFinite(s)&&s>o?Math.min(s,r):r;return{key:"custom",since:o,until:c}}let a=String(e||"7d")in Gs?String(e||"7d"):"7d",i=Gs[a];return{key:a,since:i==null?null:r-i,until:r}}var L6=(e,t)=>e!=null&&t-e<=48*Po?Po:OO;function IO(e,t){if(t===Po)return Math.floor(e/Po)*Po;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function j6(e,t){if(t===Po)return e+Po;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var $O=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),RO=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},F6=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Ks(n)]));async function MO(e,t={}){let n=t.now??Date.now(),r=Fb({...t,now:n}),o=r.since==null?OO:L6(r.since,r.until),s=T=>(r.since==null||T>=r.since)&&T<r.until,a=r.since!=null?r.until-r.since:null,i=T=>a!=null&&T>=r.since-a&&T<r.since,c={},l={},u=$O(),d=0,p=0,f=0,w=0,h=0,y=0,k=new Set,g=new Map,x=[],S=[],_=[],M=null;for(let T of e||[]){let L=(await K(T)).filter(A=>A.type==="cost.recorded");if(!L.length){T.archivedAt||_.push(T.name||T.id);continue}let H=[];for(let A of L){let C=A.ts??0;s(C)?H.push(A):i(C)&&(y+=sn(A).usd)}if(!H.length)continue;let R=po(H,{boxLabels:Cl(T)});d+=R.totalUsd,p+=R.turns,f+=R.repricedTurns||0,w+=R.asRecordedUsd||0,h+=R.unpricedTurns||0;for(let A of R.unpricedModels||[])k.add(A);RO(u,R.totalTokens);for(let[A,C]of Object.entries(R.byModel))c[A]||={usd:0,events:0,tokens:$O()},c[A].usd=Ks(c[A].usd+C.usd),c[A].events+=C.events,RO(c[A].tokens,C.tokens);for(let[A,C]of Object.entries(R.byProvider))l[A]||={usd:0,events:0},l[A].usd=Ks(l[A].usd+C.usd),l[A].events+=C.events;for(let A of H){let{usd:C}=sn(A);if(!C)continue;let W=IO(A.ts??0,o),B=g.get(W)??{t:W,total:0,byProvider:{}};B.total+=C;let Z=A.provider||"unknown";B.byProvider[Z]=(B.byProvider[Z]||0)+C,g.set(W,B),(M==null||W<M)&&(M=W)}x.push({id:T.id,name:T.name||T.id,archived:!!T.archivedAt,usd:R.totalUsd,turns:R.turns,providers:Object.keys(R.byProvider)});for(let[A,C]of Object.entries(R.byBox))S.push({trackId:T.id,trackName:T.name||T.id,boxId:A,...C})}let P=r.since!=null?IO(r.since,o):M,j=[];for(let T=P,$=0;T!=null&&T<r.until&&$<D6;T=j6(T,o),$++){let L=g.get(T);j.push(L?{t:T,total:Ks(L.total),byProvider:F6(L.byProvider)}:{t:T,total:0,byProvider:{}})}return x.sort((T,$)=>$.usd-T.usd),S.sort((T,$)=>$.usd-T.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:Ta(),window:r.key,since:r.since,until:r.until,bucketMs:o,series:j,fleet:{totalUsd:Ks(d),asRecordedUsd:Ks(w),turns:p,repricedTurns:f,unpricedTurns:h,unpricedModels:[...k],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:x.length,tracksSilentLive:_},prior:a!=null?{totalUsd:Ks(y),since:r.since-a,until:r.since}:null,tracks:x.slice(0,50),boxes:S.slice(0,B6)}}np();var U6=new Set(["cost.recorded","code.committed","code.rework","rework.cause.offered","rework.cause.answered"]),H6=20,W6=40;async function CO(e,t={}){let n=t.now??Date.now(),r=Fb({...t,now:n}),o=l=>(r.since==null||l>=r.since)&&l<r.until,s=[],a={},i=0;for(let l of e||[]){let u=await K(l),d=0,p=Cl(l);for(let f of u){if(!U6.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=wE(s,{boxMeta:a});return{generatedAt:new Date().toISOString(),window:r.key,since:r.since,until:r.until,reworkWindowDays:xo,decayEndDays:Aw,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,H6),fixes:c.fixes.slice(0,W6)}}import q6 from"node:fs";import NO from"node:fs/promises";import Dl from"node:os";import G6 from"node:path";mg();pt();var K6=1024*1024,Ef=e=>Math.round(e/K6);async function V6(e){try{let t=await NO.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:Ef(r),diskTotalMb:Ef(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function z6(){try{return(await kn()).filter(t=>t.isBostrat).length}catch{return null}}async function J6(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await NO.readdir(G6.join(kl,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function PO({stateDir:e=kl,now:t=Date.now}={}){let n=Dl.totalmem(),[r,o,s,a]=await Promise.all([bp().catch(()=>null),V6(q6.existsSync(e)?e:Dl.tmpdir()),z6(),J6()]),i=Dl.cpus()?.length||null,c=Dl.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?Ef(n-r):null,memTotalMb:Ef(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(Dl.uptime())}}Yw();be();eh();Li();yr();be();yr();var Y6=864e5;function X6(e,{tolerance:t=.005}={}){if(e.type!=="cost.recorded"||e.source==="vendor")return null;let n=e.payload?.tokens,r=Number(e.payload?.usd)||0;if(!(n?(n.input||0)+(n.output||0)+(n.cacheRead||0)+(n.cacheWrite??n.cacheCreation??0):0)||!r)return null;let s=e.payload?.model||"unknown",a=e.payload?.speed||void 0,i={input:n.input||0,output:n.output||0,cacheRead:n.cacheRead||0,cacheWrite:n.cacheWrite??n.cacheCreation??0},c=Wn(s,i,e.ts,a?{speed:a}:void 0);if(!c)return null;let l=r-c,u=Math.abs(l)>Math.max(c*t,.001);return{model:s,speed:a??null,ts:e.ts,asRecorded:r,expected:c,delta:l,divergent:u}}async function DO(e,{days:t=14,tolerance:n=.005,now:r=Date.now()}={}){let o=r-t*Y6,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=X6(p,{tolerance:n});if(!f)continue;a++;let w=f.speed?`${f.model} (${f.speed})`:f.model,h=s[w]||={rows:0,divergent:0,asRecordedUsd:0,expectedUsd:0};h.rows++,h.asRecordedUsd+=f.asRecorded,h.expectedUsd+=f.expected,f.divergent&&(i++,h.divergent++,c.length<10&&c.push({track:u.name||u.id,...f}))}}let l=u=>Math.round(u*1e4)/1e4;for(let u of Object.values(s))u.asRecordedUsd=l(u.asRecordedUsd),u.expectedUsd=l(u.expectedUsd);return{sinceMs:o,days:t,tolerance:n,rows:a,divergent:i,byModel:s,examples:c,ok:i===0}}Vw();zg();Id();xt();an();import mi from"node:fs/promises";import Z6 from"node:fs";import At from"node:path";import Q6 from"node:os";import{execFile as eK}from"node:child_process";import{promisify as tK}from"node:util";var nK=tK(eK),BO=process.env.BOSTRAT_STATE_DIR||At.join(process.env.HOME||Q6.tmpdir(),".bostrat"),_f=!1,Ub=null;async function Hb(e,t){let n=await mi.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await mi.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await mi.mkdir(At.dirname(t),{recursive:!0}),await mi.copyFile(e,t),!0)}var rK=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 If(e,t,{skip:n=null}={}){let r=0,o=await mi.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(rK.has(s.name)||n?.has(s.name))continue;let a=At.join(e,s.name);s.isDirectory()?r+=await If(a,At.join(t,s.name)):s.isFile()&&(r+=await Hb(a,At.join(t,s.name))?1:0)}return r}async function LO({stateDir:e=BO,dest:t=process.env.BOSTRAT_SYNC_DIR||`${BO}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await mi.mkdir(t,{recursive:!0});let o=0;o+=await Hb(At.join(e,"tracks.json"),At.join(t,"tracks.json"))?1:0,o+=await Hb(At.join(e,"events.jsonl"),At.join(t,"events.jsonl"))?1:0,o+=await If(At.join(e,"events"),At.join(t,"events")),o+=await If(At.join(e,"memory"),At.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await If(At.join(u.cwd,".bostrat"),At.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>nK("git",["-C",t,...u]);Z6.existsSync(At.join(t,".git"))||(await a("init","-q"),await a("config","user.email","agent@bostrat.local"),await a("config","user.name","bostrat state sync")),n&&/^https:\/\/github\.com\//i.test(n)&&(await a("config","--get","credential.useHttpPath").then(()=>!0).catch(()=>!1)||await lm(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||_f))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,_f&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),_f=!1,Ub=null}catch(u){_f=!0;let d=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
780
|
+
`).slice(-3).join(" | ").slice(0,300);d!==Ub&&(Ub=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)}}}Ng();vn();Qo();pt();_i();import{execFile as oK}from"node:child_process";import{promisify as sK}from"node:util";import{randomUUID as aK}from"node:crypto";import iK from"node:fs";import cK from"node:os";import lK from"node:path";var Bo=sK(oK),$f=e=>new Promise(t=>setTimeout(t,e)),Lo=Im,Wb=process.env.BOSTRAT_USAGE_PROBE_CWD||lK.join(cK.homedir(),".bostrat","usage-probe"),uK=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,dK=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,pK=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,jO=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Do=null,Bl=null,Ll=null;async function fK(){try{return await Bo(oe,se("has-session","-t",Lo)),!0}catch{return!1}}function mK(){Do&&clearTimeout(Do),Do=setTimeout(()=>hK().catch(()=>{}),uK),Do.unref&&Do.unref()}async function hK(){Do&&(clearTimeout(Do),Do=null);try{await Bo(oe,se("kill-session","-t",Lo))}catch{}}async function wK(){let e=Date.now()+pK;for(;;){let t=await qb().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(t)){await $f(500);return}if(Date.now()>e)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await $f(500)}}async function gK(){if(await fK())return;if(!await Zr())throw new Error("host tmux server not running \u2014 start globo-tmux.service");iK.mkdirSync(Wb,{recursive:!0}),await Wr(Wb);let e=aK();await Bo(oe,se("new-session","-d","-s",Lo,"-c",Wb,"-x","200","-y","60",...Vi()));let t=`${_m} --session-id ${e}`;await Bo(oe,se("send-keys","-t",Lo,t,"Enter")),await wK()}async function qb(){let{stdout:e}=await Bo(oe,se("capture-pane","-t",Lo,"-p"));return e}async function yK(){await gK(),await Bo(oe,se("send-keys","-t",Lo,"-l","/usage")),await Bo(oe,se("send-keys","-t",Lo,"Enter"));let e=Date.now()+dK,t="";for(;await $f(400),t=await qb(),!(/Current\s+(session|week)/i.test(t)||/\d+%\s*used/i.test(t)||Date.now()>e););return await $f(900),t=await qb(),await Bo(oe,se("send-keys","-t",Lo,"Escape")).catch(()=>{}),mK(),t}function bK(e){let t=String(e||"").split(`
|
|
781
|
+
`).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 kK(e){let t=String(e||"").split(`
|
|
782
|
+
`).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 FO({fresh:e=!1}={}){return!e&&Bl&&Date.now()-Bl.at<jO?Bl.data:Ll||(Ll=(async()=>{try{let t=await yK(),n=bK(t),r={...n,contributors:kK(t),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Bl={at:Date.now(),data:r},r}catch(t){let n={session:null,week:null,contributors:null,subscription:!1,error:t?.message||String(t),capturedAt:Date.now()};return Bl={at:Date.now()-(jO-3e3),data:n},n}finally{Ll=null}})(),Ll)}var xK=262144;function vK(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 SK=new Set([300]),AK=new Set([10080]);function TK(e){let t=Number(e);return!Number.isFinite(t)||t<=0?null:SK.has(t)?"session":AK.has(t)?"week":"other"}function EK(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 _K(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?vK((o-n)/1e3):null,resetsAt:o}}var jl=e=>({session:null,week:null,other:null,planType:null,credits:null,limitReached:null,sawTokenCount:e});function IK(e,t=Date.now()){let n=String(e||"").split(`
|
|
783
|
+
`),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 jl(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=jl(r);[c.primary,c.secondary].forEach((f,w)=>{let h=_K(f,l,t);if(!h)return;let y=TK(f.window_minutes);y===null&&(y=w===0?"session":"week"),y==="session"&&!u.session?u.session=h:y==="week"&&!u.week?u.week=h:(u.other??=[]).push({label:EK(f.window_minutes),...h})}),u.planType=typeof c.plan_type=="string"?c.plan_type:null;let p=c.credits;return p&&(p.has_credits||p.unlimited)&&(u.credits={balance:String(p.balance??""),unlimited:!!p.unlimited}),u.limitReached=typeof c.rate_limit_reached_type=="string"&&c.rate_limit_reached_type||(c.spend_control_reached?"spend_control":null),u}if(c.primary_used_percent!=null||c.secondary_used_percent!=null){let l=u=>Number.isFinite(Number(u))?{pct:Math.round(Number(u)),resets:null,resetsAt:null}:null;return{...jl(r),session:l(c.primary_used_percent),week:l(c.secondary_used_percent)}}return jl(r)}return jl(r)}async function Gb(e){let t={provider:"codex",supported:!0,contributors:null};try{let n=await Xo(e?.cwd,e?.sessionId);if(!n)return{...t,session:null,week:null,subscription:!1,error:"no codex rollout found for this box yet",capturedAt:Date.now()};let r=await jt(n.file,xK),o=IK(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 UO(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 Rf({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 Gb(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await Gb(null))}return n.has("cursor")&&r.push(await Rf({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function Rf(e,{fresh:t=!1}={}){let n=e?.agent||"claude";return n==="codex"?Gb(e):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await FO({fresh:t})}}Gr();xt();xt();function $K(e=process.env){return!!(De(e)&&e.BOSTRAT_PLANE_TOKEN)}var RK=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Of=(e,t)=>Object.assign(new Error(e),{status:t});async function HO(e,t){if(!$K(t))throw Of("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${De(t)}${e}`,{headers:RK(t)})}catch(o){throw Of(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw Of(r?.error||`plane replied ${n.status}`,n.status);return r}async function WO(e,{max:t=10,env:n=process.env}={}){let r=new URLSearchParams({q:e||""});return t&&r.set("max",String(t)),(await HO(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function qO(e,{env:t=process.env}={}){if(!e)throw Of("message id required",400);return(await HO(`/v1/gmail/messages/${encodeURIComponent(e)}`,t)).message}_c();qd();function Kb(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 OK(e){let[t,n]=e.split("/"),r=Kb(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 GO(e){return(e||"").split(",").map(t=>t.trim()).filter(Boolean).map(OK).filter(Boolean)}function MK(e){let t=e.headers["fly-client-ip"];return typeof t=="string"&&t.trim()?t.trim():(e.socket&&e.socket.remoteAddress||"").replace(/^::ffff:/,"")}function KO(e){let t=GO(e),n=String(e||"").trim().toLowerCase()==="open";return{open:n,clientIp:MK,allowed(r){if(n)return!0;let o=Kb((r||"").replace(/^::ffff:/,""));return o==null?!1:t.some(s=>s(o))}}}function VO(e,t){let n=GO(e);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=Kb(o);if(s==null||!n.some(i=>i(s)))return!1;let a=t(r);return!!(a&&a.view)}}}import zO from"node:crypto";import Mf from"node:fs";import CK from"node:path";function JO(e=process.env,{stateDir:t=kl,log:n=console}={}){let r=String(e.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=CK.join(t,"api-token");try{let a=Mf.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=zO.randomBytes(32).toString("base64url");try{Mf.mkdirSync(t,{recursive:!0}),Mf.writeFileSync(o,s+`
|
|
784
|
+
`,{mode:384}),Mf.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function Fl(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 YO(e,t){if(!e||!t)return!1;let n=Buffer.from(String(e)),r=Buffer.from(String(t));return n.length!==r.length?!1:zO.timingSafeEqual(n,r)}function XO(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 NK=[/^\/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/],PK=e=>NK.some(t=>t.test(e));function ZO({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=XO(o);return YO(c,e)?a():PK(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||Fl(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 QO(e,t,{enforce:n=!0}={}){return!n||YO(XO(e),t)?!0:Fl(e)}bn();wp();wp();import DK from"node:http";import BK from"node:net";function Ul(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 Cf(e){let t=Ul(e.headers.host);return t?{slug:t,view:lg(t)}:null}var LK=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),jK=e=>{let t=LK(e.name);return`<!doctype html><meta charset="utf-8">
|
|
785
785
|
<meta http-equiv="refresh" content="3">
|
|
786
786
|
<title>${t} \u2014 starting</title>
|
|
787
787
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
788
788
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
789
|
-
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function
|
|
789
|
+
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function eM(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 tM(e,t,n){let r=Cf(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(jK(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=DK.request({host:"127.0.0.1",port:o.port,method:e.method,path:eM(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 nM(e,t,n){let r=Cf(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
|
|
790
790
|
\r
|
|
791
791
|
bostrat: no live view "${s}"\r
|
|
792
|
-
`),t.destroy(),!0;let a=
|
|
792
|
+
`),t.destroy(),!0;let a=BK.connect(o.port,"127.0.0.1",()=>{let c=[`${e.method} ${eM(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
|
|
793
793
|
`)+`\r
|
|
794
794
|
\r
|
|
795
|
-
`),n?.length&&a.write(n),t.pipe(a),a.pipe(t)}),i=()=>{a.destroy(),t.destroy()};return a.on("error",i),t.on("error",i),!0}function Vb(e){let t=typeof e=="string"?{host:e}:e||{},n=t["x-bostrat-view-base"];if(typeof n=="string"&&n)return{proto:t["x-forwarded-proto"]==="https"?"https":"http",host:n.replace(/:\d+$/,"").toLowerCase(),source:"header"};let r=wb();if(r)return{proto:"https",host:r,source:"learned"};let o=gb();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:Ul(s)&&lg(a[0])?a.slice(1).join("."):s,source:"request"}}function
|
|
795
|
+
`),n?.length&&a.write(n),t.pipe(a),a.pipe(t)}),i=()=>{a.destroy(),t.destroy()};return a.on("error",i),t.on("error",i),!0}function Vb(e){let t=typeof e=="string"?{host:e}:e||{},n=t["x-bostrat-view-base"];if(typeof n=="string"&&n)return{proto:t["x-forwarded-proto"]==="https"?"https":"http",host:n.replace(/:\d+$/,"").toLowerCase(),source:"header"};let r=wb();if(r)return{proto:"https",host:r,source:"learned"};let o=gb();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:Ul(s)&&lg(a[0])?a.slice(1).join("."):s,source:"request"}}function rM(e,t){return bf(Vb(t),e.slug)}function oM({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
|
|
796
796
|
\r
|
|
797
|
-
`),a.destroy();return}t(s,a,i)}}}function rM(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}Ka();vg();Ap();xd();be();import oM from"node:os";import{randomUUID as Jb}from"node:crypto";var BK=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||oM.hostname(),nodeName:e.BOSTRAT_NODE_NAME||oM.hostname()});var sM=32768,aM=120,iM=1e3,cM=new Set(["claude","codex","cursor"]),LK=new Set(["off","transition","nag"]),jK=[[0,59],[0,23],[1,31],[1,12],[0,6]];function FK(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=jK[r];return String(n).split(",").every(a=>{if(!a)return!1;let[i,c]=a.split("/");if(c!==void 0&&!(Number.isInteger(Number(c))&&Number(c)>=1))return!1;if(i==="*")return!0;let[l,u]=i.includes("-")?i.split("-").map(Number):[Number(i),Number(i)];return Number.isInteger(l)&&Number.isInteger(u)&&l>=o&&u<=s&&l<=u})})}var Pn=e=>String(e??"").trim(),Vs={name(e){let t=Pn(e);if(!t)throw new Error("a loop needs a name");if(t.length>aM)throw new Error(`name is too long (${t.length}); cap is ${aM}`);return t},schedule(e){let t=Pn(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!FK(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=Pn(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>sM)throw new Error(`prompt is too long (${t.length} chars); cap is ${sM}`);return t},expectation(e){let t=Pn(e)||null;if(t&&t.length>iM)throw new Error(`expectation is too long (${t.length}); cap is ${iM}`);return t},notify(e){let t=Pn(e)||"off";if(!LK.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=Pn(typeof t=="string"?t:t?.provider)||"claude";if(!cM.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...cM].join(", ")}`);let r=typeof t=="object"&&t&&Pn(t.model)||null;return r?{provider:n,model:r}:{provider:n}},enabled(e){if(typeof e=="boolean")return e;if(e==="true"||e==="false")return e==="true";throw new Error(`enabled must be true or false, got "${e}"`)}};function UK(e,t){let n=e&&typeof e=="object"?e:{},r=Vs.name(n.name),o=Vs.schedule(n.schedule),s=Vs.prompt(n.prompt),a=Vs.expectation(n.expectation),i=Vs.notify(n.notify),c=Vs.assignment(n.assignment),l=Pn(t?.workspace?.repo)||Pn(t?.workspace?.base)||null;if(!l)throw new Error("this Track isn't based on a repo, so there's nothing for a loop to run in \u2014 propose from a repo-backed Track");let{nodeId:u,nodeName:d}=BK();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:d||null}}async function uM(e,t){let n=UK(t,e),r=Jb();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 dM(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 zb=["name","schedule","prompt","expectation","notify","assignment","enabled"],HK=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function pM(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 fM(e,t){let n=Pn(t);if(!n)throw new Error("which loop? give an id or a name \u2014 see `bostrat-loop ls`");let r=e.find(a=>a.id===n)||e.find(a=>a.name===n);if(r)return r;let o=n.toLowerCase(),s=e.filter(a=>String(a.id).toLowerCase().startsWith(o)||String(a.name).toLowerCase().startsWith(o));if(s.length===1)return s[0];throw s.length>1?new Error(`"${n}" matches ${s.length} loops: ${s.map(a=>`${a.name} (${a.id})`).join(", ")} \u2014 use the id`):new Error(`no loop matching "${n}" \u2014 use \`bostrat-loop ls\``)}function WK(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=>!zb.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: ${zb.join(", ")}`)}let o={};for(let a of zb){if(!(a in n))continue;let i=Vs[a](n[a]);(a==="assignment"?HK(i,t.assignment):i===(t[a]??(a==="notify"?"off":a==="enabled"?!0:null)))||(o[a]=i)}let s=Object.keys(o);if(!s.length)throw new Error("nothing would change \u2014 the patch equals the loop's current definition");return{patch:o,changed:s}}async function mM(e,t,n){let r=pM(t),{patch:o,changed:s}=WK(n,r),a=Jb(),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 lM=600;async function hM(e,t,n){let r=Pn(n);if(!r)throw new Error('a delete proposal needs a reason \u2014 bostrat-loop propose-delete <loop> --reason "<why this loop should go>"');if(r.length>lM)throw new Error(`reason is too long (${r.length}); cap is ${lM}`);let o=pM(t),s=Jb(),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 wM=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 gM(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 wM(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 yM(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 wM(e,"loop.delete.offered",t)).payload,s={id:e.id,cwd:e.cwd};return(n==="delete"||n==="pause")&&await N(s,{type:n==="delete"?"loop.deleted":"loop.paused",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:o.loopId,name:o.name,trackId:o.base?.trackId??null}}),await N(s,{type:"loop.delete.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:o.loopId}}),{proposalId:t,action:n,loopId:o.loopId}}be();import{randomUUID as qK}from"node:crypto";var Zb=20,GK=300,Yb=120,bM=120,kM=2e3,Nf=new Set(["build","ops","debt","spike"]),xM=new Set(["active","done","abandoned"]),Qb=new Set(["circuit.create","circuit.update","circuit.delete"]),KK=new Set(["person.create","slot.create","slot.update","slot.delete","reservation.create","reservation.update","reservation.delete"]),Hl=new Set([...Qb,...KK]),VK=["name","description","status","type"],zK=["person","startDate","endDate","estUsd"],JK=["person","label","startDate","endDate"],YK=/^\d{4}-\d{2}-\d{2}$/,en=e=>String(e??"").trim(),ek=e=>typeof e=="string"&&e.startsWith("@"),AM=864e5;function TM(e){let t=Date.parse(`${e}T00:00:00Z`),n=new Date(t).getUTCDay();return new Date(t-(n+6)%7*AM).toISOString().slice(0,10)}function XK(e){return new Date(Date.parse(`${TM(e)}T00:00:00Z`)+6*AM).toISOString().slice(0,10)}var vM=(e,t)=>{let n=en(e);if(!YK.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 ZK(e,t){let n=vM(e,"startDate"),r=vM(t,"endDate");if(r<n)throw new Error(`endDate ${r} is before startDate ${n}`);let o=TM(n),s=XK(r);return{startDate:o,endDate:s,snapped:o!==n||s!==r}}var SM=e=>{if(e==null||e==="")return null;let t=Number(e);if(!Number.isFinite(t)||t<0)throw new Error(`estUsd must be a non-negative number, got "${e}"`);return t},jo=(e,t,n)=>{let r=en(e);if(r.length>t)throw new Error(`${n} is too long (${r.length}); cap is ${t}`);return r};function Xb(e,t){let n=en(t);if(!n)throw new Error("a circuit name or id is required");if(ek(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 QK(e,t){let n=en(t);if(!n)throw new Error('a person is required \u2014 "me", a person id, or a row label');if(n==="me"||ek(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 eV(e,t,{allow:n=Hl}={}){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>Zb)throw new Error(`too many ops (${o.length}); cap is ${Zb} \u2014 propose one intent at a time`);let s=Array.isArray(t?.circuits)?t.circuits:[],a=Array.isArray(t?.people)?t.people:[],i=Array.isArray(t?.slots)?t.slots:[],c=Array.isArray(t?.reservations)?t.reservations:[],l={circuits:{},slots:{},reservations:{}},u={},d=new Set,p=!1,f=!1,w=x=>{x.id&&(l.circuits[x.id]={name:x.name,status:x.status,type:x.type??"build",updatedAt:x.updatedAt??null},u[x.id]=x.name)},h=x=>{x.id&&(u[x.id]=x.label)},y=(x,A)=>{let I=en(x);if(!I)return null;if(d.has(I))throw new Error(`ref "${I}" is used by two ops \u2014 each ref names one thing`);return d.add(I),u[`@${I}`]=A,I},k=o.map((x,A)=>{let I=x&&typeof x=="object"?x:{},M=en(I.op),P=`op ${A+1}`;if(!Hl.has(M))throw new Error(`${P}: unknown op "${M}" \u2014 one of ${[...Hl].join(", ")}`);if(!n.has(M))throw new Error(`${P}: "${M}" isn't a circuit op \u2014 roadmap changes go through \`bostrat-roadmap propose\` (one card, either CLI)`);let L=R=>{throw new Error(`${P} (${M}): ${R}`)},T=R=>{let S=I.set&&typeof I.set=="object"&&!Array.isArray(I.set)?I.set:null;S||L("needs a `set` object naming the fields to change");let C=Object.keys(S).filter(H=>!R.includes(H));return C.length&&L(`unknown field(s) ${C.join(", ")} \u2014 settable: ${R.join(", ")}`),Object.keys(S).length||L("`set` names no fields"),S},E=R=>{let{startDate:S,endDate:C,snapped:H}=ZK(R.startDate,R.endDate);return H&&(p=!0),{startDate:S,endDate:C,snapped:H}},j=R=>{let S=QK(a,R);return en(R)==="me"&&(f=!0),h(S),{key:S.id??en(R),label:S.label}},G=(R,S,C,H,B)=>{let Z=en(R);Z||L(`needs a ${H} id`);let xe=S.find(Ae=>Ae[C]===Z);return xe||L(`no ${H} "${Z}" \u2014 see \`bostrat-roadmap ls\``),B[Z]={...xe},xe};switch(M){case"circuit.create":{let R=jo(I.name,bM,"name");R||L("a circuit needs a name");let S=en(I.type)||"build";Nf.has(S)||L(`bad type "${S}" (${[...Nf].join(" | ")})`);let C=s.find(B=>String(B.name).toLowerCase()===R.toLowerCase());C&&L(`a circuit named "${C.name}" already exists (${C.id}) \u2014 update it instead of making a twin`);let H=y(I.ref,R);return{op:M,...H?{ref:H}:{},name:R,description:jo(I.description,kM,"description"),type:S}}case"circuit.update":{let R=Xb(s,I.circuit);R.id||L("can't update a circuit created in the same plan \u2014 set its fields on the create op"),w(R);let S=T(VK),C={};if(S.name!==void 0){let H=jo(S.name,bM,"name");H||L("name can't be blank"),H!==R.name&&(C.name=H)}return S.description!==void 0&&(C.description=jo(S.description,kM,"description")),S.status!==void 0&&(xM.has(S.status)||L(`bad status "${S.status}" (${[...xM].join(" | ")})`),S.status!==R.status&&(C.status=S.status)),S.type!==void 0&&(Nf.has(S.type)||L(`bad type "${S.type}" (${[...Nf].join(" | ")})`),S.type!==(R.type??"build")&&(C.type=S.type)),Object.keys(C).length||L("nothing would change \u2014 the set equals the circuit's current values"),{op:M,circuit:R.id,name:R.name,set:C}}case"circuit.delete":{let R=Xb(s,I.circuit);R.id||L("can't delete a circuit created in the same plan"),w(R);let S=o.some((C,H)=>H<A&&en(C?.op)==="circuit.update"&&C?.set?.status&&C.set.status!=="active"&&en(C?.circuit)===en(I.circuit));return R.status==="active"&&!S&&L(`\u201C${R.name}\u201D is active \u2014 an active circuit can't be deleted; propose a circuit.update to done/abandoned first (both can ride one card)`),{op:M,circuit:R.id,name:R.name,status:R.status}}case"person.create":{let R=jo(I.label,Yb,"label");R||L("a person row needs a label");let S=a.find(H=>String(H.label).toLowerCase()===R.toLowerCase());S&&L(`a roadmap row labelled "${S.label}" already exists (${S.personId})`);let C=y(I.ref,R);return{op:M,...C?{ref:C}:{},label:R}}case"slot.create":{let R=Xb(s,I.circuit);w(R);let S=j(I.person),C=E(I);return{op:M,circuit:R.id??R.ref,circuitName:R.name,person:S.key,personLabel:S.label,...C,estUsd:SM(I.estUsd)}}case"slot.update":{let R=G(I.slot,i,"slotId","slot",l.slots),S=T(zK),C={};if(S.person!==void 0){let B=j(S.person);C.person=B.key,C.personLabel=B.label}if(S.startDate!==void 0||S.endDate!==void 0){let B=E({startDate:S.startDate??R.startDate,endDate:S.endDate??R.endDate});B.startDate!==R.startDate&&(C.startDate=B.startDate),B.endDate!==R.endDate&&(C.endDate=B.endDate)}S.estUsd!==void 0&&(C.estUsd=SM(S.estUsd)),Object.keys(C).length||L("nothing would change");let H=s.find(B=>B.id===R.circuitId);return{op:M,slot:R.slotId,circuitName:H?.name??null,set:C}}case"slot.delete":{let R=G(I.slot,i,"slotId","slot",l.slots),S=s.find(C=>C.id===R.circuitId);return{op:M,slot:R.slotId,circuitName:S?.name??null,startDate:R.startDate,endDate:R.endDate}}case"reservation.create":{let R=j(I.person),S=jo(I.label,Yb,"label");return S||L("a reservation needs a label (Leave, Support rotation, \u2026)"),{op:M,person:R.key,personLabel:R.label,label:S,...E(I)}}case"reservation.update":{let R=G(I.reservation,c,"reservationId","reservation",l.reservations),S=T(JK),C={};if(S.person!==void 0){let H=j(S.person);C.person=H.key,C.personLabel=H.label}if(S.label!==void 0){let H=jo(S.label,Yb,"label");H||L("label can't be blank"),H!==R.label&&(C.label=H)}if(S.startDate!==void 0||S.endDate!==void 0){let H=E({startDate:S.startDate??R.startDate,endDate:S.endDate??R.endDate});H.startDate!==R.startDate&&(C.startDate=H.startDate),H.endDate!==R.endDate&&(C.endDate=H.endDate)}return Object.keys(C).length||L("nothing would change"),{op:M,reservation:R.reservationId,label:R.label,set:C}}case"reservation.delete":{let R=G(I.reservation,c,"reservationId","reservation",l.reservations);return{op:M,reservation:R.reservationId,label:R.label,startDate:R.startDate,endDate:R.endDate}}default:return L("unhandled op")}});k.forEach((x,A)=>{for(let I of[x.circuit,x.person,x.set?.person])if(ek(I)&&!d.has(String(I).slice(1)))throw new Error(`op ${A+1} (${x.op}): unknown ref "${I}" \u2014 no earlier op in this plan creates it`)});let g=a.some(x=>x.kind==="user"&&x.userId);return{why:jo(r.why,GK,"why"),ops:k,base:l,resolved:u,snapped:p,createsMeRow:f&&!g}}async function EM(e,t,n,r={}){let o=eV(t,n,r),s=qK(),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 tV=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 _M(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 tV(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,Zb+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}}lp();be();Yr();be();Gn();Ap();var IM=10,$M=200,nV=120,rV=24,OM=/^\d{4}-\d{2}-\d{2}$/;function tk(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var oV=(e=Date.now())=>new Date(e).toISOString().slice(0,10),Tt=(e,t="i")=>new RegExp(e,t),sV=[{re:Tt("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:Tt("\\bSPC\\b",""),reason:"business (SPC)"},{re:Tt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:Tt("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:Tt("\\bReplicas\\b",""),reason:"competitor research"},{re:Tt("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:Tt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:Tt("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],aV=[Tt("\\bSPC\\b",""),Tt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),Tt("competitor|cloudcli|\\bkiro\\b"),Tt("\\bReplicas\\b",""),Tt("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),Tt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),Tt("(^|[^\\w&])#\\d+\\b"),Tt("\\btrack/[\\w.-]+")];function iV({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of sV)if(o.test(n)||o.test(r))return s;return null}function RM(e){let t=String(e||"");for(let n of aV){let r=n.exec(t);if(r)return r[0].trim()}return null}function MM(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 cV=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function lV(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Qc(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(`
|
|
798
|
-
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,nV)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${rV}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=cV.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=RM(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>$M)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${$M}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=RM(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>IM)throw new Error(`${r.length} bullets \u2014 over the ${IM}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function uV(e,{repos:t=tk()}={}){let n=new Set(t);return(await En()).filter(r=>r.state==="merged"&&(r.resolvedAt??0)>=e&&n.has(r.repo)).sort((r,o)=>(r.resolvedAt??0)-(o.resolvedAt??0)).map(r=>({repo:r.repo,number:r.number,title:r.title||null,url:r.url||null,headRef:r.headRef||null,mergedAt:r.resolvedAt??null,excluded:iV({title:r.title,headRef:r.headRef})}))}async function CM(e,t){let n=await uV(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function NM(e,{md:t,day:n=null}){let r=lV(t),o=n==null||n===""?oV():String(n);if(!OM.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 PM(e,{day:t}){let n=String(t||"");if(!OM.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}}xs();Gn();vs();be();xs();xt();Ir();Gn();_c();op();qd();xa();an();import{execFile as vV}from"node:child_process";import{promisify as SV}from"node:util";var dV=new Set(["pull_request","check_suite","workflow_run","push"]),pV=/^[^/\s]+\/[^/\s]+$/,fV=500,nk=new Set,rk=[],Pf=new Map,mn={hourStart:0,byEvent:{},duplicates:0,rejected:0};function ok(e,t,n){let r=Math.floor(e/36e5)*36e5;r!==mn.hourStart&&(mn.hourStart=r,mn.byEvent={},mn.duplicates=0,mn.rejected=0),t==="accepted"?mn.byEvent[n]=(mn.byEvent[n]||0)+1:mn[t]+=1}function mV(e){if(!e||typeof e!="object"||e.forge!=="github"||typeof e.event!="string"||!dV.has(e.event)||typeof e.repo!="string"||!pV.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 DM(e,{now:t=Date.now(),onAccept:n=null,log:r=console}={}){let o=mV(e);if(!o)return ok(t,"rejected"),{ok:!1,error:"malformed github event envelope"};if(o.deliveryId){if(nk.has(o.deliveryId))return ok(t,"duplicates"),{ok:!0,duplicate:!0,envelope:o};for(nk.add(o.deliveryId),rk.push(o.deliveryId);rk.length>fV;)nk.delete(rk.shift())}let s=o.repo.toLowerCase(),a=Pf.get(s)||{lastDeliveryAt:0,count:0};a.lastDeliveryAt=t,a.count+=1,Pf.set(s,a),ok(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 hV(e,t=Date.now()){let n=Pf.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 BM=1440*6e4;function sk(e,t=Date.now()){let n=hV(e,t);return n.covered&&n.ageMs<BM}function LM(e=Date.now()){let t={};for(let[n,r]of Pf)t[n]={lastDeliveryAt:r.lastDeliveryAt,count:r.count,covered:e-r.lastDeliveryAt<BM};return{hourStart:mn.hourStart,byEvent:{...mn.byEvent},duplicates:mn.duplicates,rejected:mn.rejected,repos:t}}be();Gn();Gr();Ir();an();function jM(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function wV(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var gV=new Set(["merged","closed","lost"]);function yV(e,t){if(!e?.blocked)return null;let n=jM(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 bV(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function kV(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 xV(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 Df(e,t={}){let{now:n=Date.now(),slugOf:r=Me,lookup:o=ft,note:s=ST,tracks:a=async()=>(await ke()).filter(d=>!Ue(d)),asked:i=kV,emitChange:c=xV}=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(!wV(f))continue;let w=await o(p,f.number).catch(()=>null);if(!w?.trackId||gV.has(w.state))continue;let{prev:h}=await s(p,f.number,{blocked:jM(f),sha:f.headSha??null,at:n});if(!h)continue;let y=yV(h,f);if(!y||bV(w,y.change))continue;u??=new Map((await a()).map(A=>[A.id,A]));let k=u.get(w.trackId);if(!k)continue;let g=f.headSha??null;if(await i(k,y.change,f.number,g,h.at||0))continue;let x={number:Number(f.number),url:f.url||w.url,title:f.title||w.title,branch:f.headRefName||w.headRef,repo:p,sha:g,delegated:!1,reason:y.reason};y.change==="ci-fixed"?x.passed=f.checks.passed:x.base=f.baseRefName||null,await c(k,y.change,x),l.push({change:y.change,number:f.number,reason:y.reason})}catch{}}return l}var ck=SV(vV),zM=3e4,AV=30*6e4,TV=zM,EV=10*6e4,_V=10080*6e4,IV=60*6e4,FM=8,JM=1440*6e4,$V=3,YM="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",ik=60*6e4,RV=15*6e4,OV=9e4,MV=1440*6e4,Wl=new Map,ql=new Map,CV=360*6e4,NV=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}),PV=/could not resolve|not found|no pull requests?/i,DV=e=>PV.test(String(e?.message||e||""));function BV(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<ik)}var UM={fast:TV,slow:EV,decay:IV};function HM(e,t,n=sk){return XM(e,t,{grace:0})||!n(e.repo,t)&&(e.armedAt&&t-e.armedAt<AV||BV(e,t))?"fast":t-(e.firstSeen||0)>_V?"decay":"slow"}function Kl(e,t,n,r){let o=e.lanes?.get(ce(t.repo,t.number))||{};return Wd({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function LV(e,t){Sr("watch");try{let{stdout:n}=await ck("gh",["pr","view",String(t),"--repo",e,"--json",YM],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Bn({slug:e})}});return JSON.parse(n||"null")}catch(n){throw yo(n)&&bo(n),n}}async function jV(e){Sr("watch");try{let{stdout:t}=await ck("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",YM],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await Bn({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw yo(t)&&bo(t),t}}async function FV(){let e=new Map;for(let t of await ke().catch(()=>[]))e.set(t.id,t);return e}async function UV(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 rp(e,t).catch(()=>{})}async function HV(e,t){let n=await ks(e);return n?Da(n,t):null}async function WV(e,t,n){let r=await ks(e);if(!r)throw new Error(`no local checkout for ${e}`);return Ba(r,t,{method:n})}async function qV(e,t){let n=await ks(e);if(!n)throw new Error(`no local checkout for ${e}`);return Rc(n,t)}async function GV(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 hi(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 KV=10*6e4;async function VV(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>JM){await hi(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 hi(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await hi(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<KV)return;await hi(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 hi(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 hi(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await ZM(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function wi(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 zV(e,t){try{return await ck("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Gl(e,t,n,r=()=>!0){return(await K(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var gi=(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 XM(e,t,{grace:n=OV}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=MV}async function JV(e,t,n,r){let o=r.now??Date.now();if(!XM(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||Ue(s))return!1;let a=(s.boxes||[]).find(d=>d.name===s.leadBox);return await(r.midTurn??as)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await K(s).catch(()=>[])).some(d=>d.type==="pr.state.changed"&&d.payload?.number===Number(e.number))?(await(r.claim??xr)(e.repo,e.number).catch(()=>null),!1):await(r.claim??xr)(e.repo,e.number).catch(()=>null)?(await wi(s,"opened",{...gi(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 YV(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 Pd(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await Bd(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<RV;await vT(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await xT(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Gl(a,"pushed",e.number,d=>d.payload?.sha===s)&&await wi(a,"pushed",{...gi(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>ik)await Dd(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 Dd(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Kl(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await Gl(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await wi(a,"ci-fixed",{...gi(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await Dd(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>ik)await Ld(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Ld(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Kl(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await Gl(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await wi(a,"reconciled",{...gi(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await Ld(e.repo,e.number).catch(()=>{})}}}}async function XV(e,t,n,r){let o=Kl(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await Gl(s,"closed",e.number)&&await wi(s,"closed",{...gi(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 WM(e,t,n){Kl(n,e,"lost",null);let r=t.get(e.trackId);r&&!await Gl(r,"lost",e.number)&&await wi(r,"lost",gi(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function ZM(e,t,n,r,o={}){let s=Kl(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 qM(e={}){let{now:t=Date.now(),auth:n=fw,view:r=LV,list:o=jV,links:s=ET,trackMap:a=FV,emitMerged:i=UV,mark:c=wo,archiveIfArmed:l=Er,notifyIfArmed:u=_r,notifyIfResolved:d=zp,followupsIfMerged:p=Ds,batchThreshold:f=$V,detail:w=HV,merge:h=WV,updateBranch:y=qV,lookup:k=ft,disarm:g=Ec,emitDisarmed:x=GV,localHasCommit:A=zV,claim:I=xr,midTurn:M=as,only:P=null,wantDetail:L=null,covered:T=sk,verdicts:E=null,tokenFor:j=ee=>ta(ee),noteRefusal:G=bT,clearRefusal:R=kT}=e;if(!(await n().catch(()=>null))?.authenticated||Ud()>0)return 0;let C=await s().catch(()=>[]),H=YT(C),B=ee=>{try{return!!L?.(ee)}catch{return!1}},Z=P?C.filter(ee=>{try{return P(ee)}catch{return!1}}):C.filter(ee=>t-(Wl.get(ce(ee.repo,ee.number))||0)>=UM[HM(ee,t,T)]);if(!Z.length)return 0;let xe=Date.now(),Ae=new Map,_e=new Map;for(let ee of[...Z].sort(Oc)){let we=P?"event":HM(ee,t,T);Ae.set(ce(ee.repo,ee.number),{lane:we,intervalMs:UM[we]??null}),Wl.set(ce(ee.repo,ee.number),t),_e.has(ee.repo)||_e.set(ee.repo,[]),_e.get(ee.repo).push(ee)}let Q=await a().catch(()=>new Map),Xe=new Map,Pe=(ee,we)=>{let Oe=ce(ee,we);if(!Xe.has(Oe)){let Ve=Promise.resolve().then(()=>w(ee,we));Ve.catch(()=>{}),Xe.set(Oe,Ve)}return Xe.get(Oe)},le=E??((ee,we)=>Df([{path:ee,prs:[we]}],{now:t,slugOf:async()=>ee,tracks:async()=>[...Q.values()].filter(Oe=>!Ue(Oe))}).catch(()=>{})),ge={now:t,lanes:Ae,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,followupsIfMerged:p,detail:Pe,merge:h,updateBranch:y,lookup:k,disarm:g,emitDisarmed:x,localHasCommit:A,claim:I,midTurn:M};for(let[ee,we]of _e){let Oe=NV(we);if(Oe.until>t)continue;let Ve=!1;try{if(await j(ee),Oe.misses)for(let ne of we)await R(ne.repo,ne.number).catch(()=>{})}catch(ne){Ve=t0(ne)}if(Ve){let ne=Oe.misses+1;if(ne>=FM){console.log(`[pr-watch] ${ee}: not granted ${ne}\xD7 \u2014 retiring ${we.length} link(s) as lost`);for(let he of we){await WM(he,Q,ge);let Be=ce(he.repo,he.number);ql.delete(Be),Wl.delete(Be)}}else for(let he of we)await G(he.repo,he.number,{misses:ne,until:t+CV}).catch(()=>{});continue}let Nt,O=new Set,X=ne=>ne.ciFixAwait||ne.reconcileAwait||B(ne)?!0:!ne.autoMerge||ne.trackId&&H.get(ne.trackId)!==ce(ne.repo,ne.number)?!1:t-(ne.autoMerge.armedAt||0)<=JM;if(we.length>f)Nt=await o(ee).catch(()=>null);else{Nt=new Map;for(let ne of we){if(X(ne)){let he=await Pe(ee,ne.number).catch(()=>null);if(he?.state){Nt.set(Number(ne.number),{number:Number(ne.number),url:he.url,title:he.title,headRefName:he.headRefName,headRefOid:he.headSha??null,state:he.state,mergedAt:he.mergedAt??null,closedAt:he.closedAt??null});continue}}try{let he=await r(ee,ne.number);he?.number!=null&&Nt.set(Number(he.number),he)}catch(he){DV(he)&&O.add(ce(ne.repo,ne.number))}}}if(Nt)for(let ne of we){let he=ce(ne.repo,ne.number),Be=Nt.get(Number(ne.number));if(!Be){if(O.has(he)){let Pt=(ql.get(he)||0)+1;ql.set(he,Pt),Pt>=FM&&(await WM(ne,Q,ge),ql.delete(he),Wl.delete(he))}continue}if(ql.delete(he),Be.state==="MERGED"||Be.state==="CLOSED")Be.state==="MERGED"?await ZM(ne,Be,Q,ge):await XV(ne,Be,Q,ge),Wl.delete(he);else{await JV(ne,Be,Q,ge).catch(()=>{}),await YV(ne,Be,Q,ge).catch(()=>{});let Pt=!ne.trackId||H.get(ne.trackId)===he;ne.autoMerge&&Pt&&await VV(ne,Q,ge).catch(()=>{});try{if(B(ne)&&!Xe.has(he)&&Pe(ne.repo,ne.number),Xe.has(he)){let tt=await Xe.get(he).catch(()=>null);tt?.checks&&await le(ne.repo,tt)}}catch{}}}}let de={};for(let{lane:ee}of Ae.values())de[ee]=(de[ee]||0)+1;return console.log(`[pr-watch] probed ${Z.length} link(s) (${Object.entries(de).map(([ee,we])=>`${ee} ${we}`).join(", ")}) in ${Date.now()-xe}ms`),Z.length}function GM(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 ak=!1,Bf=!1,Lf=[],KM=Promise.resolve();async function ZV(){for(;Bf||Lf.length;){if(Bf){Bf=!1;try{await qM()}catch{}continue}let e=Lf;Lf=[];let t=e.map(r=>r.spec),n=Object.assign({},...e.map(r=>r.deps||{}));try{await qM({...n,only:r=>t.some(o=>GM(o,r)),wantDetail:r=>t.some(o=>o.detail&&GM(o,r))})}catch{}}}function jf(e,t){return e===null?Bf=!0:Lf.push({spec:e,deps:t}),ak||(ak=!0,KM=ZV().finally(()=>{ak=!1})),KM}function QM(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 jf({repo:n,numbers:r,branch:e.branch||null,event:e.event||null,detail:e.detail===!0},t)}var QV=6e4,VM=0;function eC(e=void 0){let t=e?.now??Date.now();return t-VM<QV?Promise.resolve():(VM=t,jf({all:!0},e))}function tC(){jf(null),setInterval(()=>jf(null),zM)}xs();sp();Jc();Ir();an();var ez=new Set(["opened","reopened","closed","synchronize","ready_for_review","converted_to_draft","edited"]);async function tz({tracks:e=ke,reconcile:t=yp}={}){for(let n of(await e().catch(()=>[])).filter(r=>!Ue(r)))await t(n,{bypassThrottle:!0}).catch(()=>{})}function nC(e,t={}){let{invalidate:n=ln,probe:r=QM,poke:o=ME,reconcileOpened:s=tz}=t;try{n()}catch{}let a={probe:null,poked:!1,reconciled:!1};if(!e?.repo)return a;if(e.event==="pull_request"&&ez.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}pt();be();Ir();an();import{randomUUID as nz}from"node:crypto";var rz=2e4,rC=3,yi=new Map,lk=new Set;async function oC(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 oz(e={}){let{sessions:t=null,tracks:n=null,append:r=N,read:o=K,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await kn(),i=n??await ke()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(Ue(u)||!u.leadBox)continue;let d=(u.boxes||[]).find(f=>f.name===u.leadBox);if(!d||d.agent!=="claude"||d.state!=="live")continue;let p=c.get(d.name);if(p)if(l.add(d.name),p.alternateOn){let f=(yi.get(d.name)||0)+1;yi.set(d.name,f),f===rC&&(lk.add(d.name),await oC(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:nz(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let f=(yi.get(d.name)||0)>=rC;if(yi.delete(d.name),f||!lk.has(d.name)){lk.add(d.name);let w=await oC(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[...yi.keys()])l.has(u)||yi.delete(u)}var Vl=null,uk=!1;function sC(){return Vl?()=>{}:(Vl=setInterval(()=>{uk||(uk=!0,oz().catch(()=>{}).finally(()=>{uk=!1}))},rz),Vl.unref?.(),()=>{clearInterval(Vl),Vl=null})}ws();Jc();be();xs();vs();ws();Pc();import{randomUUID as sz}from"node:crypto";async function az(){let e=await Promise.resolve().then(()=>(Ir(),X$));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var dk={listRepoPrs:Kn,readEvents:K,appendEvent:N,sayDeps:az,currentBranch:ko};async function iz(e,t=dk,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??ko)(e.workspace?.kind==="multi"?null:e.cwd).catch(()=>null)||e?.workspace?.branch;if(!r)return null;let o;try{o=await t.listRepoPrs(e.cwd,{caller:"pr-exists"})}catch{return null}let s=(o||[]).find(a=>a.headRefName===r);return s?{number:s.number,url:s.url??null,title:s.title??null}:null}async function pk(e,t,{actions:n,notify:r=!1}={},o=dk){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 iz(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=sz();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 fk(e,{offerId:t,choice:n}={},r=dk){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');return vo(`prx:${e.id}:${t}`,()=>cz(e,{offerId:t,choice:n},r))}async function cz(e,{offerId:t,choice:n},r){let o=await r.readEvents(e),s=o.find(u=>u.type==="pr.exists.offered"&&u.payload?.offerId===t);if(!s)throw new Error("unknown offer");if(o.some(u=>u.type==="pr.exists.answered"&&u.payload?.offerId===t))throw new Error("offer already answered");let a=s.payload||{},i=Array.isArray(a.actions)&&a.actions.length?a.actions:["commit","push","pr"],c=null,l=null;if(n==="push"){let{sayToLead:u}=await r.sayDeps();l=ho.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(e.id,Ma(l,{branch:a.branch??null,base:a.base??null,toPr:{number:a.pr?.number??null},offeredRebase:a.offeredRebase===!0}),{kind:"push-go"})}else if(n==="new"){let{sayToLead:u}=await r.sayDeps();l=i,c=await u(e.id,aT(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}}Pc();import Ur from"node:fs/promises";import bi from"node:path";import pC from"node:os";import aC from"node:fs/promises";import Ff from"node:path";import{execFile as lz}from"node:child_process";import{promisify as uz}from"node:util";import{fileURLToPath as dz}from"node:url";var pz=uz(lz),cC=Ff.dirname(dz(import.meta.url)),fz=Ff.resolve(cC,"..","..","client","dist"),mz="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",hz=new Map([...mz].map((e,t)=>[e,t]));function wz(e){let t=[],n=0,r=0;for(let o of e){let s=hz.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 gz(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=wz(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 yz(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 iC=new Map;async function bz(e){let t;try{t=await aC.stat(e)}catch{return null}let n=iC.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await aC.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:gz(r.mappings||"")};return iC.set(e,o),o}catch{return null}}var mk=null;function kz(){return mk||(mk=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):pz("git",["rev-parse","--short","HEAD"],{cwd:Ff.resolve(cC,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),mk}var xz=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function vz(e,{distDir:t=fz}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(xz)){let[a,i,c,l]=s,u=await bz(Ff.join(t,"assets",`${i}.map`)),d=u?yz(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 lC(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await kz();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await vz(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var Sz=()=>process.env.BOSTRAT_STATE_DIR||bi.join(process.env.HOME||pC.tmpdir(),".bostrat"),Fo=()=>bi.join(Sz(),"telemetry"),fC=()=>bi.join(Fo(),".relay-offsets.json"),Az=50,Tz=14,Ez=3e4,uC=500,tn=(e,t)=>typeof e=="string"?e.slice(0,t):"";function _z(e,t){if(!e||typeof e!="object")return null;let n=tn(e.name,64);if(!n)return null;let r=Number.isFinite(e.ts)?e.ts:t,o={name:n,ts:Math.abs(r-t)<168*3600*1e3?r:t,receivedAt:t,anonId:tn(e.anonId,64),sessionId:tn(e.sessionId,64),userId:tn(e.userId,64),release:tn(e.release,64),appVersion:tn(e.appVersion,32),route:tn(e.route,128),deviceClass:e.deviceClass==="phone"||e.deviceClass==="desktop"?e.deviceClass:""};return e.props&&typeof e.props=="object"&&!Array.isArray(e.props)&&(JSON.stringify(e.props).length<=8192?o.props=e.props:o.props={clamped:!0}),e.error&&typeof e.error=="object"&&(o.error={kind:tn(e.error.kind,256),message:tn(e.error.message,8192),stack:tn(e.error.stack,32768),fingerprint:tn(e.error.fingerprint,64)}),Array.isArray(e.breadcrumbs)&&(o.breadcrumbs=e.breadcrumbs.slice(-50).map(s=>({t:Number.isFinite(s?.t)?s.t:0,n:tn(s?.n,64),r:tn(s?.r,128)}))),o}var mC=e=>new Date(e).toISOString().slice(0,10),dC=0;async function hC(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,Az):[]).map(s=>_z(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await lC(s);await Ur.mkdir(Fo(),{recursive:!0});let o=bi.join(Fo(),`${mC(t)}.ndjson`);return await Ur.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
799
|
-
`).join(""),"utf8"),t-
|
|
800
|
-
`).filter(Boolean),d=[];for(let p of u)try{d.push(JSON.parse(p))}catch{}for(let p=0;p<d.length;p+=uC){let f=d.slice(p,p+uC).map(h=>$z(h)),w=await e(`${t.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.ingestKey}`},body:JSON.stringify({events:f})}).catch(()=>null);if(!w||!w.ok)return{sent:o,disabled:!1};o+=f.length}r[s]=i+l+1,await yC(r)}return{sent:o,disabled:!1}}var hk=null,wk=!1;function bC(){hk||(hk=setInterval(async()=>{if(!wk){wk=!0;try{await Rz()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{wk=!1}}},Ez),hk.unref?.())}import Wf from"node:fs/promises";import AC from"node:path";import Oz from"node:os";var TC=()=>process.env.BOSTRAT_STATE_DIR||AC.join(process.env.HOME||Oz.tmpdir(),".bostrat"),EC=()=>AC.join(TC(),"dictation-glossary.json"),Mz=100,Cz=500,Nz=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],qf=null,gk=null,yk=Promise.resolve();function kC(){let e={},t=Date.now();for(let n of Nz)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function _C(){return qf||(gk??=Wf.readFile(EC(),"utf8").then(e=>JSON.parse(e)?.terms??kC()).catch(()=>kC()).then(e=>qf=e).finally(()=>gk=null),gk)}async function xC(){let e=JSON.stringify({terms:qf},null,2),t=EC(),n=`${t}.${process.pid}.tmp`;await Wf.mkdir(TC(),{recursive:!0}),await Wf.writeFile(n,e),await Wf.rename(n,t)}function Pz(){return yk=yk.then(xC,xC),yk.catch(()=>{})}async function bk(){return Object.values(await _C()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var Dz=e=>Math.max(1,Math.ceil(e.length/4));async function Bz(){let e=[],t=0;for(let{term:n}of await bk()){let r=Dz(n);if(!(t+r>Cz)&&(e.push(n),t+=r,e.length>=Mz))break}return e}async function Lz(e,t){let n=await _C(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var vC=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Hf=e=>e.toLowerCase();function SC(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function IC(e,t){let n=vC(e),r=vC(t);if(!n.length||!r.length)return[];let o=n.length,s=r.length,a=Array.from({length:o+1},()=>new Array(s+1).fill(0));for(let u=o-1;u>=0;u--)for(let d=s-1;d>=0;d--)a[u][d]=Hf(n[u])===Hf(r[d])?a[u+1][d+1]+1:Math.max(a[u+1][d],a[u][d+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)Hf(n[c])===Hf(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(SC(r[l])&&i.add(r[l]),l++);for(;l<s;l++)SC(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await Lz(u,"learned");return await Pz(),[...i]}var zl=null;function jz(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 $C(){if(!zl)return null;let e=await Tl();if(!e||e.apiKey.slice(-6)!==zl.keyTail)return null;let{keyTail:t,...n}=zl;return n}async function RC(e,t){let n=await Tl();if(!n){let i=new Error("Deepgram is not configured \u2014 add deepgram.apiKey to ~/.bostrat/integrations.json");throw i.status=503,i.code="not_configured",i}let r=new URL("https://api.deepgram.com/v1/listen");r.searchParams.set("model","nova-3"),r.searchParams.set("smart_format","true");for(let i of await Bz())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=jz(o.status,i);c.persistent&&(zl={code:c.code,message:c.message,at:Date.now(),keyTail:n.apiKey.slice(-6)});let l=new Error(c.message);throw l.status=c.status,l.code=c.code,l}return zl=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}be();var kk={"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 Fz(e){if(!e)return null;let t=String(e).trim();if(kk[t])return t;let n=null;for(let r of Object.keys(kk))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function Uz(e,t={}){let n=kk[Fz(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 Hz(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 Jl=null;async function Wz(){if(!Jl)return null;let e=await gf();if(!e||e.apiKey.slice(-6)!==Jl.keyTail)return null;let{keyTail:t,...n}=Jl;return n}async function qz(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=Hz(t.status,o);s.persistent&&(Jl={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}Jl=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 Gz={openai:qz};async function OC(){let e=await gf();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await Wz()}:{configured:!1}}async function MC(){let e=await gf();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 Gz[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function CC(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=Uz(t,s),i=await N(e,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:t||"unknown",usd:a,tokens:{input:s.textIn+s.audioIn,output:s.textOut+s.audioOut,cacheRead:s.textInCached+s.audioInCached,cacheWrite:0},voice:s,seconds:Math.max(0,Math.round(Number(r)||0)),source:"voice-chat"}});return{usd:a,seq:i.seq}}an();mT(async()=>new Set((await ke()).map(e=>e.id)));var UC=Re.dirname(Vz(import.meta.url)),Tk=Number(process.env.PORT)||3362,Gf=Zy(process.env),HC=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,ki=qO(process.env.BOSTRAT_ALLOW_IPS),Kf=GO(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Cf),_k=VO(),WC=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",_=nn();_.disable("x-powered-by");_.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),Ul(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});_.use((e,t,n)=>{if(Fl(e)||ki.allowed(ki.clientIp(e))||Kf.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});_.use(QO);_.use(YO({token:_k,enforce:WC}));_.use("/api/mocks",nn.json({limit:"1mb"}));_.use("/api/mocks",nn.raw({type:"image/png",limit:"24mb"}));_.use("/api/mocks",nn.raw({type:["video/mp4","video/webm"],limit:"105mb"}));_.use("/api/runbooks",nn.json({limit:"1mb"}));_.use("/api/tracks/adopt",nn.json({limit:"1mb"}));_.use("/api/telemetry",nn.json({limit:"256kb"}));_.use("/api/hook",nn.json({limit:"2mb"}));_.use("/api/dictation",nn.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));_.use("/api/tracks/:id/upload",nn.raw({type:()=>!0,limit:"25mb"}));_.use(nn.json({limit:"64kb"}));_.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:zt,hostTmuxAlive:await Zr(),autoTriggerPct:HC,plane:xi?xi.status():null})});_.get("/api/sessions",async(e,t)=>{t.json({sessions:await kn()})});_.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})}});_.delete("/api/sessions/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!je(n))return t.status(400).json({error:"invalid session name"});if(!await Se(n))return t.status(404).json({error:"session not found"});try{await Lt(n),t.json({ok:!0})}catch(r){t.status(500).json({error:`failed to kill: ${r?.message||r}`})}});_.get("/api/context/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!je(n))return t.status(400).json({error:"invalid session name"});let r=await Se(n);if(!r)return t.status(404).json({error:"session not found"});let o=await Zi(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});_.post("/api/handoff/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!je(n))return t.status(400).json({error:"invalid session name"});try{let r=await YS(n);t.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){t.status(409).json({error:r?.message||String(r)})}});_.get("/api/handoff/:name",(e,t)=>{let n=decodeURIComponent(e.params.name),r=hd(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 NC=10;async function qC(){let e=await ke(),[t,n]=await Promise.all([f$(e),Z$(e)]);return{tracks:e,views:await cg(),queuedOf:r=>Ms(r),jobsOf:r=>t.get(r)??[],prPendingOf:r=>n.get(r)??[]}}_.get("/api/idle-guard",async(e,t)=>{try{t.json(Xy(await qC()))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var hn=Ls(),Zz=pR({kind:hn}),Xl=null,Ct=()=>Xl&&["auto","prompt","off"].includes(Xl)?Xl:Zz,Ek=null,Qz=Sl.has(hn),GC=async({to:e,from:t})=>!Qz||uf(await Al())?!1:(Ql.arm({to:e,from:t,kind:hn}),!0);async function e3(e){let t=Ct();Xl=e??null;let n=Ct();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){Ql.cancel();return}Ek&&await GC(Ek)}}var KC=(()=>{try{return Xz(import.meta.url)("../package.json").version||null}catch{return null}})(),Ql=mR({isBusy:async()=>Xy(await qC()).busy,onStuck:({to:e,from:t})=>or({current:t,latest:e,installKind:hn,policy:Ct(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?or({current:t,latest:e,installKind:hn,policy:Ct(),status:"updating",reason:"auto"}):or({current:t,latest:e,installKind:hn,policy:Ct(),status:"failed",error:n?.reason})).catch(()=>{})});_.get("/api/node/self-update",async(e,t)=>{t.json({installKind:hn,policy:Ct(),policySource:Xl?"plane":"node",current:KC,status:await Al(),armed:Ql.armed})});_.post("/api/node/self-update",async(e,t)=>{let n=typeof e.body?.to=="string"?e.body.to.trim():"";if(!/^\d+\.\d+\.\d+$/.test(n))return t.status(400).json({error:"body.to must be a release version (X.Y.Z)"});let r=await df({to:n,kind:hn,reason:"manual"});if(r.ok)return Ql.cancel(),or({current:r.from,latest:n,installKind:hn,policy:Ct(),status:"updating",reason:"manual"}).catch(()=>{}),t.json({ok:!0,...r});t.status(409).json({ok:!1,error:r.reason,status:r.status??null})});async function VC(){try{let e=await fR();e&&await or({current:e.from,latest:e.to,installKind:e.kind||hn,policy:Ct(),status:e.status,error:e.error,reason:e.reason}),await dy({current:KC,installKind:hn,policy:Ct()})}catch{}}VC();setInterval(VC,3e4).unref();_.get("/api/tracks",async(e,t)=>{let n=(await ke()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>NC){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,NC).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:ml,agentAvailability:await Fp(),tickets:await zR()})});_.get("/api/cost",async(e,t)=>{try{let n=await ke();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await Bb(n);if(r.cursorUsage=Kw(),e.query.format==="text")return t.type("text/plain").send(kO(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/usage/roundup",async(e,t)=>{try{let n=await jb(await ke(),{window:e.query.window,from:e.query.from,to:e.query.to});if(e.query.format==="text")return t.type("text/plain").send(TO(n));t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/usage/spend",async(e,t)=>{try{let n=await RO(await ke(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=Kw(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=Sc(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/usage/caps",(e,t)=>{t.json(Sc())});_.post("/api/usage/caps",async(e,t)=>{try{t.json(await _d(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});_.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await jO(await ke(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await Wk(e.get(pr),{byToken:hl,legacy:()=>Lc(e.body?.session_id,e.body?.cwd)});if(n==="refused")return Ei("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return Ei("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&&Ei("mismatch",{track:r.track.id,box:r.box.name}),Ei(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 gO(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&bu(r.box.cwd),t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});_.get("/api/git/trailers",async(e,t)=>{t.type("text/plain");try{let n=await hl(e.get(pr));if(!n?.track||!n?.box)return t.status(204).end();t.send((await wE(n.track,n.box)).join(`
|
|
797
|
+
`),a.destroy();return}t(s,a,i)}}}function sM(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}Ka();vg();Ap();xd();be();import aM from"node:os";import{randomUUID as Jb}from"node:crypto";var FK=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||aM.hostname(),nodeName:e.BOSTRAT_NODE_NAME||aM.hostname()});var iM=32768,cM=120,lM=1e3,uM=new Set(["claude","codex","cursor"]),UK=new Set(["off","transition","nag"]),HK=[[0,59],[0,23],[1,31],[1,12],[0,6]];function WK(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=HK[r];return String(n).split(",").every(a=>{if(!a)return!1;let[i,c]=a.split("/");if(c!==void 0&&!(Number.isInteger(Number(c))&&Number(c)>=1))return!1;if(i==="*")return!0;let[l,u]=i.includes("-")?i.split("-").map(Number):[Number(i),Number(i)];return Number.isInteger(l)&&Number.isInteger(u)&&l>=o&&u<=s&&l<=u})})}var Pn=e=>String(e??"").trim(),Vs={name(e){let t=Pn(e);if(!t)throw new Error("a loop needs a name");if(t.length>cM)throw new Error(`name is too long (${t.length}); cap is ${cM}`);return t},schedule(e){let t=Pn(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!WK(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=Pn(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>iM)throw new Error(`prompt is too long (${t.length} chars); cap is ${iM}`);return t},expectation(e){let t=Pn(e)||null;if(t&&t.length>lM)throw new Error(`expectation is too long (${t.length}); cap is ${lM}`);return t},notify(e){let t=Pn(e)||"off";if(!UK.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=Pn(typeof t=="string"?t:t?.provider)||"claude";if(!uM.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...uM].join(", ")}`);let r=typeof t=="object"&&t&&Pn(t.model)||null;return r?{provider:n,model:r}:{provider:n}},enabled(e){if(typeof e=="boolean")return e;if(e==="true"||e==="false")return e==="true";throw new Error(`enabled must be true or false, got "${e}"`)}};function qK(e,t){let n=e&&typeof e=="object"?e:{},r=Vs.name(n.name),o=Vs.schedule(n.schedule),s=Vs.prompt(n.prompt),a=Vs.expectation(n.expectation),i=Vs.notify(n.notify),c=Vs.assignment(n.assignment),l=Pn(t?.workspace?.repo)||Pn(t?.workspace?.base)||null;if(!l)throw new Error("this Track isn't based on a repo, so there's nothing for a loop to run in \u2014 propose from a repo-backed Track");let{nodeId:u,nodeName:d}=FK();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:d||null}}async function pM(e,t){let n=qK(t,e),r=Jb();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 fM(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 zb=["name","schedule","prompt","expectation","notify","assignment","enabled"],GK=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function mM(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 hM(e,t){let n=Pn(t);if(!n)throw new Error("which loop? give an id or a name \u2014 see `bostrat-loop ls`");let r=e.find(a=>a.id===n)||e.find(a=>a.name===n);if(r)return r;let o=n.toLowerCase(),s=e.filter(a=>String(a.id).toLowerCase().startsWith(o)||String(a.name).toLowerCase().startsWith(o));if(s.length===1)return s[0];throw s.length>1?new Error(`"${n}" matches ${s.length} loops: ${s.map(a=>`${a.name} (${a.id})`).join(", ")} \u2014 use the id`):new Error(`no loop matching "${n}" \u2014 use \`bostrat-loop ls\``)}function KK(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=>!zb.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: ${zb.join(", ")}`)}let o={};for(let a of zb){if(!(a in n))continue;let i=Vs[a](n[a]);(a==="assignment"?GK(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 wM(e,t,n){let r=mM(t),{patch:o,changed:s}=KK(n,r),a=Jb(),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 dM=600;async function gM(e,t,n){let r=Pn(n);if(!r)throw new Error('a delete proposal needs a reason \u2014 bostrat-loop propose-delete <loop> --reason "<why this loop should go>"');if(r.length>dM)throw new Error(`reason is too long (${r.length}); cap is ${dM}`);let o=mM(t),s=Jb(),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 yM=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 bM(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 yM(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 kM(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 yM(e,"loop.delete.offered",t)).payload,s={id:e.id,cwd:e.cwd};return(n==="delete"||n==="pause")&&await N(s,{type:n==="delete"?"loop.deleted":"loop.paused",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:o.loopId,name:o.name,trackId:o.base?.trackId??null}}),await N(s,{type:"loop.delete.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:o.loopId}}),{proposalId:t,action:n,loopId:o.loopId}}be();import{randomUUID as VK}from"node:crypto";var Zb=20,zK=300,Yb=120,xM=120,vM=2e3,Nf=new Set(["build","ops","debt","spike"]),SM=new Set(["active","done","abandoned"]),Qb=new Set(["circuit.create","circuit.update","circuit.delete"]),JK=new Set(["person.create","slot.create","slot.update","slot.delete","reservation.create","reservation.update","reservation.delete"]),Hl=new Set([...Qb,...JK]),YK=["name","description","status","type"],XK=["person","startDate","endDate","estUsd"],ZK=["person","label","startDate","endDate"],QK=/^\d{4}-\d{2}-\d{2}$/,en=e=>String(e??"").trim(),ek=e=>typeof e=="string"&&e.startsWith("@"),EM=864e5;function _M(e){let t=Date.parse(`${e}T00:00:00Z`),n=new Date(t).getUTCDay();return new Date(t-(n+6)%7*EM).toISOString().slice(0,10)}function eV(e){return new Date(Date.parse(`${_M(e)}T00:00:00Z`)+6*EM).toISOString().slice(0,10)}var AM=(e,t)=>{let n=en(e);if(!QK.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 tV(e,t){let n=AM(e,"startDate"),r=AM(t,"endDate");if(r<n)throw new Error(`endDate ${r} is before startDate ${n}`);let o=_M(n),s=eV(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},jo=(e,t,n)=>{let r=en(e);if(r.length>t)throw new Error(`${n} is too long (${r.length}); cap is ${t}`);return r};function Xb(e,t){let n=en(t);if(!n)throw new Error("a circuit name or id is required");if(ek(n))return{ref:n,id:null,name:n.slice(1)};let r=e.find(i=>i.id===n);if(r)return r;let o=n.toLowerCase(),s=e.filter(i=>String(i.name).toLowerCase()===o);if(s.length===1)return s[0];if(s.length>1)throw new Error(`"${n}" names ${s.length} circuits \u2014 use an id (bostrat-circuit ls)`);let a=e.filter(i=>String(i.name).toLowerCase().includes(o)).slice(0,5);throw new Error(a.length?`no circuit named "${n}" \u2014 near matches: ${a.map(i=>`${i.name} (${i.id})`).join(", ")}`:`no circuit named "${n}" \u2014 propose a circuit.create op if it should exist (bostrat-circuit ls lists yours)`)}function nV(e,t){let n=en(t);if(!n)throw new Error('a person is required \u2014 "me", a person id, or a row label');if(n==="me"||ek(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 rV(e,t,{allow:n=Hl}={}){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>Zb)throw new Error(`too many ops (${o.length}); cap is ${Zb} \u2014 propose one intent at a time`);let s=Array.isArray(t?.circuits)?t.circuits:[],a=Array.isArray(t?.people)?t.people:[],i=Array.isArray(t?.slots)?t.slots:[],c=Array.isArray(t?.reservations)?t.reservations:[],l={circuits:{},slots:{},reservations:{}},u={},d=new Set,p=!1,f=!1,w=x=>{x.id&&(l.circuits[x.id]={name:x.name,status:x.status,type:x.type??"build",updatedAt:x.updatedAt??null},u[x.id]=x.name)},h=x=>{x.id&&(u[x.id]=x.label)},y=(x,S)=>{let _=en(x);if(!_)return null;if(d.has(_))throw new Error(`ref "${_}" is used by two ops \u2014 each ref names one thing`);return d.add(_),u[`@${_}`]=S,_},k=o.map((x,S)=>{let _=x&&typeof x=="object"?x:{},M=en(_.op),P=`op ${S+1}`;if(!Hl.has(M))throw new Error(`${P}: unknown op "${M}" \u2014 one of ${[...Hl].join(", ")}`);if(!n.has(M))throw new Error(`${P}: "${M}" isn't a circuit op \u2014 roadmap changes go through \`bostrat-roadmap propose\` (one card, either CLI)`);let j=R=>{throw new Error(`${P} (${M}): ${R}`)},T=R=>{let A=_.set&&typeof _.set=="object"&&!Array.isArray(_.set)?_.set:null;A||j("needs a `set` object naming the fields to change");let C=Object.keys(A).filter(W=>!R.includes(W));return C.length&&j(`unknown field(s) ${C.join(", ")} \u2014 settable: ${R.join(", ")}`),Object.keys(A).length||j("`set` names no fields"),A},$=R=>{let{startDate:A,endDate:C,snapped:W}=tV(R.startDate,R.endDate);return W&&(p=!0),{startDate:A,endDate:C,snapped:W}},L=R=>{let A=nV(a,R);return en(R)==="me"&&(f=!0),h(A),{key:A.id??en(R),label:A.label}},H=(R,A,C,W,B)=>{let Z=en(R);Z||j(`needs a ${W} id`);let xe=A.find(Ae=>Ae[C]===Z);return xe||j(`no ${W} "${Z}" \u2014 see \`bostrat-roadmap ls\``),B[Z]={...xe},xe};switch(M){case"circuit.create":{let R=jo(_.name,xM,"name");R||j("a circuit needs a name");let A=en(_.type)||"build";Nf.has(A)||j(`bad type "${A}" (${[...Nf].join(" | ")})`);let C=s.find(B=>String(B.name).toLowerCase()===R.toLowerCase());C&&j(`a circuit named "${C.name}" already exists (${C.id}) \u2014 update it instead of making a twin`);let W=y(_.ref,R);return{op:M,...W?{ref:W}:{},name:R,description:jo(_.description,vM,"description"),type:A}}case"circuit.update":{let R=Xb(s,_.circuit);R.id||j("can't update a circuit created in the same plan \u2014 set its fields on the create op"),w(R);let A=T(YK),C={};if(A.name!==void 0){let W=jo(A.name,xM,"name");W||j("name can't be blank"),W!==R.name&&(C.name=W)}return A.description!==void 0&&(C.description=jo(A.description,vM,"description")),A.status!==void 0&&(SM.has(A.status)||j(`bad status "${A.status}" (${[...SM].join(" | ")})`),A.status!==R.status&&(C.status=A.status)),A.type!==void 0&&(Nf.has(A.type)||j(`bad type "${A.type}" (${[...Nf].join(" | ")})`),A.type!==(R.type??"build")&&(C.type=A.type)),Object.keys(C).length||j("nothing would change \u2014 the set equals the circuit's current values"),{op:M,circuit:R.id,name:R.name,set:C}}case"circuit.delete":{let R=Xb(s,_.circuit);R.id||j("can't delete a circuit created in the same plan"),w(R);let A=o.some((C,W)=>W<S&&en(C?.op)==="circuit.update"&&C?.set?.status&&C.set.status!=="active"&&en(C?.circuit)===en(_.circuit));return R.status==="active"&&!A&&j(`\u201C${R.name}\u201D is active \u2014 an active circuit can't be deleted; propose a circuit.update to done/abandoned first (both can ride one card)`),{op:M,circuit:R.id,name:R.name,status:R.status}}case"person.create":{let R=jo(_.label,Yb,"label");R||j("a person row needs a label");let A=a.find(W=>String(W.label).toLowerCase()===R.toLowerCase());A&&j(`a roadmap row labelled "${A.label}" already exists (${A.personId})`);let C=y(_.ref,R);return{op:M,...C?{ref:C}:{},label:R}}case"slot.create":{let R=Xb(s,_.circuit);w(R);let A=L(_.person),C=$(_);return{op:M,circuit:R.id??R.ref,circuitName:R.name,person:A.key,personLabel:A.label,...C,estUsd:TM(_.estUsd)}}case"slot.update":{let R=H(_.slot,i,"slotId","slot",l.slots),A=T(XK),C={};if(A.person!==void 0){let B=L(A.person);C.person=B.key,C.personLabel=B.label}if(A.startDate!==void 0||A.endDate!==void 0){let B=$({startDate:A.startDate??R.startDate,endDate:A.endDate??R.endDate});B.startDate!==R.startDate&&(C.startDate=B.startDate),B.endDate!==R.endDate&&(C.endDate=B.endDate)}A.estUsd!==void 0&&(C.estUsd=TM(A.estUsd)),Object.keys(C).length||j("nothing would change");let W=s.find(B=>B.id===R.circuitId);return{op:M,slot:R.slotId,circuitName:W?.name??null,set:C}}case"slot.delete":{let R=H(_.slot,i,"slotId","slot",l.slots),A=s.find(C=>C.id===R.circuitId);return{op:M,slot:R.slotId,circuitName:A?.name??null,startDate:R.startDate,endDate:R.endDate}}case"reservation.create":{let R=L(_.person),A=jo(_.label,Yb,"label");return A||j("a reservation needs a label (Leave, Support rotation, \u2026)"),{op:M,person:R.key,personLabel:R.label,label:A,...$(_)}}case"reservation.update":{let R=H(_.reservation,c,"reservationId","reservation",l.reservations),A=T(ZK),C={};if(A.person!==void 0){let W=L(A.person);C.person=W.key,C.personLabel=W.label}if(A.label!==void 0){let W=jo(A.label,Yb,"label");W||j("label can't be blank"),W!==R.label&&(C.label=W)}if(A.startDate!==void 0||A.endDate!==void 0){let W=$({startDate:A.startDate??R.startDate,endDate:A.endDate??R.endDate});W.startDate!==R.startDate&&(C.startDate=W.startDate),W.endDate!==R.endDate&&(C.endDate=W.endDate)}return Object.keys(C).length||j("nothing would change"),{op:M,reservation:R.reservationId,label:R.label,set:C}}case"reservation.delete":{let R=H(_.reservation,c,"reservationId","reservation",l.reservations);return{op:M,reservation:R.reservationId,label:R.label,startDate:R.startDate,endDate:R.endDate}}default:return j("unhandled op")}});k.forEach((x,S)=>{for(let _ of[x.circuit,x.person,x.set?.person])if(ek(_)&&!d.has(String(_).slice(1)))throw new Error(`op ${S+1} (${x.op}): unknown ref "${_}" \u2014 no earlier op in this plan creates it`)});let g=a.some(x=>x.kind==="user"&&x.userId);return{why:jo(r.why,zK,"why"),ops:k,base:l,resolved:u,snapped:p,createsMeRow:f&&!g}}async function IM(e,t,n,r={}){let o=rV(t,n,r),s=VK(),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 oV=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 $M(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 oV(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,Zb+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}}lp();be();Yr();be();Gn();Ap();var RM=10,OM=200,sV=120,aV=24,CM=/^\d{4}-\d{2}-\d{2}$/;function tk(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var iV=(e=Date.now())=>new Date(e).toISOString().slice(0,10),Tt=(e,t="i")=>new RegExp(e,t),cV=[{re:Tt("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:Tt("\\bSPC\\b",""),reason:"business (SPC)"},{re:Tt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:Tt("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:Tt("\\bReplicas\\b",""),reason:"competitor research"},{re:Tt("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:Tt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:Tt("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],lV=[Tt("\\bSPC\\b",""),Tt("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),Tt("competitor|cloudcli|\\bkiro\\b"),Tt("\\bReplicas\\b",""),Tt("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),Tt("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),Tt("(^|[^\\w&])#\\d+\\b"),Tt("\\btrack/[\\w.-]+")];function uV({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of cV)if(o.test(n)||o.test(r))return s;return null}function MM(e){let t=String(e||"");for(let n of lV){let r=n.exec(t);if(r)return r[0].trim()}return null}function NM(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 dV=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function pV(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Qc(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(`
|
|
798
|
+
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,sV)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${aV}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=dV.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=MM(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>OM)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${OM}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=MM(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>RM)throw new Error(`${r.length} bullets \u2014 over the ${RM}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function fV(e,{repos:t=tk()}={}){let n=new Set(t);return(await En()).filter(r=>r.state==="merged"&&(r.resolvedAt??0)>=e&&n.has(r.repo)).sort((r,o)=>(r.resolvedAt??0)-(o.resolvedAt??0)).map(r=>({repo:r.repo,number:r.number,title:r.title||null,url:r.url||null,headRef:r.headRef||null,mergedAt:r.resolvedAt??null,excluded:uV({title:r.title,headRef:r.headRef})}))}async function PM(e,t){let n=await fV(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function DM(e,{md:t,day:n=null}){let r=pV(t),o=n==null||n===""?iV():String(n);if(!CM.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 BM(e,{day:t}){let n=String(t||"");if(!CM.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}}xs();Gn();vs();be();xs();xt();Ir();Gn();_c();op();qd();xa();an();import{execFile as TV}from"node:child_process";import{promisify as EV}from"node:util";var mV=new Set(["pull_request","check_suite","workflow_run","push"]),hV=/^[^/\s]+\/[^/\s]+$/,wV=500,nk=new Set,rk=[],Pf=new Map,mn={hourStart:0,byEvent:{},duplicates:0,rejected:0};function ok(e,t,n){let r=Math.floor(e/36e5)*36e5;r!==mn.hourStart&&(mn.hourStart=r,mn.byEvent={},mn.duplicates=0,mn.rejected=0),t==="accepted"?mn.byEvent[n]=(mn.byEvent[n]||0)+1:mn[t]+=1}function gV(e){if(!e||typeof e!="object"||e.forge!=="github"||typeof e.event!="string"||!mV.has(e.event)||typeof e.repo!="string"||!hV.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 LM(e,{now:t=Date.now(),onAccept:n=null,log:r=console}={}){let o=gV(e);if(!o)return ok(t,"rejected"),{ok:!1,error:"malformed github event envelope"};if(o.deliveryId){if(nk.has(o.deliveryId))return ok(t,"duplicates"),{ok:!0,duplicate:!0,envelope:o};for(nk.add(o.deliveryId),rk.push(o.deliveryId);rk.length>wV;)nk.delete(rk.shift())}let s=o.repo.toLowerCase(),a=Pf.get(s)||{lastDeliveryAt:0,count:0};a.lastDeliveryAt=t,a.count+=1,Pf.set(s,a),ok(t,"accepted",o.event),r.log?.(`[github-event] ${o.event}${o.action?` ${o.action}`:""} ${o.repo}${o.number?` #${o.number}`:o.headRef?` ${o.headRef}`:""}${o.conclusion?` (${o.conclusion})`:""}${o.deliveryId?` delivery ${o.deliveryId.slice(0,8)}`:""}`);try{n?.(o)}catch(i){r.error?.("[github-event] onAccept failed:",i?.message||i)}return{ok:!0,duplicate:!1,envelope:o}}function yV(e,t=Date.now()){let n=Pf.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 jM=1440*6e4;function sk(e,t=Date.now()){let n=yV(e,t);return n.covered&&n.ageMs<jM}function FM(e=Date.now()){let t={};for(let[n,r]of Pf)t[n]={lastDeliveryAt:r.lastDeliveryAt,count:r.count,covered:e-r.lastDeliveryAt<jM};return{hourStart:mn.hourStart,byEvent:{...mn.byEvent},duplicates:mn.duplicates,rejected:mn.rejected,repos:t}}be();Gn();Gr();Ir();an();function UM(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function bV(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var kV=new Set(["merged","closed","lost"]);function xV(e,t){if(!e?.blocked)return null;let n=UM(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 vV(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function SV(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 AV(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 Df(e,t={}){let{now:n=Date.now(),slugOf:r=Me,lookup:o=ft,note:s=TT,tracks:a=async()=>(await ke()).filter(d=>!Ue(d)),asked:i=SV,emitChange:c=AV}=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(!bV(f))continue;let w=await o(p,f.number).catch(()=>null);if(!w?.trackId||kV.has(w.state))continue;let{prev:h}=await s(p,f.number,{blocked:UM(f),sha:f.headSha??null,at:n});if(!h)continue;let y=xV(h,f);if(!y||vV(w,y.change))continue;u??=new Map((await a()).map(S=>[S.id,S]));let k=u.get(w.trackId);if(!k)continue;let g=f.headSha??null;if(await i(k,y.change,f.number,g,h.at||0))continue;let x={number:Number(f.number),url:f.url||w.url,title:f.title||w.title,branch:f.headRefName||w.headRef,repo:p,sha:g,delegated:!1,reason:y.reason};y.change==="ci-fixed"?x.passed=f.checks.passed:x.base=f.baseRefName||null,await c(k,y.change,x),l.push({change:y.change,number:f.number,reason:y.reason})}catch{}}return l}var ck=EV(TV),YM=3e4,_V=30*6e4,IV=YM,$V=10*6e4,RV=10080*6e4,OV=60*6e4,HM=8,XM=1440*6e4,MV=3,ZM="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",ik=60*6e4,CV=15*6e4,NV=9e4,PV=1440*6e4,Wl=new Map,ql=new Map,DV=360*6e4,BV=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}),LV=/could not resolve|not found|no pull requests?/i,jV=e=>LV.test(String(e?.message||e||""));function FV(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<ik)}var WM={fast:IV,slow:$V,decay:OV};function qM(e,t,n=sk){return QM(e,t,{grace:0})||!n(e.repo,t)&&(e.armedAt&&t-e.armedAt<_V||FV(e,t))?"fast":t-(e.firstSeen||0)>RV?"decay":"slow"}function Kl(e,t,n,r){let o=e.lanes?.get(ce(t.repo,t.number))||{};return Wd({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function UV(e,t){Sr("watch");try{let{stdout:n}=await ck("gh",["pr","view",String(t),"--repo",e,"--json",ZM],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Bn({slug:e})}});return JSON.parse(n||"null")}catch(n){throw yo(n)&&bo(n),n}}async function HV(e){Sr("watch");try{let{stdout:t}=await ck("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",ZM],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await Bn({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw yo(t)&&bo(t),t}}async function WV(){let e=new Map;for(let t of await ke().catch(()=>[]))e.set(t.id,t);return e}async function qV(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 rp(e,t).catch(()=>{})}async function GV(e,t){let n=await ks(e);return n?Da(n,t):null}async function KV(e,t,n){let r=await ks(e);if(!r)throw new Error(`no local checkout for ${e}`);return Ba(r,t,{method:n})}async function VV(e,t){let n=await ks(e);if(!n)throw new Error(`no local checkout for ${e}`);return Rc(n,t)}async function zV(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 hi(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 JV=10*6e4;async function YV(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>XM){await hi(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 hi(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await hi(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<JV)return;await hi(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 hi(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 hi(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await eC(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function wi(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 XV(e,t){try{return await ck("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Gl(e,t,n,r=()=>!0){return(await K(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var gi=(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 QM(e,t,{grace:n=NV}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=PV}async function ZV(e,t,n,r){let o=r.now??Date.now();if(!QM(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||Ue(s))return!1;let a=(s.boxes||[]).find(d=>d.name===s.leadBox);return await(r.midTurn??as)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await K(s).catch(()=>[])).some(d=>d.type==="pr.state.changed"&&d.payload?.number===Number(e.number))?(await(r.claim??xr)(e.repo,e.number).catch(()=>null),!1):await(r.claim??xr)(e.repo,e.number).catch(()=>null)?(await wi(s,"opened",{...gi(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 QV(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 Pd(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await Bd(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<CV;await AT(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await ST(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Gl(a,"pushed",e.number,d=>d.payload?.sha===s)&&await wi(a,"pushed",{...gi(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>ik)await Dd(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 Dd(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Kl(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await Gl(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await wi(a,"ci-fixed",{...gi(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await Dd(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>ik)await Ld(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Ld(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Kl(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await Gl(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await wi(a,"reconciled",{...gi(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await Ld(e.repo,e.number).catch(()=>{})}}}}async function ez(e,t,n,r){let o=Kl(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await Gl(s,"closed",e.number)&&await wi(s,"closed",{...gi(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 GM(e,t,n){Kl(n,e,"lost",null);let r=t.get(e.trackId);r&&!await Gl(r,"lost",e.number)&&await wi(r,"lost",gi(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function eC(e,t,n,r,o={}){let s=Kl(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 KM(e={}){let{now:t=Date.now(),auth:n=fw,view:r=UV,list:o=HV,links:s=IT,trackMap:a=WV,emitMerged:i=qV,mark:c=wo,archiveIfArmed:l=Er,notifyIfArmed:u=_r,notifyIfResolved:d=zp,followupsIfMerged:p=Ds,batchThreshold:f=MV,detail:w=GV,merge:h=KV,updateBranch:y=VV,lookup:k=ft,disarm:g=Ec,emitDisarmed:x=zV,localHasCommit:S=XV,claim:_=xr,midTurn:M=as,only:P=null,wantDetail:j=null,covered:T=sk,verdicts:$=null,tokenFor:L=ee=>ta(ee),noteRefusal:H=xT,clearRefusal:R=vT}=e;if(!(await n().catch(()=>null))?.authenticated||Ud()>0)return 0;let C=await s().catch(()=>[]),W=ZT(C),B=ee=>{try{return!!j?.(ee)}catch{return!1}},Z=P?C.filter(ee=>{try{return P(ee)}catch{return!1}}):C.filter(ee=>t-(Wl.get(ce(ee.repo,ee.number))||0)>=WM[qM(ee,t,T)]);if(!Z.length)return 0;let xe=Date.now(),Ae=new Map,_e=new Map;for(let ee of[...Z].sort(Oc)){let we=P?"event":qM(ee,t,T);Ae.set(ce(ee.repo,ee.number),{lane:we,intervalMs:WM[we]??null}),Wl.set(ce(ee.repo,ee.number),t),_e.has(ee.repo)||_e.set(ee.repo,[]),_e.get(ee.repo).push(ee)}let Q=await a().catch(()=>new Map),Xe=new Map,Pe=(ee,we)=>{let Oe=ce(ee,we);if(!Xe.has(Oe)){let Ve=Promise.resolve().then(()=>w(ee,we));Ve.catch(()=>{}),Xe.set(Oe,Ve)}return Xe.get(Oe)},le=$??((ee,we)=>Df([{path:ee,prs:[we]}],{now:t,slugOf:async()=>ee,tracks:async()=>[...Q.values()].filter(Oe=>!Ue(Oe))}).catch(()=>{})),ge={now:t,lanes:Ae,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,followupsIfMerged:p,detail:Pe,merge:h,updateBranch:y,lookup:k,disarm:g,emitDisarmed:x,localHasCommit:S,claim:_,midTurn:M};for(let[ee,we]of _e){let Oe=BV(we);if(Oe.until>t)continue;let Ve=!1;try{if(await L(ee),Oe.misses)for(let ne of we)await R(ne.repo,ne.number).catch(()=>{})}catch(ne){Ve=t0(ne)}if(Ve){let ne=Oe.misses+1;if(ne>=HM){console.log(`[pr-watch] ${ee}: not granted ${ne}\xD7 \u2014 retiring ${we.length} link(s) as lost`);for(let he of we){await GM(he,Q,ge);let Be=ce(he.repo,he.number);ql.delete(Be),Wl.delete(Be)}}else for(let he of we)await H(he.repo,he.number,{misses:ne,until:t+DV}).catch(()=>{});continue}let Nt,O=new Set,X=ne=>ne.ciFixAwait||ne.reconcileAwait||B(ne)?!0:!ne.autoMerge||ne.trackId&&W.get(ne.trackId)!==ce(ne.repo,ne.number)?!1:t-(ne.autoMerge.armedAt||0)<=XM;if(we.length>f)Nt=await o(ee).catch(()=>null);else{Nt=new Map;for(let ne of we){if(X(ne)){let he=await Pe(ee,ne.number).catch(()=>null);if(he?.state){Nt.set(Number(ne.number),{number:Number(ne.number),url:he.url,title:he.title,headRefName:he.headRefName,headRefOid:he.headSha??null,state:he.state,mergedAt:he.mergedAt??null,closedAt:he.closedAt??null});continue}}try{let he=await r(ee,ne.number);he?.number!=null&&Nt.set(Number(he.number),he)}catch(he){jV(he)&&O.add(ce(ne.repo,ne.number))}}}if(Nt)for(let ne of we){let he=ce(ne.repo,ne.number),Be=Nt.get(Number(ne.number));if(!Be){if(O.has(he)){let Pt=(ql.get(he)||0)+1;ql.set(he,Pt),Pt>=HM&&(await GM(ne,Q,ge),ql.delete(he),Wl.delete(he))}continue}if(ql.delete(he),Be.state==="MERGED"||Be.state==="CLOSED")Be.state==="MERGED"?await eC(ne,Be,Q,ge):await ez(ne,Be,Q,ge),Wl.delete(he);else{await ZV(ne,Be,Q,ge).catch(()=>{}),await QV(ne,Be,Q,ge).catch(()=>{});let Pt=!ne.trackId||W.get(ne.trackId)===he;ne.autoMerge&&Pt&&await YV(ne,Q,ge).catch(()=>{});try{if(B(ne)&&!Xe.has(he)&&Pe(ne.repo,ne.number),Xe.has(he)){let tt=await Xe.get(he).catch(()=>null);tt?.checks&&await le(ne.repo,tt)}}catch{}}}}let de={};for(let{lane:ee}of Ae.values())de[ee]=(de[ee]||0)+1;return console.log(`[pr-watch] probed ${Z.length} link(s) (${Object.entries(de).map(([ee,we])=>`${ee} ${we}`).join(", ")}) in ${Date.now()-xe}ms`),Z.length}function VM(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 ak=!1,Bf=!1,Lf=[],zM=Promise.resolve();async function tz(){for(;Bf||Lf.length;){if(Bf){Bf=!1;try{await KM()}catch{}continue}let e=Lf;Lf=[];let t=e.map(r=>r.spec),n=Object.assign({},...e.map(r=>r.deps||{}));try{await KM({...n,only:r=>t.some(o=>VM(o,r)),wantDetail:r=>t.some(o=>o.detail&&VM(o,r))})}catch{}}}function jf(e,t){return e===null?Bf=!0:Lf.push({spec:e,deps:t}),ak||(ak=!0,zM=tz().finally(()=>{ak=!1})),zM}function tC(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 jf({repo:n,numbers:r,branch:e.branch||null,event:e.event||null,detail:e.detail===!0},t)}var nz=6e4,JM=0;function nC(e=void 0){let t=e?.now??Date.now();return t-JM<nz?Promise.resolve():(JM=t,jf({all:!0},e))}function rC(){jf(null),setInterval(()=>jf(null),YM)}xs();sp();Jc();Ir();an();var rz=new Set(["opened","reopened","closed","synchronize","ready_for_review","converted_to_draft","edited"]);async function oz({tracks:e=ke,reconcile:t=yp}={}){for(let n of(await e().catch(()=>[])).filter(r=>!Ue(r)))await t(n,{bypassThrottle:!0}).catch(()=>{})}function oC(e,t={}){let{invalidate:n=ln,probe:r=tC,poke:o=NE,reconcileOpened:s=oz}=t;try{n()}catch{}let a={probe:null,poked:!1,reconciled:!1};if(!e?.repo)return a;if(e.event==="pull_request"&&rz.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}pt();be();Ir();an();import{randomUUID as sz}from"node:crypto";var az=2e4,sC=3,yi=new Map,lk=new Set;async function aC(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 iz(e={}){let{sessions:t=null,tracks:n=null,append:r=N,read:o=K,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await kn(),i=n??await ke()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(Ue(u)||!u.leadBox)continue;let d=(u.boxes||[]).find(f=>f.name===u.leadBox);if(!d||d.agent!=="claude"||d.state!=="live")continue;let p=c.get(d.name);if(p)if(l.add(d.name),p.alternateOn){let f=(yi.get(d.name)||0)+1;yi.set(d.name,f),f===sC&&(lk.add(d.name),await aC(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:sz(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let f=(yi.get(d.name)||0)>=sC;if(yi.delete(d.name),f||!lk.has(d.name)){lk.add(d.name);let w=await aC(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[...yi.keys()])l.has(u)||yi.delete(u)}var Vl=null,uk=!1;function iC(){return Vl?()=>{}:(Vl=setInterval(()=>{uk||(uk=!0,iz().catch(()=>{}).finally(()=>{uk=!1}))},az),Vl.unref?.(),()=>{clearInterval(Vl),Vl=null})}ws();Jc();be();xs();vs();ws();Pc();import{randomUUID as cz}from"node:crypto";async function lz(){let e=await Promise.resolve().then(()=>(Ir(),Q$));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var dk={listRepoPrs:Kn,readEvents:K,appendEvent:N,sayDeps:lz,currentBranch:ko};async function uz(e,t=dk,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??ko)(e.workspace?.kind==="multi"?null:e.cwd).catch(()=>null)||e?.workspace?.branch;if(!r)return null;let o;try{o=await t.listRepoPrs(e.cwd,{caller:"pr-exists"})}catch{return null}let s=(o||[]).find(a=>a.headRefName===r);return s?{number:s.number,url:s.url??null,title:s.title??null}:null}async function pk(e,t,{actions:n,notify:r=!1}={},o=dk){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 uz(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=cz();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 fk(e,{offerId:t,choice:n}={},r=dk){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');return vo(`prx:${e.id}:${t}`,()=>dz(e,{offerId:t,choice:n},r))}async function dz(e,{offerId:t,choice:n},r){let o=await r.readEvents(e),s=o.find(u=>u.type==="pr.exists.offered"&&u.payload?.offerId===t);if(!s)throw new Error("unknown offer");if(o.some(u=>u.type==="pr.exists.answered"&&u.payload?.offerId===t))throw new Error("offer already answered");let a=s.payload||{},i=Array.isArray(a.actions)&&a.actions.length?a.actions:["commit","push","pr"],c=null,l=null;if(n==="push"){let{sayToLead:u}=await r.sayDeps();l=ho.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(e.id,Ma(l,{branch:a.branch??null,base:a.base??null,toPr:{number:a.pr?.number??null},offeredRebase:a.offeredRebase===!0}),{kind:"push-go"})}else if(n==="new"){let{sayToLead:u}=await r.sayDeps();l=i,c=await u(e.id,cT(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}}Pc();import Ur from"node:fs/promises";import bi from"node:path";import mC from"node:os";import cC from"node:fs/promises";import Ff from"node:path";import{execFile as pz}from"node:child_process";import{promisify as fz}from"node:util";import{fileURLToPath as mz}from"node:url";var hz=fz(pz),uC=Ff.dirname(mz(import.meta.url)),wz=Ff.resolve(uC,"..","..","client","dist"),gz="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",yz=new Map([...gz].map((e,t)=>[e,t]));function bz(e){let t=[],n=0,r=0;for(let o of e){let s=yz.get(o);if(s===void 0)return t;n+=(s&31)<<r,s&32?r+=5:(t.push(n&1?-(n>>>1):n>>>1),n=0,r=0)}return t}function kz(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=bz(c);l.length!==0&&(i+=l[0],l.length>=4&&(n+=l[1],r+=l[2],o+=l[3],a.push([i,n,r,o])))}t.push(a)}return t}function xz(e,t){if(!e||e.length===0||e[0][0]>t)return null;let n=0,r=e.length-1;for(;n<r;){let o=n+r+1>>1;e[o][0]<=t?n=o:r=o-1}return e[n]}var lC=new Map;async function vz(e){let t;try{t=await cC.stat(e)}catch{return null}let n=lC.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await cC.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:kz(r.mappings||"")};return lC.set(e,o),o}catch{return null}}var mk=null;function Sz(){return mk||(mk=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):hz("git",["rev-parse","--short","HEAD"],{cwd:Ff.resolve(uC,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),mk}var Az=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function Tz(e,{distDir:t=wz}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(Az)){let[a,i,c,l]=s,u=await vz(Ff.join(t,"assets",`${i}.map`)),d=u?xz(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(e.slice(r,s.index)),d&&u.sources[d[1]]){let p=u.sources[d[1]].replace(/^(\.\.\/)+/,"");n.push(`${p}:${d[2]+1}:${d[3]+1}`),o=!0}else n.push(a);r=s.index+a.length}return n.push(e.slice(r)),{stack:n.join(""),symbolicated:o}}async function dC(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await Sz();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await Tz(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var Ez=()=>process.env.BOSTRAT_STATE_DIR||bi.join(process.env.HOME||mC.tmpdir(),".bostrat"),Fo=()=>bi.join(Ez(),"telemetry"),hC=()=>bi.join(Fo(),".relay-offsets.json"),_z=50,Iz=14,$z=3e4,pC=500,tn=(e,t)=>typeof e=="string"?e.slice(0,t):"";function Rz(e,t){if(!e||typeof e!="object")return null;let n=tn(e.name,64);if(!n)return null;let r=Number.isFinite(e.ts)?e.ts:t,o={name:n,ts:Math.abs(r-t)<168*3600*1e3?r:t,receivedAt:t,anonId:tn(e.anonId,64),sessionId:tn(e.sessionId,64),userId:tn(e.userId,64),release:tn(e.release,64),appVersion:tn(e.appVersion,32),route:tn(e.route,128),deviceClass:e.deviceClass==="phone"||e.deviceClass==="desktop"?e.deviceClass:""};return e.props&&typeof e.props=="object"&&!Array.isArray(e.props)&&(JSON.stringify(e.props).length<=8192?o.props=e.props:o.props={clamped:!0}),e.error&&typeof e.error=="object"&&(o.error={kind:tn(e.error.kind,256),message:tn(e.error.message,8192),stack:tn(e.error.stack,32768),fingerprint:tn(e.error.fingerprint,64)}),Array.isArray(e.breadcrumbs)&&(o.breadcrumbs=e.breadcrumbs.slice(-50).map(s=>({t:Number.isFinite(s?.t)?s.t:0,n:tn(s?.n,64),r:tn(s?.r,128)}))),o}var wC=e=>new Date(e).toISOString().slice(0,10),fC=0;async function gC(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,_z):[]).map(s=>Rz(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await dC(s);await Ur.mkdir(Fo(),{recursive:!0});let o=bi.join(Fo(),`${wC(t)}.ndjson`);return await Ur.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
799
|
+
`).join(""),"utf8"),t-fC>6*3600*1e3&&(fC=t,Oz(t).catch(()=>{})),r.length}async function Oz(e=Date.now()){let t=wC(e-Iz*24*3600*1e3),n=[];try{n=await Ur.readdir(Fo())}catch{return}let r=n.filter(s=>s.endsWith(".ndjson")&&s.slice(0,10)<t);if(!r.length)return;for(let s of r)await Ur.rm(bi.join(Fo(),s),{force:!0});let o=await bC();for(let s of r)delete o[s];await kC(o)}var Uf=new Map;function yC(e,t=Date.now()){let n=e||"unknown",r=Uf.get(n);return r||(Uf.size>512&&Uf.clear(),r={tokens:40,at:t},Uf.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 bC(){try{return JSON.parse(await Ur.readFile(hC(),"utf8"))||{}}catch{return{}}}async function kC(e){await Ur.mkdir(Fo(),{recursive:!0}),await Ur.writeFile(hC(),JSON.stringify(e),"utf8")}function Mz(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||mC.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 Cz({fetchImpl:e=fetch}={}){let t=await IR();if(!t)return{sent:0,disabled:!0};let n;try{n=(await Ur.readdir(Fo())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await bC(),o=0;for(let s of n){let a=bi.join(Fo(),s),i=r[s]||0,c;try{let p=await Ur.open(a,"r");try{let f=(await p.stat()).size;if(f<=i)continue;c=Buffer.alloc(f-i),await p.read(c,0,c.length,i)}finally{await p.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
|
|
800
|
+
`).filter(Boolean),d=[];for(let p of u)try{d.push(JSON.parse(p))}catch{}for(let p=0;p<d.length;p+=pC){let f=d.slice(p,p+pC).map(h=>Mz(h)),w=await e(`${t.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.ingestKey}`},body:JSON.stringify({events:f})}).catch(()=>null);if(!w||!w.ok)return{sent:o,disabled:!1};o+=f.length}r[s]=i+l+1,await kC(r)}return{sent:o,disabled:!1}}var hk=null,wk=!1;function xC(){hk||(hk=setInterval(async()=>{if(!wk){wk=!0;try{await Cz()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{wk=!1}}},$z),hk.unref?.())}import Wf from"node:fs/promises";import EC from"node:path";import Nz from"node:os";var _C=()=>process.env.BOSTRAT_STATE_DIR||EC.join(process.env.HOME||Nz.tmpdir(),".bostrat"),IC=()=>EC.join(_C(),"dictation-glossary.json"),Pz=100,Dz=500,Bz=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],qf=null,gk=null,yk=Promise.resolve();function vC(){let e={},t=Date.now();for(let n of Bz)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function $C(){return qf||(gk??=Wf.readFile(IC(),"utf8").then(e=>JSON.parse(e)?.terms??vC()).catch(()=>vC()).then(e=>qf=e).finally(()=>gk=null),gk)}async function SC(){let e=JSON.stringify({terms:qf},null,2),t=IC(),n=`${t}.${process.pid}.tmp`;await Wf.mkdir(_C(),{recursive:!0}),await Wf.writeFile(n,e),await Wf.rename(n,t)}function Lz(){return yk=yk.then(SC,SC),yk.catch(()=>{})}async function bk(){return Object.values(await $C()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var jz=e=>Math.max(1,Math.ceil(e.length/4));async function Fz(){let e=[],t=0;for(let{term:n}of await bk()){let r=jz(n);if(!(t+r>Dz)&&(e.push(n),t+=r,e.length>=Pz))break}return e}async function Uz(e,t){let n=await $C(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var AC=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Hf=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 RC(e,t){let n=AC(e),r=AC(t);if(!n.length||!r.length)return[];let o=n.length,s=r.length,a=Array.from({length:o+1},()=>new Array(s+1).fill(0));for(let u=o-1;u>=0;u--)for(let d=s-1;d>=0;d--)a[u][d]=Hf(n[u])===Hf(r[d])?a[u+1][d+1]+1:Math.max(a[u+1][d],a[u][d+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)Hf(n[c])===Hf(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(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 Uz(u,"learned");return await Lz(),[...i]}var zl=null;function Hz(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 OC(){if(!zl)return null;let e=await Tl();if(!e||e.apiKey.slice(-6)!==zl.keyTail)return null;let{keyTail:t,...n}=zl;return n}async function MC(e,t){let n=await Tl();if(!n){let i=new Error("Deepgram is not configured \u2014 add deepgram.apiKey to ~/.bostrat/integrations.json");throw i.status=503,i.code="not_configured",i}let r=new URL("https://api.deepgram.com/v1/listen");r.searchParams.set("model","nova-3"),r.searchParams.set("smart_format","true");for(let i of await Fz())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=Hz(o.status,i);c.persistent&&(zl={code:c.code,message:c.message,at:Date.now(),keyTail:n.apiKey.slice(-6)});let l=new Error(c.message);throw l.status=c.status,l.code=c.code,l}return zl=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}be();var kk={"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 Wz(e){if(!e)return null;let t=String(e).trim();if(kk[t])return t;let n=null;for(let r of Object.keys(kk))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function qz(e,t={}){let n=kk[Wz(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 Gz(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 Jl=null;async function Kz(){if(!Jl)return null;let e=await gf();if(!e||e.apiKey.slice(-6)!==Jl.keyTail)return null;let{keyTail:t,...n}=Jl;return n}async function Vz(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=Gz(t.status,o);s.persistent&&(Jl={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}Jl=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 zz={openai:Vz};async function CC(){let e=await gf();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await Kz()}:{configured:!1}}async function NC(){let e=await gf();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 zz[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function PC(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=qz(t,s),i=await N(e,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:t||"unknown",usd:a,tokens:{input:s.textIn+s.audioIn,output:s.textOut+s.audioOut,cacheRead:s.textInCached+s.audioInCached,cacheWrite:0},voice:s,seconds:Math.max(0,Math.round(Number(r)||0)),source:"voice-chat"}});return{usd:a,seq:i.seq}}an();wT(async()=>new Set((await ke()).map(e=>e.id)));var WC=Re.dirname(Yz(import.meta.url)),Tk=Number(process.env.PORT)||3362,Gf=Zy(process.env),qC=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,ki=KO(process.env.BOSTRAT_ALLOW_IPS),Kf=VO(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Cf),_k=JO(),GC=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",E=nn();E.disable("x-powered-by");E.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),Ul(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});E.use((e,t,n)=>{if(Fl(e)||ki.allowed(ki.clientIp(e))||Kf.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});E.use(tM);E.use(ZO({token:_k,enforce:GC}));E.use("/api/mocks",nn.json({limit:"1mb"}));E.use("/api/mocks",nn.raw({type:"image/png",limit:"24mb"}));E.use("/api/mocks",nn.raw({type:["video/mp4","video/webm"],limit:"105mb"}));E.use("/api/runbooks",nn.json({limit:"1mb"}));E.use("/api/tracks/adopt",nn.json({limit:"1mb"}));E.use("/api/telemetry",nn.json({limit:"256kb"}));E.use("/api/hook",nn.json({limit:"2mb"}));E.use("/api/dictation",nn.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));E.use("/api/tracks/:id/upload",nn.raw({type:()=>!0,limit:"25mb"}));E.use(nn.json({limit:"64kb"}));E.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:zt,hostTmuxAlive:await Zr(),autoTriggerPct:qC,plane:xi?xi.status():null})});E.get("/api/sessions",async(e,t)=>{t.json({sessions:await kn()})});E.post("/api/sessions",async(e,t)=>{let{displayName:n,cwd:r}=e.body||{};if(typeof n!="string")return t.status(400).json({error:"displayName (string) required"});try{let o=await jn(n,r||void 0);t.json({ok:!0,...o})}catch(o){let s=o?.message||String(o),a=/already exists/i.test(s)?409:/must be|invalid/i.test(s)?400:500;t.status(a).json({error:s})}});E.delete("/api/sessions/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!je(n))return t.status(400).json({error:"invalid session name"});if(!await Se(n))return t.status(404).json({error:"session not found"});try{await Lt(n),t.json({ok:!0})}catch(r){t.status(500).json({error:`failed to kill: ${r?.message||r}`})}});E.get("/api/context/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!je(n))return t.status(400).json({error:"invalid session name"});let r=await Se(n);if(!r)return t.status(404).json({error:"session not found"});let o=await Zi(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});E.post("/api/handoff/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!je(n))return t.status(400).json({error:"invalid session name"});try{let r=await ZS(n);t.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){t.status(409).json({error:r?.message||String(r)})}});E.get("/api/handoff/:name",(e,t)=>{let n=decodeURIComponent(e.params.name),r=hd(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 DC=10;async function KC(){let e=await ke(),[t,n]=await Promise.all([h$(e),eR(e)]);return{tracks:e,views:await cg(),queuedOf:r=>Ms(r),jobsOf:r=>t.get(r)??[],prPendingOf:r=>n.get(r)??[]}}E.get("/api/idle-guard",async(e,t)=>{try{t.json(Xy(await KC()))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var hn=Ls(),t3=mR({kind:hn}),Xl=null,Ct=()=>Xl&&["auto","prompt","off"].includes(Xl)?Xl:t3,Ek=null,n3=Sl.has(hn),VC=async({to:e,from:t})=>!n3||uf(await Al())?!1:(Ql.arm({to:e,from:t,kind:hn}),!0);async function r3(e){let t=Ct();Xl=e??null;let n=Ct();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){Ql.cancel();return}Ek&&await VC(Ek)}}var zC=(()=>{try{return e3(import.meta.url)("../package.json").version||null}catch{return null}})(),Ql=wR({isBusy:async()=>Xy(await KC()).busy,onStuck:({to:e,from:t})=>or({current:t,latest:e,installKind:hn,policy:Ct(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?or({current:t,latest:e,installKind:hn,policy:Ct(),status:"updating",reason:"auto"}):or({current:t,latest:e,installKind:hn,policy:Ct(),status:"failed",error:n?.reason})).catch(()=>{})});E.get("/api/node/self-update",async(e,t)=>{t.json({installKind:hn,policy:Ct(),policySource:Xl?"plane":"node",current:zC,status:await Al(),armed:Ql.armed})});E.post("/api/node/self-update",async(e,t)=>{let n=typeof e.body?.to=="string"?e.body.to.trim():"";if(!/^\d+\.\d+\.\d+$/.test(n))return t.status(400).json({error:"body.to must be a release version (X.Y.Z)"});let r=await df({to:n,kind:hn,reason:"manual"});if(r.ok)return Ql.cancel(),or({current:r.from,latest:n,installKind:hn,policy:Ct(),status:"updating",reason:"manual"}).catch(()=>{}),t.json({ok:!0,...r});t.status(409).json({ok:!1,error:r.reason,status:r.status??null})});async function JC(){try{let e=await hR();e&&await or({current:e.from,latest:e.to,installKind:e.kind||hn,policy:Ct(),status:e.status,error:e.error,reason:e.reason}),await dy({current:zC,installKind:hn,policy:Ct()})}catch{}}JC();setInterval(JC,3e4).unref();E.get("/api/tracks",async(e,t)=>{let n=(await ke()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>DC){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,DC).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:ml,agentAvailability:await Fp(),tickets:await YR()})});E.get("/api/cost",async(e,t)=>{try{let n=await ke();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await Bb(n);if(r.cursorUsage=Kw(),e.query.format==="text")return t.type("text/plain").send(vO(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/usage/roundup",async(e,t)=>{try{let n=await jb(await ke(),{window:e.query.window,from:e.query.from,to:e.query.to});if(e.query.format==="text")return t.type("text/plain").send(_O(n));t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/usage/spend",async(e,t)=>{try{let n=await MO(await ke(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=Kw(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=Sc(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/usage/caps",(e,t)=>{t.json(Sc())});E.post("/api/usage/caps",async(e,t)=>{try{t.json(await _d(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});E.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await UO(await ke(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await Wk(e.get(pr),{byToken:hl,legacy:()=>Lc(e.body?.session_id,e.body?.cwd)});if(n==="refused")return Ei("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return Ei("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&&Ei("mismatch",{track:r.track.id,box:r.box.name}),Ei(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 bO(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&bu(r.box.cwd),t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});E.get("/api/git/trailers",async(e,t)=>{t.type("text/plain");try{let n=await hl(e.get(pr));if(!n?.track||!n?.box)return t.status(204).end();t.send((await yE(n.track,n.box)).join(`
|
|
801
801
|
`)+`
|
|
802
|
-
`)}catch{t.status(204).end()}});_.post("/api/git/committed",async(e,t)=>{try{let n=await hl(e.get(pr));if(!n?.track||!n?.box)return t.json({ok:!1,error:"unknown hook token"});let r=String(e.body?.sha||"").trim();if(!/^[0-9a-f]{7,40}$/i.test(r))return t.json({ok:!1,error:"sha required"});t.json({ok:!0});let o=n.box.cwd?Re.resolve(n.box.cwd):null,s=typeof e.body?.cwd=="string"&&e.body.cwd?Re.resolve(e.body.cwd):null,a=o&&s&&(s===o||s.startsWith(o+Re.sep))?s:o;gE(n.track,n.box,r,{cwd:a}).catch(i=>console.warn(`[rework] ${n.track.id}: recordCommit failed: ${i?.message||i}`))}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});_.post("/api/tracks/:id/rework-cause",async(e,t)=>{try{let n=await 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 bE(n,String(r||""),String(o||""))})}catch(n){let r=n?.message||String(n);t.status(/unknown rework/i.test(r)?404:/already answered/i.test(r)?409:400).json({error:r})}});_.post("/api/tracks/:id/rework-cause/retract",async(e,t)=>{try{let n=await me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,event:await kE(n,String(e.body?.sha||""))})}catch(n){let r=n?.message||String(n);t.status(/no standing answer/i.test(r)?409:400).json({error:r})}});_.get("/api/usage/rework",async(e,t)=>{try{t.json(await OO(await ke(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.post("/api/telemetry",async(e,t)=>{try{if(!wC(ki.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await hC(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});_.get("/api/repos",async(e,t)=>{try{let n=await fr(),r=new Set((await Promise.all(n.map(a=>Me(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await Ci()).repos||[]).filter(a=>!r.has(String(a.slug).toLowerCase())).map(a=>({slug:a.slug,name:a.slug.split("/")[1],branch:a.defaultBranch||null,private:!!a.private,remote:!0}));t.json({repos:[...n,...s]})}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/github/token",async(e,t)=>{try{t.json(await ta(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});_.get("/api/github/status",(e,t)=>{t.json({configured:uu(),calls:DT(),latency:FT(),webhook:LM()})});_.post("/api/github/event",nn.json({limit:"64kb"}),(e,t)=>{let n=DM(e.body,{onAccept:r=>nC(r)});if(!n.ok)return t.status(400).json({error:n.error});t.json({ok:!0,duplicate:n.duplicate})});_.post("/api/github/refresh",(e,t)=>{n0(),t.json({ok:!0})});_.get("/api/gmail/search",async(e,t)=>{try{let n=await UO(String(e.query.q||""),{max:Number(e.query.max)||10});t.json({messages:n})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});_.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await HO(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});async function Zl({repo:e=null,number:t=null,branch:n=null}){let r=e?await Me(e):null;if(r&&t!=null){let s=await ft(r,t),a=s&&await me(s.trackId);if(a)return a}if(!n)return null;let o=await ke();return Jd(o,n,s=>Yd(s,{branch:n,repo:e,slug:r}))}async function zC({repo:e=null,number:t=null,branch:n=null}){let r=await Zl({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!Ue(r))return r;if(!n)return null;let o=e?await Me(e).catch(()=>null):null,s=await ke();return Jd(s,n,a=>!Ue(a)&&Yd(a,{branch:n,repo:e,slug:o}))}async function Dn(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Zl({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await Me(s).catch(()=>null):null);await N({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:e,number:t,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function t3(e){let t=(await ke()).filter(n=>!Ue(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await Me(n.path);if(r)for(let o of n.prs){if(await ft(r,o.number))continue;let s=await Jd(t,o.headRefName,a=>Yd(a,{branch:o.headRefName,repo:n.path,slug:r}));s&&await Yt({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}_.get("/api/git/prs",async(e,t)=>{try{let n=await VT();await t3(n.repos).catch(()=>{}),await Df(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.post("/api/git/prs/merge",async(e,t)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:d}=e.body||{},p=null,f=null,w=null;try{if(s){let g=o||"merge";if(!$c.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 A=await Me((await Ar(n)).path);if(!A)throw new Error("repo has no GitHub remote");let I=await Zl({repo:n,number:x,branch:l}).catch(()=>null);if(!I)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(p=I,f=await $p(I,"merge",{count:1,summary:`arming auto-merge on PR #${x}`}),a&&await Rc(n,x),await Yt({repo:A,number:x,trackId:I.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Tc(A,x,{merge:{method:g}}))throw new Error("couldn't record the auto-merge arm");return await gs(A,x,i===!0),await ys(A,x,c===!0&&i!==!0),ln(),await Dn("auto-armed",{number:x,url:d,title:u,branch:l,repo:n,track:I,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:x,auto:!0})}p=await Zl({repo:n,number:r,branch:l}).catch(()=>null),p&&(f=await $p(p,"merge",{count:1,summary:`merging PR #${r}`}));let h=await Ba(n,r,{method:o||"squash"});await Dn("merged",{number:h.number,url:d,title:u,branch:l,repo:n});let y=await Me(n).catch(()=>null),k=!1;if(y&&(i===!0&&await gs(y,h.number,!0).catch(()=>{}),c===!0&&i!==!0&&await ys(y,h.number,!0).catch(()=>{}),await _r(y,h.number).catch(()=>{}),await Ds(y,h.number).catch(()=>{}),k=await Er(y,h.number).catch(()=>!1),await wo(y,h.number,"merged").catch(()=>{})),i===!0&&!k){let g=await Zl({repo:n,number:h.number,branch:l}).catch(()=>null);g&&!g.archivedAt&&await Ps(g.id).catch(()=>{})}t.json(h)}catch(h){w=h;let y=h?.message||String(h),k=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(y)?400:502;t.status(k).json({error:y})}finally{p&&f&&await Vg(p,f,w?"failed":"completed",w).catch(()=>{})}});_.post("/api/git/prs/disarm-auto",async(e,t)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=e.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await Me((await Ar(n)).path).catch(()=>null),l=c?await Ec(c,i).catch(()=>null):null;try{await ww(n,i)}catch(u){if(!l)throw u}ln(),await Dn("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),t.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;t.status(l).json({error:c})}});_.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await Me(n).catch(()=>null),s=await ZT(n,{method:r||"merge",onMerged:a=>{Dn("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&_r(o,a.number).catch(()=>{}).then(()=>Ds(o,a.number)).catch(()=>{}).then(()=>Er(o,a.number)).catch(()=>{}).then(()=>wo(o,a.number,"merged")).catch(()=>{})}});t.json(s)}catch(o){let s=o?.message||String(o),a=/already running/i.test(s)?409:/invalid|unknown repo|no PRs ready/i.test(s)?400:502;t.status(a).json({error:s})}});_.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:XT()})});_.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(!$c.has(u))throw new Error(`invalid merge method: ${u}`);if(!n)return t.status(400).json({error:"trackId required"});let d=await me(n);if(!d)return t.status(404).json({error:`Track ${n} isn't on this node`});i=d;let p=(await En()).filter(h=>h.trackId===d.id&&h.state!=="merged"&&h.state!=="closed").sort(Oc);if(!p.length)return t.status(409).json({error:"no open PRs on this Track"});c=await $p(d,"merge",{count:p.length,summary:p.length===1?"merging 1 PR":`merging ${p.length} PRs`});for(let h of p)await gs(h.repo,h.number,o===!0).catch(()=>{}),await ys(h.repo,h.number,s===!0&&o!==!0).catch(()=>{});let f=[],w=[];for(let h of p){let y={repo:h.repo,number:h.number,ok:!0,action:"armed"};f.push(y);try{let k=await ks(h.repo);if(!k)throw new Error(`no local checkout for ${h.repo}`);let g=await Da(k,h.number);w.push({r:y,l:h,repoPath:k,pr:g,plan:zT(g,{update:r!==!1})})}catch(k){y.ok=!1,y.action="failed",y.reason=k?.message||String(k)}}for(let{r:h,l:y,repoPath:k,pr:g,plan:x}of JT(w))try{if(x.action==="skip")h.action="skipped",h.reason=x.reason;else if(x.action==="merge")await Ba(k,y.number,{method:u}),await Dn("merged",{number:y.number,url:g.url,title:g.title,branch:g.headRefName,repo:k,track:d}),await _r(y.repo,y.number).catch(()=>{}),await Ds(y.repo,y.number).catch(()=>{}),await Er(y.repo,y.number).catch(()=>{}),await wo(y.repo,y.number,"merged").catch(()=>{}),h.action="merged";else{if(x.action==="update-arm"&&(await Rc(k,y.number).catch(()=>{}),h.action="updated-armed"),!await Tc(y.repo,y.number,{merge:{method:u}}))throw new Error("couldn't record the auto-merge arm");await Dn("auto-armed",{number:y.number,url:g.url,title:g.title,branch:g.headRefName,repo:k,track:d,...h.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(A){h.ok=!1,h.action="failed",h.reason=A?.message||String(A)}if(ln(),!f.some(h=>h.ok)){if(o===!0)for(let h of p)await gs(h.repo,h.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let h of p)await ys(h.repo,h.number,!1).catch(()=>{});return t.status(502).json({error:f.map(h=>`${h.repo}#${h.number}: ${h.reason}`).join("; "),results:f})}t.json({ok:!0,trackId:d.id,results:f})}catch(u){l=u;let d=u?.message||String(u),p=/invalid|required/i.test(d)?400:502;t.status(p).json({error:d})}finally{i&&c&&await Vg(i,c,l?"failed":"completed",l).catch(()=>{})}});_.post("/api/git/prs/disarm-track",async(e,t)=>{let{trackId:n}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let r=await me(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await En()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await Ec(a.repo,a.number).catch(()=>null),c=await ks(a.repo).catch(()=>null);c&&await ww(c,a.number).catch(()=>{}),i&&(await Dn("auto-canceled",{number:a.number,url:a.url,title:a.title,branch:a.headRef,repo:c,track:r}),s.push({repo:a.repo,number:a.number}))}ln(),t.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;t.status(s).json({error:o})}});_.post("/api/git/prs/open",async(e,t)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let i=await me(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=Gt(i.workspace),d,p=Array.isArray(a)?a.filter(Boolean):[];if(p.length)d=p.map(h=>{let y=u.find(k=>k.name===h||k.repo===h||k.base===h||k.dir===h);if(!y)throw new Error(`invalid repo "${h}" \u2014 not tagged on this Track`);return y});else if(d=await Es(i),!d.length)return t.status(409).json({error:"no tagged repo has work to ship"});let f=[];for(let h of d)try{let y=await yw(h.dir,{title:r,body:o,base:s}),k=h.repo||await Me(h.dir).catch(()=>null);k&&y.number&&await Yt({repo:k,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Dn("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:k??h.name}}),f.push({repo:h.name,slug:k??null,ok:!0,url:y.url,number:y.number})}catch(y){f.push({repo:h.name,slug:h.repo??null,ok:!1,error:y?.message||String(y)})}let w=f.filter(h=>h.ok);return w.length?t.json({ok:!0,multi:!0,results:f,url:w[0].url,number:w[0].number}):t.status(502).json({error:f.map(h=>`${h.repo}: ${h.error}`).join("; "),results:f})}let c=await yw(i.cwd,{title:r,body:o,base:s}),l=await Me(i.cwd).catch(()=>null);l&&c.number&&await Yt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Dn("opened",{number:c.number,url:c.url,title:r,track:i,branch:i.workspace?.branch,extra:{...l?{repo:l}:{}}}),t.json(c)}catch(i){let c=i?.message||String(i),l=/required|invalid/i.test(c)?400:502;t.status(l).json({error:c})}});_.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 Es(n),o=new Map(r.map(a=>[a.base,a])),s=Gt(n.workspace).map(a=>{let i=o.get(a.base);return{name:a.name,base:a.base,baseRef:a.baseRef??null,repo:a.repo??null,dirty:i?.dirty??!1,ahead:i?.ahead??0,changed:!!i}});t.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.post("/api/tracks/:id/repos",async(e,t)=>{let{repo:n}=e.body||{};try{if(!n||typeof n!="string")return t.status(400).json({error:"repo (path or owner/name) required"});let r=await Gp(e.params.id,n);t.json({ok:!0,added:{name:r.name,base:r.base,baseRef:r.baseRef??null,repo:r.repo??null}})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/already tagged|needs a multi|not a usable|archived|required/i.test(o)?400:502;t.status(s).json({error:o})}});_.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await Kp(e.params.id,e.params.name);t.json({ok:!0,...n})}catch(n){let r=n?.message||String(n),o=/track .* not found/i.test(r)?404:/has work|still running|last repo/i.test(r)?409:/needs a multi|not tagged/i.test(r)?400:502;t.status(o).json({error:r})}});_.post("/api/tracks/:id/reshape",async(e,t)=>{let{repos:n,skipPermissions:r}=e.body||{};try{let o=await _y(e.params.id,n,{skipPermissions:r!==!1});t.status(o.finishedAt?200:202).json({ok:!0,...Ik(o)})}catch(o){let s=o?.message||String(o),a=o?.status||(/not found/i.test(s)?404:/has work|still running|already|running|archived|last repo/i.test(s)?409:/required|not a usable|tagged twice/i.test(s)?400:502);t.status(a).json({error:s})}});_.get("/api/tracks/:id/reshape",(e,t)=>{t.json(Ik(gl(e.params.id))??{running:!1})});function Ik(e){return e?{running:!e.finishedAt,step:e.step,error:e.error??null,inPlace:e.inPlace===!0,added:e.added??[],removed:e.removed??[],scratch:e.scratch===!0,views:e.views??0,checkpoint:e.checkpoint??null,startedAt:e.startedAt??null,finishedAt:e.finishedAt??null}:null}_.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Da(n,r),s=await Me(n).catch(()=>null),a=await zC({repo:n,number:r,branch:o.headRefName});if(!a)return t.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=Gt(a.workspace).find(k=>k.base===n||s&&k.repo===s)??null,c=a.workspace?.kind==="multi",l=await QT(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?Re.join("ci",i.name):"ci",d=Re.join(a.cwd,".bostrat",u),p=Re.join(".bostrat",u,`pr-${o.number}-fail.log`);await Et.promises.mkdir(d,{recursive:!0}),await Et.promises.writeFile(Re.join(a.cwd,p),l.map(k=>`\u2550\u2550\u2550\u2550 ${k.name}${k.link?` \xB7 ${k.link}`:""} \u2550\u2550\u2550\u2550
|
|
802
|
+
`)}catch{t.status(204).end()}});E.post("/api/git/committed",async(e,t)=>{try{let n=await hl(e.get(pr));if(!n?.track||!n?.box)return t.json({ok:!1,error:"unknown hook token"});let r=String(e.body?.sha||"").trim();if(!/^[0-9a-f]{7,40}$/i.test(r))return t.json({ok:!1,error:"sha required"});t.json({ok:!0});let o=n.box.cwd?Re.resolve(n.box.cwd):null,s=typeof e.body?.cwd=="string"&&e.body.cwd?Re.resolve(e.body.cwd):null,a=o&&s&&(s===o||s.startsWith(o+Re.sep))?s:o;bE(n.track,n.box,r,{cwd:a}).catch(i=>console.warn(`[rework] ${n.track.id}: recordCommit failed: ${i?.message||i}`))}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});E.post("/api/tracks/:id/rework-cause",async(e,t)=>{try{let n=await me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let{sha:r,cause:o}=e.body||{};t.json({ok:!0,event:await xE(n,String(r||""),String(o||""))})}catch(n){let r=n?.message||String(n);t.status(/unknown rework/i.test(r)?404:/already answered/i.test(r)?409:400).json({error:r})}});E.post("/api/tracks/:id/rework-cause/retract",async(e,t)=>{try{let n=await me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,event:await vE(n,String(e.body?.sha||""))})}catch(n){let r=n?.message||String(n);t.status(/no standing answer/i.test(r)?409:400).json({error:r})}});E.get("/api/usage/rework",async(e,t)=>{try{t.json(await CO(await ke(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.post("/api/telemetry",async(e,t)=>{try{if(!yC(ki.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await gC(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});E.get("/api/repos",async(e,t)=>{try{let n=await fr(),r=new Set((await Promise.all(n.map(a=>Me(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await Ci()).repos||[]).filter(a=>!r.has(String(a.slug).toLowerCase())).map(a=>({slug:a.slug,name:a.slug.split("/")[1],branch:a.defaultBranch||null,private:!!a.private,remote:!0}));t.json({repos:[...n,...s]})}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/github/token",async(e,t)=>{try{t.json(await ta(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});E.get("/api/github/status",(e,t)=>{t.json({configured:uu(),calls:LT(),latency:HT(),webhook:FM()})});E.post("/api/github/event",nn.json({limit:"64kb"}),(e,t)=>{let n=LM(e.body,{onAccept:r=>oC(r)});if(!n.ok)return t.status(400).json({error:n.error});t.json({ok:!0,duplicate:n.duplicate})});E.post("/api/github/refresh",(e,t)=>{n0(),t.json({ok:!0})});E.get("/api/gmail/search",async(e,t)=>{try{let n=await WO(String(e.query.q||""),{max:Number(e.query.max)||10});t.json({messages:n})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});E.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await qO(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});async function Zl({repo:e=null,number:t=null,branch:n=null}){let r=e?await Me(e):null;if(r&&t!=null){let s=await ft(r,t),a=s&&await me(s.trackId);if(a)return a}if(!n)return null;let o=await ke();return Jd(o,n,s=>Yd(s,{branch:n,repo:e,slug:r}))}async function YC({repo:e=null,number:t=null,branch:n=null}){let r=await Zl({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!Ue(r))return r;if(!n)return null;let o=e?await Me(e).catch(()=>null):null,s=await ke();return Jd(s,n,a=>!Ue(a)&&Yd(a,{branch:n,repo:e,slug:o}))}async function Dn(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Zl({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await Me(s).catch(()=>null):null);await N({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:e,number:t,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function o3(e){let t=(await ke()).filter(n=>!Ue(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await Me(n.path);if(r)for(let o of n.prs){if(await ft(r,o.number))continue;let s=await Jd(t,o.headRefName,a=>Yd(a,{branch:o.headRefName,repo:n.path,slug:r}));s&&await Yt({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}E.get("/api/git/prs",async(e,t)=>{try{let n=await JT();await o3(n.repos).catch(()=>{}),await Df(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.post("/api/git/prs/merge",async(e,t)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:d}=e.body||{},p=null,f=null,w=null;try{if(s){let g=o||"merge";if(!$c.has(g))throw new Error(`invalid merge method: ${g}`);let x=Number(r);if(!Number.isInteger(x)||x<=0)throw new Error("invalid PR number");let S=await Me((await Ar(n)).path);if(!S)throw new Error("repo has no GitHub remote");let _=await Zl({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 $p(_,"merge",{count:1,summary:`arming auto-merge on PR #${x}`}),a&&await Rc(n,x),await Yt({repo:S,number:x,trackId:_.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Tc(S,x,{merge:{method:g}}))throw new Error("couldn't record the auto-merge arm");return await gs(S,x,i===!0),await ys(S,x,c===!0&&i!==!0),ln(),await Dn("auto-armed",{number:x,url:d,title:u,branch:l,repo:n,track:_,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:x,auto:!0})}p=await Zl({repo:n,number:r,branch:l}).catch(()=>null),p&&(f=await $p(p,"merge",{count:1,summary:`merging PR #${r}`}));let h=await Ba(n,r,{method:o||"squash"});await Dn("merged",{number:h.number,url:d,title:u,branch:l,repo:n});let y=await Me(n).catch(()=>null),k=!1;if(y&&(i===!0&&await gs(y,h.number,!0).catch(()=>{}),c===!0&&i!==!0&&await ys(y,h.number,!0).catch(()=>{}),await _r(y,h.number).catch(()=>{}),await Ds(y,h.number).catch(()=>{}),k=await Er(y,h.number).catch(()=>!1),await wo(y,h.number,"merged").catch(()=>{})),i===!0&&!k){let g=await Zl({repo:n,number:h.number,branch:l}).catch(()=>null);g&&!g.archivedAt&&await Ps(g.id).catch(()=>{})}t.json(h)}catch(h){w=h;let y=h?.message||String(h),k=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(y)?400:502;t.status(k).json({error:y})}finally{p&&f&&await Vg(p,f,w?"failed":"completed",w).catch(()=>{})}});E.post("/api/git/prs/disarm-auto",async(e,t)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=e.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await Me((await Ar(n)).path).catch(()=>null),l=c?await Ec(c,i).catch(()=>null):null;try{await ww(n,i)}catch(u){if(!l)throw u}ln(),await Dn("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),t.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;t.status(l).json({error:c})}});E.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await Me(n).catch(()=>null),s=await eE(n,{method:r||"merge",onMerged:a=>{Dn("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&_r(o,a.number).catch(()=>{}).then(()=>Ds(o,a.number)).catch(()=>{}).then(()=>Er(o,a.number)).catch(()=>{}).then(()=>wo(o,a.number,"merged")).catch(()=>{})}});t.json(s)}catch(o){let s=o?.message||String(o),a=/already running/i.test(s)?409:/invalid|unknown repo|no PRs ready/i.test(s)?400:502;t.status(a).json({error:s})}});E.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:QT()})});E.post("/api/git/prs/merge-track",async(e,t)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=e.body||{},i=null,c=null,l=null;try{let u=a||"merge";if(!$c.has(u))throw new Error(`invalid merge method: ${u}`);if(!n)return t.status(400).json({error:"trackId required"});let d=await me(n);if(!d)return t.status(404).json({error:`Track ${n} isn't on this node`});i=d;let p=(await En()).filter(h=>h.trackId===d.id&&h.state!=="merged"&&h.state!=="closed").sort(Oc);if(!p.length)return t.status(409).json({error:"no open PRs on this Track"});c=await $p(d,"merge",{count:p.length,summary:p.length===1?"merging 1 PR":`merging ${p.length} PRs`});for(let h of p)await gs(h.repo,h.number,o===!0).catch(()=>{}),await ys(h.repo,h.number,s===!0&&o!==!0).catch(()=>{});let f=[],w=[];for(let h of p){let y={repo:h.repo,number:h.number,ok:!0,action:"armed"};f.push(y);try{let k=await ks(h.repo);if(!k)throw new Error(`no local checkout for ${h.repo}`);let g=await Da(k,h.number);w.push({r:y,l:h,repoPath:k,pr:g,plan:YT(g,{update:r!==!1})})}catch(k){y.ok=!1,y.action="failed",y.reason=k?.message||String(k)}}for(let{r:h,l:y,repoPath:k,pr:g,plan:x}of XT(w))try{if(x.action==="skip")h.action="skipped",h.reason=x.reason;else if(x.action==="merge")await Ba(k,y.number,{method:u}),await Dn("merged",{number:y.number,url:g.url,title:g.title,branch:g.headRefName,repo:k,track:d}),await _r(y.repo,y.number).catch(()=>{}),await Ds(y.repo,y.number).catch(()=>{}),await Er(y.repo,y.number).catch(()=>{}),await wo(y.repo,y.number,"merged").catch(()=>{}),h.action="merged";else{if(x.action==="update-arm"&&(await Rc(k,y.number).catch(()=>{}),h.action="updated-armed"),!await Tc(y.repo,y.number,{merge:{method:u}}))throw new Error("couldn't record the auto-merge arm");await Dn("auto-armed",{number:y.number,url:g.url,title:g.title,branch:g.headRefName,repo:k,track:d,...h.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(S){h.ok=!1,h.action="failed",h.reason=S?.message||String(S)}if(ln(),!f.some(h=>h.ok)){if(o===!0)for(let h of p)await gs(h.repo,h.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let h of p)await ys(h.repo,h.number,!1).catch(()=>{});return t.status(502).json({error:f.map(h=>`${h.repo}#${h.number}: ${h.reason}`).join("; "),results:f})}t.json({ok:!0,trackId:d.id,results:f})}catch(u){l=u;let d=u?.message||String(u),p=/invalid|required/i.test(d)?400:502;t.status(p).json({error:d})}finally{i&&c&&await Vg(i,c,l?"failed":"completed",l).catch(()=>{})}});E.post("/api/git/prs/disarm-track",async(e,t)=>{let{trackId:n}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let r=await me(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await En()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await Ec(a.repo,a.number).catch(()=>null),c=await ks(a.repo).catch(()=>null);c&&await ww(c,a.number).catch(()=>{}),i&&(await Dn("auto-canceled",{number:a.number,url:a.url,title:a.title,branch:a.headRef,repo:c,track:r}),s.push({repo:a.repo,number:a.number}))}ln(),t.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;t.status(s).json({error:o})}});E.post("/api/git/prs/open",async(e,t)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let i=await me(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=Gt(i.workspace),d,p=Array.isArray(a)?a.filter(Boolean):[];if(p.length)d=p.map(h=>{let y=u.find(k=>k.name===h||k.repo===h||k.base===h||k.dir===h);if(!y)throw new Error(`invalid repo "${h}" \u2014 not tagged on this Track`);return y});else if(d=await Es(i),!d.length)return t.status(409).json({error:"no tagged repo has work to ship"});let f=[];for(let h of d)try{let y=await yw(h.dir,{title:r,body:o,base:s}),k=h.repo||await Me(h.dir).catch(()=>null);k&&y.number&&await Yt({repo:k,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Dn("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:k??h.name}}),f.push({repo:h.name,slug:k??null,ok:!0,url:y.url,number:y.number})}catch(y){f.push({repo:h.name,slug:h.repo??null,ok:!1,error:y?.message||String(y)})}let w=f.filter(h=>h.ok);return w.length?t.json({ok:!0,multi:!0,results:f,url:w[0].url,number:w[0].number}):t.status(502).json({error:f.map(h=>`${h.repo}: ${h.error}`).join("; "),results:f})}let c=await yw(i.cwd,{title:r,body:o,base:s}),l=await Me(i.cwd).catch(()=>null);l&&c.number&&await Yt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Dn("opened",{number:c.number,url:c.url,title:r,track:i,branch:i.workspace?.branch,extra:{...l?{repo:l}:{}}}),t.json(c)}catch(i){let c=i?.message||String(i),l=/required|invalid/i.test(c)?400:502;t.status(l).json({error:c})}});E.get("/api/tracks/:id/repos",async(e,t)=>{try{let n=await me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await Es(n),o=new Map(r.map(a=>[a.base,a])),s=Gt(n.workspace).map(a=>{let i=o.get(a.base);return{name:a.name,base:a.base,baseRef:a.baseRef??null,repo:a.repo??null,dirty:i?.dirty??!1,ahead:i?.ahead??0,changed:!!i}});t.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.post("/api/tracks/:id/repos",async(e,t)=>{let{repo:n}=e.body||{};try{if(!n||typeof n!="string")return t.status(400).json({error:"repo (path or owner/name) required"});let r=await Gp(e.params.id,n);t.json({ok:!0,added:{name:r.name,base:r.base,baseRef:r.baseRef??null,repo:r.repo??null}})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/already tagged|needs a multi|not a usable|archived|required/i.test(o)?400:502;t.status(s).json({error:o})}});E.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await Kp(e.params.id,e.params.name);t.json({ok:!0,...n})}catch(n){let r=n?.message||String(n),o=/track .* not found/i.test(r)?404:/has work|still running|last repo/i.test(r)?409:/needs a multi|not tagged/i.test(r)?400:502;t.status(o).json({error:r})}});E.post("/api/tracks/:id/reshape",async(e,t)=>{let{repos:n,skipPermissions:r}=e.body||{};try{let o=await _y(e.params.id,n,{skipPermissions:r!==!1});t.status(o.finishedAt?200:202).json({ok:!0,...Ik(o)})}catch(o){let s=o?.message||String(o),a=o?.status||(/not found/i.test(s)?404:/has work|still running|already|running|archived|last repo/i.test(s)?409:/required|not a usable|tagged twice/i.test(s)?400:502);t.status(a).json({error:s})}});E.get("/api/tracks/:id/reshape",(e,t)=>{t.json(Ik(gl(e.params.id))??{running:!1})});function Ik(e){return e?{running:!e.finishedAt,step:e.step,error:e.error??null,inPlace:e.inPlace===!0,added:e.added??[],removed:e.removed??[],scratch:e.scratch===!0,views:e.views??0,checkpoint:e.checkpoint??null,startedAt:e.startedAt??null,finishedAt:e.finishedAt??null}:null}E.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Da(n,r),s=await Me(n).catch(()=>null),a=await YC({repo:n,number:r,branch:o.headRefName});if(!a)return t.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=Gt(a.workspace).find(k=>k.base===n||s&&k.repo===s)??null,c=a.workspace?.kind==="multi",l=await tE(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?Re.join("ci",i.name):"ci",d=Re.join(a.cwd,".bostrat",u),p=Re.join(".bostrat",u,`pr-${o.number}-fail.log`);await Et.promises.mkdir(d,{recursive:!0}),await Et.promises.writeFile(Re.join(a.cwd,p),l.map(k=>`\u2550\u2550\u2550\u2550 ${k.name}${k.link?` \xB7 ${k.link}`:""} \u2550\u2550\u2550\u2550
|
|
803
803
|
${k.log??"(no fetchable log \u2014 see link)"}
|
|
804
804
|
`).join(`
|
|
805
|
-
`));let f=l.map(k=>k.name).join(", "),w=c&&i?` in the ${i.name}/ checkout`:"",h=await qt(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${w}. Failing check${l.length===1?"":"s"}: ${f}. The failure log is at ${p} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),y=s;y&&(await Yt({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await wT(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Dn("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(k=>k.name)}}),t.json({ok:!0,leadBox:h.leadBox,queued:!!h.queued,checks:l.map(k=>k.name),log:p})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});_.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Da(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await Me(n).catch(()=>null),a=await zC({repo:n,number:r,branch:o.headRefName});if(!a)return t.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=Gt(a.workspace).find(d=>d.base===n||s&&d.repo===s)??null,c=a.workspace?.kind==="multi"&&i?` In the ${i.name}/ checkout:`:"",l=await qt(a.id,`PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName} \u2014 has merge conflicts with ${o.baseRefName}.${c} Bring ${o.baseRefName} into this branch (fetch origin, then merge origin/${o.baseRefName} \u2014 don't rebase this pushed PR branch), and resolve every conflict so that BOTH sides' intent is preserved. Run the relevant tests, then commit and push to update the PR. If any conflict is a genuine ambiguity \u2014 where you can't confidently tell which side's behavior should win, or reconciling would drop work from either side \u2014 STOP and ask me here rather than guessing, laying out the specific choice and your recommendation.`,{kind:"reconcile"}),u=s;u&&(await Yt({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await gT(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Dn("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),t.json({ok:!0,leadBox:l.leadBox,queued:!!l.queued,base:o.baseRefName})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});_.post("/api/tracks/:id/pr-offer",async(e,t)=>{let{offerId:n,action:r}=e.body||{};try{let o=null;if(Array.isArray(e.body?.actions)){if(o=ho.filter(a=>e.body.actions.includes(a)),!o.length)return t.status(400).json({error:"actions must include commit, rebase, push and/or pr"})}else if(r==="pr")o=["commit","push","pr"];else if(r==="push")o=["commit","push"];else if(r!=="dismiss")return t.status(400).json({error:'offerId and actions ["commit"|"rebase"|"push"|"pr"] (or action "dismiss") required'});if(!n)return t.status(400).json({error:"offerId required"});let s=o!=null&&e.body?.notify===!0;await vo(`pr:${e.params.id}:${n}`,async()=>{let a=await me(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await K(a),c=i.find(d=>d.type==="pr.offered"&&d.payload?.offerId===n);if(!c)return t.status(404).json({error:"unknown offer"});if(i.some(d=>d.type==="pr.offer.answered"&&d.payload?.offerId===n))return t.status(409).json({error:"offer already answered"});if(o?.includes("pr")){let d=await pk(a,c,{actions:o,notify:s});if(d)return t.json({ok:!0,offerId:n,action:"pr-exists",actions:o,notify:s,existingPr:d.pr})}let l=null;o&&(l=await qt(a.id,Ma(o,{branch:c.payload.branch,base:c.payload.base,toPr:c.payload.toPr||null,repos:c.payload.repos||null,offeredRebase:Array.isArray(c.payload.offered)&&c.payload.offered.includes("rebase")}),{kind:o.includes("pr")?"pr-go":"push-go"}));let u=o?o.includes("pr")?"pr":o.includes("push")?"push":o.includes("commit")?"commit":"rebase":"dismiss";await 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&&ky(a.id,{actions:o}),t.json({ok:!0,offerId:n,action:u,actions:o,notify:s,queued:!!l?.queued})})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/already answered|not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});_.post("/api/tracks/:id/pr-exists",async(e,t)=>{let{offerId:n,choice:r}=e.body||{};try{let o=await me(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await fk(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 eu=e=>t=>({...t,url:tM(t,e.headers)});_.get("/api/views",async(e,t)=>{try{let n=await cg({trackId:e.query.track||void 0});t.json({views:n.map(eu(e)),maxLive:Ao})}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/views/:id",async(e,t)=>{let n=await L_(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:eu(e)(n),session:Ha(n.slug)})});_.post("/api/views",async(e,t)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=e.body||{};try{let i=n?await me(n):null;if(!i&&s&&(i=(await ke()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Re.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!Vb(e.headers))return t.status(412).json({error:MR});let c=await ug({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:eu(e)(c)})}catch(i){let c=i?.message||String(i),l=i?.status??(/cap reached/i.test(c)?409:/required|invalid|already owned|no free/i.test(c)?400:500);t.status(l).json({error:c})}});_.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:eu(e)(await j_(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});_.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:eu(e)(await F_(e.params.id))})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/cap reached|no known command/i.test(r)?409:500;t.status(o).json({error:r})}});_.delete("/api/views/:id",async(e,t)=>{try{t.json(await U_(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var n3=[["linear",js,db],["jira",wf,lb]],xk=new Map;_.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await cb(),r={};for(let[o,s,a]of n3){let i=await s();if(!i){xk.delete(o),r[o]={configured:!1};continue}let c=xk.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},xk.set(o,c)}r[o]={...c.body,flow:n},o==="jira"&&i.tokenExpiresAt&&(r[o].tokenExpiresAt=i.tokenExpiresAt)}t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await cb()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await TR(o),i=n?await me(n):null;!i&&r&&(i=(await ke()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Re.sep)))||null),i&&await N({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),t.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);t.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});var zs=async(e,t)=>{let n=e?await me(e):null;return!n&&t&&(n=(await ke()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Re.sep)))||null),n};_.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await nb()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});_.get("/api/roadmap",async(e,t)=>{try{t.json(await rb())}catch(n){t.status(502).json({error:n?.message||String(n)})}});_.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await zs(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 ci(o);await pf(s,a,{source:"chat"}),t.json({ok:!0,trackId:s.id,circuit:a})}catch(s){let a=s?.message||String(s);t.status(/required|no circuit|names \d|no plane/i.test(a)?400:502).json({error:a})}});_.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await zs(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await pf(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var JC=async(e,t,n)=>{if(e)return{ref:e,track:await zs(t,n)};let r=await zs(t,n),o=r?await sb(r.id):null;if(!o)throw new Error(r?"this Track isn't in a circuit \u2014 pass --circuit <name-or-id> (or link one first: bostrat-circuit link \u2026)":"run from inside a Track workspace, or pass --circuit <name-or-id>");return{ref:o,track:r}};_.post("/api/blueprints/publish",async(e,t)=>{let{trackId:n,cwd:r,circuit:o,newCircuit:s,name:a,markdown:i}=e.body||{};try{if(!i||!String(i).trim())return t.status(400).json({error:"markdown required"});let{ref:c}=s?{ref:null}:await JC(o,n,r),l=await gR({circuitRef:c,newCircuitName:s,name:a,markdown:i});t.json({ok:!0,...l})}catch(c){let l=c?.message||String(c);t.status(/required|no circuit|names \d|no plane|isn't in a circuit|run from inside/i.test(l)?400:502).json({error:l})}});_.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await JC(e.query.circuit,e.query.track,e.query.cwd);t.json(await ob(n))}catch(n){let r=n?.message||String(n);t.status(/no blueprint|no circuit|isn't in a circuit|run from inside|names \d/i.test(r)?404:502).json({error:r})}});_.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l}=e.body||{};try{let u=await zs(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=await kR(u,{title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l});t.json({ok:!0,trackId:u.id,...d})}catch(u){let d=u?.message||String(u);t.status(/required/i.test(d)?400:502).json({error:d})}});_.get("/api/decisions",async(e,t)=>{try{let n=await zs(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await xR(n.id))}catch(n){let r=n?.message||String(n);t.status(/no plane|required|run from inside/i.test(r)?400:502).json({error:r})}});_.post("/api/blueprints/tag",async(e,t)=>{let{trackId:n,cwd:r,itemIds:o}=e.body||{};try{let s=Array.isArray(o)?o.map(String):[];if(!s.length)return t.status(400).json({error:"itemIds required (BP-\u2026 ids)"});let a=await zs(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await sb(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 yR(a,i,s);t.json({ok:!0,trackId:a.id,...c})}catch(s){let a=s?.message||String(s);t.status(/unknown\/unbindable|required|no blueprint|isn't in a circuit/i.test(a)?400:502).json({error:a})}});_.post("/api/tickets/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=n?await me(n):null;if(!s&&r&&(s=(await ke()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+Re.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (issue URL or key) required"});let a=await pi(s,o,{source:n?"api":"cli"});t.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);t.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});_.get("/api/tickets/status",async(e,t)=>{let{cwd:n,track:r}=e.query;try{let o=r?await me(r):null;if(!o&&n&&(o=(await ke()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+Re.sep)))||null),!o)return t.status(400).json({error:"track (or a cwd inside a Track workspace) required"});t.json({trackId:o.id,configured:await JR(),ticket:await ir(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});_.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await ir(e.params.id)})});_.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 pi(n,(e.body||{}).ref,{source:"api"});t.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);t.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});_.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await Eb(e.params.id)})});var Vf=e=>/no ticket linked|unknown offer|unknown state/i.test(e)?404:/already answered/i.test(e)?409:/required|no ticketing/i.test(e)?400:502;_.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await QR(e.params.id))}catch(n){let r=n?.message||String(n);t.status(Vf(r)).json({error:r})}});_.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await _b(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(Vf(r)).json({error:r})}});_.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await eO(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(Vf(r)).json({error:r})}});_.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 tO(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(Vf(s)).json({error:s})}});_.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await jy(e.params.id,n,r===!0))}catch(o){let s=o?.message||String(o),a=/not found|unknown offer/.test(s)?404:/already answered/.test(s)||/mid-turn/.test(s)?409:500;t.status(a).json({error:s})}});_.post("/api/tickets/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await me(n):null;if(!s&&r&&(s=(await ke()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Re.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await rO(s,o)})}catch(s){let a=s?.message||String(s);t.status(/no ticketing|are off|needs an epic|too many|required/i.test(a)?400:502).json({error:a})}});_.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await nO()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});_.post("/api/tracks/:id/ticket-create-offer",async(e,t)=>{let{proposalId:n,accept:r,teamId:o}=e.body||{};try{let s=await me(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await oO(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal|unknown team/i.test(a)?404:/already answered/i.test(a)?409:/required|no ticketing|pick one/i.test(a)?400:502).json({error:a})}});_.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await Zc({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/mocks/:id",async(e,t)=>{let n=await Mr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var tu={dotfiles:"allow"};_.get("/api/mocks/:id/png",async(e,t)=>{let n=await Mr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=$n(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Yc(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,tu,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});_.get("/api/mocks/:id/media",async(e,t)=>{let n=await Mr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=$n(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Yc(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,tu,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});_.get("/api/mocks/:id/html",async(e,t)=>{let n=await Mr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=$n(n,e.query.v??null);if(!r?.htmlRel)return t.status(404).json({error:"this version has no HTML source (image-sourced)"});t.set("content-security-policy","sandbox"),t.sendFile(dn(n,r.v).html,tu,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});_.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&kp(n)?n:null,o=n&&!r?n:null,s=n?e.query:e.body||{},{trackId:a,name:i,cwd:c,width:l,group:u}=s;try{let d=a?await me(a):null;if(!d&&c&&(d=(await ke()).find(h=>h.cwd&&(c===h.cwd||c.startsWith(h.cwd+Re.sep)))||null),!d)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let p=(d.boxes||[]).find(w=>w.name===d.leadBox),f=await oI({trackId:d.id,name:i,html:n?null:s.html,image:r,video:o,cwd:d.cwd,width:l,group:u,box:d.leadBox,agent:p?.agent||null});t.json({ok:!0,mock:f})}catch(d){let p=d?.message||String(d);t.status(/required|invalid|must be/i.test(p)?400:500).json({error:p})}});_.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&kp(n)?n:null;try{let o=await sI(e.params.id,{html:n?null:(e.body||{}).html,image:r,video:n&&!r?n:null,width:n?e.query.width:(e.body||{}).width});t.json({ok:!0,mock:o})}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required|invalid|must be/i.test(s)?400:500;t.status(a).json({error:s})}});_.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await cI(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await qt(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),t.json({ok:!0,mock:n,siblings:r})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/nothing to pick/i.test(r)?409:500;t.status(o).json({error:r})}});_.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await EI(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await lI(e.params.id),...n.length?{danglingFlows:n}:{}})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});_.get("/api/flows",async(e,t)=>{try{t.json({flows:await xI({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/flows/:id",async(e,t)=>{let n=await vI(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});_.post("/api/flows",async(e,t)=>{let{trackId:n,name:r,steps:o,cwd:s}=e.body||{};try{let a=n?await me(n):null;if(!a&&s&&(a=(await ke()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Re.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await 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})}});_.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})}});_.delete("/api/flows/:id",async(e,t)=>{try{t.json(await TI(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});_.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await ke(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+Re.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await NI({trackId:n,status:e.query.status||void 0});t.json({runbooks:r,...o?{fleetDegraded:!0}:{}})}catch(n){t.status(500).json({error:n?.message||String(n)})}});_.get("/api/runbooks/:id",async(e,t)=>{let n=await Ag(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});_.get("/api/runbooks/:id/md",async(e,t)=>{let n=await Ag(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 PI(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(Dr(n,r.v),tu,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});_.get("/api/loops",async(e,t)=>{t.json(await xc())});_.post("/api/loops/report",async(e,t)=>{let{outcome:n,note:r,cwd:o}=e.body||{};if(!["ok","acted","attention"].includes(n))return t.status(400).json({error:"outcome must be one of ok | acted | attention"});if(!o)return t.status(400).json({error:"cwd required"});let a=(await ke()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+Re.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await xc();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 Uh({loopId:c.id,outcome:n,note:r});if(!l.ok)return t.status(l.status||503).json({error:l.error||"report failed"});t.json({ok:!0,loop:{id:c.id,name:c.name},run:l.run})});_.post("/api/loops/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await me(n):null;if(!s&&r&&(s=(await ke()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Re.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await uM(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var vi=async(e,t)=>{let n=e?await me(e):null;return!n&&t&&(n=(await ke()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Re.sep)))||null),n};_.post("/api/followups/offer",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await vi(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 ZE(s,o,{ship:{changedRepos:Es}})})}catch(s){t.status(400).json({error:s?.message||String(s)})}});_.post("/api/followups/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await vi(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 e_(s,o)})}catch(s){let a=s?.message||String(s);t.status(/unreachable/i.test(a)?503:400).json({error:a})}});_.get("/api/followups",async(e,t)=>{let n=typeof e.query?.cwd=="string"?e.query.cwd:null,r=await vi(null,n).catch(()=>null);t.json({ok:!0,trackId:r?.id??null,triggers:Lw,...await o_(r)})});var PC=new Set(["reconcile","fixing-ci","rebase","merge"]);_.get("/api/status",async(e,t)=>{let n=await vi(null,typeof e.query?.cwd=="string"?e.query.cwd:null).catch(()=>null);if(!n)return t.status(400).json({error:"a cwd inside a Track workspace is required"});t.json({ok:!0,trackId:n.id,status:n.status})});_.post("/api/status",async(e,t)=>{try{let{trackId:n,cwd:r,phase:o,summary:s,scope:a,reason:i}=e.body||{},c=await vi(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&&!PC.has(l))throw new Error(`phase must be one of ${[...PC].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=Jz();await N(c,{type:"track.phase.changed",actor:"agent",source:"cli",boxId:c.leadBox,provider:c.boxes?.find(p=>p.name===c.leadBox)?.agent??null,payload:{ts:Date.now(),phase:l||null,phaseId:d,summary:u,scope:a&&typeof a=="object"?a:null,...l?{}:{reason:i||"completed"}}}),t.json({ok:!0,trackId:c.id,phase:l||null,phaseId:d})}catch(n){t.status(400).json({error:n?.message||String(n)})}});_.post("/api/tracks/:id/followup-offer",async(e,t)=>{let{offerId:n,followupId:r,armId:o,promoted:s,all:a}=e.body||{};try{let i=await me(e.params.id);if(!i)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await n_(i,{offerId:n,followupId:r||null,armId:o||null,promoted:s||null,all:a===!0},{sayToLead:qt,unarchiveTrack:Jp})})}catch(i){let c=i?.message||String(i),l=i?.code==="removed"?410:/unknown offer/i.test(c)?404:/already answered/i.test(c)?409:/required|not on this card|nothing to run together/i.test(c)?400:/unreachable/i.test(c)?503:502;t.status(l).json({error:c})}});_.post("/api/tracks/:id/followup-arm-cancel",async(e,t)=>{try{let n=await me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await QE(n,e.body?.armId)})}catch(n){let r=n?.message||String(n);t.status(/unknown/i.test(r)?404:400).json({error:r})}});_.post("/api/tracks/:id/followup-create-offer",async(e,t)=>{let{proposalId:n,accept:r,followupId:o}=e.body||{};try{let s=await me(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await t_(s,n,r===!0,{followupId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal/i.test(a)?404:/already answered/i.test(a)?409:400).json({error:a})}});Ze.on("event",e=>{let t=e?.type==="pr.state.changed"?e.payload?.change:null;if(!t||!e.trackId)return;let n=t==="opened"?"pr.opened":t==="ci-fixed"?"checks.green":null,r=e.payload||{},o={repo:r.repo??null,prNumber:r.number??null,prTitle:r.title??null,prUrl:r.url??null};!n&&t!=="pushed"||me(e.trackId).then(async s=>{if(s&&(n&&await Dc(s,n,o),t==="opened"||t==="pushed")){let a=r_(await K(s).catch(()=>[]));a&&await Dc(s,"ship.done",{...o,shipOfferId:a})}}).catch(()=>{})});var YC=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await me(n):null;if(!s&&r&&(s=(await ke()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Re.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"}),null;let a=await xc();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=fM(a.loops,o)}catch(c){return t.status(/matches \d+ loops/.test(c.message)?409:404).json({error:c.message}),null}return{track:s,loop:i}};_.post("/api/loops/propose-update",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await YC(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await mM(s.track,s.loop,e.body?.patch)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});_.post("/api/loops/propose-delete",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await YC(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await hM(s.track,s.loop,e.body?.reason)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});_.post("/api/planning/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o,scope:s}=e.body||{},a=await vi(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i;try{i=await rb()}catch(c){return t.status(503).json({error:c?.message||String(c)})}try{let c=s==="circuit"?Qb:Hl;t.json({ok:!0,trackId:a.id,proposal:await EM(a,o,i,{allow:c})})}catch(c){t.status(400).json({error:c?.message||String(c)})}});var $k=e=>/unknown proposal/i.test(e)?404:/already answered/i.test(e)?409:400;_.post("/api/tracks/:id/planning-offer",async(e,t)=>{let{proposalId:n,action:r,results:o}=e.body||{};try{let s=await me(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await _M(s,n,r,{results:o||[]})})}catch(s){let a=s?.message||String(s);t.status($k(a)).json({error:a})}});_.post("/api/tracks/:id/loop-update-offer",async(e,t)=>{let{proposalId:n,action:r,nextFireAt:o}=e.body||{};try{let s=await me(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await gM(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status($k(a)).json({error:a})}});_.post("/api/tracks/:id/loop-delete-offer",async(e,t)=>{let{proposalId:n,action:r}=e.body||{};try{let o=await me(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await yM(o,n,r)})}catch(o){let s=o?.message||String(o);t.status($k(s)).json({error:s})}});_.post("/api/tracks/:id/loop-create-offer",async(e,t)=>{let{proposalId:n,accept:r,loopId:o,nextFireAt:s}=e.body||{};try{let a=await me(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await dM(a,n,r===!0,{loopId:o||null,nextFireAt:s??null})})}catch(a){let i=a?.message||String(a);t.status(/unknown proposal/i.test(i)?404:/already answered/i.test(i)?409:400).json({error:i})}});_.post("/api/runbooks",async(e,t)=>{let{trackId:n,name:r,md:o,cwd:s}=e.body||{};try{let a=n?await me(n):null;if(!a&&s&&(a=(await ke()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Re.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await DI({trackId:a.id,trackName:a.name,name:r,md:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,runbook:c})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});_.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await 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})}});_.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await LI(e.params.id,(e.body||{}).status)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/must be/i.test(r)?400:500;t.status(o).json({error:r})}});_.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await jI(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});_.get("/api/changelog/draft",async(e,t)=>{try{let n=MM(e.query.since||null);t.json({since:n,repos:tk(),...await CM(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var XC=async({trackId:e,cwd:t})=>{let n=e?await me(e):null;return!n&&t&&(n=(await ke()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+Re.sep)))||null),n};_.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=await XC({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 NM(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|no `- `|over the|secret VALUE|mentions "/i.test(i)?400:500).json({error:i})}});_.post("/api/changelog/retract",async(e,t)=>{let{day:n,trackId:r,cwd:o}=e.body||{};try{let s=await XC({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 PM(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var r3=120,vk=400,o3=/^[a-z0-9][a-z0-9_-]{0,23}$/,xi=null,DC=null;_.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&&!o3.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>vk)return t.status(400).json({error:`message is ${c.length} chars \u2014 over the ${vk} cap; a notification is a nudge, put the detail in chat`});let u=Qc(`${n||""}
|
|
806
|
-
${c}`);if(u.length)return t.status(400).json({error:`notification appears to contain a secret VALUE (${u.join(", ")}) \u2014 notifications ship off-box; never put a credential in one`});let d=a?await me(a):null;if(!d&&s&&(d=(await ke()).find(y=>y.cwd&&(s===y.cwd||s.startsWith(y.cwd+Re.sep)))||null),!d)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let p=(d.boxes||[]).find(h=>h.name===d.leadBox),f=(h,y)=>String(h).replace(/\s+/g," ").trim().slice(0,y),w={title:n?f(n,
|
|
805
|
+
`));let f=l.map(k=>k.name).join(", "),w=c&&i?` in the ${i.name}/ checkout`:"",h=await qt(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${w}. Failing check${l.length===1?"":"s"}: ${f}. The failure log is at ${p} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),y=s;y&&(await Yt({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await yT(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Dn("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(k=>k.name)}}),t.json({ok:!0,leadBox:h.leadBox,queued:!!h.queued,checks:l.map(k=>k.name),log:p})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});E.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Da(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await Me(n).catch(()=>null),a=await YC({repo:n,number:r,branch:o.headRefName});if(!a)return t.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=Gt(a.workspace).find(d=>d.base===n||s&&d.repo===s)??null,c=a.workspace?.kind==="multi"&&i?` In the ${i.name}/ checkout:`:"",l=await qt(a.id,`PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName} \u2014 has merge conflicts with ${o.baseRefName}.${c} Bring ${o.baseRefName} into this branch (fetch origin, then merge origin/${o.baseRefName} \u2014 don't rebase this pushed PR branch), and resolve every conflict so that BOTH sides' intent is preserved. Run the relevant tests, then commit and push to update the PR. If any conflict is a genuine ambiguity \u2014 where you can't confidently tell which side's behavior should win, or reconciling would drop work from either side \u2014 STOP and ask me here rather than guessing, laying out the specific choice and your recommendation.`,{kind:"reconcile"}),u=s;u&&(await Yt({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await bT(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Dn("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),t.json({ok:!0,leadBox:l.leadBox,queued:!!l.queued,base:o.baseRefName})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});E.post("/api/tracks/:id/pr-offer",async(e,t)=>{let{offerId:n,action:r}=e.body||{};try{let o=null;if(Array.isArray(e.body?.actions)){if(o=ho.filter(a=>e.body.actions.includes(a)),!o.length)return t.status(400).json({error:"actions must include commit, rebase, push and/or pr"})}else if(r==="pr")o=["commit","push","pr"];else if(r==="push")o=["commit","push"];else if(r!=="dismiss")return t.status(400).json({error:'offerId and actions ["commit"|"rebase"|"push"|"pr"] (or action "dismiss") required'});if(!n)return t.status(400).json({error:"offerId required"});let s=o!=null&&e.body?.notify===!0;await vo(`pr:${e.params.id}:${n}`,async()=>{let a=await me(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await K(a),c=i.find(d=>d.type==="pr.offered"&&d.payload?.offerId===n);if(!c)return t.status(404).json({error:"unknown offer"});if(i.some(d=>d.type==="pr.offer.answered"&&d.payload?.offerId===n))return t.status(409).json({error:"offer already answered"});if(o?.includes("pr")){let d=await pk(a,c,{actions:o,notify:s});if(d)return t.json({ok:!0,offerId:n,action:"pr-exists",actions:o,notify:s,existingPr:d.pr})}let l=null;o&&(l=await qt(a.id,Ma(o,{branch:c.payload.branch,base:c.payload.base,toPr:c.payload.toPr||null,repos:c.payload.repos||null,offeredRebase:Array.isArray(c.payload.offered)&&c.payload.offered.includes("rebase")}),{kind:o.includes("pr")?"pr-go":"push-go"}));let u=o?o.includes("pr")?"pr":o.includes("push")?"push":o.includes("commit")?"commit":"rebase":"dismiss";await 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&&ky(a.id,{actions:o}),t.json({ok:!0,offerId:n,action:u,actions:o,notify:s,queued:!!l?.queued})})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/already answered|not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});E.post("/api/tracks/:id/pr-exists",async(e,t)=>{let{offerId:n,choice:r}=e.body||{};try{let o=await me(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await fk(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 eu=e=>t=>({...t,url:rM(t,e.headers)});E.get("/api/views",async(e,t)=>{try{let n=await cg({trackId:e.query.track||void 0});t.json({views:n.map(eu(e)),maxLive:Ao})}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/views/:id",async(e,t)=>{let n=await F_(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:eu(e)(n),session:Ha(n.slug)})});E.post("/api/views",async(e,t)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=e.body||{};try{let i=n?await me(n):null;if(!i&&s&&(i=(await ke()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Re.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!Vb(e.headers))return t.status(412).json({error:NR});let c=await ug({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:eu(e)(c)})}catch(i){let c=i?.message||String(i),l=i?.status??(/cap reached/i.test(c)?409:/required|invalid|already owned|no free/i.test(c)?400:500);t.status(l).json({error:c})}});E.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:eu(e)(await U_(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});E.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:eu(e)(await H_(e.params.id))})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/cap reached|no known command/i.test(r)?409:500;t.status(o).json({error:r})}});E.delete("/api/views/:id",async(e,t)=>{try{t.json(await W_(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var s3=[["linear",js,db],["jira",wf,lb]],xk=new Map;E.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await cb(),r={};for(let[o,s,a]of s3){let i=await s();if(!i){xk.delete(o),r[o]={configured:!1};continue}let c=xk.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},xk.set(o,c)}r[o]={...c.body,flow:n},o==="jira"&&i.tokenExpiresAt&&(r[o].tokenExpiresAt=i.tokenExpiresAt)}t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await cb()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await _R(o),i=n?await me(n):null;!i&&r&&(i=(await ke()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Re.sep)))||null),i&&await N({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),t.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);t.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});var zs=async(e,t)=>{let n=e?await me(e):null;return!n&&t&&(n=(await ke()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Re.sep)))||null),n};E.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await nb()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});E.get("/api/roadmap",async(e,t)=>{try{t.json(await rb())}catch(n){t.status(502).json({error:n?.message||String(n)})}});E.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await zs(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 ci(o);await pf(s,a,{source:"chat"}),t.json({ok:!0,trackId:s.id,circuit:a})}catch(s){let a=s?.message||String(s);t.status(/required|no circuit|names \d|no plane/i.test(a)?400:502).json({error:a})}});E.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await zs(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await pf(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var XC=async(e,t,n)=>{if(e)return{ref:e,track:await zs(t,n)};let r=await zs(t,n),o=r?await sb(r.id):null;if(!o)throw new Error(r?"this Track isn't in a circuit \u2014 pass --circuit <name-or-id> (or link one first: bostrat-circuit link \u2026)":"run from inside a Track workspace, or pass --circuit <name-or-id>");return{ref:o,track:r}};E.post("/api/blueprints/publish",async(e,t)=>{let{trackId:n,cwd:r,circuit:o,newCircuit:s,name:a,markdown:i}=e.body||{};try{if(!i||!String(i).trim())return t.status(400).json({error:"markdown required"});let{ref:c}=s?{ref:null}:await XC(o,n,r),l=await bR({circuitRef:c,newCircuitName:s,name:a,markdown:i});t.json({ok:!0,...l})}catch(c){let l=c?.message||String(c);t.status(/required|no circuit|names \d|no plane|isn't in a circuit|run from inside/i.test(l)?400:502).json({error:l})}});E.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await XC(e.query.circuit,e.query.track,e.query.cwd);t.json(await ob(n))}catch(n){let r=n?.message||String(n);t.status(/no blueprint|no circuit|isn't in a circuit|run from inside|names \d/i.test(r)?404:502).json({error:r})}});E.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l}=e.body||{};try{let u=await zs(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=await vR(u,{title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l});t.json({ok:!0,trackId:u.id,...d})}catch(u){let d=u?.message||String(u);t.status(/required/i.test(d)?400:502).json({error:d})}});E.get("/api/decisions",async(e,t)=>{try{let n=await zs(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await SR(n.id))}catch(n){let r=n?.message||String(n);t.status(/no plane|required|run from inside/i.test(r)?400:502).json({error:r})}});E.post("/api/blueprints/tag",async(e,t)=>{let{trackId:n,cwd:r,itemIds:o}=e.body||{};try{let s=Array.isArray(o)?o.map(String):[];if(!s.length)return t.status(400).json({error:"itemIds required (BP-\u2026 ids)"});let a=await zs(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await sb(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 kR(a,i,s);t.json({ok:!0,trackId:a.id,...c})}catch(s){let a=s?.message||String(s);t.status(/unknown\/unbindable|required|no blueprint|isn't in a circuit/i.test(a)?400:502).json({error:a})}});E.post("/api/tickets/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=n?await me(n):null;if(!s&&r&&(s=(await ke()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+Re.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (issue URL or key) required"});let a=await pi(s,o,{source:n?"api":"cli"});t.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);t.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});E.get("/api/tickets/status",async(e,t)=>{let{cwd:n,track:r}=e.query;try{let o=r?await me(r):null;if(!o&&n&&(o=(await ke()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+Re.sep)))||null),!o)return t.status(400).json({error:"track (or a cwd inside a Track workspace) required"});t.json({trackId:o.id,configured:await XR(),ticket:await ir(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});E.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await ir(e.params.id)})});E.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await pi(n,(e.body||{}).ref,{source:"api"});t.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);t.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});E.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await Eb(e.params.id)})});var Vf=e=>/no ticket linked|unknown offer|unknown state/i.test(e)?404:/already answered/i.test(e)?409:/required|no ticketing/i.test(e)?400:502;E.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await tO(e.params.id))}catch(n){let r=n?.message||String(n);t.status(Vf(r)).json({error:r})}});E.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await _b(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(Vf(r)).json({error:r})}});E.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await nO(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(Vf(r)).json({error:r})}});E.post("/api/tracks/:id/ticket-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{let o=await me(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await rO(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(Vf(s)).json({error:s})}});E.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await jy(e.params.id,n,r===!0))}catch(o){let s=o?.message||String(o),a=/not found|unknown offer/.test(s)?404:/already answered/.test(s)||/mid-turn/.test(s)?409:500;t.status(a).json({error:s})}});E.post("/api/tickets/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await me(n):null;if(!s&&r&&(s=(await ke()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Re.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await sO(s,o)})}catch(s){let a=s?.message||String(s);t.status(/no ticketing|are off|needs an epic|too many|required/i.test(a)?400:502).json({error:a})}});E.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await oO()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});E.post("/api/tracks/:id/ticket-create-offer",async(e,t)=>{let{proposalId:n,accept:r,teamId:o}=e.body||{};try{let s=await me(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await aO(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal|unknown team/i.test(a)?404:/already answered/i.test(a)?409:/required|no ticketing|pick one/i.test(a)?400:502).json({error:a})}});E.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await Zc({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/mocks/:id",async(e,t)=>{let n=await Mr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var tu={dotfiles:"allow"};E.get("/api/mocks/:id/png",async(e,t)=>{let n=await Mr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=$n(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Yc(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,tu,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});E.get("/api/mocks/:id/media",async(e,t)=>{let n=await Mr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=$n(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Yc(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,tu,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});E.get("/api/mocks/:id/html",async(e,t)=>{let n=await Mr(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=$n(n,e.query.v??null);if(!r?.htmlRel)return t.status(404).json({error:"this version has no HTML source (image-sourced)"});t.set("content-security-policy","sandbox"),t.sendFile(dn(n,r.v).html,tu,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});E.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&kp(n)?n:null,o=n&&!r?n:null,s=n?e.query:e.body||{},{trackId:a,name:i,cwd:c,width:l,group:u}=s;try{let d=a?await me(a):null;if(!d&&c&&(d=(await ke()).find(h=>h.cwd&&(c===h.cwd||c.startsWith(h.cwd+Re.sep)))||null),!d)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let p=(d.boxes||[]).find(w=>w.name===d.leadBox),f=await aI({trackId:d.id,name:i,html:n?null:s.html,image:r,video:o,cwd:d.cwd,width:l,group:u,box:d.leadBox,agent:p?.agent||null});t.json({ok:!0,mock:f})}catch(d){let p=d?.message||String(d);t.status(/required|invalid|must be/i.test(p)?400:500).json({error:p})}});E.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&kp(n)?n:null;try{let o=await iI(e.params.id,{html:n?null:(e.body||{}).html,image:r,video:n&&!r?n:null,width:n?e.query.width:(e.body||{}).width});t.json({ok:!0,mock:o})}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required|invalid|must be/i.test(s)?400:500;t.status(a).json({error:s})}});E.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await uI(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await qt(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),t.json({ok:!0,mock:n,siblings:r})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/nothing to pick/i.test(r)?409:500;t.status(o).json({error:r})}});E.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await II(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await dI(e.params.id),...n.length?{danglingFlows:n}:{}})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});E.get("/api/flows",async(e,t)=>{try{t.json({flows:await SI({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/flows/:id",async(e,t)=>{let n=await AI(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});E.post("/api/flows",async(e,t)=>{let{trackId:n,name:r,steps:o,cwd:s}=e.body||{};try{let a=n?await me(n):null;if(!a&&s&&(a=(await ke()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Re.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await TI({trackId:a.id,name:r,steps:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,flow:c})}catch(a){let i=a?.message||String(a),c=/already exists/i.test(i)?409:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(i)?400:500;t.status(c).json({error:i})}});E.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await EI(e.params.id,{steps:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(r)?400:500;t.status(o).json({error:r})}});E.delete("/api/flows/:id",async(e,t)=>{try{t.json(await _I(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});E.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await ke(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+Re.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await DI({trackId:n,status:e.query.status||void 0});t.json({runbooks:r,...o?{fleetDegraded:!0}:{}})}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.get("/api/runbooks/:id",async(e,t)=>{let n=await Ag(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});E.get("/api/runbooks/:id/md",async(e,t)=>{let n=await Ag(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=NI(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 BI(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(Dr(n,r.v),tu,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});E.get("/api/loops",async(e,t)=>{t.json(await xc())});E.post("/api/loops/report",async(e,t)=>{let{outcome:n,note:r,cwd:o}=e.body||{};if(!["ok","acted","attention"].includes(n))return t.status(400).json({error:"outcome must be one of ok | acted | attention"});if(!o)return t.status(400).json({error:"cwd required"});let a=(await ke()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+Re.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await xc();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 Uh({loopId:c.id,outcome:n,note:r});if(!l.ok)return t.status(l.status||503).json({error:l.error||"report failed"});t.json({ok:!0,loop:{id:c.id,name:c.name},run:l.run})});E.post("/api/loops/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await me(n):null;if(!s&&r&&(s=(await ke()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+Re.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await pM(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var vi=async(e,t)=>{let n=e?await me(e):null;return!n&&t&&(n=(await ke()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+Re.sep)))||null),n};E.post("/api/followups/offer",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await vi(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 e_(s,o,{ship:{changedRepos:Es}})})}catch(s){t.status(400).json({error:s?.message||String(s)})}});E.post("/api/followups/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=await vi(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 n_(s,o)})}catch(s){let a=s?.message||String(s);t.status(/unreachable/i.test(a)?503:400).json({error:a})}});E.get("/api/followups",async(e,t)=>{let n=typeof e.query?.cwd=="string"?e.query.cwd:null,r=await vi(null,n).catch(()=>null);t.json({ok:!0,trackId:r?.id??null,triggers:Lw,...await a_(r)})});var BC=new Set(["reconcile","fixing-ci","rebase","merge"]);E.get("/api/status",async(e,t)=>{let n=await vi(null,typeof e.query?.cwd=="string"?e.query.cwd:null).catch(()=>null);if(!n)return t.status(400).json({error:"a cwd inside a Track workspace is required"});t.json({ok:!0,trackId:n.id,status:n.status})});E.post("/api/status",async(e,t)=>{try{let{trackId:n,cwd:r,phase:o,summary:s,scope:a,reason:i}=e.body||{},c=await vi(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&&!BC.has(l))throw new Error(`phase must be one of ${[...BC].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=Zz();await N(c,{type:"track.phase.changed",actor:"agent",source:"cli",boxId:c.leadBox,provider:c.boxes?.find(p=>p.name===c.leadBox)?.agent??null,payload:{ts:Date.now(),phase:l||null,phaseId:d,summary:u,scope:a&&typeof a=="object"?a:null,...l?{}:{reason:i||"completed"}}}),t.json({ok:!0,trackId:c.id,phase:l||null,phaseId:d})}catch(n){t.status(400).json({error:n?.message||String(n)})}});E.post("/api/tracks/:id/followup-offer",async(e,t)=>{let{offerId:n,followupId:r,armId:o,promoted:s,all:a}=e.body||{};try{let i=await me(e.params.id);if(!i)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await o_(i,{offerId:n,followupId:r||null,armId:o||null,promoted:s||null,all:a===!0},{sayToLead:qt,unarchiveTrack:Jp})})}catch(i){let c=i?.message||String(i),l=i?.code==="removed"?410:/unknown offer/i.test(c)?404:/already answered/i.test(c)?409:/required|not on this card|nothing to run together/i.test(c)?400:/unreachable/i.test(c)?503:502;t.status(l).json({error:c})}});E.post("/api/tracks/:id/followup-arm-cancel",async(e,t)=>{try{let n=await me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await t_(n,e.body?.armId)})}catch(n){let r=n?.message||String(n);t.status(/unknown/i.test(r)?404:400).json({error:r})}});E.post("/api/tracks/:id/followup-create-offer",async(e,t)=>{let{proposalId:n,accept:r,followupId:o}=e.body||{};try{let s=await me(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await r_(s,n,r===!0,{followupId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal/i.test(a)?404:/already answered/i.test(a)?409:400).json({error:a})}});Ze.on("event",e=>{let t=e?.type==="pr.state.changed"?e.payload?.change:null;if(!t||!e.trackId)return;let n=t==="opened"?"pr.opened":t==="ci-fixed"?"checks.green":null,r=e.payload||{},o={repo:r.repo??null,prNumber:r.number??null,prTitle:r.title??null,prUrl:r.url??null};!n&&t!=="pushed"||me(e.trackId).then(async s=>{if(s&&(n&&await Dc(s,n,o),t==="opened"||t==="pushed")){let a=s_(await K(s).catch(()=>[]));a&&await Dc(s,"ship.done",{...o,shipOfferId:a})}}).catch(()=>{})});var ZC=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await me(n):null;if(!s&&r&&(s=(await ke()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+Re.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"}),null;let a=await xc();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=hM(a.loops,o)}catch(c){return t.status(/matches \d+ loops/.test(c.message)?409:404).json({error:c.message}),null}return{track:s,loop:i}};E.post("/api/loops/propose-update",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await ZC(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await wM(s.track,s.loop,e.body?.patch)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});E.post("/api/loops/propose-delete",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await ZC(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await gM(s.track,s.loop,e.body?.reason)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});E.post("/api/planning/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o,scope:s}=e.body||{},a=await vi(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i;try{i=await rb()}catch(c){return t.status(503).json({error:c?.message||String(c)})}try{let c=s==="circuit"?Qb:Hl;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 $k=e=>/unknown proposal/i.test(e)?404:/already answered/i.test(e)?409:400;E.post("/api/tracks/:id/planning-offer",async(e,t)=>{let{proposalId:n,action:r,results:o}=e.body||{};try{let s=await me(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await $M(s,n,r,{results:o||[]})})}catch(s){let a=s?.message||String(s);t.status($k(a)).json({error:a})}});E.post("/api/tracks/:id/loop-update-offer",async(e,t)=>{let{proposalId:n,action:r,nextFireAt:o}=e.body||{};try{let s=await me(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await bM(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status($k(a)).json({error:a})}});E.post("/api/tracks/:id/loop-delete-offer",async(e,t)=>{let{proposalId:n,action:r}=e.body||{};try{let o=await me(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await kM(o,n,r)})}catch(o){let s=o?.message||String(o);t.status($k(s)).json({error:s})}});E.post("/api/tracks/:id/loop-create-offer",async(e,t)=>{let{proposalId:n,accept:r,loopId:o,nextFireAt:s}=e.body||{};try{let a=await me(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await fM(a,n,r===!0,{loopId:o||null,nextFireAt:s??null})})}catch(a){let i=a?.message||String(a);t.status(/unknown proposal/i.test(i)?404:/already answered/i.test(i)?409:400).json({error:i})}});E.post("/api/runbooks",async(e,t)=>{let{trackId:n,name:r,md:o,cwd:s}=e.body||{};try{let a=n?await me(n):null;if(!a&&s&&(a=(await ke()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+Re.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await LI({trackId:a.id,trackName:a.name,name:r,md:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,runbook:c})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});E.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await jI(e.params.id,{md:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/required|invalid|over the|secret VALUE/i.test(r)?400:500;t.status(o).json({error:r})}});E.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await FI(e.params.id,(e.body||{}).status)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/must be/i.test(r)?400:500;t.status(o).json({error:r})}});E.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await UI(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});E.get("/api/changelog/draft",async(e,t)=>{try{let n=NM(e.query.since||null);t.json({since:n,repos:tk(),...await PM(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var QC=async({trackId:e,cwd:t})=>{let n=e?await me(e):null;return!n&&t&&(n=(await ke()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+Re.sep)))||null),n};E.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=await QC({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 DM(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|no `- `|over the|secret VALUE|mentions "/i.test(i)?400:500).json({error:i})}});E.post("/api/changelog/retract",async(e,t)=>{let{day:n,trackId:r,cwd:o}=e.body||{};try{let s=await QC({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 BM(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var a3=120,vk=400,i3=/^[a-z0-9][a-z0-9_-]{0,23}$/,xi=null,LC=null;E.post("/api/notify",async(e,t)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a,channel:i}=e.body||{};try{let c=typeof r=="string"?r.trim():"";if(!c)return t.status(400).json({error:"body (the message) required"});let l=i==null||i===""?null:String(i).trim().toLowerCase();if(l!==null&&!i3.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>vk)return t.status(400).json({error:`message is ${c.length} chars \u2014 over the ${vk} cap; a notification is a nudge, put the detail in chat`});let u=Qc(`${n||""}
|
|
806
|
+
${c}`);if(u.length)return t.status(400).json({error:`notification appears to contain a secret VALUE (${u.join(", ")}) \u2014 notifications ship off-box; never put a credential in one`});let d=a?await me(a):null;if(!d&&s&&(d=(await ke()).find(y=>y.cwd&&(s===y.cwd||s.startsWith(y.cwd+Re.sep)))||null),!d)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let p=(d.boxes||[]).find(h=>h.name===d.leadBox),f=(h,y)=>String(h).replace(/\s+/g," ").trim().slice(0,y),w={title:n?f(n,a3):d.name,body:f(c,vk),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:xi?xi.status().connected:null})}catch(c){t.status(500).json({error:c?.message||String(c)})}});E.post("/api/dictation",async(e,t)=>{if(!Buffer.isBuffer(e.body)||e.body.length===0)return t.status(400).json({error:"expected a raw audio body"});try{t.json(await MC(e.body,e.headers["content-type"]))}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});E.get("/api/dictation/glossary",async(e,t)=>{t.json({configured:!!await Tl(),terms:await bk(),problem:await OC()})});E.post("/api/dictation/learn",async(e,t)=>{let{transcript:n,finalText:r}=e.body||{};if(typeof n!="string"||typeof r!="string")return t.status(400).json({error:"transcript and finalText are required"});t.json({learned:await RC(n,r)})});E.get("/api/tracks/:id/voice-chat",async(e,t)=>{if(!await me(e.params.id))return t.status(404).json({error:"track not found"});t.json(await CC())});E.post("/api/tracks/:id/voice-chat/session",async(e,t)=>{try{let n=await me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});if(n.archivedAt)return t.status(409).json({error:"track is archived"});t.json(await NC())}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});E.post("/api/tracks/:id/voice-chat/usage",async(e,t)=>{try{let n=await me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let{model:r,usage:o,seconds:s}=e.body||{};t.json({ok:!0,...await PC(n,{model:r,usage:o,seconds:s})})}catch(n){t.status(n?.status||500).json({error:n?.message||String(n)})}});E.post("/api/tracks",async(e,t)=>{let{name:n,cwd:r,repo:o,repos:s,agent:a,model:i,skipPermissions:c,ticket:l,circuit:u}=e.body||{};try{let d=await Vp({name:n,cwd:r,repo:o,repos:s,agent:a,model:i,skipPermissions:c!==!1}),p=null;if(l)try{await pi(d,l,{source:"create"})}catch(w){p=w?.message||String(w)}let f=null;if(u)try{await pf(d,await ci(u),{source:"picker"})}catch(w){f=w?.message||String(w)}t.json({ok:!0,track:d,...p?{ticketWarning:p}:{},...f?{circuitWarning:f}:{}})}catch(d){let p=d?.message||String(d);t.status(/required|unknown agent|invalid|must be/i.test(p)?400:500).json({error:p})}});E.get("/api/tracks/:id",async(e,t)=>{let n=await me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({track:n,handoff:eN(ai(e.params.id)),reshape:Ik(gl(e.params.id)),subtasks:Bs(e.params.id).map(nu),pendingOffers:await fy(n)})});E.delete("/api/tracks/:id",async(e,t)=>{try{let n=await My(e.params.id,{cascadeMocks:e.query.mocks!=="0",cascadeRunbooks:e.query.runbooks==="1"});await Eb(e.params.id).catch(()=>{}),t.json(n)}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});E.post("/api/tracks/:id/archive",async(e,t)=>{try{t.json({ok:!0,track:await Ps(e.params.id)})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/already archived/i.test(r)?409:500).json({error:r})}});E.post("/api/tracks/:id/rename",async(e,t)=>{try{t.json({ok:!0,track:await Dy(e.params.id,e.body?.name)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/loop run Tracks/i.test(r)?409:/required|too long/i.test(r)?400:500;t.status(o).json({error:r})}});E.post("/api/tracks/:id/unarchive",async(e,t)=>{try{t.json({ok:!0,track:await Jp(e.params.id,{freshSession:e.body?.freshSession===!0})})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/not archived/i.test(r)?409:500).json({error:r})}});E.get("/api/tracks/:id/messages",async(e,t)=>{let n=await Up(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json(n)});E.get("/api/tracks/:id/events",async(e,t)=>{let n=await me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.set({"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive","x-accel-buffering":"no"}),t.flushHeaders();let r=i=>t.write(`id: ${i.seq}
|
|
807
807
|
data: ${JSON.stringify(i)}
|
|
808
808
|
|
|
809
809
|
`),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=p0(n.id,r),a=setInterval(()=>t.write(`: hb
|
|
810
810
|
|
|
811
|
-
`),25e3);e.on("close",()=>{clearInterval(a),s()})});
|
|
812
|
-
`);
|
|
811
|
+
`),25e3);e.on("close",()=>{clearInterval(a),s()})});E.get("/api/tracks/:id/search",async(e,t)=>{let n=await me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=String(e.query.q||"").trim();if(!r)return t.status(400).json({error:"q (query) is required"});let o=Math.min(Math.max(Number(e.query.k)||8,1),50),s=mc(await K(n)),a=Zu(s,r,{k:o,role:e.query.role||null,tag:e.query.tag||null});t.json({q:r,indexed:s.N,hits:a.map(i=>({score:Number(i.score.toFixed(3)),seq:i.chunk.seq,ts:i.chunk.ts,role:i.chunk.role,box:i.chunk.boxId,provider:i.chunk.provider,tags:i.chunk.tags,text:i.chunk.text}))})});E.get("/api/memory",async(e,t)=>{t.json({stores:await $0(),harvest:T0()})});E.delete("/api/memory/:key/:file",async(e,t)=>{let n=String(e.params.key||"");if(!/^[a-z0-9][a-z0-9._-]*$/.test(n))return t.status(400).json({error:"bad store key"});try{t.json({ok:!0,...await R0(n,String(e.params.file||""))})}catch(r){let o=r?.message||String(r);t.status(/not found/i.test(o)?404:/bad fact/i.test(o)?400:500).json({error:o})}});var c3=["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(`
|
|
812
|
+
`);E.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await qt(e.params.id,c3)})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/archived/i.test(r)?409:/not running/i.test(r)?400:500).json({error:r})}});E.post("/api/tracks/:id/lead/restart",async(e,t)=>{try{t.json({ok:!0,track:await Yp(e.params.id,{freshSession:e.body?.freshSession===!0})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:n?.transferredTo||/mid-turn|terminal|archived/i.test(r)?409:500;t.status(o).json({error:r,...n?.transferredTo?{transferredTo:n.transferredTo}:{}})}});E.post("/api/tracks/:id/say",async(e,t)=>{let{text:n,attachments:r,producer:o}=e.body||{};try{t.json(await qt(e.params.id,n,{attachments:r,producer:typeof o=="string"?o:null,dismissPrompt:!0}))}catch(s){let a=s?.message||String(s);if(s?.usageCap)return t.status(429).json({error:"usage_cap",message:a,cap:s.usageCap});if(s?.transferredTo)return t.status(409).json({error:a,transferredTo:s.transferredTo});t.status(/not found/i.test(a)?404:/archived/i.test(a)?409:/required|not running/i.test(a)?400:500).json({error:a})}});E.post("/api/tracks/:id/upload",async(e,t)=>{try{let n=await 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 VI(n.cwd,{filename:e.query.filename,buffer:r});t.json({ok:!0,attachment:o})}catch(n){let r=n?.message||String(n);t.status(/too large|unsupported|empty|required/i.test(r)?400:500).json({error:r})}});E.get("/api/tracks/:id/uploads/:file",async(e,t)=>{try{let n=await me(e.params.id);if(!n)return t.status(404).json({error:`track "${e.params.id}" not found`});if(!n.cwd)return t.status(404).json({error:"track has no workspace"});let r=Re.basename(String(e.params.file||""));if(!r||!_g(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=Eg(n.cwd),s,a;try{s=await Et.promises.realpath(o),a=await Et.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 l3=/^[0-9a-fA-F-]{8,64}$/;E.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(e,t)=>{try{let n=await me(e.params.id);if(!n)return t.status(404).json({error:`track "${e.params.id}" not found`});let{box:r,uuid:o}=e.params,s=Number(e.params.idx);if(!l3.test(o)||!Number.isInteger(s)||s<0)return t.status(400).json({error:"bad image ref"});let a=(n.boxes||[]).find(p=>p.name===r);if(!a||a.agent!=="claude"||!a.cwd||!a.sessionId)return t.status(404).json({error:"image source not available"});let i=xn(a.cwd,a.sessionId);if(!i)return t.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=Jz.createInterface({input:Et.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?Wm(l)[s]:null;if(!d||!/^image\//.test(d.mediaType))return t.status(404).json({error:"image not found"});t.set("content-type",d.mediaType),t.set("cache-control","private, max-age=31536000, immutable"),t.set("content-security-policy","sandbox"),t.send(Buffer.from(d.data,"base64"))}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.post("/api/tracks/:id/interrupt",async(e,t)=>{try{t.json(await qp(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/archived/i.test(r)?409:500).json({error:r})}});E.delete("/api/tracks/:id/queue",async(e,t)=>{try{let{removed:n,restored:r,draftRev:o}=await yy(e.params.id);t.json({ok:!0,removed:n.filter(s=>s.kind!=="ship-notify"),restored:r,draftRev:o})}catch(n){t.status(500).json({error:n?.message||String(n)})}});E.post("/api/tracks/:id/answer-prompt",async(e,t)=>{let{promptId:n,answers:r,text:o,custom:s}=e.body||{};try{t.json(await Hp(e.params.id,{promptId:n,answers:r,text:o,custom:s}))}catch(a){let i=a?.message||String(a),c=/not found/i.test(i)?404:/no pending prompt|changed since|already answered|not running|not supported|needs a code|look like a sign-in code|no answer selected|custom answer|didn't take/i.test(i)?409:500;t.status(c).json({error:i})}});E.post("/api/setup/:provider/start",async(e,t)=>{try{let n=await Ry({provider:e.params.provider,fresh:e.body?.fresh===!0});t.json({ok:!0,track:n})}catch(n){let r=n?.message||String(n);t.status(/unknown setup provider|isn't available/i.test(r)?400:500).json({error:r})}});E.post("/api/tracks/:id/navigate-prompt",async(e,t)=>{let{promptId:n,questionIndex:r}=e.body||{};try{t.json(await wy(e.params.id,{promptId:n,questionIndex:r}))}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/no pending prompt|changed since|not running|not supported|out of range|review screen/i.test(s)?409:500;t.status(a).json({error:s})}});E.put("/api/tracks/:id/draft",async(e,t)=>{let{text:n,rev:r}=e.body||{};try{t.json(await Sy(e.params.id,typeof n=="string"?n:"",{rev:typeof r=="number"?r:null}))}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required/i.test(s)?400:500;t.status(a).json({error:s})}});E.get("/api/tracks/:id/usage",async(e,t)=>{let n=await me(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=n.boxes?.find(s=>s.name===n.leadBox)||null,o=e.query.fresh==="1"||e.query.fresh==="true";t.json(await Rf(r,{fresh:o}))});E.post("/api/tracks/:id/model",async(e,t)=>{let{model:n}=e.body||{};try{t.json(await Ay(e.params.id,n))}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required|unknown model|not running/i.test(o)?400:500;t.status(s).json({error:o})}});E.post("/api/tracks/:id/subtask",async(e,t)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=e.body||{};try{let a=await zy(e.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});t.json({ok:!0,subtask:nu(a)})}catch(a){let i=a?.message||String(a);if(a?.usageCap)return t.status(429).json({error:"usage_cap",message:i,cap:a.usageCap});let c=/not found/i.test(i)?404:/required|unknown agent|git repo|not running|can't resolve/i.test(i)?400:500;t.status(c).json({error:i})}});E.get("/api/tracks/:id/subtasks",(e,t)=>{t.json({subtasks:Bs(e.params.id).map(nu)})});E.get("/api/tracks/:id/subtask/:sid/messages",async(e,t)=>{let n=await my(e.params.id,e.params.sid);if(!n)return t.status(404).json({error:"subtask not found"});t.json(n)});E.post("/api/tracks/:id/subtask/:sid/cancel",(e,t)=>{try{t.json({ok:!0,subtask:nu(nf(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});E.post("/api/tracks/:id/subtask/:sid/merge-now",(e,t)=>{try{t.json({ok:!0,subtask:nu(Jy(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});function nu(e){return e?{id:e.id,agent:e.agent,task:e.task,skipPermissions:e.skipPermissions,step:e.step,running:!e.finishedAt,box:e.box,branch:e.branch,stat:e.stat,files:e.files||null,conflicts:e.conflicts||[],applied:e.applied,error:e.error,canceled:e.canceled,startedAt:e.startedAt,finishedAt:e.finishedAt}:null}E.post("/api/tracks/:id/handoff-lead",async(e,t)=>{let{toAgent:n,skipPermissions:r,reason:o}=e.body||{};try{let s=await Xp(e.params.id,n,r!==!1,o);t.json({ok:!0,step:s.step,toAgent:s.toAgent,fromBox:s.fromBox,skipPermissions:s.skipPermissions})}catch(s){let a=s?.message||String(s);if(s?.usageCap)return t.status(429).json({error:"usage_cap",message:a,cap:s.usageCap});t.status(/already running/i.test(a)?409:/not found/i.test(a)?404:400).json({error:a})}});E.post("/api/tracks/:id/spin-up-view",async(e,t)=>{try{t.json(await xy(e.params.id,e.body||{}))}catch(n){let r=n?.message||String(n);t.status(/already being spun up/i.test(r)?409:/not found/i.test(r)?404:400).json({error:r})}});E.get("/api/tracks/:id/handoff-lead",(e,t)=>{t.json(eN(ai(e.params.id))??{running:!1})});E.post("/api/tracks/:id/transfer-out",async(e,t)=>{try{let n=await qy(e.params.id);t.json({ok:!0,step:n.step,startedAt:n.startedAt})}catch(n){let r=n?.message||String(n);t.status(/already running/i.test(r)?409:/not found/i.test(r)?404:400).json({error:r})}});E.get("/api/tracks/:id/transfer-out",(e,t)=>{let n=Wy(e.params.id);if(!n)return t.json({running:!1});t.json({running:!n.finishedAt,step:n.step,error:n.error,startedAt:n.startedAt,finishedAt:n.finishedAt,...n.result??{}})});E.post("/api/tracks/:id/transfer-out/commit",async(e,t)=>{try{t.json(await Qp(e.params.id,e.body||{}))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:400).json({error:r})}});E.post("/api/tracks/adopt",async(e,t)=>{let{trackId:n}=e.body||{};try{t.json(await Ky({...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 eN(e){return e?{running:!e.finishedAt,step:e.step,error:e.error,fromBox:e.fromBox,toBox:e.toBox,toAgent:e.toAgent,skipPermissions:e.skipPermissions,startedAt:e.startedAt,finishedAt:e.finishedAt}:null}E.get("/api/debug/handoff",async(e,t)=>{let n=Math.min(Number(e.query.limit)||80,500);t.json({verbose:_u(),paths:V0,trace:await K0({limit:n})})});var Sk=Re.resolve(WC,"..","..","client","dist");Et.existsSync(Sk)&&(E.use(nn.static(Sk)),E.get(/^(?!\/api\/).*/,(e,t)=>t.sendFile(Re.join(Sk,"index.html"),tu)));var tN=HC.createServer(E),u3=new Qz({noServer:!0}),Rk=(e,t,n)=>{if(!(Fl(e)||ki.allowed(ki.clientIp(e))||Kf.allows(e))){t.write(`HTTP/1.1 403 Forbidden\r
|
|
813
813
|
\r
|
|
814
|
-
`),t.destroy();return}if(
|
|
814
|
+
`),t.destroy();return}if(nM(e,t,n))return;if(!QO(e,_k,{enforce:GC})){t.write(`HTTP/1.1 401 Unauthorized\r
|
|
815
815
|
\r
|
|
816
816
|
`),t.destroy();return}let r=mx(e.url);if(!r.ok){t.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
817
817
|
\r
|
|
818
818
|
`),t.destroy();return}let o=r.sessionName;if(!je(o)){t.write(`HTTP/1.1 400 Bad Request\r
|
|
819
819
|
\r
|
|
820
|
-
`),t.destroy();return}let{cols:s,rows:a}=r;
|
|
820
|
+
`),t.destroy();return}let{cols:s,rows:a}=r;u3.handleUpgrade(e,t,n,i=>dx(i,o,{cols:s,rows:a,revive:()=>Py(o)}))};tN.on("upgrade",Rk);var nN=process.env.BOSTRAT_CERT_DIR||Re.join(process.env.HOME||Yl.tmpdir(),".bostrat","certs"),jC=Number(process.env.BOSTRAT_TLS_PORT)||3443,FC=Re.join(nN,"bostrat.globopi.key"),UC=Re.join(nN,"bostrat.globopi.crt");if(Et.existsSync(FC)&&Et.existsSync(UC)){let e=Xz.createServer({key:Et.readFileSync(FC),cert:Et.readFileSync(UC)},E);e.on("upgrade",Rk),e.on("error",t=>{console.error(`[bostrat-agent] https listener failed (${t?.code||t?.message}) \u2014 continuing HTTP-only`)}),e.listen(jC,Gf,()=>{console.log(`[bostrat-agent] https listening on ${Gf||"0.0.0.0"}:${jC}`)})}var Ak=String(process.env.BOSTRAT_VIEW_BIND||"").trim();if(Ak){Kf.active||console.warn(`[bostrat-agent] \u26A0 BOSTRAT_VIEW_BIND=${Ak} 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=oM({app:E,onUpgrade:Rk,extractViewSlug:Ul,log:console}),t=HC.createServer(e.request);t.on("upgrade",e.upgrade),sM(t,{addr:Ak,port:Tk,log:console})}tN.listen(Tk,Gf,()=>{console.log(`[bostrat-agent] listening on ${Gf||"0.0.0.0"}:${Tk}`);let e=tR(process.env);e&&console.warn(`[bostrat-agent] \u26A0 SECURITY: plane-linked node bound to ${e.host?e.host:"ALL interfaces (BOSTRAT_HOST=all)"} \u2014 the API + Views are reachable off-loopback (LAN/VPN), where the IP allowlist is spoofable and Views need no token. The plane tunnel does not require this; set BOSTRAT_HOST=127.0.0.1 once any local reverse-proxy/WireGuard path is retired (F-2).`),console.log(`[bostrat-agent] tmux socket: ${zt}`),console.log(`[bostrat-agent] auto-trigger default: ${qC}%`),console.log(`[bostrat-agent] ip allowlist: ${ki.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(Kf.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),Uy().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),Iy().then(t=>t&&console.log(`[bostrat-agent] rolled back ${t} interrupted reshape(s)`)).catch(t=>console.error("[bostrat-agent] reshape reconcile failed:",t?.message||t)),Vy().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),By().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(()=>Ly()).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)),$y().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)),y3(),w$({listTracks:ke}),l$({trackOf:py}),f3(),m3(),g3(),DA({listTracks:ke,getTrack:me}).then(t=>console.log(`[bostrat-agent] spend caps ${t.configured?"armed":"unconfigured (fail-open)"} \u2014 seeded ${Object.keys(t.spent).length} provider counter(s)`)).catch(t=>console.error("[bostrat-agent] spend-caps seed failed (staying fail-open):",t?.message||t)),QI()&&n$({listTracks:ke}),J0().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)),rC(),iC(),iO({resolveTrack:me}),gO({listTracks:ke,createTrack:Vp,archiveTrack:Ps,listRepos:fr,resolveTrack:me,sayToLead:qt,answerPrompt:Hp,startLeadHandoff:Xp,interruptLead:qp,listSubtaskJobs:Bs,cancelSubtask:nf,readEvents:K,appendEvent:N,divertShip:pk,answerPrExists:fk,knownAgents:ml}),d3(),Ji().then(t=>{t||console.warn(`[boot] no host tmux server on ${zt} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),xC(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(xi=dR({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,apiToken:_k,nodeId:process.env.BOSTRAT_NODE_ID||Yl.hostname(),name:process.env.BOSTRAT_NODE_NAME||Yl.hostname(),listTracks:ke,detectAgents:Fp,sampleMetrics:PO,deliverySample:()=>E_(gy()),onHashKey:x_,onViewBase:BR,onTracksTransferred:Gy,onDegraded:({since:t,unacked:n,oldestAgeMs:r,mode:o})=>{let s=Math.round(r/6e4),a=`${process.env.BOSTRAT_NODE_NAME||Yl.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(()=>{}),LC=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(()=>{}),LC=null},onConnected:()=>{nC()},onRemoved:({reason:t,via:n})=>{let r=`${process.env.BOSTRAT_NODE_NAME||Yl.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{Ek={latest:t.latest,current:t.current},await or({...t,policy:Ct()}),Ct()==="auto"&&await VC({to:t.latest,from:t.current})}catch(n){console.error(`[plane-link] update notice failed: ${n?.message||n}`)}},onUpdatePolicy:t=>r3(t).catch(()=>{}),onHelloRefused:async({current:t,min:n,latest:r,installKind:o,fix:s})=>{try{let a=r||n;if(await or({current:t,latest:a,min:n,installKind:o,required:!0,policy:Ct(),status:"available",reason:"refused"}),Ct()!=="off"&&a){let i=await df({to:a,kind:hn,reason:"hard-floor"});i.ok?(Ql.cancel(),await or({current:t,latest:a,min:n,installKind:o,required:!0,policy:Ct(),status:"updating",reason:"hard-floor"})):console.error(`[self-update] hard-floor update not started: ${i.reason} \u2014 ${s}`)}}catch(a){console.error(`[plane-link] hard-floor handling failed: ${a?.message||a}`)}},updatePolicy:Ct}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),aa().catch(()=>{}),qI().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),cO().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),mI().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),OI().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),$T().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{xi?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function d3(){p3(),hp().catch(()=>{}),setInterval(()=>hp().catch(()=>{}),1e4)}function p3(){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(WC,"..","bin",e),n=Re.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!Et.existsSync(t))continue;Et.mkdirSync(n,{recursive:!0});let r=Re.join(n,e);(Et.existsSync(r)?Et.readlinkSync(r):null)!==t&&(Et.rmSync(r,{force:!0}),Et.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 f3(){let e=!1,t=async()=>{if(!e){e=!0;try{let n=await ke(),r=await LO({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 m3(){let e=null,t=async()=>{try{let n=Ta(),r=0,o=[],s=null,a=null;try{let u=await ke(),d=await Bb(u);r=d.fleet.unpricedTurns||0,o=d.fleet.unpricedModels||[],s=await DO(u,{days:7}),a=(await jb(u,{window:"7d"})).categorization}catch{}let i=[...n.problems,...a?.problems||[]];s&&!s.ok&&i.push(`${s.divergent} recent row(s) diverge from the table (implied-rate audit) \u2014 run \`npm run cost -- --audit\``);let c=o.filter(u=>u!=="unknown");c.length&&i.push(`${r} unpriced turn(s) \u2014 add rates for: ${c.join(", ")}`);let l=i.join(`
|
|
821
821
|
`);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(`
|
|
822
|
-
`).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
|
|
822
|
+
`).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 h3=Number(process.env.BOSTRAT_MEMORY_RECONCILE_MS)||5*6e4,w3=Number(process.env.BOSTRAT_MEMORY_RECONCILE_FIRST_MS)||5*6e4;function g3(){let e=async()=>{try{let n=await I0();n.ran&&console.log(`[memory] one-time full backfill sweep: ${n.harvested} fact(s) harvested`);let o=(await ke()).filter(a=>!Ue(a)&&a.kind!=="setup").flatMap(a=>[a.cwd,...(a.boxes||[]).map(i=>i.cwd)]).filter(Boolean),s=await _0([...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,h3).unref()},w3).unref()}function y3(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{let t=await ke();for(let n of t)Ue(n)||!n.boxes?.some(r=>r.state==="live")||(await Up(n.id).catch(()=>{}),await yp(n).catch(()=>{}))}catch{}finally{e=!1}}},4e3)}
|