bostrat-node 1.7.4 → 1.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +107 -41
- package/dist/index.js +219 -155
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
var
|
|
2
|
-
`).includes(a));if(!s.length)return;await
|
|
1
|
+
var yA=Object.defineProperty;var O=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var cu=(e,t)=>{for(var n in t)yA(e,n,{get:t[n],enumerable:!0})};import{createHash as bA,randomBytes as kA,timingSafeEqual as xA}from"node:crypto";function Rh(){return kA(32).toString("hex")}function $s(e){let t=typeof e=="string"?e.trim():"";return t?bA("sha256").update(t).digest("hex"):null}function $h(e,t){return typeof e!="string"||typeof t!="string"||e.length!==t.length||!e?!1:xA(Buffer.from(e),Buffer.from(t))}function Os(e,t={},{now:n=Date.now(),log:r=console.warn}={}){if(lu.set(e,(lu.get(e)||0)+1),uu.has(e)&&n-uu.get(e)<SA)return!1;uu.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=${lu.get(e)}${o?` ${o}`:""}`),!0}async function Oh(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 xi,Si,lu,uu,SA,Cs=O(()=>{xi="BOSTRAT_HOOK_TOKEN",Si="x-bostrat-hook";lu=new Map,uu=new Map,SA=6e4});import st from"node:fs/promises";import Ch from"node:os";import Qe from"node:path";import{execFile as vA}from"node:child_process";import{promisify as TA}from"node:util";function vi(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function AA(){return process.env.CLAUDE_CONFIG_DIR?Qe.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):Qe.join(Ch.homedir(),".claude.json")}function EA(){return process.env.CLAUDE_CONFIG_DIR?Qe.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):Qe.join(Ch.homedir(),".claude","settings.json")}async function Rn(e){if(!e||typeof e!="string")return;let t=Qe.resolve(e),n=AA(),r=EA(),o={};try{o=JSON.parse(await st.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 st.mkdir(Qe.dirname(n),{recursive:!0}),await st.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await st.readFile(r,"utf8"))}catch{}if(i.skipDangerousModePermissionPrompt=!0,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 "${Si}: $${xi}" --data-binary @- >/dev/null 2>&1 || true`,u=[{hooks:[{type:"command",command:l}]}];i.hooks=i.hooks||{};for(let f of["SessionStart","UserPromptSubmit","Stop"])i.hooks[f]=u;i.hooks.PostToolUse=[{matcher:"*",hooks:[{type:"command",command:l}]}]}await st.mkdir(Qe.dirname(r),{recursive:!0}),await st.writeFile(r,JSON.stringify(i,null,2)),await _A(t)}async function _A(e){try{let t=Qe.resolve(e),n=Qe.join(t,".bostrat","memory","claude"),r=Qe.join(t,".claude","settings.local.json"),o={};try{o=JSON.parse(await st.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(Qe.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await st.mkdir(n,{recursive:!0}),await st.mkdir(Qe.dirname(r),{recursive:!0}),await st.writeFile(r,JSON.stringify(o,null,2)),await IA(t,["/.claude/settings.local.json"])}catch{}}async function IA(e,t){try{let{stdout:n}=await TA(vA)("git",["-C",e,"rev-parse","--git-path","info/exclude"]),r=Qe.resolve(e,n.trim()),o=await st.readFile(r,"utf8").catch(()=>""),s=t.filter(a=>!o.split(`
|
|
2
|
+
`).includes(a));if(!s.length)return;await st.mkdir(Qe.dirname(r),{recursive:!0}),await st.appendFile(r,(o&&!o.endsWith(`
|
|
3
3
|
`)?`
|
|
4
4
|
`:"")+s.join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`)}catch{}}var
|
|
6
|
+
`)}catch{}}var Ms=O(()=>{Cs()});import du from"node:fs/promises";import RA from"node:os";import Ti from"node:path";function $A(){let e=process.env.CODEX_HOME||Ti.join(RA.homedir(),".codex");return Ti.join(e,"config.toml")}function Mh(e){return`[projects."${e}"]`}function OA(e,t){let n=Mh(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 lo(e){if(!e||typeof e!="string")return;let t=Ti.resolve(e),n=$A(),r="";try{r=await du.readFile(n,"utf8")}catch{}if(OA(r,t))return;let o=Mh(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),f=/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)+f+r.slice(l)}else{let s=`
|
|
9
9
|
${o}
|
|
10
10
|
trust_level = "trusted"
|
|
11
11
|
`;r=r.trimEnd()+s+(r.length&&!r.endsWith(`
|
|
12
12
|
`)?`
|
|
13
|
-
`:"")}try{await
|
|
14
|
-
`,"utf8")}var
|
|
13
|
+
`:"")}try{await du.mkdir(Ti.dirname(n),{recursive:!0}),await du.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var Ai=O(()=>{});import Nh from"node:fs/promises";import CA from"node:os";import Ns from"node:path";function MA(e){let n=Ns.resolve(e).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||Ns.join(CA.homedir(),".cursor");return Ns.join(r,"projects",n)}async function uo(e){if(!e||typeof e!="string")return;let t=Ns.resolve(e),n=MA(t),r=Ns.join(n,".workspace-trusted");await Nh.mkdir(n,{recursive:!0}),await Nh.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:t},null,2)}
|
|
14
|
+
`,"utf8")}var Ei=O(()=>{});import pu from"node:fs/promises";import $n from"node:path";import{execFile as NA}from"node:child_process";import{promisify as PA}from"node:util";async function UA(e){try{return(await pu.stat($n.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function HA(e){let t=i=>pu.readFile($n.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 Xt(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await UA(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:$n.basename(o),branch:await HA(o)});return}if(s>=LA)return;let i;try{i=await pu.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||FA.has(c.name)||await t($n.join(o,c.name),s+1)}for(let o of jA)await t($n.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=(await pe(o.path))?.toLowerCase();if(s){if(n.has(s))continue;n.add(s)}r.push(o)}return r.sort((o,s)=>o.name.localeCompare(s.name))}function WA(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function pe(e){if(!e)return null;if(fu.has(e))return fu.get(e);let t=null;try{let{stdout:n}=await BA("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=WA(n)}catch{t=null}return fu.set(e,t),t}async function fo(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await Xt())if((await pe(n.path))?.toLowerCase()===t)return n.path;return null}var BA,Ph,DA,jA,LA,FA,fu,Sr=O(()=>{BA=PA(NA),Ph=process.env.HOME||"/home/globo",DA=$n.join(process.env.BOSTRAT_STATE_DIR||$n.join(Ph,".bostrat"),"repos"),jA=[...(process.env.BOSTRAT_REPOS_ROOT||$n.join(Ph,"Documents")).split(","),DA].map(e=>e.trim()).filter(Boolean),LA=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,FA=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);fu=new Map});import qA from"node:fs";import Bh from"node:fs/promises";import Ps from"node:path";import KA from"node:os";import{fileURLToPath as GA}from"node:url";import{execFile as VA}from"node:child_process";import{promisify as zA}from"node:util";function be(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 _i(e=process.env){return!!(be(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Ii(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!jh.test(n))throw po("repo must be owner/name",400);let r=mu.get(n);if(r&&r.expiresAt-Date.now()>YA)return r;if(!_i(t))throw po("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await Lh(`${be(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:Fh(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw po(`plane unreachable \u2014 GitHub auth unavailable (${i?.message||i})`,503)}let s=await o.json().catch(()=>({}));if(!o.ok){if(o.status>=500&&r&&r.expiresAt>Date.now())return r;throw po(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 mu.set(n,a),a}async function Bs({env:e=process.env,force:t=!1}={}){if(!_i(e))return{configured:!1,repos:[],username:null};if(!t&&vr&&Date.now()-vr.at<ZA)return vr.data;try{let n=await Lh(`${be(e)}/v1/github/repos`,{headers:Fh(e)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return vr={at:Date.now(),data:r},r}catch(n){return vr?vr.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Uh(){mu.clear(),vr=null}async function Ct({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await pe(e):null);if(!r)return{};let{token:o}=await Ii(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function hu({env:e=process.env}={}){return(await Bs({env:e})).username??null}function nE(e){let[t,n]=String(e).split("/");return Ps.join(XA,t,n)}async function wu(e,{env:t=process.env}={}){let n=(...r)=>Dh("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${eE()}" ${tE(t)}`),await n("config","credential.useHttpPath","true")}async function Ri(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!jh.test(String(e||"")))throw po(`bad repo slug "${e}"`,400);let r=nE(e);if(qA.existsSync(Ps.join(r,".git")))return r;let{token:o}=await Ii(e,{env:t});await Bh.mkdir(Ps.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Dh("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:QA,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Bh.rm(r,{recursive:!0,force:!0}).catch(()=>{}),po(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await wu(r,{env:t}),r}var Dh,JA,XA,jh,YA,ZA,QA,Lh,mu,vr,Fh,po,eE,tE,Ve=O(()=>{Sr();Dh=zA(VA),JA=process.env.BOSTRAT_STATE_DIR||Ps.join(process.env.HOME||KA.tmpdir(),".bostrat"),XA=Ps.join(JA,"repos"),jh=/^[^/\s]+\/[^/\s]+$/,YA=5*6e4,ZA=6e4,QA=10*6e4,Lh=fetch,mu=new Map,vr=null;Fh=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),po=(e,t)=>Object.assign(new Error(e),{status:t});eE=()=>GA(new URL("../bin/bostrat-git-credential",import.meta.url)),tE=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function Hh(e=process.env){return!!(be(e)&&e.BOSTRAT_PLANE_TOKEN)}async function qh(e,t,n){let r=be(n),o=new AbortController,s=setTimeout(()=>o.abort(),oE);try{return await sE(`${r}${e}`,{...t,signal:o.signal})}finally{clearTimeout(s)}}async function Kh(e,{env:t=process.env,force:n=!1}={}){if(!Hh(t))return{facts:[],source:"unconfigured"};let r=gu.get(e);if(!n&&r&&Date.now()-r.at<rE)return{facts:r.facts,source:"cache"};try{let o=await qh(`/v1/memory/${encodeURIComponent(e)}`,{headers:Wh(t)},t);if(!o.ok)throw new Error(`plane ${o.status}`);let s=await o.json(),a=Array.isArray(s?.facts)?s.facts:[];return gu.set(e,{at:Date.now(),facts:a}),{facts:a,source:"plane"}}catch(o){return r?{facts:r.facts,source:"stale",error:o?.message}:{facts:[],source:"unreachable",error:o?.message}}}async function $i(e,t,{env:n=process.env}={}){if(!Hh(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await qh(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...Wh(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 gu.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 rE,oE,gu,sE,Wh,Gh=O(()=>{Ve();rE=6e4,oE=5e3,gu=new Map,sE=(...e)=>fetch(...e);Wh=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import K from"node:fs/promises";import aE from"node:os";import oe from"node:path";import{execFile as iE}from"node:child_process";import{promisify as cE}from"node:util";function Oi(){return oe.join(yu,"memory","repos")}function Tr(e){return e===Yt?oe.join(yu,"memory",Yt):oe.join(Oi(),e)}async function Ds(e){try{return JSON.parse(await K.readFile(oe.join(e,Yh),"utf8"))}catch{return{}}}async function bu(e,t){let n=await Ds(e);for(let[r,o]of t)n[r]=Math.max(Number(n[r])||0,o);await K.mkdir(e,{recursive:!0}),await K.writeFile(oe.join(e,Yh),JSON.stringify(n))}function zh(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 Jh(e){try{let{stdout:t}=await lE("git",["-C",e,"config","--get","remote.origin.url"]);return t.trim()||null}catch{return null}}async function ku(e){if(!e)return[];let t=zh(await Jh(e));if(t)return[t];let n=[],r=[];try{r=await K.readdir(e,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=zh(await Jh(oe.join(e,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function Ci(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 On(e,t){let n=new RegExp(`^\\s*${t}:\\s*(.+?)\\s*$`,"m").exec(e||"");return n?n[1].replace(/^"|"$/g,""):null}async function Mt(e,t=null){let n=t??await K.readFile(e,"utf8").catch(()=>""),{fm:r}=Ci(n),o=On(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await K.stat(e)).mtimeMs}catch{return 0}}function Zh(e,t){let n=On(e,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===Yt){let o=On(e,"trust");if(!o||/^operator/i.test(o))return Yt}return t}function xu(e,t,n,r=new Map){let o=(e||"").split(`
|
|
15
15
|
`),s=new Map;for(let c of(t||"").split(`
|
|
16
|
-
`)){let l=
|
|
16
|
+
`)){let l=mo(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=mo(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(`
|
|
17
17
|
`).replace(/\n{3,}/g,`
|
|
18
18
|
|
|
19
|
-
`)}function
|
|
19
|
+
`)}function Qh(e,t){let n=On(t,"name")||e.replace(/\.md$/,""),r=On(t,"description")||"";return`- [${n}](${e})${r?` \u2014 ${r}`:""}`}async function js(e){let t=[];try{t=await K.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 ew(e,t){let n=oe.join(e,".bostrat","memory"),r=await js(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=new Map,s=new Map,a=0,i=new Map;for(let l of r){let u=oe.join(n,l),f;try{f=await K.readFile(u,"utf8")}catch{continue}let{fm:d}=Ci(f),p=Zh(d,t);if(!p)continue;let m=Tr(p),w=oe.join(m,l),y=await Mt(u,f);if(i.has(m)||i.set(m,await Ds(m)),(i.get(m)[l]||0)>=y)continue;let b=await Mt(w).catch(()=>0);if(await K.access(w).then(()=>!0).catch(()=>!1)&&b>=y)continue;await K.mkdir(m,{recursive:!0}),await K.writeFile(w,f),a++;let E=o.get(m);E||o.set(m,E={won:new Set,generated:new Map}),E.won.add(l),E.generated.set(l,Qh(l,d)),s.has(p)||s.set(p,new Set),s.get(p).add(l)}let c=await K.readFile(oe.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[l,u]of o){let f=oe.join(l,"MEMORY.md"),d=await K.readFile(f,"utf8").catch(()=>"");await K.writeFile(f,xu(d,c,u.won,u.generated))}return{harvested:a,changedByKey:s}}async function dE(){let e=oe.join(yu,"workspaces"),t=[];try{t=await K.readdir(e,{withFileTypes:!0})}catch{return{harvested:0}}let n=0,r=new Map;for(let o of t){if(!o.isDirectory())continue;let s=oe.join(e,o.name);if(!await K.access(oe.join(s,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[i]=await ku(s),c=await ew(s,i||null);n+=c.harvested;for(let[l,u]of c.changedByKey){r.has(l)||r.set(l,new Set);for(let f of u)r.get(l).add(f)}}return{harvested:n,changedByKey:r}}async function mE(e,t){let n=Tr(e),r=await Ds(n),o=new Set,s=new Map,a=[],i=0;for(let c of t||[]){let l=String(c?.file||"");if(!fE.test(l)||l==="MEMORY.md")continue;let u=oe.join(n,l),f=await K.access(u).then(()=>!0).catch(()=>!1),d=f?await Mt(u):0,p=Number(c.modified)||0;if(c.deleted){p>d&&(f&&await K.rm(u,{force:!0}),await bu(n,[[l,p]]),f&&(a.push(l),i++));continue}f&&d>=p||(r[l]||0)>=p||(await K.mkdir(n,{recursive:!0}),await K.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=oe.join(n,"MEMORY.md"),l=await K.readFile(c,"utf8").catch(()=>""),u=xu(l,[...s.values()].join(`
|
|
20
20
|
`),o);if(a.length){let f=new Set(a);u=u.split(`
|
|
21
|
-
`).filter(d=>!f.has(
|
|
22
|
-
`)}await K.mkdir(n,{recursive:!0}),await K.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function
|
|
23
|
-
`);t.length>
|
|
24
|
-
`);for(;Buffer.byteLength(n,"utf8")>
|
|
25
|
-
`);return n}async function
|
|
21
|
+
`).filter(d=>!f.has(mo(d))).join(`
|
|
22
|
+
`)}await K.mkdir(n,{recursive:!0}),await K.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function Su(e,t=null){let n=Tr(e),r=t?.length?t:await js(n);if(!r.length)return{pushed:0,source:"empty"};let o=pE(await K.readFile(oe.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=oe.join(n,a),c;try{c=await K.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await Mt(i,c),indexLine:o.get(a)??null})}return $i(e,s)}async function hE(e){let{facts:t,source:n}=await Kh(e);t.length?await mE(e,t):n==="plane"&&await Su(e)}function wE(e){let t=e.split(`
|
|
23
|
+
`);t.length>Vh&&(t=t.slice(0,Vh));let n=t.join(`
|
|
24
|
+
`);for(;Buffer.byteLength(n,"utf8")>uE&&t.length;)t.pop(),n=t.join(`
|
|
25
|
+
`);return n}async function yE(e,t){if(!e||!t?.length)return{materialized:!1,reason:"no-repo-key"};let n=oe.join(e,".bostrat","memory"),r=0,o=0,s=[];for(let u of t){let f=Tr(u),d=await Ds(f);for(let[y,b]of Object.entries(d)){let x=oe.join(n,y);await K.access(x).then(()=>!0).catch(()=>!1)&&await Mt(x)<=b&&(await K.rm(x,{force:!0}),o++)}let p=await js(f);if(!p.length)continue;await K.mkdir(n,{recursive:!0});for(let y of p){let b=oe.join(f,y),x=oe.join(n,y),E=await Mt(b),_=await Mt(x).catch(()=>0);await K.access(x).then(()=>!0).catch(()=>!1)&&_>=E||(await K.copyFile(b,x),r++)}let m=await K.readFile(oe.join(f,"MEMORY.md"),"utf8").catch(()=>""),w=u===Yt?"## operator memory":`## repo: ${u}`;m.trim()&&s.push(t.length>1?`${w}
|
|
26
26
|
|
|
27
|
-
${
|
|
27
|
+
${m.trim()}`:m.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(`
|
|
28
28
|
|
|
29
29
|
`)+`
|
|
30
|
-
`,c=
|
|
31
|
-
`).filter(
|
|
32
|
-
`);await K.writeFile(c,u).catch(()=>{});let f=
|
|
33
|
-
`).filter(l=>
|
|
34
|
-
`)).catch(()=>{});let c=await
|
|
35
|
-
`)){let r=
|
|
30
|
+
`,c=wE(i);await K.mkdir(n,{recursive:!0}),await K.writeFile(oe.join(n,"MEMORY.md"),c);let l=oe.join(n,"claude");return await K.mkdir(l,{recursive:!0}),await K.writeFile(oe.join(l,"MEMORY.md"),gE,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function tw(e){try{let t=await ku(e);if(!t.length)return{materialized:!1,reason:"no-repo-key"};let n=[...t,Yt];if(Date.now()-Xh>bE){Xh=Date.now(),await kE();let{changedByKey:r}=await dE();for(let[o,s]of r)await Su(o,[...s])}for(let r of n)await hE(r);return await yE(e,n)}catch(t){return{materialized:!1,reason:"error",error:t?.message||String(t)}}}async function kE(){let e=0,t=[];try{t=await K.readdir(Oi(),{withFileTypes:!0})}catch{return{moved:e}}for(let n of t){if(!n.isDirectory())continue;let r=oe.join(Oi(),n.name),o=Tr(Yt),s=new Set,a=new Map;for(let p of await js(r)){let m=oe.join(r,p),w;try{w=await K.readFile(m,"utf8")}catch{continue}let{fm:y}=Ci(w);if(Zh(y,n.name)!==Yt)continue;let b=oe.join(o,p),x=await Mt(b).catch(()=>0),E=await Mt(m,w);await K.mkdir(o,{recursive:!0}),E>x&&await K.writeFile(b,w),await K.rm(m,{force:!0}),s.add(p),a.set(p,Qh(p,y)),e++}if(!s.size)continue;let i=Date.now();await bu(r,[...s].map(p=>[p,i])),await $i(n.name,[...s].map(p=>({file:p,deleted:!0,modified:i})));let c=oe.join(r,"MEMORY.md"),l=await K.readFile(c,"utf8").catch(()=>""),u=l.split(`
|
|
31
|
+
`).filter(p=>!s.has(mo(p))).join(`
|
|
32
|
+
`);await K.writeFile(c,u).catch(()=>{});let f=oe.join(o,"MEMORY.md"),d=await K.readFile(f,"utf8").catch(()=>"");await K.writeFile(f,xu(d,l,s,a))}return{moved:e}}async function nw(){let e=[],t=[Yt];try{for(let n of await K.readdir(Oi(),{withFileTypes:!0}))n.isDirectory()&&t.push(n.name)}catch{}for(let n of t){let r=Tr(n),o=await js(r),s=await Ds(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await K.readFile(oe.join(r,i),"utf8").catch(()=>""),{fm:l}=Ci(c);a.push({file:i,name:On(l,"name")||i.replace(/\.md$/,""),description:On(l,"description")||"",scope:On(l,"scope")||null,modified:await Mt(oe.join(r,i),c)})}e.push({key:n,facts:a,tombstones:Object.keys(s).length})}return e}async function rw(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=Tr(e),r=oe.join(n,t);if(!await K.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${t} not found in ${e}`);let s=Date.now();await K.rm(r,{force:!0}),await bu(n,[[t,s]]);let a=oe.join(n,"MEMORY.md"),i=await K.readFile(a,"utf8").catch(()=>"");await K.writeFile(a,i.split(`
|
|
33
|
+
`).filter(l=>mo(l)!==t).join(`
|
|
34
|
+
`)).catch(()=>{});let c=await $i(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function ow(e){try{let t=await ku(e),{harvested:n,changedByKey:r}=await ew(e,t[0]||null);for(let[o,s]of r)await Su(o,[...s]);return{harvested:n}}catch(t){return{harvested:0,error:t?.message||String(t)}}}var lE,yu,Vh,uE,Yt,Yh,mo,fE,pE,gE,Xh,bE,Mi=O(()=>{Gh();lE=cE(iE),yu=process.env.BOSTRAT_STATE_DIR||oe.join(process.env.HOME||aE.tmpdir(),".bostrat"),Vh=200,uE=25*1024;Yt="operator";Yh=".tombstones.json";mo=e=>/\]\(([^)]+\.md)\)/.exec(e)?.[1]||null;fE=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,pE=e=>{let t=new Map;for(let n of(e||"").split(`
|
|
35
|
+
`)){let r=mo(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};gE=`# Track memory
|
|
36
36
|
|
|
37
37
|
Shared Track memory (all providers) lives one level up: read \`../MEMORY.md\` for the
|
|
38
38
|
index and the fact files beside it. Facts you save here stay Claude-local but ride the
|
|
39
39
|
same sync; prefer adding durable cross-provider facts to \`../\` following its format.
|
|
40
|
-
`;
|
|
41
|
-
`,n!=null?{mode:n}:void 0),await
|
|
42
|
-
`,"utf8");let n=(await
|
|
43
|
-
`).filter(Boolean);n.length>
|
|
40
|
+
`;Xh=0,bE=3e4});import vu from"node:fs/promises";import xE from"node:path";async function et(e,t,{mode:n}={}){await vu.mkdir(xE.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${++SE}.tmp`;await vu.writeFile(r,JSON.stringify(t,null,2)+`
|
|
41
|
+
`,n!=null?{mode:n}:void 0),await vu.rename(r,e)}var SE,Cn=O(()=>{SE=0});import lw from"node:fs/promises";import uw from"node:os";import dw from"node:path";function Te(e=process.env){return!!(be(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Us(e,t={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),vE);try{return await RE(`${be(n)}${e}`,{...t,signal:r.signal})}finally{clearTimeout(o)}}async function pw(){return Pi||(Tu||=lw.readFile(fw(),"utf8").then(e=>JSON.parse(e)).catch(()=>({ops:[]})).then(e=>Pi={ops:Array.isArray(e?.ops)?e.ops:[],parked:Array.isArray(e?.parked)?e.parked:[]}).finally(()=>Tu=null),Tu)}async function Bi(){await et(fw(),Pi).catch(()=>{})}function iw(e){return e.op==="blob"?`blob ${e.family} ${e.id} ${e.v} ${e.kind}`:`${e.op} ${e.family} ${e.id}`}async function Eu(e){let t=await pw(),n=iw(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=>iw(o)===n);r>=0?t.ops[r]=e:t.ops.push(e)}t.ops.length>sw&&(t.ops=t.ops.slice(-sw)),await Bi()}function cw(){Ni||(Ni=setTimeout(()=>{Ni=null,ho().catch(()=>{})},$E),Ni.unref?.())}function Au(e){let t=new Error(`plane ${e}`);return t.refused=!0,t}async function CE(e,t=process.env){if(e.op==="record"){let n=await Us(`/v1/content/${encodeURIComponent(e.family)}`,{method:"POST",headers:{...Fs(t),"content-type":"application/json"},body:JSON.stringify({records:[{id:e.id,trackId:e.trackId??null,data:e.data,updatedAt:e.updatedAt,originNode:IE()}]})},t);if(!n.ok)throw Au(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 lw.readFile(e.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await Us(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}/blob?v=${e.v}&kind=${encodeURIComponent(e.kind)}`,{method:"POST",headers:{...Fs(t),"content-type":"application/octet-stream"},body:n},t);if(!r.ok&&r.status!==400)throw Au(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 Us(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}?deletedAt=${e.deletedAt}`,{method:"DELETE",headers:Fs(t)},t);if(!n.ok)throw Au(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function ho({env:e=process.env,log:t=console,now:n=Date.now()}={}){return Te(e)?Ls||(Ls=(async()=>{let r=await pw();ME(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await CE(a,e)}catch(c){if(!c?.refused)return cw(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<AE)return cw(),await Bi(),{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>aw&&(r.parked=r.parked.slice(-aw)),s++,await Bi();continue}i.rejected&&t.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await Bi()}return{flushed:o,parked:s,pending:0}})().finally(()=>{Ls=null}),Ls):{flushed:0,source:"unconfigured"}}function ME(e,t){if(!e.parked.length||e.ops.length)return;let n=e.parked.filter(r=>t-(r.parkedAt||0)>=OE);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 ze(e,t,{trackId:n=null,env:r=process.env}={}){return Te(r)?(await Eu({op:"record",family:e,id:t.id,trackId:n??t.trackId??null,data:t,updatedAt:Number(t.updatedAt)||Date.now()}),ho({env:r})):{queued:!1,source:"unconfigured"}}async function Mn(e,t,n,r,o,{env:s=process.env}={}){return Te(s)?(await Eu({op:"blob",family:e,id:t,v:Number(n),kind:r,path:o}),ho({env:s})):{queued:!1,source:"unconfigured"}}async function xt(e,t,{deletedAt:n=Date.now(),env:r=process.env}={}){return Te(r)?(await Eu({op:"delete",family:e,id:t,deletedAt:n}),ho({env:r})):{queued:!1,source:"unconfigured"}}async function Be(e,{env:t=process.env}={}){if(!Te(t))return{records:[],source:"unconfigured"};try{let n=await Us(`/v1/content/${encodeURIComponent(e)}?tombstones=1`,{headers:Fs(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 Hs(e,t,n,r,{env:o=process.env}={}){if(!Te(o))return null;try{let s=await Us(`/v1/content/${encodeURIComponent(e)}/${encodeURIComponent(t)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Fs(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 vE,TE,sw,AE,aw,EE,_E,fw,IE,RE,$E,OE,Fs,Pi,Tu,Ni,Ls,Nn=O(()=>{Ve();Cn();vE=1e4,TE=3e4,sw=2e3,AE=5,aw=100,EE=60*6e4,_E=()=>process.env.BOSTRAT_STATE_DIR||dw.join(process.env.HOME||uw.tmpdir(),".bostrat"),fw=()=>dw.join(_E(),"content-outbox.json"),IE=()=>process.env.BOSTRAT_NODE_ID||uw.hostname(),RE=(...e)=>fetch(...e),$E=TE,OE=EE;Fs=(e=process.env)=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`});Pi=null,Tu=null,Ni=null,Ls=null});import _u from"node:fs/promises";import mw from"node:path";async function hw(e,t,{box:n=null,agent:r=null}={}){if(!e||!Te())return{pushed:!1};try{let o=Date.now();return await ze("checkpoints",{id:e,trackId:e,v:o,box:n,agent:r,updatedAt:o},{trackId:e}),await Mn("checkpoints",e,o,"md",t),{pushed:!0,v:o}}catch{return{pushed:!1}}}async function Di(e){try{let t=BE(e);if(!t||!Te())return{restored:!1};let n=mw.join(e,NE,PE);if(await _u.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await Be("checkpoints");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 Hs("checkpoints",t,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await _u.mkdir(mw.dirname(n),{recursive:!0}),await _u.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var NE,PE,BE,ji=O(()=>{Nn();NE=".bostrat/handoff",PE="latest.md",BE=e=>/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1]??null});import Ws from"node:fs/promises";import DE from"node:os";import gw from"node:path";function Ar(e){let t=String(e??"");for(let n of jE)t=t.replace(n,"\u2039redacted\u203A");return t}function Li(){let e=process.env.BOSTRAT_HANDOFF_DEBUG;return e==="1"||e==="true"}async function LE(e){try{await Ws.mkdir(Iu,{recursive:!0}),await Ws.appendFile(wo,`${JSON.stringify(e)}
|
|
42
|
+
`,"utf8");let n=(await Ws.readFile(wo,"utf8")).split(`
|
|
43
|
+
`).filter(Boolean);n.length>ww&&await Ws.writeFile(wo,`${n.slice(-ww).join(`
|
|
44
44
|
`)}
|
|
45
|
-
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${
|
|
46
|
-
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=
|
|
47
|
-
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,f,d,
|
|
48
|
-
`).map(n=>n.trim()).includes(e)}catch{return!1}}function
|
|
49
|
-
`).filter(Boolean).length}catch{return 0}}async function
|
|
50
|
-
`);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
|
|
51
|
-
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function
|
|
52
|
-
`,n);i!==-1&&(t=t.slice(0,i))}let r=
|
|
53
|
-
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function
|
|
54
|
-
`)){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(
|
|
55
|
-
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=t)break}return o}finally{await n.close()}}async function
|
|
56
|
-
`)){let
|
|
57
|
-
`;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
|
|
58
|
-
`)){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 f=[];if(u.type==="function_call"&&
|
|
59
|
-
`).trim():"";
|
|
60
|
-
`).trim():""}function
|
|
61
|
-
`)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let w=c.attachment;w?.type==="queued_command"&&w.commandMode==="task-notification"&&typeof w.prompt=="string"&&w.prompt.trim()&&o.push({role:"user",text:w.prompt.trim(),ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let l=c.message;if(!l)continue;let u=l.role||c.type;if(u!=="user"&&u!=="assistant"||u==="assistant"&&l.model==="<synthetic>"&&!c.isApiErrorMessage)continue;if(u==="assistant"&&Array.isArray(l.content))for(let w of l.content){let y=w?.type==="tool_use"?w.input?.file_path||w.input?.path:null;typeof y=="string"&&
|
|
62
|
-
|
|
63
|
-
`):""}var
|
|
64
|
-
`);if(!
|
|
65
|
-
`);for(let r=n.length-1;r>=0;r--)if(
|
|
66
|
-
`);return t}function
|
|
67
|
-
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(
|
|
68
|
-
`).pop()||"";return
|
|
69
|
-
`),o=r.findIndex(i=>
|
|
70
|
-
`).map(a=>a.trim()),o=r.findIndex(a=>/Press any key to log in/.test(a)),s=r.slice(0,o<0?r.length:o).filter(a=>a&&!/^Cursor Agent$/i.test(a)&&!/^v?\d{4}\.\d{2}\.\d{2}/.test(a)).join(" ");return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,answerMode:"keys",promptId:"cursor:signin-start",questions:[{question:(s?`${s} `:"")+"Cursor isn't signed in on this box \u2014 start the sign-in to get a login link you can open from any device.",header:"Sign in",multiSelect:!1,options:[{label:"Start sign-in",description:"Shows a login link (and a QR option) \u2014 your login goes to cursor.com, never through bostrat"}]}]}}function
|
|
71
|
-
`),o="",s=r.findIndex(a=>/https:\/\//.test(a));if(s>=0){o=r[s].slice(r[s].indexOf("https://")).trim();for(let a=s+1;a<r.length;a++){let i=r[a].trim();if(!i||!/^[A-Za-z0-9~%&=?/_.-]+$/.test(i))break;o+=i}}return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,promptId:`cursor:signin-link:${(o.match(/[?&]challenge=([\w-]+)/)||[])[1]?.slice(0,16)||o.slice(-24)||"pending"}`,questions:[{question:o?`Cursor is waiting for you to finish signing in: open ${o} on any device and log in. Your login goes straight to cursor.com \u2014 bostrat never sees it. This card clears once sign-in completes.`:"Cursor is waiting for you to finish signing in \u2014 open the login link shown in the Terminal tab. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function
|
|
72
|
-
`))
|
|
73
|
-
`).slice(-
|
|
74
|
-
`);return eI.test(n)}function Gs(e,t){return Hu(e,t)==="busy"}function qi(e,t){return Hu(e,t)!=="idle"}function Ki(e,t){let n=Le[e];return n?.up?n.up.test(t):!0}function nI(e,t){let n=Le[e];return n?Wu(e,t)?!1:n.up.test(t)&&Hu(e,t)==="idle":!0}function Wu(e,t){let n=Le[e];return n?n.auth?.test(t)&&!n.up.test(t)?!0:n.block?.test(t)?e==="codex"?!0:!n.up.test(t):!1:!1}async function zw(e,t,n,{bailOnDialog:r=!1}={}){let o=Date.now()+n,s=0;for(await kr(e).catch(()=>{});;){let a=await Ae(e);if(nI(t,a)){await Bn(Ks);return}if(r&&t==="claude"&&V_.test(a)&&!Le.claude.up.test(a)){z({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"resume-confirm"}).catch(()=>{});return}if(t==="cursor"&&G_.test(a)&&!Le.cursor.up.test(a)&&s<3){if(!Vw(e,"cursor-trust")){await Bn(Fu);continue}s+=1,await z({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:s}).catch(()=>{}),await je(e,"a",{enter:!1,kind:"cursor-trust-accept"}),await Bn(Ks);continue}if(t==="codex"&&s<3){let i=J_(a);if(i){if(!Vw(e,`codex-update:${i.from}->${i.to}`)){await Bn(Fu);continue}s+=1,await z({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:s,update:`${i.from} -> ${i.to}`}).catch(()=>{}),await je(e,"3",{enter:!1,kind:"codex-update-skip"}),Fw({sessionName:e,from:i.from,to:i.to}),await Bn(Ks);continue}}if(Date.now()>o){if(Wu(t,a)){let i=`${t} is blocked on an interactive prompt`;throw t==="codex"?i=Le.codex.auth.test(a)?"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"&&Le.cursor.auth.test(a)?i="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"&&Le.claude.auth.test(a)?i="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"&&Le.claude.block.test(a)&&(i='claude is waiting on its "Press Enter to continue" screen \u2014 answer the Continue card in chat (or press Enter in the Terminal tab), then send again'),z({flow:"agent-ready",phase:"blocked",sessionName:e,agent:t,timeoutMs:n,gateAccepts:s,error:i}).catch(()=>{}),new Error(i)}if(await fo(e)){let i=`${t} 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.`;throw z({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:s,error:i}).catch(()=>{}),new Error(i)}console.warn(`[agent-ready] ${t} readiness unconfirmed in ${e} after ${n}ms \u2014 driving the box anyway.`),z({flow:"agent-ready",phase:"timeout",sessionName:e,agent:t,timeoutMs:n,gateAccepts:s,paneBlocked:!!Le[t]?.block?.test(a),paneUp:!!Le[t]?.up?.test(a),paneBusy:!!Le[t]?.busy?.test(a)}).catch(()=>{}),await Bn(Ks);return}await Bn(Fu)}}function Yw({agent:e,agentCmd:t}={}){let n=t?Cn(t):Cn(e||"");return{label:n,realClaude:t?br(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 Vs({cwd:e,sessionName:t,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Yw({agent:n,agentCmd:r});return!t||!o&&!s?null:Dn({cwd:e,sessionName:t,agent:n,agentCmd:r}).catch(()=>{})}async function Dn({cwd:e,sessionId:t,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=K_(),bailOnDialog:a=!1}={}){let{label:i,realClaude:c,realCodex:l,realCursor:u}=Yw({agent:r,agentCmd:o});if(c&&e&&await En(e),l&&e&&await to(e),u&&e&&await no(e),(c||l||u)&&n){await zw(n,i,s,{bailOnDialog:a});return}let f=Le[r]?r:Le[i]?i:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&f){await zw(n,f,s);return}await Bn(Ks)}var K_,Fu,Ks,Le,G_,V_,z_,Y_,Wi,X_,Z_,eI,tI,Gi=O(()=>{ze();Es();wi();gi();ju();Hi();Yt();K_=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Fu=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,Ks=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,Le={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,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:/(^|\n)\s*›/,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/}},G_=/Trust this workspace|\[a\]\s*Trust/,V_=/Resume from summary/i,z_=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;Y_=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,Wi=new Map;X_=68,Z_=/^\s*[─━]{10,}\s*$/;eI=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,tI=15});function Mt(e){return e==="codex"?{enter:!0,submitDelayMs:rI}:e==="claude"?{enter:!0,submitDelayMs:oI}:{enter:!0}}var rI,oI,zs=O(()=>{rI=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),oI=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})()});function Ar(e){let t=String(e||"").toLowerCase();if(!t.trim())return null;let n=null;for(let r of sI){let o=0;for(let s of aI[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function Xw(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))iI.has(r)&&(t+=o||0),cI.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function Js(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," ")}var sI,aI,iI,cI,qu=O(()=>{sI=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],aI={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 out\b/,/\btalk through\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/],"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/],design:[/\bdesign/,/\bui\b/,/\bux\b/,/\bstyl(e|es|ing)\b/,/\bcss\b/,/\blayout\b/,/\bmock-?up/,/\brender(ing|s)?\b/,/\bpolish/,/\btheme\b/,/\bfonts?\b/,/\bresponsive\b/,/\banimation/,/\bvisual/,/\bpopover\b/],orchestration:[/\bhand[ -]?(off|over)/,/\bhand (it|the lead|this) (over|off)\b/,/\bdelegat/,/\bsubtask/,/\bmerge[ -]?back\b/,/\borchestrat/,/\b(switch|change) (the )?model\b/,/\bmodel switch/],"docs-writing":[/\bdocs?\b/,/\bdocument/,/\bmemo\b/,/\breadme\b/,/\bwrite-?up\b/,/\bchangelog\b/,/\bclaude\.md\b/,/\bblog/,/\bproofread/,/\bcopy(edit|writ)/],"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/]};iI=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),cI=new Set(["Edit","Write","MultiEdit","NotebookEdit"])});function tg(e){let t=Js(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||lI.has(r)||n.push(r);return n}function uI(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(eg(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(eg(n,"assistant",o))}}return t}function eg(e,t,n){let r=Ar(n);return{id:e.id,seq:e.seq,ts:e.payload?.ts??e.ts,role:t,boxId:e.boxId??null,provider:e.provider??null,text:n,tags:r?[r.category]:[]}}function Ys(e){let t=uI(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=tg(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,f]of l){let d=n.get(u);d||n.set(u,d=new Map),d.set(i,f)}});let s=t.length;return{chunks:t,postings:n,lengths:r,N:s,avgdl:s?o/s:0}}function Vi(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(tg(t))];if(!i.length)return[];let c=new Map;for(let u of i){let f=e.postings.get(u);if(!f)continue;let d=f.size,m=Math.log(1+(e.N-d+.5)/(d+.5));for(let[h,w]of f){let y=e.lengths[h],b=w+Zw*(1-Qw+Qw*(y/(e.avgdl||1))),x=m*(w*(Zw+1)/b);c.set(h,(c.get(h)||0)+x)}}let l=[];for(let[u,f]of c){let d=e.chunks[u];o&&d.role!==o||s&&!d.tags.includes(s)||a&&d.boxId!==a||l.push({chunk:d,score:f})}return l.sort((u,f)=>f.score-u.score||f.chunk.seq-u.chunk.seq),l.slice(0,r)}var Zw,Qw,lI,Ku=O(()=>{qu();Zw=1.5,Qw=.75,lI=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 zi from"node:fs/promises";import ng from"node:path";async function dI(e){let t=ng.join(e,".bostrat","events"),n;try{n=await zi.readdir(t)}catch{return Ys([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await zi.readFile(ng.join(t,o),"utf8");for(let a of s.split(`
|
|
75
|
-
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return
|
|
76
|
-
${o}`.trim()||t.slice(0,2e3)}function
|
|
77
|
-
`):""}async function
|
|
45
|
+
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${wo}: ${t?.message||t}`)}}async function z(e={}){let t={at:new Date().toISOString(),ts:Date.now(),...e},n=[t.flow||"handoff",t.phase||t.step||"?",t.trackId?`track=${t.trackId}`:"",t.sessionName?`box=${t.sessionName}`:"",t.error?`ERR=${t.error}`:""].filter(Boolean).join(" \xB7 ");if(console.log(`[handoff-debug] ${n}`),t.sessionName&&Li())try{t.paneTail=Ar((await Ae(t.sessionName,{lines:35})).slice(-4e3))}catch{t.paneTail=null}(Li()||t.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed|launch)/.test(String(t.phase||t.step||"")))&&await LE(t)}async function yw({limit:e=80}={}){try{return(await Ws.readFile(wo,"utf8")).split(`
|
|
46
|
+
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Ar(o.paneTail)),o}catch{return{raw:Ar(r)}}})}catch{return[]}}var jE,Iu,wo,ww,bw,Zt=O(()=>{Je();jE=[/[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];Iu=process.env.BOSTRAT_DEBUG_DIR||gw.join(DE.homedir(),".bostrat","debug"),wo=gw.join(Iu,"handoff.jsonl"),ww=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;bw={DEBUG_DIR:Iu,LOG_FILE:wo}});import{execFile as FE}from"node:child_process";import{promisify as UE}from"node:util";import Ui from"node:fs";import{randomUUID as HE}from"node:crypto";function bo(e){return new Promise(t=>setTimeout(t,e))}function Fi(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 xw(e){return`/tmp/tmux-${e}/default`}function WE({explicit:e,legacyExists:t,runtimeDir:n,uid:r}){if(e)return e;let o=xw(r);return t?o:n?`${n}/bostrat/tmux-default`:o}function Ou(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${KE}=1`]}function Mu(e,t,n){let r=Er(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&&Ee(n)&&(s+=` --name ${n}`),s}function Er(e){return/(^|\/|\s)claude(\s|$)/.test(String(e||""))}function Pn(e){return/(^|\/|\s)claude(\s|$)/.test(e)?"claude":/cursor/.test(e)?"cursor":/codex/.test(e)?"codex":/gemini/.test(e)?"gemini":"agent"}function VE(e){let t=String(e||"").trim();return!t||/fake-|\.sh(\s|$)/.test(t)?!1:Er(t)||/(^|\/|\s)codex(\s|$)/.test(t)||/cursor-agent/.test(t)||/(^|\/|\s)gemini(\s|$)/.test(t)}async function Sw(e,{loginShell:t=!1,failOpen:n=!1}={}){let r=await vw(e,{loginShell:t});return r===Hi?!0:r===go?!1:n}async function vw(e,{loginShell:t=!1,timeoutMs:n=Ru}={}){if(!e)return go;let r=t?process.env.SHELL||"/bin/sh":"sh",o=t?"-lic":"-c";try{return await ge(r,[o,'command -v "$1" >/dev/null 2>&1',"_",e],{timeout:n}),Hi}catch(s){return s?.killed||s?.signal?yo:s?.code===1||s?.code===127?go:yo}}async function Wi(e,{retries:t=zE,sleep:n=JE,probe:r=vw}={}){let o=String(e||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:Ru});for(let a=0;s===yo&&a<t.length;a++)await n(t[a]),s=await r(o,{timeoutMs:Ru*2});return s}function U(...e){return["-S",at,...e]}async function Bn(){if(!Ui.existsSync(at))return!1;try{return await ge(L,U("list-sessions")),!0}catch(e){let t=e&&e.stderr?String(e.stderr):"";return!/no server running/i.test(t)}}async function qs(){if(await Bn())return!0;try{Ui.mkdirSync(at.replace(/\/[^/]+$/,""),{recursive:!0}),await ge(L,U("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await Bn()}function Ee(e){return typeof e=="string"&&XE.test(e)}async function St(){if(!Ui.existsSync(at))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}",{stdout:t}=await ge(L,U("list-sessions","-F",e)),n=Date.now();return t.trim().split(`
|
|
47
|
+
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,f,d,p]=r.split("|"),m=Number(i);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(m)?n-m*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:f||null,agent:d||(l==="1"?"claude":null),alternateOn:p==="1"}}).filter(r=>!GE.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function se(e){return(await St()).find(n=>n.name===e)||null}function QE(e){if(typeof e!="string")return!1;let t=e.trim();return t.length>=1&&t.length<=ZE&&YE.test(t)}function qi(e){return e.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function Tw(e){try{let{stdout:t}=await ge(L,U("list-sessions","-F","#{session_name}"));return t.split(`
|
|
48
|
+
`).map(n=>n.trim()).includes(e)}catch{return!1}}function r_(e){let t=String(e||"").trim().split(/\s+/)[0]||"";if(t.includes("/"))return null;let n=[];for(let o=1;o<=t.length-3;o++)n.push(t.slice(o));if(!n.length)return null;let r=n.map(o=>o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return new RegExp(`\\b(?:${r.join("|")})`)}async function o_(e,t){let n=Date.now()+e_(),r=r_(t),o=!1,s=0;for(;;){if(await bo(t_),s+=1,!await Tw(e))return;let a=await Ae(e,{lines:15}),i=!o&&n_.test(a)&&!!r?.test(a);if(!i&&s>1&&!await ko(e))return;if(i){o=!0,await z({flow:"spawn",phase:"launch-retry",sessionName:e,agent:Pn(t),promptHead:t.slice(0,120)}).catch(()=>{}),await ge(L,U("send-keys","-t",e,"C-u")),await ge(L,U("send-keys","-t",e,"-l",t)),await ge(L,U("send-keys","-t",e,"Enter"));continue}if(Date.now()>n){await z({flow:"spawn",phase:"launch-failed",sessionName:e,agent:Pn(t),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function Dn(e,t=qE,n={}){if(!QE(e))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=e.trim(),o=qi(r);if(!Ee(o))throw new Error("resulting tmux session name is invalid");if(!await qs())throw new Error(`host tmux server not running on ${at} and auto-start failed \u2014 start one: ${L} -S ${at} new-session -d -s keeper "sleep 2147483647"`);if(await Tw(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||$u,a=n.agent||Pn(s),i=!!n.resumeSessionId,c=n.resumeSessionId||HE(),l=i?s:Mu(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),f=a==="claude"&&Er(s),d=n.firstRun===!0;d||(f&&await Rn(t),a==="codex"&&await lo(t),a==="cursor"&&await uo(t),await tw(t),await Di(t));let p=f&&!d?vi():null,m=Rh(),w=["-e",`${xi}=${m}`];p&&w.push("-e",`ANTHROPIC_API_KEY=${p}`),f&&w.push(...Ou());try{await ge(L,U("new-session","-d","-s",o,"-c",t,...w))}catch(y){throw Fi(p?Fi(y,p):y,m)}return await _r(o),await ge(L,U("set-option","-t",o,"@bostrat-session","1")),await ge(L,U("set-option","-t",o,"@bostrat-title",r)),await ge(L,U("set-option","-t",o,"@bostrat-sid",c)),await ge(L,U("set-option","-t",o,"@bostrat-agent",a)),await ge(L,U("send-keys","-t",o,l,"Enter")),VE(l)&&o_(o,l).catch(()=>{}),{name:o,title:r,cwd:t,sessionId:c,agent:a,pinned:u,hookToken:m}}async function Nt(e){if(!Ee(e))throw new Error("invalid session name");await ge(L,U("kill-session","-t",e))}async function Aw(e,t){if(Ee(e))try{let n=t?U("set-option","-t",e,"@bostrat-sid",t):U("set-option","-u","-t",e,"@bostrat-sid");await ge(L,n)}catch{}}async function Ae(e,{lines:t=40}={}){if(!Ee(e))return"";try{let n=U("capture-pane","-t",e,"-p");Number.isFinite(t)&&t>0&&n.push("-S",`-${Math.floor(t)}`);let{stdout:r}=await ge(L,n);return String(r||"")}catch{return""}}async function Ew(e){if(!Ee(e))return 0;try{let{stdout:t}=await ge(L,U("list-clients","-t",e,"-F","1"));return String(t||"").trim().split(`
|
|
49
|
+
`).filter(Boolean).length}catch{return 0}}async function _r(e,{cols:t=i_,rows:n=c_}={}){if(!Ee(e))return!0;if(await Ew(e))return!1;try{let r=await _w(e);if(r.cols===t&&r.rows===n)return!0;await ge(L,U("resize-window","-t",e,"-x",String(t),"-y",String(n))),await ge(L,U("set-window-option","-t",e,"window-size","latest")),await bo(80)}catch{}return!0}async function l_(e){if(!Ee(e))return 0;try{let{stdout:t}=await ge(L,U("display-message","-t",e,"-p","#{pane_pid}"));return Number(String(t||"").trim())||0}catch{return 0}}async function ko(e){let t=await l_(e);if(!t)return!1;try{return await ge("pgrep",["-P",String(t)]),!1}catch(n){return n?.code===1}}async function _w(e){if(!Ee(e))return{cols:0,rows:0};try{let{stdout:t}=await ge(L,U("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 Ki(e,{cols:t=s_,rows:n=a_}={}){if(Ee(e)&&!await Ew(e))try{let r=await _w(e),o=Math.max(r.cols,t),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await ge(L,U("resize-window","-t",e,"-x",String(o),"-y",String(s))),await ge(L,U("set-window-option","-t",e,"window-size","latest")),await bo(80))}catch{}}async function Qt(e){return await Ki(e),Ae(e,{lines:De})}async function Oe(e,t,{settleMs:n=40,kind:r}={}){if(!Ee(e))throw new Error("invalid session name");let o=(Array.isArray(t)?t:[t]).filter(s=>u_.has(s));for(let s=0;s<o.length;s++)await ge(L,U("send-keys","-t",e,o[s])),n>0&&s<o.length-1&&await bo(n);return z({flow:"tmux",phase:"send-keys",sessionName:e,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function xo(e,t,{kind:n}={}){if(!Ee(e))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(t)||0));return r>0&&await ge(L,U("send-keys","-t",e,"-N",String(r),"BSpace")),z({flow:"tmux",phase:"send-keys",sessionName:e,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function Vi(e,t,{perCharDelayMs:n=120,kind:r}={}){if(!Ee(e))throw new Error("invalid session name");let o=[...String(t)];for(let s of o)await ge(L,U("send-keys","-t",e,"-l",s)),n>0&&await bo(n);z({flow:"tmux",phase:"type",sessionName:e,kind:r||"type",textLen:o.length,textHead:String(t).slice(0,120)}).catch(()=>{})}async function je(e,t,{enter:n=!0,submitDelayMs:r=0,kind:o}={}){if(!Ee(e))throw new Error("invalid session name");if(typeof t=="string"&&t.length>0&&await ge(L,U("send-keys","-t",e,"-l",t)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await bo(s),await ge(L,U("send-keys","-t",e,"Enter"))}z({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}).catch(()=>{})}var ge,L,kw,at,qE,$u,KE,Cu,GE,Hi,go,yo,Ru,zE,JE,Nu,XE,YE,ZE,e_,t_,n_,s_,a_,De,i_,c_,u_,Gi,Je=O(()=>{Ms();Ai();Ei();Mi();ji();Zt();Cs();ge=UE(FE);L=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";kw=typeof process.getuid=="function"?process.getuid():0,at=WE({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return Ui.existsSync(xw(kw))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:kw}),qE=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",$u=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",KE="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";Cu="bostrat-usage-probe",GE=new Set([Cu]);Hi="present",go="absent",yo="unknown",Ru=5e3,zE=[250,1e3,3e3],JE=e=>new Promise(t=>setTimeout(t,e));Nu={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"};XE=/^[A-Za-z0-9_.-]{1,64}$/;YE=/^[A-Za-z0-9 ._↻-]+$/,ZE=64;e_=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,t_=1e3,n_=/not found|Unknown command/i;s_=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,a_=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,De=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,i_=Number(process.env.BOSTRAT_BOX_COLS)||120,c_=Number(process.env.BOSTRAT_BOX_ROWS)||40;u_=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);Gi=1600});import Ks from"node:fs";import w_ from"node:fs/promises";import So from"node:path";import g_ from"node:os";function Du(e){return e.replace(/[^A-Za-z0-9]/g,"-")}function ju(e){let t=So.join(Bu,Du(e)),n;try{n=Ks.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=So.join(t,o);try{let{mtimeMs:a}=Ks.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function en(e,t){if(!t)return null;let n=So.join(Bu,Du(e),`${t}.jsonl`);return Ks.existsSync(n)?n:null}function Nw(e){if(!e)return[];let t=So.join(Bu,Du(e)),n;try{n=Ks.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=Ks.statSync(So.join(t,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function tt(e,t=131072){let n=await w_.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 b_(e){let t=e.split(`
|
|
50
|
+
`);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 Gs(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=en(e,t),o=!0,!r)return null}else if(n){if(r=ju(e),o=!1,!r)return null}else return null;let s;try{s=b_(await tt(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(Mw,s.model),i=Mw[s.model]||y_;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 Mw,y_,Bu,vt=O(()=>{Mw={"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},y_=2e5,Bu=process.env.BOSTRAT_CLAUDE_PROJECTS||So.join(g_.homedir(),".claude","projects")});function k_(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===`
|
|
51
|
+
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function Vs(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 zs(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 ke(e,t=new Set){let n=[];for(let r=1;r<e.length;r++){let o=e[r];if(o==="<<")break;if(Dw(o)){t.has(o)&&r++;continue}n.push(o)}return n}function jw(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 v_(e){let t=jw(e),n=He(ke(e).find(r=>!/^\d+(,\d+)?p$/.test(r)));return t&&n?t.from===t.to?`Read line ${t.from} of ${_e(n)}`:`Read lines ${t.from}\u2013${t.to} of ${_e(n)}`:e.includes("-i")&&n?`Edit ${_e(n)}`:null}function Lu(e){if(e.includes("--files")){let s=He(ke(e,Pw)[0]);return s&&s!=="."?`List files in ${vo(s)}`:"List files"}let t=ke(e,Pw),n=t[0];if(!n)return null;let r=He(t.slice(1).find(s=>s!==".")),o=To(n,40);return r?`Search for \u201C${o}\u201D in ${vo(r)}`:`Search for \u201C${o}\u201D`}function Fu(e){let t=ke(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 Uu(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=ke(e)[0];return n?`Run ${_e(n)}`:null}function T_(e){let t=ke(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 Bw(e){return e.find(t=>/^https?:\/\//.test(t))||null}function __(e){let t=zs(Vs(e));if(!t.length)return null;if(/^(ba|z)?sh$/.test(_e(t[0]))&&(t[1]==="-lc"||t[1]==="-c")&&t[2])return Hu(t[2]);let n=_e(t[0]);if(E_.has(n))return null;if(t.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=A_[n];return r?r(t.map((o,s)=>s===0?n:o)):null}function Hu(e){let t=String(e||"").trim();if(!t)return null;let n=t.indexOf("<<");if(n!==-1){let i=t.indexOf(`
|
|
52
|
+
`,n);i!==-1&&(t=t.slice(0,i))}let r=k_(t);for(let i=0;i<r.length-1;i++){let c=zs(Vs(r[i])),l=zs(Vs(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(_e(c[0]))&&_e(l[0])==="sed"){let u=jw(l),f=ke(c)[0];if(u&&f)return To(u.from===u.to?`Read line ${u.from} of ${_e(f)}`:`Read lines ${u.from}\u2013${u.to} of ${_e(f)}`,88)}}let o=[];for(let i of r){let c=__(i);c&&o.push({d:c,head:_e(zs(Vs(i))[0]||""),sub:ke(zs(Vs(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":To(s?s.d:o[0].d,88)}var To,He,_e,vo,Dw,Pw,x_,S_,A_,E_,Lw=O(()=>{To=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,He=e=>e&&!/[$`]/.test(e)?e:null,_e=e=>e.replace(/\/+$/,"").split("/").pop()||e,vo=e=>{let t=e.replace(/\/+$/,"").split("/").filter(Boolean);return t.length>3?t.slice(-2).join("/"):e},Dw=e=>e.startsWith("-"),Pw=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);x_={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 ${_e(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"},S_={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"};A_={git:e=>{let t=ke(e,new Set(["-C","-c"])),n=x_[t[0]];return n?n(t,e):null},gh:e=>{let t=ke(e),n=S_[t[0]];return n?n(t):null},sed:v_,rg:Lu,grep:Lu,egrep:Lu,nl:e=>{let t=He(ke(e)[0]);return t?`Read ${_e(t)}`:null},cat:e=>{let t=He(ke(e)[0]);return t?`Read ${_e(t)}`:null},head:e=>{let t=He(ke(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the start of ${_e(t)}`:null},tail:e=>{let t=He(ke(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the end of ${_e(t)}`:null},wc:e=>{let t=He(ke(e)[0]);return t?`Count lines in ${_e(t)}`:"Count input lines"},ls:e=>{let t=He(ke(e)[0]);return t&&t!=="."?`List files in ${vo(t)}`:"List files"},find:e=>He(e[1])&&!Dw(e[1])&&e[1]!=="."?`Find files in ${vo(e[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:e=>{let t=He(ke(e)[0]);return t?`Create directory ${vo(t)}`:null},touch:e=>{let t=He(ke(e)[0]);return t?`Create ${_e(t)}`:null},rm:e=>{let t=He(ke(e)[0]);return t?`Delete ${vo(t)}`:null},mv:e=>{let t=He(ke(e)[0]);return t?`Move ${_e(t)}`:null},cp:e=>{let t=He(ke(e)[0]);return t?`Copy ${_e(t)}`:null},chmod:e=>{let t=He(ke(e)[1]);return t?`Change permissions on ${_e(t)}`:null},diff:e=>{let t=ke(e);return t.length>=2?`Compare ${_e(t[0])} and ${_e(t[1])}`:null},curl:e=>{let t=Bw(e);return t?`Fetch ${To(t,56)}`:"Fetch a URL"},wget:e=>{let t=Bw(e);return t?`Fetch ${To(t,56)}`:"Fetch a URL"},npm:Fu,pnpm:Fu,yarn:Fu,npx:e=>{let t=ke(e)[0];return t?`Run ${t}`:null},node:e=>Uu(e,"Node"),python:e=>Uu(e,"Python"),python3:e=>Uu(e,"Python"),make:e=>{let t=ke(e)[0];return t?`Run make ${t}`:"Run make"},systemctl:T_,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=ke(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=ke(e)[0];return t?`Locate ${t}`:null},command:e=>e[1]==="-v"&&e[2]?`Locate ${e[2]}`:null,fly:e=>{let t=ke(e)[0];return t==="deploy"?"Deploy to Fly":t?`Run fly ${t}`:null}},E_=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"])});function Ao(e){let t=e.split(`
|
|
53
|
+
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function jn(e,t=""){let n=t.trim();return n||Hu(e)||Ao(e)}function Wu(e,t){if(e){if(Array.isArray(e)){for(let n of e)Wu(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&qu.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))Wu(n,t)}}}async function Fw(e,{maxBytes:t=1048576,limit:n=400}={}){let r;try{r=await tt(e,t)}catch{return[]}let o=[];for(let s of r.split(`
|
|
54
|
+
`)){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(Wu(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let f of l)o.push({...f,summary:jn(f.command,f.description),ts:u})}return o.slice(-n)}function Uw(e,t){let n=t.filter(l=>l.role==="assistant"&&(l.text||"").trim()).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((m,w)=>w>=s&&(m.ts||0)>(l.ts||0)),f=u===-1?r.length:u,d=r.slice(s,f);if(s=f,!d.length)continue;let p=d.map(m=>({...m,n:i(m)}));o.push({id:`batch-${l.ts}-${l.box}`,anchorTs:l.ts,anchorBox:l.box,agent:l.agent,boxRole:l.boxRole,commands:p,ts:Math.max(...p.map(m=>m.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 qu,Eo=O(()=>{vt();Lw();qu=new Set(["Bash"])});import Hw from"node:fs";import Gu from"node:fs/promises";import Ku from"node:path";import I_ from"node:os";async function Ww(e){try{return await Gu.realpath(e)}catch{return e}}function $_(e=R_){let t=[],n=r=>{let o;try{o=Hw.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=Ku.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:Hw.statSync(a).mtimeMs})}catch{}}}};return n(e),t.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function O_(e,t=1048576){let n;try{n=await Gu.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(`
|
|
55
|
+
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=t)break}return o}finally{await n.close()}}async function C_(e){let t=await O_(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 Ir(e,t){if(!e&&!t)return null;let n=$_();if(t){let o=n.find(s=>s.id===t);return o||null}let r=await Ww(e);for(let o of n){let s=await C_(o.file);if(s&&(s===e||await Ww(s)===r))return o}return null}async function qw(e,t,{limit:n=80,maxBytes:r=524288}={}){let o=await Ir(e,t);if(!o)return[];let s;try{s=await tt(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,f=null;for(let d of s.split(`
|
|
56
|
+
`)){let p=d.trim();if(!p||p[0]!=="{")continue;let m;try{m=JSON.parse(p)}catch{continue}if((m.type==="turn_context"&&m.payload?.model||m.type==="session_meta"&&m.payload?.model)&&(i=m.payload.model),m.type==="event_msg"){let x=m.payload?.type;x==="task_started"?(l=!0,u=!1,f=m.payload?.turn_id||null):x==="task_complete"&&l&&(l=!1,!u&&m.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:Rr,sourceId:m.payload?.turn_id||f||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i}));continue}if(m.type!=="response_item")continue;let w=m.payload;if(!w)continue;if((w.type==="function_call"||w.type==="custom_tool_call")&&w.name){u=!0,Vu.has(w.name)||c.push(w.name);continue}if(w.type!=="message"||w.role!=="assistant")continue;u=!0;let y=N_(w.content),b=c.length?c.slice():null;c=[],!(!y&&!b)&&a.push({role:"assistant",text:y,tools:b||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i})}if(i)for(let d of a)d.model||(d.model=i);return a.slice(-n)}function P_(e){if(typeof e?.cmd=="string"&&e.cmd.trim())return e.cmd.trim();let t=e?.command;return typeof t=="string"&&t.trim()?t.trim():Array.isArray(t)&&t.length?t.length===3&&/^(ba)?sh$/.test(t[0])&&t[1]==="-lc"?String(t[2]).trim():t.map(String).join(" ").trim():null}function B_(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+=`
|
|
57
|
+
`;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 D_(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=B_(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function Kw(e,t,{limit:n=400,maxBytes:r=524288}={}){let o=await Ir(e,t);if(!o)return[];let s;try{s=await tt(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
|
|
58
|
+
`)){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 f=[];if(u.type==="function_call"&&Js.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let m=P_(p);m&&(f=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(f=D_(u.input));if(!f.length)continue;let d=u.call_id||u.id||null;f.forEach((p,m)=>{a.push({id:d?m?`${d}:${m}`:d:null,tool:u.name,command:p,description:"",summary:jn(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function Gw(e,t){let n=await Ir(e,t);if(!n)return null;try{return Math.round((await Gu.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var R_,M_,N_,Js,Vu,Rr,$r=O(()=>{vt();Eo();R_=process.env.BOSTRAT_CODEX_SESSIONS||Ku.join(process.env.CODEX_HOME||Ku.join(I_.homedir(),".codex"),"sessions");M_=new Set(["output_text","text"]),N_=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&typeof t.text=="string"&&M_.has(t.type)).map(t=>t.text).join(`
|
|
59
|
+
`).trim():"";Js=new Set(["exec_command","shell","exec"]),Vu=new Set(["wait","write_stdin"]),Rr="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 Xs({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&Vw.has(r))return Vw.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 zw(e){if(!e||e.role!=="assistant")return null;if(e.apiError){let t=e.apiError;return{cls:t.cls||Xs({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===Rr?"other":Xs({text:t}),code:null,status:null,requestId:null}}return null}function Jw(e,{codexFailedText:t=null}={}){let n=String(e?.text||"").trim();return n?e?.model==="<synthetic>"?/^No response requested\.$/.test(n)?null:{cls:Xs({text:n}),code:null,status:j_(n),requestId:null,text:n}:t&&n===t?{cls:"other",code:null,status:null,requestId:null,text:n}:null:null}function j_(e){let t=/^API Error:\s*(\d{3})\b/.exec(e);return t?Number(t[1]):null}var KU,Vw,Ji=O(()=>{$r();KU=Object.freeze(["entitlement","auth","rate_limit","overloaded","server","other"]),Vw=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 L_(e,t){return!e||!t?null:en(e,t)}function F_(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(`
|
|
60
|
+
`).trim():""}function U_(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 H_(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&t.type==="tool_result")}function zu(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 Xw(e,t){if(!e.uuid)return null;let n=zu(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 Ju(e,{maxBytes:t=524288,limit:n=80}={}){let r;try{r=await tt(e,t)}catch{return[]}let o=[],s=new Map;for(let a of r.split(`
|
|
61
|
+
`)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let w=c.attachment;w?.type==="queued_command"&&w.commandMode==="task-notification"&&typeof w.prompt=="string"&&w.prompt.trim()&&o.push({role:"user",text:w.prompt.trim(),ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let l=c.message;if(!l)continue;let u=l.role||c.type;if(u!=="user"&&u!=="assistant"||u==="assistant"&&l.model==="<synthetic>"&&!c.isApiErrorMessage)continue;if(u==="assistant"&&Array.isArray(l.content))for(let w of l.content){let y=w?.type==="tool_use"?w.input?.file_path||w.input?.path:null;typeof y=="string"&&W_.test(y)&&s.set(w.id,y.split("/").pop())}if(H_(l.content)){let w=Xw(c,s);w&&o.push({role:"assistant",text:"",images:w,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let f=F_(l.content),d=u==="assistant"?U_(l.content):null,p=u==="user"?Xw(c,s):null;if(!f&&!d&&!p)continue;let m=u==="assistant"&&c.isApiErrorMessage?{code:typeof c.error=="string"?c.error:null,status:Number(c.apiErrorStatus)||null,requestId:c.requestId||null,cls:Xs({error:c.error,status:c.apiErrorStatus,text:f})}:null;o.push({role:u,text:f,tools:d||void 0,images:p||void 0,ts:c.timestamp?Date.parse(c.timestamp):null,model:l.model||null,apiError:m||void 0})}return o.slice(-n)}async function Yw(e,t,{turns:n=16}={}){let r=L_(e,t);if(!r)return"";let o=await Ju(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}:** ${q_(s.text||c,700)} ${i}`.trim()}).join(`
|
|
62
|
+
|
|
63
|
+
`):""}var W_,q_,Xi=O(()=>{Ji();vt();W_=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;q_=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e});import K_ from"node:fs/promises";import{execFile as G_}from"node:child_process";import{promisify as V_}from"node:util";function tg(e){Yu=typeof e=="function"?e:null}function Zu(e){let t=String(e||""),n=t.indexOf("/lib/node_modules/");return n>0?t.slice(0,n):null}function ng({sessionName:e,from:t,to:n}={}){let r=String(n||"").trim();!r||Zw.has(r)||(Zw.add(r),Qw=Qw.then(()=>J_({sessionName:e,from:t,to:r})).catch(()=>{}))}async function J_({sessionName:e,from:t,to:n}){await z({flow:"codex-update",phase:"start",sessionName:e,from:t,to:n}).catch(()=>{});try{let r=(await Xu("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await K_.realpath(r),s=Zu(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await Xu("npm",["install","-g","@openai/codex"],{env:a,timeout:z_});let i=(await Xu(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await z({flow:"codex-update",phase:"done",sessionName:e,from:t,to:n,version:i}).catch(()=>{}),await eg({sessionName:e,from:t,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await z({flow:"codex-update",phase:"failed",sessionName:e,from:t,to:n,error:o}).catch(()=>{}),await eg({sessionName:e,from:t,to:n,status:"failed",error:o})}}async function eg(e){if(Yu)try{await Yu(e)}catch{}}var Xu,z_,Zw,Qw,Yu,Yi=O(()=>{Zt();Xu=V_(G_),z_=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,Zw=new Set,Qw=Promise.resolve(),Yu=null});function Y_(e,{provider:t,box:n}){let r=String(e).replace(/\r/g,"").split(`
|
|
64
|
+
`);if(!rg.test(e))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(X_);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"&&rg.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 Or(e){let t=String(e??"").replace(/\r/g,""),n=t.split(`
|
|
65
|
+
`);for(let r=n.length-1;r>=0;r--)if(ag.test(n[r]))return n.slice(r).join(`
|
|
66
|
+
`);return t}function Z_(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 Q_(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 eI(e,t=""){let n=Or(e);if(Ys.test(n)||!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n))return null;let r=n.split(`
|
|
67
|
+
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(og);if(!u)continue;s<0&&(s=l);let f="";for(let d=l+1;d<Math.min(l+3,r.length);d++){let p=r[d].trim();if(p){if(og.test(r[d])||/^Press enter/.test(p))break;f=p;break}}o.push({n:Number(u[2]),cursor:!!u[1],label:u[3].trim(),desc:f})}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&&!ag.test(r[l])&&Z_(u)&&i.push(u)}let c=i.join(" ").slice(0,400)||"Codex isn't signed in on this box \u2014 choose a sign-in method";return{provider:"codex",box:t,multiSelect:!1,cursorIndex:Math.max(0,a.findIndex(l=>l.cursor)),answerMode:"keys",promptId:`codex:signin:${a.map(l=>l.label).join("|")}`.slice(0,200),questions:[{question:c,header:"Sign in",multiSelect:!1,options:a.map(l=>{let u=Q_(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function rI(e,t=""){let n=Or(e);if(Ys.test(n)||!tI.test(n))return null;let r=n.trimEnd().split(`
|
|
68
|
+
`).pop()||"";return nI.test(r)?{provider:"codex",box:t,multiSelect:!1,cursorIndex:0,answerMode:"keys",promptId:"codex:signin-complete",questions:[{question:"Codex successfully signed in and is waiting to open the session.",header:"Signed in",multiSelect:!1,options:[{label:"Continue",description:""}]}]}:null}function sI(e,t=""){let n=Or(e);if(Ys.test(n)||!sg.test(n))return null;let r=n.split(`
|
|
69
|
+
`),o=r.findIndex(i=>sg.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(oI);if(c){s=c[1];break}}if(!s)return null;let a=(n.match(/https:\/\/\S+/)||[])[0]||"https://auth.openai.com/codex/device";return{provider:"codex",box:t,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:`codex:device-code:${s}`,questions:[{question:`Codex is waiting for you to finish signing in: open ${a} and enter code ${s} (expires in 15 minutes). The code goes straight to OpenAI \u2014 bostrat never sees your login. This card clears once OpenAI confirms.`,header:"Sign in",multiSelect:!1,options:[{label:"Cancel sign-in",description:"Go back to the sign-in options"}]}]}}function aI(e,t=""){let n=Or(e);return Ys.test(n)||!/open the following link to authenticate/.test(n)?null:{provider:"codex",box:t,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:"codex:signin-browser",questions:[{question:"Codex started its browser sign-in, which opens on the box itself and can't complete from another device. Go back and choose \u201CSign in with Device Code\u201D instead.",header:"Sign in",multiSelect:!1,options:[{label:"Go back to sign-in options",description:""}]}]}}function iI(e,t=""){let n=Or(e);return Ys.test(n)||!/Paste or type your API key/.test(n)?null:{provider:"codex",box:t,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:"codex:signin-apikey",questions:[{question:"Codex is asking for an OpenAI API key. For safety a key can only be typed in the Terminal tab, never through chat. To use your ChatGPT subscription instead, go back and pick a sign-in option.",header:"Sign in",multiSelect:!1,options:[{label:"Go back to sign-in options",description:""}]}]}}function cI(e,t=""){let n=String(e);if(!/Press any key to log in/.test(n)||Qu.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
70
|
+
`).map(a=>a.trim()),o=r.findIndex(a=>/Press any key to log in/.test(a)),s=r.slice(0,o<0?r.length:o).filter(a=>a&&!/^Cursor Agent$/i.test(a)&&!/^v?\d{4}\.\d{2}\.\d{2}/.test(a)).join(" ");return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,answerMode:"keys",promptId:"cursor:signin-start",questions:[{question:(s?`${s} `:"")+"Cursor isn't signed in on this box \u2014 start the sign-in to get a login link you can open from any device.",header:"Sign in",multiSelect:!1,options:[{label:"Start sign-in",description:"Shows a login link (and a QR option) \u2014 your login goes to cursor.com, never through bostrat"}]}]}}function uI(e,t=""){let n=String(e);if(!lI.test(n)||Qu.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
71
|
+
`),o="",s=r.findIndex(a=>/https:\/\//.test(a));if(s>=0){o=r[s].slice(r[s].indexOf("https://")).trim();for(let a=s+1;a<r.length;a++){let i=r[a].trim();if(!i||!/^[A-Za-z0-9~%&=?/_.-]+$/.test(i))break;o+=i}}return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,promptId:`cursor:signin-link:${(o.match(/[?&]challenge=([\w-]+)/)||[])[1]?.slice(0,16)||o.slice(-24)||"pending"}`,questions:[{question:o?`Cursor is waiting for you to finish signing in: open ${o} on any device and log in. Your login goes straight to cursor.com \u2014 bostrat never sees it. This card clears once sign-in completes.`:"Cursor is waiting for you to finish signing in \u2014 open the login link shown in the Terminal tab. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function dI(e,t=""){let n=String(e);return!/Scan this QR code to log in/.test(n)||Qu.test(n)?null:{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,promptId:"cursor:signin-qr",questions:[{question:"Cursor is showing a QR sign-in code in the Terminal tab \u2014 scan it from another device to log in. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function ig(e,t,n=""){if(e!=="cursor"&&e!=="codex")return null;if(e==="codex"){let r=rI(t,n)||sI(t,n)||aI(t,n)||iI(t,n)||eI(t,n);if(r)return r}if(e==="cursor"){let r=uI(t,n)||dI(t,n)||cI(t,n);if(r)return r}return Y_(t,{provider:e,box:n})}function cg(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 X_,rg,Ys,ag,og,tI,nI,sg,oI,Qu,lI,Zi=O(()=>{X_=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,rg=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;Ys=/(^|\n)\s*›/,ag=/^\s*Welcome to Codex\b/;og=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;tI=/Successfully logged in\./,nI=/^\s*Press Enter to continue\.\s*$/;sg=/Enter this one-time code/,oI=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;Qu=/Run Everything|Add a follow-up|Plan, search, build anything/;lI=/click this link to log in|Signing in with the browser/});function wI(e){let t=String(e||"");if(!Le.codex.block.test(t))return null;let n=Or(t);if(!Le.codex.block.test(n))return null;let r=n.match(hI);return{from:r?.[1]||null,to:r?.[2]||null}}function Ln(e){return new Promise(t=>setTimeout(t,e))}function lg(e,t){let n=Date.now(),r=gI();for(let[s,a]of Qi)n-a>=r&&Qi.delete(s);let o=`${e}::${t}`;return Qi.has(o)?!1:(Qi.set(o,n),!0)}function kI(e){let t=0;for(let n of String(e).split(`
|
|
72
|
+
`))bI.test(n)&&(t=Math.max(t,n.trimEnd().length));return t}function td(e,t){let n=Le[e];if(!n?.busy)return{state:"idle",reason:null};if(rd(e,t))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(t))return{state:"idle",reason:null};if(n.busy.test(t))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(t))return{state:"busy",reason:null};let r=kI(t);return e==="claude"&&r>0&&r<yI?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function nd(e,t){return td(e,t).state}function dg(e,t){if(e!=="claude")return!1;let n=String(t||"").split(`
|
|
73
|
+
`).slice(-SI).join(`
|
|
74
|
+
`);return xI.test(n)}function Qs(e,t){return nd(e,t)==="busy"}function ec(e,t){return nd(e,t)!=="idle"}function tc(e,t){let n=Le[e];return n?.up?n.up.test(t):!0}function vI(e,t){let n=Le[e];return n?rd(e,t)?!1:n.up.test(t)&&nd(e,t)==="idle":!0}function rd(e,t){let n=Le[e];return n?n.auth?.test(t)&&!n.up.test(t)?!0:n.block?.test(t)?e==="codex"?!0:!n.up.test(t):!1:!1}async function ug(e,t,n,{bailOnDialog:r=!1}={}){let o=Date.now()+n,s=0;for(await _r(e).catch(()=>{});;){let a=await Ae(e);if(vI(t,a)){await Ln(Zs);return}if(r&&t==="claude"&&mI.test(a)&&!Le.claude.up.test(a)){z({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"resume-confirm"}).catch(()=>{});return}if(t==="cursor"&&pI.test(a)&&!Le.cursor.up.test(a)&&s<3){if(!lg(e,"cursor-trust")){await Ln(ed);continue}s+=1,await z({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:s}).catch(()=>{}),await je(e,"a",{enter:!1,kind:"cursor-trust-accept"}),await Ln(Zs);continue}if(t==="codex"&&s<3){let i=wI(a);if(i){if(!lg(e,`codex-update:${i.from}->${i.to}`)){await Ln(ed);continue}s+=1,await z({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:s,update:`${i.from} -> ${i.to}`}).catch(()=>{}),await je(e,"3",{enter:!1,kind:"codex-update-skip"}),ng({sessionName:e,from:i.from,to:i.to}),await Ln(Zs);continue}}if(Date.now()>o){if(rd(t,a)){let i=`${t} is blocked on an interactive prompt`;throw t==="codex"?i=Le.codex.auth.test(a)?"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"&&Le.cursor.auth.test(a)?i="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"&&Le.claude.auth.test(a)?i="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"&&Le.claude.block.test(a)&&(i='claude is waiting on its "Press Enter to continue" screen \u2014 answer the Continue card in chat (or press Enter in the Terminal tab), then send again'),z({flow:"agent-ready",phase:"blocked",sessionName:e,agent:t,timeoutMs:n,gateAccepts:s,error:i}).catch(()=>{}),new Error(i)}if(await ko(e)){let i=`${t} 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.`;throw z({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:s,error:i}).catch(()=>{}),new Error(i)}console.warn(`[agent-ready] ${t} readiness unconfirmed in ${e} after ${n}ms \u2014 driving the box anyway.`),z({flow:"agent-ready",phase:"timeout",sessionName:e,agent:t,timeoutMs:n,gateAccepts:s,paneBlocked:!!Le[t]?.block?.test(a),paneUp:!!Le[t]?.up?.test(a),paneBusy:!!Le[t]?.busy?.test(a)}).catch(()=>{}),await Ln(Zs);return}await Ln(ed)}}function fg({agent:e,agentCmd:t}={}){let n=t?Pn(t):Pn(e||"");return{label:n,realClaude:t?Er(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 ea({cwd:e,sessionName:t,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=fg({agent:n,agentCmd:r});return!t||!o&&!s?null:Fn({cwd:e,sessionName:t,agent:n,agentCmd:r}).catch(()=>{})}async function Fn({cwd:e,sessionId:t,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=fI(),bailOnDialog:a=!1}={}){let{label:i,realClaude:c,realCodex:l,realCursor:u}=fg({agent:r,agentCmd:o});if(c&&e&&await Rn(e),l&&e&&await lo(e),u&&e&&await uo(e),(c||l||u)&&n){await ug(n,i,s,{bailOnDialog:a});return}let f=Le[r]?r:Le[i]?i:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&f){await ug(n,f,s);return}await Ln(Zs)}var fI,ed,Zs,Le,pI,mI,hI,gI,Qi,yI,bI,xI,SI,nc=O(()=>{Je();Ms();Ai();Ei();Yi();Zi();Zt();fI=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,ed=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,Zs=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,Le={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,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:/(^|\n)\s*›/,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/}},pI=/Trust this workspace|\[a\]\s*Trust/,mI=/Resume from summary/i,hI=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;gI=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,Qi=new Map;yI=68,bI=/^\s*[─━]{10,}\s*$/;xI=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,SI=15});function Pt(e){return e==="codex"?{enter:!0,submitDelayMs:TI}:e==="claude"?{enter:!0,submitDelayMs:AI}:{enter:!0}}var TI,AI,ta=O(()=>{TI=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),AI=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})()});function Cr(e){let t=String(e||"").toLowerCase();if(!t.trim())return null;let n=null;for(let r of EI){let o=0;for(let s of _I[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function pg(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))II.has(r)&&(t+=o||0),RI.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function na(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," ")}var EI,_I,II,RI,od=O(()=>{EI=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],_I={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 out\b/,/\btalk through\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/],"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/],design:[/\bdesign/,/\bui\b/,/\bux\b/,/\bstyl(e|es|ing)\b/,/\bcss\b/,/\blayout\b/,/\bmock-?up/,/\brender(ing|s)?\b/,/\bpolish/,/\btheme\b/,/\bfonts?\b/,/\bresponsive\b/,/\banimation/,/\bvisual/,/\bpopover\b/],orchestration:[/\bhand[ -]?(off|over)/,/\bhand (it|the lead|this) (over|off)\b/,/\bdelegat/,/\bsubtask/,/\bmerge[ -]?back\b/,/\borchestrat/,/\b(switch|change) (the )?model\b/,/\bmodel switch/],"docs-writing":[/\bdocs?\b/,/\bdocument/,/\bmemo\b/,/\breadme\b/,/\bwrite-?up\b/,/\bchangelog\b/,/\bclaude\.md\b/,/\bblog/,/\bproofread/,/\bcopy(edit|writ)/],"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/]};II=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),RI=new Set(["Edit","Write","MultiEdit","NotebookEdit"])});function gg(e){let t=na(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||$I.has(r)||n.push(r);return n}function OI(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(wg(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(wg(n,"assistant",o))}}return t}function wg(e,t,n){let r=Cr(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 ra(e){let t=OI(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=gg(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,f]of l){let d=n.get(u);d||n.set(u,d=new Map),d.set(i,f)}});let s=t.length;return{chunks:t,postings:n,lengths:r,N:s,avgdl:s?o/s:0}}function rc(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(gg(t))];if(!i.length)return[];let c=new Map;for(let u of i){let f=e.postings.get(u);if(!f)continue;let d=f.size,p=Math.log(1+(e.N-d+.5)/(d+.5));for(let[m,w]of f){let y=e.lengths[m],b=w+mg*(1-hg+hg*(y/(e.avgdl||1))),x=p*(w*(mg+1)/b);c.set(m,(c.get(m)||0)+x)}}let l=[];for(let[u,f]of c){let d=e.chunks[u];o&&d.role!==o||s&&!d.tags.includes(s)||a&&d.boxId!==a||l.push({chunk:d,score:f})}return l.sort((u,f)=>f.score-u.score||f.chunk.seq-u.chunk.seq),l.slice(0,r)}var mg,hg,$I,sd=O(()=>{od();mg=1.5,hg=.75,$I=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 oc from"node:fs/promises";import yg from"node:path";async function CI(e){let t=yg.join(e,".bostrat","events"),n;try{n=await oc.readdir(t)}catch{return ra([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await oc.readFile(yg.join(t,o),"utf8");for(let a of s.split(`
|
|
75
|
+
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return ra(r)}function MI(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}
|
|
76
|
+
${o}`.trim()||t.slice(0,2e3)}function NI(e,t,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=rc(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(),f=u.length>800?u.slice(0,800)+"\u2026":u,d=`- **${l}:** ${f}`;if(i+d.length>r||(a.push(d),i+=d.length,a.length>=n))break}return a.length?["",`## ${bg}, 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(`
|
|
77
|
+
`):""}async function kg(e,t,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await oc.readFile(t,"utf8");if(a.includes(bg))return{appended:!1,reason:"already-enriched"};let i=await CI(e);if(!i.N)return{appended:!1,reason:"empty-index"};let c=NI(i,MI(a),{k:r,maxChars:o,exclude:s});return c?(await oc.appendFile(t,`
|
|
78
78
|
`+c),{appended:!0,chars:c.length,chunks:c.split(`
|
|
79
|
-
- `).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
|
|
80
|
-
`),
|
|
81
|
-
`))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function
|
|
82
|
-
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(
|
|
83
|
-
`).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
|
|
79
|
+
- `).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 bg,xg=O(()=>{sd();bg="Relevant prior discussion (verbatim, retrieved"});import ad from"node:fs/promises";import _o from"node:path";import PI from"node:os";import{EventEmitter as BI}from"node:events";import{randomUUID as DI}from"node:crypto";function Io(e){return e?.cwd?_o.join(e.cwd,".bostrat","events"):_o.join(id,"events")}async function UI(e,t){if(!sc.has(t)){let o=(await B(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);sc.set(t,o)}let n=sc.get(t)+1;return sc.set(t,n),n}function HI(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 R(e,t){let n=typeof e=="string"?e:e?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!t||!jI.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!LI.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!FI.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?_o.join(id,"events"):Io(e),o=t.epoch;if(o==null&&typeof e=="object"&&t.boxId&&Array.isArray(e?.boxes)){let c=e.boxes.find(l=>l.name===t.boxId);o=HI(c,Number(t.payload?.ts??t.ts)||Date.now())}let s=await UI(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||DI(),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}:{}};return await ad.mkdir(r,{recursive:!0}),await ad.appendFile(Sg(r,n),JSON.stringify(i)+`
|
|
80
|
+
`),nt.emit("event",i),nt.emit(`track:${n}`,i),i}async function B(e,t){let n,r;typeof e=="string"&&t?(n=e,r=t):(r=typeof e=="string"?e:e?.id,n=typeof e=="string"?_o.join(id,"events"):Io(e));let o;try{o=await ad.readFile(Sg(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
81
|
+
`))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function vg(e,t){let n=`track:${e}`;return nt.on(n,t),()=>nt.off(n,t)}var jI,LI,FI,nt,id,Sg,sc,ue=O(()=>{jI=new Set(["session.ready","turn.user","turn.agent","turn.retracted","peer.message","provider.error","turn.interrupted","tool.called","tool.result","prompt.raised","prompt.answered","prompt.autoRejected","box.fullscreen.detected","box.fullscreen.answered","context.usage","cost.recorded","memory.seed","handoff.artifact","code.pushed","handoff.started","handoff.step","handoff.done","handoff.failed","subtask.delegated","subtask.merged","job.started","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","notify.raised","usage.cap.hit","usage.cap.warning","provider.connected","track.summary.recorded","track.transferred","track.recovered","track.circuit.tagged","blueprint.item.bound","decision.recorded","changelog.published","stall.detected","error"]),LI=new Set(["user","agent","orchestrator","system","peer"]),FI=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),nt=new BI;nt.setMaxListeners(0);id=process.env.BOSTRAT_STATE_DIR||_o.join(process.env.HOME||PI.tmpdir(),".bostrat");Sg=(e,t)=>_o.join(e,`${t}.ndjson`),sc=new Map});function oa(e,t){if(e!=="claude")return null;let n=String(t).replace(/\r/g,"").split(`
|
|
82
|
+
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(Tg.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(Tg.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(WI);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 Ne(e){return String(e||"").replace(/\s+/g," ").trim()}function Ag(e,t,{slackMs:n=2e4}={}){let r=Ne(t?.text);return!r||typeof t?.at!="number"?!1:(Array.isArray(e)?e:[]).some(o=>o?.role==="user"&&typeof o.ts=="number"&&o.ts>=t.at-n&&Ne(o.text).includes(r))}function Eg(e,t,n,{busy:r=!1}={}){let o=oa(e,n);return o&&cd(t,o)?"stuck":r||o?"submitted":"unknown"}function cd(e,t){let n=Ne(e),r=Ne(t).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var Tg,WI,ld=O(()=>{Tg=/^\s*─{8,}\s*$/,WI=/^❯[ ]?(.*)$/});function JI(e){return qI.test(String(e||"").trimStart())}function Rg(e){let t=String(e||"");if(!JI(t.trimStart()))return null;let n=t.replace(KI,"").trim(),r=XI(n),o=[];for(let u of VI){let f=u.from.map(d=>Ro(r.get(d))).find(d=>d&&!_g(d));f&&o.push({h:u.h,text:sa(f,GI)})}let s=Ro(r.get("Current Work"))||Ro(r.get("Primary Request and Intent"))||ZI(n),a=ic(s),i=sa(ac(fd(a[0]||"")),zI)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(sa(ac(fd(l)),dd));for(let u of["Pending Tasks","Optional Next Step"]){let f=ic(Ro(r.get(u)))[0]||"";if(!(!f||_g(f))){c.push(sa(`Left to do: ${ac(fd(f))}`,dd));break}}if(!c.length){let u=ic(Ro(r.get("Problem Solving"))||"")[0];u&&c.push(sa(ac(u),dd))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function XI(e){let t=new Map,n=[];ud.lastIndex=0;for(let r=ud.exec(e);r;r=ud.exec(e))n.push({name:YI(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 YI(e){return Ig.find(n=>n.toLowerCase()===e.toLowerCase())||e}function Ro(e){return e?String(e).replace(/```[\s\S]*?(?:```|$)/g," ").split(`
|
|
83
|
+
`).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 ZI(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return Ro(t.split(/\n{2,}/).slice(0,2).join(" "))}function ic(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 fd(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 ac(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function _g(e){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(e||"").trim())}function sa(e,t){let n=String(e||"").trim();if(n.length<=t)return n;let r=ic(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 qI,Ig,ud,KI,GI,VI,zI,dd,$g=O(()=>{qI=/^This session is being continued from a previous conversation that ran out of context\./,Ig=["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"],ud=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${Ig.join("|")})[:*\t ]*$`,"gim"),KI=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,GI=420,VI=[{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"]}],zI=116,dd=168});function Cg(e){return pd.has(String(e||"").replace(/^\//,"").toLowerCase())}function cc(e){let t=String(e||"").trim();if(!t)return null;let n=Rg(t);if(n)return n;let r=t.match(QI);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(eR);if(o)return{kind:"skill",name:o[1]};let s=t.match(tR);if(s){let i=s[1].toLowerCase();return pd.has(i)?{kind:"suppress"}:{kind:"command",command:`/${i}`}}let a=t.match(dR);if(a&&pd.has(a[1].toLowerCase()))return{kind:"suppress"};if(nR.test(t))return{kind:"suppress"};if(oR.test(t))return{kind:"suppress"};if(rR.test(t))return{kind:"suppress"};if(sR.test(t)){let i=l=>fR(t.match(l)?.[1].trim()||"")||null,c=i(uR);return{kind:"task",summary:i(aR)||"",taskId:i(iR),toolUseId:i(cR),status:i(lR)?.toLowerCase()||null,resultHead:c?c.length>Og?`${c.slice(0,Og).trimEnd()}\u2026`:c:null}}return null}function fR(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function Mg(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 Ng(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var QI,eR,tR,nR,rR,oR,sR,aR,iR,cR,lR,uR,Og,pd,dR,md=O(()=>{$g();QI=/^Base directory for this skill:\s*(\S+)/,eR=/^Run the "([^"]+)" workflow\./,tR=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,nR=/^<local-command-(?:stdout|caveat)>/i,rR=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,oR=/^Continue from where you left off\.$/,sR=/^<task-notification>/i,aR=/<summary>([\s\S]*?)<\/summary>/i,iR=/<task-id>([\s\S]*?)<\/task-id>/i,cR=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,lR=/<status>([\s\S]*?)<\/status>/i,uR=/<result>([\s\S]*?)(?:<\/result>|$)/i,Og=600,pd=new Set(["model","clear","compact","login","exit","quit"]);dR=/^\/([a-z0-9:_-]+)$/i});import pR from"node:crypto";function gR(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,Bg)){let o=aa(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,mR)){let c=aa(i?.label);if(!c)return null;let l=aa(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<hR)return null;n.push({question:o,header:aa(r?.header).slice(0,wR),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function wd(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;Pg.lastIndex=0;let t=null;for(let o;o=Pg.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=gR(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function gd(e,t,n){let r=pR.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${e}:ask:${t||"turn"}:${r}`}function yd(e,{leadBoxId:t,provider:n,isAnswered:r,maxQuestions:o=Bg}={}){if(!Array.isArray(e)||!t||!hd.has(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||wd(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=gd(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",multiSelect:i[0].multiSelect,questionIndex:0,questionTotal:i.length,questions:i}}function Dg(e,t,n){let r=[];(e?.questions||[]).forEach((a,i)=>{let c=(t?.[i]||[]).map(l=>a.options?.[l]?.label).filter(Boolean);c.length&&r.push({name:a.header||a.question,picks:c.join(", ")})});let o=aa(n),s;return r.length?r.length===1?s=`${r[0].name}: ${r[0].picks}`:s=`Answers:
|
|
84
84
|
${r.map(a=>`- ${a.name}: ${a.picks}`).join(`
|
|
85
85
|
`)}`:s=o,r.length&&o&&(s+=`
|
|
86
|
-
${o}`),s||""}var
|
|
86
|
+
${o}`),s||""}var hd,Bg,mR,hR,wR,Pg,aa,jg,bd=O(()=>{hd=new Set(["cursor","codex"]),Bg=4,mR=4,hR=2,wR=24,Pg=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,aa=e=>typeof e=="string"?e.trim():"";jg=`
|
|
87
87
|
When you are BLOCKED on a decision only the operator can make \u2014 an ambiguous
|
|
88
88
|
requirement, an irreversible choice, several valid approaches \u2014 do not bury the
|
|
89
89
|
question in prose. End your message with exactly ONE fenced code block tagged
|
|
@@ -103,67 +103,67 @@ your turn. The operator sees the questions as an interactive card in their chat
|
|
|
103
103
|
answer. Never use the block for rhetorical questions, progress updates, or
|
|
104
104
|
anything you can decide yourself; keep asking in prose for questions without
|
|
105
105
|
enumerable options.
|
|
106
|
-
`});function XI(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 QI(e,t,n){let r=Sg(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 Er(e){let t=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=/^Agent "([\s\S]+)" finished$/,f=new Map;for(let g of e||[]){if(g.type!=="tool.called"||g.source!=="transcript")continue;let p=String(g.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(p))for(let S of p.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let $=S[0].split("/").pop().toLowerCase();f.has($)||f.set($,[]),f.get($).push(g.payload?.ts??g.ts)}}let d=3*6e4,m=20*6e4,h=(g,p)=>!!(g?.name&&f.get(String(g.name).toLowerCase()))?.some($=>$>=p-d&&$<=p+m);for(let g of e||[])if(g.type==="turn.retracted"){r.add(`${g.boxId}|${g.payload?.saidTs}`);let p=Ne(g.payload?.text);p&&(o.has(g.boxId)||o.set(g.boxId,[]),o.get(g.boxId).push(p))}else if(g.type==="turn.user"&&g.source==="orchestrator"){let p=Ne(g.payload?.text);p&&(s.has(g.boxId)||s.set(g.boxId,new Set),s.get(g.boxId).add(p))}else if(g.type==="job.started"&&g.payload?.id)a.add(g.payload.id),(g.payload.kind||"agent")==="agent"&&g.payload.name&&c.add(g.payload.name);else if(g.type==="turn.user"&&g.payload?.note?.kind==="task"){let p=g.payload.note,S={status:p.status||null,summary:p.summary||null,resultHead:p.resultHead??null,ts:se(g)};p.toolUseId&&i.set(p.toolUseId,S);let $=String(p.summary||"").match(u);$&&l.set($[1],S)}let w=new Map,y=new Map,b=null,x=new Map,E=new Map,_=new Map,q=new Map,C=new Map,N=new Map,T=new Map,W=new Map,H=new Map,ae=new Map,J=new Map,A=new Map,j=new Map,Q=()=>{for(let g of j.values())g.questions.length&&t.push(g);j.clear()},fe=null,Re=6e4,ue=new Map,ie=(g,p)=>{let S=o.get(g);if(!S?.length)return!1;let $=Ne(p),we=s.get(g);return S.some(ge=>{if(!$.startsWith(ge))return!1;let Se=Ne($.slice(ge.length));return!!Se&&!!we?.has(Se)})};for(let g of e||[]){let p=g.payload||{};switch(g.type){case"turn.user":{if(g.source==="hook"||!String(p.text||"").trim()&&!p.images?.length&&!p.attachments?.length)break;{let S=p.note||Zi(p.text);if(S){if(S.kind==="task"){if(S.toolUseId&&a.has(S.toolUseId))break;let $=String(S.summary||"").match(u);if(!S.toolUseId&&$&&c.has($[1]))break}if(S.kind==="command"&&wg(S.command))break;if(S.kind==="resume-summary"){t.push({id:g.id,kind:"resume-summary",title:S.title||"Context resumed from summary",bullets:S.bullets||[],sections:S.sections||[],rawChars:S.rawChars??null,ts:se(g),box:g.boxId,agent:g.provider});break}S.kind!=="suppress"&&t.push({id:g.id,kind:"event",type:yg(S),name:S.name??null,command:S.command??null,summary:S.summary??null,status:S.status??null,ts:se(g),box:g.boxId,agent:g.provider});break}}if(r.has(`${g.boxId}|${se(g)}`)||g.source==="transcript"&&ie(g.boxId,p.text))break;if(p.kind==="merge-notify"||p.kind==="ship-notify"){t.push({id:g.id,kind:"event",type:p.kind==="ship-notify"?"ship-notified":"merge-notified",text:p.text,ts:se(g),box:g.boxId,agent:g.provider});break}if(p.kind==="mock-picked"||p.kind==="ci-fix"||p.kind==="reconcile"||p.kind==="pr-go"||p.kind==="push-go"||p.kind==="spin-up-view"||p.kind==="prompt-answer"){p.kind==="prompt-answer"&&(fe=null);break}for(let S of x.values())S.status="stale";x.clear();for(let S of _.values())S.status="stale";_.clear(),fe&&fe.box===g.boxId&&(t.push(XI(fe)),fe=null),t.push({id:g.id,kind:"message",role:"user",text:p.text,ts:se(g),box:g.boxId,agent:g.provider,boxRole:p.boxRole,...g.actor==="orchestrator"?{orchestration:p.kind||!0}:{},...p.sourceId?{sourceId:p.sourceId}:{},...p.attachments?.length?{attachments:p.attachments}:{},...p.images?.length?{images:p.images}:{}});break}case"peer.message":{let S=p.from||{};t.push({id:g.id,kind:"event",type:"peer-message",text:p.text,from:S.trackName||S.boxId||S.name||null,fromTrackId:S.trackId||null,ts:se(g),box:g.boxId,agent:g.provider});break}case"turn.agent":{if(p.model==="<synthetic>"&&/^No response requested\.$/.test(String(p.text||"").trim()))break;let S=Mw(p,{codexFailedText:vr});if(S){t.push({id:g.id,kind:"event",type:"provider-error",cls:S.cls,status:S.status??void 0,text:S.text,provider:g.provider,legacy:!0,at:se(g),ts:se(g),box:g.boxId,agent:g.provider});break}Q();let $=(p.images||[]).filter($e=>!h($e,se(g)));if(!String(p.text||"").trim()&&!$.length)break;if(g.provider==="cursor"){let $e=`${g.boxId}|${p.text}`;if(n.has($e))break;n.add($e)}(!fe||fe.box===g.boxId)&&(fe=null);let we=g.provider==="codex"?(p.tools||[]).filter($e=>!Mu.has($e)&&!Hs.has($e)):p.tools||[],ge=p.text,Se=null;if(td.has(g.provider)){let $e=nd(String(p.text||""));$e&&(ge=$e.text,Se={questions:$e.questions})}t.push({id:g.id,kind:"message",role:"assistant",text:ge,ts:se(g),box:g.boxId,agent:g.provider,boxRole:p.boxRole,...Se?{ask:Se}:{},...we.length?{tools:we}:{},...p.model?{model:p.model}:{},...p.sourceId?{sourceId:p.sourceId}:{},...$.length?{images:$}:{}}),Se&&(fe={id:g.id,groupId:rd(g.provider,p.sourceId||g.id,Se.questions),provider:g.provider,box:g.boxId,questions:Se.questions,ts:se(g)});break}case"job.started":{if(!p.id)break;let S=se(g),$=i.get(p.id);if(!$&&(p.kind||"agent")==="agent"&&p.name){let v=l.get(p.name);v&&v.ts>=S&&($=v)}let we={jobId:p.id,jobKind:p.kind||"agent",name:p.name||null,detail:p.detail||null,status:$?$.status==="killed"?"stopped":$.status&&$.status!=="completed"?"failed":"done":"running",startedTs:S,finishedTs:$?.ts??null,summary:$?.summary??null,resultHead:$?.resultHead??null},ge=ue.get(g.boxId),Se=ge?.jobs[ge.jobs.length-1]?.startedTs;if(ge&&Se!=null&&S-Se<=Re)ge.jobs.push(we);else{let v={id:g.id,kind:"job-dock",dockId:p.id,ts:S,box:g.boxId,agent:g.provider,jobs:[we]};ue.set(g.boxId,v),t.push(v)}break}case"handoff.done":t.push(pt(g,"lead-handoff"));break;case"subtask.delegated":t.push({...pt(g,"subtask-delegated"),to:g.boxId,toAgent:g.provider});break;case"subtask.merged":t.push(pt(g,"subtask-merged"));break;case"pr.state.changed":{if(!p.change)break;if(p.change==="opened"){for(let $ of x.values())$.status="stale";x.clear();for(let $ of _.values())$.status="stale";_.clear()}let S=pt(g,`pr-${p.change}`);if(S.box=g.boxId??null,ZI.has(p.change)&&p.number!=null){let $=QI(w,y,p);if(w.size>=2){for(let ge of y.values())for(let Se of ge)Se.grouped=!0;S.groupPrs=[...w.values()];let we=[S];for(let ge of w.keys())y.set(ge,we)}else{let we=y.get($)??[];we.push(S),y.set($,we)}}else if(p.change==="merged"||p.change==="closed"||p.change==="lost"){let $=Sg(p),we=b&&!b.complete||w.size>=2;if(w.has($))w.delete($),y.delete($);else if(p.number!=null)for(let ge of[...w.keys()])ge.endsWith(`#${p.number}`)&&(w.delete(ge),y.delete(ge));if(we&&p.number!=null){(!b||b.complete)&&(b={id:g.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:se(g),ts:se(g),box:g.boxId,agent:g.provider},t.push(b)),b.prs.push({number:p.number,url:p.url??null,title:p.title??null,repo:p.repo??null,branch:p.branch??null,change:p.change,ts:se(g)}),b.remaining=[...w.values()],b.complete=w.size===0;break}}t.push(S);break}case"pr.offered":{if(!p.offerId)break;for(let $ of x.values())$.status="stale";x.clear();for(let $ of _.values())$.status="stale";_.clear();let S={id:g.id,kind:"pr-offer",offerId:p.offerId,excerpt:p.excerpt||null,branch:p.branch||null,base:p.base||null,offered:Array.isArray(p.offered)&&p.offered.length?p.offered:null,toPr:p.toPr&&typeof p.toPr=="object"?{number:p.toPr.number??null}:null,available:Array.isArray(p.available)?p.available:null,defaults:Array.isArray(p.defaults)?p.defaults:null,prState:p.prState||null,openPr:p.openPr&&typeof p.openPr=="object"?p.openPr:null,repos:Array.isArray(p.repos)&&p.repos.length?p.repos:null,status:"open",ts:se(g),box:g.boxId,agent:g.provider};x.set(p.offerId,S),E.set(p.offerId,S),t.push(S);break}case"pr.offer.answered":{let S=E.get(p.offerId);S&&(S.status=p.action||"dismiss",S.actions=Array.isArray(p.actions)?p.actions:null,S.notify=p.notify===!0,p.existingPr&&typeof p.existingPr=="object"&&(S.existingPr={number:p.existingPr.number??null}),x.delete(p.offerId));break}case"pr.exists.offered":{if(!p.offerId)break;for(let $ of _.values())$.status="stale";_.clear();let S={id:g.id,kind:"pr-exists-offer",offerId:p.offerId,srcOfferId:p.srcOfferId??null,branch:p.branch||null,base:p.base||null,pr:p.pr&&typeof p.pr=="object"?{number:p.pr.number??null,url:p.pr.url??null,title:p.pr.title??null}:null,actions:Array.isArray(p.actions)&&p.actions.length?p.actions:null,notify:p.notify===!0,status:"open",ts:se(g),box:g.boxId,agent:g.provider};_.set(p.offerId,S),q.set(p.offerId,S),t.push(S);break}case"pr.exists.answered":{let S=q.get(p.offerId);S&&(S.status=p.choice||"dismiss",_.delete(p.offerId));break}case"box.state.changed":{let S=p.change==="model"?"model-change":p.change;if(!S)break;let $=pt(g,S);S==="model-change"&&($.to=p.model),t.push($);break}case"track.transferred":case"track.recovered":t.push(pt(g,"track-moved"));break;case"view.state.changed":{if(p.change!=="created"||p.restarted)break;t.push({id:g.id,kind:"view-card",viewId:p.viewId,name:p.name,slug:p.slug,port:p.port,origin:p.origin,ts:se(g),box:g.boxId,agent:g.provider});break}case"view.spinup.failed":{t.push({id:g.id,kind:"view-spinup-failed",name:p.name||null,reason:p.reason||"no-view",ts:se(g),box:g.boxId,agent:g.provider});break}case"mock.state.changed":{if(p.change==="picked"){t.push({id:g.id,kind:"mock-picked",mockId:p.mockId,name:p.name,group:p.group??null,version:p.version??null,siblings:p.siblings??[],ts:se(g),box:g.boxId,agent:g.provider});break}if(p.change!=="created")break;t.push({id:g.id,kind:"mock-card",mockId:p.mockId,name:p.name,group:p.group??null,ts:se(g),box:g.boxId,agent:g.provider});break}case"flow.state.changed":{if(p.change!=="created")break;t.push({id:g.id,kind:"flow-card",flowId:p.flowId,name:p.name,stepCount:p.stepCount??null,mockIds:p.mockIds??[],ts:se(g),box:g.boxId,agent:g.provider});break}case"runbook.state.changed":{if(p.change!=="created")break;t.push({id:g.id,kind:"runbook-card",runbookId:p.runbookId,name:p.name,ts:se(g),box:g.boxId,agent:g.provider});break}case"ticket.linked":{t.push({id:g.id,kind:"ticket-card",provider:p.provider||"linear",key:p.key,url:p.url,title:p.title??null,linkSource:p.linkSource??null,ts:se(g),box:g.boxId,agent:g.provider});break}case"box.fullscreen.detected":{if(!p.offerId)break;for(let $ of T.values())$.box===(p.box||g.boxId)&&($.status="stale",T.delete($.offerId));let S={id:g.id,kind:"fullscreen-offer",offerId:p.offerId,status:"open",ts:se(g),box:p.box||g.boxId,agent:p.agent||g.provider};T.set(p.offerId,S),W.set(p.offerId,S),t.push(S);break}case"box.fullscreen.answered":{let S=W.get(p.offerId);S&&(S.status=p.action==="restarted"?"restarted":p.action==="cleared"?"cleared":"dismiss",T.delete(p.offerId));break}case"ticket.move.offered":{if(!p.offerId)break;for(let $ of C.values())$.key===p.key&&($.status="stale",C.delete($.offerId));let S={id:g.id,kind:"ticket-offer",offerId:p.offerId,provider:p.provider||"linear",key:p.key,url:p.url??null,phase:p.phase??null,from:p.from??null,to:p.to?.name??null,prNumber:p.prNumber??null,status:"open",ts:se(g),box:g.boxId,agent:g.provider};C.set(p.offerId,S),N.set(p.offerId,S),t.push(S);break}case"ticket.move.answered":{let S=N.get(p.offerId);S&&(S.status=p.action==="moved"?"moved":"dismiss",C.delete(p.offerId));break}case"ticket.state.changed":{for(let S of C.values())S.key===p.key&&S.to===p.to&&(S.status="stale",C.delete(S.offerId));if(p.mode==="offer")break;t.push(pt(g,"ticket-state"));break}case"ticket.commented":{t.push({id:g.id,kind:"ticket-comment",key:p.key,url:p.url??null,excerpt:p.excerpt??null,ts:se(g),box:g.boxId,agent:g.provider});break}case"ticket.flow.changed":t.push(pt(g,"ticket-flow"));break;case"ticket.create.offered":{if(!p.proposalId)break;for(let $ of H.values())$.status="stale";H.clear();let S={id:g.id,kind:"ticket-create-offer",proposalId:p.proposalId,provider:p.provider||"linear",epic:p.epic??null,issues:Array.isArray(p.issues)?p.issues:[],team:p.team??null,needsTeam:!!p.needsTeam,linkCurrentTrack:!!p.linkCurrentTrack,status:"open",ts:se(g),box:g.boxId,agent:g.provider};H.set(p.proposalId,S),ae.set(p.proposalId,S),t.push(S);break}case"ticket.create.answered":{let S=ae.get(p.proposalId);S&&(S.status=p.action==="created"?"created":"dismiss",S.createdCount=p.createdCount??null,S.failedCount=p.failedCount??null,H.delete(p.proposalId));break}case"loop.create.offered":{if(!p.proposalId)break;for(let $ of J.values())$.status="stale";J.clear();let S={id:g.id,kind:"loop-create-offer",proposalId:p.proposalId,name:p.name??"",schedule:p.schedule??"",prompt:p.prompt??"",expectation:p.expectation??null,notify:p.notify??"off",assignment:p.assignment??{provider:"claude"},repo:p.repo??null,status:"open",ts:se(g),box:g.boxId,agent:g.provider};J.set(p.proposalId,S),A.set(p.proposalId,S),t.push(S);break}case"loop.create.answered":{let S=A.get(p.proposalId);S&&(S.status=p.action==="created"?"created":"dismiss",J.delete(p.proposalId));break}case"loop.created":{let S=p.proposalId?A.get(p.proposalId):null;S&&(S.created={loopId:p.loopId??null,nextFireAt:Number.isFinite(Number(p.nextFireAt))?Number(p.nextFireAt):null});break}case"ticket.created":{let S={items:Array.isArray(p.items)?p.items:[],failed:Array.isArray(p.failed)?p.failed:[],team:p.team??null},$=p.proposalId?ae.get(p.proposalId):null;if($){$.created=S;break}t.push({id:g.id,kind:"ticket-created",...S,ts:se(g),box:g.boxId,agent:g.provider});break}case"error":{if(!p.kind)break;t.push(pt(g,p.kind));break}case"provider.error":t.push({...pt(g,"provider-error"),provider:g.provider,box:g.boxId,agent:g.provider});break;case"usage.cap.hit":t.push(pt(g,"usage-cap-hit"));break;case"usage.cap.warning":t.push(pt(g,"usage-cap-warning"));break;case"provider.connected":{t.push({id:g.id,kind:"event",type:"provider-connected",provider:p.provider||g.provider,ts:se(g),at:se(g),box:g.boxId,agent:g.provider});break}case"prompt.answered":{if(fe=null,p.control||!p.question)break;let S=p.groupId||p.promptId;if(!S)break;let $=j.get(S);$||($={id:g.id,kind:"prompt",groupId:S,provider:g.provider,box:g.boxId,agent:g.provider,questionTotal:p.questionTotal??1,questions:[],ts:se(g)},j.set(S,$));let we=p.questionIndex??0,ge={index:we,header:p.header||"",question:p.question,multiSelect:!!p.multiSelect,chosen:Array.isArray(p.chosen)?p.chosen:[]},Se=$.questions.findIndex(v=>v.index===we&&Ne(v.question)===Ne(ge.question));Se>=0?$.questions[Se]=ge:$.questions.push(ge),$.questions.sort((v,V)=>v.index-V.index),$.questionTotal=Math.max($.questionTotal,p.questionTotal??1),$.questions.length>=$.questionTotal&&(t.push($),j.delete(S));break}default:break}}return tR(t.sort((g,p)=>(g.ts??0)-(p.ts??0)))}function tR(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<=eR){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 ad(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:nR(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??Er(e)).filter(o=>o.kind==="message");return Aw(n,r)}function nR(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==go(e.command)?t:Pn(e.command,e.description||"")}function pt(e,t){let{ts:n,change:r,...o}=e.payload||{};return{id:e.id,kind:"event",type:t,...o,at:se(e),ts:se(e)}}function Tg(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)+rR,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&oR.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function Ag(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!aR.has(r.type)||!r.box)continue;let o=(r.ts??0)+sR,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}var se,ZI,Sg,eR,rR,oR,sR,aR,id=O(()=>{yo();Sr();Fi();Ju();ed();sd();se=e=>e.payload?.ts??e.ts;ZI=new Set(["opened","ci-fixed","reconciled","pushed"]),Sg=e=>`${e.repo??""}#${e.number}`;eR=2*6e4;rR=12e4,oR=new Set(["archived","unarchived","opened"]);sR=2500,aR=new Set(["pr-opened","pr-merged","pr-auto-armed"])});function dR(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 pR(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 mR(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<lR&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<uR&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function Rg(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=dR(e),s=pR(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=Er(e??[]).filter(d=>d?.kind==="message"&&typeof d.text=="string"&&d.text.trim()).slice(-iR);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(Ig[r]??Ig.failover),l.push(""),o.lastPush?.sha){let d=_g(n,o.lastPush.ts),m=d?` (~${d} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${m}`+(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 d=_g(n,i);l.push(`## Last real checkpoint (written by the previous agent${d?`, ~${d} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=mR(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(d=>`\`${d}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(d=>`\`${d}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let d of c)l.push(`**${d.role==="user"?"operator":"agent"}:** ${fR(d.text,cR)}`),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 f=l.join(`
|
|
107
|
-
`);return f.length<=
|
|
108
|
-
|
|
109
|
-
[\u2026 brief truncated]`}var
|
|
110
|
-
|
|
111
|
-
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function TR(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(Mg.test(s)){if(r.length>=Wg)continue;let a=SR(s.replace(Mg,"").replace(/[.;,:]+$/,""),qg,xR);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>Cg&&(o=`${o.slice(0,Cg-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Kg(e,t,{timeoutMs:n=9e4}={}){let r=Ln.join(t,bR),o=Ln.join(r,kR);await jn.mkdir(r,{recursive:!0});let s=await Qi(o),a=await oe(e);if(!a)throw new Error(`session "${e}" not found`);return await ec(e,a,vR(o)),await Zg(o,s,n),TR(await jn.readFile(o,"utf8"))}function Gg(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===Lg||n===Hg;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(jg)||n===wR||r.startsWith(Fg)||r.startsWith(Ug)}function Vg(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-AR;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 zg(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 Jg(e,{windows:t=[]}={}){if(Gg(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 tc(e){return cd.get(e)||null}async function Yg(e){let t=cd.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await oe(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:Ln.join(n.cwd,Pg,Bg)};return cd.set(e,r),IR(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function IR(e,t){e.step="summarize",await Xg(e.name,t),e.step="clear";let n=await oe(e.name);await ec(e.name,n,"/clear"),await dd(ER),e.step="resume",await ec(e.name,n,yR),e.step="done",e.finishedAt=Date.now()}async function Xg(e,t,{timeoutMs:n=_R}={}){let r=Ln.join(t,Pg),o=Ln.join(r,Bg);await jn.mkdir(r,{recursive:!0});let s=await Qi(o),a=await oe(e);await z({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await ec(e,a,gR(o)),await Zg(o,s,n),await z({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await Qi(o)});let i=await og(t,o,{exclude:u=>Gg(u.role,u.text)});await z({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(c){await I({id:c,cwd:t},{type:"memory.seed",actor:"orchestrator",payload:{...i,tokensEst:i.chars?Math.round(i.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let u=await jn.readFile(o,"utf8");await I({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...Dg(u),ts:Date.now()}})}catch{}}await Hh(t);let l=await RR(r,o);return c&&await Qh(c,l||o,{box:e,agent:a?.agent??null}),o}async function ld(e,{timeoutMs:t}={}){let n=await oe(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return Xg(e,n.cwd,t?{timeoutMs:t}:void 0)}async function ud(e,t,{trackName:n="this Track"}={}){let r=await B({id:e,cwd:t}),o=Rg(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=Ln.join(t,Ng);return await jn.mkdir(Ln.dirname(s),{recursive:!0}),await jn.writeFile(s,o,"utf8"),await I({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...Dg(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),Ng}async function Qi(e){try{return(await jn.stat(e)).mtimeMs}catch{return 0}}async function Zg(e,t,n){let r=Date.now()+n;for(;;){if(await Qi(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await dd(2e3)}}async function RR(e,t){try{if(!hR.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=Ln.join(e,`${n}.md`);return await jn.copyFile(t,r),r}catch{return null}}function dd(e){return new Promise(t=>setTimeout(t,e))}async function ec(e,t,n){if(t){let o=t.agent==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":t.agent==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":t.agent==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":"";await Dn({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await oe(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await Oe(e,["Escape"],{kind:"drive-wipe"}),await dd(150),await po(e,Di,{kind:"drive-wipe"}),await je(e,n,{kind:"orchestration-drive",...Mt(r.agent)})}var Pg,Bg,Og,jg,Lg,Fg,wR,gR,yR,Ug,Hg,bR,kR,Cg,Wg,qg,xR,vR,Mg,SR,AR,ER,_R,cd,Ng,fd=O(()=>{ze();Gi();Yt();zs();sg();Ti();le();Ri();$g();Pg=".bostrat/handoff",Bg="latest.md",Og=32*1024;jg="Checkpoint: write a work-in-progress handoff",Lg="HANDOFF WRITTEN",Fg="Resume from .bostrat/handoff/latest.md",wR="/clear",gR=e=>`${jg} 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 ${Lg} and take no further action.`,yR=`${Fg}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,Ug="Beat: write a",Hg="BEAT WRITTEN",bR=".bostrat/arc",kR="beat.txt",Cg=200,Wg=4,qg=5,xR=36,vR=e=>`${Ug} 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 ${Wg} further lines, each starting with "- " and naming ONE piece in at most ${qg} 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 ${Hg} and take no further action.`,Mg=/^(?:[-–—*•·]|\d+[.)])\s+/,SR=(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};AR=6e5;ER=3500,_R=3e5,cd=new Map;Ng=".bostrat/handoff/status-latest.md"});function ey(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!$R.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 OR(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:ey(o.from,"from",n),toMs:ey(o.to,"to",n)}))}return t}function MR(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 NR(e){if(!e)return null;let t=String(e).trim();return pd[t]?t:Qg[t]?Qg[t]:Object.keys(pd).find(r=>t.startsWith(r))||null}function ty(e,t){let n=NR(e);if(!n)return null;let r=MR(t);return r===null?null:CR[n].find(o=>r>=o.fromMs&&r<=o.toMs)||null}function Fn(e,t={},n){let r=ty(e,n);return r?((t.input||0)*r.input+(t.output||0)*r.output+(t.cacheWrite||0)*r.cacheWrite+(t.cacheRead||0)*r.cacheRead)/1e6:0}var pd,Qg,$R,CR,ny,ea=O(()=>{pd={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,note:"GA rates; verified 2026-08-06 via the claude-api skill."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,note:"verified 2026-08-06 via the claude-api skill."}],"claude-sonnet-5":[{from:null,to:"2026-08-31",input:2,output:10,cacheWrite:4,cacheRead:.2,note:"GA introductory pricing ($2/$10); verified 2026-08-06 via the claude-api skill."},{from:"2026-09-01",to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,note:"standard rates resume after the intro window; verified 2026-08-06."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,note:"verified 2026-08-06 via the claude-api skill."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,note:"verified 2026-08-06 via the claude-api skill."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,note:"verified 2026-08-06 via the claude-api skill; 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,note:"verified 2026-07-03; no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:null,input:5,output:30,cacheWrite:6.25,cacheRead:.5,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:null,input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:null,input:1,output:6,cacheWrite:1.25,cacheRead:.1,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}]},Qg={opus:"claude-opus-5",opusplan:"claude-opus-5",default:"claude-opus-5",sonnet:"claude-sonnet-5",haiku:"claude-haiku-4-5",fable:"claude-fable-5","gpt-5.6":"gpt-5.6-sol"},$R=/^\d{4}-\d{2}-\d{2}$/;CR=OR(pd);ny=(e,t)=>ty(e,t)!==null});function PR(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function oy(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function Qt(e){let t=e.payload?.model||"unknown",n=PR(e.payload?.tokens),r=Number(e.payload?.usd)||0,o=e.source==="vendor",s=r,a=!1;if(n&&!o){let l=Fn(t,n,e.ts);l>0&&(s=l,a=Math.abs(l-r)>1e-9)}let i=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,c=!s&&i>0&&!ny(t,e.ts);return{usd:s,model:t,tokens:n,repriced:a,unpriced:c,asRecorded:r}}function Un(e,{boxLabels:t={}}={}){let n=(e||[]).filter(d=>d.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,f=ry();for(let d of n){let{usd:m,model:h,tokens:w,repriced:y,unpriced:b,asRecorded:x}=Qt(d);i+=x;let E=d.provider||"unknown",_=d.boxId||"unattributed";if(y&&c++,b&&(l++,u.add(h)),a+=m,oy(f,w),r[h]||={usd:0,tokens:ry(),events:0},r[h].usd+=m,r[h].events+=1,oy(r[h].tokens,w),o[E]||={usd:0,events:0},o[E].usd+=m,o[E].events+=1,s[_]||={usd:0,events:0,provider:E,models:new Set,label:t[_]||_},s[_].usd+=m,s[_].events+=1,h!=="unknown"&&s[_].models.add(h),d.epoch!=null){let q=(s[_].epochs||={})[d.epoch]||={usd:0,events:0,provider:E,models:new Set};q.usd+=m,q.events+=1,q.provider=E,h!=="unknown"&&q.models.add(h)}}return{totalUsd:vo(a),asRecordedUsd:vo(i),totalTokens:f,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:nc(r,d=>({...d,usd:vo(d.usd)})),byProvider:nc(o,d=>({...d,usd:vo(d.usd)})),byBox:nc(s,d=>({usd:vo(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:nc(d.epochs,m=>({usd:vo(m.usd),events:m.events,provider:m.provider,models:[...m.models]}))}:{}}))}}var ry,vo,nc,_r=O(()=>{ea();ry=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});vo=e=>Math.round(e*1e4)/1e4,nc=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function BR(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 sy(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 f=u.payload||{};!f.repo||f.number==null||(f.change==="merged"?o.set(`${f.repo}#${f.number}`,f):f.change==="opened"&&s++)}if(o.size){let u=[...o.keys()].map(f=>{let[d,m]=f.split("#");return`${d.split("/").pop()}#${m}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(md(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(md(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(f=>`${f.fromAgent}\u2192${f.toAgent}`).join(", ");n.push(`${md(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=Un(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?BR(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var md,ay=O(()=>{_r();md=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as DR}from"node:child_process";import{promisify as jR}from"node:util";import{randomUUID as LR}from"node:crypto";async function hd(e,t){let n=Date.now()+t;for(;;){if(await fo(e))return!0;if(Date.now()>n)return!1;await rc(UR)}}async function WR(e,t){let n=cy[t]||cy.default;n.escape&&(await Oe(e,["Escape"],{kind:"swap-exit"}),await rc(150)),n.wipe&&await po(e,Di,{kind:"swap-exit"}),await ji(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Mt(t).submitDelayMs||0;await rc(Math.max(r,250)),await Oe(e,["Enter"],{kind:"swap-exit"})}async function qR(e,t){try{let{stdout:n}=await So(L,U("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function ly(e){await So(L,U("respawn-pane","-k","-t",e))}async function wd(e,t={}){if(!Ee(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await oe(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||Cn(n),a=Number(t.exitTimeoutMs)||FR;await z({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let i=!1;if(!await fo(e)&&(await WR(e,o),!await hd(e,a)&&(i=!0,await z({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await ly(e),!await hd(e,iy))))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);let c=s==="claude"&&br(n),l=c?mi():null,u=await qR(e,"ANTHROPIC_API_KEY"),f=!1;try{l?(await So(L,U("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await So(L,U("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),f=!0)}catch(y){throw l?Oi(y,l):y}if(f&&!i&&(await ly(e),!await hd(e,iy)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let d=!!t.resumeSessionId,m=t.resumeSessionId||LR(),h=d?n:bu(n,m,e),w=d?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(h);return c&&await En(r.cwd),s==="codex"&&await to(r.cwd),s==="cursor"&&await no(r.cwd),await kr(e),await So(L,U("set-option","-t",e,"@bostrat-sid",m)),await So(L,U("set-option","-t",e,"@bostrat-agent",s)),await rc(HR),await je(e,h,{kind:"swap-launch"}),await z({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:m,forced:i,envRespawned:f}),{name:e,cwd:r.cwd,sessionId:m,agent:s,pinned:w,forced:i,envRespawned:f}}var So,rc,FR,iy,UR,HR,cy,uy=O(()=>{ze();Es();wi();gi();zs();Yt();So=jR(DR),rc=e=>new Promise(t=>setTimeout(t,e)),FR=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,iy=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,UR=250,HR=400,cy={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"}}});async function dy(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await et(e,t)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
|
|
112
|
-
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.isSidechain)continue;let u=l.message?.content;if(!Array.isArray(u))continue;let f=l.timestamp?Date.parse(l.timestamp):null;for(let d of u)if(!(!d||typeof d!="object"))if(d.type==="tool_use"&&d.id){if(d.name==="Agent"){let
|
|
113
|
-
`).trim():"",
|
|
114
|
-
`),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 f=r[u].match(
|
|
115
|
-
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!
|
|
116
|
-
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(
|
|
117
|
-
`),o=-1;for(let u=r.length-1;u>=0;u--)if(
|
|
118
|
-
`),o
|
|
119
|
-
`),o
|
|
120
|
-
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=
|
|
121
|
-
`)].reverse().find(a=>
|
|
122
|
-
`),o
|
|
123
|
-
`),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(
|
|
124
|
-
`),o=
|
|
125
|
-
`).pop()||"")?!0:
|
|
126
|
-
`).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
|
|
127
|
-
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];
|
|
128
|
-
`).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
|
|
129
|
-
`).map(s=>s.trim()).filter(Boolean)}async function
|
|
130
|
-
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var
|
|
131
|
-
`),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
|
|
132
|
-
`),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
|
|
133
|
-
`);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
|
|
134
|
-
`),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 hb(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&&!fb.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=pb.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!W$.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function aa(e,t,{timeoutMs:n=U$}={}){let r=Date.now()+n;for(;;){let o=await Ae(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await pc(H$)}}async function G$(e,t){return await je(e,`/model ${t.model}`,Mt("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function V$(e,t){await Bi(e),await je(e,"/model",{enter:!1}),await pc(250),await Oe(e,["Enter"],{kind:"model-switch"});let n=await aa(e,cb);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await Oe(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=K$(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await je(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await aa(e,i=>lb(i)||!cb(i)&&o(i)&&{done:o(i)});if(!s)throw await Oe(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?fb.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 Oe(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await je(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await aa(e,i=>!lb(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 z$(){let e=ib.join(process.env.CURSOR_CONFIG_DIR||ib.join(F$.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await L$.readFile(e,"utf8"))?.model||null}catch{return null}}async function J$(e,t){await Bi(e),await ji(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await aa(e,ub),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 aa(e,ub,{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 Oe(e,["Down"],{kind:"model-switch"}),await pc(250)}if(!n||!s||!o(s)){await Oe(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await Oe(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 Oe(e,["Enter"],{kind:"model-switch"}),await pc(800);let a=await z$();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 wb(e,t,n){return e==="codex"?V$(t,n):e==="cursor"?J$(t,n):G$(t,n)}var pc,U$,H$,db,W$,q$,fb,pb,gb=O(()=>{ze();zs();pc=e=>new Promise(t=>setTimeout(t,e)),U$=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,H$=300,db=[{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"},{alias:"opusplan",label:"Opus Plan"}],W$=new Set([...db.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"]),q$=[{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"}],fb=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),pb=[{alias:"auto",label:"Auto"},{alias:"composer-2.5",label:"Composer 2.5"},{alias:"claude-opus-5",label:"Opus 5"},{alias:"claude-fable-5",label:"Fable 5"},{alias:"claude-sonnet-5",label:"Sonnet 5"},{alias:"claude-sonnet-4-6",label:"Sonnet 4.6"},{alias:"gpt-5.6-sol",label:"GPT-5.6 Sol"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}]});function hc(e){let t=e||{},n=Y$(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function Y$(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:nt(e.agent),payload:{text:e.text,boxRole:e.boxRole,...e.kind?{kind:e.kind}:{},...e.attachments?.length?{attachments:e.attachments}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:e.box,provider:nt(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:nt(e.agent),payload:{text:e.prompt,kind:e.kind,boxRole:e.boxRole}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:nt(e.toAgent),payload:{from:e.from,fromAgent:e.fromAgent,to:e.to,toAgent:e.toAgent,reason:e.reason,handoffStartedAt:e.handoffStartedAt,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,singleBox:e.singleBox,forced:e.forced,recovered:e.recovered,checkpoint:e.checkpoint}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:nt(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:nt(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:nt(e.agent),payload:{...qn(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:nt(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:nt(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":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:nt(e.agent),payload:{change:"rehydrated"}};case"transferred-in":return{type:e.mode==="recover"?"track.recovered":"track.transferred",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:nt(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"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:nt(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};default:return null}}function kb(e,t){let n=hc(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;zd(()=>I(r,n))}async function xb(e){if(!(!e?.id||yb.has(e.id))){yb.add(e.id);try{let t=await B(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=hc(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await I(e,o)}}catch{}}}function qn(e){let{type:t,at:n,...r}=e;return r}function vb(e,t,n){!n?.promptId||Vd.get(e.id)===n.promptId||(Vd.set(e.id,n.promptId),zd(()=>I(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:nt(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 Jd(e,t,n,r={}){Vd.delete(e.id),zd(()=>I(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:nt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var nt,mc,zd,bb,yb,Vd,Yd=O(()=>{le();nt=e=>e||null;mc=Promise.resolve(),zd=e=>(mc=mc.then(e).catch(()=>{}),mc),bb=()=>mc;yb=new Set;Vd=new Map});function tO(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function Ib(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of tO(n).reverse()){if(!X$.test(r))continue;let o=r.endsWith("?")&&r.length<=eO&&Q$.test(r);if(Z$.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function $b(e){let t=String(e||""),n=[];nO.test(t)&&n.push("commit"),Rb.test(t)&&n.push("rebase"),rO.test(t)&&n.push("push");let r=sO.test(t);!r&&oO.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 Ob(e){return Rb.test(String(e||""))}function Po(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(m=>`${m.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 m=a.has("commit")?"the branch":`this branch${t?` (${t})`:""}`;u.push(i?`rebase ${m} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${m} 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 m=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",h=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${h}${m}`:`push this branch${t?` (${t})`:""} from each changed checkout (${c}) to its remote${m}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${h}${m}`:`push this branch${l} to the remote${m}`)}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 f=[];a.has("commit")||f.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&f.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&f.push("don't push"),a.has("pr")||f.push("don't open a pull request");let d=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return f.length&&(d+=` ${f.join(", and ").replace(/^./,m=>m.toUpperCase())}.`),a.has("pr")&&(d+=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."),d}function Cb(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 Mb(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<=aO&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function Nb(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+iO||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function Pb(e){let t=(Array.isArray(e)?e:[]).filter(r=>Sb[r]).map(r=>Sb[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 Tb,Ab,Eb,_b,X$,Z$,Q$,eO,No,nO,Rb,rO,oO,sO,aO,iO,Sb,Bo=O(()=>{Tb=/(?<!\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,Ab=/(?<!\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,Eb=/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,_b=/(?<!\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,X$=new RegExp(`\\b(?:${Tb}|${Ab}|${Eb}|${_b})\\b`,"i"),Z$=/\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,Q$=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,eO=90;No=["commit","rebase","push","pr"],nO=new RegExp(`\\b(?:${Tb})\\b`,"i"),Rb=new RegExp(`\\b(?:${_b})\\b`,"i"),rO=new RegExp(`\\b(?:${Ab})\\b`,"i"),oO=new RegExp(`\\b(?:${Eb})\\b`,"i"),sO=/\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;aO=3e4;iO=2500;Sb={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import cO from"node:crypto";import wc from"node:fs/promises";import Bb from"node:path";import lO from"node:os";async function Ce(){return Kn||(Xd??=wc.readFile(Qd,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(Kn=e,!t&&Te()){let{records:n,source:r}=await Be("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.repo||o.data?.number==null)continue;let{id:s,updatedAt:a,...i}=o.data;Kn[X(i.repo,i.number)]=i}Object.keys(Kn).length&&await ef().catch(()=>{})}}return Kn}).finally(()=>Xd=null),Xd)}async function ef(){let e=JSON.stringify({links:Kn},null,2),t=`${Qd}.${process.pid}.tmp`;await wc.mkdir(Db,{recursive:!0}),await wc.writeFile(t,e),await wc.rename(t,Qd)}async function Fe(e){Zd=Zd.then(ef,ef),await Zd.catch(()=>{});let t=Kn?.[e];t&&Ve("prlinks",{id:jb(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function it({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 Ce(),c=X(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:uO.has(a),firstSeen:Date.now()},await Fe(X(e,t)),{link:i[c],created:!0})}async function tn(e,t){if(!e||t==null)return null;let n=(await Ce())[X(e,t)];return!n||n.announced?null:(n.announced=!0,await Fe(X(e,t)),n)}async function Je(e,t){return!e||t==null?null:(await Ce())[X(e,t)]??null}async function Gn(e,t,n){if(!e||t==null)return null;let r=(await Ce())[X(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,await Fe(X(e,t)),r):null}async function Lb(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ce())[X(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Fe(X(e,t)),o):null}async function Fb(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ce())[X(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Fe(X(e,t)),o):null}async function gc(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ce())[X(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Fe(X(e,t)),r)}async function yc(e,t){if(!e||t==null)return null;let n=(await Ce())[X(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Fe(X(e,t)),n)}async function bc(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ce())[X(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Fe(X(e,t)),r)}async function kc(e,t){if(!e||t==null)return null;let n=(await Ce())[X(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Fe(X(e,t)),n)}async function Ub(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await Ce())[X(e,t)];return r?(r.baseFoldAt=n,await Fe(X(e,t)),r):null}async function Hb(e,t){if(!e||t==null)return null;let n=(await Ce())[X(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Fe(X(e,t)),n)}async function Wb(e,t,n){if(!e||t==null||!n)return null;let r=(await Ce())[X(e,t)];return r?(r.lastHeadSha=n,await Fe(X(e,t)),r):null}async function ia(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await Ce())[X(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Fe(X(e,t)),r):null}async function Br(e,t,n=!0){if(!e||t==null)return null;let r=(await Ce())[X(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Fe(X(e,t)),r):null}async function ca(e,t,n=!0){if(!e||t==null)return null;let r=(await Ce())[X(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Fe(X(e,t)),r):null}async function qb(e,t,n){if(!e||t==null)return null;let r=(await Ce())[X(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 Fe(X(e,t)),r}async function la(e,t){if(!e||t==null)return null;let n=(await Ce())[X(e,t)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,await Fe(X(e,t)),n)}async function Kb(){return Object.values(await Ce()).filter(e=>!dO.has(e.state))}async function Dr(){return Object.values(await Ce()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function Gb(){if(!Te())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Be("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Ce(),r=0;for(let[o,s]of Object.entries(n))await Ve("prlinks",{id:jb(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var Db,Qd,X,jb,Kn,Xd,Zd,uO,dO,Vn=O(()=>{On();Db=process.env.BOSTRAT_STATE_DIR||Bb.join(process.env.HOME||lO.tmpdir(),".bostrat"),Qd=Bb.join(Db,"pr-links.json"),X=(e,t)=>`${e}#${Number(t)}`,jb=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=cO.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},Kn=null,Xd=null,Zd=Promise.resolve();uO=new Set(["api","observed","reconciled"]);dO=new Set(["merged","closed","lost"])});import fO from"node:fs";import tf from"node:fs/promises";import Yb from"node:path";import pO from"node:os";function Qb(e,t=!1){if(!t&&e-zb<mO)return;zb=e;let n=JSON.stringify({windowStart:Do,byCaller:Object.fromEntries(nn),lastHour:zn});Jb=Jb.then(()=>tf.mkdir(Xb,{recursive:!0})).then(()=>tf.writeFile(`${xc}.tmp`,n)).then(()=>tf.rename(`${xc}.tmp`,xc)).catch(()=>{})}function ek(e){e-Do<Zb||(zn={start:Do,total:vc(nn),byCaller:Object.fromEntries(nn)},zn.total&&console.log(`[github] ${zn.total} calls last hour`,zn.byCaller),Do=e,nn=new Map,Qb(e,!0))}function rn(e="other",t=Date.now()){ek(t),nn.set(e,(nn.get(e)||0)+1),Qb(t)}function tk(e=Date.now()){return ek(e),{windowStart:Do,total:vc(nn),byCaller:Object.fromEntries(nn),lastHour:zn,backoffUntil:jr>e?jr:null}}function Jn(e){return hO.test(String(e?.message||e||""))}function Yn(e,t=Date.now()){return jr<=t&&console.warn(`[github] rate limited \u2014 backing off ${Vb/6e4} min:`,String(e?.message||e||"").slice(0,200)),jr=Math.max(jr,t+Vb),jr}function Sc(e=Date.now()){return Math.max(0,jr-e)}var Xb,xc,Zb,Vb,mO,Do,nn,zn,jr,zb,vc,Jb,hO,ua=O(()=>{Xb=process.env.BOSTRAT_STATE_DIR||Yb.join(process.env.HOME||pO.tmpdir(),".bostrat"),xc=Yb.join(Xb,"github-metrics.json"),Zb=60*6e4,Vb=5*6e4,mO=6e4,Do=Date.now(),nn=new Map,zn=null,jr=0,zb=0,vc=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(fO.readFileSync(xc,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<Zb?(Do=e.windowStart,nn=new Map(Object.entries(e.byCaller||{})),zn=e.lastHour||null):vc(new Map(Object.entries(e.byCaller||{})))&&(zn={start:e.windowStart,total:vc(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}Jb=Promise.resolve();hO=/rate limit|too quickly|abuse detection|HTTP 429/i});import nk from"node:fs/promises";import rk from"node:path";import wO from"node:os";function Ac({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};Tc.push(l),Tc.length>yO&&Tc.shift();let u=nf.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)),nf.set(e,u),bO(l),l}async function bO(e){try{await nk.mkdir(ok,{recursive:!0}),await nk.appendFile(gO,JSON.stringify(e)+`
|
|
135
|
-
`)}catch{}}function sk(){let e={};for(let[t,n]of nf)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:Tc.slice(-20)}}var ok,gO,yO,Tc,nf,Ec=O(()=>{ok=process.env.BOSTRAT_STATE_DIR||rk.join(process.env.HOME||wO.tmpdir(),".bostrat"),gO=rk.join(ok,"github-latency.ndjson"),yO=100,Tc=[],nf=new Map});import{execFile as kO}from"node:child_process";import{promisify as xO}from"node:util";async function vO(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Lo("git",["-C",e,"rev-parse","HEAD"]),Lo("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await I({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function mt(e,t,n={}){let{caller:r="prs",...o}=n,s=await $t({cwd:e});rn(r);try{let{stdout:a}=await Lo("gh",t,{cwd:e,...SO,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw Jn(a)&&Yn(a),a}}async function sf(){if(_c&&Date.now()-_c.at<6e4)return _c.data;let e;try{rn("auth");let t=await Lo("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 Rs();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return _c={at:Date.now(),data:e},e}async function TO(e){try{let{stdout:t}=await Lo("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function ik(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 rf(e){let t=(e||[]).map(ik),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 cf(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function lk(e,t){if(!t)return null;let n=await de(e);if(!n)return null;try{let r=await mt(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 uk(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 dk(e,t){let n=await lk(e,t);return n===null?{...rf([]),unavailable:!0}:rf(uk(n))}function Ic(e,t=rf(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",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 EO(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){jo.delete(s);continue}r.add(s),jo.has(s)||jo.set(s,n),o.mergeabilityPending=n-jo.get(s)<AO}for(let o of jo.keys())o.startsWith(`${e}#`)&&!r.has(o)&&jo.delete(o);return t}async function Fo(e,t){let n=await on(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await mt(n.path,["pr","view",String(r),"--json",af]);return Ic(JSON.parse(o))}catch(o){if(!cf(o))throw o;let s=await mt(n.path,["pr","view",String(r),"--json",ck]),a=JSON.parse(s);return Ic(a,await dk(n.path,a.headRefOid))}}async function Nt(e,{caller:t="prs"}={}){try{let n=await mt(e,["pr","list","--json",af,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>Ic(r)).sort(ak)}catch(n){if(!cf(n))throw n;let r=await mt(e,["pr","list","--json",ck,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>Ic(o,await dk(e,o.headRefOid))))).sort(ak)}}function Bt(){da.clear()}async function RO(e,t){if(!e)return t;for(let n of t){let r=await Je(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 gc(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await bc(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function fk(){let e=await sf();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await zt())await TO(a.path)&&t.push(a);let n=Date.now(),r=Sc(n)>0,o=await Promise.all(t.map(async a=>{let i=da.get(a.path),c=i?.pending?IO:_O;if(i&&(r||n-i.at<c))return i.entry;try{let l=await de(a.path).catch(()=>null),u=EO(a.path,await Nt(a.path,{caller:"tab"}),n);if(await RO(l,u),i?.entry?.prs)for(let m of u)i.entry.prs.find(w=>w.number===m.number)?.checks?.pending>0&&m.checks.total>0&&m.checks.pending===0&&Ac({change:m.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:m.number,githubAt:m.checks.settledAt,source:"tab"});let f={name:a.name,path:a.path,prs:u},d=u.some(m=>m.mergeabilityPending||m.checks.pending>0||m.autoMergeArmed);return da.set(a.path,{at:Date.now(),entry:f,pending:d}),f}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:Pt(l)}}})),s=new Set(t.map(a=>a.path));for(let a of da.keys())s.has(a)||da.delete(a);return{auth:e,repos:o}}async function on(e){let t=(await zt()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function Lr(e){return ro(e)}async function Uo(e,t,{method:n="squash"}={}){let r=await on(e);if(!fa.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 $t({cwd:r.path})).GH_TOKEN){let a=await tu().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await mt(r.path,s)}catch(a){throw new Error(Pt(a))}finally{Bt()}return{ok:!0,number:o}}async function pa(e,t){let n=await on(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await mt(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Pt(s))}finally{Bt()}let o=await de(n.path).catch(()=>null);return o&&await Ub(o,r).catch(()=>{}),{ok:!0,number:r}}async function lf(e,t){let n=await on(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await mt(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Pt(o))}finally{Bt()}return{ok:!0,number:r}}function uf(e){return e.green&&!e.isDraft}function MO(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 pk(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:uf(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function mk(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function hk(){let e={};for(let[t,n]of of)e[t]={...n};return e}async function wk(e,{method:t="merge",onMerged:n=null}={}){let r=await on(e);if(!fa.has(t))throw new Error(`invalid merge method: ${t}`);if(of.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Nt(r.path)).filter(uf).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 of.set(r.path,s),PO(s,o,{listPrs:()=>Nt(r.path),merge:a=>Uo(r.path,a,{method:t}),sleep:CO,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function NO(e,t){let n=Date.now()+(t.settleMaxMs??OO),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($O)}}async function PO(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 NO(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: ${Pt(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(!uf(a)){e.skipped.push({number:a.number,title:a.title,reason:MO(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:Pt(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),Bt()}}async function df(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Lo("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Pt(l)}`)}await vO(e);let o=n||"";if((await $t({cwd:e})).GH_TOKEN){let l=await tu().catch(()=>null);l&&(o=`${o?`${o}
|
|
136
|
-
|
|
137
|
-
`:""}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
|
|
106
|
+
`});function yR(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 kR(e,t,n){let r=Lg(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 Un(e){let t=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=/^Agent "([\s\S]+)" finished$/,f=new Map;for(let g of e||[]){if(g.type!=="tool.called"||g.source!=="transcript")continue;let h=String(g.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(h))for(let S of h.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let I=S[0].split("/").pop().toLowerCase();f.has(I)||f.set(I,[]),f.get(I).push(g.payload?.ts??g.ts)}}let d=3*6e4,p=20*6e4,m=(g,h)=>!!(g?.name&&f.get(String(g.name).toLowerCase()))?.some(I=>I>=h-d&&I<=h+p);for(let g of e||[])if(g.type==="turn.retracted"){r.add(`${g.boxId}|${g.payload?.saidTs}`);let h=Ne(g.payload?.text);h&&(o.has(g.boxId)||o.set(g.boxId,[]),o.get(g.boxId).push(h))}else if(g.type==="turn.user"&&g.source==="orchestrator"){let h=Ne(g.payload?.text);h&&(s.has(g.boxId)||s.set(g.boxId,new Set),s.get(g.boxId).add(h))}else if(g.type==="job.started"&&g.payload?.id)a.add(g.payload.id),(g.payload.kind||"agent")==="agent"&&g.payload.name&&c.add(g.payload.name);else if(g.type==="turn.user"&&g.payload?.note?.kind==="task"){let h=g.payload.note,S={status:h.status||null,summary:h.summary||null,resultHead:h.resultHead??null,ts:ae(g)};h.toolUseId&&i.set(h.toolUseId,S);let I=String(h.summary||"").match(u);I&&l.set(I[1],S)}let w=new Map,y=new Map,b=null,x=new Map,E=new Map,_=new Map,q=new Map,C=new Map,N=new Map,T=new Map,W=new Map,H=new Map,ce=new Map,J=new Map,A=new Map,j=new Map,Q=()=>{for(let g of j.values())g.questions.length&&t.push(g);j.clear()},me=null,Re=6e4,de=new Map,le=(g,h)=>{let S=o.get(g);if(!S?.length)return!1;let I=Ne(h),fe=s.get(g);return S.some(ne=>{if(!I.startsWith(ne))return!1;let ve=Ne(I.slice(ne.length));return!!ve&&!!fe?.has(ve)})};for(let g of e||[]){let h=g.payload||{};switch(g.type){case"turn.user":{if(g.source==="hook"||!String(h.text||"").trim()&&!h.images?.length&&!h.attachments?.length)break;{let S=h.note||cc(h.text);if(S){if(S.kind==="task"){if(S.toolUseId&&a.has(S.toolUseId))break;let I=String(S.summary||"").match(u);if(!S.toolUseId&&I&&c.has(I[1]))break}if(S.kind==="command"&&Cg(S.command))break;if(S.kind==="resume-summary"){t.push({id:g.id,kind:"resume-summary",title:S.title||"Context resumed from summary",bullets:S.bullets||[],sections:S.sections||[],rawChars:S.rawChars??null,ts:ae(g),box:g.boxId,agent:g.provider});break}S.kind!=="suppress"&&t.push({id:g.id,kind:"event",type:Ng(S),name:S.name??null,command:S.command??null,summary:S.summary??null,status:S.status??null,ts:ae(g),box:g.boxId,agent:g.provider});break}}if(r.has(`${g.boxId}|${ae(g)}`)||g.source==="transcript"&&le(g.boxId,h.text))break;if(h.kind==="merge-notify"||h.kind==="ship-notify"){t.push({id:g.id,kind:"event",type:h.kind==="ship-notify"?"ship-notified":"merge-notified",text:h.text,ts:ae(g),box:g.boxId,agent:g.provider});break}if(h.kind==="mock-picked"||h.kind==="ci-fix"||h.kind==="reconcile"||h.kind==="pr-go"||h.kind==="push-go"||h.kind==="spin-up-view"||h.kind==="prompt-answer"){h.kind==="prompt-answer"&&(me=null);break}for(let S of x.values())S.status="stale";x.clear();for(let S of _.values())S.status="stale";_.clear(),me&&me.box===g.boxId&&(t.push(yR(me)),me=null),t.push({id:g.id,kind:"message",role:"user",text:h.text,ts:ae(g),box:g.boxId,agent:g.provider,boxRole:h.boxRole,...g.actor==="orchestrator"?{orchestration:h.kind||!0}:{},...h.sourceId?{sourceId:h.sourceId}:{},...h.attachments?.length?{attachments:h.attachments}:{},...h.images?.length?{images:h.images}:{}});break}case"peer.message":{let S=h.from||{};t.push({id:g.id,kind:"event",type:"peer-message",text:h.text,from:S.trackName||S.boxId||S.name||null,fromTrackId:S.trackId||null,ts:ae(g),box:g.boxId,agent:g.provider});break}case"turn.agent":{if(h.model==="<synthetic>"&&/^No response requested\.$/.test(String(h.text||"").trim()))break;let S=Jw(h,{codexFailedText:Rr});if(S){t.push({id:g.id,kind:"event",type:"provider-error",cls:S.cls,status:S.status??void 0,text:S.text,provider:g.provider,legacy:!0,at:ae(g),ts:ae(g),box:g.boxId,agent:g.provider});break}Q();let I=(h.images||[]).filter($e=>!m($e,ae(g)));if(!String(h.text||"").trim()&&!I.length)break;if(g.provider==="cursor"){let $e=`${g.boxId}|${h.text}`;if(n.has($e))break;n.add($e)}(!me||me.box===g.boxId)&&(me=null);let fe=g.provider==="codex"?(h.tools||[]).filter($e=>!Vu.has($e)&&!Js.has($e)):h.tools||[],ne=h.text,ve=null;if(hd.has(g.provider)){let $e=wd(String(h.text||""));$e&&(ne=$e.text,ve={questions:$e.questions})}t.push({id:g.id,kind:"message",role:"assistant",text:ne,ts:ae(g),box:g.boxId,agent:g.provider,boxRole:h.boxRole,...ve?{ask:ve}:{},...fe.length?{tools:fe}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},...I.length?{images:I}:{}}),ve&&(me={id:g.id,groupId:gd(g.provider,h.sourceId||g.id,ve.questions),provider:g.provider,box:g.boxId,questions:ve.questions,ts:ae(g)});break}case"job.started":{if(!h.id)break;let S=ae(g),I=i.get(h.id);if(!I&&(h.kind||"agent")==="agent"&&h.name){let v=l.get(h.name);v&&v.ts>=S&&(I=v)}let fe={jobId:h.id,jobKind:h.kind||"agent",name:h.name||null,detail:h.detail||null,status:I?I.status==="killed"?"stopped":I.status&&I.status!=="completed"?"failed":"done":"running",startedTs:S,finishedTs:I?.ts??null,summary:I?.summary??null,resultHead:I?.resultHead??null},ne=de.get(g.boxId),ve=ne?.jobs[ne.jobs.length-1]?.startedTs;if(ne&&ve!=null&&S-ve<=Re)ne.jobs.push(fe);else{let v={id:g.id,kind:"job-dock",dockId:h.id,ts:S,box:g.boxId,agent:g.provider,jobs:[fe]};de.set(g.boxId,v),t.push(v)}break}case"handoff.done":t.push(mt(g,"lead-handoff"));break;case"subtask.delegated":t.push({...mt(g,"subtask-delegated"),to:g.boxId,toAgent:g.provider});break;case"subtask.merged":t.push(mt(g,"subtask-merged"));break;case"pr.state.changed":{if(!h.change)break;if(h.change==="opened"){for(let I of x.values())I.status="stale";x.clear();for(let I of _.values())I.status="stale";_.clear()}let S=mt(g,`pr-${h.change}`);if(S.box=g.boxId??null,bR.has(h.change)&&h.number!=null){let I=kR(w,y,h);if(w.size>=2){for(let ne of y.values())for(let ve of ne)ve.grouped=!0;S.groupPrs=[...w.values()];let fe=[S];for(let ne of w.keys())y.set(ne,fe)}else{let fe=y.get(I)??[];fe.push(S),y.set(I,fe)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let I=Lg(h),fe=b&&!b.complete||w.size>=2;if(w.has(I))w.delete(I),y.delete(I);else if(h.number!=null)for(let ne of[...w.keys()])ne.endsWith(`#${h.number}`)&&(w.delete(ne),y.delete(ne));if(fe&&h.number!=null){(!b||b.complete)&&(b={id:g.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:ae(g),ts:ae(g),box:g.boxId,agent:g.provider},t.push(b)),b.prs.push({number:h.number,url:h.url??null,title:h.title??null,repo:h.repo??null,branch:h.branch??null,change:h.change,ts:ae(g)}),b.remaining=[...w.values()],b.complete=w.size===0;break}}t.push(S);break}case"pr.offered":{if(!h.offerId)break;for(let I of x.values())I.status="stale";x.clear();for(let I of _.values())I.status="stale";_.clear();let S={id:g.id,kind:"pr-offer",offerId:h.offerId,excerpt:h.excerpt||null,branch:h.branch||null,base:h.base||null,offered:Array.isArray(h.offered)&&h.offered.length?h.offered:null,toPr:h.toPr&&typeof h.toPr=="object"?{number:h.toPr.number??null}:null,available:Array.isArray(h.available)?h.available:null,defaults:Array.isArray(h.defaults)?h.defaults:null,prState:h.prState||null,openPr:h.openPr&&typeof h.openPr=="object"?h.openPr:null,repos:Array.isArray(h.repos)&&h.repos.length?h.repos:null,status:"open",ts:ae(g),box:g.boxId,agent:g.provider};x.set(h.offerId,S),E.set(h.offerId,S),t.push(S);break}case"pr.offer.answered":{let S=E.get(h.offerId);S&&(S.status=h.action||"dismiss",S.actions=Array.isArray(h.actions)?h.actions:null,S.notify=h.notify===!0,h.existingPr&&typeof h.existingPr=="object"&&(S.existingPr={number:h.existingPr.number??null}),x.delete(h.offerId));break}case"pr.exists.offered":{if(!h.offerId)break;for(let I of _.values())I.status="stale";_.clear();let S={id:g.id,kind:"pr-exists-offer",offerId:h.offerId,srcOfferId:h.srcOfferId??null,branch:h.branch||null,base:h.base||null,pr:h.pr&&typeof h.pr=="object"?{number:h.pr.number??null,url:h.pr.url??null,title:h.pr.title??null}:null,actions:Array.isArray(h.actions)&&h.actions.length?h.actions:null,notify:h.notify===!0,status:"open",ts:ae(g),box:g.boxId,agent:g.provider};_.set(h.offerId,S),q.set(h.offerId,S),t.push(S);break}case"pr.exists.answered":{let S=q.get(h.offerId);S&&(S.status=h.choice||"dismiss",_.delete(h.offerId));break}case"box.state.changed":{let S=h.change==="model"?"model-change":h.change;if(!S)break;let I=mt(g,S);if(S==="model-change"&&(I.to=h.model),S==="node-update"&&I.latest){let fe=t.findIndex(ne=>ne.kind==="event"&&ne.type==="node-update"&&ne.latest===I.latest);if(fe>=0){let ne=t[fe];t[fe]={...I,ts:ne.ts,at:ne.at,firstAt:ne.firstAt??ne.ts,updatedAt:I.ts};break}}t.push(I);break}case"track.transferred":case"track.recovered":t.push(mt(g,"track-moved"));break;case"view.state.changed":{if(h.change!=="created"||h.restarted)break;t.push({id:g.id,kind:"view-card",viewId:h.viewId,name:h.name,slug:h.slug,port:h.port,origin:h.origin,ts:ae(g),box:g.boxId,agent:g.provider});break}case"view.spinup.failed":{t.push({id:g.id,kind:"view-spinup-failed",name:h.name||null,reason:h.reason||"no-view",ts:ae(g),box:g.boxId,agent:g.provider});break}case"mock.state.changed":{if(h.change==="picked"){t.push({id:g.id,kind:"mock-picked",mockId:h.mockId,name:h.name,group:h.group??null,version:h.version??null,siblings:h.siblings??[],ts:ae(g),box:g.boxId,agent:g.provider});break}if(h.change!=="created")break;t.push({id:g.id,kind:"mock-card",mockId:h.mockId,name:h.name,group:h.group??null,ts:ae(g),box:g.boxId,agent:g.provider});break}case"flow.state.changed":{if(h.change!=="created")break;t.push({id:g.id,kind:"flow-card",flowId:h.flowId,name:h.name,stepCount:h.stepCount??null,mockIds:h.mockIds??[],ts:ae(g),box:g.boxId,agent:g.provider});break}case"runbook.state.changed":{if(h.change!=="created")break;t.push({id:g.id,kind:"runbook-card",runbookId:h.runbookId,name:h.name,ts:ae(g),box:g.boxId,agent:g.provider});break}case"ticket.linked":{t.push({id:g.id,kind:"ticket-card",provider:h.provider||"linear",key:h.key,url:h.url,title:h.title??null,linkSource:h.linkSource??null,ts:ae(g),box:g.boxId,agent:g.provider});break}case"box.fullscreen.detected":{if(!h.offerId)break;for(let I of T.values())I.box===(h.box||g.boxId)&&(I.status="stale",T.delete(I.offerId));let S={id:g.id,kind:"fullscreen-offer",offerId:h.offerId,status:"open",ts:ae(g),box:h.box||g.boxId,agent:h.agent||g.provider};T.set(h.offerId,S),W.set(h.offerId,S),t.push(S);break}case"box.fullscreen.answered":{let S=W.get(h.offerId);S&&(S.status=h.action==="restarted"?"restarted":h.action==="cleared"?"cleared":"dismiss",T.delete(h.offerId));break}case"ticket.move.offered":{if(!h.offerId)break;for(let I of C.values())I.key===h.key&&(I.status="stale",C.delete(I.offerId));let S={id:g.id,kind:"ticket-offer",offerId:h.offerId,provider:h.provider||"linear",key:h.key,url:h.url??null,phase:h.phase??null,from:h.from??null,to:h.to?.name??null,prNumber:h.prNumber??null,status:"open",ts:ae(g),box:g.boxId,agent:g.provider};C.set(h.offerId,S),N.set(h.offerId,S),t.push(S);break}case"ticket.move.answered":{let S=N.get(h.offerId);S&&(S.status=h.action==="moved"?"moved":"dismiss",C.delete(h.offerId));break}case"ticket.state.changed":{for(let S of C.values())S.key===h.key&&S.to===h.to&&(S.status="stale",C.delete(S.offerId));if(h.mode==="offer")break;t.push(mt(g,"ticket-state"));break}case"ticket.commented":{t.push({id:g.id,kind:"ticket-comment",key:h.key,url:h.url??null,excerpt:h.excerpt??null,ts:ae(g),box:g.boxId,agent:g.provider});break}case"ticket.flow.changed":t.push(mt(g,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let I of H.values())I.status="stale";H.clear();let S={id:g.id,kind:"ticket-create-offer",proposalId:h.proposalId,provider:h.provider||"linear",epic:h.epic??null,issues:Array.isArray(h.issues)?h.issues:[],team:h.team??null,needsTeam:!!h.needsTeam,linkCurrentTrack:!!h.linkCurrentTrack,status:"open",ts:ae(g),box:g.boxId,agent:g.provider};H.set(h.proposalId,S),ce.set(h.proposalId,S),t.push(S);break}case"ticket.create.answered":{let S=ce.get(h.proposalId);S&&(S.status=h.action==="created"?"created":"dismiss",S.createdCount=h.createdCount??null,S.failedCount=h.failedCount??null,H.delete(h.proposalId));break}case"loop.create.offered":{if(!h.proposalId)break;for(let I of J.values())I.status="stale";J.clear();let S={id:g.id,kind:"loop-create-offer",proposalId:h.proposalId,name:h.name??"",schedule:h.schedule??"",prompt:h.prompt??"",expectation:h.expectation??null,notify:h.notify??"off",assignment:h.assignment??{provider:"claude"},repo:h.repo??null,status:"open",ts:ae(g),box:g.boxId,agent:g.provider};J.set(h.proposalId,S),A.set(h.proposalId,S),t.push(S);break}case"loop.create.answered":{let S=A.get(h.proposalId);S&&(S.status=h.action==="created"?"created":"dismiss",J.delete(h.proposalId));break}case"loop.created":{let S=h.proposalId?A.get(h.proposalId):null;S&&(S.created={loopId:h.loopId??null,nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null});break}case"ticket.created":{let S={items:Array.isArray(h.items)?h.items:[],failed:Array.isArray(h.failed)?h.failed:[],team:h.team??null},I=h.proposalId?ce.get(h.proposalId):null;if(I){I.created=S;break}t.push({id:g.id,kind:"ticket-created",...S,ts:ae(g),box:g.boxId,agent:g.provider});break}case"error":{if(!h.kind)break;t.push(mt(g,h.kind));break}case"provider.error":t.push({...mt(g,"provider-error"),provider:g.provider,box:g.boxId,agent:g.provider});break;case"usage.cap.hit":t.push(mt(g,"usage-cap-hit"));break;case"usage.cap.warning":t.push(mt(g,"usage-cap-warning"));break;case"provider.connected":{t.push({id:g.id,kind:"event",type:"provider-connected",provider:h.provider||g.provider,ts:ae(g),at:ae(g),box:g.boxId,agent:g.provider});break}case"prompt.answered":{if(me=null,h.control||!h.question)break;let S=h.groupId||h.promptId;if(!S)break;let I=j.get(S);I||(I={id:g.id,kind:"prompt",groupId:S,provider:g.provider,box:g.boxId,agent:g.provider,questionTotal:h.questionTotal??1,questions:[],ts:ae(g)},j.set(S,I));let fe=h.questionIndex??0,ne={index:fe,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},ve=I.questions.findIndex(v=>v.index===fe&&Ne(v.question)===Ne(ne.question));ve>=0?I.questions[ve]=ne:I.questions.push(ne),I.questions.sort((v,V)=>v.index-V.index),I.questionTotal=Math.max(I.questionTotal,h.questionTotal??1),I.questions.length>=I.questionTotal&&(t.push(I),j.delete(S));break}default:break}}return SR(t.sort((g,h)=>(g.ts??0)-(h.ts??0)))}function SR(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<=xR){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 kd(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:vR(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??Un(e)).filter(o=>o.kind==="message");return Uw(n,r)}function vR(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==Ao(e.command)?t:jn(e.command,e.description||"")}function mt(e,t){let{ts:n,change:r,...o}=e.payload||{};return{id:e.id,kind:"event",type:t,...o,at:ae(e),ts:ae(e)}}function Fg(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)+TR,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&AR.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function Ug(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!_R.has(r.type)||!r.box)continue;let o=(r.ts??0)+ER,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 Hg(e){return(e||[]).filter(t=>IR.has(t?.kind)&&t.status==="open")}var ae,bR,Lg,xR,TR,AR,ER,_R,IR,xd=O(()=>{Eo();$r();Ji();ld();md();bd();ae=e=>e.payload?.ts??e.ts;bR=new Set(["opened","ci-fixed","reconciled","pushed"]),Lg=e=>`${e.repo??""}#${e.number}`;xR=2*6e4;TR=12e4,AR=new Set(["archived","unarchived","opened"]);ER=2500,_R=new Set(["pr-opened","pr-merged","pr-auto-armed"]);IR=new Set(["pr-offer","pr-exists-offer","ticket-offer","ticket-create-offer","loop-create-offer","fullscreen-offer"])});function MR(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 PR(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 BR(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<OR&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<CR&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function Gg(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=MR(e),s=PR(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=Un(e??[]).filter(d=>d?.kind==="message"&&typeof d.text=="string"&&d.text.trim()).slice(-RR);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(Kg[r]??Kg.failover),l.push(""),o.lastPush?.sha){let d=qg(n,o.lastPush.ts),p=d?` (~${d} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${p}`+(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 d=qg(n,i);l.push(`## Last real checkpoint (written by the previous agent${d?`, ~${d} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=BR(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(d=>`\`${d}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(d=>`\`${d}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let d of c)l.push(`**${d.role==="user"?"operator":"agent"}:** ${NR(d.text,$R)}`),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 f=l.join(`
|
|
107
|
+
`);return f.length<=Wg?f:`${f.slice(0,Wg)}
|
|
108
|
+
|
|
109
|
+
[\u2026 brief truncated]`}var RR,$R,Wg,OR,CR,NR,qg,Kg,Vg=O(()=>{xd();RR=12,$R=2e3,Wg=48e3,OR=30,CR=10;NR=(e,t)=>{let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1)}\u2026`},qg=(e,t)=>e&&t?Math.max(1,Math.round((e-t)/6e4)):null;Kg={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.",transfer:"**This Track was MOVED to a new machine while it was alive.** The previous node checkpointed and pushed its work as part of the move; this workspace was re-created from that push (uncommitted work re-applied when possible). **Run git status and git log to confirm what carried over**, then continue the objective below."}});import DR from"node:fs";import Hn from"node:fs/promises";import Wn from"node:path";function ey(e){let t=Ar(String(e??"")),n=Buffer.byteLength(t,"utf8");return n<=zg?{content:t,bytes:n,truncated:!1}:{content:`${Buffer.from(t,"utf8").subarray(0,zg).toString("utf8").replace(/�+$/,"")}
|
|
110
|
+
|
|
111
|
+
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function GR(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(Xg.test(s)){if(r.length>=ay)continue;let a=KR(s.replace(Xg,"").replace(/[.;,:]+$/,""),iy,WR);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>Jg&&(o=`${o.slice(0,Jg-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function cy(e,t,{timeoutMs:n=9e4}={}){let r=Wn.join(t,UR),o=Wn.join(r,HR);await Hn.mkdir(r,{recursive:!0});let s=await lc(o),a=await se(e);if(!a)throw new Error(`session "${e}" not found`);return await uc(e,a,qR(o)),await hy(o,s,n),GR(await Hn.readFile(o,"utf8"))}function ly(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===ny||n===sy;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(ty)||n===jR||r.startsWith(ry)||r.startsWith(oy)}function uy(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-VR;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 dy(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 fy(e,{windows:t=[]}={}){if(ly(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 dc(e){return Sd.get(e)||null}async function py(e){let t=Sd.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:Wn.join(n.cwd,Zg,Qg)};return Sd.set(e,r),XR(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function XR(e,t){e.step="summarize",await my(e.name,t),e.step="clear";let n=await se(e.name);await uc(e.name,n,"/clear"),await Ad(zR),e.step="resume",await uc(e.name,n,FR),e.step="done",e.finishedAt=Date.now()}async function my(e,t,{timeoutMs:n=JR}={}){let r=Wn.join(t,Zg),o=Wn.join(r,Qg);await Hn.mkdir(r,{recursive:!0});let s=await lc(o),a=await se(e);await z({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await uc(e,a,LR(o)),await hy(o,s,n),await z({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await lc(o)});let i=await kg(t,o,{exclude:u=>ly(u.role,u.text)});await z({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(c){await R({id:c,cwd:t},{type:"memory.seed",actor:"orchestrator",payload:{...i,tokensEst:i.chars?Math.round(i.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let u=await Hn.readFile(o,"utf8");await R({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...ey(u),ts:Date.now()}})}catch{}}await ow(t);let l=await YR(r,o);return c&&await hw(c,l||o,{box:e,agent:a?.agent??null}),o}async function vd(e,{timeoutMs:t}={}){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}"`);return my(e,n.cwd,t?{timeoutMs:t}:void 0)}async function Td(e,t,{trackName:n="this Track"}={}){let r=await B({id:e,cwd:t}),o=Gg(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=Wn.join(t,Yg);return await Hn.mkdir(Wn.dirname(s),{recursive:!0}),await Hn.writeFile(s,o,"utf8"),await R({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...ey(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),Yg}async function lc(e){try{return(await Hn.stat(e)).mtimeMs}catch{return 0}}async function hy(e,t,n){let r=Date.now()+n;for(;;){if(await lc(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await Ad(2e3)}}async function YR(e,t){try{if(!DR.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=Wn.join(e,`${n}.md`);return await Hn.copyFile(t,r),r}catch{return null}}function Ad(e){return new Promise(t=>setTimeout(t,e))}async function uc(e,t,n){if(t){let o=t.agent==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":t.agent==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":t.agent==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":"";await Fn({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 Oe(e,["Escape"],{kind:"drive-wipe"}),await Ad(150),await xo(e,Gi,{kind:"drive-wipe"}),await je(e,n,{kind:"orchestration-drive",...Pt(r.agent)})}var Zg,Qg,zg,ty,ny,ry,jR,LR,FR,oy,sy,UR,HR,Jg,ay,iy,WR,qR,Xg,KR,VR,zR,JR,Sd,Yg,Ed=O(()=>{Je();nc();Zt();ta();xg();Mi();ue();ji();Vg();Zg=".bostrat/handoff",Qg="latest.md",zg=32*1024;ty="Checkpoint: write a work-in-progress handoff",ny="HANDOFF WRITTEN",ry="Resume from .bostrat/handoff/latest.md",jR="/clear",LR=e=>`${ty} 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 ${ny} and take no further action.`,FR=`${ry}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,oy="Beat: write a",sy="BEAT WRITTEN",UR=".bostrat/arc",HR="beat.txt",Jg=200,ay=4,iy=5,WR=36,qR=e=>`${oy} 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 ${ay} further lines, each starting with "- " and naming ONE piece in at most ${iy} 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 ${sy} and take no further action.`,Xg=/^(?:[-–—*•·]|\d+[.)])\s+/,KR=(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};VR=6e5;zR=3500,JR=3e5,Sd=new Map;Yg=".bostrat/handoff/status-latest.md"});function gy(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!ZR.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 QR(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:gy(o.from,"from",n),toMs:gy(o.to,"to",n)}))}return t}function t$(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 n$(e){if(!e)return null;let t=String(e).trim();return _d[t]?t:wy[t]?wy[t]:Object.keys(_d).find(r=>t.startsWith(r))||null}function yy(e,t){let n=n$(e);if(!n)return null;let r=t$(t);return r===null?null:e$[n].find(o=>r>=o.fromMs&&r<=o.toMs)||null}function qn(e,t={},n){let r=yy(e,n);return r?((t.input||0)*r.input+(t.output||0)*r.output+(t.cacheWrite||0)*r.cacheWrite+(t.cacheRead||0)*r.cacheRead)/1e6:0}var _d,wy,ZR,e$,by,ia=O(()=>{_d={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,note:"GA rates; verified 2026-08-06 via the claude-api skill."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,note:"verified 2026-08-06 via the claude-api skill."}],"claude-sonnet-5":[{from:null,to:"2026-08-31",input:2,output:10,cacheWrite:4,cacheRead:.2,note:"GA introductory pricing ($2/$10); verified 2026-08-06 via the claude-api skill."},{from:"2026-09-01",to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,note:"standard rates resume after the intro window; verified 2026-08-06."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,note:"verified 2026-08-06 via the claude-api skill."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,note:"verified 2026-08-06 via the claude-api skill."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,note:"verified 2026-08-06 via the claude-api skill; 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,note:"verified 2026-07-03; no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:null,input:5,output:30,cacheWrite:6.25,cacheRead:.5,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:null,input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:null,input:1,output:6,cacheWrite:1.25,cacheRead:.1,note:"verified 2026-07-18; cacheWrite = 1.25\xD7 input (30-min TTL)."}]},wy={opus:"claude-opus-5",opusplan:"claude-opus-5",default:"claude-opus-5",sonnet:"claude-sonnet-5",haiku:"claude-haiku-4-5",fable:"claude-fable-5","gpt-5.6":"gpt-5.6-sol"},ZR=/^\d{4}-\d{2}-\d{2}$/;e$=QR(_d);by=(e,t)=>yy(e,t)!==null});function r$(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function xy(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function tn(e){let t=e.payload?.model||"unknown",n=r$(e.payload?.tokens),r=Number(e.payload?.usd)||0,o=e.source==="vendor",s=r,a=!1;if(n&&!o){let l=qn(t,n,e.ts);l>0&&(s=l,a=Math.abs(l-r)>1e-9)}let i=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,c=!s&&i>0&&!by(t,e.ts);return{usd:s,model:t,tokens:n,repriced:a,unpriced:c,asRecorded:r}}function Kn(e,{boxLabels:t={}}={}){let n=(e||[]).filter(d=>d.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,f=ky();for(let d of n){let{usd:p,model:m,tokens:w,repriced:y,unpriced:b,asRecorded:x}=tn(d);i+=x;let E=d.provider||"unknown",_=d.boxId||"unattributed";if(y&&c++,b&&(l++,u.add(m)),a+=p,xy(f,w),r[m]||={usd:0,tokens:ky(),events:0},r[m].usd+=p,r[m].events+=1,xy(r[m].tokens,w),o[E]||={usd:0,events:0},o[E].usd+=p,o[E].events+=1,s[_]||={usd:0,events:0,provider:E,models:new Set,label:t[_]||_},s[_].usd+=p,s[_].events+=1,m!=="unknown"&&s[_].models.add(m),d.epoch!=null){let q=(s[_].epochs||={})[d.epoch]||={usd:0,events:0,provider:E,models:new Set};q.usd+=p,q.events+=1,q.provider=E,m!=="unknown"&&q.models.add(m)}}return{totalUsd:$o(a),asRecordedUsd:$o(i),totalTokens:f,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:fc(r,d=>({...d,usd:$o(d.usd)})),byProvider:fc(o,d=>({...d,usd:$o(d.usd)})),byBox:fc(s,d=>({usd:$o(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:fc(d.epochs,p=>({usd:$o(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var ky,$o,fc,Mr=O(()=>{ia();ky=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});$o=e=>Math.round(e*1e4)/1e4,fc=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function o$(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 Sy(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 f=u.payload||{};!f.repo||f.number==null||(f.change==="merged"?o.set(`${f.repo}#${f.number}`,f):f.change==="opened"&&s++)}if(o.size){let u=[...o.keys()].map(f=>{let[d,p]=f.split("#");return`${d.split("/").pop()}#${p}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(Id(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Id(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(f=>`${f.fromAgent}\u2192${f.toAgent}`).join(", ");n.push(`${Id(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=Kn(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?o$(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Id,vy=O(()=>{Mr();Id=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as s$}from"node:child_process";import{promisify as a$}from"node:util";import{randomUUID as i$}from"node:crypto";async function Rd(e,t){let n=Date.now()+t;for(;;){if(await ko(e))return!0;if(Date.now()>n)return!1;await pc(l$)}}async function d$(e,t){let n=Ay[t]||Ay.default;n.escape&&(await Oe(e,["Escape"],{kind:"swap-exit"}),await pc(150)),n.wipe&&await xo(e,Gi,{kind:"swap-exit"}),await Vi(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Pt(t).submitDelayMs||0;await pc(Math.max(r,250)),await Oe(e,["Enter"],{kind:"swap-exit"})}async function f$(e,t){try{let{stdout:n}=await Oo(L,U("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function Ey(e){await Oo(L,U("respawn-pane","-k","-t",e))}async function $d(e,t={}){if(!Ee(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||Pn(n),a=Number(t.exitTimeoutMs)||c$;await z({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let i=!1;if(!await ko(e)&&(await d$(e,o),!await Rd(e,a)&&(i=!0,await z({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await Ey(e),!await Rd(e,Ty))))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);let c=s==="claude"&&Er(n),l=c?vi():null,u=await f$(e,"ANTHROPIC_API_KEY"),f=!1;try{l?(await Oo(L,U("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await Oo(L,U("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),f=!0)}catch(y){throw l?Fi(y,l):y}if(f&&!i&&(await Ey(e),!await Rd(e,Ty)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let d=!!t.resumeSessionId,p=t.resumeSessionId||i$(),m=d?n:Mu(n,p,e),w=d?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await Rn(r.cwd),s==="codex"&&await lo(r.cwd),s==="cursor"&&await uo(r.cwd),await _r(e),await Oo(L,U("set-option","-t",e,"@bostrat-sid",p)),await Oo(L,U("set-option","-t",e,"@bostrat-agent",s)),await pc(u$),await je(e,m,{kind:"swap-launch"}),await z({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:p,forced:i,envRespawned:f}),{name:e,cwd:r.cwd,sessionId:p,agent:s,pinned:w,forced:i,envRespawned:f}}var Oo,pc,c$,Ty,l$,u$,Ay,_y=O(()=>{Je();Ms();Ai();Ei();ta();Zt();Oo=a$(s$),pc=e=>new Promise(t=>setTimeout(t,e)),c$=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Ty=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,l$=250,u$=400,Ay={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"}}});async function Iy(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await tt(e,t)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
|
|
112
|
+
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.isSidechain)continue;let u=l.message?.content;if(!Array.isArray(u))continue;let f=l.timestamp?Date.parse(l.timestamp):null;for(let d of u)if(!(!d||typeof d!="object"))if(d.type==="tool_use"&&d.id){if(d.name==="Agent"){let p=d.input||{};o.set(d.id,{id:d.id,kind:"agent",name:String(p.description||"").trim()||null,detail:String(p.subagent_type||"").trim()||null,ts:f,proven:!1})}else if(d.name==="Bash"&&d.input?.run_in_background===!0){let p=String(d.input.command||"").trim();o.set(d.id,{id:d.id,kind:"command",name:String(d.input.description||"").trim()||(p?Ao(p):null),detail:p?Ao(p):null,ts:f,proven:!0})}}else d.type==="tool_result"&&d.tool_use_id&&p$.test(m$(d.content))&&s.add(d.tool_use_id)}let a=[];for(let i of o.values()){if(!i.proven&&!s.has(i.id))continue;let{proven:c,...l}=i;a.push(l)}return a.slice(-n)}var p$,m$,Ry=O(()=>{vt();Eo();p$=/^Async (?:agent|task) launched/i,m$=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 Gn(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 h$(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 $y(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:Tt(`read ${Gn(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${Co(r)}`:`Read ${Co(r)}`}}case"Grep":{let r=t?.pattern;if(!r)return null;let o=t?.path?Gn(t.path,n):null,s=t?.glob?` --glob ${Od(t.glob)}`:"",a=Tt(String(r),40);return{command:Tt(`grep ${Od(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?Gn(t.target_directory,n):null;return{command:Tt(`glob ${Od(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${Tt(String(r),40)}\u201D`}}case"StrReplace":return t?.path?{command:Tt(`edit ${Gn(t.path,n)}`,140),description:`Edit ${Co(t.path)}`}:null;case"Write":return t?.path?{command:Tt(`write ${Gn(t.path,n)}`,140),description:`Write ${Co(t.path)}`}:null;case"Delete":return t?.path?{command:Tt(`delete ${Gn(t.path,n)}`,140),description:`Delete ${Co(t.path)}`}:null;case"ReadLints":{let o=(Array.isArray(t?.paths)?t.paths:[]).map(s=>Gn(s,n)).filter(Boolean);return{command:Tt(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=h$(t);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:Tt(`apply_patch ${Gn(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${Co(r[0])}`}}case"Await":{let r=t?.shell_id??t?.task_id;return{command:Tt(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var mc,Tt,Co,Od,Oy=O(()=>{mc=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),Tt=(e,t=88)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,Co=e=>String(e).replace(/\/+$/,"").split("/").pop()||String(e);Od=e=>`"${String(e).replace(/"/g,'\\"')}"`});import ca from"node:fs";import w$ from"node:fs/promises";import Nr from"node:path";import g$ from"node:os";import y$ from"node:crypto";import b$ from"node:zlib";function Ny(e){let t=y$.createHash("md5").update(String(e)).digest("hex");return Nr.join(k$,t)}function Py(e){try{let t=JSON.parse(ca.readFileSync(Nr.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 x$(e){let t=Ny(e),n;try{n=ca.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=Nr.join(t,o.name),a=Nr.join(s,"store.db");if(!ca.existsSync(a))continue;let i=Py(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 hc(e,t){if(!e)return null;if(t){let n=Nr.join(Ny(e),t),r=Nr.join(n,"store.db");if(ca.existsSync(r))return{id:t,dir:n,store:r,...Py(n)||{createdAtMs:null,updatedAtMs:null}}}return x$(e)}function Cy(e,t){let n=e.prepare("SELECT data FROM blobs WHERE id = ?").get(t);return n?Buffer.from(n.data):null}function S$(e){if(!e)return null;let t=e;if(t.length>1&&t[0]===31&&t[1]===139)try{t=b$.gunzipSync(t)}catch{}try{return JSON.parse(t.toString("utf8"))}catch{return null}}function v$(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 T$(e,t){if(!Array.isArray(e))return[];let n=[];for(let r of e){if(!r||r.type!=="tool-call"||!mc.has(r.toolName))continue;let o=$y(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 Cd(e,t){if(!My)return Mo;let{DatabaseSync:n}=My,r;try{r=new n(e,{readOnly:!0})}catch{return Mo}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 Mo;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return Mo}let a=s.lastUsedModel||null,i=s.latestRootBlobId?Cy(r,s.latestRootBlobId):null;if(!i)return{...Mo,model:a};let c=[],l=[],u=[],f=null,d=[];for(let p of v$(i)){let m=S$(Cy(r,p));if(!m)continue;let w=m.providerOptions?.cursor;if(w?.requestId&&d.push(w.requestId),w?.modelProviderMessageId&&d.push(w.modelProviderMessageId),m.role!=="assistant")continue;if(Array.isArray(m.content))for(let x of m.content){let E=x?.providerOptions?.cursor?.modelName;E&&(f=E)}l.push(...T$(m.content,t).map((x,E)=>({...x,id:x.id||`${p}:${E}`}))),u.push(...E$(m.content));let y=A$(m.content);if(!y)continue;let b=[...new Set(u)];u=[],c.push({id:p,text:y,tools:b.length?b:void 0,commands:l,model:f}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:d}}catch{return Mo}finally{try{r.close()}catch{}}}function By(e,t){let n=e.updatedAtMs;if(!n)try{n=Math.round(ca.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 Dy(e,t,{limit:n=80}={}){let r=hc(e,t);if(!r)return[];let{turns:o,model:s}=await Cd(r.store,e);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=By(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 jy(e,t){let n=hc(e,t);if(!n)return null;let{turns:r,requestIds:o,model:s}=await Cd(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 Ly(e,t,{limit:n=80}={}){let r=hc(e,t);if(!r)return[];let{turns:o,trailingCommands:s}=await Cd(r.store,e);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=By(r,a.length||1),l=[];a.forEach((u,f)=>{for(let d of u.commands)l.push({...d,summary:jn(d.command,d.description),ts:c(f)})});for(let u of s)l.push({...u,summary:jn(u.command,u.description),ts:i+1});return l}async function Fy(e,t){let n=hc(e,t);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await w$.stat(n.store)).mtimeMs)}catch{return null}}var k$,A$,E$,My,Mo,Md=O(()=>{Eo();Oy();k$=process.env.BOSTRAT_CURSOR_CHATS||Nr.join(g$.homedir(),".cursor","chats");A$=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(`
|
|
113
|
+
`).trim():"",E$=e=>Array.isArray(e)?e.filter(t=>t&&t.type==="tool-call"&&t.toolName).map(t=>t.toolName):[],My=process.getBuiltinModule?.("node:sqlite"),Mo={turns:[],trailingCommands:[],model:null,requestIds:[]}});import _$ from"node:fs/promises";function Dd(e){return Uy.set(e.agent,e),e}function la(e){return Uy.get(e)||I$}async function Ld(e,t={}){return(await la(e.agent).readMessages(e,t)).map(jd(e))}async function Hy(e,t={}){return(await la(e.agent).readCommands(e,t)).map(jd(e))}async function Wy(e,t={}){return(await la(e.agent).readJobs(e,t)).map(jd(e))}var No,Nd,Pd,Bd,I$,Uy,jd,qy=O(()=>{vt();Xi();Eo();Ry();Md();$r();No=class{constructor(t,n=new Set){this.agent=t,this.cliTools=n}async readMessages(){return[]}async readCommands(){return[]}async readJobs(){return[]}async updatedAt(){return null}},Nd=class extends No{transcript(t){return!t?.cwd||!t?.sessionId?null:en(t.cwd,t.sessionId)}async readMessages(t,n){let r=this.transcript(t);return r?Ju(r,n):[]}async readCommands(t,n){let r=this.transcript(t);return r?Fw(r,n):[]}async readJobs(t,n){let r=this.transcript(t);return r?Iy(r,n):[]}async updatedAt(t){let n=this.transcript(t);if(!n)return null;try{return Math.round((await _$.stat(n)).mtimeMs)}catch{return null}}},Pd=class extends No{async readMessages(t,n){return Dy(t.cwd,t.sessionId,n)}async readCommands(t,n){return Ly(t.cwd,t.sessionId,n)}async updatedAt(t){return Fy(t.cwd,t.sessionId)}},Bd=class extends No{sessionId(t){return t?.pinned?t.sessionId:null}async readMessages(t,n){return qw(t.cwd,this.sessionId(t),n)}async readCommands(t,n){return Kw(t.cwd,this.sessionId(t),n)}async updatedAt(t){return Gw(t.cwd,this.sessionId(t))}},I$=new No("unknown"),Uy=new Map;Dd(new Nd("claude",qu));Dd(new Pd("cursor",mc));Dd(new Bd("codex",Js));jd=e=>t=>({...t,box:e.name,agent:e.agent,boxRole:e.role})});function Dr(e){let t=e.search(Wd);return(t>=0?e.slice(0,t):e).trim()}function R$(e){if(!e)return"";let t=e.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!t||/hidden|\d+\s+lines?/i.test(t)?"":t}function $$(e){let t=String(e).trim();return!!(!t||Br.test(e)||Wd.test(t)||/hidden|\d+\s+lines?/i.test(t)||/notespress|press n to add/i.test(t)||/^\s*notes?\s*$/i.test(t))}function O$(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=C$(n,t);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function C$(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(d=>d.length>3&&n.includes(d)).length;if(u)return u;let f=l.slice(0,8).trim();return f.length>=4&&n.includes(f)?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 Gy(e){return e.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function M$(e,t=""){let n=String(e);if(!zy.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
114
|
+
`),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 f=r[u].match(Pr);if(f)s.push({cursor:!!f[1],label:f[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let d=r[u].trim();d&&!Br.test(r[u])&&a.push(d)}}if(s.length<2)return null;let i=s.map(u=>({label:Dr(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 P$(e,t=""){let n=String(e).replace(/\r/g,"").split(`
|
|
115
|
+
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!Jy.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(Pr);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&&!(N$.test(n[c])||Br.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:Dr(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 B$(e,t=""){let n=String(e);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
116
|
+
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(Pr);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 j$(e,t=""){let n=String(e);if(!Fd.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
117
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(D$.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 f=r[u].match(Pr);if(f)s.push({cursor:!!f[1],label:f[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let d=r[u].trim();d&&!Br.test(r[u])&&a.push(d)}}if(s.length<2||!s.some(u=>Fd.test(u.label)))return null;let i=s.map(u=>({label:Dr(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 qd(e){let t=[];for(let r=0;r<e.length;r++){let o=e[r].match(Pr);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 L$(e,t=""){let n=String(e);if(!Xy.test(n)||!Yy.test(n)||jr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
118
|
+
`),o=qd(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:Dr(s.label),description:""}))}]}:null}function F$(e,t=""){let n=String(e);if(!Zy.test(n)||jr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
119
|
+
`),o=qd(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:Dr(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 H$(e,t){let n=e[t].trim();for(let r=t+1;r<e.length;r++){let o=e[r].trim();if(!o||!U$.test(o)||Kd.test(e[r]))break;n+=o}return n}function W$(e,t=""){let n=String(e);if(!Kd.test(n)||jr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
120
|
+
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=H$(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 K$(e,t=""){let n=String(e);if(!Ud.test(n)||!q$.test(n)||jr.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
121
|
+
`)].reverse().find(a=>Ud.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 G$(e,t=""){let n=String(e);if(!Qy.test(n)||jr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
122
|
+
`),o=qd(r);return o?{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"trust",promptId:`claude:setup:trust:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks whether it can read, edit, and run files in this workspace. This box's workspace is bostrat-managed scratch space, so trusting it is safe.",header:"Folder trust",multiSelect:!1,options:o.map(s=>({label:Dr(s.label),description:""}))}]}:null}function V$(e,t=""){let n=String(e);if(jr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
123
|
+
`),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(eb);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(Br.test(r[l])||Wd.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 z$(e){let t=-1;for(let r=e.length-1;r>=0;r--)if(Hd.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 J$(e,t=""){let n=String(e);if(!Vy.test(n)||!Hd.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
124
|
+
`),o=z$(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let T=i;T<c;T++){let W=r[T].match(Pr);W&&(u<0&&(u=T),l.push({line:T,cursor:!!W[1],checked:W[3]!=null&&W[3]!==" ",hasCheckbox:W[3]!=null,label:W[4].trim(),desc:""}))}if(!l.length)return null;for(let T of l)for(let W=T.line+1;W<Math.min(r.length,T.line+4)&&!(Pr.test(r[W])||Br.test(r[W]));W++){let H=r[W].trim();if(!(!H||$$(r[W])||Ky.test(H))){T.desc=R$(H);break}}let f=l.filter(T=>!Ky.test(T.label));if(!f.length)return null;let d=f.some(T=>T.hasCheckbox),p=Math.max(0,f.findIndex(T=>T.cursor)),m=s>=0?s+1:0,w=[];for(let T=m;T<u;T++){let W=r[T].trim();W&&!Br.test(r[T])&&!/[☐☒]/.test(r[T])&&w.push(W)}let y=w.join(" ")||(s>=0?Gy(r[s]):"")||"The agent is asking a question",b=s>=0?O$(r[s],y):null,x=b?.currentTabLabel||(s>=0?Gy(r[s]):""),E=f.map(T=>({label:Dr(T.label),description:T.desc})),_=E.map(T=>T.label).join("|"),C=`claude:${b?`q${b.questionIndex+1}/${b.questionTotal}`:"q1/1"}:${x}:${y}:${_}`.slice(0,240),N=b?`claude:ask:${b.tabs.map(T=>T.label).join("|")}`.slice(0,240):C;return{provider:"claude",box:t,multiSelect:d,cursorIndex:p,promptId:C,groupId:N,...b?{questionIndex:b.questionIndex,questionTotal:b.questionTotal,currentTabLabel:b.currentTabLabel,tabs:b.tabs}:{},questions:[{question:y,header:x,multiSelect:d,options:E}]}}function Lr(e,t){let n=String(t||"");return n?e==="claude"?Vy.test(n)&&Hd.test(n)||zy.test(n)||Fd.test(n)||Jy.test(n.trimEnd().split(`
|
|
125
|
+
`).pop()||"")?!0:jr.test(n)?!1:Xy.test(n)&&Yy.test(n)||Zy.test(n)||Kd.test(n)||Ud.test(n)||Qy.test(n)||eb.test(n.trimEnd().split(`
|
|
126
|
+
`).pop()||""):e==="codex"&&/Sign in with ChatGPT|one-time code|Paste or type your API key|link to authenticate|Successfully logged in\.[\s\S]*Press Enter to continue\./i.test(n)?!0:e==="cursor"||e==="codex"?/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i.test(n):!1:!1}function Vn(e,t){return!e||!t?null:e.agent==="claude"?B$(t,e.name)||J$(t,e.name)||M$(t,e.name)||j$(t,e.name)||K$(t,e.name)||W$(t,e.name)||F$(t,e.name)||L$(t,e.name)||G$(t,e.name)||V$(t,e.name)||P$(t,e.name):e.agent==="cursor"||e.agent==="codex"?ig(e.agent,t,e.name):null}async function Gd(e){if(!e)return null;let t=await Qt(e.name).catch(()=>"");return Vn(e,t)}function tb(e,t){let n=Array.isArray(t?.[0])?t[0].filter(a=>Number.isInteger(a)&&a>=0):[],r=Number.isInteger(e?.cursorIndex)?e.cursorIndex:0,o=(a,i)=>{let c=i-a;return Array.from({length:Math.abs(c)},()=>c>0?"Down":"Up")},s=[];if(e?.multiSelect){let a=r;for(let i of[...n].sort((c,l)=>c-l))s.push(...o(a,i),"Enter"),a=i;s.push("Tab","Enter")}else{let a=n.length?n[0]:0;s.push(...o(r,a),"Enter")}return s}function nb(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 Vy,Hd,Pr,Ky,Br,Wd,zy,Jy,N$,Fd,D$,jr,Xy,Yy,Zy,Kd,U$,Ud,q$,Qy,eb,Vd=O(()=>{Je();Zi();Vy=/to navigate\b.*\bEsc to cancel|Enter to select\b/,Hd=/Chat about this/i,Pr=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Ky=/^(type something|chat about this|submit)\b/i,Br=/^[─—-]{6,}\s*$/,Wd=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;zy=/^\s*Switch model\?\s*$/m;Jy=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,N$=/^\s*[─—▔-]{6,}\s*$/;Fd=/Resume from summary/i,D$=/^\s*This session is\b/;jr=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;Xy=/Choose the text style that looks best with your terminal/,Yy=/Let's get started\./;Zy=/Select login method:/;Kd=/Paste code here if prompted/,U$=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;Ud=/OAuth error:/,q$=/Press Enter to retry\./;Qy=/Quick safety check: Is this a project you created or one you trust/;eb=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function wc(e,t,n){if(!e||!t)return null;for(let r of X$)if(r.provider===e.agent)try{if(r.match(t,n))return r}catch{}return null}function Z$(e){let t=zd.get(e);return t||(t={sentAt:[],last:0,emitted:new Set},zd.set(e,t)),t}function sb(e,t,n=Date.now()){let r=Z$(e);if(r.sentAt=r.sentAt.filter(s=>n-s<ob),r.sentAt.length>=rb)return{action:"give-up"};if(n-r.last<Y$)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 ab(e,t=Date.now()){let n=zd.get(e);return n?n.sentAt.filter(r=>t-r<ob).length>=rb:!1}var X$,Y$,rb,ob,zd,Jd=O(()=>{X$=[{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||""}
|
|
127
|
+
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];Y$=5e3,rb=5,ob=3600*1e3,zd=new Map});function Po(e){return typeof e=="string"?e.trim():""}var ib=O(()=>{});import{execFile as Q$}from"node:child_process";import{promisify as eO}from"node:util";async function Ie(e,t,{input:n}={}){let r=Yd("git",["-C",e,...t],{maxBuffer:cb});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Bo(e){if(!e)return!1;try{let{stdout:t}=await Ie(e,["rev-parse","--is-inside-work-tree"]);return t.trim()==="true"}catch{return!1}}async function lb(e){let{stdout:t}=await Ie(e,["rev-parse","--show-toplevel"]);return t.trim()}async function tO(e){try{return await Ie(e,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function Do(e,t="bostrat: subtask snapshot"){let{stdout:n}=await Ie(e,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>Yd("git",["-C",e,...a],{maxBuffer:cb,env:o,...i});try{let a=await tO(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 Yd("rm",["-f",r])}catch{}}}async function Zd(e,t){let{stdout:n}=await Ie(e,["status","--porcelain"]);return n.trim()?(await Ie(e,["add","-A"]),await Ie(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",t]),!0):!1}async function ua(e,{ignorePlumbing:t=!1}={}){let{stdout:n}=await Ie(e,["status","--porcelain"]),r=n.split(`
|
|
128
|
+
`).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 da(e,t){if(!t)return 0;try{let{stdout:n}=await Ie(e,["rev-list","--count",`${t}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function jo(e,t,n,r){return await Ie(e,["worktree","add","--no-track","-b",n,t,r]),t}async function ub(e,t){try{return await Ie(e,["rev-parse","--verify","--quiet",`refs/heads/${t}`]),!0}catch{return!1}}async function gc(e,t,n){return await Ie(e,["worktree","add",t,n]),t}async function Lo(e){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:t}=await Ie(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 Ie(e,["rev-parse","--verify","--quiet",t]),n}catch{}return"HEAD"}async function Qd(e){await Ie(e,["init","-q"]),await Ie(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function it(e,t){try{await Ie(e,["worktree","remove","--force",t])}catch{try{await Ie(e,["worktree","prune"])}catch{}}}function nO(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 ef(e,t){return!t||!t.length?e:[...e,"--",".",...t.map(n=>`:(exclude)${n}`)]}async function tf(e,t,n,r){let{stdout:o}=await Ie(e,ef(["diff","--shortstat",t,n],r));return nO(o)}async function nf(e,t,n,r){let{stdout:o}=await Ie(e,ef(["diff","--name-only",t,n],r));return o.split(`
|
|
129
|
+
`).map(s=>s.trim()).filter(Boolean)}async function rf(e,t,n,r){let{stdout:o}=await Ie(e,ef(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await Xd(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await Xd(e,o,["--3way","--whitespace=nowarn"]),a=await rO(e);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await Xd(e,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function Xd(e,t,n){try{return await Ie(e,["apply",...n,"-"],{input:t}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function rO(e){try{let{stdout:t}=await Ie(e,["diff","--name-only","--diff-filter=U"]);return t.split(`
|
|
130
|
+
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var Yd,cb,yc=O(()=>{Yd=eO(Q$),cb=64*1024*1024});import of from"node:fs/promises";import fb from"node:path";import oO from"node:os";function wb(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function gb(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function yb(e){let t=new Date(wb(e));return t.setDate(t.getDate()+7),t.getTime()}function bb(e){let t=new Date(gb(e));return t.setMonth(t.getMonth()+1),t.getTime()}function kc(e=Date.now()){for(let[t,n]of Object.entries(Fo)){let r=n.startOf(e);if(r!==nn[t]){nn[t]=r;for(let o of Fr.values())o[t]=0;for(let o of Uo)o.includes(`:${t}:`)&&!o.endsWith(`:${r}`)&&Uo.delete(o)}}}function kb(e,t){let n=At?.caps?.[e]?.[Fo[t].capKey];return Number.isFinite(n)&&n>0?n:null}function Ur(e,t=Date.now()){if(!e||!af||!At?.caps)return null;kc(t);let n=Fr.get(e);for(let r of Object.keys(Fo)){let o=kb(e,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:e,window:r,limitUsd:o,spentUsd:bc(s),resetsAt:Fo[r].nextStart(t)}}return null}function xb(e,t){let n=`hit:${e}:${t.provider}:${t.window}:${nn[t.window]}`;return Uo.has(n)?!1:(Uo.add(n),!0)}function db(e,t,n,r=Date.now()){if(!e||!t)return;kc(r);let o=sO(e);for(let[s,a]of Object.entries(Fo))n>=nn[s]&&n<a.nextStart(r)&&(o[s]+=t)}async function aO(e,t,n=Date.now()){if(!At?.caps||!sf)return;let r=Fr.get(e);if(r)for(let[o,s]of Object.entries(Fo)){let a=kb(e,o);if(a==null)continue;let i=r[o]||0;if(i<a*hb||i>=a)continue;let c=`warn:${e}:${o}:${nn[o]}`;if(!Uo.has(c)){Uo.add(c);try{let l=await sf(t);if(!l)continue;await R(l,{type:"usage.cap.warning",actor:"system",provider:e,payload:{provider:e,window:o,limitUsd:a,spentUsd:bc(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function Sb(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 xc(e,{source:t="local",updatedAt:n=Date.now()}={}){At={caps:Sb(e),updatedAt:n,source:t};try{await of.mkdir(pb,{recursive:!0}),await of.writeFile(mb(),JSON.stringify(At,null,2))}catch{}return fa()}function fa(e=Date.now()){kc(e);let t=new Set([...Fr.keys(),...Object.keys(At?.caps||{})]),n={};for(let r of t){let o=Fr.get(r);n[r]={weeklyUsd:bc(o?.weekly||0),monthlyUsd:bc(o?.monthly||0)}}return{caps:At?.caps||{},configured:!!At,seeded:af,updatedAt:At?.updatedAt??null,spent:n,windows:{weekly:{start:nn.weekly,resetsAt:yb(e)},monthly:{start:nn.monthly,resetsAt:bb(e)}},warnPct:hb}}async function vb({listTracks:e,getTrack:t}={}){sf=t||null,kc();try{let o=JSON.parse(await of.readFile(mb(),"utf8"));At={caps:Sb(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{At=null}let n=Date.now();nt.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=tn(o);db(o.provider||"unknown",s,o.ts??Date.now()),aO(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(nn.weekly,nn.monthly);for(let o of await e?.()||[]){let s;try{s=await B(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||db(a.provider||"unknown",tn(a).usd,i,n)}}return af=!0,fa()}var pb,mb,hb,Fo,At,af,nn,Fr,Uo,sf,sO,bc,Sc=O(()=>{ue();Mr();pb=process.env.BOSTRAT_STATE_DIR||fb.join(process.env.HOME||oO.tmpdir(),".bostrat"),mb=()=>fb.join(pb,"usage-caps.json"),hb=.8;Fo={weekly:{startOf:wb,nextStart:yb,capKey:"weeklyUsd"},monthly:{startOf:gb,nextStart:bb,capKey:"monthlyUsd"}},At=null,af=!1,nn={weekly:0,monthly:0},Fr=new Map,Uo=new Set,sf=null,sO=e=>{let t=Fr.get(e);return t||(t={weekly:0,monthly:0},Fr.set(e,t)),t};bc=e=>Math.round(e*1e4)/1e4});import iO from"node:fs/promises";import cO from"node:os";import Tb from"node:path";function Ob(e){return e==="codex"?fO:e==="cursor"?$b:Ib}function Ab(e){let t=String(e||"").split(`
|
|
131
|
+
`),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 Eb(e){let t=String(e||"").split(`
|
|
132
|
+
`),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 pO(e){let t=String(e||"").split(`
|
|
133
|
+
`);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 _b(e){let t=String(e||"").split(`
|
|
134
|
+
`),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 Cb(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&&!Rb.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=$b.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!dO.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function pa(e,t,{timeoutMs:n=lO}={}){let r=Date.now()+n;for(;;){let o=await Ae(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await vc(uO)}}async function mO(e,t){return await je(e,`/model ${t.model}`,Pt("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function hO(e,t){await Ki(e),await je(e,"/model",{enter:!1}),await vc(250),await Oe(e,["Enter"],{kind:"model-switch"});let n=await pa(e,Ab);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await Oe(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=pO(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await je(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await pa(e,i=>Eb(i)||!Ab(i)&&o(i)&&{done:o(i)});if(!s)throw await Oe(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?Rb.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 Oe(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await je(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await pa(e,i=>!Eb(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 wO(){let e=Tb.join(process.env.CURSOR_CONFIG_DIR||Tb.join(cO.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await iO.readFile(e,"utf8"))?.model||null}catch{return null}}async function gO(e,t){await Ki(e),await Vi(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await pa(e,_b),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 pa(e,_b,{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 Oe(e,["Down"],{kind:"model-switch"}),await vc(250)}if(!n||!s||!o(s)){await Oe(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await Oe(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 Oe(e,["Enter"],{kind:"model-switch"}),await vc(800);let a=await wO();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 Mb(e,t,n){return e==="codex"?hO(t,n):e==="cursor"?gO(t,n):mO(t,n)}var vc,lO,uO,Ib,dO,fO,Rb,$b,Nb=O(()=>{Je();ta();vc=e=>new Promise(t=>setTimeout(t,e)),lO=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,uO=300,Ib=[{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"},{alias:"opusplan",label:"Opus Plan"}],dO=new Set([...Ib.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"]),fO=[{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"}],Rb=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),$b=[{alias:"auto",label:"Auto"},{alias:"composer-2.5",label:"Composer 2.5"},{alias:"claude-opus-5",label:"Opus 5"},{alias:"claude-fable-5",label:"Fable 5"},{alias:"claude-sonnet-5",label:"Sonnet 5"},{alias:"claude-sonnet-4-6",label:"Sonnet 4.6"},{alias:"gpt-5.6-sol",label:"GPT-5.6 Sol"},{alias:"gpt-5.3-codex",label:"Codex 5.3"}]});function Ac(e){let t=e||{},n=yO(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function yO(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:rt(e.agent),payload:{text:e.text,boxRole:e.boxRole,...e.kind?{kind:e.kind}:{},...e.attachments?.length?{attachments:e.attachments}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:e.box,provider:rt(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:rt(e.agent),payload:{text:e.prompt,kind:e.kind,boxRole:e.boxRole}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:rt(e.toAgent),payload:{from:e.from,fromAgent:e.fromAgent,to:e.to,toAgent:e.toAgent,reason:e.reason,handoffStartedAt:e.handoffStartedAt,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,singleBox:e.singleBox,forced:e.forced,recovered:e.recovered,checkpoint:e.checkpoint}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:rt(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:rt(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:rt(e.agent),payload:{...zn(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:rt(e.agent||e.toAgent),payload:{kind:"subtask-failed",...zn(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:rt(e.agent),payload:{change:"subtask-resumed",...zn(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",...zn(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...zn(e)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...zn(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:rt(e.agent),payload:{change:"rehydrated"}};case"transferred-in":return{type:e.mode==="recover"?"track.recovered":"track.transferred",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:rt(e.agent),payload:{...zn(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...zn(e)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:rt(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};default:return null}}function Db(e,t){let n=Ac(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;lf(()=>R(r,n))}async function jb(e){if(!(!e?.id||Pb.has(e.id))){Pb.add(e.id);try{let t=await B(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=Ac(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await R(e,o)}}catch{}}}function zn(e){let{type:t,at:n,...r}=e;return r}function Lb(e,t,n){!n?.promptId||cf.get(e.id)===n.promptId||(cf.set(e.id,n.promptId),lf(()=>R(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:rt(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 uf(e,t,n,r={}){cf.delete(e.id),lf(()=>R(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:rt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var rt,Tc,lf,Bb,Pb,cf,df=O(()=>{ue();rt=e=>e||null;Tc=Promise.resolve(),lf=e=>(Tc=Tc.then(e).catch(()=>{}),Tc),Bb=()=>Tc;Pb=new Set;cf=new Map});function vO(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function Kb(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of vO(n).reverse()){if(!bO.test(r))continue;let o=r.endsWith("?")&&r.length<=SO&&xO.test(r);if(kO.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function Vb(e){let t=String(e||""),n=[];TO.test(t)&&n.push("commit"),Gb.test(t)&&n.push("rebase"),AO.test(t)&&n.push("push");let r=_O.test(t);!r&&EO.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 zb(e){return Gb.test(String(e||""))}function Wo(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(p=>`${p.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 p=a.has("commit")?"the branch":`this branch${t?` (${t})`:""}`;u.push(i?`rebase ${p} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${p} 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 p=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",m=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${m}${p}`:`push this branch${t?` (${t})`:""} from each changed checkout (${c}) to its remote${p}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${m}${p}`:`push this branch${l} to the remote${p}`)}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 f=[];a.has("commit")||f.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&f.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&f.push("don't push"),a.has("pr")||f.push("don't open a pull request");let d=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return f.length&&(d+=` ${f.join(", and ").replace(/^./,p=>p.toUpperCase())}.`),a.has("pr")&&(d+=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."),d}function Jb(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 Xb(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<=IO&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function Yb(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+RO||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function Zb(e){let t=(Array.isArray(e)?e:[]).filter(r=>Fb[r]).map(r=>Fb[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 Ub,Hb,Wb,qb,bO,kO,xO,SO,Ho,TO,Gb,AO,EO,_O,IO,RO,Fb,qo=O(()=>{Ub=/(?<!\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,Hb=/(?<!\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,Wb=/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,qb=/(?<!\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,bO=new RegExp(`\\b(?:${Ub}|${Hb}|${Wb}|${qb})\\b`,"i"),kO=/\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,xO=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,SO=90;Ho=["commit","rebase","push","pr"],TO=new RegExp(`\\b(?:${Ub})\\b`,"i"),Gb=new RegExp(`\\b(?:${qb})\\b`,"i"),AO=new RegExp(`\\b(?:${Hb})\\b`,"i"),EO=new RegExp(`\\b(?:${Wb})\\b`,"i"),_O=/\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;IO=3e4;RO=2500;Fb={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import $O from"node:crypto";import Ec from"node:fs/promises";import Qb from"node:path";import OO from"node:os";async function Ce(){return Jn||(ff??=Ec.readFile(mf,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(Jn=e,!t&&Te()){let{records:n,source:r}=await Be("prlinks").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.repo||o.data?.number==null)continue;let{id:s,updatedAt:a,...i}=o.data;Jn[Y(i.repo,i.number)]=i}Object.keys(Jn).length&&await hf().catch(()=>{})}}return Jn}).finally(()=>ff=null),ff)}async function hf(){let e=JSON.stringify({links:Jn},null,2),t=`${mf}.${process.pid}.tmp`;await Ec.mkdir(ek,{recursive:!0}),await Ec.writeFile(t,e),await Ec.rename(t,mf)}async function Fe(e){pf=pf.then(hf,hf),await pf.catch(()=>{});let t=Jn?.[e];t&&ze("prlinks",{id:tk(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function ct({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 Ce(),c=Y(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:CO.has(a),firstSeen:Date.now()},await Fe(Y(e,t)),{link:i[c],created:!0})}async function rn(e,t){if(!e||t==null)return null;let n=(await Ce())[Y(e,t)];return!n||n.announced?null:(n.announced=!0,await Fe(Y(e,t)),n)}async function Xe(e,t){return!e||t==null?null:(await Ce())[Y(e,t)]??null}async function Xn(e,t,n){if(!e||t==null)return null;let r=(await Ce())[Y(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,await Fe(Y(e,t)),r):null}async function nk(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ce())[Y(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Fe(Y(e,t)),o):null}async function rk(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ce())[Y(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Fe(Y(e,t)),o):null}async function _c(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ce())[Y(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Fe(Y(e,t)),r)}async function Ic(e,t){if(!e||t==null)return null;let n=(await Ce())[Y(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Fe(Y(e,t)),n)}async function Rc(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ce())[Y(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Fe(Y(e,t)),r)}async function $c(e,t){if(!e||t==null)return null;let n=(await Ce())[Y(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Fe(Y(e,t)),n)}async function ok(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await Ce())[Y(e,t)];return r?(r.baseFoldAt=n,await Fe(Y(e,t)),r):null}async function sk(e,t){if(!e||t==null)return null;let n=(await Ce())[Y(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Fe(Y(e,t)),n)}async function ak(e,t,n){if(!e||t==null||!n)return null;let r=(await Ce())[Y(e,t)];return r?(r.lastHeadSha=n,await Fe(Y(e,t)),r):null}async function ma(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await Ce())[Y(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Fe(Y(e,t)),r):null}async function Hr(e,t,n=!0){if(!e||t==null)return null;let r=(await Ce())[Y(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Fe(Y(e,t)),r):null}async function ha(e,t,n=!0){if(!e||t==null)return null;let r=(await Ce())[Y(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Fe(Y(e,t)),r):null}async function ik(e,t,n){if(!e||t==null)return null;let r=(await Ce())[Y(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 Fe(Y(e,t)),r}async function wa(e,t){if(!e||t==null)return null;let n=(await Ce())[Y(e,t)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,await Fe(Y(e,t)),n)}async function ck(){return Object.values(await Ce()).filter(e=>!MO.has(e.state))}async function Wr(){return Object.values(await Ce()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function lk(){if(!Te())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Be("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Ce(),r=0;for(let[o,s]of Object.entries(n))await ze("prlinks",{id:tk(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var ek,mf,Y,tk,Jn,ff,pf,CO,MO,Yn=O(()=>{Nn();ek=process.env.BOSTRAT_STATE_DIR||Qb.join(process.env.HOME||OO.tmpdir(),".bostrat"),mf=Qb.join(ek,"pr-links.json"),Y=(e,t)=>`${e}#${Number(t)}`,tk=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=$O.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},Jn=null,ff=null,pf=Promise.resolve();CO=new Set(["api","observed","reconciled"]);MO=new Set(["merged","closed","lost"])});import NO from"node:fs";import wf from"node:fs/promises";import pk from"node:path";import PO from"node:os";function wk(e,t=!1){if(!t&&e-dk<BO)return;dk=e;let n=JSON.stringify({windowStart:Ko,byCaller:Object.fromEntries(on),lastHour:Zn});fk=fk.then(()=>wf.mkdir(mk,{recursive:!0})).then(()=>wf.writeFile(`${Oc}.tmp`,n)).then(()=>wf.rename(`${Oc}.tmp`,Oc)).catch(()=>{})}function gk(e){e-Ko<hk||(Zn={start:Ko,total:Cc(on),byCaller:Object.fromEntries(on)},Zn.total&&console.log(`[github] ${Zn.total} calls last hour`,Zn.byCaller),Ko=e,on=new Map,wk(e,!0))}function sn(e="other",t=Date.now()){gk(t),on.set(e,(on.get(e)||0)+1),wk(t)}function yk(e=Date.now()){return gk(e),{windowStart:Ko,total:Cc(on),byCaller:Object.fromEntries(on),lastHour:Zn,backoffUntil:qr>e?qr:null}}function Qn(e){return DO.test(String(e?.message||e||""))}function er(e,t=Date.now()){return qr<=t&&console.warn(`[github] rate limited \u2014 backing off ${uk/6e4} min:`,String(e?.message||e||"").slice(0,200)),qr=Math.max(qr,t+uk),qr}function Mc(e=Date.now()){return Math.max(0,qr-e)}var mk,Oc,hk,uk,BO,Ko,on,Zn,qr,dk,Cc,fk,DO,ga=O(()=>{mk=process.env.BOSTRAT_STATE_DIR||pk.join(process.env.HOME||PO.tmpdir(),".bostrat"),Oc=pk.join(mk,"github-metrics.json"),hk=60*6e4,uk=5*6e4,BO=6e4,Ko=Date.now(),on=new Map,Zn=null,qr=0,dk=0,Cc=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(NO.readFileSync(Oc,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<hk?(Ko=e.windowStart,on=new Map(Object.entries(e.byCaller||{})),Zn=e.lastHour||null):Cc(new Map(Object.entries(e.byCaller||{})))&&(Zn={start:e.windowStart,total:Cc(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}fk=Promise.resolve();DO=/rate limit|too quickly|abuse detection|HTTP 429/i});import bk from"node:fs/promises";import kk from"node:path";import jO from"node:os";function Pc({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};Nc.push(l),Nc.length>FO&&Nc.shift();let u=gf.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)),gf.set(e,u),UO(l),l}async function UO(e){try{await bk.mkdir(xk,{recursive:!0}),await bk.appendFile(LO,JSON.stringify(e)+`
|
|
135
|
+
`)}catch{}}function Sk(){let e={};for(let[t,n]of gf)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:Nc.slice(-20)}}var xk,LO,FO,Nc,gf,Bc=O(()=>{xk=process.env.BOSTRAT_STATE_DIR||kk.join(process.env.HOME||jO.tmpdir(),".bostrat"),LO=kk.join(xk,"github-latency.ndjson"),FO=100,Nc=[],gf=new Map});import{execFile as HO}from"node:child_process";import{promisify as WO}from"node:util";async function qO(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Vo("git",["-C",e,"rev-parse","HEAD"]),Vo("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await R({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function ht(e,t,n={}){let{caller:r="prs",...o}=n,s=await Ct({cwd:e});sn(r);try{let{stdout:a}=await Vo("gh",t,{cwd:e,...KO,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw Qn(a)&&er(a),a}}async function kf(){if(Dc&&Date.now()-Dc.at<6e4)return Dc.data;let e;try{sn("auth");let t=await Vo("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 Bs();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return Dc={at:Date.now(),data:e},e}async function GO(e){try{let{stdout:t}=await Vo("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function Tk(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 yf(e){let t=(e||[]).map(Tk),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 Sf(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function Ek(e,t){if(!t)return null;let n=await pe(e);if(!n)return null;try{let r=await ht(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 _k(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 Ik(e,t){let n=await Ek(e,t);return n===null?{...yf([]),unavailable:!0}:yf(_k(n))}function jc(e,t=yf(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",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 zO(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Go.delete(s);continue}r.add(s),Go.has(s)||Go.set(s,n),o.mergeabilityPending=n-Go.get(s)<VO}for(let o of Go.keys())o.startsWith(`${e}#`)&&!r.has(o)&&Go.delete(o);return t}async function zo(e,t){let n=await an(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await ht(n.path,["pr","view",String(r),"--json",xf]);return jc(JSON.parse(o))}catch(o){if(!Sf(o))throw o;let s=await ht(n.path,["pr","view",String(r),"--json",Ak]),a=JSON.parse(s);return jc(a,await Ik(n.path,a.headRefOid))}}async function Bt(e,{caller:t="prs"}={}){try{let n=await ht(e,["pr","list","--json",xf,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>jc(r)).sort(vk)}catch(n){if(!Sf(n))throw n;let r=await ht(e,["pr","list","--json",Ak,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>jc(o,await Ik(e,o.headRefOid))))).sort(vk)}}function jt(){ya.clear()}async function YO(e,t){if(!e)return t;for(let n of t){let r=await Xe(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 _c(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Rc(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function Rk(){let e=await kf();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await Xt())await GO(a.path)&&t.push(a);let n=Date.now(),r=Mc(n)>0,o=await Promise.all(t.map(async a=>{let i=ya.get(a.path),c=i?.pending?XO:JO;if(i&&(r||n-i.at<c))return i.entry;try{let l=await pe(a.path).catch(()=>null),u=zO(a.path,await Bt(a.path,{caller:"tab"}),n);if(await YO(l,u),i?.entry?.prs)for(let p of u)i.entry.prs.find(w=>w.number===p.number)?.checks?.pending>0&&p.checks.total>0&&p.checks.pending===0&&Pc({change:p.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:p.number,githubAt:p.checks.settledAt,source:"tab"});let f={name:a.name,path:a.path,prs:u},d=u.some(p=>p.mergeabilityPending||p.checks.pending>0||p.autoMergeArmed);return ya.set(a.path,{at:Date.now(),entry:f,pending:d}),f}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:Dt(l)}}})),s=new Set(t.map(a=>a.path));for(let a of ya.keys())s.has(a)||ya.delete(a);return{auth:e,repos:o}}async function an(e){let t=(await Xt()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function Kr(e){return fo(e)}async function Jo(e,t,{method:n="squash"}={}){let r=await an(e);if(!ba.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 Ct({cwd:r.path})).GH_TOKEN){let a=await hu().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await ht(r.path,s)}catch(a){throw new Error(Dt(a))}finally{jt()}return{ok:!0,number:o}}async function ka(e,t){let n=await an(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await ht(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Dt(s))}finally{jt()}let o=await pe(n.path).catch(()=>null);return o&&await ok(o,r).catch(()=>{}),{ok:!0,number:r}}async function vf(e,t){let n=await an(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await ht(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Dt(o))}finally{jt()}return{ok:!0,number:r}}function Tf(e){return e.green&&!e.isDraft}function tC(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 $k(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:Tf(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function Ok(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function Ck(){let e={};for(let[t,n]of bf)e[t]={...n};return e}async function Mk(e,{method:t="merge",onMerged:n=null}={}){let r=await an(e);if(!ba.has(t))throw new Error(`invalid merge method: ${t}`);if(bf.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Bt(r.path)).filter(Tf).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 bf.set(r.path,s),rC(s,o,{listPrs:()=>Bt(r.path),merge:a=>Jo(r.path,a,{method:t}),sleep:eC,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function nC(e,t){let n=Date.now()+(t.settleMaxMs??QO),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(ZO)}}async function rC(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 nC(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: ${Dt(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(!Tf(a)){e.skipped.push({number:a.number,title:a.title,reason:tC(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:Dt(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),jt()}}async function Af(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Vo("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Dt(l)}`)}await qO(e);let o=n||"";if((await Ct({cwd:e})).GH_TOKEN){let l=await hu().catch(()=>null);l&&(o=`${o?`${o}
|
|
136
|
+
|
|
137
|
+
`:""}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 ht(e,s)}catch(l){throw new Error(Dt(l))}finally{jt()}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 Nk(e,t){let n=await an(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await ht(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(Dt(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!Sf(i))throw i;let c=await ht(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await Ek(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(Dt(i));o=_k(l).map(Tk).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=oC(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await ht(n.path,["run","view",c,"--log-failed"]).then(u=>sC(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function oC(e){return String(e||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function sC(e,{maxLines:t=400,maxBytes:n=64*1024}={}){let r=String(e||""),o=r.split(`
|
|
138
138
|
`);return o.length>t&&(r=`\u2026 (${o.length-t} earlier lines truncated)
|
|
139
139
|
`+o.slice(-t).join(`
|
|
140
140
|
`)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
|
|
141
|
-
`+r.slice(-n)),r}function
|
|
142
|
-
`).find(n=>n.trim())||"gh failed"}var
|
|
143
|
-
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+aC(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 mf(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function Ok(e){let t=Rk(e),n=t.match(rC);if(!n)return null;let r=mf(t,n.index);return sC.test(r)?null:{repo:r.match($k)?.[1]??null,head:r.match(iC)?.[1]??null}}function Ck(e){let t=Rk(e),n=t.match(oC);if(!n)return null;let r=mf(t,n.index),o=mf(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("-")){lC.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match($k)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function dC(e,t,{exclude:n=()=>!1}={}){let r=async i=>{rn("observer");let{stdout:c}=await Ak("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",uC],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw Jn(l)&&Yn(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-eC&&!n(c)),s=await $t({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 fC(e,t){if(t)return[t];let n=[];for(let r of Mk(e)){let o=await de(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await zt().catch(()=>[])){let o=await de(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function Mk(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 pC(e,t,n){let r=await qo(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)||cC(c.headRefName,e.id),i=[];for(let c of await fC(e,t?.repo??null)){let l;try{l=await dC(c,n,{exclude:a})}catch{continue}for(let u of l){let f=await Je(c,u.number);if(s(u.headRefName)){if(f&&f.trackId===e.id&&f.announced)return{settled:!0};if(f&&f.trackId!==e.id)continue;return{slug:c,pr:u}}f&&(f.trackId!==e.id||f.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 Nk(e,t,{delays:n=Ek,turnEnd:r}={}){let o=Ok(t.command);if(!o)return null;let s=!!Ck(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(Xn.has(a))return null;Xn.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(w=>setTimeout(w,l));let u=await pC(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:f,pr:d}=u,{link:m,created:h}=await it({repo:f,number:d.number,trackId:e.id,url:d.url,headRef:d.headRefName,title:d.title,source:"observed"});return!h&&!(m?.trackId===e.id&&await tn(f,d.number))?null:(await pf(t.box,t.agent,r),await I({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:d.number,url:d.url,title:d.title,branch:d.headRefName,repo:f,...s?{selfMerge:!0}:{}}}),d)}return null}finally{Xn.delete(a)}})();return c.catch(()=>Xn.delete(a)),c}function Pk(e,t,{delays:n=Ek,turnEnd:r,openedWait:o}={}){let s=Ck(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(Xn.has(a))return null;Xn.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 f=[];if(c)f=[c];else{let h=Mk(e);if(h.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of h){let y=await de(w).catch(()=>null);y&&!f.includes(y)&&f.push(y)}}if(l||=(await qo(e).catch(()=>[])).filter(h=>h!==e.workspace?.branch)[0]||e.workspace?.branch,!f.length||!l)return null;c=f[0];let d=async(h,w)=>{let y=await Je(c,h.number);return y&&y.trackId!==e.id?null:(y||await it({repo:c,number:h.number,trackId:e.id,url:h.url,headRef:h.headRefName,title:h.title,source:"merge-observed"}),await ia(c,h.number),w&&((await B(e).catch(()=>[])).some(E=>E.type==="pr.state.changed"&&(E.payload?.change==="auto-armed"||E.payload?.change==="merged")&&E.payload?.number===h.number)||(await pf(t.box,t.agent,r),await I({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:h.number,url:h.url,title:h.title,branch:h.headRefName,repo:c}}))),h)},m=null;for(let h of n){await new Promise(C=>setTimeout(C,h));let w=[];for(let C of f)try{rn("observer");let{stdout:N}=await Ak("gh",["pr","view",String(l),"--repo",C,"--json",mC],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await $t({slug:C})}}),T=JSON.parse(N||"null");T?.number&&w.push({slug:C,pr:T})}catch(N){Jn(N)&&Yn(N)}let y=w.find(C=>C.pr.state==="MERGED")??(w.length===1?w[0]:null);if(!y)continue;c=y.slug;let b=y.pr;if(!b?.number)continue;if(b.state==="CLOSED")return null;if(b.state!=="MERGED"){if(m=b,s.auto)return d(b,!0);continue}let x=await Je(c,b.number);if(x&&x.trackId!==e.id)return null;x||await it({repo:c,number:b.number,trackId:e.id,url:b.url,headRef:b.headRefName,title:b.title,source:"merge-observed"}),await pf(t.box,t.agent,r);let E=await B(e).catch(()=>[]);if(E.some(C=>C.type==="pr.state.changed"&&C.payload?.change==="merged"&&C.payload?.number===b.number))return null;let _=C=>C.some(N=>N.type==="pr.state.changed"&&N.payload?.change==="opened"&&N.payload?.number===b.number),q=0;if(!_(E)){if(Ok(t.command)&&await tn(c,b.number)){let C=Date.now();q=C+1,await I({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:C,change:"opened",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c,selfMerge:!0}})}else if((await Je(c,b.number))?.announced){let C=o?.capMs??tC,N=o?.pollMs??nC,T=Date.now()+C;for(;Date.now()<T&&(await new Promise(W=>setTimeout(W,N)),!_(await B(e).catch(()=>[]))););}}return await I({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(),q),change:"merged",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c}}),await an(c,b.number).catch(()=>{}),await sn(c,b.number).catch(()=>{}),await Gn(c,b.number,"merged"),b}return m?d(m,!1):null}finally{Xn.delete(a)}})();return i.catch(()=>Xn.delete(a)),i}var Ak,Ek,ZO,QO,eC,tC,nC,_k,Ik,rC,oC,sC,Tk,aC,$k,iC,cC,lC,uC,Xn,mC,Bk=O(()=>{le();hr();Ge();ua();Vn();Ur();ha();Ko();Ak=XO(YO),Ek=[4e3,15e3,45e3],ZO=2500,QO=5*6e4;eC=15e3,tC=9e4,nC=2e3,_k=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,Ik=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,rC=new RegExp(`${_k}${Ik}create\\b`),oC=new RegExp(`${_k}${Ik}merge\\b`),sC=/\s--web\b/,Tk=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,aC=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");$k=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,iC=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,cC=Sk;lC=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);uC="number,url,title,headRefName,createdAt";Xn=new Set;mC="number,url,title,headRefName,state"});import hC from"node:fs/promises";import wf from"node:path";import wC from"node:os";function hf(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function Cc(e){let t=gC.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=hf(n,"from"),s=o?o.replace(/^uds:/,""):null,a=Dk(s);return{socket:s,pid:a,name:hf(n,"from-name"),mode:hf(n,"from-mode"),text:r.trim()}}function Dk(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function yC(){return process.env.CLAUDE_CONFIG_DIR?wf.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):wf.join(wC.homedir(),".claude","sessions")}async function bC(e){let t=Dk(e);if(!t)return null;try{let n=await hC.readFile(wf.join(yC(),`${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 jk(e,t){let n=await bC(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 gC,gf=O(()=>{gC=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function yf(e=process.env){return!!(be(e)&&e.BOSTRAT_PLANE_TOKEN)}async function bf({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!yf(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),Lk);try{let a=await Fk(`${be(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 Uk(e,{provider:t=null,cls:n="other",status:r=null,text:o=""}={},{env:s=process.env,now:a=Date.now}={}){if(!e?.id||!String(e.name||"").startsWith("\u21BB"))return{ok:!1,source:"not-a-loop-track"};if(!yf(s))return{ok:!1,source:"unconfigured"};let i=Mc.get(e.id)||0;if(a()-i<kC)return{ok:!1,source:"debounced"};Mc.set(e.id,a());let c=await Nc({env:s}),l=c.loops.find(d=>d.trackId===e.id);if(!l)return Mc.delete(e.id),{ok:!1,source:c.source==="plane"?"no-loop":c.source};let u=`${t||"provider"} ${r?r+" ":""}${n}: ${String(o||"").replace(/\s+/g," ").trim()}`.slice(0,500),f=await bf({loopId:l.id,outcome:"errored",note:u,env:s});return f.ok||Mc.delete(e.id),{...f,loop:{id:l.id,name:l.name}}}async function Nc({env:e=process.env}={}){if(!yf(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),Lk);try{let r=await Fk(`${be(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 Lk,Fk,kC,Mc,kf=O(()=>{Ge();Lk=1e4,Fk=(...e)=>fetch(...e);kC=10*6e4,Mc=new Map});import{randomUUID as xC}from"node:crypto";function wa(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function Hk(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function Wk(e){return`job|${e.box}|id:${e.id}`}async function vf(e){return xf.has(e.id)?xf.get(e.id):(Pc.has(e.id)||Pc.set(e.id,B(e).then(t=>{let n={keys:new Set,cmdN:0};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(wa({role:o,sourceId:r.payload.sourceId})),n.keys.add(wa({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(wa({role:"user",sourceId:r.payload.sourceId})),n.keys.add(wa({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(Hk({id:o.id,box:r.boxId,ts:o.ts,command:o.command})),Number(o.n)>n.cmdN&&(n.cmdN=Number(o.n))}else r.type==="job.started"&&r.payload?.id&&n.keys.add(Wk({id:r.payload.id,box:r.boxId}));return xf.set(e.id,n),n}).finally(()=>Pc.delete(e.id))),Pc.get(e.id))}async function qk(e,t){try{let n=(await vf(e)).keys;for(let r of t||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=wa(r);if(n.has(o))continue;n.add(o);let s=r.role==="user"?Cc(r.text):null;if(s){await I(e,{type:"peer.message",actor:"peer",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:s.text,ts:r.ts,keyText:String(r.text||"").slice(0,40),from:await jk(s,ga),boxRole:r.boxRole,sourceId:r.sourceId||void 0}});continue}let a=Cw(r);if(a){await I(e,{type:"provider.error",actor:"system",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{cls:a.cls,code:a.code||void 0,status:a.status||void 0,requestId:a.requestId||void 0,text:r.text,ts:r.ts,model:r.model||null,boxRole:r.boxRole,sourceId:r.sourceId||void 0}}),Uk(e,{provider:r.agent||null,cls:a.cls,status:a.status,text:r.text}).catch(()=>{});continue}if(await I(e,{type:r.role==="user"?"turn.user":"turn.agent",actor:r.role==="user"?"user":"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:r.text,ts:r.ts,model:r.model||null,tools:r.tools||void 0,boxRole:r.boxRole,sourceId:r.sourceId||void 0,note:r.note||void 0,images:r.images?.length?r.images:void 0}}),r.role==="assistant"&&r.box===e.leadBox&&e.workspace?.branch){let i=Ib(r.text);if(i){let c=Ob(i),l;if(e.workspace?.kind==="multi"){let m=await Go(e).catch(()=>[]);l=m.length?m.map(h=>({name:h.name,slug:h.repo??null})):void 0}let u=(e.workspace?.kind==="multi"?null:await Wo(e.cwd).catch(()=>null))||e.workspace.branch,f=await yk(e,{branch:u,rebase:c}).catch(()=>null),d=f?null:$b(i);if(f&&f.available.length===0)continue;await I(e,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:xC(),ts:r.ts,excerpt:i,branch:u,base:e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:r.boxRole,available:f?.available,defaults:f?.defaults,prState:f?.prState,openPr:f?.openPr||void 0,repos:f?.repos||l,offered:f?.defaults||d?.offered||void 0,toPr:d?.toPr||void 0}})}}}}catch{}}async function Kk(e,t){try{let n=await vf(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=Hk(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await I(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:n.cmdN}}),Nk(e,o),Pk(e,o))}}catch{}}async function Gk(e,t){try{let n=await vf(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=Wk(o);n.keys.has(s)||(n.keys.add(s),await I(e,{type:"job.started",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,detail:o.detail||null,ts:o.ts??null,boxRole:o.boxRole}}))}}catch{}}var xf,Pc,Vk=O(()=>{le();Bo();bk();Bk();Ko();gf();Ur();Fi();kf();xf=new Map,Pc=new Map});function vC(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
144
|
-
`)){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 SC(e,t){let n=`${e.id}:${t.name}`;if(!Sf.has(n)){let r=await B(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);Sf.set(n,o)}return Sf.get(n)}async function zk(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await xr(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await SC(e,t),o=vC(await et(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 I(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:Fn(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await I(e,{type:"context.usage",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{occupancy:a.occupancy,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}var Sf,Jk=O(()=>{le();Sr();vt();ea();Sf=new Map});import Bc from"node:fs";import Af from"node:path";import TC from"node:os";async function _C({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(EC,{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:Yk})});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<Yk)break}return r}function IC(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 Zk(){try{return JSON.parse(Bc.readFileSync(Dc(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function $C(e){try{Bc.mkdirSync(Af.dirname(Dc()),{recursive:!0});let t=Dc()+".tmp";Bc.writeFileSync(t,JSON.stringify(e)),Bc.renameSync(t,Dc())}catch{}}async function CC({fetchImpl:e,now:t=Date.now()}={}){if(Zn.rows&&t-Zn.at<RC)return Zn.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=Zk(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await _C({sinceMs:o,cookie:n,fetchImpl:e});Zn={at:t,rows:s.map(IC).filter(Boolean),error:null}}catch(s){Zn={at:t,rows:null,error:String(s?.message||s)}}return Zn.rows}async function MC(e,t){let n=`${e.id}:${t.name}`;if(!Tf.has(n)){let r=await B(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);Tf.set(n,o)}return Tf.get(n)}async function Qk(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!Xk())return;let r=await CC({fetchImpl:n});if(!r||!r.length)return;let o=await xy(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await MC(e,t),a=Zk(),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",f=l.tokens||null,d=l.vendorCents!=null?l.vendorCents/100:0;!d&&f&&(d=Fn(u,f)),!d&&l.chargedCents!=null&&(d=l.chargedCents/100),await I(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.name,provider:"cursor",payload:{usd:d,model:u,...f?{tokens:f}:{},...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}})}$C({sinceMs:c,seen:[...i].slice(-OC)})}catch{}}var AC,Xk,EC,Yk,RC,Zn,Dc,OC,Ef,Tf,_f=O(()=>{le();bd();ea();AC=process.env.BOSTRAT_STATE_DIR||Af.join(process.env.HOME||TC.tmpdir(),".bostrat"),Xk=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,EC="https://cursor.com/api/dashboard/get-filtered-usage-events",Yk=100;RC=5*6e4,Zn={at:0,rows:null,error:null},Dc=()=>Af.join(AC,"cursor-usage.json");OC=5e3;Ef=()=>({enabled:Xk(),lastPollAt:Zn.at||null,error:Zn.error}),Tf=new Map});import ka from"node:fs/promises";import rt from"node:path";import NC from"node:os";import PC from"node:net";import{randomUUID as BC}from"node:crypto";import{execFile as DC}from"node:child_process";import{promisify as jC}from"node:util";function KC(e){for(let t of Nf(e))if(!sx.test(t))return t;return null}function ax(e){let t=Nf(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function GC(e,t){let n=ax(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return rt.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return rt.resolve(e,n[r+1])}return e}function zC(e){let t=!1,n=!1;for(let r of Nf(e)){if(!t){if(sx.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&If(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,If(r))return`serves the filesystem root ("${r}")`;continue}if(If(r))return`serves the filesystem root ("${r}")`;if(VC(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function YC(e,t){if(rt.resolve(e)===rt.resolve(t))return!0;let[n,r]=await Promise.all([jc(e),jc(t)]);return n===r}async function XC(e){let n=(await ka.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 ix(rt.join(e,o.name,Of))?`./${o.name}`:null))).filter(Boolean)}async function ZC(e,t){let[n,r]=await Promise.all([jc(e),jc(t)]),o=rt.relative(n,r);if(o&&(o.startsWith("..")||rt.isAbsolute(o)))throw ya(`serve directory escapes the Track workspace: ${t}`)}async function QC(e,t){let n=zC(t);if(n)throw ya(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await JC(e))throw ya(`serve directory does not exist: ${e}`);let r=KC(t);if(!r)return;if(!r.includes("/")&&!await aw(r,{loginShell:!0,failOpen:!0}))throw ya(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(HC.test(t)||WC.has(r)||!UC.has(r)||!ax(t).includes("run")||qC.test(t))return;let o=GC(e,t);if(await ix(rt.join(o,Of)))return;let s=await XC(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw ya(`no ${Of} in ${o}${a}`)}async function un(){return Dt||(Rf||=(async()=>{try{Dt=JSON.parse(await ka.readFile(ox,"utf8"))}catch{Dt={views:[],adoptWatermarks:{}}}finally{Rf=null}return Dt.views||=[],Dt.adoptWatermarks||={},Dt})(),Rf)}async function Lt(){await Qe(ox,Dt)}function ln(e,t,n={}){return I({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 Bf(e){return ba.get(e)||null}function lx(e,{name:t,box:n}={}){let r=ba.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 ba.set(e,o),o}function Cf(e,t){let n=ba.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function Lc(e,{error:t=null}={}){let n=ba.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function ux(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 Mf(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function eM(e,t){let n=Mf(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!nx.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!nx.has(s))return s}}function xa(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=PC.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 Df(e){if(process.platform!=="darwin"){await jt("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await jt("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 dx(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function tM(e,t){let n=new Set(e.views.map(o=>o.port)),r=dx();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=ex;o<=tx;o++)if(!(n.has(o)||r.has(o))&&!await xa(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${ex}-${tx}`)}async function nM(e){try{let{stdout:t}=await jt(L,U("list-sessions","-F","#{session_name}"));return t.split(`
|
|
145
|
-
`).some(n=>n.trim()===
|
|
141
|
+
`+r.slice(-n)),r}function Dt(e){return String(e?.stderr||e?.message||e||"").trim().split(`
|
|
142
|
+
`).find(n=>n.trim())||"gh failed"}var Vo,KO,Dc,xf,Ak,VO,Go,vk,JO,XO,ya,ba,ZO,QO,eC,bf,Xo=O(()=>{Sr();Ve();Yn();ga();Bc();ue();Vo=WO(HO),KO={timeout:3e4,maxBuffer:8*1024*1024};Dc=null;xf="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",Ak=xf.replace("statusCheckRollup,","");VO=9e4,Go=new Map;vk=(e,t)=>e.number-t.number;JO=3e4,XO=1500,ya=new Map;ba=new Set(["squash","merge","rebase"]);ZO=2500,QO=9e4,eC=e=>new Promise(t=>setTimeout(t,e));bf=new Map});import{execFile as aC}from"node:child_process";import{promisify as iC}from"node:util";async function lC(e,t){try{let{stdout:n}=await cC("git",["-C",e,"rev-list","--count",t]);return Number(n.trim())||0}catch{return null}}async function uC(e,t,n,{rebase:r=!1,listPrs:o=Bt}={}){let s=await ua(e.dir,{ignorePlumbing:!0}).catch(()=>!1),a=await da(e.dir,n).catch(()=>0),i=t?await lC(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(w=>w.headRefName===t)||null,u=l?"open":"none"}catch{}let f=s||a>0,d=[];s&&d.push("commit"),r&&d.push("rebase"),(s||c)&&d.push("push"),f&&(u!=="open"||s||c)&&d.push("pr");let p=d.filter(m=>!(m==="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:Lc.filter(m=>d.includes(m)),defaults:Lc.filter(m=>p.includes(m))}}async function Pk(e,{branch:t,rebase:n=!1,listPrs:r=Bt}={}){let o=e?.workspace,s=o?.kind==="multi"?(o.repos||[]).filter(d=>d?.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 Bo(s[0].dir).catch(()=>!1))return null;let a=[];for(let d of s)a.push(await uC(d,t,d.baseRef||o.baseRef,{rebase:n,listPrs:r}));let i=a.filter(d=>d.dirty||d.aheadBase>0||d.openPr),c=i.length?i:a,l=Lc.filter(d=>c.some(p=>p.available.includes(d))),u=Lc.filter(d=>c.some(p=>p.defaults.includes(d))),f=c.filter(d=>d.openPr);return{available:l,defaults:u,prState:f.length?"open":c.every(d=>d.prState==="none")?"none":"unknown",openPr:c.length===1?c[0].openPr:null,repos:c.map(d=>({name:d.name,slug:d.slug,dirty:d.dirty,unpushed:d.unpushed,prState:d.prState,openPr:d.openPr}))}}var cC,Lc,Bk=O(()=>{yc();Xo();cC=iC(aC),Lc=["commit","rebase","push","pr"]});function Fc(e,t,n){let{state:r,reason:o}=td(t,n);if(r!=="unknown"){if(e){let a=Date.now();for(let[i,c]of xa)a-c.at>=Dk()&&xa.delete(i);xa.set(e,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!e)return!1;let s=xa.get(e);return s?Date.now()-s.at>=Dk()?(xa.delete(e),!1):s.busy:!1}async function Ef(e,t){let n={working:!1,prompted:!1};if(!e)return n;let r=await Ae(e,{lines:De}).catch(()=>"");if(!Fc(e,t,r))return dg(t,r)?{working:!0,prompted:!1}:n;if(Lr(t,r)){let o=await Qt(e).catch(()=>r),s=Vn({name:e,agent:t},o);if(s)return!wc({name:e,agent:t},s,o)||ab(e)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function Gr(e,t){return(await Ef(e,t)).working}var Dk,xa,Sa=O(()=>{Je();nc();Vd();Jd();Dk=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,xa=new Map});import{execFile as dC}from"node:child_process";import{promisify as fC}from"node:util";async function Yo(e){if(!e)return null;let t=jk.get(e);if(t&&Date.now()-t.at<mC)return t.branch;let n=null;try{let{stdout:r}=await pC("git",["-C",e,"symbolic-ref","--short","-q","HEAD"],{timeout:5e3});n=r.trim()||null}catch{n=null}return jk.set(e,{at:Date.now(),branch:n}),n}function Lk(e){return String(e||"").match(hC)?.[1]??null}function wC(e,t){let n=Lk(e);return!!n&&!!t&&n===t}function Fk(e,t){let n=Lk(e);return!!n&&n!==t}function gC(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 Zo(e){let t=[],n=r=>{r&&!t.includes(r)&&t.push(r)};n(e?.workspace?.branch??null);for(let r of gC(e))n(await Yo(r));return t}async function Uc(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=>wC(t,a.id));if(s)return s;for(let a of r)if(!(a.archivedAt||a.transferredTo)&&(await Zo(a)).includes(t))return a;return null}var pC,mC,jk,hC,Qo=O(()=>{pC=fC(dC),mC=5e3,jk=new Map;hC=/^track\/.+-([0-9a-f]{8})$/});import{execFile as yC}from"node:child_process";import{promisify as bC}from"node:util";async function _f(e,t,{midTurn:n=Gr,pollMs:r=kC,capMs:o=xC}={}){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 Gk(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){Uk.lastIndex=n;let o=Uk.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(`
|
|
143
|
+
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+IC(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 If(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function zk(e){let t=Gk(e),n=t.match(AC);if(!n)return null;let r=If(t,n.index);return _C.test(r)?null:{repo:r.match(Vk)?.[1]??null,head:r.match(RC)?.[1]??null}}function Jk(e){let t=Gk(e),n=t.match(EC);if(!n)return null;let r=If(t,n.index),o=If(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("-")){OC.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(Vk)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function MC(e,t,{exclude:n=()=>!1}={}){let r=async i=>{sn("observer");let{stdout:c}=await Hk("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",CC],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw Qn(l)&&er(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-SC&&!n(c)),s=await Ct({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 NC(e,t){if(t)return[t];let n=[];for(let r of Xk(e)){let o=await pe(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await Xt().catch(()=>[])){let o=await pe(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function Xk(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 PC(e,t,n){let r=await Zo(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)||$C(c.headRefName,e.id),i=[];for(let c of await NC(e,t?.repo??null)){let l;try{l=await MC(c,n,{exclude:a})}catch{continue}for(let u of l){let f=await Xe(c,u.number);if(s(u.headRefName)){if(f&&f.trackId===e.id&&f.announced)return{settled:!0};if(f&&f.trackId!==e.id)continue;return{slug:c,pr:u}}f&&(f.trackId!==e.id||f.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 Yk(e,t,{delays:n=Wk,turnEnd:r}={}){let o=zk(t.command);if(!o)return null;let s=!!Jk(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(w=>setTimeout(w,l));let u=await PC(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:f,pr:d}=u,{link:p,created:m}=await ct({repo:f,number:d.number,trackId:e.id,url:d.url,headRef:d.headRefName,title:d.title,source:"observed"});return!m&&!(p?.trackId===e.id&&await rn(f,d.number))?null:(await _f(t.box,t.agent,r),await R({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:d.number,url:d.url,title:d.title,branch:d.headRefName,repo:f,...s?{selfMerge:!0}:{}}}),d)}return null}finally{tr.delete(a)}})();return c.catch(()=>tr.delete(a)),c}function Zk(e,t,{delays:n=Wk,turnEnd:r,openedWait:o}={}){let s=Jk(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 f=[];if(c)f=[c];else{let m=Xk(e);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of m){let y=await pe(w).catch(()=>null);y&&!f.includes(y)&&f.push(y)}}if(l||=(await Zo(e).catch(()=>[])).filter(m=>m!==e.workspace?.branch)[0]||e.workspace?.branch,!f.length||!l)return null;c=f[0];let d=async(m,w)=>{let y=await Xe(c,m.number);return y&&y.trackId!==e.id?null:(y||await ct({repo:c,number:m.number,trackId:e.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await ma(c,m.number),w&&((await B(e).catch(()=>[])).some(E=>E.type==="pr.state.changed"&&(E.payload?.change==="auto-armed"||E.payload?.change==="merged")&&E.payload?.number===m.number)||(await _f(t.box,t.agent,r),await R({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:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c}}))),m)},p=null;for(let m of n){await new Promise(C=>setTimeout(C,m));let w=[];for(let C of f)try{sn("observer");let{stdout:N}=await Hk("gh",["pr","view",String(l),"--repo",C,"--json",BC],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await Ct({slug:C})}}),T=JSON.parse(N||"null");T?.number&&w.push({slug:C,pr:T})}catch(N){Qn(N)&&er(N)}let y=w.find(C=>C.pr.state==="MERGED")??(w.length===1?w[0]:null);if(!y)continue;c=y.slug;let b=y.pr;if(!b?.number)continue;if(b.state==="CLOSED")return null;if(b.state!=="MERGED"){if(p=b,s.auto)return d(b,!0);continue}let x=await Xe(c,b.number);if(x&&x.trackId!==e.id)return null;x||await ct({repo:c,number:b.number,trackId:e.id,url:b.url,headRef:b.headRefName,title:b.title,source:"merge-observed"}),await _f(t.box,t.agent,r);let E=await B(e).catch(()=>[]);if(E.some(C=>C.type==="pr.state.changed"&&C.payload?.change==="merged"&&C.payload?.number===b.number))return null;let _=C=>C.some(N=>N.type==="pr.state.changed"&&N.payload?.change==="opened"&&N.payload?.number===b.number),q=0;if(!_(E)){if(zk(t.command)&&await rn(c,b.number)){let C=Date.now();q=C+1,await R({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:C,change:"opened",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c,selfMerge:!0}})}else if((await Xe(c,b.number))?.announced){let C=o?.capMs??vC,N=o?.pollMs??TC,T=Date.now()+C;for(;Date.now()<T&&(await new Promise(W=>setTimeout(W,N)),!_(await B(e).catch(()=>[]))););}}return await R({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(),q),change:"merged",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c}}),await ln(c,b.number).catch(()=>{}),await cn(c,b.number).catch(()=>{}),await Xn(c,b.number,"merged"),b}return p?d(p,!1):null}finally{tr.delete(a)}})();return i.catch(()=>tr.delete(a)),i}var Hk,Wk,kC,xC,SC,vC,TC,qk,Kk,AC,EC,_C,Uk,IC,Vk,RC,$C,OC,CC,tr,BC,Qk=O(()=>{ue();Sr();Ve();ga();Yn();Vr();Sa();Qo();Hk=bC(yC),Wk=[4e3,15e3,45e3],kC=2500,xC=5*6e4;SC=15e3,vC=9e4,TC=2e3,qk=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,Kk=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,AC=new RegExp(`${qk}${Kk}create\\b`),EC=new RegExp(`${qk}${Kk}merge\\b`),_C=/\s--web\b/,Uk=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,IC=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");Vk=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,RC=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,$C=Fk;OC=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);CC="number,url,title,headRefName,createdAt";tr=new Set;BC="number,url,title,headRefName,state"});import DC from"node:fs/promises";import $f from"node:path";import jC from"node:os";function Rf(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function Hc(e){let t=LC.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=Rf(n,"from"),s=o?o.replace(/^uds:/,""):null,a=ex(s);return{socket:s,pid:a,name:Rf(n,"from-name"),mode:Rf(n,"from-mode"),text:r.trim()}}function ex(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function FC(){return process.env.CLAUDE_CONFIG_DIR?$f.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):$f.join(jC.homedir(),".claude","sessions")}async function UC(e){let t=ex(e);if(!t)return null;try{let n=await DC.readFile($f.join(FC(),`${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 tx(e,t){let n=await UC(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 LC,Of=O(()=>{LC=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function Cf(e=process.env){return!!(be(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Mf({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!Cf(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),nx);try{let a=await rx(`${be(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 ox(e,{provider:t=null,cls:n="other",status:r=null,text:o=""}={},{env:s=process.env,now:a=Date.now}={}){if(!e?.id||!String(e.name||"").startsWith("\u21BB"))return{ok:!1,source:"not-a-loop-track"};if(!Cf(s))return{ok:!1,source:"unconfigured"};let i=Wc.get(e.id)||0;if(a()-i<HC)return{ok:!1,source:"debounced"};Wc.set(e.id,a());let c=await qc({env:s}),l=c.loops.find(d=>d.trackId===e.id);if(!l)return Wc.delete(e.id),{ok:!1,source:c.source==="plane"?"no-loop":c.source};let u=`${t||"provider"} ${r?r+" ":""}${n}: ${String(o||"").replace(/\s+/g," ").trim()}`.slice(0,500),f=await Mf({loopId:l.id,outcome:"errored",note:u,env:s});return f.ok||Wc.delete(e.id),{...f,loop:{id:l.id,name:l.name}}}async function qc({env:e=process.env}={}){if(!Cf(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),nx);try{let r=await rx(`${be(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 nx,rx,HC,Wc,Nf=O(()=>{Ve();nx=1e4,rx=(...e)=>fetch(...e);HC=10*6e4,Wc=new Map});import{randomUUID as WC}from"node:crypto";function va(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function sx(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function ax(e){return`job|${e.box}|id:${e.id}`}async function Bf(e){return Pf.has(e.id)?Pf.get(e.id):(Kc.has(e.id)||Kc.set(e.id,B(e).then(t=>{let n={keys:new Set,cmdN:0};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(va({role:o,sourceId:r.payload.sourceId})),n.keys.add(va({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(va({role:"user",sourceId:r.payload.sourceId})),n.keys.add(va({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(sx({id:o.id,box:r.boxId,ts:o.ts,command:o.command})),Number(o.n)>n.cmdN&&(n.cmdN=Number(o.n))}else r.type==="job.started"&&r.payload?.id&&n.keys.add(ax({id:r.payload.id,box:r.boxId}));return Pf.set(e.id,n),n}).finally(()=>Kc.delete(e.id))),Kc.get(e.id))}async function ix(e,t){try{let n=(await Bf(e)).keys;for(let r of t||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=va(r);if(n.has(o))continue;n.add(o);let s=r.role==="user"?Hc(r.text):null;if(s){await R(e,{type:"peer.message",actor:"peer",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:s.text,ts:r.ts,keyText:String(r.text||"").slice(0,40),from:await tx(s,Ta),boxRole:r.boxRole,sourceId:r.sourceId||void 0}});continue}let a=zw(r);if(a){await R(e,{type:"provider.error",actor:"system",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{cls:a.cls,code:a.code||void 0,status:a.status||void 0,requestId:a.requestId||void 0,text:r.text,ts:r.ts,model:r.model||null,boxRole:r.boxRole,sourceId:r.sourceId||void 0}}),ox(e,{provider:r.agent||null,cls:a.cls,status:a.status,text:r.text}).catch(()=>{});continue}if(await R(e,{type:r.role==="user"?"turn.user":"turn.agent",actor:r.role==="user"?"user":"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{text:r.text,ts:r.ts,model:r.model||null,tools:r.tools||void 0,boxRole:r.boxRole,sourceId:r.sourceId||void 0,note:r.note||void 0,images:r.images?.length?r.images:void 0}}),r.role==="assistant"&&r.box===e.leadBox&&e.workspace?.branch){let i=Kb(r.text);if(i){let c=zb(i),l;if(e.workspace?.kind==="multi"){let p=await es(e).catch(()=>[]);l=p.length?p.map(m=>({name:m.name,slug:m.repo??null})):void 0}let u=(e.workspace?.kind==="multi"?null:await Yo(e.cwd).catch(()=>null))||e.workspace.branch,f=await Pk(e,{branch:u,rebase:c}).catch(()=>null),d=f?null:Vb(i);if(f&&f.available.length===0)continue;await R(e,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:WC(),ts:r.ts,excerpt:i,branch:u,base:e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:r.boxRole,available:f?.available,defaults:f?.defaults,prState:f?.prState,openPr:f?.openPr||void 0,repos:f?.repos||l,offered:f?.defaults||d?.offered||void 0,toPr:d?.toPr||void 0}})}}}}catch{}}async function cx(e,t){try{let n=await Bf(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=sx(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await R(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:n.cmdN}}),Yk(e,o),Zk(e,o))}}catch{}}async function lx(e,t){try{let n=await Bf(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=ax(o);n.keys.has(s)||(n.keys.add(s),await R(e,{type:"job.started",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,detail:o.detail||null,ts:o.ts??null,boxRole:o.boxRole}}))}}catch{}}var Pf,Kc,ux=O(()=>{ue();qo();Bk();Qk();Qo();Of();Vr();Ji();Nf();Pf=new Map,Kc=new Map});function qC(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
144
|
+
`)){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 KC(e,t){let n=`${e.id}:${t.name}`;if(!Df.has(n)){let r=await B(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);Df.set(n,o)}return Df.get(n)}async function dx(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await Ir(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await KC(e,t),o=qC(await tt(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 R(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:qn(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await R(e,{type:"context.usage",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{occupancy:a.occupancy,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}var Df,fx=O(()=>{ue();$r();vt();ia();Df=new Map});import Gc from"node:fs";import Lf from"node:path";import GC from"node:os";async function JC({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(zC,{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:px})});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<px)break}return r}function XC(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 hx(){try{return JSON.parse(Gc.readFileSync(Vc(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function ZC(e){try{Gc.mkdirSync(Lf.dirname(Vc()),{recursive:!0});let t=Vc()+".tmp";Gc.writeFileSync(t,JSON.stringify(e)),Gc.renameSync(t,Vc())}catch{}}async function eM({fetchImpl:e,now:t=Date.now()}={}){if(nr.rows&&t-nr.at<YC)return nr.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=hx(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await JC({sinceMs:o,cookie:n,fetchImpl:e});nr={at:t,rows:s.map(XC).filter(Boolean),error:null}}catch(s){nr={at:t,rows:null,error:String(s?.message||s)}}return nr.rows}async function tM(e,t){let n=`${e.id}:${t.name}`;if(!jf.has(n)){let r=await B(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);jf.set(n,o)}return jf.get(n)}async function wx(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!mx())return;let r=await eM({fetchImpl:n});if(!r||!r.length)return;let o=await jy(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await tM(e,t),a=hx(),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",f=l.tokens||null,d=l.vendorCents!=null?l.vendorCents/100:0;!d&&f&&(d=qn(u,f)),!d&&l.chargedCents!=null&&(d=l.chargedCents/100),await R(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.name,provider:"cursor",payload:{usd:d,model:u,...f?{tokens:f}:{},...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}})}ZC({sinceMs:c,seen:[...i].slice(-QC)})}catch{}}var VC,mx,zC,px,YC,nr,Vc,QC,Ff,jf,Uf=O(()=>{ue();Md();ia();VC=process.env.BOSTRAT_STATE_DIR||Lf.join(process.env.HOME||GC.tmpdir(),".bostrat"),mx=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,zC="https://cursor.com/api/dashboard/get-filtered-usage-events",px=100;YC=5*6e4,nr={at:0,rows:null,error:null},Vc=()=>Lf.join(VC,"cursor-usage.json");QC=5e3;Ff=()=>({enabled:mx(),lastPollAt:nr.at||null,error:nr.error}),jf=new Map});import _a from"node:fs/promises";import ot from"node:path";import nM from"node:os";import rM from"node:net";import{randomUUID as oM}from"node:crypto";import{execFile as sM}from"node:child_process";import{promisify as aM}from"node:util";function pM(e){for(let t of zf(e))if(!Sx.test(t))return t;return null}function vx(e){let t=zf(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function mM(e,t){let n=vx(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return ot.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return ot.resolve(e,n[r+1])}return e}function wM(e){let t=!1,n=!1;for(let r of zf(e)){if(!t){if(Sx.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&Hf(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,Hf(r))return`serves the filesystem root ("${r}")`;continue}if(Hf(r))return`serves the filesystem root ("${r}")`;if(hM(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function yM(e,t){if(ot.resolve(e)===ot.resolve(t))return!0;let[n,r]=await Promise.all([zc(e),zc(t)]);return n===r}async function bM(e){let n=(await _a.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 Tx(ot.join(e,o.name,Kf))?`./${o.name}`:null))).filter(Boolean)}async function kM(e,t){let[n,r]=await Promise.all([zc(e),zc(t)]),o=ot.relative(n,r);if(o&&(o.startsWith("..")||ot.isAbsolute(o)))throw Aa(`serve directory escapes the Track workspace: ${t}`)}async function xM(e,t){let n=wM(t);if(n)throw Aa(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await gM(e))throw Aa(`serve directory does not exist: ${e}`);let r=pM(t);if(!r)return;if(!r.includes("/")&&!await Sw(r,{loginShell:!0,failOpen:!0}))throw Aa(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(uM.test(t)||dM.has(r)||!lM.has(r)||!vx(t).includes("run")||fM.test(t))return;let o=mM(e,t);if(await Tx(ot.join(o,Kf)))return;let s=await bM(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Aa(`no ${Kf} in ${o}${a}`)}async function fn(){return Lt||(Wf||=(async()=>{try{Lt=JSON.parse(await _a.readFile(xx,"utf8"))}catch{Lt={views:[],adoptWatermarks:{}}}finally{Wf=null}return Lt.views||=[],Lt.adoptWatermarks||={},Lt})(),Wf)}async function Ut(){await et(xx,Lt)}function dn(e,t,n={}){return R({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 Xf(e){return Ea.get(e)||null}function Ex(e,{name:t,box:n}={}){let r=Ea.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 Ea.set(e,o),o}function Gf(e,t){let n=Ea.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function Jc(e,{error:t=null}={}){let n=Ea.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function _x(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 Vf(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function SM(e,t){let n=Vf(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!bx.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!bx.has(s))return s}}function Ia(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=rM.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 Yf(e){if(process.platform!=="darwin"){await Ft("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await Ft("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 Ix(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function vM(e,t){let n=new Set(e.views.map(o=>o.port)),r=Ix();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=gx;o<=yx;o++)if(!(n.has(o)||r.has(o))&&!await Ia(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${gx}-${yx}`)}async function TM(e){try{let{stdout:t}=await Ft(L,U("list-sessions","-F","#{session_name}"));return t.split(`
|
|
145
|
+
`).some(n=>n.trim()===ts(e))}catch{return!1}}async function Ra(e){try{await Ft(L,U("kill-session","-t",ts(e)))}catch{}}async function Zf(e){if(!await qs())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=ts(e.slug),n=e.serveCwd??e.cwd,r=ot.dirname(process.execPath);await Ft(L,U("new-session","-d","-s",t,"-c",n)),await Ft(L,U("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await Ft(L,U("send-keys","-t",t,"Enter")),await Ft(L,U("send-keys","-t",t,"-l",e.cmd)),await Ft(L,U("send-keys","-t",t,"Enter"))}async function kx(e,t=12){try{let{stdout:n}=await Ft(L,U("capture-pane","-t",ts(e),"-p"));return String(n||"").trimEnd().split(`
|
|
146
146
|
`).filter(Boolean).slice(-t).join(`
|
|
147
|
-
`)}catch{return""}}async function oM(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await nM(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await rx(e.slug)||"server exited before listening"),cn.delete(e.id),await ln(e,e.state,e.error?{error:e.error}:{}),!0}if(await xa(e.port))return cn.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await ln(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>rM?(e.state="error",e.error=await rx(e.slug)||"never started listening",await ln(e,"error",{error:e.error}),!0):!1;let o=(cn.get(e.id)||0)+1;return cn.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function Fc(){if(!$f){$f=!0;try{let e=await un(),t=!1;for(let n of e.views)await oM(n)&&(t=!0);t&&await Lt()}finally{$f=!1}}}async function Lf({trackId:e}={}){await Fc();let t=await un();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function fx(e){let n=(await un()).views.find(r=>r.id===e);return n?{...n}:null}function Ff(e){return Dt&&Dt.views.find(t=>t.slug===e)||null}async function Uf({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=rt.resolve(r,o??"."),f=i==="detected"?null:async()=>{await ZC(r,u),await QC(u,n)},d=await un();if(i!=="detected"){let w=Mf(t),y=String(n).trim(),b=d.views.find(x=>x.trackId===e&&(x.slug===w||Mf(x.name)===w||x.cmd===y&&rt.resolve(x.serveCwd??x.cwd)===u));if(b)return sM(d,b,{cmd:y,box:a,branch:c,serveCwd:l?u:void 0,validate:f})}if(f&&await f(),d.views.filter(w=>w.state==="live"||w.state==="starting").length>=Qn)throw new Error(`view cap reached (${Qn} live) \u2014 stop one first`);let h={id:BC().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:eM(d,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await tM(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await jf(h),d.views.push(h),await Lt(),await ln(h,"created",{origin:i}),Cf(e,h.id),i==="detected"&&await xa(h.port)&&(h.state="live",h.lastHealthyAt=Date.now(),await Lt(),await ln(h,"live")),{...h}}async function sM(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 YC(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await xa(t.port))return await Lt(),Cf(t.trackId,t.id),{...t,reused:!0};if(a&&await a(),e.views.filter(f=>f.id!==t.id&&(f.state==="live"||f.state==="starting")).length>=Qn)throw new Error(`view cap reached (${Qn} live) \u2014 stop one first`);return t.origin==="detected"?(await Df(t.port),t.origin="agent"):await va(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),cn.delete(t.id),await jf(t),await Lt(),await ln(t,"created",{restarted:!0,reused:!0}),Cf(t.trackId,t.id),{...t,reused:!0}}async function px(e){let n=(await un()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await Df(n.port):await va(n.slug),n.state!=="stopped"&&(n.state="stopped",cn.delete(n.id),await Lt(),await ln(n,"stopped")),{...n}}async function mx(e){let t=await un(),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>=Qn)throw new Error(`view cap reached (${Qn} live) \u2014 stop one first`);return await va(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),cn.delete(n.id),await jf(n),await Lt(),await ln(n,"created",{restarted:!0}),{...n}}async function hx(e){let t=await un(),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 Df(r.port):await va(r.slug),t.views.splice(n,1),cn.delete(r.id),await Lt(),await ln(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function wx(e){let t=await un(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await va(r.slug),cn.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await Lt()),n.length}async function gx(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await un(),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 Lt()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(aM)){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(!dx().has(c)&&!n.views.some(l=>l.port===c)&&await xa(c))try{i.push(await Uf({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 jt,LC,ox,ex,tx,Qn,FC,Vo,nx,ya,UC,Of,HC,WC,qC,Nf,sx,If,VC,JC,ix,jc,Dt,Rf,ba,Pf,cx,cn,rM,$f,aM,Uc=O(()=>{ze();le();Rn();jt=jC(DC),LC=process.env.BOSTRAT_STATE_DIR||rt.join(process.env.HOME||NC.tmpdir(),".bostrat"),ox=rt.join(LC,"views.json"),[ex,tx]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),Qn=Number(process.env.BOSTRAT_VIEW_MAX)||3,FC="bostrat-view-",Vo=e=>`${FC}${e}`,nx=new Set(["bostrat","www","api","views","app"]),ya=e=>Object.assign(new Error(e),{status:400}),UC=new Set(["npm","pnpm","yarn","bun"]),Of="package.json",HC=/[|;&`]|\$\(/,WC=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),qC=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Nf=e=>String(e).trim().split(/\s+/).filter(Boolean),sx=/^[A-Za-z_][A-Za-z0-9_]*=/;If=e=>e==="/",VC=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");JC=e=>ka.stat(e).then(t=>t.isDirectory(),()=>!1),ix=e=>ka.stat(e).then(t=>t.isFile(),()=>!1),jc=e=>ka.realpath(e).catch(()=>rt.resolve(e));Dt=null,Rf=null;ba=new Map,Pf=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,cx=12e3;cn=new Map,rM=12e4;$f=!1;aM=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function Hc(e,t=Date.now()){let n=null;for(let r of e||[]){let o=r.payload?.ts??r.ts;r.type==="pr.offer.answered"?n=r.payload?.action==="pr"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null,offerType:"offer"}:null:r.type==="pr.exists.answered"?n=r.payload?.choice==="push"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null,offerType:"exists"}:null:r.type==="pr.state.changed"&&(r.payload?.change==="opened"||r.payload?.change==="pushed")&&n!=null&&o>=n.ts&&(n=null)}return n!=null&&t-n.ts>cM?null:n}function lM(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function yx(e){let t=e?.workspace?.branch;if(!t)return;let n;try{n=await B(e)}catch{return}let r=Hc(n);if(r==null)return;let o=(e.boxes||[]).find(u=>u.name===e.leadBox);if(await Fr(e.leadBox,o?.agent).catch(()=>!1))return;let s=Date.now();if(s-(Hf.get(e.id)||0)<iM)return;Hf.set(e.id,s);let a;try{a=await Nt(e.cwd,{caller:"reconcile"})}catch{return}let i=await qo(e).catch(()=>[t]),c=(a||[]).find(u=>i.includes(u.headRefName));if(!c)return;let l=await de(e.cwd).catch(()=>null);if(l){let{link:u,created:f}=await it({repo:l,number:c.number,trackId:e.id,url:c.url,headRef:c.headRefName||t,title:c.title,source:"reconciled"});if(!f&&!(u?.trackId===e.id&&await tn(l,c.number))){if(!lM(u,r))return;let d=await B(e).catch(()=>null);if(d&&Hc(d)==null)return;await I(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"pushed",number:c.number,url:c.url,title:c.title,branch:t,repo:l}});return}}await I(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:c.number,url:c.url,title:c.title,branch:t,...l?{repo:l}:{}}}).catch(()=>{}),Hf.delete(e.id)}var iM,Hf,cM,Wf=O(()=>{le();Ho();hr();Vn();ha();Ko();iM=6e3,Hf=new Map,cM=30*6e4});import qf from"node:os";import{execFile as uM}from"node:child_process";import{promisify as dM}from"node:util";async function Wc(){if(process.platform!=="darwin")return qf.freemem();try{let{stdout:e}=await fM("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:qf.freemem()}catch{return qf.freemem()}}var fM,Kf=O(()=>{fM=dM(uM)});import Me from"node:fs/promises";import Pe from"node:path";import xx from"node:os";import{randomUUID as pM}from"node:crypto";import{execFile as mM}from"node:child_process";import{promisify as hM}from"node:util";import{chromium as wM}from"playwright-core";async function Ht(){return Hr||(Gf||=(async()=>{try{Hr=JSON.parse(await Me.readFile(Sx,"utf8"))}catch{Hr={mocks:[]}}finally{Gf=null}return Hr.mocks||=[],Hr})(),Gf)}async function ht(){await Qe(Sx,Hr)}function Ex(e,t){Ut(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&Ut(e,t.v,"png")}function Ye(e,t,n={}){return I({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 wt(e,t){let n=Ta(e.cwd);return{html:Pe.join(n,`${e.id}.v${t}.html`),png:Pe.join(n,`${e.id}.v${t}.png`)}}function Sa(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?Pe.join(e.cwd,r):null}function Et(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 Jf(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function qc(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(AM)}function EM(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 _M(e){let t=Buffer.alloc(24),n=await Me.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function IM({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 RM(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((h,w)=>{r.onload=h,r.onerror=()=>w(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,f=s,d=-1,m=-1;for(let h=0;h<s;h++)for(let w=h*o*4,y=0;y<o;y++,w+=4)Math.abs(c[w]-l[0])+Math.abs(c[w+1]-l[1])+Math.abs(c[w+2]-l[2])>24&&(y<u&&(u=y),y>d&&(d=y),h<f&&(f=h),m=h);return d<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:f,right:d,bottom:m}}},t.toString("base64"))}async function $M(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((f,d)=>{c.onload=f,c.onerror=()=>d(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 OM({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await wM.launch({executablePath:yM,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new At("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:zo})).newPage();a.setDefaultTimeout(Jo),await a.goto(`file://${e}`,{waitUntil:"load",timeout:Jo}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Jo});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await RM(a,i),u=l&&IM({...l,pad:xM*zo,align:zo});u&&(i=await $M(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Tx)throw new At(`capture produced a ${i.length}-byte image`);let c=Jf(i);if(!c)throw new At("output is not a PNG");return await Me.writeFile(t,i),{width:Math.round(c.width/zo),height:Math.round(c.height/zo),dpr:zo,renderMs:Date.now()-r}}catch(s){throw s instanceof At?s:new At(/timeout/i.test(s?.message||"")?`render timed out after ${Jo}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function kx({htmlPath:e,outPath:t,width:n}){let r=await Me.mkdtemp(Pe.join(xx.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await zf(bM,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Jo,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new At(c?.killed?`render timed out after ${Jo}ms`:"firefox exited non-zero",s)}let a=await Me.stat(t).catch(()=>null);if(!a||a.size<Tx)throw new At(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Jf(await _M(t));if(!i)throw new At("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await Me.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function CM(e){if(gM==="firefox")return kx(e);try{return await OM(e)}catch(t){if(/failed to launch/i.test(t?.message||""))return kx(e);throw t}}function _x(e){return Vf=Vf.then(e,e),Vf}async function Ix(e,t){let n=await Ht(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await ht(),await Ye(r,"rendering",{version:t});try{let o=await Wc();if(o<vM)throw new At(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=wt(r,t),a=await MM({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await Me.rm(s.png,{force:!0}).catch(()=>{});return}Yf(r,{v:t,source:"html",htmlRel:Pe.relative(r.cwd,s.html),pngRel:Pe.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await ht(),await Ye(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),Ft(r),Ut(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await ht(),await Ye(r,"error",{version:t,error:r.error}),Ft(r)}}}function Yf(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 NM({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?kM:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Aa({trackId:e,group:t}={}){let n=await Ht(),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 dn(e){let n=(await Ht()).mocks.find(r=>r.id===e);return n?{...n}:null}function Rx({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&&!qc(t))throw new Error("image must be a PNG");if(n){let o=EM(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>bx)throw new Error(`video must be ${bx/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function $x({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let f=NM({trackId:e,name:t,cwd:s,width:a}),d=typeof n=="string"&&n.trim(),m=Rx({html:n,image:r,video:o}),h=await Ht(),w={id:pM().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:f,height:null,dpr:null,state:"queued",source:d?"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 Me.mkdir(Ta(s),{recursive:!0}),h.mocks.push(w),d?(await Me.writeFile(wt(w,1).html,n),await ht(),await Ye(w,"created"),Ft(w),Ut(w,1,"html"),_x(()=>Ix(w.id,1))):o?(await Cx(w,1,o,m),await ht(),await Ye(w,"created"),await Ye(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr,durationS:w.versions[0]?.durationS??null}),Ft(w),Ex(w,w.versions[0])):(await Mx(w,1,r),await ht(),await Ye(w,"created"),await Ye(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr}),Ft(w),Ut(w,1,"png")),{...w}}async function Ox(e,{html:t,image:n,video:r,width:o}={}){let a=(await Ht()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=Rx({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 Me.mkdir(Ta(a.cwd),{recursive:!0}),i?(a.state="queued",await Me.writeFile(wt(a,l).html,t),await ht(),await Ye(a,"updated",{version:l}),Ft(a),Ut(a,l,"html"),_x(()=>Ix(a.id,l))):r?(await Cx(a,l,r,c),await ht(),await Ye(a,"updated",{version:l}),await Ye(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:Et(a)?.durationS??null}),Ft(a),Ex(a,a.versions.find(u=>u.v===l))):(await Mx(a,l,n),await ht(),await Ye(a,"updated",{version:l}),await Ye(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Ft(a),Ut(a,l,"png")),{...a}}async function PM(e){try{let{stdout:t}=await zf(SM,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:Ax,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 BM(e,t){try{await zf(TM,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:Ax,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await Me.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function Cx(e,t,n,r){let o=Ta(e.cwd),s=Pe.join(o,`${e.id}.v${t}.${r.ext}`),a=Pe.join(o,`${e.id}.v${t}.poster.png`);await Me.writeFile(s,n);let i=await PM(s),c=await BM(s,a);Yf(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:Pe.relative(e.cwd,s),posterRel:c?Pe.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 Mx(e,t,n){let r=Jf(n);if(!r)throw new Error("image must be a PNG");let o=wt(e,t);await Me.writeFile(o.png,n),Yf(e,{v:t,source:"image",htmlRel:null,pngRel:Pe.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function Nx(e){let t=await Ht(),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 Ye(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Px(e){let t=await Ht(),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=wt(r,o);await Me.rm(s.html,{force:!0}).catch(()=>{}),await Me.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await Me.rm(Pe.join(r.cwd,s),{force:!0}).catch(()=>{});return await ht(),await Ye(r,"deleted"),kt("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function Bx(e){let n=(await Ht()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=Pe.join(vx,"mock-archive",e.id),o=Ta(e.cwd),s=Pe.join(r,".bostrat","mocks");await Me.mkdir(s,{recursive:!0});let a=await Me.readdir(o).catch(()=>[]);for(let i of a)await Me.copyFile(Pe.join(o,i),Pe.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 Me.stat(Pe.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(),Ft(i)}return await ht(),n.length}async function Dx(e){let t=await Ht(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await ht();for(let r of n)kt("mocks",r.id).catch(()=>{})}return n.length}async function jx(){if(!Te())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Be("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Ht();for(let r of n.mocks){await Ft(r);for(let o of r.versions||[])o.htmlRel&&await Ut(r,o.v,"html"),(o.pngRel||o.posterRel)&&await Ut(r,o.v,"png"),o.videoRel&&await Ut(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var zf,vx,Sx,gM,yM,bM,kM,zo,xM,Tx,Jo,vM,bx,SM,TM,Ax,At,Hr,Gf,Ft,Ut,Ta,AM,MM,Vf,Yo=O(()=>{le();Rn();Kf();On();zf=hM(mM),vx=process.env.BOSTRAT_STATE_DIR||Pe.join(process.env.HOME||xx.tmpdir(),".bostrat"),Sx=Pe.join(vx,"mocks.json"),gM=process.env.BOSTRAT_MOCK_ENGINE||"chromium",yM=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",bM=process.env.BOSTRAT_FIREFOX_BIN||"firefox",kM=1300,zo=2,xM=32,Tx=1500,Jo=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,vM=700*1024*1024,bx=100*1024*1024,SM=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",TM=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",Ax=2e4,At=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
147
|
+
`)}catch{return""}}async function EM(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await TM(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await kx(e.slug)||"server exited before listening"),un.delete(e.id),await dn(e,e.state,e.error?{error:e.error}:{}),!0}if(await Ia(e.port))return un.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await dn(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>AM?(e.state="error",e.error=await kx(e.slug)||"never started listening",await dn(e,"error",{error:e.error}),!0):!1;let o=(un.get(e.id)||0)+1;return un.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function Xc(){if(!qf){qf=!0;try{let e=await fn(),t=!1;for(let n of e.views)await EM(n)&&(t=!0);t&&await Ut()}finally{qf=!1}}}async function Yc({trackId:e}={}){await Xc();let t=await fn();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function Rx(e){let n=(await fn()).views.find(r=>r.id===e);return n?{...n}:null}function Qf(e){return Lt&&Lt.views.find(t=>t.slug===e)||null}async function ep({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=ot.resolve(r,o??"."),f=i==="detected"?null:async()=>{await kM(r,u),await xM(u,n)},d=await fn();if(i!=="detected"){let w=Vf(t),y=String(n).trim(),b=d.views.find(x=>x.trackId===e&&(x.slug===w||Vf(x.name)===w||x.cmd===y&&ot.resolve(x.serveCwd??x.cwd)===u));if(b)return _M(d,b,{cmd:y,box:a,branch:c,serveCwd:l?u:void 0,validate:f})}if(f&&await f(),d.views.filter(w=>w.state==="live"||w.state==="starting").length>=rr)throw new Error(`view cap reached (${rr} live) \u2014 stop one first`);let m={id:oM().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:SM(d,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await vM(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Zf(m),d.views.push(m),await Ut(),await dn(m,"created",{origin:i}),Gf(e,m.id),i==="detected"&&await Ia(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await Ut(),await dn(m,"live")),{...m}}async function _M(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 yM(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Ia(t.port))return await Ut(),Gf(t.trackId,t.id),{...t,reused:!0};if(a&&await a(),e.views.filter(f=>f.id!==t.id&&(f.state==="live"||f.state==="starting")).length>=rr)throw new Error(`view cap reached (${rr} live) \u2014 stop one first`);return t.origin==="detected"?(await Yf(t.port),t.origin="agent"):await Ra(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),un.delete(t.id),await Zf(t),await Ut(),await dn(t,"created",{restarted:!0,reused:!0}),Gf(t.trackId,t.id),{...t,reused:!0}}async function $x(e){let n=(await fn()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await Yf(n.port):await Ra(n.slug),n.state!=="stopped"&&(n.state="stopped",un.delete(n.id),await Ut(),await dn(n,"stopped")),{...n}}async function Ox(e){let t=await fn(),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>=rr)throw new Error(`view cap reached (${rr} live) \u2014 stop one first`);return await Ra(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),un.delete(n.id),await Zf(n),await Ut(),await dn(n,"created",{restarted:!0}),{...n}}async function Cx(e){let t=await fn(),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 Yf(r.port):await Ra(r.slug),t.views.splice(n,1),un.delete(r.id),await Ut(),await dn(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function Mx(e){let t=await fn(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Ra(r.slug),un.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await Ut()),n.length}async function Nx(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await fn(),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 Ut()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(IM)){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(!Ix().has(c)&&!n.views.some(l=>l.port===c)&&await Ia(c))try{i.push(await ep({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 Ft,iM,xx,gx,yx,rr,cM,ts,bx,Aa,lM,Kf,uM,dM,fM,zf,Sx,Hf,hM,gM,Tx,zc,Lt,Wf,Ea,Jf,Ax,un,AM,qf,IM,Zc=O(()=>{Je();ue();Cn();Ft=aM(sM),iM=process.env.BOSTRAT_STATE_DIR||ot.join(process.env.HOME||nM.tmpdir(),".bostrat"),xx=ot.join(iM,"views.json"),[gx,yx]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),rr=Number(process.env.BOSTRAT_VIEW_MAX)||3,cM="bostrat-view-",ts=e=>`${cM}${e}`,bx=new Set(["bostrat","www","api","views","app"]),Aa=e=>Object.assign(new Error(e),{status:400}),lM=new Set(["npm","pnpm","yarn","bun"]),Kf="package.json",uM=/[|;&`]|\$\(/,dM=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),fM=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,zf=e=>String(e).trim().split(/\s+/).filter(Boolean),Sx=/^[A-Za-z_][A-Za-z0-9_]*=/;Hf=e=>e==="/",hM=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");gM=e=>_a.stat(e).then(t=>t.isDirectory(),()=>!1),Tx=e=>_a.stat(e).then(t=>t.isFile(),()=>!1),zc=e=>_a.realpath(e).catch(()=>ot.resolve(e));Lt=null,Wf=null;Ea=new Map,Jf=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,Ax=12e3;un=new Map,AM=12e4;qf=!1;IM=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function Qc(e,t=Date.now()){let n=null;for(let r of e||[]){let o=r.payload?.ts??r.ts;r.type==="pr.offer.answered"?n=r.payload?.action==="pr"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null,offerType:"offer"}:null:r.type==="pr.exists.answered"?n=r.payload?.choice==="push"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null,offerType:"exists"}:null:r.type==="pr.state.changed"&&(r.payload?.change==="opened"||r.payload?.change==="pushed")&&n!=null&&o>=n.ts&&(n=null)}return n!=null&&t-n.ts>$M?null:n}function OM(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function Px(e){let t=e?.workspace?.branch;if(!t)return;let n;try{n=await B(e)}catch{return}let r=Qc(n);if(r==null)return;let o=(e.boxes||[]).find(u=>u.name===e.leadBox);if(await Gr(e.leadBox,o?.agent).catch(()=>!1))return;let s=Date.now();if(s-(tp.get(e.id)||0)<RM)return;tp.set(e.id,s);let a;try{a=await Bt(e.cwd,{caller:"reconcile"})}catch{return}let i=await Zo(e).catch(()=>[t]),c=(a||[]).find(u=>i.includes(u.headRefName));if(!c)return;let l=await pe(e.cwd).catch(()=>null);if(l){let{link:u,created:f}=await ct({repo:l,number:c.number,trackId:e.id,url:c.url,headRef:c.headRefName||t,title:c.title,source:"reconciled"});if(!f&&!(u?.trackId===e.id&&await rn(l,c.number))){if(!OM(u,r))return;let d=await B(e).catch(()=>null);if(d&&Qc(d)==null)return;await R(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"pushed",number:c.number,url:c.url,title:c.title,branch:t,repo:l}});return}}await R(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:c.number,url:c.url,title:c.title,branch:t,...l?{repo:l}:{}}}).catch(()=>{}),tp.delete(e.id)}var RM,tp,$M,np=O(()=>{ue();Xo();Sr();Yn();Sa();Qo();RM=6e3,tp=new Map,$M=30*6e4});import rp from"node:os";import{execFile as CM}from"node:child_process";import{promisify as MM}from"node:util";async function el(){if(process.platform!=="darwin")return rp.freemem();try{let{stdout:e}=await NM("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:rp.freemem()}catch{return rp.freemem()}}var NM,op=O(()=>{NM=MM(CM)});import Me from"node:fs/promises";import Pe from"node:path";import jx from"node:os";import{randomUUID as PM}from"node:crypto";import{execFile as BM}from"node:child_process";import{promisify as DM}from"node:util";import{chromium as jM}from"playwright-core";async function qt(){return zr||(sp||=(async()=>{try{zr=JSON.parse(await Me.readFile(Fx,"utf8"))}catch{zr={mocks:[]}}finally{sp=null}return zr.mocks||=[],zr})(),sp)}async function wt(){await et(Fx,zr)}function Wx(e,t){Wt(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&Wt(e,t.v,"png")}function Ye(e,t,n={}){return R({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 gt(e,t){let n=Oa(e.cwd);return{html:Pe.join(n,`${e.id}.v${t}.html`),png:Pe.join(n,`${e.id}.v${t}.png`)}}function $a(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?Pe.join(e.cwd,r):null}function _t(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 cp(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function tl(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(VM)}function zM(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 JM(e){let t=Buffer.alloc(24),n=await Me.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function XM({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 YM(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((m,w)=>{r.onload=m,r.onerror=()=>w(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,f=s,d=-1,p=-1;for(let m=0;m<s;m++)for(let w=m*o*4,y=0;y<o;y++,w+=4)Math.abs(c[w]-l[0])+Math.abs(c[w+1]-l[1])+Math.abs(c[w+2]-l[2])>24&&(y<u&&(u=y),y>d&&(d=y),m<f&&(f=m),p=m);return d<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:f,right:d,bottom:p}}},t.toString("base64"))}async function ZM(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((f,d)=>{c.onload=f,c.onerror=()=>d(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 QM({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await jM.launch({executablePath:FM,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new Et("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:ns})).newPage();a.setDefaultTimeout(rs),await a.goto(`file://${e}`,{waitUntil:"load",timeout:rs}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:rs});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await YM(a,i),u=l&&XM({...l,pad:WM*ns,align:ns});u&&(i=await ZM(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Ux)throw new Et(`capture produced a ${i.length}-byte image`);let c=cp(i);if(!c)throw new Et("output is not a PNG");return await Me.writeFile(t,i),{width:Math.round(c.width/ns),height:Math.round(c.height/ns),dpr:ns,renderMs:Date.now()-r}}catch(s){throw s instanceof Et?s:new Et(/timeout/i.test(s?.message||"")?`render timed out after ${rs}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function Dx({htmlPath:e,outPath:t,width:n}){let r=await Me.mkdtemp(Pe.join(jx.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await ip(UM,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:rs,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new Et(c?.killed?`render timed out after ${rs}ms`:"firefox exited non-zero",s)}let a=await Me.stat(t).catch(()=>null);if(!a||a.size<Ux)throw new Et(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=cp(await JM(t));if(!i)throw new Et("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await Me.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function eN(e){if(LM==="firefox")return Dx(e);try{return await QM(e)}catch(t){if(/failed to launch/i.test(t?.message||""))return Dx(e);throw t}}function qx(e){return ap=ap.then(e,e),ap}async function Kx(e,t){let n=await qt(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await wt(),await Ye(r,"rendering",{version:t});try{let o=await el();if(o<qM)throw new Et(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=gt(r,t),a=await tN({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await Me.rm(s.png,{force:!0}).catch(()=>{});return}lp(r,{v:t,source:"html",htmlRel:Pe.relative(r.cwd,s.html),pngRel:Pe.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await wt(),await Ye(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),Ht(r),Wt(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await wt(),await Ye(r,"error",{version:t,error:r.error}),Ht(r)}}}function lp(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 nN({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?HM:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Ca({trackId:e,group:t}={}){let n=await qt(),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 pn(e){let n=(await qt()).mocks.find(r=>r.id===e);return n?{...n}:null}function Gx({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&&!tl(t))throw new Error("image must be a PNG");if(n){let o=zM(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>Bx)throw new Error(`video must be ${Bx/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function Vx({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let f=nN({trackId:e,name:t,cwd:s,width:a}),d=typeof n=="string"&&n.trim(),p=Gx({html:n,image:r,video:o}),m=await qt(),w={id:PM().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:f,height:null,dpr:null,state:"queued",source:d?"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 Me.mkdir(Oa(s),{recursive:!0}),m.mocks.push(w),d?(await Me.writeFile(gt(w,1).html,n),await wt(),await Ye(w,"created"),Ht(w),Wt(w,1,"html"),qx(()=>Kx(w.id,1))):o?(await Jx(w,1,o,p),await wt(),await Ye(w,"created"),await Ye(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr,durationS:w.versions[0]?.durationS??null}),Ht(w),Wx(w,w.versions[0])):(await Xx(w,1,r),await wt(),await Ye(w,"created"),await Ye(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr}),Ht(w),Wt(w,1,"png")),{...w}}async function zx(e,{html:t,image:n,video:r,width:o}={}){let a=(await qt()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=Gx({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 Me.mkdir(Oa(a.cwd),{recursive:!0}),i?(a.state="queued",await Me.writeFile(gt(a,l).html,t),await wt(),await Ye(a,"updated",{version:l}),Ht(a),Wt(a,l,"html"),qx(()=>Kx(a.id,l))):r?(await Jx(a,l,r,c),await wt(),await Ye(a,"updated",{version:l}),await Ye(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:_t(a)?.durationS??null}),Ht(a),Wx(a,a.versions.find(u=>u.v===l))):(await Xx(a,l,n),await wt(),await Ye(a,"updated",{version:l}),await Ye(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Ht(a),Wt(a,l,"png")),{...a}}async function rN(e){try{let{stdout:t}=await ip(KM,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:Hx,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 oN(e,t){try{await ip(GM,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:Hx,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await Me.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function Jx(e,t,n,r){let o=Oa(e.cwd),s=Pe.join(o,`${e.id}.v${t}.${r.ext}`),a=Pe.join(o,`${e.id}.v${t}.poster.png`);await Me.writeFile(s,n);let i=await rN(s),c=await oN(s,a);lp(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:Pe.relative(e.cwd,s),posterRel:c?Pe.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 Xx(e,t,n){let r=cp(n);if(!r)throw new Error("image must be a PNG");let o=gt(e,t);await Me.writeFile(o.png,n),lp(e,{v:t,source:"image",htmlRel:null,pngRel:Pe.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function Yx(e){let t=await qt(),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 Ye(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Zx(e){let t=await qt(),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=gt(r,o);await Me.rm(s.html,{force:!0}).catch(()=>{}),await Me.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await Me.rm(Pe.join(r.cwd,s),{force:!0}).catch(()=>{});return await wt(),await Ye(r,"deleted"),xt("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function Qx(e){let n=(await qt()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=Pe.join(Lx,"mock-archive",e.id),o=Oa(e.cwd),s=Pe.join(r,".bostrat","mocks");await Me.mkdir(s,{recursive:!0});let a=await Me.readdir(o).catch(()=>[]);for(let i of a)await Me.copyFile(Pe.join(o,i),Pe.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 Me.stat(Pe.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(),Ht(i)}return await wt(),n.length}async function e0(e){let t=await qt(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await wt();for(let r of n)xt("mocks",r.id).catch(()=>{})}return n.length}async function t0(){if(!Te())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Be("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await qt();for(let r of n.mocks){await Ht(r);for(let o of r.versions||[])o.htmlRel&&await Wt(r,o.v,"html"),(o.pngRel||o.posterRel)&&await Wt(r,o.v,"png"),o.videoRel&&await Wt(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var ip,Lx,Fx,LM,FM,UM,HM,ns,WM,Ux,rs,qM,Bx,KM,GM,Hx,Et,zr,sp,Ht,Wt,Oa,VM,tN,ap,os=O(()=>{ue();Cn();op();Nn();ip=DM(BM),Lx=process.env.BOSTRAT_STATE_DIR||Pe.join(process.env.HOME||jx.tmpdir(),".bostrat"),Fx=Pe.join(Lx,"mocks.json"),LM=process.env.BOSTRAT_MOCK_ENGINE||"chromium",FM=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",UM=process.env.BOSTRAT_FIREFOX_BIN||"firefox",HM=1300,ns=2,WM=32,Ux=1500,rs=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,qM=700*1024*1024,Bx=100*1024*1024,KM=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",GM=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",Hx=2e4,Et=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
148
148
|
${n.trim().split(`
|
|
149
149
|
`).slice(-6).join(`
|
|
150
|
-
`)}`:t),this.name="MockRenderError"}},Hr=null,Gf=null;Ft=e=>Ve("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),Ut=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?Sa(e,r,"video"):r&&Sa(e,r,n)||(n==="html"?wt(e,t).html:null);return o?$n("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Ta=e=>Pe.join(e,".bostrat","mocks");AM=Buffer.from([137,80,78,71,13,10,26,10]);MM=CM,Vf=Promise.resolve()});import DM from"node:fs/promises";import Wx from"node:path";import jM from"node:os";import{randomUUID as qx}from"node:crypto";async function pn(){return fn||(Xf||=(async()=>{let e=!0;try{fn=JSON.parse(await DM.readFile(Kx,"utf8"))}catch{fn={flows:[]},e=!1}finally{Xf=null}if(fn.flows||=[],!e&&Te()){let{records:t,source:n}=await Be("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&fn.flows.push(r.data);fn.flows.length&&await Xo().catch(()=>{})}}return fn})(),Xf)}async function Xo(){await Qe(Kx,fn)}function Zf(e,t,n={}){return I({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 Gx({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>Lx)throw new Error(`steps must be an array of 1-${Lx}`);let r=await Aa({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>Ux)throw new Error(`${l}: caption over ${Ux} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>Hx)throw new Error(`${l}: title over ${Hx} chars`);let d=String(c.mock??"").trim().replace(/^mock:/,"");if(!d)throw new Error(`${l}: mock (name or id) required`);let m=r.filter(x=>x.id===d);if(m.length||(m=r.filter(x=>x.name.toLowerCase()===d.toLowerCase())),!m.length)throw new Error(`${l}: no mock matching "${d}" in this Track \u2014 see \`bostrat-mock ls\``);if(m.length>1)throw new Error(`${l}: "${d}" is ambiguous (${m.map(x=>`mock:${x.id}`).join(", ")}) \u2014 use the id`);let h=m[0],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(h.versions||[]).some(x=>x.v===w)))throw new Error(`${l}: "${h.name}" has no captured v${c.v} to pin`);let y=o.findIndex(x=>x.mockId===h.id),b=y>=0?o.splice(y,1)[0].id:qx().slice(0,8);s.push({id:b,mockId:h.id,caption:u,title:f,v:w})}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 FM(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>Fx)throw new Error(`name over ${Fx} chars`);return e.trim()}async function Vx({trackId:e}={}){let t=await pn();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function UM(e){let n=(await pn()).flows.find(r=>r.id===e);return n?{...n}:null}async function zx(e){let t=await UM(e);if(!t)return null;let n=new Map((await Aa({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=Et(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 Jx({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=FM(t),i=await pn(),c=i.flows.find(m=>m.trackId===e&&m.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 Gx({trackId:e,steps:n}),f=Date.now(),d={id:qx().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:f}],steps:l,createdAt:f,updatedAt:f};return i.flows.push(d),await Xo(),await Zf(d,"created"),Kc(d),{...d}}async function Yx(e,{steps:t,note:n}={}){let o=(await pn()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await Gx({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 Xo(),await Zf(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Kc(o),{...o}}async function Xx(e){let t=await pn(),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 Xo(),await Zf(r,"deleted"),kt("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function Zx(e){return(await pn()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function Qx(e){let n=(await pn()).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(),Kc(r);return n.length&&await Xo(),n.length}async function e0(e){let t=await pn(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await Xo();for(let r of n)kt("flows",r.id).catch(()=>{})}return n.length}async function t0(){if(!Te())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Be("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await pn();for(let r of n.flows)await Kc(r);return{pushed:n.flows.length,source:"plane"}}var LM,Kx,Lx,Fx,Ux,Hx,fn,Xf,Kc,Qf=O(()=>{le();Rn();Yo();On();LM=process.env.BOSTRAT_STATE_DIR||Wx.join(process.env.HOME||jM.tmpdir(),".bostrat"),Kx=Wx.join(LM,"flows.json"),Lx=20,Fx=80,Ux=500,Hx=80,fn=null,Xf=null;Kc=e=>Ve("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import wn from"node:fs/promises";import gn from"node:path";import HM from"node:os";import{randomUUID as WM}from"node:crypto";function Ea(e){return qM.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function Wt(){return mn||(ep||=(async()=>{let e=!0;try{mn=JSON.parse(await wn.readFile(r0,"utf8"))}catch{mn={runbooks:[]},e=!1}finally{ep=null}return mn.runbooks||=[],!e&&Te()&&await KM().catch(()=>{}),mn})(),ep)}async function qr(){await Qe(r0,mn)}function Vc(e){return _a(Ve("runbooks",{...e},{trackId:e.trackId}))}async function KM(){let{records:e,source:t}=await Be("runbooks");if(!(t!=="plane"||!e.length)){await wn.mkdir(Gc,{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 Ps("runbooks",r.id,o.v,"md");typeof s=="string"&&await wn.writeFile(hn(r,o.v),s).catch(()=>{})}mn.runbooks.push(r)}mn.runbooks.length&&await qr()}}function Zo(e,t,n={}){return I({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 hn(e,t){return gn.join(Gc,`${e.id}.v${t}.md`)}function o0(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 s0(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>n0)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${n0/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Ea(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 GM(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function VM({trackId:e,status:t}={}){let n=await Wt(),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 a0({trackId:e,status:t}={}){let n=await Wt(),r=await VM({trackId:e,status:t});if(!Te())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await Be("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 tp(e){let t=await zM(e);if(t)return t;if(!Te())return null;let{records:n,source:r}=await Be("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 i0(e,t){let n=await Ps("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function zM(e){let n=(await Wt()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function c0({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=s0(r),c=await Wt(),l={id:WM().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 wn.mkdir(Gc,{recursive:!0}),await wn.writeFile(hn(l,1),r),l.versions.push({v:1,mdRel:gn.relative(Wr,hn(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await qr(),await Zo(l,"created",{version:1}),Vc(l),_a($n("runbooks",l.id,1,"md",hn(l,1))),{...l}}async function l0(e,{md:t,note:n}={}){let o=(await Wt()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");GM(o,"update");let s=s0(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await wn.mkdir(Gc,{recursive:!0}),await wn.writeFile(hn(o,a),t),o.versions.push({v:a,mdRel:gn.relative(Wr,hn(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await qr(),await Zo(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Vc(o),_a($n("runbooks",o.id,a,"md",hn(o,a))),{...o}}async function u0(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await Wt()).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 qr(),await Zo(r,t==="done"?"done":"reopened"),Vc(r),{...r})}async function d0(e){let t=await Wt(),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 wn.rm(gn.join(Wr,o.mdRel),{force:!0}).catch(()=>{});return await qr(),r.trackDeletedAt||await Zo(r,"deleted"),_a(kt("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function f0(e){let n=(await Wt()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Zo(r,"orphaned"),r.cwd=null,Vc(r);return n.length&&await qr(),n.length}async function p0(e){let t=await Wt(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await Zo(r,"deleted");for(let o of r.versions||[])await wn.rm(gn.join(Wr,o.mdRel),{force:!0}).catch(()=>{});_a(kt("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await qr()),n.length}async function m0(){if(!Te())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Be("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Wt();for(let r of n.runbooks){await Ve("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await $n("runbooks",r.id,o.v,"md",gn.join(Wr,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var Wr,r0,Gc,n0,qM,mn,ep,_a,zc=O(()=>{Rn();le();On();Wr=process.env.BOSTRAT_STATE_DIR||gn.join(process.env.HOME||HM.tmpdir(),".bostrat"),r0=gn.join(Wr,"runbooks.json"),Gc=gn.join(Wr,"runbooks"),n0=256*1024,qM=[{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/}];mn=null,ep=null;_a=e=>Promise.resolve(e).catch(()=>{})});import np from"node:fs/promises";import Ia from"node:path";import{randomUUID as JM}from"node:crypto";function op(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return YM.has(t)?"image":XM.has(t)?"doc":null}function ZM(e){let r=Ia.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 g0(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>h0)throw new Error(`file too large \u2014 max ${Math.floor(h0/(1024*1024))} MB`);let r=ZM(t),o=Ia.extname(r).slice(1),s=op(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=JM().slice(0,8),i=rp(e);await np.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await np.writeFile(Ia.join(i,c),n,{mode:384}),await np.chmod(Ia.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${w0}/${c}`,kind:s,size:n.length}}function y0(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function sp(e){return e.role==="user"&&QM.test((e.text||"").trim())}var w0,rp,h0,YM,XM,QM,ap=O(()=>{w0=".bostrat/uploads",rp=e=>Ia.join(e,w0),h0=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,YM=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),XM=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"]);QM=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function Kr(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}var b0=O(()=>{});import{execFile as eN}from"node:child_process";import{promisify as tN}from"node:util";function ip(e){return`refs/bostrat/wip/${e}`}function oN(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function v0(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function sN(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:rN}async function cp(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await er(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await er(r,["remote","get-url","origin"])}catch{return k0.has(r)||(k0.add(r),t.log?.(`[wip-sync] ${n}: no origin remote \u2014 durable code fidelity is chat/memory-only for this Track`)),{pushed:!1,reason:"no-origin"}}try{let o=await Ro(r,"bostrat: wip snapshot"),{stdout:s}=await er(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(Jc.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=ip(n);return await er(r,["push","--force","origin",`${o}:${i}`],x0),Jc.set(r,a),await I({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,wip:!0,ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${n}: pushed ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return t.log?.(`[wip-sync] ${n}: push failed (will retry): ${String(o?.message||o).split(`
|
|
151
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function
|
|
152
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function
|
|
150
|
+
`)}`:t),this.name="MockRenderError"}},zr=null,sp=null;Ht=e=>ze("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),Wt=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?$a(e,r,"video"):r&&$a(e,r,n)||(n==="html"?gt(e,t).html:null);return o?Mn("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Oa=e=>Pe.join(e,".bostrat","mocks");VM=Buffer.from([137,80,78,71,13,10,26,10]);tN=eN,ap=Promise.resolve()});import sN from"node:fs/promises";import a0 from"node:path";import aN from"node:os";import{randomUUID as i0}from"node:crypto";async function hn(){return mn||(up||=(async()=>{let e=!0;try{mn=JSON.parse(await sN.readFile(c0,"utf8"))}catch{mn={flows:[]},e=!1}finally{up=null}if(mn.flows||=[],!e&&Te()){let{records:t,source:n}=await Be("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&mn.flows.push(r.data);mn.flows.length&&await ss().catch(()=>{})}}return mn})(),up)}async function ss(){await et(c0,mn)}function dp(e,t,n={}){return R({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 l0({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>n0)throw new Error(`steps must be an array of 1-${n0}`);let r=await Ca({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>o0)throw new Error(`${l}: caption over ${o0} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>s0)throw new Error(`${l}: title over ${s0} chars`);let d=String(c.mock??"").trim().replace(/^mock:/,"");if(!d)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(x=>x.id===d);if(p.length||(p=r.filter(x=>x.name.toLowerCase()===d.toLowerCase())),!p.length)throw new Error(`${l}: no mock matching "${d}" in this Track \u2014 see \`bostrat-mock ls\``);if(p.length>1)throw new Error(`${l}: "${d}" is ambiguous (${p.map(x=>`mock:${x.id}`).join(", ")}) \u2014 use the id`);let m=p[0],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(m.versions||[]).some(x=>x.v===w)))throw new Error(`${l}: "${m.name}" has no captured v${c.v} to pin`);let y=o.findIndex(x=>x.mockId===m.id),b=y>=0?o.splice(y,1)[0].id:i0().slice(0,8);s.push({id:b,mockId:m.id,caption:u,title:f,v:w})}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 cN(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>r0)throw new Error(`name over ${r0} chars`);return e.trim()}async function u0({trackId:e}={}){let t=await hn();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function lN(e){let n=(await hn()).flows.find(r=>r.id===e);return n?{...n}:null}async function d0(e){let t=await lN(e);if(!t)return null;let n=new Map((await Ca({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=_t(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 f0({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=cN(t),i=await hn(),c=i.flows.find(p=>p.trackId===e&&p.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 l0({trackId:e,steps:n}),f=Date.now(),d={id:i0().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:f}],steps:l,createdAt:f,updatedAt:f};return i.flows.push(d),await ss(),await dp(d,"created"),nl(d),{...d}}async function p0(e,{steps:t,note:n}={}){let o=(await hn()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await l0({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 ss(),await dp(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),nl(o),{...o}}async function m0(e){let t=await hn(),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 ss(),await dp(r,"deleted"),xt("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function h0(e){return(await hn()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function w0(e){let n=(await hn()).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(),nl(r);return n.length&&await ss(),n.length}async function g0(e){let t=await hn(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await ss();for(let r of n)xt("flows",r.id).catch(()=>{})}return n.length}async function y0(){if(!Te())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Be("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await hn();for(let r of n.flows)await nl(r);return{pushed:n.flows.length,source:"plane"}}var iN,c0,n0,r0,o0,s0,mn,up,nl,fp=O(()=>{ue();Cn();os();Nn();iN=process.env.BOSTRAT_STATE_DIR||a0.join(process.env.HOME||aN.tmpdir(),".bostrat"),c0=a0.join(iN,"flows.json"),n0=20,r0=80,o0=500,s0=80,mn=null,up=null;nl=e=>ze("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import yn from"node:fs/promises";import bn from"node:path";import uN from"node:os";import{randomUUID as dN}from"node:crypto";function Ma(e){return fN.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function Kt(){return wn||(pp||=(async()=>{let e=!0;try{wn=JSON.parse(await yn.readFile(k0,"utf8"))}catch{wn={runbooks:[]},e=!1}finally{pp=null}return wn.runbooks||=[],!e&&Te()&&await pN().catch(()=>{}),wn})(),pp)}async function Xr(){await et(k0,wn)}function ol(e){return Na(ze("runbooks",{...e},{trackId:e.trackId}))}async function pN(){let{records:e,source:t}=await Be("runbooks");if(!(t!=="plane"||!e.length)){await yn.mkdir(rl,{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 Hs("runbooks",r.id,o.v,"md");typeof s=="string"&&await yn.writeFile(gn(r,o.v),s).catch(()=>{})}wn.runbooks.push(r)}wn.runbooks.length&&await Xr()}}function as(e,t,n={}){return R({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 gn(e,t){return bn.join(rl,`${e.id}.v${t}.md`)}function x0(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 S0(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>b0)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${b0/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Ma(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 mN(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function hN({trackId:e,status:t}={}){let n=await Kt(),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 v0({trackId:e,status:t}={}){let n=await Kt(),r=await hN({trackId:e,status:t});if(!Te())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await Be("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 mp(e){let t=await wN(e);if(t)return t;if(!Te())return null;let{records:n,source:r}=await Be("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 T0(e,t){let n=await Hs("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function wN(e){let n=(await Kt()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function A0({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=S0(r),c=await Kt(),l={id:dN().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 yn.mkdir(rl,{recursive:!0}),await yn.writeFile(gn(l,1),r),l.versions.push({v:1,mdRel:bn.relative(Jr,gn(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Xr(),await as(l,"created",{version:1}),ol(l),Na(Mn("runbooks",l.id,1,"md",gn(l,1))),{...l}}async function E0(e,{md:t,note:n}={}){let o=(await Kt()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");mN(o,"update");let s=S0(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await yn.mkdir(rl,{recursive:!0}),await yn.writeFile(gn(o,a),t),o.versions.push({v:a,mdRel:bn.relative(Jr,gn(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Xr(),await as(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),ol(o),Na(Mn("runbooks",o.id,a,"md",gn(o,a))),{...o}}async function _0(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await Kt()).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 Xr(),await as(r,t==="done"?"done":"reopened"),ol(r),{...r})}async function I0(e){let t=await Kt(),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 yn.rm(bn.join(Jr,o.mdRel),{force:!0}).catch(()=>{});return await Xr(),r.trackDeletedAt||await as(r,"deleted"),Na(xt("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function R0(e){let n=(await Kt()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await as(r,"orphaned"),r.cwd=null,ol(r);return n.length&&await Xr(),n.length}async function $0(e){let t=await Kt(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await as(r,"deleted");for(let o of r.versions||[])await yn.rm(bn.join(Jr,o.mdRel),{force:!0}).catch(()=>{});Na(xt("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await Xr()),n.length}async function O0(){if(!Te())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Be("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Kt();for(let r of n.runbooks){await ze("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await Mn("runbooks",r.id,o.v,"md",bn.join(Jr,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var Jr,k0,rl,b0,fN,wn,pp,Na,sl=O(()=>{Cn();ue();Nn();Jr=process.env.BOSTRAT_STATE_DIR||bn.join(process.env.HOME||uN.tmpdir(),".bostrat"),k0=bn.join(Jr,"runbooks.json"),rl=bn.join(Jr,"runbooks"),b0=256*1024,fN=[{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/}];wn=null,pp=null;Na=e=>Promise.resolve(e).catch(()=>{})});import hp from"node:fs/promises";import Pa from"node:path";import{randomUUID as gN}from"node:crypto";function gp(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return yN.has(t)?"image":bN.has(t)?"doc":null}function kN(e){let r=Pa.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 N0(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>C0)throw new Error(`file too large \u2014 max ${Math.floor(C0/(1024*1024))} MB`);let r=kN(t),o=Pa.extname(r).slice(1),s=gp(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=gN().slice(0,8),i=wp(e);await hp.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await hp.writeFile(Pa.join(i,c),n,{mode:384}),await hp.chmod(Pa.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${M0}/${c}`,kind:s,size:n.length}}function P0(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function yp(e){return e.role==="user"&&xN.test((e.text||"").trim())}var M0,wp,C0,yN,bN,xN,bp=O(()=>{M0=".bostrat/uploads",wp=e=>Pa.join(e,M0),C0=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,yN=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),bN=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"]);xN=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function Yr(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}var B0=O(()=>{});import{execFile as SN}from"node:child_process";import{promisify as vN}from"node:util";function kp(e){return`refs/bostrat/wip/${e}`}function EN(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function L0(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function _N(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:AN}async function xp(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await or(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await or(r,["remote","get-url","origin"])}catch{return D0.has(r)||(D0.add(r),t.log?.(`[wip-sync] ${n}: no origin remote \u2014 durable code fidelity is chat/memory-only for this Track`)),{pushed:!1,reason:"no-origin"}}try{let o=await Do(r,"bostrat: wip snapshot"),{stdout:s}=await or(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(al.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=kp(n);return await or(r,["push","--force","origin",`${o}:${i}`],j0),al.set(r,a),await R({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,wip:!0,ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${n}: pushed ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return t.log?.(`[wip-sync] ${n}: push failed (will retry): ${String(o?.message||o).split(`
|
|
151
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function Sp(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await or(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await or(r,["remote","get-url","origin"])}catch{return{pushed:!1,reason:"no-origin"}}try{let o=await Do(r,"bostrat: transfer rescue snapshot"),{stdout:s}=await or(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(al.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=EN(n);return await or(r,["push","origin",`${o}:${i}`],j0),al.set(r,a),await R({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,rescue:!0,ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${n}: rescued stranded work ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return t.log?.(`[wip-sync] ${n}: rescue push failed: ${String(o?.message||o).split(`
|
|
152
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function F0({listTracks:e,sweepMs:t=_N(),log:n=console}){let r=!1,o=async()=>{if(!r){r=!0;try{let a=await e()??[];for(let i of a)i?.archivedAt||i?.kind==="setup"||i?.transferredTo||await xp(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 TN,AN,j0,or,al,D0,vp=O(()=>{yc();ue();TN=vN(SN),AN=12e4,j0=6e4;or=(e,t,n=15e3)=>TN("git",["-C",e,...t],{timeout:n,maxBuffer:4*1024*1024}),al=new Map,D0=new Set});import IN from"node:fs";import Tp from"node:fs/promises";import RN from"node:path";function MN(e=process.env){return!!(be(e)&&e.BOSTRAT_PLANE_TOKEN)}async function U0(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(!MN(t))return{restored:!1,reason:"plane not configured"};let s=Io(e),a=RN.join(s,`${o}.ndjson`);try{if(IN.statSync(a).size>0)return{restored:!1,reason:"log not empty"}}catch{}let i=be(t),c={authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`},l=[],u=0;for(;;){let d=new AbortController,p=setTimeout(()=>d.abort(),CN),m;try{m=await n(`${i}/v1/spine/${encodeURIComponent(o)}?after=${u}&limit=${$N}`,{headers:c,signal:d.signal})}finally{clearTimeout(p)}if(m.status===404)return{restored:!1,reason:"unknown to plane"};if(!m.ok)return{restored:!1,reason:`plane ${m.status}`};let w=await m.json();for(let y of w.events||[])l.push(JSON.stringify(y));if(l.length>ON)return{restored:!1,reason:"event count over safety cap"};if(w.next==null)break;u=w.next}if(!l.length)return{restored:!1,reason:"unknown to plane"};await Tp.mkdir(s,{recursive:!0});let f=`${a}.pull-${process.pid}-${Date.now()}`;return await Tp.writeFile(f,l.join(`
|
|
153
153
|
`)+`
|
|
154
|
-
`),await
|
|
154
|
+
`),await Tp.rename(f,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var $N,ON,CN,H0=O(()=>{Ve();ue();$N=1e3,ON=5e5,CN=3e4});var SS={};cu(SS,{HANDOFF_LATEST_REL:()=>Zr,KNOWN_AGENTS:()=>Ka,addTrackRepo:()=>Yp,adoptTrack:()=>fm,agentAvailability:()=>ul,announceNodeUpdate:()=>cr,answerFullscreenOffer:()=>sm,answerPrompt:()=>fl,archiveTrack:()=>eo,archiveTrackForMergedPr:()=>cn,augmentFirstPromptAfterHandoff:()=>Z0,buildAgentCmd:()=>X0,canResumeSubtask:()=>bS,cancelQueued:()=>za,cancelQueuedToDraft:()=>qp,cancelStopJob:()=>Ja,cancelSubtask:()=>vl,changedRepos:()=>es,commitTransferOut:()=>xl,createSetupTrack:()=>em,createTrack:()=>hl,deleteTrack:()=>nm,demoteTransferredTracks:()=>dm,ensureLeadEpochs:()=>bl,forceMergeSubtask:()=>wm,getHandoffJob:()=>hs,getSubtaskJob:()=>PP,getTrack:()=>ee,getTransferOutJob:()=>cm,interruptLead:()=>ml,isArchiving:()=>AP,isHandoffNoteEcho:()=>Np,isInterruptEcho:()=>Pp,isModelSwitchEcho:()=>Bp,listSubtaskJobs:()=>to,listTracks:()=>ie,matchTranscriptsToBoxes:()=>tS,mergeNotifyMessage:()=>hS,navigatePrompt:()=>Wp,notifyTrackForMergedPr:()=>ln,notifyTrackForResolvedPr:()=>wl,pendingOffers:()=>Up,queueShipCompleteNotify:()=>Gp,queuedMessages:()=>Qr,reapArchivedOrphans:()=>om,reconcileHandoffOutcome:()=>wS,reconcileInterruptedHandoffs:()=>im,reconcileInterruptedSubtasks:()=>pm,reconcileSubtaskOutcome:()=>yS,refreshStandingInstructions:()=>Qp,refuseIfTransferred:()=>um,rehydrateLead:()=>gl,removeTrackRepo:()=>Zp,reposOf:()=>ut,resolveTrackBox:()=>Ta,resolveTrackBoxByHookToken:()=>Fp,restartLead:()=>yl,resumeAgentCmd:()=>Lp,sayToLead:()=>bt,sendOptionsForAgent:()=>Pt,setTrackDraft:()=>Jp,setTrackModel:()=>Xp,startLeadHandoff:()=>kl,startSpinUpView:()=>Vp,startSubtask:()=>hm,startTransferOut:()=>lm,stopJobActive:()=>sS,subtaskMessages:()=>Hp,trackMessages:()=>dl,unarchiveTrack:()=>rm});import D from"node:fs/promises";import F from"node:path";import{randomUUID as ms}from"node:crypto";import{execFile as NN}from"node:child_process";import{promisify as PN}from"node:util";function X0(e,{skipPermissions:t=!0}={}){let n=ll[e];if(!n)throw new Error(`unknown agent "${e}"`);let r=process.env[n.envVar];return r||(t&&n.skipFlag?`${n.base} ${n.skipFlag}`:n.base)}function Lp(e,t,{skipPermissions:n=!0}={}){let r=ll[e];if(!r)throw new Error(`unknown agent "${e}"`);let o=process.env[r.envVar];if(o)return o;let s=n&&r.skipFlag?` ${r.skipFlag}`:"";switch(e){case"claude":return`${r.base} --resume ${t}${s}`;case"cursor":return`${r.base} --continue${s}`;case"codex":return`${r.base} resume --last${s}`;default:return`${r.base}${s}`}}function Ga(e){let t=ll[e];if(t)return process.env[t.envVar]||t.base}async function ul({maxAgeMs:e=6e4}={}){if(sr.value&&Date.now()-sr.at<e)return sr.value;let t=sr.value,n=await Promise.all(Ka.map(async r=>{let o=await Wi(Ga(r),{retries:[]});return o===yo?[r,t?.[r]??!0]:[r,o===Hi]}));return sr.value=Object.fromEntries(n),sr.at=Date.now(),sr.value}async function Va(e,t=!0){let n=X0(e,{skipPermissions:t});if(await Wi(n)===go){sr.at=0;let r=n.split(/\s+/)[0],o=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.":Nu[r]?`Install it on this node: ${Nu[r]}`:"Its CLI must be installed and on PATH first.";throw new Error(`agent "${e}" isn't available on this box (command not found: ${r}). ${o}`)}return n}function Z0(e,t=Zr){return`${e.trim()}
|
|
155
155
|
|
|
156
|
-
(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
|
|
157
|
-
`;return pp=pp.then(()=>D.mkdir(qt,{recursive:!0})).then(()=>D.appendFile(mN,n)).catch(()=>{}),pp}async function _p({current:e,latest:t,min:n=null,installKind:r=null,required:o=!1}={}){if(!t)return{announced:0};let s=null;try{s=JSON.parse(await D.readFile(mp,"utf8"))}catch{}if(s?.latest===t&&s?.current===e)return{announced:0,deduped:!0};let a=await G(),i=0;for(let c of a.tracks)c.archivedAt||!c.leadBox||(pe(c,"node-update",{current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},required:o===!0}),i++);await Z().catch(()=>{});try{await D.mkdir(F.dirname(mp),{recursive:!0}),await D.writeFile(mp,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:i}}function B0(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=>kN.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 D0(e,t,n,{kind:r}={}){let o=await oe(t);if(!o)throw new Error(`box "${t}" is not running`);let s=La(o.agent);await Dn({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s}),await je(t,n,{...Mt(o.agent),kind:r||"orchestration"}),await z({flow:"orchestration",phase:"sent",trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await G(),i=te(a,e);if(!i)return;let c=i.boxes.find(l=>l.name===t);pe(i,"orchestration-prompt",{box:t,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await Z()}async function xN(e){let t=F.join(ko(e),`${e.id}.ndjson`),n;try{n=await D.stat(t)}catch{return null}let r=E0.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await B(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return E0.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function vN(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:ff(n.name,n.agent)}async function Kt(e,t=null){let n=t??new Map((await xt()).map(i=>[i.name,i])),r=e.boxes.map(i=>{let c=n.get(i.name),l=nr.has(i.state)?i.state:c?i.role==="lead"?"live":i.state:"stopped";return{...i,state:l,cwd:c?.cwd||i.cwd,agent:c?.agent||i.agent,sessionId:c?.sessionId||i.sessionId}}),[o,{working:s,prompted:a}]=await Promise.all([xN(e),vN(e,r)]);return{...e,boxes:r,lastResponseAt:o,working:s,prompted:a,archiving:Ba.has(e.id)}}function j0(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),f=l.createdAt-SN,d=r[u+1]?.createdAt??1/0,m=o.find(h=>!s.has(h.id)&&h.birthMs>=f&&h.birthMs<d);m&&(s.add(m.id),c.push({box:l.name,sessionId:m.id}))}return c}async function L0(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(d=>d.box.sessionId),a=yw(r),i=new Set(a.map(d=>d.id)),c=s.every(d=>d&&i.has(d)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(d=>d.box),f=j0(u,a,d=>i.has(d));for(let d of f){let m=u.find(h=>h.name===d.box);m&&(m.sessionId=d.sessionId),await lw(d.box,d.sessionId),n=!0}}return n}async function ce(){let e=await G();await L0(e)&&await Z();let t=new Map((await xt()).map(n=>[n.name,n]));return Promise.all(e.tracks.map(n=>Kt(n,t)))}async function ga(e,t){let n=await G();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 Ip(e){let t=Ss(e);if(!t)return null;let n=await G();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&mh(o.hookTokenHash,t))return{track:r,box:o};return null}async function ee(e){let t=await G(),n=te(t,e);return n?(await L0(t)&&await Z(),Kt(n)):null}function vp(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(TN[e.agent]||[]).some(n=>n.test(t))}function Sp(e){let t=(e.text||"").trim();return t?AN.some(n=>n.test(t)):!1}async function el(e,t={}){let n=await ee(e);if(!n)return null;let r=new Set([...n.events.filter(v=>v.type==="orchestration-prompt"&&v.prompt).map(v=>String(v.prompt).trim()),...n.events.filter(v=>v.type==="said"&&v.text).map(v=>String(v.text).trim())]),o=cs(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=Bf(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...ux(n),...l],d={windows:[...Vg(n),...zg(n,tc),...a,...u]},h=(await Promise.all(n.boxes.map(v=>Ad(v,t)))).flat(),w=h.map(v=>{let V={kind:"message",...v};if(V.role==="assistant"&&(V.text||"").trim()&&V.tools?.length){let Tn=na(V.agent).cliTools,Y=V.tools.filter(P=>!Tn.has(P));return{...V,tools:Y.length?Y:void 0}}return V}).map(v=>{if(v.role!=="user")return v;let V=Zi(v.text);return V?V.kind==="suppress"?null:{...v,note:V,text:gg(V)}:v}).filter(Boolean).filter(v=>!(v.role==="user"&&r.has((v.text||"").trim()))).filter(v=>!vp(v)).filter(v=>!Sp(v)).filter(v=>!xp(v)).filter(v=>!sp(v)).filter(v=>v.role!=="assistant"||!!(v.text||"").trim()||!!v.images?.length).filter(v=>!Jg(v,d)),y=w.filter(v=>v.failed&&v.text===vr);if(y.length){let v=new Map;for(let V of y){if(!v.has(V.box)){let P=[...(await Ae(V.box,{lines:De}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(R=>R[1]);v.set(V.box,P.length?P[P.length-1]:null)}let Tn=v.get(V.box);Tn&&(V.text=`\u25A0 ${Tn}`)}}let b=await Promise.all(n.boxes.map(v=>Ey(v,t)));await Gk(n,b.flat()),await qk(n,w),await xb(n),await gx(n,w).catch(()=>{});for(let v of n.boxes)v.agent==="codex"&&v.state==="live"&&await zk(n,v),v.agent==="cursor"&&v.state==="live"&&await Qk(n,v);let x=await Promise.all(n.boxes.map(v=>Ay(v,t)));await Kk(n,x.flat().filter(v=>!hp(v.box,v.ts,u)));let E=await B(n),_=c&&i.viewId?i.viewId:null,q=Mb(E),C=Er(E).filter(v=>!(v.kind==="message"&&(vp(v)||Sp(v)||xp(v)||sp(v)))&&!(v.kind==="view-card"&&_&&v.viewId===_)),N=Nb(C,q),T=C.filter(v=>!(v.kind==="message"&&v.role==="assistant"&&!N.has(v)&&hp(v.box,v.ts,q))),W=ad(E,T).map(v=>({kind:"command-batch",id:v.id,ts:v.pending?v.ts:(v.anchorTs??v.ts)-1,anchorTs:v.anchorTs,anchorBox:v.anchorBox,agent:v.agent,boxRole:v.boxRole,commands:v.commands,pending:v.pending})).filter(v=>!hp(v.anchorBox,v.anchorTs??v.ts,u)),H=Tg(Ag([...T,...W].filter(v=>v.ts!=null).sort((v,V)=>v.ts-V.ts))),ae=_N(n),J=ae.size?H.filter(v=>v.kind==="event"||!ae.has(v.box??v.anchorBox)):H,A=n.boxes.find(v=>v.name===n.leadBox)||null,j=A?await Ls(A.cwd,A.sessionId,{fallbackNewest:!1}):null;if(!j&&A){let v=[...E].reverse().find(V=>V.type==="context.usage"&&V.boxId===A.name);v?.payload?.window&&(j={pct:v.payload.pct??(v.payload.occupancy!=null?v.payload.occupancy/v.payload.window:null),occupancy:v.payload.occupancy??null,window:v.payload.window,windowKnown:!0,model:v.payload.model||null})}let Q=[...h].reverse().find(v=>v.role==="assistant"&&v.model),fe=Q?.model||null,Re=[...n.events||[]].reverse().find(v=>v.type==="model-change"),ue=j?.model||fe;Re&&(!Q?.ts||Re.at>=Q.ts)&&(ue=Re.to);let ie=A?await Ae(A.name,{lines:De}).catch(()=>""):"",g=A&&Mr(A.agent,ie)?await Xt(A.name).catch(()=>ie):ie,p=A?Wn(A,g):null;if(p&&A){let v=ac(A,p,g);if(v){let V=Hy(A.name,p.promptId);V.action!=="give-up"&&(V.action==="send"&&(Oe(A.name,v.keys,{kind:"auto-reject"}).catch(()=>{}),V.first&&I(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:A.name,provider:A.agent,payload:{ts:Date.now(),promptId:p.promptId,rule:v.id}}).catch(()=>{})),p=null)}}!p&&A&&!Gs(A.agent,ie)&&!rs(n.id).length&&(p=od(J,{leadBoxId:A.name,provider:A.agent,isAnswered:v=>E.some(V=>V.type==="prompt.answered"&&V.payload?.promptId===v)})),p&&vb(n,A,p);let S=J.filter(v=>!(v.kind==="message"&&v.role==="assistant"&&v.ask&&!String(v.text||"").trim()&&!v.images?.length)),we=!!A&&$c(A.name,A.agent,ie)&&!p;A&&!A.readyAt&&(we||p||ie&&Ki(A.agent,ie))&&(A.readyAt=Date.now(),await Z());let ge=!!A&&!A.readyAt&&Date.now()-(n.createdAt||0)<EN;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&A&&Ki(A.agent,ie)&&!p){let v=await G(),V=te(v,n.id);V?.setup&&!V.setup.connectedAt&&(V.setup.connectedAt=Date.now(),n.setup.connectedAt=V.setup.connectedAt,await I(V,{type:"provider.connected",actor:"system",source:"pane",boxId:A.name,provider:A.agent,payload:{provider:A.agent,ts:V.setup.connectedAt}}).catch(()=>{}),await Z())}let Se=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,$e=null;if(c){let v=Date.now()-i.startedAt;if(i.viewId&&!we){let V=Lc(n.id);V&&(pe(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:V.finishedAt}),await Z())}else if(p||v>cx&&!we&&!i.viewId||v>Pf){let V=p?"needs-input":v>Pf?"timeout":"no-view";Lc(n.id,{error:V})&&await I(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:A?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:V}}).catch(()=>{})}else $e={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:S,context:j?{pct:j.pct,occupancy:j.occupancy,window:j.window,windowKnown:j.windowKnown,model:j.model}:null,models:{current:ue,options:mb(A?.agent)},pendingPrompt:p,working:we,stopping:H0(n.id),leadStarting:ge,handoff:Se,view:$e,queued:rs(n.id).filter(v=>v.kind!=="ship-notify"&&v.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function _N(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 Rp(e,t){let n=await ee(e);if(!n)return null;let r=zr(e).find(f=>f.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 B(n),a=Er(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=ad(s.filter(f=>f.boxId===o),a.filter(f=>f.kind==="message")).map(f=>({kind:"command-batch",id:f.id,ts:f.pending?f.ts:(f.anchorTs??f.ts)-1,anchorTs:f.anchorTs,anchorBox:f.anchorBox,agent:f.agent,boxRole:f.boxRole,commands:f.commands,pending:f.pending})),c=[...a,...i].filter(f=>f.ts!=null).sort((f,d)=>f.ts-d.ts),l=n.boxes.find(f=>f.name===o)||null,u=!1;if(l&&l.state==="live"){let f=await Ae(o,{lines:De}).catch(()=>"");u=$c(o,l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function tl(e,{promptId:t,answers:n,text:r}={}){let o=te(await G(),e);if(!o)throw new Error(`track "${e}" not found`);Ha(e,"prompt-answer");let s=o.boxes.find(h=>h.name===o.leadBox);if(!s)throw new Error(`track "${e}" has no lead box`);if(!await oe(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Cd(s);if(i||(i=await IN(o,s)),!i)throw new Error("no pending prompt to answer");if(t&&t!==i.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let c;if(i.answerMode==="text"){let h=typeof r=="string"?r.trim():"";if(!h)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(h)||h.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await je(o.leadBox,h,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await Oe(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let h=xg(i,n||[],typeof r=="string"?r:"");if(!h)throw new Error("no answer selected \u2014 pick an option or reply in chat");await yt(o.id,h,{kind:"prompt-answer"}),c={mode:"say",value:h,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let h=jy(i,n||[]);await Oe(o.leadBox,h,{kind:"answer-prompt"}),c={mode:"keys",value:h.join(" "),count:h.length}}else{let h=Gw(i,n||[]);await je(o.leadBox,h,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:h,count:1}}if(i.answerMode==="say"){let h=i.questions||[];for(let w=0;w<h.length;w++){let y=h[w],b=new Set(Array.isArray(n?.[w])?n[w]:[]),x=[...b].map(_=>y.options?.[_]).filter(Boolean).map(_=>({label:_.label,description:_.description||""})),E=(y.options||[]).map((_,q)=>b.has(q)?null:_?.label).filter(Boolean);Jd(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:w,questionTotal:i.questionTotal??h.length,header:y.header||"",question:y.question||"",multiSelect:!!y.multiSelect,chosen:x,rejected:E})}return await z({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=new Set(Array.isArray(n?.[0])?n[0]:[]),f=[...u].map(h=>l.options?.[h]).filter(Boolean).map(h=>({label:h.label,description:h.description||""})),d=(l.options||[]).map((h,w)=>u.has(w)?null:h?.label).filter(Boolean),m=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:f,rejected:d,...m?{control:m}:{}},await z({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),Jd(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function IN(e,t){if(!t)return null;let n=await B(e).catch(()=>[]);return od(Er(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function $p(e,{promptId:t,questionIndex:n}={}){let r=te(await G(),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 oe(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Cd(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=Ly(a,n);return c.length?(await Oe(r.leadBox,c,{kind:"navigate-prompt"}),await z({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 nl(e){let t=Ca.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},Ca.set(e,t)),t}function rs(e){return(Ca.get(e)?.items??[]).map(t=>({...t}))}function Ua(e){let t=Ca.get(e);if(!t)return[];let n=t.items.splice(0);return t.idleSince=null,t.timer&&(clearInterval(t.timer),t.timer=null),n}async function Op(e){let t=Ua(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=te(await G(),e);if(o)return r=os(o,n),await Z(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function Cp(e){let t=nl(e);t.timer||(t.timer=setInterval(()=>$N(e).catch(()=>{}),RN))}async function $N(e){let t=Ca.get(e);if(!(!t||t.flushing)){if(!t.items.length){t.timer&&(clearInterval(t.timer),t.timer=null);return}t.flushing=!0;try{let n=te(await G(),e);if(!n||n.archivedAt){Ua(e);return}let r=cs(e);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){Kr(t,!1);return}let o=await oe(n.leadBox);if(!o){Kr(t,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox);if(Pr(s?.agent)){Kr(t,!1);return}let a=await Ae(n.leadBox,{lines:De}).catch(()=>"");if(qi(s?.agent,a)){Kr(t,!1);return}if(Mr(s?.agent,a)){let c=await Xt(n.leadBox).catch(()=>a);if(Wn(s,c)){Kr(t,!1);return}}if(!Kr(t,!0))return;let i=t.items.shift();t.idleSince=null,await U0(n,s,o,i.text,i.kind??null,i.attachments??null),await Z()}finally{t.flushing=!1}}}async function F0(e,t,n,r){let o=Pr(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(os(e,[n]),await Z()),ob(e.id,o))try{await I(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 ON(e,t){let n=await Ae(e.leadBox,{lines:De}).catch(()=>"");if(!Mr(t?.agent,n))return!1;let r=await Xt(e.leadBox).catch(()=>n);return!!Wn(t,r)}async function U0(e,t,n,r,o=null,s=null){await F0(e,t,r,o);let a=Ma.get(e.id)||0,i=r,c=!1,l=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(i=P0(r,e.pendingHandoffHydrate.artifact||Gr),delete e.pendingHandoffHydrate,c=!0);let u=Array.isArray(s)?s.filter(d=>d&&d.rel):[];if(u.length){let d=y0(u.map(m=>m.rel));i=i.trim()?`${i.trim()}
|
|
156
|
+
(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 Np(e){return e.role==="user"&&jN.test((e.text||"").trim())}async function G(){return yt||(Ap||=(async()=>{try{yt=JSON.parse(await D.readFile(J0,"utf8"))}catch{yt={tracks:[]}}finally{Ap=null}return yt})(),Ap)}async function Z(){await et(J0,yt)}function UN(e,t){let n=JSON.stringify({trackId:e,...t})+`
|
|
157
|
+
`;return Ep=Ep.then(()=>D.mkdir(Gt,{recursive:!0})).then(()=>D.appendFile(BN,n)).catch(()=>{}),Ep}async function cr({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 f=null;try{f=JSON.parse(await D.readFile(_p,"utf8"))}catch{}if(f?.latest===t&&f?.current===e)return{announced:0,deduped:!0}}let l=await G(),u=0;for(let f of l.tracks)f.archivedAt||!f.leadBox||(he(f,"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 Z().catch(()=>{}),s==="available")try{await D.mkdir(F.dirname(_p),{recursive:!0}),await D.writeFile(_p,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}function Q0(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=>HN.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 eS(e,t,n,{kind:r}={}){let o=await se(t);if(!o)throw new Error(`box "${t}" is not running`);let s=Ga(o.agent);await Fn({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s}),await je(t,n,{...Pt(o.agent),kind:r||"orchestration"}),await z({flow:"orchestration",phase:"sent",trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await G(),i=te(a,e);if(!i)return;let c=i.boxes.find(l=>l.name===t);he(i,"orchestration-prompt",{box:t,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await Z()}async function WN(e){let t=F.join(Io(e),`${e.id}.ndjson`),n;try{n=await D.stat(t)}catch{return null}let r=W0.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await B(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return W0.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function qN(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Ef(n.name,n.agent)}async function Vt(e,t=null){let n=t??new Map((await St()).map(i=>[i.name,i])),r=e.boxes.map(i=>{let c=n.get(i.name),l=ar.has(i.state)?i.state:c?i.role==="lead"?"live":i.state:"stopped";return{...i,state:l,cwd:c?.cwd||i.cwd,agent:c?.agent||i.agent,sessionId:c?.sessionId||i.sessionId}}),[o,{working:s,prompted:a}]=await Promise.all([WN(e),qN(e,r)]);return{...e,boxes:r,lastResponseAt:o,working:s,prompted:a,archiving:Wa.has(e.id)}}function tS(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),f=l.createdAt-KN,d=r[u+1]?.createdAt??1/0,p=o.find(m=>!s.has(m.id)&&m.birthMs>=f&&m.birthMs<d);p&&(s.add(p.id),c.push({box:l.name,sessionId:p.id}))}return c}async function nS(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(d=>d.box.sessionId),a=Nw(r),i=new Set(a.map(d=>d.id)),c=s.every(d=>d&&i.has(d)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(d=>d.box),f=tS(u,a,d=>i.has(d));for(let d of f){let p=u.find(m=>m.name===d.box);p&&(p.sessionId=d.sessionId),await Aw(d.box,d.sessionId),n=!0}}return n}async function ie(){let e=await G();await nS(e)&&await Z();let t=new Map((await St()).map(n=>[n.name,n]));return Promise.all(e.tracks.map(n=>Vt(n,t)))}async function Ta(e,t){let n=await G();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 Fp(e){let t=$s(e);if(!t)return null;let n=await G();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&$h(o.hookTokenHash,t))return{track:r,box:o};return null}async function ee(e){let t=await G(),n=te(t,e);return n?(await nS(t)&&await Z(),Vt(n)):null}async function Up(e){let t=await B(e).catch(()=>[]),n;try{n=Un(t)}catch{return[]}return Hg(n).map(r=>({kind:r.kind,id:r.offerId??r.proposalId??r.id,ts:r.ts??null}))}function Pp(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(GN[e.agent]||[]).some(n=>n.test(t))}function Bp(e){let t=(e.text||"").trim();return t?VN.some(n=>n.test(t)):!1}async function dl(e,t={}){let n=await ee(e);if(!n)return null;let r=new Set([...n.events.filter(v=>v.type==="orchestration-prompt"&&v.prompt).map(v=>String(v.prompt).trim()),...n.events.filter(v=>v.type==="said"&&v.text).map(v=>String(v.text).trim())]),o=hs(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=Xf(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[..._x(n),...l],d={windows:[...uy(n),...dy(n,dc),...a,...u]},m=(await Promise.all(n.boxes.map(v=>Ld(v,t)))).flat(),w=m.map(v=>{let V={kind:"message",...v};if(V.role==="assistant"&&(V.text||"").trim()&&V.tools?.length){let _n=la(V.agent).cliTools,X=V.tools.filter(P=>!_n.has(P));return{...V,tools:X.length?X:void 0}}return V}).map(v=>{if(v.role!=="user")return v;let V=cc(v.text);return V?V.kind==="suppress"?null:{...v,note:V,text:Mg(V)}:v}).filter(Boolean).filter(v=>!(v.role==="user"&&r.has((v.text||"").trim()))).filter(v=>!Pp(v)).filter(v=>!Bp(v)).filter(v=>!Np(v)).filter(v=>!yp(v)).filter(v=>v.role!=="assistant"||!!(v.text||"").trim()||!!v.images?.length).filter(v=>!fy(v,d)),y=w.filter(v=>v.failed&&v.text===Rr);if(y.length){let v=new Map;for(let V of y){if(!v.has(V.box)){let P=[...(await Ae(V.box,{lines:De}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map($=>$[1]);v.set(V.box,P.length?P[P.length-1]:null)}let _n=v.get(V.box);_n&&(V.text=`\u25A0 ${_n}`)}}let b=await Promise.all(n.boxes.map(v=>Wy(v,t)));await lx(n,b.flat()),await ix(n,w),await jb(n),await Nx(n,w).catch(()=>{});for(let v of n.boxes)v.agent==="codex"&&v.state==="live"&&await dx(n,v),v.agent==="cursor"&&v.state==="live"&&await wx(n,v);let x=await Promise.all(n.boxes.map(v=>Hy(v,t)));await cx(n,x.flat().filter(v=>!Ip(v.box,v.ts,u)));let E=await B(n),_=c&&i.viewId?i.viewId:null,q=Xb(E),C=Un(E).filter(v=>!(v.kind==="message"&&(Pp(v)||Bp(v)||Np(v)||yp(v)))&&!(v.kind==="view-card"&&_&&v.viewId===_)),N=Yb(C,q),T=C.filter(v=>!(v.kind==="message"&&v.role==="assistant"&&!N.has(v)&&Ip(v.box,v.ts,q))),W=kd(E,T).map(v=>({kind:"command-batch",id:v.id,ts:v.pending?v.ts:(v.anchorTs??v.ts)-1,anchorTs:v.anchorTs,anchorBox:v.anchorBox,agent:v.agent,boxRole:v.boxRole,commands:v.commands,pending:v.pending})).filter(v=>!Ip(v.anchorBox,v.anchorTs??v.ts,u)),H=Fg(Ug([...T,...W].filter(v=>v.ts!=null).sort((v,V)=>v.ts-V.ts))),ce=JN(n),J=ce.size?H.filter(v=>v.kind==="event"||!ce.has(v.box??v.anchorBox)):H,A=n.boxes.find(v=>v.name===n.leadBox)||null,j=A?await Gs(A.cwd,A.sessionId,{fallbackNewest:!1}):null;if(!j&&A){let v=[...E].reverse().find(V=>V.type==="context.usage"&&V.boxId===A.name);v?.payload?.window&&(j={pct:v.payload.pct??(v.payload.occupancy!=null?v.payload.occupancy/v.payload.window:null),occupancy:v.payload.occupancy??null,window:v.payload.window,windowKnown:!0,model:v.payload.model||null})}let Q=[...m].reverse().find(v=>v.role==="assistant"&&v.model),me=Q?.model||null,Re=[...n.events||[]].reverse().find(v=>v.type==="model-change"),de=j?.model||me;Re&&(!Q?.ts||Re.at>=Q.ts)&&(de=Re.to);let le=A?await Ae(A.name,{lines:De}).catch(()=>""):"",g=A&&Lr(A.agent,le)?await Qt(A.name).catch(()=>le):le,h=A?Vn(A,g):null;if(h&&A){let v=wc(A,h,g);if(v){let V=sb(A.name,h.promptId);V.action!=="give-up"&&(V.action==="send"&&(Oe(A.name,v.keys,{kind:"auto-reject"}).catch(()=>{}),V.first&&R(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:A.name,provider:A.agent,payload:{ts:Date.now(),promptId:h.promptId,rule:v.id}}).catch(()=>{})),h=null)}}!h&&A&&!Qs(A.agent,le)&&!Qr(n.id).length&&(h=yd(J,{leadBoxId:A.name,provider:A.agent,isAnswered:v=>E.some(V=>V.type==="prompt.answered"&&V.payload?.promptId===v)})),h&&Lb(n,A,h);let S=J.filter(v=>!(v.kind==="message"&&v.role==="assistant"&&v.ask&&!String(v.text||"").trim()&&!v.images?.length)),fe=!!A&&Fc(A.name,A.agent,le)&&!h;A&&!A.readyAt&&(fe||h||le&&tc(A.agent,le))&&(A.readyAt=Date.now(),await Z());let ne=!!A&&!A.readyAt&&Date.now()-(n.createdAt||0)<zN;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&A&&tc(A.agent,le)&&!h){let v=await G(),V=te(v,n.id);V?.setup&&!V.setup.connectedAt&&(V.setup.connectedAt=Date.now(),n.setup.connectedAt=V.setup.connectedAt,await R(V,{type:"provider.connected",actor:"system",source:"pane",boxId:A.name,provider:A.agent,payload:{provider:A.agent,ts:V.setup.connectedAt}}).catch(()=>{}),await Z())}let ve=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,$e=null;if(c){let v=Date.now()-i.startedAt;if(i.viewId&&!fe){let V=Jc(n.id);V&&(he(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:V.finishedAt}),await Z())}else if(h||v>Ax&&!fe&&!i.viewId||v>Jf){let V=h?"needs-input":v>Jf?"timeout":"no-view";Jc(n.id,{error:V})&&await R(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:A?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:V}}).catch(()=>{})}else $e={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:S,context:j?{pct:j.pct,occupancy:j.occupancy,window:j.window,windowKnown:j.windowKnown,model:j.model}:null,models:{current:de,options:Ob(A?.agent)},pendingPrompt:h,working:fe,stopping:sS(n.id),leadStarting:ne,handoff:ve,view:$e,queued:Qr(n.id).filter(v=>v.kind!=="ship-notify"&&v.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function JN(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 Hp(e,t){let n=await ee(e);if(!n)return null;let r=to(e).find(f=>f.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 B(n),a=Un(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=kd(s.filter(f=>f.boxId===o),a.filter(f=>f.kind==="message")).map(f=>({kind:"command-batch",id:f.id,ts:f.pending?f.ts:(f.anchorTs??f.ts)-1,anchorTs:f.anchorTs,anchorBox:f.anchorBox,agent:f.agent,boxRole:f.boxRole,commands:f.commands,pending:f.pending})),c=[...a,...i].filter(f=>f.ts!=null).sort((f,d)=>f.ts-d.ts),l=n.boxes.find(f=>f.name===o)||null,u=!1;if(l&&l.state==="live"){let f=await Ae(o,{lines:De}).catch(()=>"");u=Fc(o,l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function fl(e,{promptId:t,answers:n,text:r}={}){let o=te(await G(),e);if(!o)throw new Error(`track "${e}" not found`);Ja(e,"prompt-answer");let s=o.boxes.find(m=>m.name===o.leadBox);if(!s)throw new Error(`track "${e}" has no lead box`);if(!await se(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Gd(s);if(i||(i=await XN(o,s)),!i)throw new Error("no pending prompt to answer");if(t&&t!==i.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let c;if(i.answerMode==="text"){let m=typeof r=="string"?r.trim():"";if(!m)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(m)||m.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await je(o.leadBox,m,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await Oe(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let m=Dg(i,n||[],typeof r=="string"?r:"");if(!m)throw new Error("no answer selected \u2014 pick an option or reply in chat");await bt(o.id,m,{kind:"prompt-answer"}),c={mode:"say",value:m,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let m=tb(i,n||[]);await Oe(o.leadBox,m,{kind:"answer-prompt"}),c={mode:"keys",value:m.join(" "),count:m.length}}else{let m=cg(i,n||[]);await je(o.leadBox,m,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:m,count:1}}if(i.answerMode==="say"){let m=i.questions||[];for(let w=0;w<m.length;w++){let y=m[w],b=new Set(Array.isArray(n?.[w])?n[w]:[]),x=[...b].map(_=>y.options?.[_]).filter(Boolean).map(_=>({label:_.label,description:_.description||""})),E=(y.options||[]).map((_,q)=>b.has(q)?null:_?.label).filter(Boolean);uf(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:w,questionTotal:i.questionTotal??m.length,header:y.header||"",question:y.question||"",multiSelect:!!y.multiSelect,chosen:x,rejected:E})}return await z({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=new Set(Array.isArray(n?.[0])?n[0]:[]),f=[...u].map(m=>l.options?.[m]).filter(Boolean).map(m=>({label:m.label,description:m.description||""})),d=(l.options||[]).map((m,w)=>u.has(w)?null:m?.label).filter(Boolean),p=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:f,rejected:d,...p?{control:p}:{}},await z({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),uf(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function XN(e,t){if(!t)return null;let n=await B(e).catch(()=>[]);return yd(Un(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function Wp(e,{promptId:t,questionIndex:n}={}){let r=te(await G(),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 Gd(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=nb(a,n);return c.length?(await Oe(r.leadBox,c,{kind:"navigate-prompt"}),await z({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 pl(e){let t=La.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},La.set(e,t)),t}function Qr(e){return(La.get(e)?.items??[]).map(t=>({...t}))}function za(e){let t=La.get(e);if(!t)return[];let n=t.items.splice(0);return t.idleSince=null,t.timer&&(clearInterval(t.timer),t.timer=null),n}async function qp(e){let t=za(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=te(await G(),e);if(o)return r=ds(o,n),await Z(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function Kp(e){let t=pl(e);t.timer||(t.timer=setInterval(()=>ZN(e).catch(()=>{}),YN))}async function ZN(e){let t=La.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=te(await G(),e);if(!n||n.archivedAt){za(e);return}let r=hs(e);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){Yr(t,!1);return}let o=await se(n.leadBox);if(!o){Yr(t,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox);if(Ur(s?.agent)){Yr(t,!1);return}let a=await Ae(n.leadBox,{lines:De}).catch(()=>"");if(ec(s?.agent,a)){Yr(t,!1);return}if(Lr(s?.agent,a)){let c=await Qt(n.leadBox).catch(()=>a);if(Vn(s,c)){Yr(t,!1);return}}if(!Yr(t,!0))return;let i=t.items.shift();t.idleSince=null,await oS(n,s,o,i.text,i.kind??null,i.attachments??null),await Z()}finally{t.flushing=!1}}}async function rS(e,t,n,r){let o=Ur(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(ds(e,[n]),await Z()),xb(e.id,o))try{await R(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 QN(e,t){let n=await Ae(e.leadBox,{lines:De}).catch(()=>"");if(!Lr(t?.agent,n))return!1;let r=await Qt(e.leadBox).catch(()=>n);return!!Vn(t,r)}async function oS(e,t,n,r,o=null,s=null){await rS(e,t,r,o);let a=Fa.get(e.id)||0,i=r,c=!1,l=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(i=Z0(r,e.pendingHandoffHydrate.artifact||Zr),delete e.pendingHandoffHydrate,c=!0);let u=Array.isArray(s)?s.filter(d=>d&&d.rel):[];if(u.length){let d=P0(u.map(p=>p.rel));i=i.trim()?`${i.trim()}
|
|
158
158
|
|
|
159
|
-
${d}`:d}let f=
|
|
159
|
+
${d}`:d}let f=he(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,...o?{kind:o}:{},...u.length?{attachments:u}:{}});us(e,null);try{if(await Fn({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:Ga(n.agent),bailOnDialog:!0}),(Fa.get(e.id)||0)!==a){!o&&r.trim()&&(ds(e,[r]),await Z());let d=new Error("delivery stopped \u2014 the message was handed back as the draft");throw d.interrupted=!0,d}if(await QN(e,t)){l&&(e.pendingHandoffHydrate=l);let d=pl(e.id),p={id:ms(),text:r,ts:Date.now(),...o?{kind:o}:{},...u.length?{attachments:u}:{}};d.items.unshift(p),Kp(e.id);let m=new Error("lead is blocked on a prompt \u2014 message queued");throw m.requeued={id:p.id,position:d.items.length},m}await je(e.leadBox,i,{...Pt(t?.agent),kind:c?"lead-handoff-first-prompt":"say"})}catch(d){if(he(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:f.at,text:r,...d?.interrupted?{reason:"interrupted"}:{},...d?.requeued?{reason:"requeued"}:{}}),d?.requeued)return{queued:!0,...d.requeued};throw d}await mP(e,t,i)||(await aS(e.leadBox,t?.agent,i.length+8).catch(()=>{}),he(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:f.at,text:r,reason:"unsubmitted"}),o||ds(e,[r])),c&&await z({flow:"lead-handoff",phase:"first-prompt",trackId:e.id,sessionName:e.leadBox,agent:t?.agent,userTextLen:r.length})}function Gp(e,{actions:t}={}){pl(e).items.push({id:ms(),text:Zb(t),ts:Date.now(),kind:"ship-notify"}),Kp(e)}async function bt(e,t,{kind:n=null,attachments:r=null}={}){let o=Array.isArray(r)?r.filter(p=>p&&typeof p.rel=="string"):[];if((typeof t!="string"||!Po(t))&&!o.length)throw new Error("text (string) or an attachment required");let s=te(await G(),e);if(!s)throw new Error(`track "${e}" not found`);if(s.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before driving it`);um(s);let a=s.boxes.find(p=>p.name===s.leadBox),i=Po(t);await rS(s,a,i,n),Ja(e,"say");let c=await se(s.leadBox);if(!c&&await gl(s)&&(await Z(),c=await se(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=pl(e),u=await Ae(s.leadBox,{lines:De}).catch(()=>""),f=l.items.length>0||ec(a?.agent,u);if(!f&&Lr(a?.agent,u)){let p=await Qt(s.leadBox).catch(()=>u);f=!!Vn(a,p)}if(f){let p={id:ms(),text:i,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(p),Kp(e),us(s,null),await Z(),{ok:!0,leadBox:s.leadBox,queued:!0,id:p.id,position:l.items.length}}let d=await oS(s,a,c,i,n,o);return await Z(),d?.queued?{ok:!0,leadBox:s.leadBox,queued:!0,id:d.id,position:d.position}:{ok:!0,leadBox:s.leadBox}}async function Vp(e,{hint:t}={}){let n=te(await G(),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=Xf(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";Ex(n.id,{name:s,box:n.leadBox});let a=String(t||"").trim()?`${q0}
|
|
160
160
|
|
|
161
|
-
What to preview: ${String(t).trim()}`:
|
|
161
|
+
What to preview: ${String(t).trim()}`:q0;try{await bt(e,a,{kind:"spin-up-view"})}catch(i){throw Jc(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function ml(e){let t=te(await G(),e);if(!t)throw new Error(`track "${e}" not found`);if(t.archivedAt)throw new Error(`track "${e}" is archived`);Fa.set(e,(Fa.get(e)||0)+1);let n=za(e),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=ls.get(e);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=ds(t,r),await Z()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:t.draftRev||0}}let s=sP(e,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,ir(eP)]);let a=te(await G(),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 sS(e){let t=ls.get(e);return!!t&&!t.canceled}function Ja(e,t){let n=ls.get(e);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=t||null)}function zp(e){Ja(e,"track-ended"),Fa.delete(e),za(e)}function sP(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}),ls.set(e,r),(async()=>{let s=te(await G(),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<tP&&!r.canceled;y++){await Oe(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let b of nP){await ir(b);let x=await Ae(s.leadBox,{lines:De}).catch(()=>"");if(!Qs(a?.agent,x)){c=!0;break e}}}if(!c){l=!0;for(let y=0;y<oP&&!r.canceled&&(await ir(rP),!r.canceled);y++){let b=await Ae(s.leadBox,{lines:De}).catch(()=>"");if(!Qs(a?.agent,b)){c=!0;break}await Oe(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=te(await G(),e)||s,f=u?.boxes.find(y=>y.name===u.leadBox)||a,d=null,p=null;u&&f&&i&&c&&!r.canceled&&(d=await lP(u,f).catch(()=>null),d||(p=await fP(u,f).catch(()=>null)));let m=u&&i&&!r.canceled?await iP(u).catch(()=>null):null,w=[d||p,...r.queuedTexts].filter(Boolean);u&&w.length&&(r.restored=ds(u,w),await Z()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await aP(u||s,f,{ts:r.startedAt,result:r.result,dropped:t,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!d,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!m,durationMs:Date.now()-r.startedAt,...r.cancelReason?{cancelReason:r.cancelReason}:{}}),await z({flow:"interrupt",phase:r.result==="gave-up"?"failed":"done",trackId:e,sessionName:u?.leadBox,agent:f?.agent,result:r.result,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!d,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!m}).catch(()=>{})})().catch(()=>{}).finally(()=>{r.result||(r.result=r.canceled?"canceled":"gave-up"),ls.get(e)===r&&ls.delete(e),o()}),r}async function aP(e,t,n){await R(e,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:e.leadBox,provider:t?.agent||null,payload:n}).catch(()=>{})}async function iP(e){let t=Qc(await B(e));if(!t?.offerId)return null;let n=t.offerType==="exists";return await R(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 lP(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 cP){await ir(r);let o=await Ae(e.leadBox,{lines:De}).catch(()=>""),s=oa(t.agent,o);if(s&&cd(n.text,s))return await aS(e.leadBox,t.agent,n.text.length+8),he(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:n.at,text:n.text}),n.text}return null}async function fP(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>uP||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!Ne(r.text))return null;for(let s of dP){await ir(s);let a;try{a=await Ld(t,{})}catch{return null}if(Ag(a,r))return null;let i=await Ae(e.leadBox,{lines:De}).catch(()=>"");if(oa(t.agent,i))return null}return he(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}async function mP(e,t,n){if(t?.agent!=="claude")return!0;for(let r=0;r<=Rp;r++){let o=!1;for(let s of pP){await ir(s);let a=await Ae(e.leadBox,{lines:De}).catch(()=>""),i=Eg(t.agent,n,a,{busy:Qs(t.agent,a)});if(i==="stuck"){o=!0;break}if(i==="submitted")return!0}if(!o)return!0;r<Rp&&(await Oe(e.leadBox,["Enter"],{kind:"submit-retry"}),z({flow:"say",phase:"submit-retry",trackId:e.id,sessionName:e.leadBox,agent:t.agent,attempt:r+1,textHead:n.slice(0,120)}).catch(()=>{}))}return z({flow:"say",phase:"submit-failed",trackId:e.id,sessionName:e.leadBox,agent:t.agent,retries:Rp,textHead:n.slice(0,120)}).catch(()=>{}),!1}async function aS(e,t,n){for(let r=0;r<3;r++){await xo(e,n,{kind:"unsend-wipe"}).catch(()=>{}),await ir(200);let o=await Ae(e,{lines:De}).catch(()=>""),s=oa(t,o);if(!s)return!0;n=s.length+8}return!1}function us(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 ds(e,t){let n=[],r=o=>n.some(s=>Ne(s).includes(Ne(o)));for(let o of[e.draft?.text,...t])o&&o.trim()&&!r(o)&&n.push(o);return n.length?us(e,n.join(`
|
|
162
162
|
|
|
163
|
-
`),{source:"restore"})?.text??null:null}function
|
|
163
|
+
`),{source:"restore"})?.text??null:null}function wP(e,t){let n=Ne(t);if(!n)return!1;if(Qr(e.id).some(o=>Ne(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>hP)break;if(!(s.type!=="said"||!s.text||Ne(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function Jp(e,t,{rev:n=null}={}){if(typeof t!="string")throw new Error("text (string) required");let r=te(await G(),e);if(!r)throw new Error(`track "${e}" not found`);let o=t,s=Po(t);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&wP(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;Ne(o).includes(Ne(i))?us(r,o):us(r,`${i}
|
|
164
164
|
|
|
165
|
-
${o}`,{source:"restore"})}else
|
|
166
|
-
`);return`${
|
|
165
|
+
${o}`,{source:"restore"})}else us(r,o);return await Z(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Xp(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=te(await G(),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(f=>f.name===r.leadBox)?.agent||o.agent||"claude",i=Cb(a,n),l=[...r.events||[]].reverse().find(f=>f.type==="model-change")?.to??null;l||(l=(await Gs(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await Fn({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:Ga(o.agent)});let u=await Mb(a,r.leadBox,i);return he(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await Z(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function iS(e){for(let t of e){if(!t||!await Bo(t))continue;let n=await lb(t).catch(()=>null);if(!n)continue;if((await D.stat(F.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function ut(e){let t=e;return t?t.kind==="multi"?Array.isArray(t.repos)?t.repos.filter(Boolean):[]:t.base?[{name:F.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 ut(e?.workspace)){if(!n?.dir)continue;let r=await ua(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await da(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function cS(e){let t=e;typeof t=="string"&&!F.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await fo(t)??await Ri(t));let n=await iS([t]);if(!n)throw new Error(`tagged repo "${e}" is not a usable base (need a primary checkout or a synced owner/name)`);return n}async function gP(e,t,n,r){let o=`track/${mm(t)}-${e}`,s=[];try{await D.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await cS(i);if(s.some(p=>p.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await pe(c).catch(()=>null),u=F.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let p=2;a.has(u);p++)u=`${F.basename(c)}-${p}`;a.add(u);let f=F.join(n,u),d=await Lo(c);await jo(c,f,o,d),s.push({name:u,dir:f,base:c,baseRef:d,repo:l})}return await Xa(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await it(i.base,i.dir).catch(()=>{});throw await D.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function Yp(e,t){let n=await G(),r=te(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 cS(typeof t=="string"?t.trim():t);if((o.repos??[]).some(d=>d?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await pe(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(d=>d?.name).filter(Boolean)]),c=F.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let d=2;i.has(c);d++)c=`${F.basename(s)}-${d}`;let l=F.join(o.dir,c),u=await Lo(s);await ub(s,o.branch)?await gc(s,l,o.branch):await jo(s,l,o.branch,u);let f={name:c,dir:l,base:s,baseRef:u,repo:a};return o.repos=[...(o.repos??[]).filter(Boolean),f],await Xa(o.dir,o.repos,o.branch),he(r,"retagged",{action:"add",repo:a??s,name:c,workspace:lS(o)}),await Z(),f}async function Zp(e,t){let n=await G(),r=te(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 lt.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 ua(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await da(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 it(s.base,s.dir).catch(()=>{}),await D.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await Xa(o.dir,o.repos,o.branch),he(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:lS(o)}),await Z(),{removed:s.name}}async function yP(e,t,{repo:n,repos:r,cwd:o}={}){await D.mkdir(Da,{recursive:!0});let s=F.join(Da,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return gP(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!F.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await fo(n)??await Ri(n));let i=await iS([n,o,DN]);if(i){let c=`track/${mm(t)}-${e}`;try{let l=await Lo(i);await jo(i,s,c,l),await Ha(s);let u=await pe(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await D.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await D.mkdir(s,{recursive:!0}),await Qd(s),await Ha(s),{dir:s,base:null,branch:null,kind:"repo"}}async function Ha(e){try{await D.writeFile(F.join(e,"AGENTS.md"),Ba,{flag:"wx"})}catch{return}await SP(e,["/AGENTS.md"])}function xP(e,t){let n=e.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
|
|
166
|
+
`);return`${kP}
|
|
167
167
|
|
|
168
168
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
169
169
|
git worktree per tagged repo, ALL on the same branch \`${t}\`:
|
|
@@ -175,20 +175,20 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
175
175
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
176
176
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
177
177
|
or preview an app, run it from that app's own repo directory.
|
|
178
|
-
`}async function
|
|
178
|
+
`}async function Xa(e,t,n){let r=xP(t,n);for(let o of["CLAUDE.md","AGENTS.md"])await D.writeFile(F.join(e,o),r).catch(()=>{})}async function dS(e){let{stdout:t}=await z0("git",["-C",e,"rev-parse","--git-path","info/exclude"]);return F.resolve(e,t.trim())}async function SP(e,t){try{let n=await dS(e);await D.mkdir(F.dirname(n),{recursive:!0});let r=await D.readFile(n,"utf8").catch(()=>""),o=t.filter(s=>!r.split(`
|
|
179
179
|
`).includes(s));o.length&&await D.appendFile(n,(r.endsWith(`
|
|
180
180
|
`)||!r?"":`
|
|
181
181
|
`)+o.join(`
|
|
182
182
|
`)+`
|
|
183
|
-
`)}catch{}}async function
|
|
183
|
+
`)}catch{}}async function vP(e,t){try{let n=await dS(e),r=await D.readFile(n,"utf8"),o=r.split(`
|
|
184
184
|
`).filter(s=>s!==t);o.length!==r.split(`
|
|
185
185
|
`).length&&await D.writeFile(n,o.join(`
|
|
186
|
-
`))}catch{}}async function
|
|
186
|
+
`))}catch{}}async function Qp(){let e={refreshed:0,removed:0,codexGlobal:null};try{await D.mkdir(Gt,{recursive:!0}),await D.writeFile(F.join(Gt,"CLAUDE.md"),bP).catch(()=>{}),await D.writeFile(F.join(Gt,"AGENTS.md"),Ba).catch(()=>{});let t=process.env.CODEX_HOME||F.join(process.env.HOME||"/tmp",".codex");if((await D.stat(t).catch(()=>null))?.isDirectory()){let r=F.join(t,"AGENTS.md"),o=await D.readFile(r,"utf8").catch(()=>null);o===null?(await D.writeFile(r,Op,{flag:"wx"}).catch(()=>{}),e.codexGlobal="created"):$p(o)&&o!==Op&&(await D.writeFile(r,Op).catch(()=>{}),e.codexGlobal="refreshed")}for(let r of[Da,ps]){let o=await D.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=F.join(r,s.name),i=F.join(a,"AGENTS.md"),c=await D.readFile(i,"utf8").catch(()=>null);c!==null&&$p(c)&&c!==Ba&&(await D.writeFile(i,Ba).catch(()=>{}),e.refreshed++);let l=F.join(a,"CLAUDE.md"),u=await D.readFile(l,"utf8").catch(()=>null);u!==null&&$p(u)&&(await D.rm(l,{force:!0}).catch(()=>{}),await vP(a,"/CLAUDE.md"),e.removed++)}}}catch{}return e}async function fS(e){let t=e.workspace;if(!(!t||!t.dir))try{if(t.kind==="multi"){for(let n of ut(t))n.base&&n.dir&&await it(n.base,n.dir).catch(()=>{});await D.rm(t.dir,{recursive:!0,force:!0})}else t.kind==="worktree"&&t.base?await it(t.base,t.dir):await D.rm(t.dir,{recursive:!0,force:!0})}catch{}}async function hl({name:e,cwd:t,repo:n,repos:r,agent:o="claude",skipPermissions:s=!0}){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");let a=await Va(o,s),i=await G(),c=ms().slice(0,8),l=await yP(c,e,{repo:n,repos:r,cwd:t}),u=new Set(i.tracks.flatMap(m=>(m.boxes||[]).map(w=>w.name))),f=`${e.trim()} lead`;u.has(qi(f))&&(f=`${e.trim().slice(0,50).trim()} ${c} lead`);let d=await Dn(f,l.dir,{agentCmd:a,agent:o});ea({cwd:d.cwd,sessionName:d.name,agent:o,agentCmd:a});let p={id:c,name:e.trim(),cwd:d.cwd,workspace:l,leadBox:d.name,boxes:[am(d,"lead","live")],events:[],createdAt:Date.now()};return he(p,"opened",{agent:o,box:d.name,skipPermissions:s,name:p.name,cwd:p.cwd,workspace:{base:l.base??null,branch:l.branch,kind:l.kind,...l.baseRef?{baseRef:l.baseRef}:{},...l.repo?{repo:l.repo}:{},...l.repos?{repos:l.repos.map(({name:m,base:w,baseRef:y,repo:b})=>({name:m,base:w,baseRef:y,repo:b}))}:{}}}),i.tracks.push(p),await Z(),Vt(p)}async function em({provider:e="claude",fresh:t=!1}={}){if(!TP.has(e))throw new Error(`unknown setup provider "${e}" (claude | codex | cursor)`);let r=(await G()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===e&&!u.archivedAt);if(r){if(!t){let u=await Vt(r);if(u.boxes.find(d=>d.name===u.leadBox)?.state==="live")return u}await eo(r.id).catch(()=>{})}let o=(await Va(e,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=ms().slice(0,8),a=F.join(Gt,"setup",s);await D.mkdir(a,{recursive:!0});let i=await Dn(`Connect ${e} ${s}`,a,{agentCmd:o,agent:e,firstRun:!0});e==="codex"&&ea({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:[am(i,"lead","live")],events:[],createdAt:Date.now()};return he(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 G()).tracks.push(c),await Z(),Vt(c)}async function tm(e,t,n){await Mx(e).catch(()=>{});let r=fs.get(e);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),fs.delete(e),t.handoff&&!t.handoff.finishedAt&&(t.handoff.step="error",t.handoff.error=n,t.handoff.finishedAt=Date.now());for(let a of to(e))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await gm(a).catch(()=>{}),lt.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 G(),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 Nt(a.name).catch(()=>{})}async function nm(e,{cascadeMocks:t=!0,cascadeRunbooks:n=!1}={}){let r=await G(),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 Qx(s),zp(e),await tm(e,s,"track deleted"),n?await $0(e).catch(()=>{}):await R0(e).catch(()=>{}),await Bb(),await fS(s),t&&await e0(e).catch(()=>{}),t?await g0(e).catch(()=>{}):await w0(s).catch(()=>{}),he(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await Z(),{ok:!0,id:e,name:s.name,boxes:s.boxes.length}}async function eo(e){let t=await G(),n=te(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(),zp(e),he(n,"archived",{boxes:n.boxes.length}),await Z(),n.kind==="setup"?(await pS(e),Vt(n)):(Wa.add(e),_P(e).catch(()=>{}).finally(()=>Wa.delete(e)),{...await Vt(n),archiving:!0})}async function _P(e){try{await IP(e)}catch{}await pS(e).catch(()=>{})}async function pS(e){let t=await G(),n=te(t,e);if(!(!n||!n.archivedAt)){await tm(e,n,"track archived");for(let r of n.boxes)ar.has(r.state)||(r.state="stopped");await Z()}}async function IP(e){let t=await G(),n=te(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 cy(n.leadBox,i.cwd,{timeoutMs:EP})),s="lead-cli"}catch{}if(r||(r=Sy(n,await B(n).catch(()=>[]))),!r)return;let c={text:r,source:s,at:Date.now()};o.length&&(c.items=o),n.beat=c,await R(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 Z()}async function cn(e,t){let n=await Xe(e,t).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await mS(n);if(r.length){for(let o of r)await Hr(o.repo,o.number,!0).catch(()=>{});return!1}try{return await eo(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function mS(e){return(await Wr().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 ln(e,t){return wl(e,t,"merged")}async function wl(e,t,n="merged"){let r=await Xe(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 mS(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await ik(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await bt(r.trackId,hS(a),{kind:"merge-notify"}),!0}catch{return!1}}function hS(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 ${K0[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 ${K0[i.outcome]??i.outcome}`).join(`
|
|
187
187
|
`),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}
|
|
188
188
|
${s}
|
|
189
|
-
${a}`}async function al(e){let t=e.boxes.find(o=>o.name===e.leadBox);if(!t)return!1;let n=bp.get(t.name);if(n)return n;let r=iP(e,t).finally(()=>bp.delete(t.name));return bp.set(t.name,r),r}async function iP(e,t){if(e.archivedAt||e.transferredTo||nr.has(t.state)||await oe(t.name))return!1;let n=Ep(t.agent,t.sessionId),r=await Ni(n);if(r===co)return pe(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;r===lo&&console.warn(`[track] ${t.name}: could not confirm "${t.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);try{let o=await Nn(t.name,t.cwd,{agentCmd:n,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=Ss(o.hookToken),Vs({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:n}),t.state="live",pe(e,"rehydrated",{box:t.name,agent:t.agent}),!0}catch(o){return pe(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(o?.message||o)}),!1}}async function qp(e){let t=await G(),n=te(t,e);if(!n)throw new Error(`track "${e}" not found`);if(!n.archivedAt)throw new Error(`track "${e}" is not archived`);return delete n.archivedAt,Ba.delete(e),pe(n,"unarchived",{}),await al(n),await Z(),Kt(n)}async function Kp(){let e=await G(),t=new Set((await xt()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!s.archivedAt).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(s.archivedAt)for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Ct(a.name).catch(()=>{}),pe(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!nr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await Z(),r}async function il(e){let t=await G(),n=te(t,e);if(!n)throw new Error(`track "${e}" not found`);if(n.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before restarting the lead`);Xp(n);let r=n.boxes.find(a=>a.name===n.leadBox);if(!r)throw new Error(`track "${e}" has no lead box`);if(nr.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await oe(n.leadBox)){let a=await Ae(n.leadBox,{lines:De}).catch(()=>"");if(qi(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Ct(n.leadBox),r.state="stopped"}let s=await al(n);if(await Z(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Kt(n)}async function Gp(e,t,n){let r=await ee(e);if(!r)throw new Error(`track "${e}" not found`);if(!t)throw new Error("unknown offer");let o=await B(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 il(e),await I(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 cl(e){return(!Array.isArray(e.epochs)||e.epochs.length===0)&&(e.epochs=[{agent:e.agent??null,sessionId:e.sessionId??null,pinned:e.pinned===!0,startedAt:e.createdAt??null,endedAt:null}]),e.epochs}async function Xc(e,t){let n=await G(),r=te(n,e);r&&(r.handoff={...r.handoff||{},...t},await Z())}function Q0(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 zp(){let e=await G(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=Q0(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await oe(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(),f=cl(l),d=f[f.length-1],m=d.agent,h=d.sessionId??null;d.endedAt=u,r.reason&&(d.endReason=r.reason);let w=c.agent==="claude"&&!!c.sessionId;f.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:w,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=w,l.state="live",n.pendingHandoffHydrate={artifact:Gr,at:u},pe(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:m,toAgent:r.toAgent,fromSessionId:h,toSessionId:c.sessionId||null,epoch:f.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await z({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 z({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Ct(i).catch(()=>{}),a&&await Ct(a).catch(()=>{})}return t&&await Z(),t}async function ll(e,t,n=!0,r=null){let o=ss.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=te(await G(),e);if(!s)throw new Error(`track "${e}" not found`);Ha(e,"handoff");let a=await Fa(t,n),i=Pr(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 oe(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:B0(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return ss.set(e,l),await Xc(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),cP(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 Xc(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),z({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),z({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function cP(e,t,n,r,o){let s=async(_,q={})=>{e.step=_,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${_}`),await Xc(e.trackId,{step:_,toBox:e.toBox}),await z({flow:"lead-handoff",phase:_,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:q.sessionName,...q})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await G(),i=te(a,e.trackId)?.boxes.find(_=>_.name===e.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:e.fromBox}),e.checkpoint="llm",Pr(c))e.checkpoint="skipped-capped";else try{await ld(e.fromBox,{timeoutMs:N0})}catch(_){e.checkpoint="reconstructed",await z({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:_?.message||String(_)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await ud(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 d=await wd(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox}),await Dn({cwd:n.cwd,sessionId:d.sessionId,sessionName:e.fromBox,agent:o,agentCmd:La(o)});let m=await Ae(e.fromBox,{lines:De}).catch(()=>"");if(!Ki(o,m)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let _=Ep(c,l,{skipPermissions:e.skipPermissions});await wd(e.fromBox,{agentCmd:_,agent:c,resumeSessionId:l,pinned:u});let q=await G(),C=te(q,e.trackId);if(C){let N=C.boxes.find(T=>T.name===e.fromBox);N&&(N.state="live"),pe(C,"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 Z()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let h=await G(),w=te(h,e.trackId);if(!w)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let y=w.boxes.find(_=>_.name===e.fromBox);if(!y)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let b=Date.now(),x=cl(y),E=x[x.length-1];E.endedAt=b,e.reason&&(E.endReason=e.reason),x.push({agent:o,sessionId:d.sessionId,pinned:d.pinned,startedAt:b,endedAt:null}),y.agent=o,y.sessionId=d.sessionId,y.pinned=d.pinned,y.state="live",y.role="lead",w.pendingHandoffHydrate={artifact:e.statusArtifact||Gr,at:b},pe(w,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:d.sessionId,epoch:x.length-1,singleBox:!0,forced:d.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await Z(),await s("done",{sessionName:e.fromBox}),await Xc(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function Yp(e){let t=Ap.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await G(),r=te(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=ss.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of ct.values())if(a.trackId===e&&!a.finishedAt)throw new Error("a subtask is still running on this Track \u2014 wait for it to merge back before moving");let s={trackId:e,step:"checkpoint",startedAt:Date.now(),finishedAt:null,error:null};return Ap.set(e,s),lP(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 lP(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await oe(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&Pr(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await ld(t.leadBox,{timeoutMs:N0})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await ud(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 lt(t.workspace)){if(!u?.dir)continue;let f={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await yn(u.dir,["push","origin",o],Tp);let{stdout:d}=await yn(u.dir,["rev-parse","HEAD"]);f.baseSha=d.trim(),f.pushed=!0}catch(d){f.error=String(d?.message||d).split(`
|
|
190
|
-
`)[0]}s.push(f)}e.step="wip";let a=await
|
|
191
|
-
`)[0]}else l?.sha&&(T="wip snapshots cover single-repo worktrees only");await
|
|
189
|
+
${a}`}async function gl(e){let t=e.boxes.find(o=>o.name===e.leadBox);if(!t)return!1;let n=Cp.get(t.name);if(n)return n;let r=RP(e,t).finally(()=>Cp.delete(t.name));return Cp.set(t.name,r),r}async function RP(e,t){if(e.archivedAt||e.transferredTo||ar.has(t.state)||await se(t.name))return!1;let n=Lp(t.agent,t.sessionId),r=await Wi(n);if(r===go)return he(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;r===yo&&console.warn(`[track] ${t.name}: could not confirm "${t.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);try{let o=await Dn(t.name,t.cwd,{agentCmd:n,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=$s(o.hookToken),ea({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:n}),t.state="live",he(e,"rehydrated",{box:t.name,agent:t.agent}),!0}catch(o){return he(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(o?.message||o)}),!1}}async function rm(e){let t=await G(),n=te(t,e);if(!n)throw new Error(`track "${e}" not found`);if(!n.archivedAt)throw new Error(`track "${e}" is not archived`);return delete n.archivedAt,Wa.delete(e),he(n,"unarchived",{}),await gl(n),await Z(),Vt(n)}async function om(){let e=await G(),t=new Set((await St()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!s.archivedAt).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(s.archivedAt)for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Nt(a.name).catch(()=>{}),he(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!ar.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await Z(),r}async function yl(e){let t=await G(),n=te(t,e);if(!n)throw new Error(`track "${e}" not found`);if(n.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before restarting the lead`);um(n);let r=n.boxes.find(a=>a.name===n.leadBox);if(!r)throw new Error(`track "${e}" has no lead box`);if(ar.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await se(n.leadBox)){let a=await Ae(n.leadBox,{lines:De}).catch(()=>"");if(ec(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Nt(n.leadBox),r.state="stopped"}let s=await gl(n);if(await Z(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Vt(n)}async function sm(e,t,n){let r=await ee(e);if(!r)throw new Error(`track "${e}" not found`);if(!t)throw new Error("unknown offer");let o=await B(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 yl(e),await R(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 bl(e){return(!Array.isArray(e.epochs)||e.epochs.length===0)&&(e.epochs=[{agent:e.agent??null,sessionId:e.sessionId??null,pinned:e.pinned===!0,startedAt:e.createdAt??null,endedAt:null}]),e.epochs}async function cl(e,t){let n=await G(),r=te(n,e);r&&(r.handoff={...r.handoff||{},...t},await Z())}function wS(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 im(){let e=await G(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=wS(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(),f=bl(l),d=f[f.length-1],p=d.agent,m=d.sessionId??null;d.endedAt=u,r.reason&&(d.endReason=r.reason);let w=c.agent==="claude"&&!!c.sessionId;f.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:w,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=w,l.state="live",n.pendingHandoffHydrate={artifact:Zr,at:u},he(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,toAgent:r.toAgent,fromSessionId:m,toSessionId:c.sessionId||null,epoch:f.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await z({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 z({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Nt(i).catch(()=>{}),a&&await Nt(a).catch(()=>{})}return t&&await Z(),t}async function kl(e,t,n=!0,r=null){let o=fs.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=te(await G(),e);if(!s)throw new Error(`track "${e}" not found`);Ja(e,"handoff");let a=await Va(t,n),i=Ur(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:Q0(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return fs.set(e,l),await cl(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),$P(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 cl(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),z({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),z({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function $P(e,t,n,r,o){let s=async(_,q={})=>{e.step=_,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${_}`),await cl(e.trackId,{step:_,toBox:e.toBox}),await z({flow:"lead-handoff",phase:_,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:q.sessionName,...q})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await G(),i=te(a,e.trackId)?.boxes.find(_=>_.name===e.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:e.fromBox}),e.checkpoint="llm",Ur(c))e.checkpoint="skipped-capped";else try{await vd(e.fromBox,{timeoutMs:Y0})}catch(_){e.checkpoint="reconstructed",await z({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:_?.message||String(_)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await Td(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 d=await $d(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox}),await Fn({cwd:n.cwd,sessionId:d.sessionId,sessionName:e.fromBox,agent:o,agentCmd:Ga(o)});let p=await Ae(e.fromBox,{lines:De}).catch(()=>"");if(!tc(o,p)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let _=Lp(c,l,{skipPermissions:e.skipPermissions});await $d(e.fromBox,{agentCmd:_,agent:c,resumeSessionId:l,pinned:u});let q=await G(),C=te(q,e.trackId);if(C){let N=C.boxes.find(T=>T.name===e.fromBox);N&&(N.state="live"),he(C,"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 Z()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let m=await G(),w=te(m,e.trackId);if(!w)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let y=w.boxes.find(_=>_.name===e.fromBox);if(!y)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let b=Date.now(),x=bl(y),E=x[x.length-1];E.endedAt=b,e.reason&&(E.endReason=e.reason),x.push({agent:o,sessionId:d.sessionId,pinned:d.pinned,startedAt:b,endedAt:null}),y.agent=o,y.sessionId=d.sessionId,y.pinned=d.pinned,y.state="live",y.role="lead",w.pendingHandoffHydrate={artifact:e.statusArtifact||Zr,at:b},he(w,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:d.sessionId,epoch:x.length-1,singleBox:!0,forced:d.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await Z(),await s("done",{sessionName:e.fromBox}),await cl(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function lm(e){let t=jp.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await G(),r=te(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=fs.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of lt.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 jp.set(e,s),OP(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 OP(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&&Ur(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await vd(t.leadBox,{timeoutMs:Y0})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await Td(t.id,t.cwd,{trackName:t.name||t.id}).catch(()=>null),e.step="push";let o=t.workspace?.branch??null,s=[];if(o)for(let u of ut(t.workspace)){if(!u?.dir)continue;let f={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await kn(u.dir,["push","origin",o],Dp);let{stdout:d}=await kn(u.dir,["rev-parse","HEAD"]);f.baseSha=d.trim(),f.pushed=!0}catch(d){f.error=String(d?.message||d).split(`
|
|
190
|
+
`)[0]}s.push(f)}e.step="wip";let a=await xp({id:t.id,cwd:t.cwd}),i=await G(),c=te(i,t.id),l=c?.boxes.find(u=>u.name===c.leadBox)||null;l&&bl(l),await Z(),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:f,baseSha:d})=>({name:u,repo:f,baseSha:d})),unpushed:s.filter(u=>!u.pushed).map(({name:u,repo:f,error:d})=>({name:u,repo:f,error:d})),wip:a.pushed?{ref:a.ref,sha:a.sha}:null,checkpoint:e.checkpoint,epochs:l?.epochs??[],codeless:!o||!s.some(u=>u.pushed)},e.step="ready",e.finishedAt=Date.now()}function um(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 xl(e,{toNode:t,mode:n="transfer"}={}){if(!t||typeof t!="string")throw new Error("toNode required");let r=await G(),o=te(r,e);if(!o)throw new Error(`track "${e}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};zp(e),await tm(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();for(let i of o.boxes){ar.has(i.state)||(i.state="stopped");let c=Array.isArray(i.epochs)?i.epochs[i.epochs.length-1]:null;c&&typeof c=="object"&&c.endedAt==null&&(c.endedAt=s,c.endReason==null&&(c.endReason=n==="recover"?"node-offline":"node-transfer"))}let a=await Sp({id:o.id,cwd:o.cwd});return o.transferredTo=t,o.transferredAt=s,he(o,"transferred-out",{toNode:t,mode:n,box:o.leadBox,...a.pushed?{rescueRef:a.ref,rescueSha:a.sha}:{}}),await Z(),{ok:!0,toNode:t,rescue:a.pushed?{ref:a.ref,sha:a.sha}:null}}async function dm(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await xl(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 G0(e,t,n){try{await gc(e,t,n)}catch{await kn(e,["worktree","prune"]).catch(()=>{}),await gc(e,t,n)}return t}async function fm(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(Mp.has(t))throw new Error("an adopt is already running for this Track");Mp.set(t,{startedAt:Date.now()});try{return await CP({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{Mp.delete(t)}}async function CP({trackId:e,name:t,agent:n,skipPermissions:r,mode:o,fromNode:s=null,toNode:a=null,branch:i=null,repos:c=[],wip:l=null,brief:u=null,epochs:f=[],createdAt:d=null,leadBox:p=null,reason:m=null}){let w=await Va(n,r),y=await G(),b=te(y,e);if(b&&!b.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);b&&(await Sp({id:b.id,cwd:b.cwd}).catch(()=>{}),await fS(b),y.tracks.splice(y.tracks.indexOf(b),1),await Z()),await D.mkdir(Da,{recursive:!0});let x=F.join(Da,e);await D.rm(x,{recursive:!0,force:!0}).catch(()=>{});let E=null,_=!1,q=[];try{let C=[];if(i)for(let g of Array.isArray(c)?c:[]){let h=typeof g=="string"?g:g?.repo;if(!h||!/^[^/\s]+\/[^/\s]+$/.test(h))continue;let S=await fo(h)??await Ri(h);await kn(S,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],Dp),C.push({slug:h,base:S,name:typeof g=="object"?g?.name??null:null})}if(i&&C.length===1){let{base:g,slug:h}=C[0];await G0(g,x,i),q.push({base:g,dir:x}),await Ha(x);let S=await Lo(g);E={dir:x,base:g,branch:i,kind:"worktree",baseRef:S,repo:h}}else if(i&&C.length>=2){await D.mkdir(x,{recursive:!0});let g=new Set([".bostrat"]),h=[];for(let S of C){let I=S.name||F.basename(S.base);g.has(I)&&(I=S.slug.replace("/","-"));for(let ve=2;g.has(I);ve++)I=`${F.basename(S.base)}-${ve}`;g.add(I);let fe=F.join(x,I);await G0(S.base,fe,i),q.push({base:S.base,dir:fe});let ne=await Lo(S.base);h.push({name:I,dir:fe,base:S.base,baseRef:ne,repo:S.slug})}await Xa(x,h,i),E={dir:x,kind:"multi",branch:i,repos:h}}else _=!0,await D.mkdir(x,{recursive:!0}),await Qd(x),await Ha(x),E={dir:x,base:null,branch:null,kind:"repo"};let N=!1,T=null;if(l?.sha&&E.kind==="worktree")try{await kn(x,["fetch","origin",l.ref||kp(e)],Dp);let{stdout:g}=await kn(x,["rev-parse","FETCH_HEAD^"]),{stdout:h}=await kn(x,["rev-parse","HEAD"]);g.trim()===h.trim()?(await kn(x,["read-tree","--reset","-u","FETCH_HEAD"]),await kn(x,["reset","-q"]),N=!0):T="snapshot does not parent the fetched branch head"}catch(g){T=String(g?.message||g).split(`
|
|
191
|
+
`)[0]}else l?.sha&&(T="wip snapshots cover single-repo worktrees only");await U0({id:e,cwd:x}).catch(()=>({restored:!1}));let W=F.join(x,Zr),H=null;(await Di(x)).restored?H="checkpoint":typeof u=="string"&&u.trim()&&(await D.mkdir(F.dirname(W),{recursive:!0}),await D.writeFile(W,u),H="brief");let J=new Set(y.tracks.flatMap(g=>(g.boxes||[]).map(h=>h.name))),A=null;p&&!J.has(p)&&!await se(p).catch(()=>null)?A=p:(A=`${t.trim()} lead`,J.has(qi(A))&&(A=`${t.trim().slice(0,50).trim()} ${e} lead`));let j=await Dn(A,x,{agentCmd:w,agent:n});ea({cwd:j.cwd,sessionName:j.name,agent:n,agentCmd:w});let Q=Date.now(),me=am(j,"lead","live"),Re=(Array.isArray(f)?f:[]).filter(g=>g&&typeof g=="object").map(g=>({...g})),de=Re[Re.length-1];de&&de.endedAt==null&&(de.endedAt=Q,de.endReason==null&&(de.endReason=o==="recover"?"node-offline":"node-transfer")),Re.push({agent:n,sessionId:j.sessionId??null,pinned:j.pinned===!0,startedAt:Q,endedAt:null}),me.epochs=Re;let le={id:e,name:t.trim(),cwd:j.cwd,workspace:E,leadBox:j.name,boxes:[me],events:[],createdAt:Number.isFinite(d)?d:Q,...H?{pendingHandoffHydrate:{artifact:Zr,at:Q}}:{}};return he(le,"transferred-in",{box:j.name,agent:n,fromNode:s,toNode:a,mode:o,reason:m,branch:E.branch??null,wip:N,epoch:Re.length-1,name:le.name,cwd:le.cwd,workspace:{base:E.base??null,branch:E.branch??null,kind:E.kind,...E.baseRef?{baseRef:E.baseRef}:{},...E.repo?{repo:E.repo}:{},...E.repos?{repos:E.repos.map(({name:g,base:h,baseRef:S,repo:I})=>({name:g,base:h,baseRef:S,repo:I}))}:{}}}),y.tracks.push(le),await Z(),{...await Vt(le),adopt:{wipApplied:N,wipSkipped:T,codeless:_,artifactSource:H}}}catch(C){for(let N of q)await it(N.base,N.dir).catch(()=>{});throw await D.rm(x,{recursive:!0,force:!0}).catch(()=>{}),C}}async function gS(e,t,n){let r=await G(),o=te(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await Z())}async function cs(e,t){e.step=t,await Ya(e)}function yS(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 bS(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function BP(e,t,n){if(!bS(n)||lt.has(t)||!await se(n.box).catch(()=>null))return!1;try{await D.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 lt.set(t,s),xS(s,o).catch(a=>kS(s,a)),!0}async function pm(){let e=await G(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(lt.has(r))continue;if(await BP(n,r,o)){t=!0,he(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await z({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=yS(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!ar.has(i.state)&&(i.state="canceled"),he(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 z({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await V0(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||ar.has(r.state)||lt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?F.join(ps,`${n.id}-${r.subtaskId}`):null;await z({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await V0(n,r.name,o)}}return t=await DP(e)||t,t&&await Z(),t}async function V0(e,t,n){if(t&&await Nt(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of ut(e.workspace)){let s=F.join(n,o.name);await it(o.base,s).then(()=>!0,()=>!1)||await it(o.dir,s).catch(()=>{})}await D.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await se(e.leadBox).catch(()=>null);r?.cwd&&await it(r.cwd,n).catch(()=>{})}async function DP(e){let t;try{t=await D.readdir(ps)}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 lt.values())o.dir&&n.add(F.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=F.join(ps,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of ut(a.workspace))await it(i.base,F.join(s,i.name)).catch(()=>{});await D.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await se(a.leadBox).catch(()=>null):null;i?.cwd?await it(i.cwd,s).catch(()=>{}):await D.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await z({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function hm(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Po(n))throw new Error("task (string) required");let s=await Va(t,r),a=Ur(t);if(a){let p=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 p.usageCap=a,p}let i=te(await G(),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 p=ut(i.workspace);if(!p.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let m of p)if(!m?.dir||!await Bo(m.dir))throw new Error(`tagged checkout "${m?.name??"?"}" is missing from the lead workspace (${m?.dir??"no dir"})`);u=p.map(m=>({name:m.name,leadDir:m.dir,base:null}))}else if(!await Bo(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let f=ms().slice(0,8),d={id:f,trackId:e,trackName:i.name,agent:t,task:Po(n),skipPermissions:r,reason:Q0(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${mm(i.name)}-${f}`,base:null,repos:u,dir:F.join(ps,`${e}-${f}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return lt.set(f,d),await Ya(d),jP(d,c,s).catch(p=>kS(d,p)),d}async function kS(e,t){e.failedStep=e.step,e.error=t instanceof qa?"canceled":t?.message||String(t),e.step=t instanceof qa?"canceled":"error",e.finishedAt=Date.now(),await gm(e).catch(()=>{}),await Ya(e).catch(()=>{}),await HP(e).catch(()=>{})}function vl(e){let t=lt.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 wm(e){let t=lt.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 jP(e,t,n){if(await cs(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await Do(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await Ya(e)}else e.base=await Do(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await cs(e,"spawn"),await D.mkdir(ps,{recursive:!0}),e.repos){await D.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await jo(a.leadDir,F.join(e.dir,a.name),e.branch,a.base);let s=ut((te(await G(),e.trackId)||{}).workspace);await Xa(e.dir,e.repos.map(a=>{let i=s.find(c=>c?.name===a.name);return{name:a.name,repo:i?.repo??null,baseRef:null}}),e.branch)}else await jo(t.cwd,e.dir,e.branch,e.base),await Ha(e.dir);await LP(e,t);let r=e.trackName.slice(0,38).trim(),o=await Dn(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await FP(e,o),await eS(e.trackId,o.name,LN(e.task,!!e.repos),{kind:"subtask"}),await xS(e,t)}async function xS(e,t){await cs(e,"working");let n=F.join(e.dir,il);if(await qP(n,e),e.canceled)throw new qa;if(await cs(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=F.join(e.dir,l.name);await Zd(u,`bostrat: subtask ${e.id} result (auto-committed)`),l.stat=await tf(l.leadDir,l.base,e.branch,is),l.files=await nf(l.leadDir,l.base,e.branch,is);let f=await rf(l.leadDir,l.base,e.branch,is);l.applied=f.applied,l.conflicts=f.conflicts??[],s.files+=l.stat.files,s.insertions+=l.stat.insertions,s.deletions+=l.stat.deletions,a.push(...l.files.map(d=>F.join(l.name,d))),i.push(...l.conflicts.map(d=>d.startsWith("patch did not apply cleanly")?`${l.name}: ${d}`:F.join(l.name,d))),f.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await Zd(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`),e.stat=await tf(t.cwd,e.base,e.branch,is),e.files=await nf(t.cwd,e.base,e.branch,is);let s=await rf(t.cwd,e.base,e.branch,is);e.applied=s.applied,e.conflicts=s.conflicts}await Ya(e);let r=await WP(e,t,n);await cs(e,"resume"),await eS(e.trackId,e.leadBox,FN(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await cs(e,"reap"),await gm(e),e.step="done";let o=Date.now();await gS(e.trackId,e.id,{...Sl(e),finishedAt:o}),await UP(e,o),e.finishedAt=o}async function LP(e,t){let n=F.join(e.dir,F.dirname(ja));await D.mkdir(n,{recursive:!0});let r="";try{r=await Yw(t.cwd,t.sessionId)}catch{}if(!r)try{r=await D.readFile(F.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}
|
|
192
192
|
|
|
193
193
|
**Delegated task:** ${e.task}
|
|
194
194
|
|
|
@@ -199,15 +199,15 @@ ${a}`}async function al(e){let t=e.boxes.find(o=>o.name===e.leadBox);if(!t)retur
|
|
|
199
199
|
## Conversation so far (lead \u2192 you)
|
|
200
200
|
|
|
201
201
|
${r}
|
|
202
|
-
`;await D.writeFile(F.join(e.dir,
|
|
202
|
+
`;await D.writeFile(F.join(e.dir,ja),o)}async function FP(e,t){let n=await G(),r=te(n,e.trackId);r&&(r.boxes.push({name:t.name,agent:t.agent,role:"subtask",state:"live",cwd:t.cwd,sessionId:t.sessionId,hookTokenHash:$s(t.hookToken),createdAt:Date.now(),subtaskId:e.id,branch:e.branch,task:e.task}),he(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]||{},...Sl(e)},await Z())}async function UP(e,t=e.finishedAt){let n=await G(),r=te(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),he(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 Z()}async function HP(e){let t=await G(),n=te(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"),he(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]||{},...Sl(e)},await Z()}async function WP(e,t,n){let r=`.bostrat/handoff/subtask-${e.id}.md`;try{let o=F.join(t.cwd,r);await D.mkdir(F.dirname(o),{recursive:!0}),await D.copyFile(n,o)}catch{}return r}async function gm(e){if(e.box&&await Nt(e.box).catch(()=>{}),e.repos&&e.dir){for(let t of e.repos)await it(t.leadDir,F.join(e.dir,t.name)).catch(()=>{});await D.rm(e.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(e.dir){let n=(await se(e.leadBox).catch(()=>null))?.cwd;n&&await it(n,e.dir).catch(()=>{})}}async function qP(e,t){let n=(t.startedAt||Date.now())+MP;for(;;){if(t.canceled||t.forceMerge)return;try{await D.access(e);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await ir(NP)}}function ir(e){return new Promise(t=>setTimeout(t,e))}var z0,Gt,J0,BN,Da,DN,ll,Ka,sr,Zr,Y0,jN,il,ja,LN,FN,yt,Ap,te,Ep,he,_p,Ip,HN,ar,W0,KN,GN,VN,zN,YN,La,q0,eP,tP,nP,rP,oP,ls,Fa,cP,uP,dP,pP,Rp,hP,lS,Ua,uS,bP,Ba,$p,kP,Op,TP,Wa,AP,EP,K0,Cp,am,fs,hs,Dp,kn,jp,cm,Mp,MP,NP,ps,is,lt,PP,to,Sl,Ya,mm,qa,Vr=O(()=>{Je();Ed();vy();nc();Cn();Yi();_y();Ve();Zt();Cs();Xi();qy();$r();Vd();Jd();ib();ld();Zi();bd();vt();yc();Sr();ta();Sc();Nb();df();ux();xd();md();fx();Uf();ue();Zc();Sa();qo();np();os();fp();sl();bp();Yn();B0();vp();H0();ji();z0=PN(NN),Gt=process.env.BOSTRAT_STATE_DIR||F.join(process.env.HOME||"/tmp",".bostrat"),J0=F.join(Gt,"tracks.json"),BN=F.join(Gt,"events.jsonl"),Da=F.join(Gt,"workspaces"),DN=process.env.BOSTRAT_DEFAULT_REPO||null,ll={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"}},Ka=Object.keys(ll);sr={at:0,value:null};Zr=".bostrat/handoff/latest.md",Y0=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;jN=/\(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\.\)$/;il=".bostrat/handoff/subtask-result.md",ja=".bostrat/handoff/lead-context.md",LN=(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 ${ja} \u2014 read that first for context. Your scoped task:
|
|
203
203
|
|
|
204
204
|
${e}
|
|
205
205
|
|
|
206
|
-
Work ONLY inside this directory. cd into the repo a change belongs to before running git commands, and commit per repo with a clear message (repos you don't touch need nothing). When you are finished: (1) make your code changes, (2) commit them in each changed repo, (3) LAST, write a short markdown digest to ${
|
|
206
|
+
Work ONLY inside this directory. cd into the repo a change belongs to before running git commands, and commit per repo with a clear message (repos you don't touch need nothing). When you are finished: (1) make your code changes, (2) commit them in each changed repo, (3) LAST, write a short markdown digest to ${il} (at this directory's root) with sections \u2014 Result (what you did), Files (paths you changed, prefixed with the repo directory), Notes (anything the lead must know). Writing that file is your DONE signal: do it last, then stop and take no further action.`:`You are a SUBTASK agent working in an isolated git worktree, delegated by a lead agent on a shared workstream. The lead's full working tree is already checked out here, and the conversation so far is summarized in ${ja} \u2014 read that first for context. Your scoped task:
|
|
207
207
|
|
|
208
208
|
${e}
|
|
209
209
|
|
|
210
|
-
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 ${
|
|
210
|
+
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 ${il} 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.`,FN=(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."),yt=null,Ap=null;te=(e,t)=>e.tracks.find(n=>n.id===t)||null,Ep=Promise.resolve();he=(e,t,n={})=>{let r={at:Date.now(),type:t,...n};return e.events.push(r),UN(e.id,r),Db(e,r),r};tg(async({sessionName:e,from:t,to:n,version:r,status:o,error:s})=>{let i=(await G()).tracks.find(c=>c.leadBox===e||(c.boxes||[]).some(l=>l.name===e));i&&(he(i,"codex-update",{box:e,from:t,to:r||n,status:o,...s?{error:s}:{}}),await Z().catch(()=>{}))});_p=F.join(Gt,"plane-link","update-notice.json");Ip=(e,t,n)=>t!=null&&n.some(r=>e===r.box&&t>=r.from&&t<=r.to),HN=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);ar=new Set(["merged","conflict","canceled","failed","handed-off"]),W0=new Map;KN=5e3;GN={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};VN=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];zN=10*6e4;YN=1500,La=new Map;q0="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.";eP=4e3,tP=3,nP=[300,350,400],rP=1e3,oP=12,ls=new Map,Fa=new Map;cP=[250,400,500,600];uP=3e4,dP=[350,650,900];pP=[250,450,700],Rp=2;hP=12e4;lS=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}))});Ua="# bostrat workspace",uS=`${Ua}
|
|
211
211
|
|
|
212
212
|
TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
|
|
213
213
|
before substantive work \u2014 it is the shared cross-provider memory for this repo
|
|
@@ -475,68 +475,132 @@ which steps to run (and which to skip). An offer buried mid-message, phrased as
|
|
|
475
475
|
statement, or omitted entirely renders no controls \u2014 so close EVERY completed
|
|
476
476
|
implementation turn this way, every time. When the operator has already asked you
|
|
477
477
|
to commit / push / open the PR, just do it; do not re-offer.
|
|
478
|
-
`,
|
|
479
|
-
`)),
|
|
480
|
-
`,`${
|
|
478
|
+
`,bP=uS,Ba=uS+jg;$p=e=>typeof e=="string"&&(e===Ua||e.startsWith(`${Ua}
|
|
479
|
+
`)),kP="# bostrat multi-repo workspace";Op=Ba.replace(`${Ua}
|
|
480
|
+
`,`${Ua}
|
|
481
481
|
|
|
482
482
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
483
483
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
484
484
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
485
485
|
When working anywhere else, ignore everything below.
|
|
486
|
-
`);nP=new Set(["claude","codex","cursor"]);Ba=new Set,rP=e=>Ba.has(e);oP=9e4;I0={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};bp=new Map;Vp=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:Ss(e.hookToken),createdAt:Date.now()});ss=new Map,cs=e=>ss.get(e)||(gt?te(gt,e)?.handoff:null)||null;Tp=6e4,yn=(e,t,n=15e3)=>O0("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),Ap=new Map,Jp=e=>Ap.get(e)||null;kp=new Map;dP=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,fP=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,as=F.join(qt,"worktrees"),Qo=[Oa,Yc],ct=new Map,pP=e=>ct.get(e)||(gt?gt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,zr=e=>{let t=new Map([...ct.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=gt?te(gt,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)},dl=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});qa=e=>ev(e.trackId,e.id,dl(e));tm=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",Da=class extends Error{}});ze();import me from"node:path";import qe from"node:fs";import iL from"node:readline";import{fileURLToPath as cL}from"node:url";import bt from"express";import lL from"node:http";import uL from"node:https";import uh from"node:os";import{WebSocketServer as dL}from"ws";ze();import WE from"node-pty";async function pw(e,t,n={}){if(!Ee(t)){Li(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await Mn()){Li(e,`host tmux server not running on ${st}`),e.close(1011,"tmux server unavailable");return}if(!await oe(t)){Li(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=fw(n.cols,120,mw),o=fw(n.rows,32,hw),s;try{s=WE.spawn(L,["-S",st,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){Li(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&xu(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(xu(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"&&VE(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{}GE(t)})}var qE=6,KE=250;async function GE(e,t=qE,n=KE){for(let r=0;r<t;r++){if(await kr(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var mw=20,hw=6;function VE(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=mw&&Number(t)>=hw}function fw(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function xu(e,t){try{e.send(JSON.stringify(t))}catch{}}function Li(e,t){xu(e,{type:"error",message:t})}function ww(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}}vt();Ui();fd();Ur();var SP=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function av({tracks:e=[],views:t=[],queuedOf:n=()=>[]}={}){let r=[];for(let o of e)!o||o.archivedAt&&!o.archiving||o.transferredTo||(o.working&&r.push(`working:${o.id}`),o.prompted&&r.push(`prompted:${o.id}`),(n(o.id)??[]).length&&r.push(`queued:${o.id}`),(o.boxes||[]).some(s=>s?.role==="subtask"&&s?.state==="live")&&r.push(`subtask:${o.id}`),Object.values(o.subtasks||{}).some(SP)&&r.push(`merge-back:${o.id}`));return t.some(o=>o?.state==="live"||o?.state==="starting")&&r.push("views"),{busy:r.length>0,reasons:r}}le();Ge();var TP=(...e)=>fetch(...e);function AP(e=process.env){return!!(be(e)&&e.BOSTRAT_PLANE_TOKEN)}async function sm(e=process.env){if(!AP(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 TP(`${be(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 ls(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await sm(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 pl(e,t,{source:n="chat"}={}){return I(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}le();Ge();var EP=(...e)=>fetch(...e);async function ml(e,t,n,r=process.env){if(!(be(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 EP(`${be(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 _P(e,t=process.env){return ml("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function iv({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await _P(t,o):await ls(e,o),a=await ml("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function am(e,t=process.env){let n=await ls(e,t),r=await ml("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function im(e,t=process.env){return(await ml("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function cv(e,t,n,r=process.env){let{blueprint:o,items:s}=await am(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 I(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}le();Ge();var IP=(...e)=>fetch(...e);async function RP(e,t,n,r=process.env){if(!(be(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 IP(`${be(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)}}async function lv(e,{title:t,context:n="",decision:r,alternatives:o=[],supersedesId:s=null}){if(!String(t??"").trim())throw new Error("a title is required");if(!String(r??"").trim())throw new Error("the decision itself is required (what was decided)");return await I(e,{type:"decision.recorded",actor:"orchestrator",source:"orchestrator",payload:{title:String(t).trim(),context:String(n??"").trim(),decision:String(r).trim(),alternatives:(Array.isArray(o)?o:[]).map(a=>typeof a=="string"?{label:a}:a).filter(a=>a?.label),...s?{supersedesId:s}:{}}}),{title:String(t).trim()}}function uv(e,t=process.env){return RP("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}ap();le();import Ya from"node:fs/promises";import Sl from"node:path";import bB from"node:os";Rn();import $P from"node:fs/promises";import dv from"node:path";import OP from"node:os";var fv=()=>dv.join(process.env.BOSTRAT_STATE_DIR||dv.join(process.env.HOME||OP.tmpdir(),".bostrat"),"integrations.json");async function bn(){try{return JSON.parse(await $P.readFile(fv(),"utf8"))||{}}catch{return{}}}var hl=["linked","prOpened","prMerged","create"],cm=["prompt","auto","off"],CP={create:["prompt","off"]},pv=e=>CP[e]??cm;function wl(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of hl){let s=pv(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 Jr(){let e=await bn(),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:wl(e),defaultTeam:n}:null}async function gl(){let e=await bn(),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:wl(e)}}async function lm(){return wl(await bn())}async function mv(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${hl.join(", ")}`);for(let[r,o]of t){if(!hl.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${hl.join(", ")}`);let s=pv(r);if(!s.includes(o))throw new Error(s.length<cm.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${cm.join(", ")}`)}let n=await bn();return n.ticketFlow={...wl(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Qe(fv(),n,{mode:384}),n.ticketFlow}async function Ka(){let e=(await bn()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function MP(){let e=(await bn()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var NP={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function yl(){let t=(await bn()).voiceChat||{},n=t.provider||"openai",r=NP[n];if(!r)return null;let o=n==="openai"?await MP():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function hv(){let e=(await bn()).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 Yr(){let e=(await bn()).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}}le();On();_r();Yo();import Va from"node:fs/promises";import Tv from"node:path";import sB from"node:os";import{randomUUID as Av}from"node:crypto";var dm={};zl(dm,{createComment:()=>WP,findIssueUrl:()=>jP,issueStates:()=>GP,mdToAdf:()=>yv,moveIssue:()=>VP,parseIssueRef:()=>DP,resolveIssue:()=>LP,uploadImage:()=>qP,upsertCard:()=>FP,whoami:()=>um});var us=e=>String(e.baseUrl||"").replace(/\/$/,""),gv=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function Xr(e,t,n,r){let o=await fetch(`${us(e)}${n}`,{method:t,headers:{authorization:gv(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 PP=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,BP=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function DP(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(PP);return r?r[1].toUpperCase():null}let n=t.match(BP);return n?n[1]:null}function jP(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 LP(e,t){let n;try{n=await Xr(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:`${us(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function FP(e,t,{url:n,title:r,subtitle:o}){await Xr(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 UP=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function HP(e){let t=[],n=0;for(let r of e.matchAll(UP)){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 yv(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
487
|
-
|
|
486
|
+
`);TP=new Set(["claude","codex","cursor"]);Wa=new Set,AP=e=>Wa.has(e);EP=9e4;K0={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};Cp=new Map;am=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:$s(e.hookToken),createdAt:Date.now()});fs=new Map,hs=e=>fs.get(e)||(yt?te(yt,e)?.handoff:null)||null;Dp=6e4,kn=(e,t,n=15e3)=>z0("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),jp=new Map,cm=e=>jp.get(e)||null;Mp=new Map;MP=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,NP=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,ps=F.join(Gt,"worktrees"),is=[ja,il],lt=new Map,PP=e=>lt.get(e)||(yt?yt.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,to=e=>{let t=new Map([...lt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=yt?te(yt,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)},Sl=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});Ya=e=>gS(e.trackId,e.id,Sl(e));mm=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",qa=class extends Error{}});Je();import we from"node:path";import Ke from"node:fs";import FL from"node:readline";import{fileURLToPath as UL}from"node:url";import kt from"express";import HL from"node:http";import WL from"node:https";import Ah from"node:os";import{WebSocketServer as qL}from"ws";Je();import d_ from"node-pty";async function Rw(e,t,n={}){if(!Ee(t)){zi(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await Bn()){zi(e,`host tmux server not running on ${at}`),e.close(1011,"tmux server unavailable");return}if(!await se(t)){zi(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=Iw(n.cols,120,$w),o=Iw(n.rows,32,Ow),s;try{s=d_.spawn(L,["-S",at,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){zi(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&Pu(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(Pu(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"&&h_(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{}m_(t)})}var f_=6,p_=250;async function m_(e,t=f_,n=p_){for(let r=0;r<t;r++){if(await _r(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var $w=20,Ow=6;function h_(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=$w&&Number(t)>=Ow}function Iw(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function Pu(e,t){try{e.send(JSON.stringify(t))}catch{}}function zi(e,t){Pu(e,{type:"error",message:t})}function Cw(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}}vt();Xi();Ed();Vr();import{createRequire as KL}from"node:module";var KP=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function ym({tracks:e=[],views:t=[],queuedOf:n=()=>[]}={}){let r=[];for(let o of e)!o||o.archivedAt&&!o.archiving||o.transferredTo||(o.working&&r.push(`working:${o.id}`),o.prompted&&r.push(`prompted:${o.id}`),(n(o.id)??[]).length&&r.push(`queued:${o.id}`),(o.boxes||[]).some(s=>s?.role==="subtask"&&s?.state==="live")&&r.push(`subtask:${o.id}`),Object.values(o.subtasks||{}).some(KP)&&r.push(`merge-back:${o.id}`));return t.some(o=>o?.state==="live"||o?.state==="starting")&&r.push("views"),{busy:r.length>0,reasons:r}}import RS from"node:fs";import lr from"node:fs/promises";import iB from"node:os";import We from"node:path";import{spawn as cB}from"node:child_process";import{createRequire as lB}from"node:module";ue();Sc();import _S from"node:fs";import km from"node:fs/promises";import zP from"node:http";import Sn from"node:path";import Al from"node:os";import{createRequire as JP}from"node:module";import xm from"ws";import GP from"node:path";import VP from"node:os";var Za=process.env.BOSTRAT_STATE_DIR||GP.join(process.env.HOME||VP.tmpdir(),".bostrat");function bm(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 vS(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=bm(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function TS(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}var xn=(()=>{try{return JP(import.meta.url)("../package.json").version||null}catch{return null}})(),XP=process.env.BOSTRAT_STATE_DIR||Sn.join(process.env.HOME||Al.tmpdir(),".bostrat");function no({env:e=process.env,pkgDir:t=Sn.resolve(Sn.dirname(new URL(import.meta.url).pathname),".."),exists:n=_S.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(Sn.join(t,"..",".git"))?"git-checkout":"dev"}function Qa(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 AS(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 YP({home:e=process.env.HOME||Al.homedir(),env:t=process.env}={}){let n=r=>{try{return _S.statSync(r),!0}catch{return!1}};return{claude:n(Sn.join(e,".claude",".credentials.json")),codex:n(Sn.join(t.CODEX_HOME||Sn.join(e,".codex"),"auth.json"))}}var Ue=1,ES=1e3,ZP=3e4,QP=6e4,eB=1500,tB=500,Tl=100,nB=2e3,rB=25e3,oB=6e4,sB=6e4,aB=6e4;function IS({url:e,token:t,nodeId:n=Al.hostname(),name:r=Al.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,metricsMs:c=sB,onUpdateAvailable:l=null,installKind:u=no(),selfUrl:f=`http://127.0.0.1:${process.env.PORT||3362}`,log:d=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=TS(e);let p=f.replace(/^http/,"ws"),m=new Set,w=new Map,y=new Map,b=Sn.join(XP,"plane-link","cursor.json"),x={},E={},_=new Map,q=null,C=!1,N=!1,T=ES,W=null,H=null,ce=null,J=null,A=null,j=!1,Q=0;async function me(){try{x=JSON.parse(await km.readFile(b,"utf8"))}catch{x={}}}function Re(){j=!0,!ce&&(ce=setTimeout(async()=>{if(ce=null,!!j){j=!1;try{await km.mkdir(Sn.dirname(b),{recursive:!0}),await km.writeFile(b,JSON.stringify(x))}catch(X){d.error("[plane-link] cursor flush failed:",X?.message||X)}}},nB),ce.unref?.())}function de(X){return!q||q.readyState!==xm.OPEN?!1:(q.send(JSON.stringify(X)),!0)}function le(X){let P=Date.now();for(let{trackId:$,event:M}of X){let ye=_.get(M.id);_.set(M.id,{trackId:$,seq:M.seq,event:M,sentAt:P,tries:(ye?.tries??0)+1})}return de({v:Ue,ch:"events",type:"append",events:X})}function g(X){let P=null;for(let $ of _.values())$.trackId===X&&(P==null||$.seq<P)&&(P=$.seq);return P}function h(X){let P=E[X];if(P==null)return;let $=g(X),M=$==null?P:Math.min(P,$-1);M>(x[X]??0)&&(x[X]=M,Re())}function S(){if(!C)return;let X=Date.now(),P=[..._.values()].filter($=>$.event&&X-$.sentAt>=oB);if(P.length){P.sort(($,M)=>$.trackId===M.trackId?$.seq-M.seq:$.trackId<M.trackId?-1:1),d.log(`[plane-link] re-sending ${P.length} unacked event(s) the plane never confirmed`);for(let $=0;$<P.length;$+=Tl){let M=P.slice($,$+Tl).map(ye=>({trackId:ye.trackId,event:ye.event}));if(!le(M))return}}}async function I(){if(!i)return;let X;try{X=await i()}catch(P){d.error("[plane-link] metrics sample failed:",P?.message||P);return}X&&de({v:Ue,ch:"ctl",type:"metrics",metrics:X})}function fe(){i&&(A&&clearInterval(A),I(),A=setInterval(I,c),A.unref?.())}async function ne(){let X;try{X=await o()}catch(P){d.error("[plane-link] backfill listTracks failed:",P?.message||P);return}de({v:Ue,ch:"ctl",type:"tracks",ids:X.filter(P=>!P.transferredTo).map(P=>P.id).filter(P=>typeof P=="string")});for(let P of X){if(!C)return;let $;try{$=await B(P)}catch{continue}let M=x[P.id]??0,ye=$.filter(xe=>xe.seq>M&&typeof xe.id=="string");for(let xe=0;xe<ye.length;xe+=Tl){let Ge=ye.slice(xe,xe+Tl).map(ft=>({trackId:P.id,event:ft}));if(!le(Ge))return}}}function ve(X){!C||!X?.trackId||typeof X.id!="string"||le([{trackId:X.trackId,event:X}])}let $e=new Map;async function v(X){let{reqId:P,method:$,path:M,headers:ye={},body:xe}=X,Ge=Object.fromEntries(Object.entries(ye).filter(([,Ot])=>Ot!=null)),ft=new AbortController;$e.set(P,ft);try{if(X.host){await V(X,Ge,ft);return}let Ot=await fetch(f+M,{method:$,headers:Ge,body:xe?Buffer.from(xe,"base64"):void 0,signal:ft.signal}),In=Object.fromEntries(Ot.headers);for(let pt of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete In[pt];if(In["content-type"]||(In["content-type"]="application/octet-stream"),de({v:Ue,ch:"control",type:"http_head",reqId:P,status:Ot.status,headers:In}),Ot.body){for await(let pt of Ot.body)if(!de({v:Ue,ch:"control",type:"http_data",reqId:P,data:Buffer.from(pt).toString("base64")}))break}de({v:Ue,ch:"control",type:"http_end",reqId:P,error:null})}finally{$e.delete(P)}}function V(X,P,$){let{reqId:M,method:ye,path:xe,body:Ge}=X,ft=new URL(f);return new Promise(Ot=>{let In=zP.request({host:ft.hostname,port:ft.port,method:ye,path:xe,headers:{...P,host:X.host},signal:$.signal},pt=>{let{connection:rF,"keep-alive":oF,"transfer-encoding":sF,upgrade:aF,...wA}=pt.headers;de({v:Ue,ch:"control",type:"http_head",reqId:M,status:pt.statusCode||502,headers:wA}),pt.on("data",gA=>{de({v:Ue,ch:"control",type:"http_data",reqId:M,data:Buffer.from(gA).toString("base64")})||pt.destroy()}),pt.on("end",()=>{de({v:Ue,ch:"control",type:"http_end",reqId:M,error:null}),Ot()}),pt.on("error",()=>{de({v:Ue,ch:"control",type:"http_end",reqId:M,error:"view upstream error"}),Ot()})});In.on("error",pt=>{de({v:Ue,ch:"control",type:"http_end",reqId:M,error:pt?.message||"view request failed"}),Ot()}),Ge&&In.write(Buffer.from(Ge,"base64")),In.end()})}function _n(){if(N)return;q=new xm(e,{headers:{authorization:`Bearer ${t}`}}),q.on("open",async()=>{let P=null;try{P=await s?.()||null}catch{P=null}de({v:Ue,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:xn,installKind:u,providers:YP(),...P?{agents:P}:{}})}),q.on("message",P=>{let $;try{$=JSON.parse(P)}catch{return}if($.ch==="ctl"&&$.type==="hello_ok"){C=!0,T=ES,W=null,d.log(`[plane-link] connected to plane as ${n}`),$.minNodeVersion&&xn&&Qa(xn,$.minNodeVersion)&&d.error(`[plane-link] this node is bostrat-node ${xn}; the plane asks for \u2265${$.minNodeVersion} \u2014 update with: ${AS(u)}`);let M=typeof $.latestNodeVersion=="string"?$.latestNodeVersion:null;if(M&&xn&&Qa(xn,M)&&!m.has(M)){m.add(M),d.log(`[plane-link] bostrat-node ${M} is available (this node runs ${xn}) \u2014 ${AS(u)}`);try{l?.({current:xn,latest:M,min:$.minNodeVersion??null,installKind:u,required:!!($.minNodeVersion&&Qa(xn,$.minNodeVersion))})}catch(ye){d.error(`[plane-link] update notice failed: ${ye?.message||ye}`)}}J&&clearInterval(J),J=setInterval(()=>{de({v:Ue,ch:"ctl",type:"ping"}),S()},rB),J.unref?.(),fe(),ne().catch(ye=>d.error("[plane-link] backfill failed:",ye?.message||ye));return}if($.ch==="ctl"&&$.type==="usage_caps"){xc($.caps??{},{source:"plane",updatedAt:$.updatedAt??Date.now()}).then(M=>d.log(`[plane-link] spend caps ${Object.keys(M.caps).length?`updated (${Object.keys(M.caps).join(", ")})`:"cleared"} from plane`)).catch(M=>d.error("[plane-link] spend-caps push rejected:",M?.message||M));return}if($.ch==="ctl"&&$.type==="transferred"&&Array.isArray($.tracks)){Promise.resolve().then(()=>a?.($.tracks)).catch(M=>d.error("[plane-link] transfer notice failed:",M?.message||M));return}if($.ch==="events"&&$.type==="ack"&&Array.isArray($.ids)){let M=new Set;for(let ye of $.ids){let xe=_.get(ye);xe&&(_.delete(ye),(E[xe.trackId]??0)<xe.seq&&(E[xe.trackId]=xe.seq),M.add(xe.trackId))}for(let ye of M)h(ye);return}if($.ch==="control"&&$.type==="http_open"){v($).catch(M=>de({v:Ue,ch:"control",type:"http_end",reqId:$.reqId,error:M?.message||"tunnel error"}));return}if($.ch==="control"&&$.type==="http_cancel"){$e.get($.reqId)?.abort();return}if($.ch==="attach"&&$.type==="open"){let M=new xm(p+$.path,Array.isArray($.protocols)?$.protocols:[],$.host?{headers:{host:$.host}}:void 0);w.set($.streamId,M),y.set($.streamId,[]),M.on("open",()=>{let xe=y.get($.streamId)||[];y.delete($.streamId);for(let{buf:Ge,bin:ft}of xe)M.send(Ge,{binary:ft})}),M.on("message",(xe,Ge)=>{let ft=Ge?xe:Buffer.from(String(xe));de({v:Ue,ch:"attach",type:"data",streamId:$.streamId,data:ft.toString("base64"),...Ge?{bin:!0}:{}})});let ye=()=>{y.delete($.streamId),w.delete($.streamId)&&de({v:Ue,ch:"attach",type:"close",streamId:$.streamId})};M.on("close",ye),M.on("error",ye);return}if($.ch==="attach"&&$.type==="data"){let M=w.get($.streamId);if(!M)return;let ye=Buffer.from($.data,"base64"),xe=$.bin===!0,Ge=y.get($.streamId);Ge?Ge.push({buf:ye,bin:xe}):M.send(ye,{binary:xe});return}if($.ch==="attach"&&$.type==="close"){let M=w.get($.streamId);w.delete($.streamId),y.delete($.streamId),M?.close();return}});let X=()=>{C&&d.log("[plane-link] disconnected from plane"),C=!1,J&&(clearInterval(J),J=null),A&&(clearInterval(A),A=null),_.clear(),E={};for(let M of w.values())M.close();w.clear(),y.clear();for(let M of $e.values())M.abort();if($e.clear(),N||H)return;W==null&&(W=Date.now());let P=Date.now()-W<QP,$=P?eB+Math.random()*tB:T;H=setTimeout(()=>{H=null,_n()},$),H.unref?.(),P||(T=Math.min(T*2,ZP))};q.on("close",X),q.on("error",P=>{let $=/Unexpected server response: (\d+)/.exec(P?.message||"")?.[1];($==="401"||$==="403")&&Date.now()-Q>=aB&&(Q=Date.now(),d.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)`)),X()})}return nt.on("event",ve),me().then(_n),{stop:()=>{N=!0,nt.off("event",ve),H&&clearTimeout(H),ce&&clearTimeout(ce),J&&clearInterval(J),A&&clearInterval(A),de({v:Ue,ch:"ctl",type:"bye",reason:"shutdown"}),q?.close()},status:()=>({connected:C,nodeId:n,cursor:{...x},inflight:_.size})}}Yi();var El=(()=>{try{return lB(import.meta.url)("../package.json").version||null}catch{return null}})(),uB=process.env.BOSTRAT_STATE_DIR||We.join(process.env.HOME||iB.tmpdir(),".bostrat"),$S=We.join(uB,"self-update"),_l=We.join($S,"status.json"),dB=1440*6e4,fB=6e4,Sm=new Set(["npm-global","git-checkout"]);function OS({kind:e=no(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:Sm.has(e)?e==="git-checkout"||pB(t)?"auto":"prompt":"off"}function pB(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var It=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function mB({kind:e,to:t,from:n=El,statusFile:r=_l,logFile:o=We.join($S,"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||We.resolve(We.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!Sm.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 f=(y,b="")=>`write_status ${It(y)} ${b}`,d=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${It(i)}`,p=`
|
|
487
|
+
health() {
|
|
488
|
+
for i in $(seq 1 45); do
|
|
489
|
+
sleep 1
|
|
490
|
+
if curl -sf -o /dev/null -I "http://127.0.0.1:${s}/"; then return 0; fi
|
|
491
|
+
done
|
|
492
|
+
return 1
|
|
493
|
+
}`,m=`#!/usr/bin/env bash
|
|
494
|
+
# bostrat-node self-update one-shot \u2014 written by agent/src/self-update.js.
|
|
495
|
+
# ${n??"?"} \u2192 ${t} (${e}). Status: ${r}
|
|
496
|
+
set -u
|
|
497
|
+
FROM=${It(n??"")}
|
|
498
|
+
TO=${It(t)}
|
|
499
|
+
STATUS_FILE=${It(r)}
|
|
500
|
+
STARTED_AT=${Number(u)}
|
|
501
|
+
write_status() { # <status> [error]
|
|
502
|
+
local err="\${2:-}"
|
|
503
|
+
err="\${err//\\\\/\\\\\\\\}"; err="\${err//\\"/\\\\\\"}"
|
|
504
|
+
printf '{"status":"%s","from":"%s","to":"%s","kind":"%s","startedAt":%s,"updatedAt":%s%s}\\n' \\
|
|
505
|
+
"$1" "$FROM" "$TO" ${It(e)} "$STARTED_AT" "$(date +%s000)" \\
|
|
506
|
+
"\${err:+,\\"error\\":\\"$err\\"}" > "$STATUS_FILE.tmp" && mv "$STATUS_FILE.tmp" "$STATUS_FILE"
|
|
507
|
+
}
|
|
508
|
+
${p}
|
|
509
|
+
`;if(e==="npm-global"){let y=a?We.join(a,"bin","npm"):"npm",b=x=>`${It(y)} install -g "bostrat-node@${x}" --no-audit --no-fund`;return m+`
|
|
510
|
+
${f("updating",'"" ')}
|
|
511
|
+
if ! ${b("$TO")}; then
|
|
512
|
+
${f("failed",'"npm install failed"')}
|
|
513
|
+
exit 1
|
|
514
|
+
fi
|
|
515
|
+
${f("restarting")}
|
|
516
|
+
if ! ${d}; then
|
|
517
|
+
${f("failed",'"service restart failed"')}
|
|
518
|
+
exit 1
|
|
519
|
+
fi
|
|
520
|
+
if health; then
|
|
521
|
+
# the restarted agent settles "done" once it hellos on the new version
|
|
522
|
+
exit 0
|
|
523
|
+
fi
|
|
524
|
+
# \u2500\u2500 rollback: the same primitive with the previous version \u2500\u2500
|
|
525
|
+
${f("rolling-back",'"agent did not answer on :'+s+' after the update"')}
|
|
526
|
+
if [ -n "$FROM" ] && ${b("$FROM")} && ${d} && health; then
|
|
527
|
+
${f("rolled-back",'"agent did not answer on :'+s+' after the update; rolled back"')}
|
|
528
|
+
exit 1
|
|
529
|
+
fi
|
|
530
|
+
${f("failed",'"agent did not answer after the update AND the rollback"')}
|
|
531
|
+
exit 1
|
|
532
|
+
`}let w=y=>`( cd ${It(l)} && git fetch -q origin main --tags && git show origin/main:scripts/deploy-pi.sh > "$TMP_SCRIPT" && bash "$TMP_SCRIPT" "v${y}" )`;return m+`
|
|
533
|
+
LIVE=${It(l)}
|
|
534
|
+
TMP_SCRIPT="$(mktemp)"
|
|
535
|
+
trap 'rm -f "$TMP_SCRIPT"' EXIT
|
|
536
|
+
# "restarting" is written BEFORE deploy-pi.sh runs (it does fetch \u2192 build \u2192
|
|
537
|
+
# restart \u2192 health in one call): the agent it restarts settles "done" on boot,
|
|
538
|
+
# and nothing here may write after that boot or the settle would repeat.
|
|
539
|
+
${f("restarting")}
|
|
540
|
+
if ${w("$TO")}; then
|
|
541
|
+
exit 0
|
|
542
|
+
fi
|
|
543
|
+
${f("rolling-back",'"deploy-pi.sh v$TO failed"')}
|
|
544
|
+
if [ -n "$FROM" ] && ${w("$FROM")}; then
|
|
545
|
+
${f("rolled-back",'"deploy-pi.sh v$TO failed; rolled back to v$FROM"')}
|
|
546
|
+
exit 1
|
|
547
|
+
fi
|
|
548
|
+
${f("failed",'"deploy-pi.sh v$TO failed AND the rollback failed"')}
|
|
549
|
+
exit 1
|
|
550
|
+
`}async function ei(e=_l){try{return JSON.parse(await lr.readFile(e,"utf8"))}catch{return null}}var hB=new Set(["updating","restarting","rolling-back"]),Il=e=>!!(e&&hB.has(e.status));async function vm({to:e,from:t=El,kind:n=no(),reason:r="manual",statusFile:o=_l,spawnImpl:s=cB,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!Sm.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!Qa(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await ei(o);if(Il(l))return{ok:!1,reason:`an update to ${l.to} is already in flight`,status:l};if(l&&l.to===e&&(l.status==="failed"||l.status==="rolled-back"))return{ok:!1,reason:`update to ${e} already ${l.status} (${l.error||"no detail"}) \u2014 retried when a newer release exists`,status:l};await lr.mkdir(We.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?Zu(await wB())||We.resolve(We.dirname(process.execPath),".."):null,f=mB({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),d=We.join(We.dirname(o),`update-${e}.sh`),p=We.join(We.dirname(o),"last.log");await lr.writeFile(d,f,{mode:448}),await lr.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let m=RS.existsSync("/run/systemd/system")&&await gB("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",d]]:["setsid",["-f","bash","-c",`exec bash ${It(d)} >> ${It(p)} 2>&1`]];try{s(m[0],m[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(w){return await lr.writeFile(o,JSON.stringify({status:"failed",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a(),error:`could not spawn updater: ${w?.message||w}`})),{ok:!1,reason:`could not spawn updater: ${w?.message||w}`}}return c.log?.(`[self-update] ${t??"?"} \u2192 ${e} (${n}, ${r}) started \u2014 status ${o}`),{ok:!0,to:e,from:t,kind:n,scriptFile:d,statusFile:o}}async function wB(){try{return await lr.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function gB(e){for(let t of String(process.env.PATH||"").split(We.delimiter))try{return await lr.access(We.join(t,e),RS.constants.X_OK),!0}catch{}return!1}var yB=45*6e4;async function CS({statusFile:e=_l,current:t=El,now:n=Date.now,staleMs:r=yB}={}){let o=await ei(e);if(!o)return null;let s=a=>lr.writeFile(e,JSON.stringify(a)).catch(()=>{});if(Il(o)){if(t&&o.to===t){let a={...o,status:"done",updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}if(n()-Number(o.updatedAt||o.startedAt||0)>r){let a={...o,status:"failed",error:`updater went silent while ${o.status} (no status change for ${Math.round(r/6e4)} min)`,updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}return null}if(["failed","rolled-back","done"].includes(o.status)&&!o.announced){let a={...o,announced:!0};return await s(a),a}return null}function MS({isBusy:e,start:t=vm,onStuck:n=null,onStarted:r=null,tickMs:o=fB,stuckMs:s=dB,now:a=Date.now,setIntervalImpl:i=setInterval,clearIntervalImpl:c=clearInterval,log:l=console}={}){let u=null,f=()=>{u?.timer&&c(u.timer),u=null},d=async()=>{if(!u)return;let p=!0;try{p=await e()!==!1}catch{p=!0}if(p){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 m=u;f();let w=await t({to:m.to,from:m.from,kind:m.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${w.ok?"started":`not started: ${w.reason}`}`);try{await r?.({...m,result:w})}catch{}};return{arm({to:p,from:m=El,kind:w=no()}){return u?.to===p?!1:(f(),u={to:p,from:m,kind:w,since:a(),stuckFired:!1,timer:i(d,o)},u.timer.unref?.(),!0)},tick:d,cancel:f,get armed(){return u?{to:u.to,from:u.from,kind:u.kind,since:u.since}:null}}}ue();Ve();var bB=(...e)=>fetch(...e);function kB(e=process.env){return!!(be(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Tm(e=process.env){if(!kB(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 bB(`${be(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 ws(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await Tm(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 Rl(e,t,{source:n="chat"}={}){return R(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}ue();Ve();var xB=(...e)=>fetch(...e);async function $l(e,t,n,r=process.env){if(!(be(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 xB(`${be(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 SB(e,t=process.env){return $l("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function NS({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await SB(t,o):await ws(e,o),a=await $l("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function Am(e,t=process.env){let n=await ws(e,t),r=await $l("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function Em(e,t=process.env){return(await $l("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function PS(e,t,n,r=process.env){let{blueprint:o,items:s}=await Am(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 R(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}ue();Ve();var vB=(...e)=>fetch(...e);async function TB(e,t,n,r=process.env){if(!(be(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 vB(`${be(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)}}async function BS(e,{title:t,context:n="",decision:r,alternatives:o=[],supersedesId:s=null}){if(!String(t??"").trim())throw new Error("a title is required");if(!String(r??"").trim())throw new Error("the decision itself is required (what was decided)");return await R(e,{type:"decision.recorded",actor:"orchestrator",source:"orchestrator",payload:{title:String(t).trim(),context:String(n??"").trim(),decision:String(r).trim(),alternatives:(Array.isArray(o)?o:[]).map(a=>typeof a=="string"?{label:a}:a).filter(a=>a?.label),...s?{supersedesId:s}:{}}}),{title:String(t).trim()}}function DS(e,t=process.env){return TB("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}bp();ue();import ai from"node:fs/promises";import Ll from"node:path";import mD from"node:os";Cn();import AB from"node:fs/promises";import jS from"node:path";import EB from"node:os";var LS=()=>jS.join(process.env.BOSTRAT_STATE_DIR||jS.join(process.env.HOME||EB.tmpdir(),".bostrat"),"integrations.json");async function vn(){try{return JSON.parse(await AB.readFile(LS(),"utf8"))||{}}catch{return{}}}var Ol=["linked","prOpened","prMerged","create"],_m=["prompt","auto","off"],_B={create:["prompt","off"]},FS=e=>_B[e]??_m;function Cl(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of Ol){let s=FS(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 ro(){let e=await vn(),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:Cl(e),defaultTeam:n}:null}async function Ml(){let e=await vn(),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:Cl(e)}}async function Im(){return Cl(await vn())}async function US(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${Ol.join(", ")}`);for(let[r,o]of t){if(!Ol.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Ol.join(", ")}`);let s=FS(r);if(!s.includes(o))throw new Error(s.length<_m.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${_m.join(", ")}`)}let n=await vn();return n.ticketFlow={...Cl(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await et(LS(),n,{mode:384}),n.ticketFlow}async function ti(){let e=(await vn()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function IB(){let e=(await vn()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var RB={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Nl(){let t=(await vn()).voiceChat||{},n=t.provider||"openai",r=RB[n];if(!r)return null;let o=n==="openai"?await IB():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function HS(){let e=(await vn()).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 oo(){let e=(await vn()).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}}ue();Nn();Mr();os();import ri from"node:fs/promises";import YS from"node:path";import eD from"node:os";import{randomUUID as ZS}from"node:crypto";var $m={};cu($m,{createComment:()=>jB,findIssueUrl:()=>MB,issueStates:()=>UB,mdToAdf:()=>KS,moveIssue:()=>HB,parseIssueRef:()=>CB,resolveIssue:()=>NB,uploadImage:()=>LB,upsertCard:()=>PB,whoami:()=>Rm});var gs=e=>String(e.baseUrl||"").replace(/\/$/,""),qS=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function so(e,t,n,r){let o=await fetch(`${gs(e)}${n}`,{method:t,headers:{authorization:qS(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 $B=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,OB=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function CB(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match($B);return r?r[1].toUpperCase():null}let n=t.match(OB);return n?n[1]:null}function MB(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 NB(e,t){let n;try{n=await so(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:`${gs(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function PB(e,t,{url:n,title:r,subtitle:o}){await so(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 BB=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function DB(e){let t=[],n=0;for(let r of e.matchAll(BB)){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 KS(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
551
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...DB(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 jB(e,t,n){let r=await so(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/comment`,{body:KS(n)});return r?.id?`${gs(e)}/browse/${t.key}?focusedCommentId=${r.id}`:null}async function Rm(e){let t=await so(e,"GET","/rest/api/3/myself"),n=null;try{n=new URL(gs(e)).host}catch{n=e.baseUrl??null}return{user:t?.displayName??t?.emailAddress??null,org:n}}async function LB(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(`${gs(e)}/rest/api/3/issue/${encodeURIComponent(t)}/attachments`,{method:"POST",headers:{authorization:qS(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??`${gs(e)}/rest/api/3/attachment/content/${c.id}`}var FB={new:"unstarted",indeterminate:"started",done:"completed"},WS=e=>FB[e?.key]??"started";async function UB(e,t){let n=encodeURIComponent(t.key),[r,o]=await Promise.all([so(e,"GET",`/rest/api/3/issue/${n}?fields=status`),so(e,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:WS(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:WS(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function HB(e,t,n){await so(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/transitions`,{transition:{id:String(n)}})}var Nm={};cu(Nm,{createComment:()=>XB,createIssue:()=>Mm,createProject:()=>Cm,findIssueUrl:()=>VB,issueStates:()=>ZB,moveIssue:()=>QB,parseIssueRef:()=>GB,resolveIssue:()=>zB,teams:()=>ys,uploadImage:()=>YB,upsertCard:()=>JB,whoami:()=>Om});var WB=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function zt(e,t,n={}){let r=await fetch(WB(),{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 qB=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,KB=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function GB(e){let t=String(e||"").trim(),n=t.match(qB);if(n)return n[1].toUpperCase();let r=t.match(KB);return r?r[1]:null}function VB(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 zB(e,t){let r=(await zt(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 JB(e,t,{url:n,title:r,subtitle:o}){let s=await zt(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 XB(e,t,n){let r=await zt(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 Om(e){let t=await zt(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 YB(e,{filename:t,contentType:n,buffer:r}){let o=await zt(e,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
488
552
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
489
553
|
success
|
|
490
554
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
491
555
|
}
|
|
492
|
-
}`,{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 rB(e,t){let n=await Gt(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 oB(e,t,n){if(!(await Gt(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 ds(e){return((await Gt(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var bv=255;async function pm(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>bv,a=s?`${o.slice(0,bv-1).trimEnd()}\u2026`:o,i=await Gt(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 mm(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await Gt(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}}var Ev={linear:{api:hm,config:Jr},jira:{api:dm,config:gl}};async function vm(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(Ev)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function Zr(e){let t=Ev[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function Sm(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 _v=()=>process.env.BOSTRAT_STATE_DIR||Tv.join(process.env.HOME||sB.tmpdir(),".bostrat"),bm=()=>Tv.join(_v(),"tickets.json"),Iv=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),bl=e=>`${Iv()}/track/${e}`,or=null,wm=null,gm=Promise.resolve();async function Ja(){return or||(wm??=Va.readFile(bm(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(or=e,!t&&Te()){let{records:n,source:r}=await Be("tickets").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.id)continue;let{id:s,updatedAt:a,...i}=o.data;or[s]=i}Object.keys(or).length&&await km().catch(()=>{})}}return or}).finally(()=>wm=null),wm)}async function km(){let e=JSON.stringify({tickets:or},null,2),t=`${bm()}.${process.pid}.tmp`;await Va.mkdir(_v(),{recursive:!0}),await Va.writeFile(t,e),await Va.rename(t,bm())}async function sr(e=null){if(gm=gm.then(km,km),await gm.catch(()=>{}),!e)return;let t=or?.[e];t?Ve("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):kt("tickets",e).catch(()=>{})}async function Vt(e){return(await Ja())[e]??null}async function Rv(){return{...await Ja()}}async function $v(){return(await vm()).length>0}async function fs(e,t,{source:n="api"}={}){let r=await vm();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 f=u.api.parseIssueRef(t);if(f)try{s=await u.api.resolveIssue(u.cfg,f),o=u;break}catch(d){a=d}}if(!o)throw a||new Error(`"${t}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(e.boxes||[]).find(u=>u.name===e.leadBox),c={provider:o.name,key:s.key,issueId:s.issueId,url:s.url,title:s.title,state:s.state,source:n,linkedAt:Date.now(),track:{name:e.name,branch:e.workspace?.branch??null,leadAgent:i?.agent??null,cwd:e.cwd??null,...e.workspace?.kind==="multi"?{repos:(e.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await Ja())[e.id]=c,await sr(e.id),await I({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 za(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 Ga(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await Mv(e.id,c,"linked"),c}async function Tm(e){let t=await Ja();return t[e]?(delete t[e],await sr(e),!0):!1}var xm=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function Ov(e,t){if(!e.track?.cwd)return null;let n=Un(await B({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function aB(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${xm(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${xm(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function iB(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(xm(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function za(e,t){let n=await Zr(t);if(!n)return;let r=await Ov(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:bl(e),title:`Bostrat Track: ${t.track.name}`,subtitle:iB(t,r)})}function kl(e){let t=new URL(Iv());return`${t.protocol}//${e}.${t.host}/`}var cB={linked:"linked",opened:"prOpened",merged:"prMerged"};function lB(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 Cv(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await sr(e),await I({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 Mv(e,t,n){let r=await Zr(t);if(!r)return;let o=r.cfg.ticketFlow?.[cB[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=Sm(s,a),c=lB(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 Cv(e,t,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await I({id:e,cwd:t.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:Av(),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 Nv(e){let t=await Vt(e);if(!t)throw new Error("no ticket linked to this Track");let n=await Zr(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=Sm(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await sr(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 Am(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await Vt(e);if(!r)throw new Error("no ticket linked to this Track");let o=await Zr(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Sm(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 sr(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await Cv(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function Pv(e,t){let n=await Vt(e);if(!n)throw new Error("no ticket linked to this Track");let r=await Zr(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 I({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 Bv(e,t,n){if(!t)throw new Error("offerId required");let r=await B({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 Am(e.id,o.payload.to?.id,{mode:"offer"})),await I({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 kv=50;async function Dv(){let e=await Jr();if(!e)throw new Error("no ticketing integration configured");return ds(e)}function uB(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>kv)throw new Error(`too many issues (${n.length}); cap is ${kv} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function dB(e,t,n,r){let o=r??await ds(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 Vt(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 jv(e,t){let n=await Jr();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=uB(t),o=await ds(n),s=await dB(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=Av();return await I({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 Lv(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await B({id:e.id,cwd:e.cwd}),s=o.find(h=>h.type==="ticket.create.offered"&&h.payload?.proposalId===t);if(!s)throw new Error("unknown proposal");if(o.some(h=>h.type==="ticket.create.answered"&&h.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await I({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 Jr();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let h=(await ds(a)).find(w=>w.id===r);if(!h)throw new Error("unknown team");c={id:h.id,key:h.key,name:h.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],f=null;if(i.epic){let h=await pm(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});f=h.id,l.push({kind:"epic",title:h.name,url:h.url})}for(let h of i.issues||[])try{let w=await mm(a,{teamId:c.id,title:h.title,description:h.description,projectId:f,priority:h.priority});l.push({kind:"issue",key:w.key,title:w.title,url:w.url})}catch(w){u.push({title:h.title,message:w?.message||String(w)})}await I({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 I({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 d=l.filter(h=>h.kind==="issue"),m=null;return i.linkCurrentTrack&&d.length===1&&!await Vt(e.id)&&(m=await fs(e,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:m?m.key:null}}async function Ga(e,t){let n=await Zr(e);n&&await n.api.createComment(n.cfg,e,t)}async function fB(e,t,n){let r=await Zr(e);if(!r)return null;let o=await dn(t);if(!o)return null;let s=Et(o,n);if(!s)return null;let a=await Va.readFile(wt(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 xv=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),ym=new Map;function pB(e,t){if(xv()<=0)return za(e,t);if(ym.has(e))return;let n=setTimeout(()=>{ym.delete(e),Vt(e).then(r=>r?za(e,r):null).catch(()=>{})},xv());n.unref?.(),ym.set(e,n)}var vv=new Map;async function Sv(e,t,n){let r=vv.get(e)??0;Date.now()-r<600*1e3||(vv.set(e,Date.now()),await I({id:e,cwd:t},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function mB(e,{resolveTrack:t=null}={}){let n=await Vt(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await vm())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 fs(s,o,{source:"chat"})}catch(a){await Sv(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 sr(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await sr(e.trackId),await za(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 sr(e.trackId),await za(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await Ov(n,e.trackId).catch(()=>null):null;await Ga(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
556
|
+
}`,{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 ZB(e,t){let n=await zt(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 QB(e,t,n){if(!(await zt(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 ys(e){return((await zt(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var GS=255;async function Cm(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>GS,a=s?`${o.slice(0,GS-1).trimEnd()}\u2026`:o,i=await zt(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 Mm(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await zt(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}}var QS={linear:{api:Nm,config:ro},jira:{api:$m,config:Ml}};async function Um(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(QS)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function ao(e){let t=QS[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function Hm(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 ev=()=>process.env.BOSTRAT_STATE_DIR||YS.join(process.env.HOME||eD.tmpdir(),".bostrat"),jm=()=>YS.join(ev(),"tickets.json"),tv=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Pl=e=>`${tv()}/track/${e}`,ur=null,Pm=null,Bm=Promise.resolve();async function si(){return ur||(Pm??=ri.readFile(jm(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(ur=e,!t&&Te()){let{records:n,source:r}=await Be("tickets").catch(()=>({records:[],source:"error"}));if(r==="plane"){for(let o of n){if(o.deletedAt||!o.data?.id)continue;let{id:s,updatedAt:a,...i}=o.data;ur[s]=i}Object.keys(ur).length&&await Lm().catch(()=>{})}}return ur}).finally(()=>Pm=null),Pm)}async function Lm(){let e=JSON.stringify({tickets:ur},null,2),t=`${jm()}.${process.pid}.tmp`;await ri.mkdir(ev(),{recursive:!0}),await ri.writeFile(t,e),await ri.rename(t,jm())}async function dr(e=null){if(Bm=Bm.then(Lm,Lm),await Bm.catch(()=>{}),!e)return;let t=ur?.[e];t?ze("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):xt("tickets",e).catch(()=>{})}async function Jt(e){return(await si())[e]??null}async function nv(){return{...await si()}}async function rv(){return(await Um()).length>0}async function bs(e,t,{source:n="api"}={}){let r=await Um();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 f=u.api.parseIssueRef(t);if(f)try{s=await u.api.resolveIssue(u.cfg,f),o=u;break}catch(d){a=d}}if(!o)throw a||new Error(`"${t}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(e.boxes||[]).find(u=>u.name===e.leadBox),c={provider:o.name,key:s.key,issueId:s.issueId,url:s.url,title:s.title,state:s.state,source:n,linkedAt:Date.now(),track:{name:e.name,branch:e.workspace?.branch??null,leadAgent:i?.agent??null,cwd:e.cwd??null,...e.workspace?.kind==="multi"?{repos:(e.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await si())[e.id]=c,await dr(e.id),await R({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 oi(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 ni(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await av(e.id,c,"linked"),c}async function Wm(e){let t=await si();return t[e]?(delete t[e],await dr(e),!0):!1}var Fm=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function ov(e,t){if(!e.track?.cwd)return null;let n=Kn(await B({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function tD(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Fm(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${Fm(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function nD(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(Fm(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function oi(e,t){let n=await ao(t);if(!n)return;let r=await ov(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:Pl(e),title:`Bostrat Track: ${t.track.name}`,subtitle:nD(t,r)})}function Bl(e){let t=new URL(tv());return`${t.protocol}//${e}.${t.host}/`}var rD={linked:"linked",opened:"prOpened",merged:"prMerged"};function oD(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 sv(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await dr(e),await R({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 av(e,t,n){let r=await ao(t);if(!r)return;let o=r.cfg.ticketFlow?.[rD[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=Hm(s,a),c=oD(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 sv(e,t,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await R({id:e,cwd:t.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:ZS(),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 iv(e){let t=await Jt(e);if(!t)throw new Error("no ticket linked to this Track");let n=await ao(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=Hm(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await dr(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 qm(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await Jt(e);if(!r)throw new Error("no ticket linked to this Track");let o=await ao(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Hm(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 dr(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await sv(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function cv(e,t){let n=await Jt(e);if(!n)throw new Error("no ticket linked to this Track");let r=await ao(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 R({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 lv(e,t,n){if(!t)throw new Error("offerId required");let r=await B({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 qm(e.id,o.payload.to?.id,{mode:"offer"})),await R({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 VS=50;async function uv(){let e=await ro();if(!e)throw new Error("no ticketing integration configured");return ys(e)}function sD(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>VS)throw new Error(`too many issues (${n.length}); cap is ${VS} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function aD(e,t,n,r){let o=r??await ys(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 Jt(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 dv(e,t){let n=await ro();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=sD(t),o=await ys(n),s=await aD(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=ZS();return await R({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 fv(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await B({id:e.id,cwd:e.cwd}),s=o.find(m=>m.type==="ticket.create.offered"&&m.payload?.proposalId===t);if(!s)throw new Error("unknown proposal");if(o.some(m=>m.type==="ticket.create.answered"&&m.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await R({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 ro();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await ys(a)).find(w=>w.id===r);if(!m)throw new Error("unknown team");c={id:m.id,key:m.key,name:m.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],f=null;if(i.epic){let m=await Cm(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});f=m.id,l.push({kind:"epic",title:m.name,url:m.url})}for(let m of i.issues||[])try{let w=await Mm(a,{teamId:c.id,title:m.title,description:m.description,projectId:f,priority:m.priority});l.push({kind:"issue",key:w.key,title:w.title,url:w.url})}catch(w){u.push({title:m.title,message:w?.message||String(w)})}await R({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 R({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 d=l.filter(m=>m.kind==="issue"),p=null;return i.linkCurrentTrack&&d.length===1&&!await Jt(e.id)&&(p=await bs(e,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:p?p.key:null}}async function ni(e,t){let n=await ao(e);n&&await n.api.createComment(n.cfg,e,t)}async function iD(e,t,n){let r=await ao(e);if(!r)return null;let o=await pn(t);if(!o)return null;let s=_t(o,n);if(!s)return null;let a=await ri.readFile(gt(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 zS=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),Dm=new Map;function cD(e,t){if(zS()<=0)return oi(e,t);if(Dm.has(e))return;let n=setTimeout(()=>{Dm.delete(e),Jt(e).then(r=>r?oi(e,r):null).catch(()=>{})},zS());n.unref?.(),Dm.set(e,n)}var JS=new Map;async function XS(e,t,n){let r=JS.get(e)??0;Date.now()-r<600*1e3||(JS.set(e,Date.now()),await R({id:e,cwd:t},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function lD(e,{resolveTrack:t=null}={}){let n=await Jt(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await Um())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 bs(s,o,{source:"chat"})}catch(a){await XS(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 dr(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await dr(e.trackId),await oi(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 dr(e.trackId),await oi(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await ov(n,e.trackId).catch(()=>null):null;await ni(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
493
557
|
|
|
494
|
-
Agent cost for this Track: ${
|
|
558
|
+
Agent cost for this Track: ${tD(o)}.`:"")),await av(e.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await iD(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await ni(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
495
559
|
|
|
496
|
-
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await
|
|
497
|
-
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await
|
|
498
|
-
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${
|
|
560
|
+
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await ni(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 ni(n,`**Bostrat** \u2014 live preview **${r.name}**: ${Bl(r.slug)}
|
|
561
|
+
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await cD(e.trackId,n);break;default:break}}catch(o){let s=t?await t(e.trackId).catch(()=>null):null;await XS(e.trackId,s?.cwd??null,o)}}function pv({resolveTrack:e}){nt.on("event",t=>{lD(t,{resolveTrack:e}).catch(()=>{})})}async function mv(){if(!Te())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Be("tickets");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await si(),r=0;for(let[o,s]of Object.entries(n))await ze("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}qo();os();import uD from"ws";var hv=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function Dl(e,t,n={}){let r=await fetch(`${hv()}/${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 Km=(e,t)=>Dl("chat.postMessage",e,t),io=(e,t)=>Dl("chat.update",e,t);async function Ze(e,t){await fetch(e,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(t)}).catch(()=>{})}async function dD(e,t,n={}){let r=await fetch(`${hv()}/${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 wv(e,{filename:t,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await dD("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 Dl("files.completeUploadExternal",e,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var fD=2200;function gv({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 Dl("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new uD(l),o.on("message",u=>{let f;try{f=JSON.parse(u)}catch{return}if(f.type==="hello"){a=1e3,n("socket connected");return}if(f.type==="disconnect"){try{o.close()}catch{}return}if(!f.envelope_id)return;let d=w=>{try{o.send(JSON.stringify(w?{envelope_id:f.envelope_id,payload:w}:{envelope_id:f.envelope_id}))}catch{}},p=!1,m=setTimeout(()=>{p=!0,d()},fD);Promise.resolve().then(()=>t(f)).then(w=>{clearTimeout(m),!p&&(p=!0,d(f.accepts_response_payload?w:void 0))},w=>{clearTimeout(m),p||(p=!0,d()),n(`envelope handler failed: ${w?.message||w}`)})}),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 kv=()=>process.env.BOSTRAT_STATE_DIR||Ll.join(process.env.HOME||mD.tmpdir(),".bostrat"),Xm=()=>Ll.join(kv(),"slack.json"),jl=null,Gm=null,Vm=Promise.resolve();async function Zm(){return jl||(Gm??=ai.readFile(Xm(),"utf8").then(e=>JSON.parse(e)?.threads??{}).catch(()=>({})).then(e=>jl=e).finally(()=>Gm=null),Gm)}async function yv(){let e=JSON.stringify({threads:jl},null,2),t=`${Xm()}.${process.pid}.tmp`;await ai.mkdir(kv(),{recursive:!0}),await ai.writeFile(t,e),await ai.rename(t,Xm())}async function Rt(){Vm=Vm.then(yv,yv),await Vm.catch(()=>{})}async function Qm(e){return(await Zm())[e]??null}async function hD(e,t){let n=await Zm();for(let[r,o]of Object.entries(n))if(o.channel===e&&o.ts===t)return{trackId:r,rec:o};return null}var re=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");function ii(e,t){let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function wD(e,t=500){let n=String(e??"").trim().split(/\n\s*\n/)[0]??"";return ii(n.replace(/\n+/g," "),t)}function xv(e){let t=`*${re(e.name)}*${e.archivedAt?" \xB7 _archived_":""}`,n=`${re(e.repoLabel)} \xB7 \`${re(e.branch||"?")}\` \xB7 lead *${re(e.lead)}*`,r=e.kickoff?`> ${re(ii(e.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${t}
|
|
562
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${Pl(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Ym(e,t){await io(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:xv(t)})}async function pr(e,t,n,r=void 0){return Km(e.botToken,{channel:t.channel,thread_ts:t.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var bv=new Set;function fr(e){bv.add(e),e.catch(()=>{}).finally(()=>bv.delete(e))}var Se=e=>({response_type:"ephemeral",text:e}),gD="*/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._",yD=["claude","cursor","codex"];function zm(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 Jm=(e,t)=>Se(`No live Track matches \u201C${re(t)}\u201D. Live: ${e.map(n=>`\`${re(n.name)}\``).join(" \xB7 ")||"none"}`);async function Sv(e,t,{name:n,repo:r,responseUrl:o}){let s=e.deps;try{let a=(await s.listTracks()).filter(f=>!f.archivedAt);if(a.length>=t.maxConcurrent){await Ze(o,{...Se(`At capacity \u2014 ${a.length} live Tracks (limit ${t.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!t.channel){await Ze(o,{...Se("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?Ll.basename(c.base):"fresh workspace"),lead:"claude"},u=await Km(t.botToken,{channel:t.channel,text:`Track started: ${l.name}`,blocks:xv(l),unfurl_links:!1});l.ts=u.ts,(await Zm())[i.id]=l,await Rt(),await Ze(o,{...Se(`\u2713 *${re(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await Ze(o,{...Se(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function bD(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.archiveTrack(n.id);let s=await Qm(n.id);s&&(s.archivedAt=Date.now(),await Rt(),await Ym(t,s).catch(()=>{})),await Ze(r,{...Se(`\u2713 *${re(n.name)}* archived.`),replace_original:!0})}catch(s){await Ze(r,{...Se(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function kD(e,t,{track:n,agent:r,responseUrl:o}){try{await e.deps.startLeadHandoff(n.id,r),await Ze(o,{...Se(`\u2713 Handing *${re(n.name)}* to ${re(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await Ze(o,{...Se(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function xD(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 Ze(r,{...Se(`\u2713 Stopped *${re(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 Ze(r,{...Se(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function SD(e,t){let n=[...t.slice(0,99).map(r=>({text:{type:"plain_text",text:ii(`${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 *${re(e)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function vD(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?Ll.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 *${re(s.name)}* \u2014 ${re(i)}${a.branch?` \`${re(a.branch)}\``:""} \xB7 lead ${re(c)} \xB7 ${l}`}),o=t.length-n.length;return r.join(`
|
|
499
563
|
`)+(o?`
|
|
500
|
-
_${o} archived_`:"")}async function
|
|
501
|
-
\u2713 *${
|
|
502
|
-
\u2713 _answered_`}}]})}async function
|
|
503
|
-
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return
|
|
504
|
-
`)){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;i&&t.set(i,{messageId:i,tokens:a,model:o.message?.model||o.model||null})}return[...t.values()]}function JB(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 YB=new Set(["Write","Edit","NotebookEdit"]),XB=220,ZB=160;function QB(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(YB.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,XB)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=yr(o.command.trim().replace(/\s+/g," ")).slice(0,ZB));return r}function eD(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?Zt(t.cwd,t.sessionId):t?.cwd?Tu(t.cwd):null}async function Xv(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 I(r,{...s,...c}))};switch(e?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:e.session_id}});break;case"UserPromptSubmit":{let c=String(e.prompt??e.user_prompt??""),l=Cc(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:QB(e,o)});break;case"Stop":await tD(r,o,e,i);break;default:break}return a}async function tD(e,t,n,r){let o=[],s=eD(n,t);if(s)try{o=zB(await VB.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=JB(await B(e)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let f of c){let d=f.model||"unknown",m=u.get(d)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},messageIds:[]};for(let h of Object.keys(m.tokens))m.tokens[h]+=f.tokens[h];m.messageIds.push(f.messageId),u.set(d,m)}for(let[f,d]of u)await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:f,usd:Fn(f,d.tokens),tokens:d.tokens,messageId:d.messageIds[d.messageIds.length-1],messageIds:d.messageIds,apiCalls:d.messageIds.length}})}let l=a.tokens.input+a.tokens.cacheWrite+a.tokens.cacheRead;await r({type:"context.usage",actor:"system",source:"transcript",payload:{model:a.model,occupancy:l,tokens:a.tokens}})}As();le();fc();import nS from"node:fs";import Bm from"node:fs/promises";import oD from"node:http";import xn from"node:path";import Al from"node:os";import{createRequire as sD}from"node:module";import Dm from"ws";import nD from"node:path";import rD from"node:os";var Za=process.env.BOSTRAT_STATE_DIR||nD.join(process.env.HOME||rD.tmpdir(),".bostrat");function Pm(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 Zv(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=Pm(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function Qv(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}var kn=(()=>{try{return sD(import.meta.url)("../package.json").version||null}catch{return null}})(),aD=process.env.BOSTRAT_STATE_DIR||xn.join(process.env.HOME||Al.tmpdir(),".bostrat");function iD({env:e=process.env,pkgDir:t=xn.resolve(xn.dirname(new URL(import.meta.url).pathname),".."),exists:n=nS.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(xn.join(t,"..",".git"))?"git-checkout":"dev"}function jm(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 eS(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 cD({home:e=process.env.HOME||Al.homedir(),env:t=process.env}={}){let n=r=>{try{return nS.statSync(r),!0}catch{return!1}};return{claude:n(xn.join(e,".claude",".credentials.json")),codex:n(xn.join(t.CODEX_HOME||xn.join(e,".codex"),"auth.json"))}}var Ue=1,tS=1e3,lD=3e4,uD=6e4,dD=1500,fD=500,Tl=100,pD=2e3,mD=25e3,hD=6e4,wD=6e4,gD=6e4;function rS({url:e,token:t,nodeId:n=Al.hostname(),name:r=Al.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,metricsMs:c=wD,onUpdateAvailable:l=null,installKind:u=iD(),selfUrl:f=`http://127.0.0.1:${process.env.PORT||3362}`,log:d=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=Qv(e);let m=f.replace(/^http/,"ws"),h=new Set,w=new Map,y=new Map,b=xn.join(aD,"plane-link","cursor.json"),x={},E={},_=new Map,q=null,C=!1,N=!1,T=tS,W=null,H=null,ae=null,J=null,A=null,j=!1,Q=0;async function fe(){try{x=JSON.parse(await Bm.readFile(b,"utf8"))}catch{x={}}}function Re(){j=!0,!ae&&(ae=setTimeout(async()=>{if(ae=null,!!j){j=!1;try{await Bm.mkdir(xn.dirname(b),{recursive:!0}),await Bm.writeFile(b,JSON.stringify(x))}catch(Y){d.error("[plane-link] cursor flush failed:",Y?.message||Y)}}},pD),ae.unref?.())}function ue(Y){return!q||q.readyState!==Dm.OPEN?!1:(q.send(JSON.stringify(Y)),!0)}function ie(Y){let P=Date.now();for(let{trackId:R,event:M}of Y){let ye=_.get(M.id);_.set(M.id,{trackId:R,seq:M.seq,event:M,sentAt:P,tries:(ye?.tries??0)+1})}return ue({v:Ue,ch:"events",type:"append",events:Y})}function g(Y){let P=null;for(let R of _.values())R.trackId===Y&&(P==null||R.seq<P)&&(P=R.seq);return P}function p(Y){let P=E[Y];if(P==null)return;let R=g(Y),M=R==null?P:Math.min(P,R-1);M>(x[Y]??0)&&(x[Y]=M,Re())}function S(){if(!C)return;let Y=Date.now(),P=[..._.values()].filter(R=>R.event&&Y-R.sentAt>=hD);if(P.length){P.sort((R,M)=>R.trackId===M.trackId?R.seq-M.seq:R.trackId<M.trackId?-1:1),d.log(`[plane-link] re-sending ${P.length} unacked event(s) the plane never confirmed`);for(let R=0;R<P.length;R+=Tl){let M=P.slice(R,R+Tl).map(ye=>({trackId:ye.trackId,event:ye.event}));if(!ie(M))return}}}async function $(){if(!i)return;let Y;try{Y=await i()}catch(P){d.error("[plane-link] metrics sample failed:",P?.message||P);return}Y&&ue({v:Ue,ch:"ctl",type:"metrics",metrics:Y})}function we(){i&&(A&&clearInterval(A),$(),A=setInterval($,c),A.unref?.())}async function ge(){let Y;try{Y=await o()}catch(P){d.error("[plane-link] backfill listTracks failed:",P?.message||P);return}ue({v:Ue,ch:"ctl",type:"tracks",ids:Y.filter(P=>!P.transferredTo).map(P=>P.id).filter(P=>typeof P=="string")});for(let P of Y){if(!C)return;let R;try{R=await B(P)}catch{continue}let M=x[P.id]??0,ye=R.filter(xe=>xe.seq>M&&typeof xe.id=="string");for(let xe=0;xe<ye.length;xe+=Tl){let Ke=ye.slice(xe,xe+Tl).map(dt=>({trackId:P.id,event:dt}));if(!ie(Ke))return}}}function Se(Y){!C||!Y?.trackId||typeof Y.id!="string"||ie([{trackId:Y.trackId,event:Y}])}let $e=new Map;async function v(Y){let{reqId:P,method:R,path:M,headers:ye={},body:xe}=Y,Ke=Object.fromEntries(Object.entries(ye).filter(([,Rt])=>Rt!=null)),dt=new AbortController;$e.set(P,dt);try{if(Y.host){await V(Y,Ke,dt);return}let Rt=await fetch(f+M,{method:R,headers:Ke,body:xe?Buffer.from(xe,"base64"):void 0,signal:dt.signal}),An=Object.fromEntries(Rt.headers);for(let ft of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete An[ft];if(An["content-type"]||(An["content-type"]="application/octet-stream"),ue({v:Ue,ch:"control",type:"http_head",reqId:P,status:Rt.status,headers:An}),Rt.body){for await(let ft of Rt.body)if(!ue({v:Ue,ch:"control",type:"http_data",reqId:P,data:Buffer.from(ft).toString("base64")}))break}ue({v:Ue,ch:"control",type:"http_end",reqId:P,error:null})}finally{$e.delete(P)}}function V(Y,P,R){let{reqId:M,method:ye,path:xe,body:Ke}=Y,dt=new URL(f);return new Promise(Rt=>{let An=oD.request({host:dt.hostname,port:dt.port,method:ye,path:xe,headers:{...P,host:Y.host},signal:R.signal},ft=>{let{connection:vL,"keep-alive":SL,"transfer-encoding":TL,upgrade:AL,...zT}=ft.headers;ue({v:Ue,ch:"control",type:"http_head",reqId:M,status:ft.statusCode||502,headers:zT}),ft.on("data",JT=>{ue({v:Ue,ch:"control",type:"http_data",reqId:M,data:Buffer.from(JT).toString("base64")})||ft.destroy()}),ft.on("end",()=>{ue({v:Ue,ch:"control",type:"http_end",reqId:M,error:null}),Rt()}),ft.on("error",()=>{ue({v:Ue,ch:"control",type:"http_end",reqId:M,error:"view upstream error"}),Rt()})});An.on("error",ft=>{ue({v:Ue,ch:"control",type:"http_end",reqId:M,error:ft?.message||"view request failed"}),Rt()}),Ke&&An.write(Buffer.from(Ke,"base64")),An.end()})}function Tn(){if(N)return;q=new Dm(e,{headers:{authorization:`Bearer ${t}`}}),q.on("open",async()=>{let P=null;try{P=await s?.()||null}catch{P=null}ue({v:Ue,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:kn,installKind:u,providers:cD(),...P?{agents:P}:{}})}),q.on("message",P=>{let R;try{R=JSON.parse(P)}catch{return}if(R.ch==="ctl"&&R.type==="hello_ok"){C=!0,T=tS,W=null,d.log(`[plane-link] connected to plane as ${n}`),R.minNodeVersion&&kn&&jm(kn,R.minNodeVersion)&&d.error(`[plane-link] this node is bostrat-node ${kn}; the plane asks for \u2265${R.minNodeVersion} \u2014 update with: ${eS(u)}`);let M=typeof R.latestNodeVersion=="string"?R.latestNodeVersion:null;if(M&&kn&&jm(kn,M)&&!h.has(M)){h.add(M),d.log(`[plane-link] bostrat-node ${M} is available (this node runs ${kn}) \u2014 ${eS(u)}`);try{l?.({current:kn,latest:M,min:R.minNodeVersion??null,installKind:u,required:!!(R.minNodeVersion&&jm(kn,R.minNodeVersion))})}catch(ye){d.error(`[plane-link] update notice failed: ${ye?.message||ye}`)}}J&&clearInterval(J),J=setInterval(()=>{ue({v:Ue,ch:"ctl",type:"ping"}),S()},mD),J.unref?.(),we(),ge().catch(ye=>d.error("[plane-link] backfill failed:",ye?.message||ye));return}if(R.ch==="ctl"&&R.type==="usage_caps"){dc(R.caps??{},{source:"plane",updatedAt:R.updatedAt??Date.now()}).then(M=>d.log(`[plane-link] spend caps ${Object.keys(M.caps).length?`updated (${Object.keys(M.caps).join(", ")})`:"cleared"} from plane`)).catch(M=>d.error("[plane-link] spend-caps push rejected:",M?.message||M));return}if(R.ch==="ctl"&&R.type==="transferred"&&Array.isArray(R.tracks)){Promise.resolve().then(()=>a?.(R.tracks)).catch(M=>d.error("[plane-link] transfer notice failed:",M?.message||M));return}if(R.ch==="events"&&R.type==="ack"&&Array.isArray(R.ids)){let M=new Set;for(let ye of R.ids){let xe=_.get(ye);xe&&(_.delete(ye),(E[xe.trackId]??0)<xe.seq&&(E[xe.trackId]=xe.seq),M.add(xe.trackId))}for(let ye of M)p(ye);return}if(R.ch==="control"&&R.type==="http_open"){v(R).catch(M=>ue({v:Ue,ch:"control",type:"http_end",reqId:R.reqId,error:M?.message||"tunnel error"}));return}if(R.ch==="control"&&R.type==="http_cancel"){$e.get(R.reqId)?.abort();return}if(R.ch==="attach"&&R.type==="open"){let M=new Dm(m+R.path,Array.isArray(R.protocols)?R.protocols:[],R.host?{headers:{host:R.host}}:void 0);w.set(R.streamId,M),y.set(R.streamId,[]),M.on("open",()=>{let xe=y.get(R.streamId)||[];y.delete(R.streamId);for(let{buf:Ke,bin:dt}of xe)M.send(Ke,{binary:dt})}),M.on("message",(xe,Ke)=>{let dt=Ke?xe:Buffer.from(String(xe));ue({v:Ue,ch:"attach",type:"data",streamId:R.streamId,data:dt.toString("base64"),...Ke?{bin:!0}:{}})});let ye=()=>{y.delete(R.streamId),w.delete(R.streamId)&&ue({v:Ue,ch:"attach",type:"close",streamId:R.streamId})};M.on("close",ye),M.on("error",ye);return}if(R.ch==="attach"&&R.type==="data"){let M=w.get(R.streamId);if(!M)return;let ye=Buffer.from(R.data,"base64"),xe=R.bin===!0,Ke=y.get(R.streamId);Ke?Ke.push({buf:ye,bin:xe}):M.send(ye,{binary:xe});return}if(R.ch==="attach"&&R.type==="close"){let M=w.get(R.streamId);w.delete(R.streamId),y.delete(R.streamId),M?.close();return}});let Y=()=>{C&&d.log("[plane-link] disconnected from plane"),C=!1,J&&(clearInterval(J),J=null),A&&(clearInterval(A),A=null),_.clear(),E={};for(let M of w.values())M.close();w.clear(),y.clear();for(let M of $e.values())M.abort();if($e.clear(),N||H)return;W==null&&(W=Date.now());let P=Date.now()-W<uD,R=P?dD+Math.random()*fD:T;H=setTimeout(()=>{H=null,Tn()},R),H.unref?.(),P||(T=Math.min(T*2,lD))};q.on("close",Y),q.on("error",P=>{let R=/Unexpected server response: (\d+)/.exec(P?.message||"")?.[1];(R==="401"||R==="403")&&Date.now()-Q>=gD&&(Q=Date.now(),d.error(`[plane-link] plane refused this node's credentials (HTTP ${R}) \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)`)),Y()})}return tt.on("event",Se),fe().then(Tn),{stop:()=>{N=!0,tt.off("event",Se),H&&clearTimeout(H),ae&&clearTimeout(ae),J&&clearInterval(J),A&&clearInterval(A),ue({v:Ue,ch:"ctl",type:"bye",reason:"shutdown"}),q?.close()},status:()=>({connected:C,nodeId:n,cursor:{...x},inflight:_.size})}}import yD from"node:fs";import oS from"node:fs/promises";import Qa from"node:os";import bD from"node:path";Kf();ze();var kD=1024*1024,El=e=>Math.round(e/kD);async function xD(e){try{let t=await oS.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:El(r),diskTotalMb:El(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function vD(){try{return(await xt()).filter(t=>t.isBostrat).length}catch{return null}}async function SD(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await oS.readdir(bD.join(Za,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function sS({stateDir:e=Za,now:t=Date.now}={}){let n=Qa.totalmem(),[r,o,s,a]=await Promise.all([Wc().catch(()=>null),xD(yD.existsSync(e)?e:Qa.tmpdir()),vD(),SD()]),i=Qa.cpus()?.length||null,c=Qa.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?El(n-r):null,memTotalMb:El(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(Qa.uptime())}}le();Ku();Ti();le();_r();function Lm(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 aS=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),iS=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},_l=e=>Math.round(e*1e4)/1e4;async function cS(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=aS(),f=0,d=[];for(let m of e||[]){let h=await B(m),w=Un(h,{boxLabels:Lm(m)});if(w.turns===0){m.archivedAt?f++:d.push(m.name||m.id);continue}o+=w.totalUsd,s+=w.turns,a+=w.repricedTurns||0,i+=w.asRecordedUsd||0,c+=w.unpricedTurns||0;for(let y of w.unpricedModels||[])l.add(y);iS(u,w.totalTokens);for(let[y,b]of Object.entries(w.byModel))n[y]||={usd:0,events:0,tokens:aS()},n[y].usd=_l(n[y].usd+b.usd),n[y].events+=b.events,iS(n[y].tokens,b.tokens);for(let[y,b]of Object.entries(w.byProvider))r[y]||={usd:0,events:0},r[y].usd=_l(r[y].usd+b.usd),r[y].events+=b.events;t.push({id:m.id,name:m.name,usd:w.totalUsd,rollup:w})}return t.sort((m,h)=>h.usd-m.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:_l(o),asRecordedUsd:_l(i),turns:s,repricedTurns:a,unpricedTurns:c,unpricedModels:[...l],totalTokens:u,byModel:n,byProvider:r,tracksWithCost:t.length,tracksSilent:f+d.length,tracksSilentArchived:f,tracksSilentLive:d},tracks:t}}var cr=e=>`$${e.toFixed(4)}`,ps=(e,t)=>String(e).padEnd(t);function lS(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||[];r.push(`fleet total ${cr(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 ${cr(t.asRecordedUsd)} \xB7 current-rates total ${cr(t.totalUsd)} (${t.totalUsd>=t.asRecordedUsd?"+":""}${cr(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(", ")}`),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(` ${ps(a,26)} ${ps(cr(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(` ${ps(a.name||a.id,30)} ${cr(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${ps(c.label,40)} ${ps(cr(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((f,d)=>Number(f[0])-Number(d[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${ps(cr(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
505
|
-
`)}_f();fc();le();Yd();import ID from"node:fs/promises";import RD from"node:path";qu();_r();var Il=e=>e.payload?.ts??e.ts,TD=12e4,lr=e=>Math.round(e*1e4)/1e4,AD=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function ED(e,{since:t}){let{track:n,events:r,brief:o}=e,s=x=>Il(x)>=t,a=Ar([Js(n?.name),Js(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 E=Ar(x.payload.task);E&&i.set(x.boxId,E)}let c=new Map,l=x=>{let E=x||"unattributed";return c.has(E)||c.set(E,{segments:[u(null)],lastTs:null,provider:null}),c.get(E)},u=x=>({textCat:x,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),f=[...r].sort((x,E)=>Il(x)-Il(E)),d=0,m=0,h=0,w={turns:0};for(let x of f){let E=l(x.boxId),_=E.segments[E.segments.length-1],q=Il(x);switch(E.lastTs!=null&&q>E.lastTs&&s(x)&&(_.activeMs+=Math.min(q-E.lastTs,TD)),E.lastTs=q,x.provider&&(E.provider=x.provider),x.type){case"turn.user":{let C=x.payload?.text;if(x.source==="hook"||!String(C||"").trim())break;let N=u(Ar(C));E.segments.push(N),s(x)&&(N.turns+=1);break}case"peer.message":{let C=x.payload?.text;if(!String(C||"").trim())break;let N=u(Ar(C));E.segments.push(N),s(x)&&(N.turns+=1);break}case"tool.called":{if(x.source==="transcript")break;let C=x.payload?.tool;C&&(_.tools[C]=(_.tools[C]||0)+1),s(x)&&(_.toolCalls+=1);break}case"cost.recorded":{if(!s(x))break;let{usd:C,tokens:N,model:T,unpriced:W}=Qt(x);W&&(w.turns+=1),_.usd+=C,N&&(_.tokens.input+=N.input,_.tokens.output+=N.output,_.tokens.cacheRead+=N.cacheRead,_.tokens.cacheWrite+=N.cacheWrite),_.models[T]=(_.models[T]||0)+C,x.provider&&(_.provider=x.provider);break}case"handoff.done":s(x)&&(d+=1);break;case"subtask.merged":s(x)&&(m+=1);break;case"error":s(x)&&x.payload?.kind==="subtask-failed"&&(h+=1);break;default:break}}let y=new Map,b=!1;for(let[x,E]of c)for(let _ of E.segments){if(!(_.usd>0||_.activeMs>0||_.turns>0||_.toolCalls>0))continue;let C=_.textCat?.category??i.get(x)?.category??Xw(_.tools)?.category??a?.category??"uncategorized";y.has(C)||y.set(C,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let N=y.get(C);N.usd+=_.usd,N.activeMs+=_.activeMs,N.turns+=_.turns,N.toolCalls+=_.toolCalls;for(let[W,H]of Object.entries(_.models))N.models[W]=(N.models[W]||0)+H;let T=_.provider||E.provider;T&&(N.providers[T]=(N.providers[T]||0)+_.usd),T==="cursor"&&_.usd===0&&(b=!0)}return{perCategory:y,handoffs:d,merged:m,failed:h,sawCursorActivity:b,unpricedTurns:w.turns}}function dS(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;for(let w of e||[]){let{track:y,events:b}=w;if(!b?.length){a+=1;continue}let{perCategory:x,handoffs:E,merged:_,failed:q,sawCursorActivity:C,unpricedTurns:N}=ED(w,{since:r});C&&(i=!0),c+=N;let T={trackId:y?.id,name:y?.name||y?.id,archived:!!y?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:E,subtasksMerged:_,subtasksFailed:q};for(let[W,H]of x){T.usd+=H.usd,T.activeMs+=H.activeMs,T.turns+=H.turns,T.categories[W]={usd:lr(H.usd),activeMs:H.activeMs};for(let[A,j]of Object.entries(H.models))T.models[A]=lr((T.models[A]||0)+j);for(let[A,j]of Object.entries(H.providers))T.providers[A]=lr((T.providers[A]||0)+j);o.has(W)||o.set(W,AD());let ae=o.get(W);ae.usd+=H.usd,ae.activeMs+=H.activeMs,ae.turns+=H.turns,ae.toolCalls+=H.toolCalls;for(let[A,j]of Object.entries(H.models))ae.models[A]=(ae.models[A]||0)+j;for(let[A,j]of Object.entries(H.providers))ae.providers[A]=(ae.providers[A]||0)+j;let J=ae.byTrack.get(y?.id)||{usd:0,activeMs:0};J.usd+=H.usd,J.activeMs+=H.activeMs,ae.byTrack.set(y?.id,J)}(T.usd>0||T.activeMs>0||T.turns>0)&&(T.usd=lr(T.usd),T.dominantCategory=_D(T.categories),s.push(T))}let l=[...o.values()].reduce((w,y)=>w+y.usd,0),u=[...o.values()].reduce((w,y)=>w+y.activeMs,0),f=l>0?"usd":"time",d=f==="usd"?l:u,m=new Map((e||[]).map(w=>[w.track?.id,w.track?.name||w.track?.id])),h=[...o.entries()].map(([w,y])=>({category:w,sharePct:d>0?Math.round((f==="usd"?y.usd:y.activeMs)/d*100):0,shareBasis:f,usd:lr(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:uS(y.models),providers:uS(y.providers),topWorkstreams:[...y.byTrack.entries()].sort((b,x)=>x[1].usd-b[1].usd||x[1].activeMs-b[1].activeMs).slice(0,2).map(([b,x])=>({trackId:b,name:m.get(b),usd:lr(x.usd),activeMs:x.activeMs}))})).sort((w,y)=>y.usd-w.usd||y.activeMs-w.activeMs);return s.sort((w,y)=>y.usd-w.usd||y.activeMs-w.activeMs),{since:r,capturedAt:n,totalUsd:lr(l),activeMs:u,turns:[...o.values()].reduce((w,y)=>w+y.turns,0),bullets:h,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var _D=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},uS=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,lr(n)]));var ms={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function $D(e){let t=String(e||"7d");return t in ms?{key:t,ms:ms[t]}:{key:"7d",ms:ms["7d"]}}function OD(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=hc(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 CD(e){if(!e?.cwd)return null;try{let n=(await ID.readFile(RD.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 fS(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}:$D(t),f=[];for(let d of e||[]){let m=await B(d),h=OD(d,m);i&&c<o&&(h=h.filter(w=>(w.ts??0)<=c)),f.push({track:d,events:h,brief:await CD(d)})}return{window:l,...dS(f,{windowMs:u,now:c})}}le();_r();var ur=36e5,wS=24*ur,MD=400,ND=12,hs=e=>Math.round(e*1e4)/1e4;function PD({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 ms?String(e||"7d"):"7d",i=ms[a];return{key:a,since:i==null?null:r-i,until:r}}var BD=(e,t)=>e!=null&&t-e<=48*ur?ur:wS;function pS(e,t){if(t===ur)return Math.floor(e/ur)*ur;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function DD(e,t){if(t===ur)return e+ur;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var mS=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),hS=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},jD=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,hs(n)]));async function gS(e,t={}){let n=t.now??Date.now(),r=PD({...t,now:n}),o=r.since==null?wS:BD(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=mS(),f=0,d=0,m=0,h=0,w=0,y=new Set,b=new Map,x=[],E=[],_=[],q=null;for(let T of e||[]){let H=(await B(T)).filter(A=>A.type==="cost.recorded");if(!H.length){T.archivedAt||_.push(T.name||T.id);continue}let ae=[];for(let A of H){let j=A.ts??0;s(j)?ae.push(A):i(j)&&(w+=Qt(A).usd)}if(!ae.length)continue;let J=Un(ae,{boxLabels:Lm(T)});f+=J.totalUsd,d+=J.turns,m+=J.repricedTurns||0,h+=J.unpricedTurns||0;for(let A of J.unpricedModels||[])y.add(A);hS(u,J.totalTokens);for(let[A,j]of Object.entries(J.byModel))c[A]||={usd:0,events:0,tokens:mS()},c[A].usd=hs(c[A].usd+j.usd),c[A].events+=j.events,hS(c[A].tokens,j.tokens);for(let[A,j]of Object.entries(J.byProvider))l[A]||={usd:0,events:0},l[A].usd=hs(l[A].usd+j.usd),l[A].events+=j.events;for(let A of ae){let{usd:j}=Qt(A);if(!j)continue;let Q=pS(A.ts??0,o),fe=b.get(Q)??{t:Q,total:0,byProvider:{}};fe.total+=j;let Re=A.provider||"unknown";fe.byProvider[Re]=(fe.byProvider[Re]||0)+j,b.set(Q,fe),(q==null||Q<q)&&(q=Q)}x.push({id:T.id,name:T.name||T.id,archived:!!T.archivedAt,usd:J.totalUsd,turns:J.turns,providers:Object.keys(J.byProvider)});for(let[A,j]of Object.entries(J.byBox))E.push({trackId:T.id,trackName:T.name||T.id,boxId:A,...j})}let C=r.since!=null?pS(r.since,o):q,N=[];for(let T=C,W=0;T!=null&&T<r.until&&W<MD;T=DD(T,o),W++){let H=b.get(T);N.push(H?{t:T,total:hs(H.total),byProvider:jD(H.byProvider)}:{t:T,total:0,byProvider:{}})}return x.sort((T,W)=>W.usd-T.usd),E.sort((T,W)=>W.usd-T.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",window:r.key,since:r.since,until:r.until,bucketMs:o,series:N,fleet:{totalUsd:hs(f),turns:d,repricedTurns:m,unpricedTurns:h,unpricedModels:[...y],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:x.length,tracksSilentLive:_},prior:a!=null?{totalUsd:hs(w),since:r.since-a,until:r.since}:null,tracks:x.slice(0,50),boxes:E.slice(0,ND)}}Ge();import ws from"node:fs/promises";import LD from"node:fs";import We from"node:path";import FD from"node:os";import{execFile as UD}from"node:child_process";import{promisify as HD}from"node:util";var WD=HD(UD),yS=process.env.BOSTRAT_STATE_DIR||We.join(process.env.HOME||FD.tmpdir(),".bostrat"),Rl=!1,Fm=null;async function Um(e,t){let n=await ws.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await ws.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await ws.mkdir(We.dirname(t),{recursive:!0}),await ws.copyFile(e,t),!0)}var qD=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 $l(e,t,{skip:n=null}={}){let r=0,o=await ws.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(qD.has(s.name)||n?.has(s.name))continue;let a=We.join(e,s.name);s.isDirectory()?r+=await $l(a,We.join(t,s.name)):s.isFile()&&(r+=await Um(a,We.join(t,s.name))?1:0)}return r}async function bS({stateDir:e=yS,dest:t=process.env.BOSTRAT_SYNC_DIR||`${yS}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await ws.mkdir(t,{recursive:!0});let o=0;o+=await Um(We.join(e,"tracks.json"),We.join(t,"tracks.json"))?1:0,o+=await Um(We.join(e,"events.jsonl"),We.join(t,"events.jsonl"))?1:0,o+=await $l(We.join(e,"events"),We.join(t,"events")),o+=await $l(We.join(e,"memory"),We.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await $l(We.join(u.cwd,".bostrat"),We.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>WD("git",["-C",t,...u]);LD.existsSync(We.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 nu(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||Rl))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,Rl&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),Rl=!1,Fm=null}catch(u){Rl=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
506
|
-
`).slice(-3).join(" | ").slice(0,300);f!==
|
|
507
|
-
`).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
|
|
508
|
-
`).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
|
|
509
|
-
`),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
|
|
510
|
-
`,{mode:384}),
|
|
564
|
+
_${o} archived_`:"")}async function TD(e,t){let n=await oo();if(!n)return Se("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 Se("Usage: `/bostrat start <name> [repo]`");let i=await r.listRepos(),c=a[a.length-1]?.toLowerCase(),l=a.length>1?i.find(f=>f.name.toLowerCase()===c):null,u=(l?a.slice(0,-1):a).join(" ");return l?(fr(Sv(e,n,{name:u,repo:l.path,responseUrl:t.response_url})),Se(`Starting *${re(u)}* on ${re(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:SD(u,i)}}case"archive":{if(!a.length)return Se("Usage: `/bostrat archive <track>`");let{match:i,live:c}=zm(await r.listTracks(),a.join(" "));return i?(fr(bD(e,n,{track:i,responseUrl:t.response_url})),Se(`Archiving *${re(i.name)}*\u2026`)):Jm(c,a.join(" "))}case"handoff":{let i=r.knownAgents??yD,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return Se(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:f}=zm(await r.listTracks(),l);return u?(fr(kD(e,n,{track:u,agent:c,responseUrl:t.response_url})),Se(`Handing *${re(u.name)}* to ${re(c)}\u2026`)):Jm(f,l)}case"stop":{if(!a.length)return Se("Usage: `/bostrat stop <track>`");let{match:i,live:c}=zm(await r.listTracks(),a.join(" "));return i?(fr(xD(e,n,{track:i,responseUrl:t.response_url})),Se(`Stopping *${re(i.name)}*\u2026`)):Jm(c,a.join(" "))}case"list":return Se(await vD(r));default:return Se(gD)}}async function AD(e,t,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Qm(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 Rt(),await io(t.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${re(l||"question")}~
|
|
565
|
+
\u2713 *${re(a)}*`}}]}).catch(()=>{})}}catch(c){await Ze(n.response_url,Se(`Couldn't answer: ${c?.message||c}`))}}async function ED(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(d=>d.type==="pr.offered"&&d.payload?.offerId===s);if(!u)throw new Error("unknown offer");if(l.some(d=>d.type==="pr.offer.answered"&&d.payload?.offerId===s))throw new Error("offer already answered");let f=Array.isArray(r.actions)?Ho.filter(d=>r.actions.includes(d)):a==="pr"?["commit","push","pr"]:a==="push"?["commit","push"]:null;if(f?.includes("pr")&&i.divertShip&&await i.divertShip(c,u,{actions:f}))return;f&&await i.sayToLead(c.id,Wo(f,{branch:u.payload.branch,base:u.payload.base}),{kind:f.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:f?a:"dismiss",actions:f}})}catch(c){await Ze(n.response_url,Se(`Couldn't act on the offer: ${c?.message||c}`))}}async function _D(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 Ze(n.response_url,Se(`Couldn't act on the offer: ${i?.message||i}`))}}async function ID(e,t){let n=t.actions?.[0];if(!n?.action_id)return;let r=await oo();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 Ze(t.response_url,{...Se(`Starting *${re(s)}*\u2026`),replace_original:!0}),fr(Sv(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"?fr(AD(e,r,t,o)):n.action_id==="bostrat-ship"?fr(ED(e,r,t,o)):n.action_id==="bostrat-pr-exists"&&fr(_D(e,r,t,o))}async function RD(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 hD(t.channel,t.thread_ts);if(!n)return;let r=await oo();if(r)try{await e.deps.sayToLead(n.trackId,t.text.trim())}catch(o){await pr(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function $D(e){return async t=>{if(t.type==="slash_commands")return TD(e,t.payload);if(t.type==="interactive"){await ID(e,t.payload);return}if(t.type==="events_api"){await RD(e,t.payload?.event);return}}}var OD=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 CD(e,t){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${re(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:ii(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 MD(e,t,n){let r=n.payload||{};if(!r.promptId||t.prompt?.promptId===r.promptId)return;t.prompt&&await io(e.botToken,{channel:t.channel,ts:t.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${re(t.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await pr(e,t,r.question||"The lead needs an answer",CD(n.trackId,r));t.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await Rt()}async function ND(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 Rt(),await io(e.botToken,{channel:t.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${re(s||"question")}~
|
|
566
|
+
\u2713 _answered_`}}]})}async function PD(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(f=>l[f]).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 pr(e,t,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${re(ii(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await Rt()}async function BD(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await Rt();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 io(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function DD(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 pr(e,t,`PR ${o} already open \u2014 push or new PR?`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${o}${r.branch?` (${re(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 Rt()}async function jD(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await Rt();let s=r.choice==="dismiss"?"dismissed":r.choice==="new"?"\u2713 opening a new PR":"\u2713 pushing to the open PR";await io(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function LD(e,t,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??FD)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await wv(e.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:t.channel,threadTs:t.ts,initialComment:`\u{1F3A8} mock *${re(o.name)}*${a}${o.group?` (group \`${re(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${re(a.name)}*`).join(", ");await pr(e,t,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${re(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function FD(e,t){let n=await pn(e);if(!n)return null;let r=_t(n,t);if(!r)return null;let o=await ai.readFile(gt(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function UD(e,t){if(!OD.has(t.type))return;let n=await Qm(t.trackId);if(!n)return;let r=await oo();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 Rt(),await Ym(r,n);return;case"turn.agent":{if(!o.text)return;let s=wD(o.text);if(!s)return;await pr(r,n,s,[{type:"section",text:{type:"mrkdwn",text:re(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${Pl(t.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return MD(r,n,t);case"prompt.answered":return ND(r,n,t);case"pr.offered":return PD(r,n,t);case"pr.offer.answered":return BD(r,n,t);case"pr.exists.offered":return DD(r,n,t);case"pr.exists.answered":return jD(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 pr(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 Rt(),await Ym(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await pr(r,n,`Live preview ${o.name}: ${Bl(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${re(o.name)}*: ${Bl(o.slug)}
|
|
567
|
+
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return LD(r,n,t,e.deps)}}function vv(e){let t={deps:e};nt.on("event",s=>{UD(t,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await oo()||(r&&clearInterval(r),r=null,n=gv({getToken:async()=>(await oo())?.appToken,onEnvelope:$D(t),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}vt();ue();Zt();Of();ia();import HD from"node:fs/promises";function WD(e){let t=new Map;for(let n of String(e||"").split(`
|
|
568
|
+
`)){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;i&&t.set(i,{messageId:i,tokens:a,model:o.message?.model||o.model||null})}return[...t.values()]}function qD(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 KD=new Set(["Write","Edit","NotebookEdit"]),GD=220,VD=160;function zD(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(KD.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,GD)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=Ar(o.command.trim().replace(/\s+/g," ")).slice(0,VD));return r}function JD(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?en(t.cwd,t.sessionId):t?.cwd?ju(t.cwd):null}async function Tv(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 R(r,{...s,...c}))};switch(e?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:e.session_id}});break;case"UserPromptSubmit":{let c=String(e.prompt??e.user_prompt??""),l=Hc(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:zD(e,o)});break;case"Stop":await XD(r,o,e,i);break;default:break}return a}async function XD(e,t,n,r){let o=[],s=JD(n,t);if(s)try{o=WD(await HD.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=qD(await B(e)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let f of c){let d=f.model||"unknown",p=u.get(d)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},messageIds:[]};for(let m of Object.keys(p.tokens))p.tokens[m]+=f.tokens[m];p.messageIds.push(f.messageId),u.set(d,p)}for(let[f,d]of u)await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:f,usd:qn(f,d.tokens),tokens:d.tokens,messageId:d.messageIds[d.messageIds.length-1],messageIds:d.messageIds,apiCalls:d.messageIds.length}})}let l=a.tokens.input+a.tokens.cacheWrite+a.tokens.cacheRead;await r({type:"context.usage",actor:"system",source:"transcript",payload:{model:a.model,occupancy:l,tokens:a.tokens}})}Cs();import YD from"node:fs";import Av from"node:fs/promises";import ci from"node:os";import ZD from"node:path";op();Je();var QD=1024*1024,Fl=e=>Math.round(e/QD);async function ej(e){try{let t=await Av.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:Fl(r),diskTotalMb:Fl(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function tj(){try{return(await St()).filter(t=>t.isBostrat).length}catch{return null}}async function nj(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await Av.readdir(ZD.join(Za,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function Ev({stateDir:e=Za,now:t=Date.now}={}){let n=ci.totalmem(),[r,o,s,a]=await Promise.all([el().catch(()=>null),ej(YD.existsSync(e)?e:ci.tmpdir()),tj(),nj()]),i=ci.cpus()?.length||null,c=ci.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?Fl(n-r):null,memTotalMb:Fl(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(ci.uptime())}}ue();sd();Mi();ue();Mr();function eh(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 _v=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),Iv=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},Ul=e=>Math.round(e*1e4)/1e4;async function Rv(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=_v(),f=0,d=[];for(let p of e||[]){let m=await B(p),w=Kn(m,{boxLabels:eh(p)});if(w.turns===0){p.archivedAt?f++:d.push(p.name||p.id);continue}o+=w.totalUsd,s+=w.turns,a+=w.repricedTurns||0,i+=w.asRecordedUsd||0,c+=w.unpricedTurns||0;for(let y of w.unpricedModels||[])l.add(y);Iv(u,w.totalTokens);for(let[y,b]of Object.entries(w.byModel))n[y]||={usd:0,events:0,tokens:_v()},n[y].usd=Ul(n[y].usd+b.usd),n[y].events+=b.events,Iv(n[y].tokens,b.tokens);for(let[y,b]of Object.entries(w.byProvider))r[y]||={usd:0,events:0},r[y].usd=Ul(r[y].usd+b.usd),r[y].events+=b.events;t.push({id:p.id,name:p.name,usd:w.totalUsd,rollup:w})}return t.sort((p,m)=>m.usd-p.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:Ul(o),asRecordedUsd:Ul(i),turns:s,repricedTurns:a,unpricedTurns:c,unpricedModels:[...l],totalTokens:u,byModel:n,byProvider:r,tracksWithCost:t.length,tracksSilent:f+d.length,tracksSilentArchived:f,tracksSilentLive:d},tracks:t}}var mr=e=>`$${e.toFixed(4)}`,ks=(e,t)=>String(e).padEnd(t);function $v(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||[];r.push(`fleet total ${mr(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 ${mr(t.asRecordedUsd)} \xB7 current-rates total ${mr(t.totalUsd)} (${t.totalUsd>=t.asRecordedUsd?"+":""}${mr(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(", ")}`),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(` ${ks(a,26)} ${ks(mr(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(` ${ks(a.name||a.id,30)} ${mr(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${ks(c.label,40)} ${ks(mr(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((f,d)=>Number(f[0])-Number(d[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${ks(mr(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
569
|
+
`)}Uf();Sc();ue();df();import ij from"node:fs/promises";import cj from"node:path";od();Mr();var Hl=e=>e.payload?.ts??e.ts,rj=12e4,hr=e=>Math.round(e*1e4)/1e4,oj=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function sj(e,{since:t}){let{track:n,events:r,brief:o}=e,s=x=>Hl(x)>=t,a=Cr([na(n?.name),na(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 E=Cr(x.payload.task);E&&i.set(x.boxId,E)}let c=new Map,l=x=>{let E=x||"unattributed";return c.has(E)||c.set(E,{segments:[u(null)],lastTs:null,provider:null}),c.get(E)},u=x=>({textCat:x,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),f=[...r].sort((x,E)=>Hl(x)-Hl(E)),d=0,p=0,m=0,w={turns:0};for(let x of f){let E=l(x.boxId),_=E.segments[E.segments.length-1],q=Hl(x);switch(E.lastTs!=null&&q>E.lastTs&&s(x)&&(_.activeMs+=Math.min(q-E.lastTs,rj)),E.lastTs=q,x.provider&&(E.provider=x.provider),x.type){case"turn.user":{let C=x.payload?.text;if(x.source==="hook"||!String(C||"").trim())break;let N=u(Cr(C));E.segments.push(N),s(x)&&(N.turns+=1);break}case"peer.message":{let C=x.payload?.text;if(!String(C||"").trim())break;let N=u(Cr(C));E.segments.push(N),s(x)&&(N.turns+=1);break}case"tool.called":{if(x.source==="transcript")break;let C=x.payload?.tool;C&&(_.tools[C]=(_.tools[C]||0)+1),s(x)&&(_.toolCalls+=1);break}case"cost.recorded":{if(!s(x))break;let{usd:C,tokens:N,model:T,unpriced:W}=tn(x);W&&(w.turns+=1),_.usd+=C,N&&(_.tokens.input+=N.input,_.tokens.output+=N.output,_.tokens.cacheRead+=N.cacheRead,_.tokens.cacheWrite+=N.cacheWrite),_.models[T]=(_.models[T]||0)+C,x.provider&&(_.provider=x.provider);break}case"handoff.done":s(x)&&(d+=1);break;case"subtask.merged":s(x)&&(p+=1);break;case"error":s(x)&&x.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let y=new Map,b=!1;for(let[x,E]of c)for(let _ of E.segments){if(!(_.usd>0||_.activeMs>0||_.turns>0||_.toolCalls>0))continue;let C=_.textCat?.category??i.get(x)?.category??pg(_.tools)?.category??a?.category??"uncategorized";y.has(C)||y.set(C,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let N=y.get(C);N.usd+=_.usd,N.activeMs+=_.activeMs,N.turns+=_.turns,N.toolCalls+=_.toolCalls;for(let[W,H]of Object.entries(_.models))N.models[W]=(N.models[W]||0)+H;let T=_.provider||E.provider;T&&(N.providers[T]=(N.providers[T]||0)+_.usd),T==="cursor"&&_.usd===0&&(b=!0)}return{perCategory:y,handoffs:d,merged:p,failed:m,sawCursorActivity:b,unpricedTurns:w.turns}}function Cv(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;for(let w of e||[]){let{track:y,events:b}=w;if(!b?.length){a+=1;continue}let{perCategory:x,handoffs:E,merged:_,failed:q,sawCursorActivity:C,unpricedTurns:N}=sj(w,{since:r});C&&(i=!0),c+=N;let T={trackId:y?.id,name:y?.name||y?.id,archived:!!y?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:E,subtasksMerged:_,subtasksFailed:q};for(let[W,H]of x){T.usd+=H.usd,T.activeMs+=H.activeMs,T.turns+=H.turns,T.categories[W]={usd:hr(H.usd),activeMs:H.activeMs};for(let[A,j]of Object.entries(H.models))T.models[A]=hr((T.models[A]||0)+j);for(let[A,j]of Object.entries(H.providers))T.providers[A]=hr((T.providers[A]||0)+j);o.has(W)||o.set(W,oj());let ce=o.get(W);ce.usd+=H.usd,ce.activeMs+=H.activeMs,ce.turns+=H.turns,ce.toolCalls+=H.toolCalls;for(let[A,j]of Object.entries(H.models))ce.models[A]=(ce.models[A]||0)+j;for(let[A,j]of Object.entries(H.providers))ce.providers[A]=(ce.providers[A]||0)+j;let J=ce.byTrack.get(y?.id)||{usd:0,activeMs:0};J.usd+=H.usd,J.activeMs+=H.activeMs,ce.byTrack.set(y?.id,J)}(T.usd>0||T.activeMs>0||T.turns>0)&&(T.usd=hr(T.usd),T.dominantCategory=aj(T.categories),s.push(T))}let l=[...o.values()].reduce((w,y)=>w+y.usd,0),u=[...o.values()].reduce((w,y)=>w+y.activeMs,0),f=l>0?"usd":"time",d=f==="usd"?l:u,p=new Map((e||[]).map(w=>[w.track?.id,w.track?.name||w.track?.id])),m=[...o.entries()].map(([w,y])=>({category:w,sharePct:d>0?Math.round((f==="usd"?y.usd:y.activeMs)/d*100):0,shareBasis:f,usd:hr(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:Ov(y.models),providers:Ov(y.providers),topWorkstreams:[...y.byTrack.entries()].sort((b,x)=>x[1].usd-b[1].usd||x[1].activeMs-b[1].activeMs).slice(0,2).map(([b,x])=>({trackId:b,name:p.get(b),usd:hr(x.usd),activeMs:x.activeMs}))})).sort((w,y)=>y.usd-w.usd||y.activeMs-w.activeMs);return s.sort((w,y)=>y.usd-w.usd||y.activeMs-w.activeMs),{since:r,capturedAt:n,totalUsd:hr(l),activeMs:u,turns:[...o.values()].reduce((w,y)=>w+y.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var aj=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},Ov=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,hr(n)]));var xs={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function lj(e){let t=String(e||"7d");return t in xs?{key:t,ms:xs[t]}:{key:"7d",ms:xs["7d"]}}function uj(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=Ac(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 dj(e){if(!e?.cwd)return null;try{let n=(await ij.readFile(cj.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 Mv(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}:lj(t),f=[];for(let d of e||[]){let p=await B(d),m=uj(d,p);i&&c<o&&(m=m.filter(w=>(w.ts??0)<=c)),f.push({track:d,events:m,brief:await dj(d)})}return{window:l,...Cv(f,{windowMs:u,now:c})}}ue();Mr();var wr=36e5,Dv=24*wr,fj=400,pj=12,Ss=e=>Math.round(e*1e4)/1e4;function mj({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 xs?String(e||"7d"):"7d",i=xs[a];return{key:a,since:i==null?null:r-i,until:r}}var hj=(e,t)=>e!=null&&t-e<=48*wr?wr:Dv;function Nv(e,t){if(t===wr)return Math.floor(e/wr)*wr;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function wj(e,t){if(t===wr)return e+wr;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var Pv=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),Bv=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},gj=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Ss(n)]));async function jv(e,t={}){let n=t.now??Date.now(),r=mj({...t,now:n}),o=r.since==null?Dv:hj(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=Pv(),f=0,d=0,p=0,m=0,w=0,y=new Set,b=new Map,x=[],E=[],_=[],q=null;for(let T of e||[]){let H=(await B(T)).filter(A=>A.type==="cost.recorded");if(!H.length){T.archivedAt||_.push(T.name||T.id);continue}let ce=[];for(let A of H){let j=A.ts??0;s(j)?ce.push(A):i(j)&&(w+=tn(A).usd)}if(!ce.length)continue;let J=Kn(ce,{boxLabels:eh(T)});f+=J.totalUsd,d+=J.turns,p+=J.repricedTurns||0,m+=J.unpricedTurns||0;for(let A of J.unpricedModels||[])y.add(A);Bv(u,J.totalTokens);for(let[A,j]of Object.entries(J.byModel))c[A]||={usd:0,events:0,tokens:Pv()},c[A].usd=Ss(c[A].usd+j.usd),c[A].events+=j.events,Bv(c[A].tokens,j.tokens);for(let[A,j]of Object.entries(J.byProvider))l[A]||={usd:0,events:0},l[A].usd=Ss(l[A].usd+j.usd),l[A].events+=j.events;for(let A of ce){let{usd:j}=tn(A);if(!j)continue;let Q=Nv(A.ts??0,o),me=b.get(Q)??{t:Q,total:0,byProvider:{}};me.total+=j;let Re=A.provider||"unknown";me.byProvider[Re]=(me.byProvider[Re]||0)+j,b.set(Q,me),(q==null||Q<q)&&(q=Q)}x.push({id:T.id,name:T.name||T.id,archived:!!T.archivedAt,usd:J.totalUsd,turns:J.turns,providers:Object.keys(J.byProvider)});for(let[A,j]of Object.entries(J.byBox))E.push({trackId:T.id,trackName:T.name||T.id,boxId:A,...j})}let C=r.since!=null?Nv(r.since,o):q,N=[];for(let T=C,W=0;T!=null&&T<r.until&&W<fj;T=wj(T,o),W++){let H=b.get(T);N.push(H?{t:T,total:Ss(H.total),byProvider:gj(H.byProvider)}:{t:T,total:0,byProvider:{}})}return x.sort((T,W)=>W.usd-T.usd),E.sort((T,W)=>W.usd-T.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",window:r.key,since:r.since,until:r.until,bucketMs:o,series:N,fleet:{totalUsd:Ss(f),turns:d,repricedTurns:p,unpricedTurns:m,unpricedModels:[...y],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:x.length,tracksSilentLive:_},prior:a!=null?{totalUsd:Ss(w),since:r.since-a,until:r.since}:null,tracks:x.slice(0,50),boxes:E.slice(0,pj)}}Ve();import vs from"node:fs/promises";import yj from"node:fs";import qe from"node:path";import bj from"node:os";import{execFile as kj}from"node:child_process";import{promisify as xj}from"node:util";var Sj=xj(kj),Lv=process.env.BOSTRAT_STATE_DIR||qe.join(process.env.HOME||bj.tmpdir(),".bostrat"),Wl=!1,th=null;async function nh(e,t){let n=await vs.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await vs.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await vs.mkdir(qe.dirname(t),{recursive:!0}),await vs.copyFile(e,t),!0)}var vj=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 ql(e,t,{skip:n=null}={}){let r=0,o=await vs.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(vj.has(s.name)||n?.has(s.name))continue;let a=qe.join(e,s.name);s.isDirectory()?r+=await ql(a,qe.join(t,s.name)):s.isFile()&&(r+=await nh(a,qe.join(t,s.name))?1:0)}return r}async function Fv({stateDir:e=Lv,dest:t=process.env.BOSTRAT_SYNC_DIR||`${Lv}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await vs.mkdir(t,{recursive:!0});let o=0;o+=await nh(qe.join(e,"tracks.json"),qe.join(t,"tracks.json"))?1:0,o+=await nh(qe.join(e,"events.jsonl"),qe.join(t,"events.jsonl"))?1:0,o+=await ql(qe.join(e,"events"),qe.join(t,"events")),o+=await ql(qe.join(e,"memory"),qe.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await ql(qe.join(u.cwd,".bostrat"),qe.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>Sj("git",["-C",t,...u]);yj.existsSync(qe.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 wu(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||Wl))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,Wl&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),Wl=!1,th=null}catch(u){Wl=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
570
|
+
`).slice(-3).join(" | ").slice(0,300);f!==th&&(th=f,console.warn(`[state-sync] PUSH FAILING (retrying each cycle; off-box copy is STALE): ${f}`))}return{staged:o,committed:i,pushed:l}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}vp();vt();$r();Je();Ms();import{execFile as Tj}from"node:child_process";import{promisify as Aj}from"node:util";import{randomUUID as Ej}from"node:crypto";import _j from"node:fs";import Ij from"node:os";import Rj from"node:path";var yr=Aj(Tj),Kl=e=>new Promise(t=>setTimeout(t,e)),br=Cu,rh=process.env.BOSTRAT_USAGE_PROBE_CWD||Rj.join(Ij.homedir(),".bostrat","usage-probe"),$j=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,Oj=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,Cj=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,Uv=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,gr=null,li=null,ui=null;async function Mj(){try{return await yr(L,U("has-session","-t",br)),!0}catch{return!1}}function Nj(){gr&&clearTimeout(gr),gr=setTimeout(()=>Pj().catch(()=>{}),$j),gr.unref&&gr.unref()}async function Pj(){gr&&(clearTimeout(gr),gr=null);try{await yr(L,U("kill-session","-t",br))}catch{}}async function Bj(){let e=Date.now()+Cj;for(;;){let t=await oh().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(t)){await Kl(500);return}if(Date.now()>e)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await Kl(500)}}async function Dj(){if(await Mj())return;if(!await Bn())throw new Error("host tmux server not running \u2014 start globo-tmux.service");_j.mkdirSync(rh,{recursive:!0}),await Rn(rh);let e=Ej();await yr(L,U("new-session","-d","-s",br,"-c",rh,"-x","200","-y","60",...Ou()));let t=`${$u} --session-id ${e}`;await yr(L,U("send-keys","-t",br,t,"Enter")),await Bj()}async function oh(){let{stdout:e}=await yr(L,U("capture-pane","-t",br,"-p"));return e}async function jj(){await Dj(),await yr(L,U("send-keys","-t",br,"-l","/usage")),await yr(L,U("send-keys","-t",br,"Enter"));let e=Date.now()+Oj,t="";for(;await Kl(400),t=await oh(),!(/Current\s+(session|week)/i.test(t)||/\d+%\s*used/i.test(t)||Date.now()>e););return await Kl(900),t=await oh(),await yr(L,U("send-keys","-t",br,"Escape")).catch(()=>{}),Nj(),t}function Lj(e){let t=String(e||"").split(`
|
|
571
|
+
`).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 Fj(e){let t=String(e||"").split(`
|
|
572
|
+
`).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 Hv({fresh:e=!1}={}){return!e&&li&&Date.now()-li.at<Uv?li.data:ui||(ui=(async()=>{try{let t=await jj(),n=Lj(t),r={...n,contributors:Fj(t),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return li={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 li={at:Date.now()-(Uv-3e3),data:n},n}finally{ui=null}})(),ui)}var Uj=262144;function Hj(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 Wj=new Set([300]),qj=new Set([10080]);function Kj(e){let t=Number(e);return!Number.isFinite(t)||t<=0?null:Wj.has(t)?"session":qj.has(t)?"week":"other"}function Gj(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 Vj(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?Hj((o-n)/1e3):null,resetsAt:o}}var di=e=>({session:null,week:null,other:null,planType:null,credits:null,limitReached:null,sawTokenCount:e});function zj(e,t=Date.now()){let n=String(e||"").split(`
|
|
573
|
+
`),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 di(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=di(r);[c.primary,c.secondary].forEach((p,m)=>{let w=Vj(p,l,t);if(!w)return;let y=Kj(p.window_minutes);y===null&&(y=m===0?"session":"week"),y==="session"&&!u.session?u.session=w:y==="week"&&!u.week?u.week=w:(u.other??=[]).push({label:Gj(p.window_minutes),...w})}),u.planType=typeof c.plan_type=="string"?c.plan_type:null;let d=c.credits;return d&&(d.has_credits||d.unlimited)&&(u.credits={balance:String(d.balance??""),unlimited:!!d.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{...di(r),session:l(c.primary_used_percent),week:l(c.secondary_used_percent)}}return di(r)}return di(r)}async function sh(e){let t={provider:"codex",supported:!0,contributors:null};try{let n=await Ir(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 tt(n.file,Uj),o=zj(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 Wv(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 Gl({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 sh(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await sh(null))}return n.has("cursor")&&r.push(await Gl({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function Gl(e,{fresh:t=!1}={}){let n=e?.agent||"claude";return n==="codex"?sh(e):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await Hv({fresh:t})}}Sr();Ve();Ve();function Jj(e=process.env){return!!(be(e)&&e.BOSTRAT_PLANE_TOKEN)}var Xj=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Vl=(e,t)=>Object.assign(new Error(e),{status:t});async function qv(e,t){if(!Jj(t))throw Vl("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${be(t)}${e}`,{headers:Xj(t)})}catch(o){throw Vl(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw Vl(r?.error||`plane replied ${n.status}`,n.status);return r}async function Kv(e,{max:t=10,env:n=process.env}={}){let r=new URLSearchParams({q:e||""});return t&&r.set("max",String(t)),(await qv(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function Gv(e,{env:t=process.env}={}){if(!e)throw Vl("message id required",400);return(await qv(`/v1/gmail/messages/${encodeURIComponent(e)}`,t)).message}ga();Bc();function ah(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 Yj(e){let[t,n]=e.split("/"),r=ah(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 Vv(e){return(e||"").split(",").map(t=>t.trim()).filter(Boolean).map(Yj).filter(Boolean)}function Zj(e){let t=e.headers["fly-client-ip"];return typeof t=="string"&&t.trim()?t.trim():(e.socket&&e.socket.remoteAddress||"").replace(/^::ffff:/,"")}function zv(e){let t=Vv(e),n=String(e||"").trim().toLowerCase()==="open";return{open:n,clientIp:Zj,allowed(r){if(n)return!0;let o=ah((r||"").replace(/^::ffff:/,""));return o==null?!1:t.some(s=>s(o))}}}function Jv(e,t){let n=Vv(e);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=ah(o);if(s==null||!n.some(i=>i(s)))return!1;let a=t(r);return!!(a&&a.view)}}}import Xv from"node:crypto";import zl from"node:fs";import Qj from"node:path";function Yv(e=process.env,{stateDir:t=Za,log:n=console}={}){let r=String(e.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=Qj.join(t,"api-token");try{let a=zl.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=Xv.randomBytes(32).toString("base64url");try{zl.mkdirSync(t,{recursive:!0}),zl.writeFileSync(o,s+`
|
|
574
|
+
`,{mode:384}),zl.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function fi(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 Zv(e,t){if(!e||!t)return!1;let n=Buffer.from(String(e)),r=Buffer.from(String(t));return n.length!==r.length?!1:Xv.timingSafeEqual(n,r)}function Qv(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 e1=[/^\/api\/github\/token\b/,/^\/api\/gmail\b/],t1=e=>e1.some(t=>t.test(e));function eT({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=Qv(o);return Zv(c,e)?a():t1(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||fi(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 tT(e,t,{enforce:n=!0}={}){return!n||Zv(Qv(e),t)?!0:fi(e)}Zt();Zc();Zc();import n1 from"node:http";import r1 from"node:net";function Jl(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 Xl(e){let t=Jl(e.headers.host);return t?{slug:t,view:Qf(t)}:null}var o1=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),s1=e=>{let t=o1(e.name);return`<!doctype html><meta charset="utf-8">
|
|
511
575
|
<meta http-equiv="refresh" content="3">
|
|
512
576
|
<title>${t} \u2014 starting</title>
|
|
513
577
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
514
578
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
515
|
-
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function
|
|
579
|
+
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function nT(e,t,n){let r=Xl(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(s1(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;let i=n1.request({host:"127.0.0.1",port:o.port,method:e.method,path: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 rT(e,t,n){let r=Xl(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
|
|
516
580
|
\r
|
|
517
581
|
bostrat: no live view "${s}"\r
|
|
518
|
-
`),t.destroy(),!0;let a=
|
|
582
|
+
`),t.destroy(),!0;let a=r1.connect(o.port,"127.0.0.1",()=>{let c=[`${e.method} ${e.url} HTTP/1.1`];for(let[l,u]of Object.entries(e.headers))l!=="host"&&(l==="authorization"||l==="cookie"||c.push(`${l}: ${Array.isArray(u)?u.join(", "):u}`));c.push(`host: 127.0.0.1:${o.port}`),a.write(c.join(`\r
|
|
519
583
|
`)+`\r
|
|
520
584
|
\r
|
|
521
|
-
`),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
|
|
522
|
-
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,M1)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${N1}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=D1.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.`);if(i=`**${u[1].trim().replace(/[:.]\s*$/,"")}** \u2014 ${u[2].trim()}`,i.length>KS)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${KS}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(!r.length)throw new Error("no `- ` bullets found \u2014 an entry is a bullet list, optionally under a `# headline`");if(r.length>qS)throw new Error(`${r.length} bullets \u2014 over the ${qS}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function VS(e,{repos:t=Gm()}={}){let n=new Set(t);return(await Dr()).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,mergedAt:r.resolvedAt??null}))}async function zS(e,{md:t,day:n=null}){let r=j1(t),o=n==null||n===""?B1():String(n);if(!P1.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await I({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}}Ho();Vn();Ko();le();Ho();Ge();Ur();Vn();ua();Ec();ha();import{execFile as L1}from"node:child_process";import{promisify as F1}from"node:util";var zm=F1(L1),JS=3e4,U1=30*6e4,H1=JS,W1=10*6e4,q1=10080*6e4,K1=60*6e4,G1=8,V1=1440*6e4,z1=3,YS="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Vm=60*6e4,J1=15*6e4,Y1=9e4,X1=1440*6e4,Dl=new Map,jl=new Map,Z1=/could not resolve|not found|no pull requests?/i,Q1=e=>Z1.test(String(e?.message||e||""));function ej(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<Vm)}var XS={fast:H1,slow:W1,decay:K1};function ZS(e,t){return e.armedAt&&t-e.armedAt<U1||ej(e,t)||QS(e,t,{grace:0})?"fast":t-(e.firstSeen||0)>q1?"decay":"slow"}function tj(e,t){return XS[ZS(e,t)]}function si(e,t,n,r){let o=e.lanes?.get(X(t.repo,t.number))||{};return Ac({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function nj(e,t){rn("watch");try{let{stdout:n}=await zm("gh",["pr","view",String(t),"--repo",e,"--json",YS],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await $t({slug:e})}});return JSON.parse(n||"null")}catch(n){throw Jn(n)&&Yn(n),n}}async function rj(e){rn("watch");try{let{stdout:t}=await zm("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",YS],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await $t({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw Jn(t)&&Yn(t),t}}async function oj(){let e=new Map;for(let t of await ce().catch(()=>[]))e.set(t.id,t);return e}async function sj(e,t){await I({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...t}})}async function aj(e,t){let n=await Lr(e);return n?Fo(n,t):null}async function ij(e,t,n){let r=await Lr(e);if(!r)throw new Error(`no local checkout for ${e}`);return Uo(r,t,{method:n})}async function cj(e,t){let n=await Lr(e);if(!n)throw new Error(`no local checkout for ${e}`);return pa(n,t)}async function lj(e,t){await I({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 gs(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 uj=10*6e4;async function dj(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>V1){await gs(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 gs(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await gs(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<uj)return;await gs(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 gs(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 gs(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await eT(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function ys(e,t,n){await I({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 fj(e,t){try{return await zm("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function oi(e,t,n,r=()=>!0){return(await B(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var bs=(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 QS(e,t,{grace:n=Y1}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=X1}async function pj(e,t,n,r){let o=r.now??Date.now();if(!QS(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||s.archivedAt||s.transferredTo)return!1;let a=(s.boxes||[]).find(f=>f.name===s.leadBox);return await(r.midTurn??Fr)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await B(s).catch(()=>[])).some(f=>f.type==="pr.state.changed"&&f.payload?.number===Number(e.number))?(await(r.claim??tn)(e.repo,e.number).catch(()=>null),!1):await(r.claim??tn)(e.repo,e.number).catch(()=>null)?(await ys(s,"opened",{...bs(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 mj(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 gc(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await bc(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<J1;await Wb(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await Hb(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await oi(a,"pushed",e.number,f=>f.payload?.sha===s)&&await ys(a,"pushed",{...bs(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>Vm)await yc(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 yc(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=si(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await oi(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await ys(a,"ci-fixed",{...bs(e,t),passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await yc(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>Vm)await kc(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await kc(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){si(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await oi(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await ys(a,"reconciled",{...bs(e,t),base:i.baseRefName||null}).catch(()=>{}),await kc(e.repo,e.number).catch(()=>{})}}}}async function hj(e,t,n,r){let o=si(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await oi(s,"closed",e.number)&&await ys(s,"closed",{...bs(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 wj(e,t,n){si(n,e,"lost",null);let r=t.get(e.trackId);r&&!await oi(r,"lost",e.number)&&await ys(r,"lost",bs(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function eT(e,t,n,r,o={}){let s=si(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await B(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.archiveIfArmed(e.repo,e.number).catch(()=>{}),await r.mark(e.repo,e.number,"merged").catch(()=>{})}async function gj(e={}){let{now:t=Date.now(),auth:n=sf,view:r=nj,list:o=rj,links:s=Kb,trackMap:a=oj,emitMerged:i=sj,mark:c=Gn,archiveIfArmed:l=sn,notifyIfArmed:u=an,notifyIfResolved:f=sl,batchThreshold:d=z1,detail:m=aj,merge:h=ij,updateBranch:w=cj,lookup:y=Je,disarm:b=la,emitDisarmed:x=lj,localHasCommit:E=fj,claim:_=tn,midTurn:q=Fr}=e;if(!(await n().catch(()=>null))?.authenticated||Sc()>0)return 0;let T=(await s().catch(()=>[])).filter(Q=>t-(Dl.get(X(Q.repo,Q.number))||0)>=tj(Q,t));if(!T.length)return 0;let W=Date.now(),H=new Map,ae=new Map;for(let Q of T){let fe=ZS(Q,t);H.set(X(Q.repo,Q.number),{lane:fe,intervalMs:XS[fe]}),Dl.set(X(Q.repo,Q.number),t),ae.has(Q.repo)||ae.set(Q.repo,[]),ae.get(Q.repo).push(Q)}let J=await a().catch(()=>new Map),A={now:t,lanes:H,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,detail:m,merge:h,updateBranch:w,lookup:y,disarm:b,emitDisarmed:x,localHasCommit:E,claim:_,midTurn:q};for(let[Q,fe]of ae){let Re,ue=new Set;if(fe.length>d)Re=await o(Q).catch(()=>null);else{Re=new Map;for(let ie of fe)try{let g=await r(Q,ie.number);g?.number!=null&&Re.set(Number(g.number),g)}catch(g){Q1(g)&&ue.add(X(ie.repo,ie.number))}}if(Re)for(let ie of fe){let g=X(ie.repo,ie.number),p=Re.get(Number(ie.number));if(!p){if(ue.has(g)){let S=(jl.get(g)||0)+1;jl.set(g,S),S>=G1&&(await wj(ie,J,A),jl.delete(g),Dl.delete(g))}continue}jl.delete(g),p.state==="MERGED"||p.state==="CLOSED"?(p.state==="MERGED"?await eT(ie,p,J,A):await hj(ie,p,J,A),Dl.delete(g)):(await pj(ie,p,J,A).catch(()=>{}),await mj(ie,p,J,A).catch(()=>{}),ie.autoMerge&&await dj(ie,J,A).catch(()=>{}))}}let j={};for(let{lane:Q}of H.values())j[Q]=(j[Q]||0)+1;return console.log(`[pr-watch] probed ${T.length} link(s) (${Object.entries(j).map(([Q,fe])=>`${Q} ${fe}`).join(", ")}) in ${Date.now()-W}ms`),T.length}function tT(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{await gj()}catch{}finally{e=!1}}},JS)}ze();le();Ur();import{randomUUID as yj}from"node:crypto";var bj=2e4,nT=3,ks=new Map,Jm=new Set;async function rT(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 kj(e={}){let{sessions:t=null,tracks:n=null,append:r=I,read:o=B,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await xt(),i=n??await ce()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(u.archivedAt||u.transferredTo||!u.leadBox)continue;let f=(u.boxes||[]).find(m=>m.name===u.leadBox);if(!f||f.agent!=="claude"||f.state!=="live")continue;let d=c.get(f.name);if(d)if(l.add(f.name),d.alternateOn){let m=(ks.get(f.name)||0)+1;ks.set(f.name,m),m===nT&&(Jm.add(f.name),await rT(u,f.name,o)||await r(u,{type:"box.fullscreen.detected",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:yj(),box:f.name,agent:f.agent}}).catch(()=>{}))}else{let m=(ks.get(f.name)||0)>=nT;if(ks.delete(f.name),m||!Jm.has(f.name)){Jm.add(f.name);let h=await rT(u,f.name,o);h&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:h,action:"cleared"}}).catch(()=>{})}}}for(let u of[...ks.keys()])l.has(u)||ks.delete(u)}var ai=null,Ym=!1;function oT(){return ai?()=>{}:(ai=setInterval(()=>{Ym||(Ym=!0,kj().catch(()=>{}).finally(()=>{Ym=!1}))},bj),ai.unref?.(),()=>{clearInterval(ai),ai=null})}Bo();Wf();le();Ho();Ko();Bo();import{randomUUID as xj}from"node:crypto";async function vj(){let e=await Promise.resolve().then(()=>(Ur(),sv));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var Xm={listRepoPrs:Nt,readEvents:B,appendEvent:I,sayDeps:vj,currentBranch:Wo};async function Sj(e,t=Xm,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??Wo)(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 Zm(e,t,{actions:n,notify:r=!1}={},o=Xm){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 Sj(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=xj();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 Qm(e,{offerId:t,choice:n}={},r=Xm){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');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=No.filter(f=>f!=="pr"&&(i.includes(f)||f==="push")),c=await u(e.id,Po(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,Cb(i,{branch:a.branch??null,base:a.base??null,prNumber:a.pr?.number??null,offeredRebase:a.offeredRebase===!0}),{kind:"pr-go"})}if(await r.appendEvent({id:e.id,cwd:e.cwd},{type:"pr.exists.answered",actor:"user",source:"orchestrator",boxId:s.boxId,payload:{ts:Date.now(),offerId:t,choice:n}}),n!=="dismiss"&&a.notify===!0){let{queueShipCompleteNotify:u}=await r.sayDeps();u(e.id,{actions:l})}return{say:c,choice:n}}import Sn from"node:fs/promises";import xs from"node:path";import dT from"node:os";import sT from"node:fs/promises";import Ll from"node:path";import{execFile as Tj}from"node:child_process";import{promisify as Aj}from"node:util";import{fileURLToPath as Ej}from"node:url";var _j=Aj(Tj),iT=Ll.dirname(Ej(import.meta.url)),Ij=Ll.resolve(iT,"..","..","client","dist"),Rj="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",$j=new Map([...Rj].map((e,t)=>[e,t]));function Oj(e){let t=[],n=0,r=0;for(let o of e){let s=$j.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 Cj(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=Oj(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 Mj(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 aT=new Map;async function Nj(e){let t;try{t=await sT.stat(e)}catch{return null}let n=aT.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await sT.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:Cj(r.mappings||"")};return aT.set(e,o),o}catch{return null}}var eh=null;function Pj(){return eh||(eh=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):_j("git",["rev-parse","--short","HEAD"],{cwd:Ll.resolve(iT,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),eh}var Bj=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function Dj(e,{distDir:t=Ij}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(Bj)){let[a,i,c,l]=s,u=await Nj(Ll.join(t,"assets",`${i}.map`)),f=u?Mj(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(e.slice(r,s.index)),f&&u.sources[f[1]]){let d=u.sources[f[1]].replace(/^(\.\.\/)+/,"");n.push(`${d}:${f[2]+1}:${f[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 cT(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await Pj();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await Dj(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var jj=()=>process.env.BOSTRAT_STATE_DIR||xs.join(process.env.HOME||dT.tmpdir(),".bostrat"),mr=()=>xs.join(jj(),"telemetry"),fT=()=>xs.join(mr(),".relay-offsets.json"),Lj=50,Fj=14,Uj=3e4,lT=500,ut=(e,t)=>typeof e=="string"?e.slice(0,t):"";function Hj(e,t){if(!e||typeof e!="object")return null;let n=ut(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:ut(e.anonId,64),sessionId:ut(e.sessionId,64),userId:ut(e.userId,64),release:ut(e.release,64),appVersion:ut(e.appVersion,32),route:ut(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:ut(e.error.kind,256),message:ut(e.error.message,8192),stack:ut(e.error.stack,32768),fingerprint:ut(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:ut(s?.n,64),r:ut(s?.r,128)}))),o}var pT=e=>new Date(e).toISOString().slice(0,10),uT=0;async function mT(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,Lj):[]).map(s=>Hj(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await cT(s);await Sn.mkdir(mr(),{recursive:!0});let o=xs.join(mr(),`${pT(t)}.ndjson`);return await Sn.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
523
|
-
`).join(""),"utf8"),t-
|
|
524
|
-
`).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=lT){let m=f.slice(d,d+lT).map(w=>qj(w)),h=await e(`${t.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.ingestKey}`},body:JSON.stringify({events:m})}).catch(()=>null);if(!h||!h.ok)return{sent:o,disabled:!1};o+=m.length}r[s]=i+l+1,await gT(r)}return{sent:o,disabled:!1}}var th=null,nh=!1;function yT(){th||(th=setInterval(async()=>{if(!nh){nh=!0;try{await Kj()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{nh=!1}}},Uj),th.unref?.())}import Hl from"node:fs/promises";import ST from"node:path";import Gj from"node:os";var TT=()=>process.env.BOSTRAT_STATE_DIR||ST.join(process.env.HOME||Gj.tmpdir(),".bostrat"),AT=()=>ST.join(TT(),"dictation-glossary.json"),Vj=100,zj=500,Jj=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Wl=null,rh=null,oh=Promise.resolve();function bT(){let e={},t=Date.now();for(let n of Jj)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function ET(){return Wl||(rh??=Hl.readFile(AT(),"utf8").then(e=>JSON.parse(e)?.terms??bT()).catch(()=>bT()).then(e=>Wl=e).finally(()=>rh=null),rh)}async function kT(){let e=JSON.stringify({terms:Wl},null,2),t=AT(),n=`${t}.${process.pid}.tmp`;await Hl.mkdir(TT(),{recursive:!0}),await Hl.writeFile(n,e),await Hl.rename(n,t)}function Yj(){return oh=oh.then(kT,kT),oh.catch(()=>{})}async function sh(){return Object.values(await ET()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var Xj=e=>Math.max(1,Math.ceil(e.length/4));async function Zj(){let e=[],t=0;for(let{term:n}of await sh()){let r=Xj(n);if(!(t+r>zj)&&(e.push(n),t+=r,e.length>=Vj))break}return e}async function Qj(e,t){let n=await ET(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var xT=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Ul=e=>e.toLowerCase();function vT(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function _T(e,t){let n=xT(e),r=xT(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 f=s-1;f>=0;f--)a[u][f]=Ul(n[u])===Ul(r[f])?a[u+1][f+1]+1:Math.max(a[u+1][f],a[u][f+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)Ul(n[c])===Ul(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(vT(r[l])&&i.add(r[l]),l++);for(;l<s;l++)vT(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await Qj(u,"learned");return await Yj(),[...i]}var ii=null;function eL(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 IT(){if(!ii)return null;let e=await Ka();if(!e||e.apiKey.slice(-6)!==ii.keyTail)return null;let{keyTail:t,...n}=ii;return n}async function RT(e,t){let n=await Ka();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 Zj())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=eL(o.status,i);c.persistent&&(ii={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 ii=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}le();var ah={"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 tL(e){if(!e)return null;let t=String(e).trim();if(ah[t])return t;let n=null;for(let r of Object.keys(ah))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function nL(e,t={}){let n=ah[tL(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 rL(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 ci=null;async function oL(){if(!ci)return null;let e=await yl();if(!e||e.apiKey.slice(-6)!==ci.keyTail)return null;let{keyTail:t,...n}=ci;return n}async function sL(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=rL(t.status,o);s.persistent&&(ci={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}ci=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 aL={openai:sL};async function $T(){let e=await yl();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await oL()}:{configured:!1}}async function OT(){let e=await yl();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 aL[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function CT(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=nL(t,s),i=await I(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}}var jT=me.dirname(cL(import.meta.url)),MT=Number(process.env.PORT)||3362,ql=Pm(process.env),LT=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,vs=_S(process.env.BOSTRAT_ALLOW_IPS),dh=IS(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Bl),FT=$S(),UT=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",k=bt();k.disable("x-powered-by");k.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),Pl(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});k.use((e,t,n)=>{if(ri(e)||vs.allowed(vs.clientIp(e))||dh.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});k.use(PS);k.use(MS({token:FT,enforce:UT}));k.use("/api/mocks",bt.json({limit:"1mb"}));k.use("/api/mocks",bt.raw({type:"image/png",limit:"24mb"}));k.use("/api/mocks",bt.raw({type:["video/mp4","video/webm"],limit:"105mb"}));k.use("/api/runbooks",bt.json({limit:"1mb"}));k.use("/api/tracks/adopt",bt.json({limit:"1mb"}));k.use("/api/telemetry",bt.json({limit:"256kb"}));k.use("/api/hook",bt.json({limit:"2mb"}));k.use("/api/dictation",bt.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));k.use("/api/tracks/:id/upload",bt.raw({type:()=>!0,limit:"25mb"}));k.use(bt.json({limit:"64kb"}));k.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:st,hostTmuxAlive:await Mn(),autoTriggerPct:LT})});k.get("/api/sessions",async(e,t)=>{t.json({sessions:await xt()})});k.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 Nn(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})}});k.delete("/api/sessions/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ee(n))return t.status(400).json({error:"invalid session name"});if(!await oe(n))return t.status(404).json({error:"session not found"});try{await Ct(n),t.json({ok:!0})}catch(r){t.status(500).json({error:`failed to kill: ${r?.message||r}`})}});k.get("/api/context/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ee(n))return t.status(400).json({error:"invalid session name"});let r=await oe(n);if(!r)return t.status(404).json({error:"session not found"});let o=await Ls(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});k.post("/api/handoff/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ee(n))return t.status(400).json({error:"invalid session name"});try{let r=await Yg(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)})}});k.get("/api/handoff/:name",(e,t)=>{let n=decodeURIComponent(e.params.name),r=tc(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 NT=10;k.get("/api/idle-guard",async(e,t)=>{try{t.json(av({tracks:await ce(),views:await Lf(),queuedOf:n=>rs(n)}))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});k.get("/api/tracks",async(e,t)=>{let n=(await ce()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>NT){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,NT).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:ja,agentAvailability:await Qc(),tickets:await Rv()})});k.get("/api/cost",async(e,t)=>{try{let n=await ce();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await cS(n);if(r.cursorUsage=Ef(),e.query.format==="text")return t.type("text/plain").send(lS(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/usage/roundup",async(e,t)=>{try{t.json(await fS(await ce(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/usage/spend",async(e,t)=>{try{let n=await gS(await ce(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=Ef(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=sa(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/usage/caps",(e,t)=>{t.json(sa())});k.post("/api/usage/caps",async(e,t)=>{try{t.json(await dc(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});k.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await vS(await ce(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await hh(e.get(pi),{byToken:Ip,legacy:()=>ga(e.body?.session_id,e.body?.cwd)});if(n==="refused")return Ts("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return Ts("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&&Ts("mismatch",{track:r.track.id,box:r.box.name}),Ts(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 Xv(e.body||{},()=>r);t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});k.post("/api/telemetry",async(e,t)=>{try{if(!hT(vs.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await mT(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});k.get("/api/repos",async(e,t)=>{try{let n=await zt(),r=new Set((await Promise.all(n.map(a=>de(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await Rs()).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)})}});k.get("/api/github/token",async(e,t)=>{try{t.json(await bi(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});k.get("/api/github/status",(e,t)=>{t.json({configured:yi(),calls:tk(),latency:sk()})});k.post("/api/github/refresh",(e,t)=>{Ah(),t.json({ok:!0})});k.get("/api/gmail/search",async(e,t)=>{try{let n=await TS(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)})}});k.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await AS(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});function fh(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}async function Kl({repo:e=null,number:t=null,branch:n=null}){let r=e?await de(e):null;if(r&&t!=null){let s=await Je(r,t),a=s&&await ee(s.trackId);if(a)return a}if(!n)return null;let o=await ce();return Oc(o,n,s=>fh(s,e,r))}async function HT({repo:e=null,number:t=null,branch:n=null}){let r=await Kl({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!r.archivedAt&&!r.transferredTo)return r;if(!n)return null;let o=e?await de(e).catch(()=>null):null,s=await ce();return Oc(s,n,a=>!a.archivedAt&&!a.transferredTo&&fh(a,e,o))}async function It(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Kl({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await de(s).catch(()=>null):null);await I({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 fL(e){let t=(await ce()).filter(n=>!n.archivedAt&&!n.transferredTo);for(let n of e??[]){if(!n.prs?.length)continue;let r=await de(n.path);if(r)for(let o of n.prs){if(await Je(r,o.number))continue;let s=await Oc(t,o.headRefName,a=>fh(a,n.path,r));s&&await it({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}k.get("/api/git/prs",async(e,t)=>{try{let n=await fk();await fL(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.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:f}=e.body||{};try{if(s){let w=o||"merge";if(!fa.has(w))throw new Error(`invalid merge method: ${w}`);let y=Number(r);if(!Number.isInteger(y)||y<=0)throw new Error("invalid PR number");let b=await de((await on(n)).path);if(!b)throw new Error("repo has no GitHub remote");let x=await Kl({repo:n,number:y,branch:l}).catch(()=>null);if(!x)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await pa(n,y),await it({repo:b,number:y,trackId:x.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await ia(b,y,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await Br(b,y,i===!0),await ca(b,y,c===!0&&i!==!0),Bt(),await It("auto-armed",{number:y,url:f,title:u,branch:l,repo:n,track:x,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:y,auto:!0})}let d=await Uo(n,r,{method:o||"squash"});await It("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let m=await de(n).catch(()=>null),h=!1;if(m&&(i===!0&&await Br(m,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await ca(m,d.number,!0).catch(()=>{}),await an(m,d.number).catch(()=>{}),h=await sn(m,d.number).catch(()=>!1),await Gn(m,d.number,"merged").catch(()=>{})),i===!0&&!h){let w=await Kl({repo:n,number:d.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await Vr(w.id).catch(()=>{})}t.json(d)}catch(d){let m=d?.message||String(d),h=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(m)?400:502;t.status(h).json({error:m})}});k.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 de((await on(n)).path).catch(()=>null),l=c?await la(c,i).catch(()=>null):null;try{await lf(n,i)}catch(u){if(!l)throw u}Bt(),await It("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})}});k.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await de(n).catch(()=>null),s=await wk(n,{method:r||"merge",onMerged:a=>{It("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&an(o,a.number).catch(()=>{}).then(()=>sn(o,a.number)).catch(()=>{}).then(()=>Gn(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})}});k.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:hk()})});k.post("/api/git/prs/merge-track",async(e,t)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=e.body||{};try{let i=a||"merge";if(!fa.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return t.status(400).json({error:"trackId required"});let c=await ee(n);if(!c)return t.status(404).json({error:`Track ${n} isn't on this node`});let l=(await Dr()).filter(d=>d.trackId===c.id&&d.state!=="merged"&&d.state!=="closed");if(!l.length)return t.status(409).json({error:"no open PRs on this Track"});for(let d of l)await Br(d.repo,d.number,o===!0).catch(()=>{}),await ca(d.repo,d.number,s===!0&&o!==!0).catch(()=>{});let u=[],f=[];for(let d of l){let m={repo:d.repo,number:d.number,ok:!0,action:"armed"};u.push(m);try{let h=await Lr(d.repo);if(!h)throw new Error(`no local checkout for ${d.repo}`);let w=await Fo(h,d.number);f.push({r:m,l:d,repoPath:h,pr:w,plan:pk(w,{update:r!==!1})})}catch(h){m.ok=!1,m.action="failed",m.reason=h?.message||String(h)}}for(let{r:d,l:m,repoPath:h,pr:w,plan:y}of mk(f))try{if(y.action==="skip")d.action="skipped",d.reason=y.reason;else if(y.action==="merge")await Uo(h,m.number,{method:i}),await It("merged",{number:m.number,url:w.url,title:w.title,branch:w.headRefName,repo:h,track:c}),await an(m.repo,m.number).catch(()=>{}),await sn(m.repo,m.number).catch(()=>{}),await Gn(m.repo,m.number,"merged").catch(()=>{}),d.action="merged";else{if(y.action==="update-arm"&&(await pa(h,m.number).catch(()=>{}),d.action="updated-armed"),!await ia(m.repo,m.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await It("auto-armed",{number:m.number,url:w.url,title:w.title,branch:w.headRefName,repo:h,track:c,...d.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(b){d.ok=!1,d.action="failed",d.reason=b?.message||String(b)}if(Bt(),!u.some(d=>d.ok)){if(o===!0)for(let d of l)await Br(d.repo,d.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let d of l)await ca(d.repo,d.number,!1).catch(()=>{});return t.status(502).json({error:u.map(d=>`${d.repo}#${d.number}: ${d.reason}`).join("; "),results:u})}t.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;t.status(l).json({error:c})}});k.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 ee(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await Dr()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await la(a.repo,a.number).catch(()=>null),c=await Lr(a.repo).catch(()=>null);c&&await lf(c,a.number).catch(()=>{}),i&&(await It("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}))}Bt(),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})}});k.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 ee(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=lt(i.workspace),f,d=Array.isArray(a)?a.filter(Boolean):[];if(d.length)f=d.map(w=>{let y=u.find(b=>b.name===w||b.repo===w||b.base===w||b.dir===w);if(!y)throw new Error(`invalid repo "${w}" \u2014 not tagged on this Track`);return y});else if(f=await Go(i),!f.length)return t.status(409).json({error:"no tagged repo has work to ship"});let m=[];for(let w of f)try{let y=await df(w.dir,{title:r,body:o,base:s}),b=w.repo||await de(w.dir).catch(()=>null);b&&y.number&&await it({repo:b,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await It("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:b??w.name}}),m.push({repo:w.name,slug:b??null,ok:!0,url:y.url,number:y.number})}catch(y){m.push({repo:w.name,slug:w.repo??null,ok:!1,error:y?.message||String(y)})}let h=m.filter(w=>w.ok);return h.length?t.json({ok:!0,multi:!0,results:m,url:h[0].url,number:h[0].number}):t.status(502).json({error:m.map(w=>`${w.repo}: ${w.error}`).join("; "),results:m})}let c=await df(i.cwd,{title:r,body:o,base:s}),l=await de(i.cwd).catch(()=>null);l&&c.number&&await it({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await It("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})}});k.get("/api/tracks/:id/repos",async(e,t)=>{try{let n=await ee(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await Go(n),o=new Map(r.map(a=>[a.base,a])),s=lt(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)})}});k.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 jp(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})}});k.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await Lp(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})}});k.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Fo(n,r),s=await de(n).catch(()=>null),a=await HT({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=lt(a.workspace).find(b=>b.base===n||s&&b.repo===s)??null,c=a.workspace?.kind==="multi",l=await gk(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?me.join("ci",i.name):"ci",f=me.join(a.cwd,".bostrat",u),d=me.join(".bostrat",u,`pr-${o.number}-fail.log`);await qe.promises.mkdir(f,{recursive:!0}),await qe.promises.writeFile(me.join(a.cwd,d),l.map(b=>`\u2550\u2550\u2550\u2550 ${b.name}${b.link?` \xB7 ${b.link}`:""} \u2550\u2550\u2550\u2550
|
|
585
|
+
`),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 oT(e,t){let n=typeof t=="string"?{host:t}:t||{},r=n["x-bostrat-view-base"];return typeof r=="string"&&r?`${n["x-forwarded-proto"]==="https"?"https":"http"}://${e.slug}.${r.replace(/:\d+$/,"")}/`:`http://${e.slug}.${a1(n.host)}/`}function a1(e){if(process.env.BOSTRAT_PUBLIC_HOST)return String(process.env.BOSTRAT_PUBLIC_HOST).replace(/:\d+$/,"");let t=String(e||"").replace(/:\d+$/,"").toLowerCase();if(!t||!t.includes(".")||/^[\d.]+$/.test(t))return"bostrat.globopi";let n=t.split(".");return Jl(t)&&Qf(n[0])?n.slice(1).join("."):t}os();fp();sl();Nf();ue();import{randomUUID as i1}from"node:crypto";var sT=32768,aT=120,iT=1e3,cT=new Set(["claude","codex","cursor"]),c1=new Set(["off","transition","nag"]),l1=[[0,59],[0,23],[1,31],[1,12],[0,6]];function u1(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=l1[r];return String(n).split(",").every(a=>{if(!a)return!1;let[i,c]=a.split("/");if(c!==void 0&&!(Number.isInteger(Number(c))&&Number(c)>=1))return!1;if(i==="*")return!0;let[l,u]=i.includes("-")?i.split("-").map(Number):[Number(i),Number(i)];return Number.isInteger(l)&&Number.isInteger(u)&&l>=o&&u<=s&&l<=u})})}var Tn=e=>String(e??"").trim();function d1(e,t){let n=e&&typeof e=="object"?e:{},r=Tn(n.name);if(!r)throw new Error("a loop plan needs a name");if(r.length>aT)throw new Error(`name is too long (${r.length}); cap is ${aT}`);let o=Tn(n.schedule);if(!o)throw new Error('a loop plan needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!u1(o))throw new Error(`invalid cron schedule: "${o}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);let s=Tn(n.prompt);if(!s)throw new Error("a loop plan needs a prompt \u2014 it is what the run does");if(s.length>sT)throw new Error(`prompt is too long (${s.length} chars); cap is ${sT}`);let a=Tn(n.expectation)||null;if(a&&a.length>iT)throw new Error(`expectation is too long (${a.length}); cap is ${iT}`);let i=Tn(n.notify)||"off";if(!c1.has(i))throw new Error(`notify must be one of off | transition | nag, got "${n.notify}"`);let c=n.assignment??null,l=Tn(typeof c=="string"?c:c?.provider)||"claude";if(!cT.has(l))throw new Error(`unknown provider "${l}" \u2014 one of ${[...cT].join(", ")}`);let u=typeof c=="object"&&c&&Tn(c.model)||null,f=Tn(t?.workspace?.repo)||Tn(t?.workspace?.base)||null;if(!f)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");return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:u?{provider:l,model:u}:{provider:l},repo:f}}async function lT(e,t){let n=d1(t,e),r=i1();return await R({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 uT(e,t,n,{loopId:r=null,nextFireAt:o=null}={}){if(!t)throw new Error("proposalId required");let s=await B({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 R({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 R({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,nextFireAt:Number.isFinite(Number(o))?Number(o):null}}),await R({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)}}Nn();ue();Yn();sl();var dT=10,fT=200,f1=120,p1=24,m1=/^\d{4}-\d{2}-\d{2}$/;function ih(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var h1=(e=Date.now())=>new Date(e).toISOString().slice(0,10);function pT(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 w1=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function g1(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Ma(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(`
|
|
586
|
+
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,f1)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${p1}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=w1.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.`);if(i=`**${u[1].trim().replace(/[:.]\s*$/,"")}** \u2014 ${u[2].trim()}`,i.length>fT)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${fT}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(!r.length)throw new Error("no `- ` bullets found \u2014 an entry is a bullet list, optionally under a `# headline`");if(r.length>dT)throw new Error(`${r.length} bullets \u2014 over the ${dT}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function mT(e,{repos:t=ih()}={}){let n=new Set(t);return(await Wr()).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,mergedAt:r.resolvedAt??null}))}async function hT(e,{md:t,day:n=null}){let r=g1(t),o=n==null||n===""?h1():String(n);if(!m1.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await R({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}}Xo();Yn();Qo();ue();Xo();Ve();Vr();Yn();ga();Bc();Sa();import{execFile as y1}from"node:child_process";import{promisify as b1}from"node:util";var lh=b1(y1),wT=3e4,k1=30*6e4,x1=wT,S1=10*6e4,v1=10080*6e4,T1=60*6e4,A1=8,E1=1440*6e4,_1=3,gT="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",ch=60*6e4,I1=15*6e4,R1=9e4,$1=1440*6e4,Yl=new Map,Zl=new Map,O1=/could not resolve|not found|no pull requests?/i,C1=e=>O1.test(String(e?.message||e||""));function M1(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<ch)}var yT={fast:x1,slow:S1,decay:T1};function bT(e,t){return e.armedAt&&t-e.armedAt<k1||M1(e,t)||kT(e,t,{grace:0})?"fast":t-(e.firstSeen||0)>v1?"decay":"slow"}function N1(e,t){return yT[bT(e,t)]}function mi(e,t,n,r){let o=e.lanes?.get(Y(t.repo,t.number))||{};return Pc({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function P1(e,t){sn("watch");try{let{stdout:n}=await lh("gh",["pr","view",String(t),"--repo",e,"--json",gT],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Ct({slug:e})}});return JSON.parse(n||"null")}catch(n){throw Qn(n)&&er(n),n}}async function B1(e){sn("watch");try{let{stdout:t}=await lh("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",gT],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await Ct({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw Qn(t)&&er(t),t}}async function D1(){let e=new Map;for(let t of await ie().catch(()=>[]))e.set(t.id,t);return e}async function j1(e,t){await R({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...t}})}async function L1(e,t){let n=await Kr(e);return n?zo(n,t):null}async function F1(e,t,n){let r=await Kr(e);if(!r)throw new Error(`no local checkout for ${e}`);return Jo(r,t,{method:n})}async function U1(e,t){let n=await Kr(e);if(!n)throw new Error(`no local checkout for ${e}`);return ka(n,t)}async function H1(e,t){await R({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 Ts(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 W1=10*6e4;async function q1(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>E1){await Ts(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 Ts(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await Ts(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<W1)return;await Ts(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 Ts(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 Ts(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await xT(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function As(e,t,n){await R({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 K1(e,t){try{return await lh("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function pi(e,t,n,r=()=>!0){return(await B(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var Es=(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 kT(e,t,{grace:n=R1}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=$1}async function G1(e,t,n,r){let o=r.now??Date.now();if(!kT(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||s.archivedAt||s.transferredTo)return!1;let a=(s.boxes||[]).find(f=>f.name===s.leadBox);return await(r.midTurn??Gr)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await B(s).catch(()=>[])).some(f=>f.type==="pr.state.changed"&&f.payload?.number===Number(e.number))?(await(r.claim??rn)(e.repo,e.number).catch(()=>null),!1):await(r.claim??rn)(e.repo,e.number).catch(()=>null)?(await As(s,"opened",{...Es(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 V1(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 _c(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await Rc(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<I1;await ak(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await sk(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await pi(a,"pushed",e.number,f=>f.payload?.sha===s)&&await As(a,"pushed",{...Es(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>ch)await Ic(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 Ic(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=mi(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await pi(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await As(a,"ci-fixed",{...Es(e,t),passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await Ic(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>ch)await $c(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await $c(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){mi(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await pi(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await As(a,"reconciled",{...Es(e,t),base:i.baseRefName||null}).catch(()=>{}),await $c(e.repo,e.number).catch(()=>{})}}}}async function z1(e,t,n,r){let o=mi(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await pi(s,"closed",e.number)&&await As(s,"closed",{...Es(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 J1(e,t,n){mi(n,e,"lost",null);let r=t.get(e.trackId);r&&!await pi(r,"lost",e.number)&&await As(r,"lost",Es(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function xT(e,t,n,r,o={}){let s=mi(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await B(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.archiveIfArmed(e.repo,e.number).catch(()=>{}),await r.mark(e.repo,e.number,"merged").catch(()=>{})}async function X1(e={}){let{now:t=Date.now(),auth:n=kf,view:r=P1,list:o=B1,links:s=ck,trackMap:a=D1,emitMerged:i=j1,mark:c=Xn,archiveIfArmed:l=cn,notifyIfArmed:u=ln,notifyIfResolved:f=wl,batchThreshold:d=_1,detail:p=L1,merge:m=F1,updateBranch:w=U1,lookup:y=Xe,disarm:b=wa,emitDisarmed:x=H1,localHasCommit:E=K1,claim:_=rn,midTurn:q=Gr}=e;if(!(await n().catch(()=>null))?.authenticated||Mc()>0)return 0;let T=(await s().catch(()=>[])).filter(Q=>t-(Yl.get(Y(Q.repo,Q.number))||0)>=N1(Q,t));if(!T.length)return 0;let W=Date.now(),H=new Map,ce=new Map;for(let Q of T){let me=bT(Q,t);H.set(Y(Q.repo,Q.number),{lane:me,intervalMs:yT[me]}),Yl.set(Y(Q.repo,Q.number),t),ce.has(Q.repo)||ce.set(Q.repo,[]),ce.get(Q.repo).push(Q)}let J=await a().catch(()=>new Map),A={now:t,lanes:H,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,detail:p,merge:m,updateBranch:w,lookup:y,disarm:b,emitDisarmed:x,localHasCommit:E,claim:_,midTurn:q};for(let[Q,me]of ce){let Re,de=new Set;if(me.length>d)Re=await o(Q).catch(()=>null);else{Re=new Map;for(let le of me)try{let g=await r(Q,le.number);g?.number!=null&&Re.set(Number(g.number),g)}catch(g){C1(g)&&de.add(Y(le.repo,le.number))}}if(Re)for(let le of me){let g=Y(le.repo,le.number),h=Re.get(Number(le.number));if(!h){if(de.has(g)){let S=(Zl.get(g)||0)+1;Zl.set(g,S),S>=A1&&(await J1(le,J,A),Zl.delete(g),Yl.delete(g))}continue}Zl.delete(g),h.state==="MERGED"||h.state==="CLOSED"?(h.state==="MERGED"?await xT(le,h,J,A):await z1(le,h,J,A),Yl.delete(g)):(await G1(le,h,J,A).catch(()=>{}),await V1(le,h,J,A).catch(()=>{}),le.autoMerge&&await q1(le,J,A).catch(()=>{}))}}let j={};for(let{lane:Q}of H.values())j[Q]=(j[Q]||0)+1;return console.log(`[pr-watch] probed ${T.length} link(s) (${Object.entries(j).map(([Q,me])=>`${Q} ${me}`).join(", ")}) in ${Date.now()-W}ms`),T.length}function ST(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{await X1()}catch{}finally{e=!1}}},wT)}Je();ue();Vr();import{randomUUID as Y1}from"node:crypto";var Z1=2e4,vT=3,_s=new Map,uh=new Set;async function TT(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 Q1(e={}){let{sessions:t=null,tracks:n=null,append:r=R,read:o=B,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await St(),i=n??await ie()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(u.archivedAt||u.transferredTo||!u.leadBox)continue;let f=(u.boxes||[]).find(p=>p.name===u.leadBox);if(!f||f.agent!=="claude"||f.state!=="live")continue;let d=c.get(f.name);if(d)if(l.add(f.name),d.alternateOn){let p=(_s.get(f.name)||0)+1;_s.set(f.name,p),p===vT&&(uh.add(f.name),await TT(u,f.name,o)||await r(u,{type:"box.fullscreen.detected",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:Y1(),box:f.name,agent:f.agent}}).catch(()=>{}))}else{let p=(_s.get(f.name)||0)>=vT;if(_s.delete(f.name),p||!uh.has(f.name)){uh.add(f.name);let m=await TT(u,f.name,o);m&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:m,action:"cleared"}}).catch(()=>{})}}}for(let u of[..._s.keys()])l.has(u)||_s.delete(u)}var hi=null,dh=!1;function AT(){return hi?()=>{}:(hi=setInterval(()=>{dh||(dh=!0,Q1().catch(()=>{}).finally(()=>{dh=!1}))},Z1),hi.unref?.(),()=>{clearInterval(hi),hi=null})}qo();np();ue();Xo();Qo();qo();import{randomUUID as eL}from"node:crypto";async function tL(){let e=await Promise.resolve().then(()=>(Vr(),SS));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var fh={listRepoPrs:Bt,readEvents:B,appendEvent:R,sayDeps:tL,currentBranch:Yo};async function nL(e,t=fh,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??Yo)(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 ph(e,t,{actions:n,notify:r=!1}={},o=fh){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 nL(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=eL();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 mh(e,{offerId:t,choice:n}={},r=fh){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');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(f=>f!=="pr"&&(i.includes(f)||f==="push")),c=await u(e.id,Wo(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,Jb(i,{branch:a.branch??null,base:a.base??null,prNumber:a.pr?.number??null,offeredRebase:a.offeredRebase===!0}),{kind:"pr-go"})}if(await r.appendEvent({id:e.id,cwd:e.cwd},{type:"pr.exists.answered",actor:"user",source:"orchestrator",boxId:s.boxId,payload:{ts:Date.now(),offerId:t,choice:n}}),n!=="dismiss"&&a.notify===!0){let{queueShipCompleteNotify:u}=await r.sayDeps();u(e.id,{actions:l})}return{say:c,choice:n}}import An from"node:fs/promises";import Is from"node:path";import CT from"node:os";import ET from"node:fs/promises";import Ql from"node:path";import{execFile as rL}from"node:child_process";import{promisify as oL}from"node:util";import{fileURLToPath as sL}from"node:url";var aL=oL(rL),IT=Ql.dirname(sL(import.meta.url)),iL=Ql.resolve(IT,"..","..","client","dist"),cL="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",lL=new Map([...cL].map((e,t)=>[e,t]));function uL(e){let t=[],n=0,r=0;for(let o of e){let s=lL.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 dL(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=uL(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 fL(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 _T=new Map;async function pL(e){let t;try{t=await ET.stat(e)}catch{return null}let n=_T.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await ET.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:dL(r.mappings||"")};return _T.set(e,o),o}catch{return null}}var hh=null;function mL(){return hh||(hh=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):aL("git",["rev-parse","--short","HEAD"],{cwd:Ql.resolve(IT,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),hh}var hL=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function wL(e,{distDir:t=iL}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(hL)){let[a,i,c,l]=s,u=await pL(Ql.join(t,"assets",`${i}.map`)),f=u?fL(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(e.slice(r,s.index)),f&&u.sources[f[1]]){let d=u.sources[f[1]].replace(/^(\.\.\/)+/,"");n.push(`${d}:${f[2]+1}:${f[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 RT(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await mL();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await wL(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var gL=()=>process.env.BOSTRAT_STATE_DIR||Is.join(process.env.HOME||CT.tmpdir(),".bostrat"),kr=()=>Is.join(gL(),"telemetry"),MT=()=>Is.join(kr(),".relay-offsets.json"),yL=50,bL=14,kL=3e4,$T=500,dt=(e,t)=>typeof e=="string"?e.slice(0,t):"";function xL(e,t){if(!e||typeof e!="object")return null;let n=dt(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:dt(e.anonId,64),sessionId:dt(e.sessionId,64),userId:dt(e.userId,64),release:dt(e.release,64),appVersion:dt(e.appVersion,32),route:dt(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:dt(e.error.kind,256),message:dt(e.error.message,8192),stack:dt(e.error.stack,32768),fingerprint:dt(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:dt(s?.n,64),r:dt(s?.r,128)}))),o}var NT=e=>new Date(e).toISOString().slice(0,10),OT=0;async function PT(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,yL):[]).map(s=>xL(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await RT(s);await An.mkdir(kr(),{recursive:!0});let o=Is.join(kr(),`${NT(t)}.ndjson`);return await An.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
587
|
+
`).join(""),"utf8"),t-OT>6*3600*1e3&&(OT=t,SL(t).catch(()=>{})),r.length}async function SL(e=Date.now()){let t=NT(e-bL*24*3600*1e3),n=[];try{n=await An.readdir(kr())}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 An.rm(Is.join(kr(),s),{force:!0});let o=await DT();for(let s of r)delete o[s];await jT(o)}var eu=new Map;function BT(e,t=Date.now()){let n=e||"unknown",r=eu.get(n);return r||(eu.size>512&&eu.clear(),r={tokens:40,at:t},eu.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 DT(){try{return JSON.parse(await An.readFile(MT(),"utf8"))||{}}catch{return{}}}async function jT(e){await An.mkdir(kr(),{recursive:!0}),await An.writeFile(MT(),JSON.stringify(e),"utf8")}function vL(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||CT.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 TL({fetchImpl:e=fetch}={}){let t=await HS();if(!t)return{sent:0,disabled:!0};let n;try{n=(await An.readdir(kr())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await DT(),o=0;for(let s of n){let a=Is.join(kr(),s),i=r[s]||0,c;try{let d=await An.open(a,"r");try{let p=(await d.stat()).size;if(p<=i)continue;c=Buffer.alloc(p-i),await d.read(c,0,c.length,i)}finally{await d.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
|
|
588
|
+
`).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=$T){let p=f.slice(d,d+$T).map(w=>vL(w)),m=await e(`${t.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.ingestKey}`},body:JSON.stringify({events:p})}).catch(()=>null);if(!m||!m.ok)return{sent:o,disabled:!1};o+=p.length}r[s]=i+l+1,await jT(r)}return{sent:o,disabled:!1}}var wh=null,gh=!1;function LT(){wh||(wh=setInterval(async()=>{if(!gh){gh=!0;try{await TL()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{gh=!1}}},kL),wh.unref?.())}import nu from"node:fs/promises";import qT from"node:path";import AL from"node:os";var KT=()=>process.env.BOSTRAT_STATE_DIR||qT.join(process.env.HOME||AL.tmpdir(),".bostrat"),GT=()=>qT.join(KT(),"dictation-glossary.json"),EL=100,_L=500,IL=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],ru=null,yh=null,bh=Promise.resolve();function FT(){let e={},t=Date.now();for(let n of IL)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function VT(){return ru||(yh??=nu.readFile(GT(),"utf8").then(e=>JSON.parse(e)?.terms??FT()).catch(()=>FT()).then(e=>ru=e).finally(()=>yh=null),yh)}async function UT(){let e=JSON.stringify({terms:ru},null,2),t=GT(),n=`${t}.${process.pid}.tmp`;await nu.mkdir(KT(),{recursive:!0}),await nu.writeFile(n,e),await nu.rename(n,t)}function RL(){return bh=bh.then(UT,UT),bh.catch(()=>{})}async function kh(){return Object.values(await VT()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var $L=e=>Math.max(1,Math.ceil(e.length/4));async function OL(){let e=[],t=0;for(let{term:n}of await kh()){let r=$L(n);if(!(t+r>_L)&&(e.push(n),t+=r,e.length>=EL))break}return e}async function CL(e,t){let n=await VT(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var HT=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),tu=e=>e.toLowerCase();function WT(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function zT(e,t){let n=HT(e),r=HT(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 f=s-1;f>=0;f--)a[u][f]=tu(n[u])===tu(r[f])?a[u+1][f+1]+1:Math.max(a[u+1][f],a[u][f+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)tu(n[c])===tu(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(WT(r[l])&&i.add(r[l]),l++);for(;l<s;l++)WT(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await CL(u,"learned");return await RL(),[...i]}var wi=null;function ML(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 JT(){if(!wi)return null;let e=await ti();if(!e||e.apiKey.slice(-6)!==wi.keyTail)return null;let{keyTail:t,...n}=wi;return n}async function XT(e,t){let n=await ti();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 OL())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=ML(o.status,i);c.persistent&&(wi={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 wi=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}ue();var xh={"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 NL(e){if(!e)return null;let t=String(e).trim();if(xh[t])return t;let n=null;for(let r of Object.keys(xh))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function PL(e,t={}){let n=xh[NL(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 BL(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 gi=null;async function DL(){if(!gi)return null;let e=await Nl();if(!e||e.apiKey.slice(-6)!==gi.keyTail)return null;let{keyTail:t,...n}=gi;return n}async function jL(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=BL(t.status,o);s.persistent&&(gi={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}gi=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 LL={openai:jL};async function YT(){let e=await Nl();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await DL()}:{configured:!1}}async function ZT(){let e=await Nl();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 LL[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function QT(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=PL(t,s),i=await R(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}}var sA=we.dirname(UL(import.meta.url)),eA=Number(process.env.PORT)||3362,ou=bm(process.env),aA=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,Rs=zv(process.env.BOSTRAT_ALLOW_IPS),Eh=Jv(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Xl),iA=Yv(),cA=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",k=kt();k.disable("x-powered-by");k.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),Jl(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});k.use((e,t,n)=>{if(fi(e)||Rs.allowed(Rs.clientIp(e))||Eh.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});k.use(nT);k.use(eT({token:iA,enforce:cA}));k.use("/api/mocks",kt.json({limit:"1mb"}));k.use("/api/mocks",kt.raw({type:"image/png",limit:"24mb"}));k.use("/api/mocks",kt.raw({type:["video/mp4","video/webm"],limit:"105mb"}));k.use("/api/runbooks",kt.json({limit:"1mb"}));k.use("/api/tracks/adopt",kt.json({limit:"1mb"}));k.use("/api/telemetry",kt.json({limit:"256kb"}));k.use("/api/hook",kt.json({limit:"2mb"}));k.use("/api/dictation",kt.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));k.use("/api/tracks/:id/upload",kt.raw({type:()=>!0,limit:"25mb"}));k.use(kt.json({limit:"64kb"}));k.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:at,hostTmuxAlive:await Bn(),autoTriggerPct:aA})});k.get("/api/sessions",async(e,t)=>{t.json({sessions:await St()})});k.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 Dn(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})}});k.delete("/api/sessions/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ee(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 Nt(n),t.json({ok:!0})}catch(r){t.status(500).json({error:`failed to kill: ${r?.message||r}`})}});k.get("/api/context/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ee(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 Gs(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});k.post("/api/handoff/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!Ee(n))return t.status(400).json({error:"invalid session name"});try{let r=await py(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)})}});k.get("/api/handoff/:name",(e,t)=>{let n=decodeURIComponent(e.params.name),r=dc(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 tA=10;k.get("/api/idle-guard",async(e,t)=>{try{t.json(ym({tracks:await ie(),views:await Yc(),queuedOf:n=>Qr(n)}))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var En=no(),GL=(()=>{try{return KL(import.meta.url)("../package.json").version||null}catch{return null}})(),xr=OS({kind:En}),_h=MS({isBusy:async()=>ym({tracks:await ie(),views:await Yc(),queuedOf:e=>Qr(e)}).busy,onStuck:({to:e,from:t})=>cr({current:t,latest:e,installKind:En,policy:xr,status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?cr({current:t,latest:e,installKind:En,policy:xr,status:"updating",reason:"auto"}):cr({current:t,latest:e,installKind:En,policy:xr,status:"failed",error:n?.reason})).catch(()=>{})});k.get("/api/node/self-update",async(e,t)=>{t.json({installKind:En,policy:xr,current:GL,status:await ei(),armed:_h.armed})});k.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 vm({to:n,kind:En,reason:"manual"});if(r.ok)return _h.cancel(),cr({current:r.from,latest:n,installKind:En,policy:xr,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 lA(){try{let e=await CS();e&&await cr({current:e.from,latest:e.to,installKind:e.kind||En,policy:xr,status:e.status,error:e.error,reason:e.reason})}catch{}}lA();setInterval(lA,3e4).unref();k.get("/api/tracks",async(e,t)=>{let n=(await ie()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>tA){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,tA).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:Ka,agentAvailability:await ul(),tickets:await nv()})});k.get("/api/cost",async(e,t)=>{try{let n=await ie();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await Rv(n);if(r.cursorUsage=Ff(),e.query.format==="text")return t.type("text/plain").send($v(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/usage/roundup",async(e,t)=>{try{t.json(await Mv(await ie(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/usage/spend",async(e,t)=>{try{let n=await jv(await ie(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=Ff(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=fa(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/usage/caps",(e,t)=>{t.json(fa())});k.post("/api/usage/caps",async(e,t)=>{try{t.json(await xc(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});k.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await Wv(await ie(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await Oh(e.get(Si),{byToken:Fp,legacy:()=>Ta(e.body?.session_id,e.body?.cwd)});if(n==="refused")return Os("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return Os("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&&Os("mismatch",{track:r.track.id,box:r.box.name}),Os(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 Tv(e.body||{},()=>r);t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});k.post("/api/telemetry",async(e,t)=>{try{if(!BT(Rs.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await PT(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});k.get("/api/repos",async(e,t)=>{try{let n=await Xt(),r=new Set((await Promise.all(n.map(a=>pe(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await Bs()).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)})}});k.get("/api/github/token",async(e,t)=>{try{t.json(await Ii(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});k.get("/api/github/status",(e,t)=>{t.json({configured:_i(),calls:yk(),latency:Sk()})});k.post("/api/github/refresh",(e,t)=>{Uh(),t.json({ok:!0})});k.get("/api/gmail/search",async(e,t)=>{try{let n=await Kv(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)})}});k.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await Gv(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});function Ih(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}async function su({repo:e=null,number:t=null,branch:n=null}){let r=e?await pe(e):null;if(r&&t!=null){let s=await Xe(r,t),a=s&&await ee(s.trackId);if(a)return a}if(!n)return null;let o=await ie();return Uc(o,n,s=>Ih(s,e,r))}async function uA({repo:e=null,number:t=null,branch:n=null}){let r=await su({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!r.archivedAt&&!r.transferredTo)return r;if(!n)return null;let o=e?await pe(e).catch(()=>null):null,s=await ie();return Uc(s,n,a=>!a.archivedAt&&!a.transferredTo&&Ih(a,e,o))}async function $t(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await su({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await pe(s).catch(()=>null):null);await R({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 VL(e){let t=(await ie()).filter(n=>!n.archivedAt&&!n.transferredTo);for(let n of e??[]){if(!n.prs?.length)continue;let r=await pe(n.path);if(r)for(let o of n.prs){if(await Xe(r,o.number))continue;let s=await Uc(t,o.headRefName,a=>Ih(a,n.path,r));s&&await ct({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}k.get("/api/git/prs",async(e,t)=>{try{let n=await Rk();await VL(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.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:f}=e.body||{};try{if(s){let w=o||"merge";if(!ba.has(w))throw new Error(`invalid merge method: ${w}`);let y=Number(r);if(!Number.isInteger(y)||y<=0)throw new Error("invalid PR number");let b=await pe((await an(n)).path);if(!b)throw new Error("repo has no GitHub remote");let x=await su({repo:n,number:y,branch:l}).catch(()=>null);if(!x)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await ka(n,y),await ct({repo:b,number:y,trackId:x.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await ma(b,y,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await Hr(b,y,i===!0),await ha(b,y,c===!0&&i!==!0),jt(),await $t("auto-armed",{number:y,url:f,title:u,branch:l,repo:n,track:x,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:y,auto:!0})}let d=await Jo(n,r,{method:o||"squash"});await $t("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let p=await pe(n).catch(()=>null),m=!1;if(p&&(i===!0&&await Hr(p,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await ha(p,d.number,!0).catch(()=>{}),await ln(p,d.number).catch(()=>{}),m=await cn(p,d.number).catch(()=>!1),await Xn(p,d.number,"merged").catch(()=>{})),i===!0&&!m){let w=await su({repo:n,number:d.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await eo(w.id).catch(()=>{})}t.json(d)}catch(d){let p=d?.message||String(d),m=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(p)?400:502;t.status(m).json({error:p})}});k.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 pe((await an(n)).path).catch(()=>null),l=c?await wa(c,i).catch(()=>null):null;try{await vf(n,i)}catch(u){if(!l)throw u}jt(),await $t("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})}});k.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await pe(n).catch(()=>null),s=await Mk(n,{method:r||"merge",onMerged:a=>{$t("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&ln(o,a.number).catch(()=>{}).then(()=>cn(o,a.number)).catch(()=>{}).then(()=>Xn(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})}});k.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:Ck()})});k.post("/api/git/prs/merge-track",async(e,t)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=e.body||{};try{let i=a||"merge";if(!ba.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return t.status(400).json({error:"trackId required"});let c=await ee(n);if(!c)return t.status(404).json({error:`Track ${n} isn't on this node`});let l=(await Wr()).filter(d=>d.trackId===c.id&&d.state!=="merged"&&d.state!=="closed");if(!l.length)return t.status(409).json({error:"no open PRs on this Track"});for(let d of l)await Hr(d.repo,d.number,o===!0).catch(()=>{}),await ha(d.repo,d.number,s===!0&&o!==!0).catch(()=>{});let u=[],f=[];for(let d of l){let p={repo:d.repo,number:d.number,ok:!0,action:"armed"};u.push(p);try{let m=await Kr(d.repo);if(!m)throw new Error(`no local checkout for ${d.repo}`);let w=await zo(m,d.number);f.push({r:p,l:d,repoPath:m,pr:w,plan:$k(w,{update:r!==!1})})}catch(m){p.ok=!1,p.action="failed",p.reason=m?.message||String(m)}}for(let{r:d,l:p,repoPath:m,pr:w,plan:y}of Ok(f))try{if(y.action==="skip")d.action="skipped",d.reason=y.reason;else if(y.action==="merge")await Jo(m,p.number,{method:i}),await $t("merged",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:m,track:c}),await ln(p.repo,p.number).catch(()=>{}),await cn(p.repo,p.number).catch(()=>{}),await Xn(p.repo,p.number,"merged").catch(()=>{}),d.action="merged";else{if(y.action==="update-arm"&&(await ka(m,p.number).catch(()=>{}),d.action="updated-armed"),!await ma(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await $t("auto-armed",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:m,track:c,...d.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(b){d.ok=!1,d.action="failed",d.reason=b?.message||String(b)}if(jt(),!u.some(d=>d.ok)){if(o===!0)for(let d of l)await Hr(d.repo,d.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let d of l)await ha(d.repo,d.number,!1).catch(()=>{});return t.status(502).json({error:u.map(d=>`${d.repo}#${d.number}: ${d.reason}`).join("; "),results:u})}t.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;t.status(l).json({error:c})}});k.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 ee(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await Wr()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await wa(a.repo,a.number).catch(()=>null),c=await Kr(a.repo).catch(()=>null);c&&await vf(c,a.number).catch(()=>{}),i&&(await $t("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}))}jt(),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})}});k.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 ee(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=ut(i.workspace),f,d=Array.isArray(a)?a.filter(Boolean):[];if(d.length)f=d.map(w=>{let y=u.find(b=>b.name===w||b.repo===w||b.base===w||b.dir===w);if(!y)throw new Error(`invalid repo "${w}" \u2014 not tagged on this Track`);return y});else if(f=await es(i),!f.length)return t.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let w of f)try{let y=await Af(w.dir,{title:r,body:o,base:s}),b=w.repo||await pe(w.dir).catch(()=>null);b&&y.number&&await ct({repo:b,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await $t("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:b??w.name}}),p.push({repo:w.name,slug:b??null,ok:!0,url:y.url,number:y.number})}catch(y){p.push({repo:w.name,slug:w.repo??null,ok:!1,error:y?.message||String(y)})}let m=p.filter(w=>w.ok);return m.length?t.json({ok:!0,multi:!0,results:p,url:m[0].url,number:m[0].number}):t.status(502).json({error:p.map(w=>`${w.repo}: ${w.error}`).join("; "),results:p})}let c=await Af(i.cwd,{title:r,body:o,base:s}),l=await pe(i.cwd).catch(()=>null);l&&c.number&&await ct({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await $t("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})}});k.get("/api/tracks/:id/repos",async(e,t)=>{try{let n=await ee(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=ut(n.workspace).map(a=>{let i=o.get(a.base);return{name:a.name,base:a.base,baseRef:a.baseRef??null,repo:a.repo??null,dirty:i?.dirty??!1,ahead:i?.ahead??0,changed:!!i}});t.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.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 Yp(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})}});k.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await Zp(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})}});k.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await zo(n,r),s=await pe(n).catch(()=>null),a=await uA({repo:n,number:r,branch:o.headRefName});if(!a)return t.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=ut(a.workspace).find(b=>b.base===n||s&&b.repo===s)??null,c=a.workspace?.kind==="multi",l=await Nk(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?we.join("ci",i.name):"ci",f=we.join(a.cwd,".bostrat",u),d=we.join(".bostrat",u,`pr-${o.number}-fail.log`);await Ke.promises.mkdir(f,{recursive:!0}),await Ke.promises.writeFile(we.join(a.cwd,d),l.map(b=>`\u2550\u2550\u2550\u2550 ${b.name}${b.link?` \xB7 ${b.link}`:""} \u2550\u2550\u2550\u2550
|
|
525
589
|
${b.log??"(no fetchable log \u2014 see link)"}
|
|
526
590
|
`).join(`
|
|
527
|
-
`));let m=l.map(b=>b.name).join(", "),h=c&&i?` in the ${i.name}/ checkout`:"",w=await yt(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${h}. Failing check${l.length===1?"":"s"}: ${m}. The failure log is at ${d} 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 it({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Lb(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await It("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(b=>b.name)}}),t.json({ok:!0,leadBox:w.leadBox,queued:!!w.queued,checks:l.map(b=>b.name),log:d})}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})}});k.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await Fo(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await de(n).catch(()=>null),a=await HT({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=lt(a.workspace).find(f=>f.base===n||s&&f.repo===s)??null,c=a.workspace?.kind==="multi"&&i?` In the ${i.name}/ checkout:`:"",l=await yt(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 it({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Fb(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await It("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})}});k.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=No.filter(f=>e.body.actions.includes(f)),!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,a=await ee(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await B(a),c=i.find(f=>f.type==="pr.offered"&&f.payload?.offerId===n);if(!c)return t.status(404).json({error:"unknown offer"});if(i.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===n))return t.status(409).json({error:"offer already answered"});if(o?.includes("pr")){let f=await Zm(a,c,{actions:o,notify:s});if(f)return t.json({ok:!0,offerId:n,action:"pr-exists",actions:o,notify:s,existingPr:f.pr})}let l=null;o&&(l=await yt(a.id,Po(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 I({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&&Mp(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:/not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});k.post("/api/tracks/:id/pr-exists",async(e,t)=>{let{offerId:n,choice:r}=e.body||{};try{let o=await ee(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await Qm(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 li=e=>t=>({...t,url:DS(t,e.headers)});k.get("/api/views",async(e,t)=>{try{let n=await Lf({trackId:e.query.track||void 0});t.json({views:n.map(li(e)),maxLive:Qn})}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/views/:id",async(e,t)=>{let n=await fx(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:li(e)(n),session:Vo(n.slug)})});k.post("/api/views",async(e,t)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=e.body||{};try{let i=n?await ee(n):null;if(!i&&s&&(i=(await ce()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+me.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await Uf({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:li(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})}});k.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:li(e)(await px(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});k.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:li(e)(await mx(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})}});k.delete("/api/views/:id",async(e,t)=>{try{t.json(await hx(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var pL=[["linear",Jr,fm],["jira",gl,um]],ih=new Map;k.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await lm(),r={};for(let[o,s,a]of pL){let i=await s();if(!i){ih.delete(o),r[o]={configured:!1};continue}let c=ih.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},ih.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)})}});k.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await lm()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await mv(o),i=n?await ee(n):null;!i&&r&&(i=(await ce()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+me.sep)))||null),i&&await I({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 eo=async(e,t)=>{let n=e?await ee(e):null;return!n&&t&&(n=(await ce()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+me.sep)))||null),n};k.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await sm()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});k.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await eo(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 ls(o);await pl(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})}});k.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await eo(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await pl(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var WT=async(e,t,n)=>{if(e)return{ref:e,track:await eo(t,n)};let r=await eo(t,n),o=r?await im(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}};k.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 WT(o,n,r),l=await iv({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})}});k.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await WT(e.query.circuit,e.query.track,e.query.cwd);t.json(await am(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})}});k.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,alternatives:i,supersedesId:c}=e.body||{};try{let l=await eo(n,r);if(!l)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=await lv(l,{title:o,context:s,decision:a,alternatives:i,supersedesId:c});t.json({ok:!0,trackId:l.id,...u})}catch(l){let u=l?.message||String(l);t.status(/required/i.test(u)?400:502).json({error:u})}});k.get("/api/decisions",async(e,t)=>{try{let n=await eo(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await uv(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})}});k.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 eo(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await im(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 cv(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})}});k.post("/api/tickets/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=n?await ee(n):null;if(!s&&r&&(s=(await ce()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+me.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 fs(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})}});k.get("/api/tickets/status",async(e,t)=>{let{cwd:n,track:r}=e.query;try{let o=r?await ee(r):null;if(!o&&n&&(o=(await ce()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+me.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 $v(),ticket:await Vt(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});k.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await Vt(e.params.id)})});k.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await ee(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await fs(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})}});k.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await Tm(e.params.id)})});var Vl=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;k.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await Nv(e.params.id))}catch(n){let r=n?.message||String(n);t.status(Vl(r)).json({error:r})}});k.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await Am(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(Vl(r)).json({error:r})}});k.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await Pv(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(Vl(r)).json({error:r})}});k.post("/api/tracks/:id/ticket-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{let o=await ee(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await Bv(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(Vl(s)).json({error:s})}});k.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await Gp(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})}});k.post("/api/tickets/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await ee(n):null;if(!s&&r&&(s=(await ce()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+me.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 jv(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})}});k.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await Dv()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});k.post("/api/tracks/:id/ticket-create-offer",async(e,t)=>{let{proposalId:n,accept:r,teamId:o}=e.body||{};try{let s=await ee(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await Lv(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})}});k.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await Aa({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/mocks/:id",async(e,t)=>{let n=await dn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var ui={dotfiles:"allow"};k.get("/api/mocks/:id/png",async(e,t)=>{let n=await dn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=Et(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Sa(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,ui,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});k.get("/api/mocks/:id/media",async(e,t)=>{let n=await dn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=Et(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Sa(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,ui,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});k.get("/api/mocks/:id/html",async(e,t)=>{let n=await dn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=Et(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(wt(n,r.v).html,ui,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});k.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&qc(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 f=a?await ee(a):null;if(!f&&c&&(f=(await ce()).find(w=>w.cwd&&(c===w.cwd||c.startsWith(w.cwd+me.sep)))||null),!f)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=(f.boxes||[]).find(h=>h.name===f.leadBox),m=await $x({trackId:f.id,name:i,html:n?null:s.html,image:r,video:o,cwd:f.cwd,width:l,group:u,box:f.leadBox,agent:d?.agent||null});t.json({ok:!0,mock:m})}catch(f){let d=f?.message||String(f);t.status(/required|invalid|must be/i.test(d)?400:500).json({error:d})}});k.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&qc(n)?n:null;try{let o=await Ox(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})}});k.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await Nx(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await yt(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})}});k.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await Zx(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await Px(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})}});k.get("/api/flows",async(e,t)=>{try{t.json({flows:await Vx({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/flows/:id",async(e,t)=>{let n=await zx(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});k.post("/api/flows",async(e,t)=>{let{trackId:n,name:r,steps:o,cwd:s}=e.body||{};try{let a=n?await ee(n):null;if(!a&&s&&(a=(await ce()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+me.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 Jx({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})}});k.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await Yx(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})}});k.delete("/api/flows/:id",async(e,t)=>{try{t.json(await Xx(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});k.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await ce(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+me.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await a0({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)})}});k.get("/api/runbooks/:id",async(e,t)=>{let n=await tp(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});k.get("/api/runbooks/:id/md",async(e,t)=>{let n=await tp(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=o0(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 i0(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(hn(n,r.v),ui,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});k.get("/api/loops",async(e,t)=>{t.json(await Nc())});k.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 ce()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+me.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await Nc();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 bf({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})});k.post("/api/loops/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await ee(n):null;if(!s&&r&&(s=(await ce()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+me.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 HS(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});k.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 ee(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await WS(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})}});k.post("/api/runbooks",async(e,t)=>{let{trackId:n,name:r,md:o,cwd:s}=e.body||{};try{let a=n?await ee(n):null;if(!a&&s&&(a=(await ce()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+me.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 c0({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})}});k.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await l0(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})}});k.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await u0(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})}});k.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await d0(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});k.get("/api/changelog/draft",async(e,t)=>{try{let n=GS(e.query.since||null);t.json({since:n,repos:Gm(),prs:await VS(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});k.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=o?await ee(o):null;if(!a&&s&&(a=(await ce()).find(c=>c.cwd&&(s===c.cwd||String(s).startsWith(c.cwd+me.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,entry:await zS(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|no `- `|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});var mL=120,ch=400,Gl=null;k.post("/api/notify",async(e,t)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a}=e.body||{};try{let i=typeof r=="string"?r.trim():"";if(!i)return t.status(400).json({error:"body (the message) required"});if(i.length>ch)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${ch} cap; a notification is a nudge, put the detail in chat`});let c=Ea(`${n||""}
|
|
528
|
-
${i}`);if(c.length)return t.status(400).json({error:`notification appears to contain a secret VALUE (${c.join(", ")}) \u2014 notifications ship off-box; never put a credential in one`});let l=a?await ee(a):null;if(!l&&s&&(l=(await
|
|
591
|
+
`));let p=l.map(b=>b.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",w=await bt(a.id,`CI is red on PR #${o.number} "${o.title}" (${o.url}) \u2014 branch ${o.headRefName}${m}. Failing check${l.length===1?"":"s"}: ${p}. The failure log is at ${d} 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 ct({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await nk(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await $t("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(b=>b.name)}}),t.json({ok:!0,leadBox:w.leadBox,queued:!!w.queued,checks:l.map(b=>b.name),log:d})}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})}});k.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await zo(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await pe(n).catch(()=>null),a=await uA({repo:n,number:r,branch:o.headRefName});if(!a)return t.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=ut(a.workspace).find(f=>f.base===n||s&&f.repo===s)??null,c=a.workspace?.kind==="multi"&&i?` In the ${i.name}/ checkout:`:"",l=await bt(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 ct({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await rk(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await $t("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})}});k.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(f=>e.body.actions.includes(f)),!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,a=await ee(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await B(a),c=i.find(f=>f.type==="pr.offered"&&f.payload?.offerId===n);if(!c)return t.status(404).json({error:"unknown offer"});if(i.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===n))return t.status(409).json({error:"offer already answered"});if(o?.includes("pr")){let f=await ph(a,c,{actions:o,notify:s});if(f)return t.json({ok:!0,offerId:n,action:"pr-exists",actions:o,notify:s,existingPr:f.pr})}let l=null;o&&(l=await bt(a.id,Wo(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 R({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&&Gp(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:/not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});k.post("/api/tracks/:id/pr-exists",async(e,t)=>{let{offerId:n,choice:r}=e.body||{};try{let o=await ee(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await mh(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 yi=e=>t=>({...t,url:oT(t,e.headers)});k.get("/api/views",async(e,t)=>{try{let n=await Yc({trackId:e.query.track||void 0});t.json({views:n.map(yi(e)),maxLive:rr})}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/views/:id",async(e,t)=>{let n=await Rx(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:yi(e)(n),session:ts(n.slug)})});k.post("/api/views",async(e,t)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=e.body||{};try{let i=n?await ee(n):null;if(!i&&s&&(i=(await ie()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+we.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await ep({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:yi(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})}});k.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:yi(e)(await $x(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});k.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:yi(e)(await Ox(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})}});k.delete("/api/views/:id",async(e,t)=>{try{t.json(await Cx(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var zL=[["linear",ro,Om],["jira",Ml,Rm]],Sh=new Map;k.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await Im(),r={};for(let[o,s,a]of zL){let i=await s();if(!i){Sh.delete(o),r[o]={configured:!1};continue}let c=Sh.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},Sh.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)})}});k.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await Im()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await US(o),i=n?await ee(n):null;!i&&r&&(i=(await ie()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+we.sep)))||null),i&&await R({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 co=async(e,t)=>{let n=e?await ee(e):null;return!n&&t&&(n=(await ie()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+we.sep)))||null),n};k.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await Tm()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});k.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await co(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 ws(o);await Rl(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})}});k.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await co(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await Rl(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var dA=async(e,t,n)=>{if(e)return{ref:e,track:await co(t,n)};let r=await co(t,n),o=r?await Em(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}};k.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 dA(o,n,r),l=await NS({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})}});k.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await dA(e.query.circuit,e.query.track,e.query.cwd);t.json(await Am(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})}});k.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,alternatives:i,supersedesId:c}=e.body||{};try{let l=await co(n,r);if(!l)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=await BS(l,{title:o,context:s,decision:a,alternatives:i,supersedesId:c});t.json({ok:!0,trackId:l.id,...u})}catch(l){let u=l?.message||String(l);t.status(/required/i.test(u)?400:502).json({error:u})}});k.get("/api/decisions",async(e,t)=>{try{let n=await co(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await DS(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})}});k.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 co(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await Em(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 PS(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})}});k.post("/api/tickets/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=n?await ee(n):null;if(!s&&r&&(s=(await ie()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+we.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 bs(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})}});k.get("/api/tickets/status",async(e,t)=>{let{cwd:n,track:r}=e.query;try{let o=r?await ee(r):null;if(!o&&n&&(o=(await ie()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+we.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 rv(),ticket:await Jt(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});k.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await Jt(e.params.id)})});k.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await ee(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await bs(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})}});k.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await Wm(e.params.id)})});var iu=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;k.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await iv(e.params.id))}catch(n){let r=n?.message||String(n);t.status(iu(r)).json({error:r})}});k.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await qm(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(iu(r)).json({error:r})}});k.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await cv(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(iu(r)).json({error:r})}});k.post("/api/tracks/:id/ticket-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{let o=await ee(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await lv(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(iu(s)).json({error:s})}});k.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await sm(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})}});k.post("/api/tickets/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await ee(n):null;if(!s&&r&&(s=(await ie()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+we.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 dv(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})}});k.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await uv()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});k.post("/api/tracks/:id/ticket-create-offer",async(e,t)=>{let{proposalId:n,accept:r,teamId:o}=e.body||{};try{let s=await ee(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await fv(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})}});k.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await Ca({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/mocks/:id",async(e,t)=>{let n=await pn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var bi={dotfiles:"allow"};k.get("/api/mocks/:id/png",async(e,t)=>{let n=await pn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=_t(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=$a(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,bi,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});k.get("/api/mocks/:id/media",async(e,t)=>{let n=await pn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=_t(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=$a(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,bi,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});k.get("/api/mocks/:id/html",async(e,t)=>{let n=await pn(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=_t(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(gt(n,r.v).html,bi,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});k.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&tl(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 f=a?await ee(a):null;if(!f&&c&&(f=(await ie()).find(w=>w.cwd&&(c===w.cwd||c.startsWith(w.cwd+we.sep)))||null),!f)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=(f.boxes||[]).find(m=>m.name===f.leadBox),p=await Vx({trackId:f.id,name:i,html:n?null:s.html,image:r,video:o,cwd:f.cwd,width:l,group:u,box:f.leadBox,agent:d?.agent||null});t.json({ok:!0,mock:p})}catch(f){let d=f?.message||String(f);t.status(/required|invalid|must be/i.test(d)?400:500).json({error:d})}});k.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&tl(n)?n:null;try{let o=await zx(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})}});k.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await Yx(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await bt(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})}});k.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await h0(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await Zx(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})}});k.get("/api/flows",async(e,t)=>{try{t.json({flows:await u0({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.get("/api/flows/:id",async(e,t)=>{let n=await d0(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});k.post("/api/flows",async(e,t)=>{let{trackId:n,name:r,steps:o,cwd:s}=e.body||{};try{let a=n?await ee(n):null;if(!a&&s&&(a=(await ie()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+we.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 f0({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})}});k.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await p0(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})}});k.delete("/api/flows/:id",async(e,t)=>{try{t.json(await m0(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});k.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await ie(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+we.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await v0({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)})}});k.get("/api/runbooks/:id",async(e,t)=>{let n=await mp(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});k.get("/api/runbooks/:id/md",async(e,t)=>{let n=await mp(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=x0(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 T0(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(gn(n,r.v),bi,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});k.get("/api/loops",async(e,t)=>{t.json(await qc())});k.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 ie()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+we.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await qc();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 Mf({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})});k.post("/api/loops/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await ee(n):null;if(!s&&r&&(s=(await ie()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+we.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 lT(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});k.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 ee(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await uT(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})}});k.post("/api/runbooks",async(e,t)=>{let{trackId:n,name:r,md:o,cwd:s}=e.body||{};try{let a=n?await ee(n):null;if(!a&&s&&(a=(await ie()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+we.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 A0({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})}});k.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await E0(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})}});k.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await _0(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})}});k.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await I0(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});k.get("/api/changelog/draft",async(e,t)=>{try{let n=pT(e.query.since||null);t.json({since:n,repos:ih(),prs:await mT(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});k.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=o?await ee(o):null;if(!a&&s&&(a=(await ie()).find(c=>c.cwd&&(s===c.cwd||String(s).startsWith(c.cwd+we.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,entry:await hT(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|no `- `|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});var JL=120,vh=400,au=null;k.post("/api/notify",async(e,t)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a}=e.body||{};try{let i=typeof r=="string"?r.trim():"";if(!i)return t.status(400).json({error:"body (the message) required"});if(i.length>vh)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${vh} cap; a notification is a nudge, put the detail in chat`});let c=Ma(`${n||""}
|
|
592
|
+
${i}`);if(c.length)return t.status(400).json({error:`notification appears to contain a secret VALUE (${c.join(", ")}) \u2014 notifications ship off-box; never put a credential in one`});let l=a?await ee(a):null;if(!l&&s&&(l=(await ie()).find(m=>m.cwd&&(s===m.cwd||s.startsWith(m.cwd+we.sep)))||null),!l)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(p=>p.name===l.leadBox),f=(p,m)=>String(p).replace(/\s+/g," ").trim().slice(0,m),d={title:n?f(n,JL):l.name,body:f(i,vh),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await R(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:d}),t.json({ok:!0,notification:d,delivered:au?au.status().connected:null})}catch(i){t.status(500).json({error:i?.message||String(i)})}});k.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 XT(e.body,e.headers["content-type"]))}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});k.get("/api/dictation/glossary",async(e,t)=>{t.json({configured:!!await ti(),terms:await kh(),problem:await JT()})});k.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 zT(n,r)})});k.get("/api/tracks/:id/voice-chat",async(e,t)=>{if(!await ee(e.params.id))return t.status(404).json({error:"track not found"});t.json(await YT())});k.post("/api/tracks/:id/voice-chat/session",async(e,t)=>{try{let n=await ee(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 ZT())}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});k.post("/api/tracks/:id/voice-chat/usage",async(e,t)=>{try{let n=await ee(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 QT(n,{model:r,usage:o,seconds:s})})}catch(n){t.status(n?.status||500).json({error:n?.message||String(n)})}});k.post("/api/tracks",async(e,t)=>{let{name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i,ticket:c,circuit:l}=e.body||{};try{let u=await hl({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),f=null;if(c)try{await bs(u,c,{source:"create"})}catch(p){f=p?.message||String(p)}let d=null;if(l)try{await Rl(u,await ws(l),{source:"picker"})}catch(p){d=p?.message||String(p)}t.json({ok:!0,track:u,...f?{ticketWarning:f}:{},...d?{circuitWarning:d}:{}})}catch(u){let f=u?.message||String(u);t.status(/required|unknown agent|invalid|must be/i.test(f)?400:500).json({error:f})}});k.get("/api/tracks/:id",async(e,t)=>{let n=await ee(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({track:n,handoff:fA(hs(e.params.id)),subtasks:to(e.params.id).map(ki),pendingOffers:await Up(n)})});k.delete("/api/tracks/:id",async(e,t)=>{try{let n=await nm(e.params.id,{cascadeMocks:e.query.mocks!=="0",cascadeRunbooks:e.query.runbooks==="1"});await Wm(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})}});k.post("/api/tracks/:id/archive",async(e,t)=>{try{t.json({ok:!0,track:await eo(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})}});k.post("/api/tracks/:id/unarchive",async(e,t)=>{try{t.json({ok:!0,track:await rm(e.params.id)})}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})}});k.get("/api/tracks/:id/messages",async(e,t)=>{let n=await dl(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json(n)});k.get("/api/tracks/:id/events",async(e,t)=>{let n=await ee(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}
|
|
529
593
|
data: ${JSON.stringify(i)}
|
|
530
594
|
|
|
531
|
-
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await B(n))i.seq>o&&r(i);let s=
|
|
595
|
+
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await B(n))i.seq>o&&r(i);let s=vg(n.id,r),a=setInterval(()=>t.write(`: hb
|
|
532
596
|
|
|
533
|
-
`),25e3);e.on("close",()=>{clearInterval(a),s()})});k.get("/api/tracks/:id/search",async(e,t)=>{let n=await ee(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=
|
|
534
|
-
`);k.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await
|
|
597
|
+
`),25e3);e.on("close",()=>{clearInterval(a),s()})});k.get("/api/tracks/:id/search",async(e,t)=>{let n=await ee(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=ra(await B(n)),a=rc(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}))})});k.get("/api/memory",async(e,t)=>{t.json({stores:await nw()})});k.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 rw(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 XL=["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(`
|
|
598
|
+
`);k.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await bt(e.params.id,XL)})}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})}});k.post("/api/tracks/:id/lead/restart",async(e,t)=>{try{t.json({ok:!0,track:await yl(e.params.id)})}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}:{}})}});k.post("/api/tracks/:id/say",async(e,t)=>{let{text:n,attachments:r}=e.body||{};try{t.json(await bt(e.params.id,n,{attachments:r}))}catch(o){let s=o?.message||String(o);if(o?.usageCap)return t.status(429).json({error:"usage_cap",message:s,cap:o.usageCap});if(o?.transferredTo)return t.status(409).json({error:s,transferredTo:o.transferredTo});t.status(/not found/i.test(s)?404:/archived/i.test(s)?409:/required|not running/i.test(s)?400:500).json({error:s})}});k.post("/api/tracks/:id/upload",async(e,t)=>{try{let n=await ee(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 N0(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})}});k.get("/api/tracks/:id/uploads/:file",async(e,t)=>{try{let n=await ee(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=we.basename(String(e.params.file||""));if(!r||!gp(we.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=wp(n.cwd),s,a;try{s=await Ke.promises.realpath(o),a=await Ke.promises.realpath(we.join(o,r))}catch{return t.status(404).json({error:"upload not found"})}if(!a.startsWith(s+we.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 YL=/^[0-9a-fA-F-]{8,64}$/;k.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(e,t)=>{try{let n=await ee(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(!YL.test(o)||!Number.isInteger(s)||s<0)return t.status(400).json({error:"bad image ref"});let a=(n.boxes||[]).find(d=>d.name===r);if(!a||a.agent!=="claude"||!a.cwd||!a.sessionId)return t.status(404).json({error:"image source not available"});let i=en(a.cwd,a.sessionId);if(!i)return t.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=FL.createInterface({input:Ke.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let d of u){if(!d.includes(c))continue;let p;try{p=JSON.parse(d)}catch{continue}if(p.uuid===o){l=p,u.close();break}}let f=l?zu(l)[s]:null;if(!f||!/^image\//.test(f.mediaType))return t.status(404).json({error:"image not found"});t.set("content-type",f.mediaType),t.set("cache-control","private, max-age=31536000, immutable"),t.set("content-security-policy","sandbox"),t.send(Buffer.from(f.data,"base64"))}catch(n){t.status(500).json({error:n?.message||String(n)})}});k.post("/api/tracks/:id/interrupt",async(e,t)=>{try{t.json(await ml(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})}});k.delete("/api/tracks/:id/queue",async(e,t)=>{try{let{removed:n,restored:r,draftRev:o}=await qp(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)})}});k.post("/api/tracks/:id/answer-prompt",async(e,t)=>{let{promptId:n,answers:r,text:o}=e.body||{};try{t.json(await fl(e.params.id,{promptId:n,answers:r,text:o}))}catch(s){let a=s?.message||String(s),i=/not found/i.test(a)?404:/no pending prompt|changed since|not running|not supported|needs a code|look like a sign-in code|no answer selected/i.test(a)?409:500;t.status(i).json({error:a})}});k.post("/api/setup/:provider/start",async(e,t)=>{try{let n=await em({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})}});k.post("/api/tracks/:id/navigate-prompt",async(e,t)=>{let{promptId:n,questionIndex:r}=e.body||{};try{t.json(await Wp(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})}});k.put("/api/tracks/:id/draft",async(e,t)=>{let{text:n,rev:r}=e.body||{};try{t.json(await Jp(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})}});k.get("/api/tracks/:id/usage",async(e,t)=>{let n=await ee(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 Gl(r,{fresh:o}))});k.post("/api/tracks/:id/model",async(e,t)=>{let{model:n}=e.body||{};try{t.json(await Xp(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})}});k.post("/api/tracks/:id/subtask",async(e,t)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=e.body||{};try{let a=await hm(e.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});t.json({ok:!0,subtask:ki(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})}});k.get("/api/tracks/:id/subtasks",(e,t)=>{t.json({subtasks:to(e.params.id).map(ki)})});k.get("/api/tracks/:id/subtask/:sid/messages",async(e,t)=>{let n=await Hp(e.params.id,e.params.sid);if(!n)return t.status(404).json({error:"subtask not found"});t.json(n)});k.post("/api/tracks/:id/subtask/:sid/cancel",(e,t)=>{try{t.json({ok:!0,subtask:ki(vl(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});k.post("/api/tracks/:id/subtask/:sid/merge-now",(e,t)=>{try{t.json({ok:!0,subtask:ki(wm(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});function ki(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}k.post("/api/tracks/:id/handoff-lead",async(e,t)=>{let{toAgent:n,skipPermissions:r,reason:o}=e.body||{};try{let s=await kl(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})}});k.post("/api/tracks/:id/spin-up-view",async(e,t)=>{try{t.json(await Vp(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})}});k.get("/api/tracks/:id/handoff-lead",(e,t)=>{t.json(fA(hs(e.params.id))??{running:!1})});k.post("/api/tracks/:id/transfer-out",async(e,t)=>{try{let n=await lm(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})}});k.get("/api/tracks/:id/transfer-out",(e,t)=>{let n=cm(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??{}})});k.post("/api/tracks/:id/transfer-out/commit",async(e,t)=>{try{t.json(await xl(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})}});k.post("/api/tracks/adopt",async(e,t)=>{let{trackId:n}=e.body||{};try{t.json(await fm({...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 fA(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}k.get("/api/debug/handoff",async(e,t)=>{let n=Math.min(Number(e.query.limit)||80,500);t.json({verbose:Li(),paths:bw,trace:await yw({limit:n})})});var Th=we.resolve(sA,"..","..","client","dist");Ke.existsSync(Th)&&(k.use(kt.static(Th)),k.get(/^(?!\/api\/).*/,(e,t)=>t.sendFile(we.join(Th,"index.html"),bi)));var pA=HL.createServer(k),ZL=new qL({noServer:!0}),mA=(e,t,n)=>{if(!(fi(e)||Rs.allowed(Rs.clientIp(e))||Eh.allows(e))){t.write(`HTTP/1.1 403 Forbidden\r
|
|
535
599
|
\r
|
|
536
|
-
`),t.destroy();return}if(
|
|
600
|
+
`),t.destroy();return}if(rT(e,t,n))return;if(!tT(e,iA,{enforce:cA})){t.write(`HTTP/1.1 401 Unauthorized\r
|
|
537
601
|
\r
|
|
538
|
-
`),t.destroy();return}let r=
|
|
602
|
+
`),t.destroy();return}let r=Cw(e.url);if(!r.ok){t.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
539
603
|
\r
|
|
540
604
|
`),t.destroy();return}let o=r.sessionName;if(!Ee(o)){t.write(`HTTP/1.1 400 Bad Request\r
|
|
541
605
|
\r
|
|
542
|
-
`),t.destroy();return}let{cols:s,rows:a}=r;
|
|
606
|
+
`),t.destroy();return}let{cols:s,rows:a}=r;ZL.handleUpgrade(e,t,n,i=>Rw(i,o,{cols:s,rows:a}))};pA.on("upgrade",mA);var hA=process.env.BOSTRAT_CERT_DIR||we.join(process.env.HOME||Ah.tmpdir(),".bostrat","certs"),nA=Number(process.env.BOSTRAT_TLS_PORT)||3443,rA=we.join(hA,"bostrat.globopi.key"),oA=we.join(hA,"bostrat.globopi.crt");if(Ke.existsSync(rA)&&Ke.existsSync(oA)){let e=WL.createServer({key:Ke.readFileSync(rA),cert:Ke.readFileSync(oA)},k);e.on("upgrade",mA),e.on("error",t=>{console.error(`[bostrat-agent] https listener failed (${t?.code||t?.message}) \u2014 continuing HTTP-only`)}),e.listen(nA,ou,()=>{console.log(`[bostrat-agent] https listening on ${ou||"0.0.0.0"}:${nA}`)})}pA.listen(eA,ou,()=>{console.log(`[bostrat-agent] listening on ${ou||"0.0.0.0"}:${eA}`);let e=vS(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: ${at}`),console.log(`[bostrat-agent] auto-trigger default: ${aA}%`),console.log(`[bostrat-agent] ip allowlist: ${Rs.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(Eh.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),im().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),pm().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),om().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)),Qp().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)),nF(),tF(),vb({listTracks:ie,getTrack:ee}).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)),L0()&&F0({listTracks:ie}),ST(),AT(),pv({resolveTrack:ee}),vv({listTracks:ie,createTrack:hl,archiveTrack:eo,listRepos:Xt,resolveTrack:ee,sayToLead:bt,answerPrompt:fl,startLeadHandoff:kl,interruptLead:ml,listSubtaskJobs:to,cancelSubtask:vl,readEvents:B,appendEvent:R,divertShip:ph,answerPrExists:mh,knownAgents:Ka}),QL(),qs().then(t=>{t||console.warn(`[boot] no host tmux server on ${at} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),LT(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(au=IS({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||Ah.hostname(),name:process.env.BOSTRAT_NODE_NAME||Ah.hostname(),listTracks:ie,detectAgents:ul,sampleMetrics:Ev,onTracksTransferred:dm,onUpdateAvailable:async t=>{try{await cr({...t,policy:xr}),xr==="auto"&&!Il(await ei())&&_h.arm({to:t.latest,from:t.current,kind:En})}catch(n){console.error(`[plane-link] update notice failed: ${n?.message||n}`)}}}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),ho().catch(()=>{}),O0().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),mv().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),t0().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),y0().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),lk().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{au?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function QL(){eF(),Xc().catch(()=>{}),setInterval(()=>Xc().catch(()=>{}),1e4)}function eF(){for(let e of["bostrat-view","bostrat-mock","bostrat-flow","bostrat-ticket","bostrat-circuit","bostrat-blueprint","bostrat-decision","bostrat-runbook","bostrat-loop","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let t=we.resolve(sA,"..","bin",e),n=we.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!Ke.existsSync(t))continue;Ke.mkdirSync(n,{recursive:!0});let r=we.join(n,e);(Ke.existsSync(r)?Ke.readlinkSync(r):null)!==t&&(Ke.rmSync(r,{force:!0}),Ke.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 tF(){let e=!1,t=async()=>{if(!e){e=!0;try{let n=await ie(),r=await Fv({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 nF(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{let t=await ie();for(let n of t)n.archivedAt||n.transferredTo||!n.boxes?.some(r=>r.state==="live")||(await dl(n.id).catch(()=>{}),await Px(n).catch(()=>{}))}catch{}finally{e=!1}}},4e3)}
|