bostrat-node 0.2.17 → 0.2.19
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 +33 -33
- package/dist/index.js +147 -147
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,121 +1,121 @@
|
|
|
1
|
-
var
|
|
2
|
-
`).includes(a));if(!s.length)return;await
|
|
1
|
+
var Z0=Object.defineProperty;var af=(t,e)=>{for(var n in e)Z0(t,n,{get:e[n],enumerable:!0})};import ft from"node:path";import Gt from"node:fs";import TN from"node:readline";import{fileURLToPath as AN}from"node:url";import Ie from"express";import EN from"node:http";import _N from"node:https";import nf from"node:os";import{WebSocketServer as IN}from"ws";import{execFile as gS}from"node:child_process";import{promisify as yS}from"node:util";import oa from"node:fs";import{randomUUID as bS}from"node:crypto";import Vt from"node:fs/promises";import df from"node:os";import jt from"node:path";import{execFile as rx}from"node:child_process";import{promisify as ox}from"node:util";import{createHash as Q0,randomBytes as tx,timingSafeEqual as ex}from"node:crypto";var Ws="BOSTRAT_HOOK_TOKEN",qs="x-bostrat-hook";function cf(){return tx(32).toString("hex")}function go(t){let e=typeof t=="string"?t.trim():"";return e?Q0("sha256").update(e).digest("hex"):null}function lf(t,e){return typeof t!="string"||typeof e!="string"||t.length!==e.length||!t?!1:ex(Buffer.from(t),Buffer.from(e))}var rc=new Map,oc=new Map,nx=6e4;function Ks(t,e={},{now:n=Date.now(),log:r=console.warn}={}){if(rc.set(t,(rc.get(t)||0)+1),oc.has(t)&&n-oc.get(t)<nx)return!1;oc.set(t,n);let o=Object.entries(e).filter(([,s])=>s!=null&&s!=="").map(([s,a])=>`${s}=${a}`).join(" ");return r(`[hook-audit] ${t} n=${rc.get(t)}${o?` ${o}`:""}`),!0}async function uf(t,{byToken:e,legacy:n}){let r=typeof t=="string"?t.trim():"";if(!r)return{mode:"legacy",target:await n()};let o=await e(r);return o?.track?{mode:"token",target:o}:{mode:"refused",target:null}}function Gs(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function sx(){return process.env.CLAUDE_CONFIG_DIR?jt.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):jt.join(df.homedir(),".claude.json")}function ax(){return process.env.CLAUDE_CONFIG_DIR?jt.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):jt.join(df.homedir(),".claude","settings.json")}async function tn(t){if(!t||typeof t!="string")return;let e=jt.resolve(t),n=sx(),r=ax(),o={};try{o=JSON.parse(await Vt.readFile(n,"utf8"))}catch{}o.hasCompletedOnboarding=!0,o.theme||(o.theme="dark"),o.projects=o.projects||{},o.projects[e]=o.projects[e]||{};let s=o.projects[e];Array.isArray(s.allowedTools)||(s.allowedTools=[]),s.hasTrustDialogAccepted=!0,s.hasCompletedProjectOnboarding=!0;let a=process.env.ANTHROPIC_API_KEY;if(a){let c=a.slice(-20);o.customApiKeyResponses=o.customApiKeyResponses||{},o.customApiKeyResponses.approved=o.customApiKeyResponses.approved||[],o.customApiKeyResponses.rejected=o.customApiKeyResponses.rejected||[],o.customApiKeyResponses.approved.includes(c)||o.customApiKeyResponses.approved.push(c)}await Vt.mkdir(jt.dirname(n),{recursive:!0}),await Vt.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await Vt.readFile(r,"utf8"))}catch{}if(i.skipDangerousModePermissionPrompt=!0,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 "${qs}: $${Ws}" --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 Vt.mkdir(jt.dirname(r),{recursive:!0}),await Vt.writeFile(r,JSON.stringify(i,null,2)),await ix(e)}async function ix(t){try{let e=jt.resolve(t),n=jt.join(e,".bostrat","memory","claude"),r=jt.join(e,".claude","settings.local.json"),o={};try{o=JSON.parse(await Vt.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(jt.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await Vt.mkdir(n,{recursive:!0}),await Vt.mkdir(jt.dirname(r),{recursive:!0}),await Vt.writeFile(r,JSON.stringify(o,null,2)),await cx(e,["/.claude/settings.local.json"])}catch{}}async function cx(t,e){try{let{stdout:n}=await ox(rx)("git",["-C",t,"rev-parse","--git-path","info/exclude"]),r=jt.resolve(t,n.trim()),o=await Vt.readFile(r,"utf8").catch(()=>""),s=e.filter(a=>!o.split(`
|
|
2
|
+
`).includes(a));if(!s.length)return;await Vt.mkdir(jt.dirname(r),{recursive:!0}),await Vt.appendFile(r,(o&&!o.endsWith(`
|
|
3
3
|
`)?`
|
|
4
4
|
`:"")+s.join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`)}catch{}}import
|
|
6
|
+
`)}catch{}}import sc from"node:fs/promises";import lx from"node:os";import Vs from"node:path";function ux(){let t=process.env.CODEX_HOME||Vs.join(lx.homedir(),".codex");return Vs.join(t,"config.toml")}function ff(t){return`[projects."${t}"]`}function dx(t,e){let n=ff(e),r=t.indexOf(n);if(r===-1)return!1;let o=t.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 dr(t){if(!t||typeof t!="string")return;let e=Vs.resolve(t),n=ux(),r="";try{r=await sc.readFile(n,"utf8")}catch{}if(dx(r,e))return;let o=ff(e);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")}import j from"node:fs/promises";import
|
|
13
|
+
`:"")}try{await sc.mkdir(Vs.dirname(n),{recursive:!0}),await sc.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${e}: ${s?.message||s}`)}}import pf from"node:fs/promises";import fx from"node:os";import yo from"node:path";function px(t){let n=yo.resolve(t).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||yo.join(fx.homedir(),".cursor");return yo.join(r,"projects",n)}async function fr(t){if(!t||typeof t!="string")return;let e=yo.resolve(t),n=px(e),r=yo.join(n,".workspace-trusted");await pf.mkdir(n,{recursive:!0}),await pf.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:e},null,2)}
|
|
14
|
+
`,"utf8")}import j from"node:fs/promises";import Fx from"node:os";import Z from"node:path";import{execFile as Ux}from"node:child_process";import{promisify as Hx}from"node:util";import Tx from"node:fs";import hf from"node:fs/promises";import ko from"node:path";import Ax from"node:os";import{fileURLToPath as Ex}from"node:url";import{execFile as _x}from"node:child_process";import{promisify as Ix}from"node:util";import ic from"node:fs/promises";import en from"node:path";import{execFile as mx}from"node:child_process";import{promisify as hx}from"node:util";var wx=hx(mx),mf=process.env.HOME||"/home/globo",gx=en.join(process.env.BOSTRAT_STATE_DIR||en.join(mf,".bostrat"),"repos"),yx=[...(process.env.BOSTRAT_REPOS_ROOT||en.join(mf,"Documents")).split(","),gx].map(t=>t.trim()).filter(Boolean),bx=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,kx=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);async function xx(t){try{return(await ic.stat(en.join(t,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function Sx(t){let e=i=>ic.readFile(en.join(t,".git",i),"utf8").then(c=>c.trim()).catch(()=>null),r=(await e("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 e(`refs/heads/${i}`)||(o??=await e("packed-refs")||"",new RegExp(`^[0-9a-f]{40} refs/heads/${i}$`,"m").test(o)))return i;let a=(await e("HEAD"))?.match(/^ref:\s*refs\/heads\/(.+)/);return a?a[1].trim():null}async function Re(){let t=new Map;async function e(o,s){if(t.has(o))return;let a=await xx(o);if(a==="worktree")return;if(a==="repo"){t.set(o,{path:o,name:en.basename(o),branch:await Sx(o)});return}if(s>=bx)return;let i;try{i=await ic.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||kx.has(c.name)||await e(en.join(o,c.name),s+1)}for(let o of yx)await e(en.resolve(o),0);let n=new Set,r=[];for(let o of t.values()){let s=(await ot(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 vx(t){let e=String(t||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return e?`${e[1]}/${e[2]}`:null}var ac=new Map;async function ot(t){if(!t)return null;if(ac.has(t))return ac.get(t);let e=null;try{let{stdout:n}=await wx("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});e=vx(n)}catch{e=null}return ac.set(t,e),e}async function bo(t){let e=String(t||"").toLowerCase();if(!e)return null;for(let n of await Re())if((await ot(n.path))?.toLowerCase()===e)return n.path;return null}var wf=Ix(_x),Rx=process.env.BOSTRAT_STATE_DIR||ko.join(process.env.HOME||Ax.tmpdir(),".bostrat"),$x=ko.join(Rx,"repos"),gf=/^[^/\s]+\/[^/\s]+$/,Ox=5*6e4,Mx=6e4,Cx=10*6e4,yf=fetch,cc=new Map,Nn=null;function te(t=process.env){let e=t.BOSTRAT_PLANE_URL||"";if(!e)return null;try{let n=new URL(e);return n.protocol=n.protocol==="ws:"?"http:":"https:",n.origin}catch{return null}}function zs(t=process.env){return!!(te(t)&&t.BOSTRAT_PLANE_TOKEN)}var bf=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),pr=(t,e)=>Object.assign(new Error(t),{status:e});async function Js(t,{env:e=process.env}={}){let n=String(t||"").toLowerCase();if(!gf.test(n))throw pr("repo must be owner/name",400);let r=cc.get(n);if(r&&r.expiresAt-Date.now()>Ox)return r;if(!zs(e))throw pr("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await yf(`${te(e)}/v1/github/token?repo=${encodeURIComponent(t)}`,{headers:bf(e)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw pr(`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 pr(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 cc.set(n,a),a}async function xo({env:t=process.env,force:e=!1}={}){if(!zs(t))return{configured:!1,repos:[],username:null};if(!e&&Nn&&Date.now()-Nn.at<Mx)return Nn.data;try{let n=await yf(`${te(t)}/v1/github/repos`,{headers:bf(t)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Nn={at:Date.now(),data:r},r}catch(n){return Nn?Nn.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function kf(){cc.clear(),Nn=null}async function de({cwd:t=null,slug:e=null,env:n=process.env}={}){try{let r=e||(t?await ot(t):null);if(!r)return{};let{token:o}=await Js(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function lc({env:t=process.env}={}){return(await xo({env:t})).username??null}var Nx=()=>Ex(new URL("../bin/bostrat-git-credential",import.meta.url)),Px=(t=process.env)=>`http://127.0.0.1:${Number(t.PORT)||3362}`;function Dx(t){let[e,n]=String(t).split("/");return ko.join($x,e,n)}async function uc(t,{env:e=process.env}={}){let n=(...r)=>wf("git",["-C",t,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${Nx()}" ${Px(e)}`),await n("config","credential.useHttpPath","true")}async function dc(t,{env:e=process.env,baseUrl:n="https://github.com"}={}){if(!gf.test(String(t||"")))throw pr(`bad repo slug "${t}"`,400);let r=Dx(t);if(Tx.existsSync(ko.join(r,".git")))return r;let{token:o}=await Js(t,{env:e});await hf.mkdir(ko.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await wf("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${t}.git`,r],{timeout:Cx,env:{...e,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await hf.rm(r,{recursive:!0,force:!0}).catch(()=>{}),pr(`clone of ${t} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await uc(r,{env:e}),r}var Bx=6e4,jx=5e3,fc=new Map,Lx=(...t)=>fetch(...t);function xf(t=process.env){return!!(te(t)&&t.BOSTRAT_PLANE_TOKEN)}var Sf=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function vf(t,e,n){let r=te(n),o=new AbortController,s=setTimeout(()=>o.abort(),jx);try{return await Lx(`${r}${t}`,{...e,signal:o.signal})}finally{clearTimeout(s)}}async function Tf(t,{env:e=process.env,force:n=!1}={}){if(!xf(e))return{facts:[],source:"unconfigured"};let r=fc.get(t);if(!n&&r&&Date.now()-r.at<Bx)return{facts:r.facts,source:"cache"};try{let o=await vf(`/v1/memory/${encodeURIComponent(t)}`,{headers:Sf(e)},e);if(!o.ok)throw new Error(`plane ${o.status}`);let s=await o.json(),a=Array.isArray(s?.facts)?s.facts:[];return fc.set(t,{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 Xs(t,e,{env:n=process.env}={}){if(!xf(n))return{pushed:0,source:"unconfigured"};if(!e?.length)return{pushed:0,source:"empty"};try{let r=await vf(`/v1/memory/${encodeURIComponent(t)}`,{method:"POST",headers:{...Sf(n),"content-type":"application/json"},body:JSON.stringify({facts:e})},n);if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json(),s=Array.isArray(o?.results)?o.results:[];return fc.delete(t),{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 Wx=Hx(Ux),pc=process.env.BOSTRAT_STATE_DIR||Z.join(process.env.HOME||Fx.tmpdir(),".bostrat"),Af=200,qx=25*1024;function Ys(){return Z.join(pc,"memory","repos")}var $e="operator";function Pn(t){return t===$e?Z.join(pc,"memory",$e):Z.join(Ys(),t)}var Rf=".tombstones.json";async function So(t){try{return JSON.parse(await j.readFile(Z.join(t,Rf),"utf8"))}catch{return{}}}async function mc(t,e){let n=await So(t);for(let[r,o]of e)n[r]=Math.max(Number(n[r])||0,o);await j.mkdir(t,{recursive:!0}),await j.writeFile(Z.join(t,Rf),JSON.stringify(n))}function Ef(t){let e=String(t||"").trim();if(!e)return null;let r=(e.replace(/\/+$/,"").split(/[/:]/).pop()||"").replace(/\.git$/i,"").toLowerCase();return/^[a-z0-9][a-z0-9._-]*$/.test(r)?r:null}async function _f(t){try{let{stdout:e}=await Wx("git",["-C",t,"config","--get","remote.origin.url"]);return e.trim()||null}catch{return null}}async function hc(t){if(!t)return[];let e=Ef(await _f(t));if(e)return[e];let n=[],r=[];try{r=await j.readdir(t,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=Ef(await _f(Z.join(t,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function Zs(t){let e=/^---\n([\s\S]*?)\n---\n?/.exec(t);return e?{fm:e[1],body:t.slice(e[0].length)}:{fm:null,body:t}}function nn(t,e){let n=new RegExp(`^\\s*${e}:\\s*(.+?)\\s*$`,"m").exec(t||"");return n?n[1].replace(/^"|"$/g,""):null}async function fe(t,e=null){let n=e??await j.readFile(t,"utf8").catch(()=>""),{fm:r}=Zs(n),o=nn(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await j.stat(t)).mtimeMs}catch{return 0}}function $f(t,e){let n=nn(t,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===$e){let o=nn(t,"trust");if(!o||/^operator/i.test(o))return $e}return e}var mr=t=>/\]\(([^)]+\.md)\)/.exec(t)?.[1]||null;function wc(t,e,n,r=new Map){let o=(t||"").split(`
|
|
15
15
|
`),s=new Map;for(let c of(e||"").split(`
|
|
16
|
-
`)){let l=
|
|
16
|
+
`)){let l=mr(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=mr(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
|
|
20
|
-
`)){let r=
|
|
19
|
+
`)}function Of(t,e){let n=nn(e,"name")||t.replace(/\.md$/,""),r=nn(e,"description")||"";return`- [${n}](${t})${r?` \u2014 ${r}`:""}`}async function vo(t){let e=[];try{e=await j.readdir(t,{withFileTypes:!0})}catch{return[]}return e.filter(n=>n.isFile()&&n.name.endsWith(".md")&&n.name!=="MEMORY.md").map(n=>n.name)}async function Mf(t,e){let n=Z.join(t,".bostrat","memory"),r=await vo(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=Z.join(n,l),f;try{f=await j.readFile(u,"utf8")}catch{continue}let{fm:d}=Zs(f),p=$f(d,e);if(!p)continue;let m=Pn(p),w=Z.join(m,l),y=await fe(u,f);if(i.has(m)||i.set(m,await So(m)),(i.get(m)[l]||0)>=y)continue;let b=await fe(w).catch(()=>0);if(await j.access(w).then(()=>!0).catch(()=>!1)&&b>=y)continue;await j.mkdir(m,{recursive:!0}),await j.writeFile(w,f),a++;let _=o.get(m);_||o.set(m,_={won:new Set,generated:new Map}),_.won.add(l),_.generated.set(l,Of(l,d)),s.has(p)||s.set(p,new Set),s.get(p).add(l)}let c=await j.readFile(Z.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[l,u]of o){let f=Z.join(l,"MEMORY.md"),d=await j.readFile(f,"utf8").catch(()=>"");await j.writeFile(f,wc(d,c,u.won,u.generated))}return{harvested:a,changedByKey:s}}async function Kx(){let t=Z.join(pc,"workspaces"),e=[];try{e=await j.readdir(t,{withFileTypes:!0})}catch{return{harvested:0}}let n=0,r=new Map;for(let o of e){if(!o.isDirectory())continue;let s=Z.join(t,o.name);if(!await j.access(Z.join(s,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[i]=await hc(s),c=await Mf(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}}var Gx=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,Vx=t=>{let e=new Map;for(let n of(t||"").split(`
|
|
20
|
+
`)){let r=mr(n);r&&n.trim().startsWith("-")&&e.set(r,n)}return e};async function zx(t,e){let n=Pn(t),r=await So(n),o=new Set,s=new Map,a=[],i=0;for(let c of e||[]){let l=String(c?.file||"");if(!Gx.test(l)||l==="MEMORY.md")continue;let u=Z.join(n,l),f=await j.access(u).then(()=>!0).catch(()=>!1),d=f?await fe(u):0,p=Number(c.modified)||0;if(c.deleted){p>d&&(f&&await j.rm(u,{force:!0}),await mc(n,[[l,p]]),f&&(a.push(l),i++));continue}f&&d>=p||(r[l]||0)>=p||(await j.mkdir(n,{recursive:!0}),await j.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=Z.join(n,"MEMORY.md"),l=await j.readFile(c,"utf8").catch(()=>""),u=wc(l,[...s.values()].join(`
|
|
21
21
|
`),o);if(a.length){let f=new Set(a);u=u.split(`
|
|
22
|
-
`).filter(d=>!f.has(
|
|
23
|
-
`)}await j.mkdir(n,{recursive:!0}),await j.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function
|
|
24
|
-
`);e.length>
|
|
25
|
-
`);for(;Buffer.byteLength(n,"utf8")>
|
|
26
|
-
`);return n}var
|
|
22
|
+
`).filter(d=>!f.has(mr(d))).join(`
|
|
23
|
+
`)}await j.mkdir(n,{recursive:!0}),await j.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function gc(t,e=null){let n=Pn(t),r=e?.length?e:await vo(n);if(!r.length)return{pushed:0,source:"empty"};let o=Vx(await j.readFile(Z.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=Z.join(n,a),c;try{c=await j.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await fe(i,c),indexLine:o.get(a)??null})}return Xs(t,s)}async function Jx(t){let{facts:e,source:n}=await Tf(t);e.length?await zx(t,e):n==="plane"&&await gc(t)}function Xx(t){let e=t.split(`
|
|
24
|
+
`);e.length>Af&&(e=e.slice(0,Af));let n=e.join(`
|
|
25
|
+
`);for(;Buffer.byteLength(n,"utf8")>qx&&e.length;)e.pop(),n=e.join(`
|
|
26
|
+
`);return n}var Yx=`# Track memory
|
|
27
27
|
|
|
28
28
|
Shared Track memory (all providers) lives one level up: read \`../MEMORY.md\` for the
|
|
29
29
|
index and the fact files beside it. Facts you save here stay Claude-local but ride the
|
|
30
30
|
same sync; prefer adding durable cross-provider facts to \`../\` following its format.
|
|
31
|
-
`;async function
|
|
31
|
+
`;async function Zx(t,e){if(!t||!e?.length)return{materialized:!1,reason:"no-repo-key"};let n=Z.join(t,".bostrat","memory"),r=0,o=0,s=[];for(let u of e){let f=Pn(u),d=await So(f);for(let[y,b]of Object.entries(d)){let S=Z.join(n,y);await j.access(S).then(()=>!0).catch(()=>!1)&&await fe(S)<=b&&(await j.rm(S,{force:!0}),o++)}let p=await vo(f);if(!p.length)continue;await j.mkdir(n,{recursive:!0});for(let y of p){let b=Z.join(f,y),S=Z.join(n,y),_=await fe(b),O=await fe(S).catch(()=>0);await j.access(S).then(()=>!0).catch(()=>!1)&&O>=_||(await j.copyFile(b,S),r++)}let m=await j.readFile(Z.join(f,"MEMORY.md"),"utf8").catch(()=>""),w=u===$e?"## operator memory":`## repo: ${u}`;m.trim()&&s.push(e.length>1?`${w}
|
|
32
32
|
|
|
33
33
|
${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(`
|
|
34
34
|
|
|
35
35
|
`)+`
|
|
36
|
-
`,c=
|
|
37
|
-
`).filter(p=>!s.has(
|
|
38
|
-
`);await j.writeFile(c,u).catch(()=>{});let f=
|
|
39
|
-
`).filter(l=>
|
|
40
|
-
`)).catch(()=>{});let c=await
|
|
41
|
-
`,n!=null?{mode:n}:void 0),await
|
|
42
|
-
`,"utf8");let n=(await
|
|
43
|
-
`).filter(Boolean);n.length>
|
|
36
|
+
`,c=Xx(i);await j.mkdir(n,{recursive:!0}),await j.writeFile(Z.join(n,"MEMORY.md"),c);let l=Z.join(n,"claude");return await j.mkdir(l,{recursive:!0}),await j.writeFile(Z.join(l,"MEMORY.md"),Yx,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}var If=0,Qx=3e4;async function Cf(t){try{let e=await hc(t);if(!e.length)return{materialized:!1,reason:"no-repo-key"};let n=[...e,$e];if(Date.now()-If>Qx){If=Date.now(),await tS();let{changedByKey:r}=await Kx();for(let[o,s]of r)await gc(o,[...s])}for(let r of n)await Jx(r);return await Zx(t,n)}catch(e){return{materialized:!1,reason:"error",error:e?.message||String(e)}}}async function tS(){let t=0,e=[];try{e=await j.readdir(Ys(),{withFileTypes:!0})}catch{return{moved:t}}for(let n of e){if(!n.isDirectory())continue;let r=Z.join(Ys(),n.name),o=Pn($e),s=new Set,a=new Map;for(let p of await vo(r)){let m=Z.join(r,p),w;try{w=await j.readFile(m,"utf8")}catch{continue}let{fm:y}=Zs(w);if($f(y,n.name)!==$e)continue;let b=Z.join(o,p),S=await fe(b).catch(()=>0),_=await fe(m,w);await j.mkdir(o,{recursive:!0}),_>S&&await j.writeFile(b,w),await j.rm(m,{force:!0}),s.add(p),a.set(p,Of(p,y)),t++}if(!s.size)continue;let i=Date.now();await mc(r,[...s].map(p=>[p,i])),await Xs(n.name,[...s].map(p=>({file:p,deleted:!0,modified:i})));let c=Z.join(r,"MEMORY.md"),l=await j.readFile(c,"utf8").catch(()=>""),u=l.split(`
|
|
37
|
+
`).filter(p=>!s.has(mr(p))).join(`
|
|
38
|
+
`);await j.writeFile(c,u).catch(()=>{});let f=Z.join(o,"MEMORY.md"),d=await j.readFile(f,"utf8").catch(()=>"");await j.writeFile(f,wc(d,l,s,a))}return{moved:t}}async function Nf(){let t=[],e=[$e];try{for(let n of await j.readdir(Ys(),{withFileTypes:!0}))n.isDirectory()&&e.push(n.name)}catch{}for(let n of e){let r=Pn(n),o=await vo(r),s=await So(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await j.readFile(Z.join(r,i),"utf8").catch(()=>""),{fm:l}=Zs(c);a.push({file:i,name:nn(l,"name")||i.replace(/\.md$/,""),description:nn(l,"description")||"",scope:nn(l,"scope")||null,modified:await fe(Z.join(r,i),c)})}t.push({key:n,facts:a,tombstones:Object.keys(s).length})}return t}async function Pf(t,e){if(!/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/.test(e)||e==="MEMORY.md")throw new Error("bad fact filename");let n=Pn(t),r=Z.join(n,e);if(!await j.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${e} not found in ${t}`);let s=Date.now();await j.rm(r,{force:!0}),await mc(n,[[e,s]]);let a=Z.join(n,"MEMORY.md"),i=await j.readFile(a,"utf8").catch(()=>"");await j.writeFile(a,i.split(`
|
|
39
|
+
`).filter(l=>mr(l)!==e).join(`
|
|
40
|
+
`)).catch(()=>{});let c=await Xs(t,[{file:e,deleted:!0,modified:s}]);return{deleted:e,key:t,plane:c.source}}async function Df(t){try{let e=await hc(t),{harvested:n,changedByKey:r}=await Mf(t,e[0]||null);for(let[o,s]of r)await gc(o,[...s]);return{harvested:n}}catch(e){return{harvested:0,error:e?.message||String(e)}}}import xc from"node:fs/promises";import Kf from"node:path";import Lf from"node:fs/promises";import Ff from"node:os";import Uf from"node:path";import yc from"node:fs/promises";import eS from"node:path";var nS=0;async function Lt(t,e,{mode:n}={}){await yc.mkdir(eS.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.${++nS}.tmp`;await yc.writeFile(r,JSON.stringify(e,null,2)+`
|
|
41
|
+
`,n!=null?{mode:n}:void 0),await yc.rename(r,t)}var rS=1e4,oS=3e4,Bf=2e3,sS=()=>process.env.BOSTRAT_STATE_DIR||Uf.join(process.env.HOME||Ff.tmpdir(),".bostrat"),Hf=()=>Uf.join(sS(),"content-outbox.json"),aS=()=>process.env.BOSTRAT_NODE_ID||Ff.hostname(),iS=(...t)=>fetch(...t),cS=oS;function wt(t=process.env){return!!(te(t)&&t.BOSTRAT_PLANE_TOKEN)}var Ao=(t=process.env)=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`});async function Eo(t,e={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),rS);try{return await iS(`${te(n)}${t}`,{...e,signal:r.signal})}finally{clearTimeout(o)}}var ta=null,bc=null,Qs=null,To=null;async function Wf(){return ta||(bc||=Lf.readFile(Hf(),"utf8").then(t=>JSON.parse(t)).catch(()=>({ops:[]})).then(t=>ta={ops:Array.isArray(t?.ops)?t.ops:[]}).finally(()=>bc=null),bc)}async function qf(){await Lt(Hf(),ta).catch(()=>{})}function jf(t){return t.op==="blob"?`blob ${t.family} ${t.id} ${t.v} ${t.kind}`:`${t.op} ${t.family} ${t.id}`}async function kc(t){let e=await Wf(),n=jf(t);if(t.op==="delete")e.ops=e.ops.filter(r=>!(r.family===t.family&&r.id===t.id)),e.ops.push(t);else{e.ops=e.ops.filter(o=>!(o.op==="delete"&&o.family===t.family&&o.id===t.id));let r=e.ops.findIndex(o=>jf(o)===n);r>=0?e.ops[r]=t:e.ops.push(t)}e.ops.length>Bf&&(e.ops=e.ops.slice(-Bf)),await qf()}function lS(){Qs||(Qs=setTimeout(()=>{Qs=null,hr().catch(()=>{})},cS),Qs.unref?.())}async function uS(t,e=process.env){if(t.op==="record"){let n=await Eo(`/v1/content/${encodeURIComponent(t.family)}`,{method:"POST",headers:{...Ao(e),"content-type":"application/json"},body:JSON.stringify({records:[{id:t.id,trackId:t.trackId??null,data:t.data,updatedAt:t.updatedAt,originNode:aS()}]})},e);if(!n.ok)throw new Error(`plane ${n.status}`);let o=(await n.json().catch(()=>null))?.results?.[0];return o?.outcome==="rejected"?{delivered:!1,rejected:o.reason}:{delivered:!0}}if(t.op==="blob"){let n=await Lf.readFile(t.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await Eo(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}/blob?v=${t.v}&kind=${encodeURIComponent(t.kind)}`,{method:"POST",headers:{...Ao(e),"content-type":"application/octet-stream"},body:n},e);if(!r.ok&&r.status!==400)throw new Error(`plane ${r.status}`);return r.status===400?{delivered:!1,rejected:(await r.json().catch(()=>null))?.error||"rejected"}:{delivered:!0}}if(t.op==="delete"){let n=await Eo(`/v1/content/${encodeURIComponent(t.family)}/${encodeURIComponent(t.id)}?deletedAt=${t.deletedAt}`,{method:"DELETE",headers:Ao(e)},e);if(!n.ok)throw new Error(`plane ${n.status}`);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function hr({env:t=process.env,log:e=console}={}){return wt(t)?To||(To=(async()=>{let n=await Wf(),r=0;for(;n.ops.length;){let o=n.ops[0],s;try{s=await uS(o,t)}catch(a){return lS(),{flushed:r,pending:n.ops.length,error:a?.message||String(a)}}s.rejected&&e.error?.(`[content-plane] ALARM plane rejected ${o.op} ${o.family}/${o.id}: ${s.rejected}`),n.ops.shift(),r++,await qf()}return{flushed:r,pending:0}})().finally(()=>{To=null}),To):{flushed:0,source:"unconfigured"}}async function Dt(t,e,{trackId:n=null,env:r=process.env}={}){return wt(r)?(await kc({op:"record",family:t,id:e.id,trackId:n??e.trackId??null,data:e,updatedAt:Number(e.updatedAt)||Date.now()}),hr({env:r})):{queued:!1,source:"unconfigured"}}async function rn(t,e,n,r,o,{env:s=process.env}={}){return wt(s)?(await kc({op:"blob",family:t,id:e,v:Number(n),kind:r,path:o}),hr({env:s})):{queued:!1,source:"unconfigured"}}async function re(t,e,{deletedAt:n=Date.now(),env:r=process.env}={}){return wt(r)?(await kc({op:"delete",family:t,id:e,deletedAt:n}),hr({env:r})):{queued:!1,source:"unconfigured"}}async function Ot(t,{env:e=process.env}={}){if(!wt(e))return{records:[],source:"unconfigured"};try{let n=await Eo(`/v1/content/${encodeURIComponent(t)}?tombstones=1`,{headers:Ao(e)},e);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 ea(t,e,n,r,{env:o=process.env}={}){if(!wt(o))return null;try{let s=await Eo(`/v1/content/${encodeURIComponent(t)}/${encodeURIComponent(e)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:Ao(o)},o);if(!s.ok)return null;let a=Buffer.from(await s.arrayBuffer());return r==="png"?a:a.toString("utf8")}catch{return null}}var dS=".bostrat/handoff",fS="latest.md",pS=t=>/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1]??null;async function Gf(t,e,{box:n=null,agent:r=null}={}){if(!t||!wt())return{pushed:!1};try{let o=Date.now();return await Dt("checkpoints",{id:t,trackId:t,v:o,box:n,agent:r,updatedAt:o},{trackId:t}),await rn("checkpoints",t,o,"md",e),{pushed:!0,v:o}}catch{return{pushed:!1}}}async function Vf(t){try{let e=pS(t);if(!e||!wt())return{restored:!1};let n=Kf.join(t,dS,fS);if(await xc.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await Ot("checkpoints");if(o!=="plane")return{restored:!1};let s=r.find(i=>!i.deletedAt&&i.data?.id===e);if(!s?.data?.v)return{restored:!1};let a=await ea("checkpoints",e,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await xc.mkdir(Kf.dirname(n),{recursive:!0}),await xc.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}import _o from"node:fs/promises";import mS from"node:os";import Jf from"node:path";var hS=[/[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];function Io(t){let e=String(t??"");for(let n of hS)e=e.replace(n,"\u2039redacted\u203A");return e}var Sc=process.env.BOSTRAT_DEBUG_DIR||Jf.join(mS.homedir(),".bostrat","debug"),wr=Jf.join(Sc,"handoff.jsonl"),zf=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;function na(){let t=process.env.BOSTRAT_HANDOFF_DEBUG;return t==="1"||t==="true"}async function wS(t){try{await _o.mkdir(Sc,{recursive:!0}),await _o.appendFile(wr,`${JSON.stringify(t)}
|
|
42
|
+
`,"utf8");let n=(await _o.readFile(wr,"utf8")).split(`
|
|
43
|
+
`).filter(Boolean);n.length>zf&&await _o.writeFile(wr,`${n.slice(-zf).join(`
|
|
44
44
|
`)}
|
|
45
|
-
`,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${
|
|
46
|
-
`).filter(Boolean).slice(-t).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]=r.split("|"),p=Number(i);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(p)?n-p*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:f||null,agent:d||(l==="1"?"claude":null)}}).filter(r=>!
|
|
48
|
-
`).map(n=>n.trim()).includes(t)}catch{return!1}}var
|
|
49
|
-
`).filter(Boolean).length}catch{return 0}}async function
|
|
50
|
-
`);for(let n=e.length-1;n>=0;n--){let r=e[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
|
-
`).trim():""}function
|
|
52
|
-
`)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let m=c.attachment;m?.type==="queued_command"&&m.commandMode==="task-notification"&&typeof m.prompt=="string"&&m.prompt.trim()&&o.push({role:"user",text:m.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 m of l.content){let
|
|
53
|
-
|
|
54
|
-
`):""}import
|
|
55
|
-
`);if(!
|
|
56
|
-
`);for(let r=n.length-1;r>=0;r--)if(
|
|
57
|
-
`);return e}function
|
|
58
|
-
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(
|
|
59
|
-
`),o=r.findIndex(i=>
|
|
60
|
-
`).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:e,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"}]}]}}var
|
|
61
|
-
`),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:e,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
|
|
62
|
-
`))
|
|
63
|
-
`).slice(-
|
|
64
|
-
`);return
|
|
65
|
-
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return
|
|
66
|
-
${o}`.trim()||e.slice(0,2e3)}function
|
|
67
|
-
`):""}async function
|
|
45
|
+
`,"utf8")}catch(e){console.warn(`[handoff-debug] failed to write ${wr}: ${e?.message||e}`)}}async function G(t={}){let e={at:new Date().toISOString(),ts:Date.now(),...t},n=[e.flow||"handoff",e.phase||e.step||"?",e.trackId?`track=${e.trackId}`:"",e.sessionName?`box=${e.sessionName}`:"",e.error?`ERR=${e.error}`:""].filter(Boolean).join(" \xB7 ");if(console.log(`[handoff-debug] ${n}`),e.sessionName&&na())try{e.paneTail=Io((await mt(e.sessionName,{lines:35})).slice(-4e3))}catch{e.paneTail=null}(na()||e.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed|launch)/.test(String(e.phase||e.step||"")))&&await wS(e)}async function Xf({limit:t=80}={}){try{return(await _o.readFile(wr,"utf8")).split(`
|
|
46
|
+
`).filter(Boolean).slice(-t).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Io(o.paneTail)),o}catch{return{raw:Io(r)}}})}catch{return[]}}var Yf={DEBUG_DIR:Sc,LOG_FILE:wr};var st=yS(gS);function br(t){return new Promise(e=>setTimeout(e,t))}function ra(t,e){if(!e)return t;let n=o=>typeof o=="string"?o.split(e).join("<redacted>"):o,r=new Error(n(t?.message)||"command failed");return t?.code!==void 0&&(r.code=t.code),t?.stderr!==void 0&&(r.stderr=n(t.stderr)),r}var M=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";function Qf(t){return`/tmp/tmux-${t}/default`}function kS({explicit:t,legacyExists:e,runtimeDir:n,uid:r}){if(t)return t;let o=Qf(r);return e?o:n?`${n}/bostrat/tmux-default`:o}var Zf=typeof process.getuid=="function"?process.getuid():0,zt=kS({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return oa.existsSync(Qf(Zf))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:Zf}),xS=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",Tc=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",SS="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";function Ac(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${SS}=1`]}var Ec="bostrat-usage-probe",vS=new Set([Ec]);function _c(t,e){let n=Dn(t),r=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(t);return!n||r?t:`${t} --session-id ${e}`}function Dn(t){return/(^|\/|\s)claude(\s|$)/.test(String(t||""))}function on(t){return/(^|\/|\s)claude(\s|$)/.test(t)?"claude":/cursor/.test(t)?"cursor":/codex/.test(t)?"codex":/gemini/.test(t)?"gemini":"agent"}function TS(t){let e=String(t||"").trim();return!e||/fake-|\.sh(\s|$)/.test(e)?!1:Dn(e)||/(^|\/|\s)codex(\s|$)/.test(e)||/cursor-agent/.test(e)||/(^|\/|\s)gemini(\s|$)/.test(e)}async function tp(t,{loginShell:e=!1,failOpen:n=!1}={}){let r=await ep(t,{loginShell:e});return r===sa?!0:r===gr?!1:n}var sa="present",gr="absent",yr="unknown",vc=5e3,AS=[250,1e3,3e3],ES=t=>new Promise(e=>setTimeout(e,t));async function ep(t,{loginShell:e=!1,timeoutMs:n=vc}={}){if(!t)return gr;let r=e?process.env.SHELL||"/bin/sh":"sh",o=e?"-lic":"-c";try{return await st(r,[o,'command -v "$1" >/dev/null 2>&1',"_",t],{timeout:n}),sa}catch(s){return s?.killed||s?.signal?yr:s?.code===1||s?.code===127?gr:yr}}async function aa(t,{retries:e=AS,sleep:n=ES,probe:r=ep}={}){let o=String(t||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:vc});for(let a=0;s===yr&&a<e.length;a++)await n(e[a]),s=await r(o,{timeoutMs:vc*2});return s}var Ic={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"};function N(...t){return["-S",zt,...t]}async function sn(){if(!oa.existsSync(zt))return!1;try{return await st(M,N("list-sessions")),!0}catch(t){let e=t&&t.stderr?String(t.stderr):"";return!/no server running/i.test(e)}}async function Ro(){if(await sn())return!0;try{oa.mkdirSync(zt.replace(/\/[^/]+$/,""),{recursive:!0}),await st(M,N("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await sn()}var _S=/^[A-Za-z0-9_.-]{1,64}$/;function bt(t){return typeof t=="string"&&_S.test(t)}async function Oe(){if(!oa.existsSync(zt))return[];try{let t="#{session_name}|#{session_windows}|#{session_attached}|#{session_activity}|#{pane_current_path}|#{@bostrat-session}|#{@bostrat-title}|#{@bostrat-sid}|#{@bostrat-agent}",{stdout:e}=await st(M,N("list-sessions","-F",t)),n=Date.now();return e.trim().split(`
|
|
47
|
+
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,f,d]=r.split("|"),p=Number(i);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(p)?n-p*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:f||null,agent:d||(l==="1"?"claude":null)}}).filter(r=>!vS.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function rt(t){return(await Oe()).find(n=>n.name===t)||null}var IS=/^[A-Za-z0-9 ._↻-]+$/,RS=64;function $S(t){if(typeof t!="string")return!1;let e=t.trim();return e.length>=1&&e.length<=RS&&IS.test(e)}function Rc(t){return t.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function np(t){try{let{stdout:e}=await st(M,N("list-sessions","-F","#{session_name}"));return e.split(`
|
|
48
|
+
`).map(n=>n.trim()).includes(t)}catch{return!1}}var OS=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,MS=1e3,CS=/not found|Unknown command/i;function NS(t){let e=String(t||"").trim().split(/\s+/)[0]||"";if(e.includes("/"))return null;let n=[];for(let o=1;o<=e.length-3;o++)n.push(e.slice(o));if(!n.length)return null;let r=n.map(o=>o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return new RegExp(`\\b(?:${r.join("|")})`)}async function PS(t,e){let n=Date.now()+OS(),r=NS(e),o=!1,s=0;for(;;){if(await br(MS),s+=1,!await np(t))return;let a=await mt(t,{lines:15}),i=!o&&CS.test(a)&&!!r?.test(a);if(!i&&s>1&&!await kr(t))return;if(i){o=!0,await G({flow:"spawn",phase:"launch-retry",sessionName:t,agent:on(e),promptHead:e.slice(0,120)}).catch(()=>{}),await st(M,N("send-keys","-t",t,"C-u")),await st(M,N("send-keys","-t",t,"-l",e)),await st(M,N("send-keys","-t",t,"Enter"));continue}if(Date.now()>n){await G({flow:"spawn",phase:"launch-failed",sessionName:t,agent:on(e),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function Bn(t,e=xS,n={}){if(!$S(t))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=t.trim(),o=Rc(r);if(!bt(o))throw new Error("resulting tmux session name is invalid");if(!await Ro())throw new Error(`host tmux server not running on ${zt} and auto-start failed \u2014 start one: ${M} -S ${zt} new-session -d -s keeper "sleep 2147483647"`);if(await np(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||Tc,a=n.agent||on(s),i=!!n.resumeSessionId,c=n.resumeSessionId||bS(),l=i?s:_c(s,c),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),f=a==="claude"&&Dn(s),d=n.firstRun===!0;d||(f&&await tn(e),a==="codex"&&await dr(e),a==="cursor"&&await fr(e),await Cf(e),await Vf(e));let p=f&&!d?Gs():null,m=cf(),w=["-e",`${Ws}=${m}`];p&&w.push("-e",`ANTHROPIC_API_KEY=${p}`),f&&w.push(...Ac());try{await st(M,N("new-session","-d","-s",o,"-c",e,...w))}catch(y){throw ra(p?ra(y,p):y,m)}return await jn(o),await st(M,N("set-option","-t",o,"@bostrat-session","1")),await st(M,N("set-option","-t",o,"@bostrat-title",r)),await st(M,N("set-option","-t",o,"@bostrat-sid",c)),await st(M,N("set-option","-t",o,"@bostrat-agent",a)),await st(M,N("send-keys","-t",o,l,"Enter")),TS(l)&&PS(o,l).catch(()=>{}),{name:o,title:r,cwd:e,sessionId:c,agent:a,pinned:u,hookToken:m}}async function pe(t){if(!bt(t))throw new Error("invalid session name");await st(M,N("kill-session","-t",t))}async function rp(t,e){if(bt(t))try{let n=e?N("set-option","-t",t,"@bostrat-sid",e):N("set-option","-u","-t",t,"@bostrat-sid");await st(M,n)}catch{}}async function mt(t,{lines:e=40}={}){if(!bt(t))return"";try{let n=N("capture-pane","-t",t,"-p");Number.isFinite(e)&&e>0&&n.push("-S",`-${Math.floor(e)}`);let{stdout:r}=await st(M,n);return String(r||"")}catch{return""}}var DS=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,BS=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,Et=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,jS=Number(process.env.BOSTRAT_BOX_COLS)||120,LS=Number(process.env.BOSTRAT_BOX_ROWS)||40;async function op(t){if(!bt(t))return 0;try{let{stdout:e}=await st(M,N("list-clients","-t",t,"-F","1"));return String(e||"").trim().split(`
|
|
49
|
+
`).filter(Boolean).length}catch{return 0}}async function jn(t,{cols:e=jS,rows:n=LS}={}){if(!bt(t))return!0;if(await op(t))return!1;try{let r=await sp(t);if(r.cols===e&&r.rows===n)return!0;await st(M,N("resize-window","-t",t,"-x",String(e),"-y",String(n))),await st(M,N("set-window-option","-t",t,"window-size","latest")),await br(80)}catch{}return!0}async function FS(t){if(!bt(t))return 0;try{let{stdout:e}=await st(M,N("display-message","-t",t,"-p","#{pane_pid}"));return Number(String(e||"").trim())||0}catch{return 0}}async function kr(t){let e=await FS(t);if(!e)return!1;try{return await st("pgrep",["-P",String(e)]),!1}catch(n){return n?.code===1}}async function sp(t){if(!bt(t))return{cols:0,rows:0};try{let{stdout:e}=await st(M,N("display-message","-t",t,"-p","#{window_width} #{window_height}")),[n,r]=String(e||"").trim().split(/\s+/).map(o=>Number(o)||0);return{cols:n,rows:r}}catch{return{cols:0,rows:0}}}async function ia(t,{cols:e=DS,rows:n=BS}={}){if(bt(t)&&!await op(t))try{let r=await sp(t),o=Math.max(r.cols,e),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await st(M,N("resize-window","-t",t,"-x",String(o),"-y",String(s))),await st(M,N("set-window-option","-t",t,"window-size","latest")),await br(80))}catch{}}async function an(t){return await ia(t),mt(t,{lines:Et})}var US=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);async function vt(t,e,{settleMs:n=40,kind:r}={}){if(!bt(t))throw new Error("invalid session name");let o=(Array.isArray(e)?e:[e]).filter(s=>US.has(s));for(let s=0;s<o.length;s++)await st(M,N("send-keys","-t",t,o[s])),n>0&&s<o.length-1&&await br(n);return G({flow:"tmux",phase:"send-keys",sessionName:t,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function ca(t,e,{kind:n}={}){if(!bt(t))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(e)||0));return r>0&&await st(M,N("send-keys","-t",t,"-N",String(r),"BSpace")),G({flow:"tmux",phase:"send-keys",sessionName:t,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function la(t,e,{perCharDelayMs:n=120,kind:r}={}){if(!bt(t))throw new Error("invalid session name");let o=[...String(e)];for(let s of o)await st(M,N("send-keys","-t",t,"-l",s)),n>0&&await br(n);G({flow:"tmux",phase:"type",sessionName:t,kind:r||"type",textLen:o.length,textHead:String(e).slice(0,120)}).catch(()=>{})}async function _t(t,e,{enter:n=!0,submitDelayMs:r=0,kind:o}={}){if(!bt(t))throw new Error("invalid session name");if(typeof e=="string"&&e.length>0&&await st(M,N("send-keys","-t",t,"-l",e)),n){let s=Number(r);Number.isFinite(s)&&s>0&&await br(s),await st(M,N("send-keys","-t",t,"Enter"))}G({flow:"tmux",phase:"send",sessionName:t,kind:o||"text",textLen:typeof e=="string"?e.length:0,textHead:typeof e=="string"?e.slice(0,120):"",enter:n,submitDelayMs:r}).catch(()=>{})}import HS from"node-pty";async function ip(t,e,n={}){if(!bt(e)){ua(t,`invalid session name: ${e}`),t.close(1008,"invalid session name");return}if(!await sn()){ua(t,`host tmux server not running on ${zt}`),t.close(1011,"tmux server unavailable");return}if(!await rt(e)){ua(t,`session "${e}" not found`),t.close(1008,"session not found");return}let r=ap(n.cols,120,cp),o=ap(n.rows,32,lp),s;try{s=HS.spawn(M,["-S",zt,"attach-session","-d","-t",e],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){ua(t,`failed to spawn tmux attach: ${a.message}`),t.close(1011,"spawn failed");return}s.onData(a=>{t.readyState===t.OPEN&&$c(t,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{t.readyState===t.OPEN&&($c(t,{type:"exit",code:a??null,signal:i??null}),t.close(1e3,"tmux exited"))}),t.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"&&GS(i.cols,i.rows))try{s.resize(Math.min(500,i.cols|0),Math.min(500,i.rows|0))}catch{}}}),t.on("close",()=>{try{s.kill()}catch{}KS(e)})}var WS=6,qS=250;async function KS(t,e=WS,n=qS){for(let r=0;r<e;r++){if(await jn(t).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var cp=20,lp=6;function GS(t,e){return Number.isFinite(Number(t))&&Number.isFinite(Number(e))&&Number(t)>=cp&&Number(e)>=lp}function ap(t,e,n){let r=Number(t);return!Number.isFinite(r)||r<n?e:Math.min(500,r|0)}function $c(t,e){try{t.send(JSON.stringify(e))}catch{}}function ua(t,e){$c(t,{type:"error",message:e})}function up(t){let e=typeof t=="string"&&t?t:"/",n=e.indexOf("?"),r=n===-1?e:e.slice(0,n),o=n===-1?"":e.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}}import $o from"node:fs";import VS from"node:fs/promises";import xr from"node:path";import zS from"node:os";var dp={"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},JS=2e5,Oc=process.env.BOSTRAT_CLAUDE_PROJECTS||xr.join(zS.homedir(),".claude","projects");function Mc(t){return t.replace(/[^A-Za-z0-9]/g,"-")}function Cc(t){let e=xr.join(Oc,Mc(t)),n;try{n=$o.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=xr.join(e,o);try{let{mtimeMs:a}=$o.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function Me(t,e){if(!e)return null;let n=xr.join(Oc,Mc(t),`${e}.jsonl`);return $o.existsSync(n)?n:null}function fp(t){if(!t)return[];let e=xr.join(Oc,Mc(t)),n;try{n=$o.readdirSync(e).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=$o.statSync(xr.join(e,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function Ft(t,e=131072){let n=await VS.open(t,"r");try{let{size:r}=await n.stat(),o=Math.max(0,r-e),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 XS(t){let e=t.split(`
|
|
50
|
+
`);for(let n=e.length-1;n>=0;n--){let r=e[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 Oo(t,e=null,{fallbackNewest:n=!0}={}){if(!t)return null;let r,o;if(e){if(r=Me(t,e),o=!0,!r)return null}else if(n){if(r=Cc(t),o=!1,!r)return null}else return null;let s;try{s=XS(await Ft(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(dp,s.model),i=dp[s.model]||JS;return{source:"transcript",exact:o,model:s.model,windowKnown:a,window:i,occupancy:s.occupancy,pct:Math.min(1,s.occupancy/i),transcript:r}}function YS(t,e){return!t||!e?null:Me(t,e)}function ZS(t){return typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&e.type==="text"&&typeof e.text=="string").map(e=>e.text).join(`
|
|
51
|
+
`).trim():""}function QS(t){if(!Array.isArray(t))return null;let e=t.filter(n=>n&&n.type==="tool_use").map(n=>n.name).filter(Boolean);return e.length?e:null}function tv(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&e.type==="tool_result")}function Nc(t){let e=[],n=t?.message?.content;if(!Array.isArray(n))return e;for(let r of n)if(r){if(r.type==="image"&&r.source?.type==="base64"&&r.source.data)e.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&&e.push({mediaType:o.source.media_type||"image/png",data:o.source.data,toolUseId:r.tool_use_id||null})}return e}function pp(t,e){if(!t.uuid)return null;let n=Nc(t).map((r,o)=>({uuid:t.uuid,index:o,mediaType:r.mediaType,name:r.toolUseId&&e.get(r.toolUseId)||null}));return n.length?n:null}var ev=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;async function Pc(t,{maxBytes:e=524288,limit:n=80}={}){let r;try{r=await Ft(t,e)}catch{return[]}let o=[],s=new Map;for(let a of r.split(`
|
|
52
|
+
`)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let m=c.attachment;m?.type==="queued_command"&&m.commandMode==="task-notification"&&typeof m.prompt=="string"&&m.prompt.trim()&&o.push({role:"user",text:m.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 m of l.content){let w=m?.type==="tool_use"?m.input?.file_path||m.input?.path:null;typeof w=="string"&&ev.test(w)&&s.set(m.id,w.split("/").pop())}if(tv(l.content)){let m=pp(c,s);m&&o.push({role:"assistant",text:"",images:m,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let f=ZS(l.content),d=u==="assistant"?QS(l.content):null,p=u==="user"?pp(c,s):null;!f&&!d&&!p||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})}return o.slice(-n)}var nv=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t;async function mp(t,e,{turns:n=16}={}){let r=YS(t,e);if(!r)return"";let o=await Pc(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}:** ${nv(s.text||c,700)} ${i}`.trim()}).join(`
|
|
53
|
+
|
|
54
|
+
`):""}import Zv from"node:fs";import Ar from"node:fs/promises";import Er from"node:path";import rv from"node:fs/promises";import{execFile as ov}from"node:child_process";import{promisify as sv}from"node:util";var Dc=sv(ov),av=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,hp=new Set,wp=Promise.resolve(),Bc=null;function yp(t){Bc=typeof t=="function"?t:null}function iv(t){let e=String(t||""),n=e.indexOf("/lib/node_modules/");return n>0?e.slice(0,n):null}function bp({sessionName:t,from:e,to:n}={}){let r=String(n||"").trim();!r||hp.has(r)||(hp.add(r),wp=wp.then(()=>cv({sessionName:t,from:e,to:r})).catch(()=>{}))}async function cv({sessionName:t,from:e,to:n}){await G({flow:"codex-update",phase:"start",sessionName:t,from:e,to:n}).catch(()=>{});try{let r=(await Dc("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await rv.realpath(r),s=iv(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await Dc("npm",["install","-g","@openai/codex"],{env:a,timeout:av});let i=(await Dc(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await G({flow:"codex-update",phase:"done",sessionName:t,from:e,to:n,version:i}).catch(()=>{}),await gp({sessionName:t,from:e,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await G({flow:"codex-update",phase:"failed",sessionName:t,from:e,to:n,error:o}).catch(()=>{}),await gp({sessionName:t,from:e,to:n,status:"failed",error:o})}}async function gp(t){if(Bc)try{await Bc(t)}catch{}}var lv=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,kp=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;function uv(t,{provider:e,box:n}){let r=String(t).replace(/\r/g,"").split(`
|
|
55
|
+
`);if(!kp.test(t))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(lv);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"&&kp.test(a)?a:"The agent is asking for approval";return{provider:e,box:n,promptId:`${e}:${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:""}))}]}}var da=/(^|\n)\s*›/,vp=/^\s*Welcome to Codex\b/;function Sr(t){let e=String(t??"").replace(/\r/g,""),n=e.split(`
|
|
56
|
+
`);for(let r=n.length-1;r>=0;r--)if(vp.test(n[r]))return n.slice(r).join(`
|
|
57
|
+
`);return e}function dv(t){let e=String(t).trim();if(!e)return!1;let n=(e.match(/[A-Za-z]/g)||[]).length;return n>=3&&n/e.length>=.5}function fv(t){return/device code/i.test(t)?"works from the device you're reading this on":/sign in with chatgpt/i.test(t)?"opens a browser link on the node itself \u2014 can't finish from another device":""}var xp=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;function pv(t,e=""){let n=Sr(t);if(da.test(n)||!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n))return null;let r=n.split(`
|
|
58
|
+
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(xp);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(xp.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&&!vp.test(r[l])&&dv(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:e,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=fv(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}var Sp=/Enter this one-time code/,mv=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;function hv(t,e=""){let n=Sr(t);if(da.test(n)||!Sp.test(n))return null;let r=n.split(`
|
|
59
|
+
`),o=r.findIndex(i=>Sp.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(mv);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:e,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 wv(t,e=""){let n=Sr(t);return da.test(n)||!/open the following link to authenticate/.test(n)?null:{provider:"codex",box:e,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 gv(t,e=""){let n=Sr(t);return da.test(n)||!/Paste or type your API key/.test(n)?null:{provider:"codex",box:e,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:""}]}]}}var jc=/Run Everything|Add a follow-up|Plan, search, build anything/;function yv(t,e=""){let n=String(t);if(!/Press any key to log in/.test(n)||jc.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
60
|
+
`).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:e,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"}]}]}}var bv=/click this link to log in|Signing in with the browser/;function kv(t,e=""){let n=String(t);if(!bv.test(n)||jc.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
61
|
+
`),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:e,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 xv(t,e=""){let n=String(t);return!/Scan this QR code to log in/.test(n)||jc.test(n)?null:{provider:"cursor",box:e,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 Tp(t,e,n=""){if(t!=="cursor"&&t!=="codex")return null;if(t==="codex"){let r=hv(e,n)||wv(e,n)||gv(e,n)||pv(e,n);if(r)return r}if(t==="cursor"){let r=kv(e,n)||xv(e,n)||yv(e,n);if(r)return r}return uv(e,{provider:t,box:n})}function Ap(t,e){let n=Array.isArray(e?.[0])?e[0]:[],o=(Number.isInteger(n[0])&&n[0]>=0?n[0]:0)+1;return String(o)}var Sv=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Lc=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,Mo=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,Mt={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/},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/}},vv=/Trust this workspace|\[a\]\s*Trust/,Tv=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;function Av(t){let e=String(t||"");if(!Mt.codex.block.test(e))return null;let n=Sr(e);if(!Mt.codex.block.test(n))return null;let r=n.match(Tv);return{from:r?.[1]||null,to:r?.[2]||null}}function cn(t){return new Promise(e=>setTimeout(e,t))}var Ev=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,fa=new Map;function Ep(t,e){let n=Date.now(),r=Ev();for(let[s,a]of fa)n-a>=r&&fa.delete(s);let o=`${t}::${e}`;return fa.has(o)?!1:(fa.set(o,n),!0)}var _v=68,Iv=/^\s*[─━]{10,}\s*$/;function Rv(t){let e=0;for(let n of String(t).split(`
|
|
62
|
+
`))Iv.test(n)&&(e=Math.max(e,n.trimEnd().length));return e}function Fc(t,e){let n=Mt[t];if(!n?.busy)return{state:"idle",reason:null};if(Hc(t,e))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(e))return{state:"idle",reason:null};if(n.busy.test(e))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(e))return{state:"busy",reason:null};let r=Rv(e);return t==="claude"&&r>0&&r<_v?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function Uc(t,e){return Fc(t,e).state}var $v=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,Ov=15;function Ip(t,e){if(t!=="claude")return!1;let n=String(e||"").split(`
|
|
63
|
+
`).slice(-Ov).join(`
|
|
64
|
+
`);return $v.test(n)}function Co(t,e){return Uc(t,e)==="busy"}function pa(t,e){return Uc(t,e)!=="idle"}function ma(t,e){let n=Mt[t];return n?.up?n.up.test(e):!0}function Mv(t,e){let n=Mt[t];return n?Hc(t,e)?!1:n.up.test(e)&&Uc(t,e)==="idle":!0}function Hc(t,e){let n=Mt[t];return n?n.auth?.test(e)&&!n.up.test(e)?!0:n.block?.test(e)?t==="codex"?!0:!n.up.test(e):!1:!1}async function _p(t,e,n){let r=Date.now()+n,o=0;for(await jn(t).catch(()=>{});;){let s=await mt(t);if(Mv(e,s)){await cn(Mo);return}if(e==="cursor"&&vv.test(s)&&!Mt.cursor.up.test(s)&&o<3){if(!Ep(t,"cursor-trust")){await cn(Lc);continue}o+=1,await G({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o}).catch(()=>{}),await _t(t,"a",{enter:!1,kind:"cursor-trust-accept"}),await cn(Mo);continue}if(e==="codex"&&o<3){let a=Av(s);if(a){if(!Ep(t,`codex-update:${a.from}->${a.to}`)){await cn(Lc);continue}o+=1,await G({flow:"agent-ready",phase:"gate-accept",sessionName:t,agent:e,attempt:o,update:`${a.from} -> ${a.to}`}).catch(()=>{}),await _t(t,"3",{enter:!1,kind:"codex-update-skip"}),bp({sessionName:t,from:a.from,to:a.to}),await cn(Mo);continue}}if(Date.now()>r){if(Hc(e,s)){let a=`${e} is blocked on an interactive prompt`;throw e==="codex"?a=Mt.codex.auth.test(s)?"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":e==="cursor"&&Mt.cursor.auth.test(s)?a="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":e==="claude"&&Mt.claude.auth.test(s)?a="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":e==="claude"&&Mt.claude.block.test(s)&&(a='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'),G({flow:"agent-ready",phase:"blocked",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}if(await kr(t)){let a=`${e} isn't running in this box \u2014 its pane is sitting at a bare shell (the launch command was likely swallowed at shell startup, or the CLI exited). Relaunch it from the Terminal tab, or stop and reopen the box, then send again.`;throw G({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,error:a}).catch(()=>{}),new Error(a)}console.warn(`[agent-ready] ${e} readiness unconfirmed in ${t} after ${n}ms \u2014 driving the box anyway.`),G({flow:"agent-ready",phase:"timeout",sessionName:t,agent:e,timeoutMs:n,gateAccepts:o,paneBlocked:!!Mt[e]?.block?.test(s),paneUp:!!Mt[e]?.up?.test(s),paneBusy:!!Mt[e]?.busy?.test(s)}).catch(()=>{}),await cn(Mo);return}await cn(Lc)}}function Rp({agent:t,agentCmd:e}={}){let n=e?on(e):on(t||"");return{label:n,realClaude:e?Dn(e):n==="claude",realCodex:e?/codex/.test(e)&&!/fake-codex|fake-lead/.test(e):n==="codex",realCursor:e?/cursor/.test(e)&&!/fake-cursor|fake-box/.test(e):n==="cursor"}}function ha({cwd:t,sessionName:e,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Rp({agent:n,agentCmd:r});return!e||!o&&!s?null:ln({cwd:t,sessionName:e,agent:n,agentCmd:r}).catch(()=>{})}async function ln({cwd:t,sessionId:e,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=Sv()}={}){let{label:a,realClaude:i,realCodex:c,realCursor:l}=Rp({agent:r,agentCmd:o});if(i&&t&&await tn(t),c&&t&&await dr(t),l&&t&&await fr(t),(i||c||l)&&n){await _p(n,a,s);return}let u=Mt[r]?r:Mt[a]?a:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&u){await _p(n,u,s);return}await cn(Mo)}var Cv=(()=>{let t=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})(),Nv=(()=>{let t=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(t)&&t>=0?t:120})();function Ce(t){return t==="codex"?{enter:!0,submitDelayMs:Cv}:t==="claude"?{enter:!0,submitDelayMs:Nv}:{enter:!0}}import ga from"node:fs/promises";import Pp from"node:path";var Pv=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],Dv={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/]};function vr(t){let e=String(t||"").toLowerCase();if(!e.trim())return null;let n=null;for(let r of Pv){let o=0;for(let s of Dv[r])s.test(e)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}var Bv=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),jv=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function $p(t){let e=0,n=0;for(let[r,o]of Object.entries(t||{}))Bv.has(r)&&(e+=o||0),jv.has(r)&&(n+=o||0);return e>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function No(t){return String(t||"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/[-_/]+/g," ")}var Op=1.5,Mp=.75,Lv=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(" "));function Np(t){let e=No(String(t||"")).toLowerCase(),n=[];for(let r of e.split(/[^a-z0-9]+/))r.length<2||Lv.has(r)||n.push(r);return n}function Fv(t){let e=[];for(let n of t||[]){let r=n.payload||{},o=String(r.text||"").trim();if(n.type==="turn.user"){if(n.source==="hook"||!o)continue;e.push(Cp(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;e.push(Cp(n,"assistant",o))}}return e}function Cp(t,e,n){let r=vr(n);return{id:t.id,seq:t.seq,ts:t.payload?.ts??t.ts,role:e,boxId:t.boxId??null,provider:t.provider??null,text:n,tags:r?[r.category]:[]}}function Po(t){let e=Fv(t),n=new Map,r=new Array(e.length).fill(0),o=0;e.forEach((a,i)=>{let c=Np(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=e.length;return{chunks:e,postings:n,lengths:r,N:s,avgdl:s?o/s:0}}function wa(t,e,n={}){let{k:r=8,role:o=null,tag:s=null,boxId:a=null}=n;if(!t||!t.N)return[];let i=[...new Set(Np(e))];if(!i.length)return[];let c=new Map;for(let u of i){let f=t.postings.get(u);if(!f)continue;let d=f.size,p=Math.log(1+(t.N-d+.5)/(d+.5));for(let[m,w]of f){let y=t.lengths[m],b=w+Op*(1-Mp+Mp*(y/(t.avgdl||1))),S=p*(w*(Op+1)/b);c.set(m,(c.get(m)||0)+S)}}let l=[];for(let[u,f]of c){let d=t.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 Dp="Relevant prior discussion (verbatim, retrieved";async function Uv(t){let e=Pp.join(t,".bostrat","events"),n;try{n=await ga.readdir(e)}catch{return Po([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await ga.readFile(Pp.join(e,o),"utf8");for(let a of s.split(`
|
|
65
|
+
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return Po(r)}function Hv(t){let e=String(t||""),n=a=>{let i=e.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}
|
|
66
|
+
${o}`.trim()||e.slice(0,2e3)}function Wv(t,e,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=wa(t,e,{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?["",`## ${Dp}, 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(`
|
|
67
|
+
`):""}async function Bp(t,e,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await ga.readFile(e,"utf8");if(a.includes(Dp))return{appended:!1,reason:"already-enriched"};let i=await Uv(t);if(!i.N)return{appended:!1,reason:"empty-index"};let c=Wv(i,Hv(a),{k:r,maxChars:o,exclude:s});return c?(await ga.appendFile(e,`
|
|
68
68
|
`+c),{appended:!0,chars:c.length,chunks:c.split(`
|
|
69
|
-
- `).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)}}}import
|
|
70
|
-
`),
|
|
71
|
-
`))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function
|
|
69
|
+
- `).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)}}}import Wc from"node:fs/promises";import Tr from"node:path";import qv from"node:os";import{EventEmitter as Kv}from"node:events";import{randomUUID as Gv}from"node:crypto";var Vv=new Set(["session.ready","turn.user","turn.agent","turn.retracted","turn.interrupted","tool.called","tool.result","prompt.raised","prompt.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","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","notify.raised","usage.cap.hit","usage.cap.warning","provider.connected","track.summary.recorded","changelog.published","stall.detected","error"]),zv=new Set(["user","agent","orchestrator","system"]),Jv=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),Ut=new Kv;Ut.setMaxListeners(0);var qc=process.env.BOSTRAT_STATE_DIR||Tr.join(process.env.HOME||qv.tmpdir(),".bostrat");function ba(t){return t?.cwd?Tr.join(t.cwd,".bostrat","events"):Tr.join(qc,"events")}var jp=(t,e)=>Tr.join(t,`${e}.ndjson`),ya=new Map;async function Xv(t,e){if(!ya.has(e)){let o=(await L(t,e)).reduce((s,a)=>a.seq>s?a.seq:s,0);ya.set(e,o)}let n=ya.get(e)+1;return ya.set(e,n),n}function Yv(t,e){let n=Array.isArray(t?.epochs)?t.epochs:null;if(!(!n||n.length===0)){if(!Number.isFinite(e))return n.length-1;for(let r=n.length-1;r>=0;r--){let o=n[r].startedAt;if(o==null||o<=e)return r}return 0}}async function I(t,e){let n=typeof t=="string"?t:t?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!e||!Vv.has(e.type))throw new Error(`appendEvent: unknown event type "${e?.type}"`);if(e.actor&&!zv.has(e.actor))throw new Error(`appendEvent: bad actor "${e.actor}"`);if(e.source&&!Jv.has(e.source))throw new Error(`appendEvent: bad source "${e.source}"`);let r=typeof t=="string"?Tr.join(qc,"events"):ba(t),o=e.epoch;if(o==null&&typeof t=="object"&&e.boxId&&Array.isArray(t?.boxes)){let c=t.boxes.find(l=>l.name===e.boxId);o=Yv(c,Number(e.payload?.ts??e.ts)||Date.now())}let s=await Xv(r,n),a=e.userId??process.env.BOSTRAT_OWNER??null,i={id:e.id||Gv(),trackId:n,boxId:e.boxId??null,...o!=null?{epoch:o}:{},...a?{userId:a}:{},seq:s,ts:e.ts??Date.now(),type:e.type,actor:e.actor??"system",provider:e.provider??null,source:e.source??"orchestrator",payload:e.payload??{},...e.causationId?{causationId:e.causationId}:{},...e.correlationId?{correlationId:e.correlationId}:{}};return await Wc.mkdir(r,{recursive:!0}),await Wc.appendFile(jp(r,n),JSON.stringify(i)+`
|
|
70
|
+
`),Ut.emit("event",i),Ut.emit(`track:${n}`,i),i}async function L(t,e){let n,r;typeof t=="string"&&e?(n=t,r=e):(r=typeof t=="string"?t:t?.id,n=typeof t=="string"?Tr.join(qc,"events"):ba(t));let o;try{o=await Wc.readFile(jp(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
71
|
+
`))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function Lp(t,e){let n=`track:${t}`;return Ut.on(n,e),()=>Ut.off(n,e)}var Wp=".bostrat/handoff",qp="latest.md",Fp=32*1024;function Qv(t){let e=Io(String(t??"")),n=Buffer.byteLength(e,"utf8");return n<=Fp?{content:e,bytes:n,truncated:!1}:{content:`${Buffer.from(e,"utf8").subarray(0,Fp).toString("utf8").replace(/�+$/,"")}
|
|
72
72
|
|
|
73
|
-
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}var gp="Checkpoint: write a work-in-progress handoff",yp="HANDOFF WRITTEN",bp="Resume from .bostrat/handoff/latest.md",rv="/clear",ov=t=>`${gp} to the EXACT absolute path ${t} (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 ${yp} and take no further action.`,sv=`${bp}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,kp="Beat: write a",xp="BEAT WRITTEN",av=".bostrat/arc",iv="beat.txt",pp=200,Sp=4,vp=5,cv=36,lv=t=>`${kp} short summary of what this workstream accomplished, to the EXACT absolute path ${t} (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 ${Sp} further lines, each starting with "- " and naming ONE piece in at most ${vp} 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 ${xp} and take no further action.`,mp=/^(?:[-–—*•·]|\d+[.)])\s+/,uv=(t,e,n)=>{let r=t.split(/\s+/).filter(Boolean).slice(0,e).join(" ");return r.length>n?`${r.slice(0,n-1).trimEnd()}\u2026`:r};function dv(t){let e=String(t||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of e)if(mp.test(s)){if(r.length>=Sp)continue;let a=uv(s.replace(mp,"").replace(/[.;,:]+$/,""),vp,cv);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>pp&&(o=`${o.slice(0,pp-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Tp(t,e,{timeoutMs:n=9e4}={}){let r=yr.join(e,av),o=yr.join(r,iv);await gr.mkdir(r,{recursive:!0});let s=await aa(o),a=await Q(t);if(!a)throw new Error(`session "${t}" not found`);return await ia(t,a,lv(o)),await Cp(o,s,n),dv(await gr.readFile(o,"utf8"))}function Ap(t,e){let n=(e||"").trim();return n?t==="assistant"?n===yp||n===xp:n.startsWith(gp)||n===rv||n.startsWith(bp)||n.startsWith(kp):!1}var fv=6e5;function Ep(t){let e=[];for(let r of t?.events||[]){if(r.type!=="lead-handoff"||!r.from)continue;let o=r.at??Date.now(),s=r.handoffStartedAt??o-fv;e.push({box:r.from,from:s,to:o}),r.to&&r.to!==r.from&&e.push({box:r.to,from:s,to:o})}let n=t?.handoff;return n?.fromBox&&n.startedAt&&n.finishedAt&&e.push({box:n.fromBox,from:n.startedAt,to:n.finishedAt}),n?.toBox&&n.startedAt&&n.finishedAt&&e.push({box:n.toBox,from:n.startedAt,to:n.finishedAt}),e}function _p(t,e){let n=[];for(let r of t?.boxes||[]){let o=e?.(r.name);o?.startedAt&&n.push({box:r.name,from:o.startedAt,to:o.finishedAt||Date.now()+1e12})}return n}function Ip(t,{windows:e=[]}={}){if(Ap(t.role,t.text))return!0;if(t.role!=="assistant"||!t.ts)return!1;for(let n of e)if(t.box===n.box&&t.ts>=n.from&&t.ts<=n.to)return!0;return!1}var pv=3500,mv=3e5,Ac=new Map;function ca(t){return Ac.get(t)||null}async function Rp(t){let e=Ac.get(t);if(e&&!e.finishedAt)throw new Error("a handoff is already running for this session");let n=await Q(t);if(!n)throw new Error(`session "${t}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${t}"`);let r={name:t,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:yr.join(n.cwd,hp,wp)};return Ac.set(t,r),hv(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function hv(t,e){t.step="summarize",await $p(t.name,e),t.step="clear";let n=await Q(t.name);await ia(t.name,n,"/clear"),await Mp(pv),t.step="resume",await ia(t.name,n,sv),t.step="done",t.finishedAt=Date.now()}async function $p(t,e){let n=yr.join(e,hp),r=yr.join(n,wp);await gr.mkdir(n,{recursive:!0});let o=await aa(r),s=await Q(t);await q({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:r,beforeMtime:o}),await ia(t,s,ov(r)),await Cp(r,o,mv),await q({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:r,afterMtime:await aa(r)});let a=await lp(e,r,{exclude:l=>Ap(l.role,l.text)});await q({flow:"checkpoint",phase:"seed-enriched",sessionName:t,file:r,...a});let i=/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1];if(i){await R({id:i,cwd:e},{type:"memory.seed",actor:"orchestrator",payload:{...a,tokensEst:a.chars?Math.round(a.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let l=await gr.readFile(r,"utf8");await R({id:i,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...nv(l),ts:Date.now()}})}catch{}}await uf(e);let c=await wv(n,r);return i&&await kf(i,c||r,{box:t,agent:s?.agent??null}),r}async function Op(t){let e=await Q(t);if(!e)throw new Error(`session "${t}" not found`);if(!e.cwd)throw new Error(`can't resolve cwd for "${t}"`);return $p(t,e.cwd)}async function aa(t){try{return(await gr.stat(t)).mtimeMs}catch{return 0}}async function Cp(t,e,n){let r=Date.now()+n;for(;;){if(await aa(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await Mp(2e3)}}async function wv(t,e){try{if(!ev.existsSync(e))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=yr.join(t,`${n}.md`);return await gr.copyFile(e,r),r}catch{return null}}function Mp(t){return new Promise(e=>setTimeout(e,t))}async function ia(t,e,n){if(e){let o=e.agent==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":e.agent==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":e.agent==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":"";await tn({cwd:e.cwd,sessionId:e.sessionId,sessionName:t,agent:e.agent,agentCmd:o})}let r=await Q(t);if(!r)throw new Error(`session "${t}" disappeared before the orchestration prompt could be sent`);await St(t,n,{kind:"orchestration-drive",..._e(r.agent)})}import U from"node:fs/promises";import H from"node:path";import{randomUUID as fs}from"node:crypto";import{execFile as _I}from"node:child_process";import{promisify as II}from"node:util";import yh from"node:path";var Ec={"claude-opus-5":{input:5,output:25,cacheWrite:6.25,cacheRead:.5},"claude-opus-4-8":{input:5,output:25,cacheWrite:6.25,cacheRead:.5},"claude-sonnet-5":{input:3,output:15,cacheWrite:3.75,cacheRead:.3},"claude-sonnet-4-6":{input:3,output:15,cacheWrite:3.75,cacheRead:.3},"claude-haiku-4-5":{input:1,output:5,cacheWrite:1.25,cacheRead:.1},"claude-fable-5":{input:10,output:50,cacheWrite:12.5,cacheRead:1},"gpt-5.5":{input:5,output:30,cacheWrite:5,cacheRead:.5},"gpt-5.6-sol":{input:5,output:30,cacheWrite:6.25,cacheRead:.5},"gpt-5.6-terra":{input:2.5,output:15,cacheWrite:3.125,cacheRead:.25},"gpt-5.6-luna":{input:1,output:6,cacheWrite:1.25,cacheRead:.1}},Np={opus:"claude-opus-4-8",opusplan:"claude-opus-4-8",default:"claude-opus-4-8",sonnet:"claude-sonnet-4-6",haiku:"claude-haiku-4-5",fable:"claude-fable-5","gpt-5.6":"gpt-5.6-sol"};function Pp(t){if(!t)return null;let e=String(t).trim();return Ec[e]?e:Np[e]?Np[e]:Object.keys(Ec).find(r=>e.startsWith(r))||null}var la=t=>Pp(t)!==null;function le(t,e={}){let n=Ec[Pp(t)];return n?((e.input||0)*n.input+(e.output||0)*n.output+(e.cacheWrite||0)*n.cacheWrite+(e.cacheRead||0)*n.cacheRead)/1e6:0}var Bp=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});function gv(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function Dp(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function da(t){let e=t.payload?.model||"unknown",n=gv(t.payload?.tokens),r=Number(t.payload?.usd)||0,o=!1;!r&&n&&(r=le(e,n),r>0&&(o=!0));let s=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,a=!r&&s>0&&!la(e);return{usd:r,model:e,tokens:n,repriced:o,unpriced:a}}function ue(t,{boxLabels:e={}}={}){let n=(t||[]).filter(f=>f.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=new Set,u=Bp();for(let f of n){let{usd:d,model:p,tokens:m,repriced:h,unpriced:y}=da(f),b=f.provider||"unknown",S=f.boxId||"unattributed";if(h&&i++,y&&(c++,l.add(p)),a+=d,Dp(u,m),r[p]||={usd:0,tokens:Bp(),events:0},r[p].usd+=d,r[p].events+=1,Dp(r[p].tokens,m),o[b]||={usd:0,events:0},o[b].usd+=d,o[b].events+=1,s[S]||={usd:0,events:0,provider:b,models:new Set,label:e[S]||S},s[S].usd+=d,s[S].events+=1,p!=="unknown"&&s[S].models.add(p),f.epoch!=null){let I=(s[S].epochs||={})[f.epoch]||={usd:0,events:0,provider:b,models:new Set};I.usd+=d,I.events+=1,I.provider=b,p!=="unknown"&&I.models.add(p)}}return{totalUsd:Ao(a),totalTokens:u,turns:n.length,repricedTurns:i,unpricedTurns:c,unpricedModels:[...l],byModel:ua(r,f=>({...f,usd:Ao(f.usd)})),byProvider:ua(o,f=>({...f,usd:Ao(f.usd)})),byBox:ua(s,f=>({usd:Ao(f.usd),events:f.events,provider:f.provider,label:f.label,models:[...f.models],...f.epochs?{epochs:ua(f.epochs,d=>({usd:Ao(d.usd),events:d.events,provider:d.provider,models:[...d.models]}))}:{}}))}}var Ao=t=>Math.round(t*1e4)/1e4,ua=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]));import kt from"node:fs/promises";import Bt from"node:path";import Eo from"node:os";import{randomUUID as yv}from"node:crypto";import{execFile as bv}from"node:child_process";import{promisify as kv}from"node:util";import{chromium as xv}from"playwright-core";var Lp=kv(bv),Fp=process.env.BOSTRAT_STATE_DIR||Bt.join(process.env.HOME||Eo.tmpdir(),".bostrat"),Up=Bt.join(Fp,"mocks.json"),Sv=process.env.BOSTRAT_MOCK_ENGINE||"chromium",vv=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",Tv=process.env.BOSTRAT_FIREFOX_BIN||"firefox",Av=1300,br=2,Ev=32,Hp=1500,kr=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,_v=700*1024*1024;async function Iv(){if(process.platform!=="darwin")return Eo.freemem();try{let{stdout:t}=await Lp("vm_stat",[],{timeout:3e3}),e=Number(/page size of (\d+) bytes/.exec(t)?.[1])||4096,n=o=>Number(new RegExp(`Pages ${o}:\\s+(\\d+)\\.`).exec(t)?.[1])||0,r=(n("free")+n("inactive")+n("speculative"))*e;return r>0?r:Eo.freemem()}catch{return Eo.freemem()}}var te=class extends Error{constructor(e,n=""){super(n?`${e}
|
|
73
|
+
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}var Kp="Checkpoint: write a work-in-progress handoff",Gp="HANDOFF WRITTEN",Vp="Resume from .bostrat/handoff/latest.md",tT="/clear",eT=t=>`${Kp} to the EXACT absolute path ${t} (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 ${Gp} and take no further action.`,nT=`${Vp}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,zp="Beat: write a",Jp="BEAT WRITTEN",rT=".bostrat/arc",oT="beat.txt",Up=200,Xp=4,Yp=5,sT=36,aT=t=>`${zp} short summary of what this workstream accomplished, to the EXACT absolute path ${t} (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 ${Xp} further lines, each starting with "- " and naming ONE piece in at most ${Yp} 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 ${Jp} and take no further action.`,Hp=/^(?:[-–—*•·]|\d+[.)])\s+/,iT=(t,e,n)=>{let r=t.split(/\s+/).filter(Boolean).slice(0,e).join(" ");return r.length>n?`${r.slice(0,n-1).trimEnd()}\u2026`:r};function cT(t){let e=String(t||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of e)if(Hp.test(s)){if(r.length>=Xp)continue;let a=iT(s.replace(Hp,"").replace(/[.;,:]+$/,""),Yp,sT);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>Up&&(o=`${o.slice(0,Up-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Zp(t,e,{timeoutMs:n=9e4}={}){let r=Er.join(e,rT),o=Er.join(r,oT);await Ar.mkdir(r,{recursive:!0});let s=await ka(o),a=await rt(t);if(!a)throw new Error(`session "${t}" not found`);return await xa(t,a,aT(o)),await am(o,s,n),cT(await Ar.readFile(o,"utf8"))}function Qp(t,e){let n=(e||"").trim();return n?t==="assistant"?n===Gp||n===Jp:n.startsWith(Kp)||n===tT||n.startsWith(Vp)||n.startsWith(zp):!1}var lT=6e5;function tm(t){let e=[];for(let r of t?.events||[]){if(r.type!=="lead-handoff"||!r.from)continue;let o=r.at??Date.now(),s=r.handoffStartedAt??o-lT;e.push({box:r.from,from:s,to:o}),r.to&&r.to!==r.from&&e.push({box:r.to,from:s,to:o})}let n=t?.handoff;return n?.fromBox&&n.startedAt&&n.finishedAt&&e.push({box:n.fromBox,from:n.startedAt,to:n.finishedAt}),n?.toBox&&n.startedAt&&n.finishedAt&&e.push({box:n.toBox,from:n.startedAt,to:n.finishedAt}),e}function em(t,e){let n=[];for(let r of t?.boxes||[]){let o=e?.(r.name);o?.startedAt&&n.push({box:r.name,from:o.startedAt,to:o.finishedAt||Date.now()+1e12})}return n}function nm(t,{windows:e=[]}={}){if(Qp(t.role,t.text))return!0;if(t.role!=="assistant"||!t.ts)return!1;for(let n of e)if(t.box===n.box&&t.ts>=n.from&&t.ts<=n.to)return!0;return!1}var uT=3500,dT=3e5,Kc=new Map;function Sa(t){return Kc.get(t)||null}async function rm(t){let e=Kc.get(t);if(e&&!e.finishedAt)throw new Error("a handoff is already running for this session");let n=await rt(t);if(!n)throw new Error(`session "${t}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${t}"`);let r={name:t,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:Er.join(n.cwd,Wp,qp)};return Kc.set(t,r),fT(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function fT(t,e){t.step="summarize",await om(t.name,e),t.step="clear";let n=await rt(t.name);await xa(t.name,n,"/clear"),await im(uT),t.step="resume",await xa(t.name,n,nT),t.step="done",t.finishedAt=Date.now()}async function om(t,e){let n=Er.join(e,Wp),r=Er.join(n,qp);await Ar.mkdir(n,{recursive:!0});let o=await ka(r),s=await rt(t);await G({flow:"checkpoint",phase:"summarize-start",sessionName:t,cwd:e,file:r,beforeMtime:o}),await xa(t,s,eT(r)),await am(r,o,dT),await G({flow:"checkpoint",phase:"summarize-done",sessionName:t,file:r,afterMtime:await ka(r)});let a=await Bp(e,r,{exclude:l=>Qp(l.role,l.text)});await G({flow:"checkpoint",phase:"seed-enriched",sessionName:t,file:r,...a});let i=/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1];if(i){await I({id:i,cwd:e},{type:"memory.seed",actor:"orchestrator",payload:{...a,tokensEst:a.chars?Math.round(a.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let l=await Ar.readFile(r,"utf8");await I({id:i,cwd:e},{type:"handoff.artifact",actor:"orchestrator",payload:{...Qv(l),ts:Date.now()}})}catch{}}await Df(e);let c=await pT(n,r);return i&&await Gf(i,c||r,{box:t,agent:s?.agent??null}),r}async function sm(t){let e=await rt(t);if(!e)throw new Error(`session "${t}" not found`);if(!e.cwd)throw new Error(`can't resolve cwd for "${t}"`);return om(t,e.cwd)}async function ka(t){try{return(await Ar.stat(t)).mtimeMs}catch{return 0}}async function am(t,e,n){let r=Date.now()+n;for(;;){if(await ka(t)>e)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${t})`);await im(2e3)}}async function pT(t,e){try{if(!Zv.existsSync(e))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=Er.join(t,`${n}.md`);return await Ar.copyFile(e,r),r}catch{return null}}function im(t){return new Promise(e=>setTimeout(e,t))}async function xa(t,e,n){if(e){let o=e.agent==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":e.agent==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":e.agent==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":"";await ln({cwd:e.cwd,sessionId:e.sessionId,sessionName:t,agent:e.agent,agentCmd:o})}let r=await rt(t);if(!r)throw new Error(`session "${t}" disappeared before the orchestration prompt could be sent`);await _t(t,n,{kind:"orchestration-drive",...Ce(r.agent)})}import F from"node:fs/promises";import U from"node:path";import{randomUUID as Ts}from"node:crypto";import{execFile as BR}from"node:child_process";import{promisify as jR}from"node:util";import Vh from"node:path";var Gc={"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)."}]},cm={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"},mT=/^\d{4}-\d{2}-\d{2}$/;function lm(t,e,n){if(t==null)return e==="from"?-1/0:1/0;if(!mT.test(String(t)))throw new Error(`pricing.js: ${n} window ${e} must be YYYY-MM-DD, got ${JSON.stringify(t)}`);let r=Date.parse(`${t}T${e==="from"?"00:00:00.000":"23:59:59.999"}Z`);if(!Number.isFinite(r))throw new Error(`pricing.js: ${n} window ${e} is not a real date: ${t}`);return r}function hT(t){let e={};for(let[n,r]of Object.entries(t)){if(!Array.isArray(r)||r.length===0)throw new Error(`pricing.js: ${n} must be a non-empty array of rate windows`);e[n]=r.map(o=>({...o,fromMs:lm(o.from,"from",n),toMs:lm(o.to,"to",n)}))}return e}var wT=hT(Gc);function gT(t){if(t==null)return Date.now();if(typeof t=="number")return Number.isFinite(t)?t:null;if(t instanceof Date){let n=t.getTime();return Number.isFinite(n)?n:null}let e=Date.parse(String(t));return Number.isFinite(e)?e:null}function yT(t){if(!t)return null;let e=String(t).trim();return Gc[e]?e:cm[e]?cm[e]:Object.keys(Gc).find(r=>e.startsWith(r))||null}function um(t,e){let n=yT(t);if(!n)return null;let r=gT(e);return r===null?null:wT[n].find(o=>r>=o.fromMs&&r<=o.toMs)||null}var va=(t,e)=>um(t,e)!==null;function me(t,e={},n){let r=um(t,n);return r?((e.input||0)*r.input+(e.output||0)*r.output+(e.cacheWrite||0)*r.cacheWrite+(e.cacheRead||0)*r.cacheRead)/1e6:0}var dm=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});function bT(t){return t?{input:t.input||0,output:t.output||0,cacheRead:t.cacheRead||0,cacheWrite:t.cacheWrite??t.cacheCreation??0}:null}function fm(t,e){e&&(t.input+=e.input,t.output+=e.output,t.cacheRead+=e.cacheRead,t.cacheCreation+=e.cacheWrite)}function Ln(t){let e=t.payload?.model||"unknown",n=bT(t.payload?.tokens),r=Number(t.payload?.usd)||0,o=!1;!r&&n&&(r=me(e,n),r>0&&(o=!0));let s=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,a=!r&&s>0&&!va(e);return{usd:r,model:e,tokens:n,repriced:o,unpriced:a}}function he(t,{boxLabels:e={}}={}){let n=(t||[]).filter(f=>f.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=new Set,u=dm();for(let f of n){let{usd:d,model:p,tokens:m,repriced:w,unpriced:y}=Ln(f),b=f.provider||"unknown",S=f.boxId||"unattributed";if(w&&i++,y&&(c++,l.add(p)),a+=d,fm(u,m),r[p]||={usd:0,tokens:dm(),events:0},r[p].usd+=d,r[p].events+=1,fm(r[p].tokens,m),o[b]||={usd:0,events:0},o[b].usd+=d,o[b].events+=1,s[S]||={usd:0,events:0,provider:b,models:new Set,label:e[S]||S},s[S].usd+=d,s[S].events+=1,p!=="unknown"&&s[S].models.add(p),f.epoch!=null){let _=(s[S].epochs||={})[f.epoch]||={usd:0,events:0,provider:b,models:new Set};_.usd+=d,_.events+=1,_.provider=b,p!=="unknown"&&_.models.add(p)}}return{totalUsd:Do(a),totalTokens:u,turns:n.length,repricedTurns:i,unpricedTurns:c,unpricedModels:[...l],byModel:Ta(r,f=>({...f,usd:Do(f.usd)})),byProvider:Ta(o,f=>({...f,usd:Do(f.usd)})),byBox:Ta(s,f=>({usd:Do(f.usd),events:f.events,provider:f.provider,label:f.label,models:[...f.models],...f.epochs?{epochs:Ta(f.epochs,d=>({usd:Do(d.usd),events:d.events,provider:d.provider,models:[...d.models]}))}:{}}))}}var Do=t=>Math.round(t*1e4)/1e4,Ta=(t,e)=>Object.fromEntries(Object.entries(t).map(([n,r])=>[n,e(r)]));import At from"node:fs/promises";import Ht from"node:path";import mm from"node:os";import{randomUUID as vT}from"node:crypto";import{execFile as TT}from"node:child_process";import{promisify as AT}from"node:util";import{chromium as ET}from"playwright-core";import Vc from"node:os";import{execFile as kT}from"node:child_process";import{promisify as xT}from"node:util";var ST=xT(kT);async function Aa(){if(process.platform!=="darwin")return Vc.freemem();try{let{stdout:t}=await ST("vm_stat",[],{timeout:3e3}),e=Number(/page size of (\d+) bytes/.exec(t)?.[1])||4096,n=o=>Number(new RegExp(`Pages ${o}:\\s+(\\d+)\\.`).exec(t)?.[1])||0,r=(n("free")+n("inactive")+n("speculative"))*e;return r>0?r:Vc.freemem()}catch{return Vc.freemem()}}var _T=AT(TT),hm=process.env.BOSTRAT_STATE_DIR||Ht.join(process.env.HOME||mm.tmpdir(),".bostrat"),wm=Ht.join(hm,"mocks.json"),IT=process.env.BOSTRAT_MOCK_ENGINE||"chromium",RT=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",$T=process.env.BOSTRAT_FIREFOX_BIN||"firefox",OT=1300,_r=2,MT=32,gm=1500,Ir=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,CT=700*1024*1024,oe=class extends Error{constructor(e,n=""){super(n?`${e}
|
|
74
74
|
${n.trim().split(`
|
|
75
75
|
`).slice(-6).join(`
|
|
76
|
-
`)}`:e),this.name="MockRenderError"}},Mn=null,_c=null;async function fe(){return Mn||(_c||=(async()=>{try{Mn=JSON.parse(await kt.readFile(Up,"utf8"))}catch{Mn={mocks:[]}}finally{_c=null}return Mn.mocks||=[],Mn})(),_c)}async function de(){await Nt(Up,Mn)}var en=t=>$t("mocks",{...t},{trackId:t.trackId}).catch(()=>{}),Nn=(t,e,n)=>Je("mocks",t.id,e,n,Wt(t,e)[n]).catch(()=>{});function ee(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,mockId:t.id,name:t.name,...t.group?{group:t.group}:{},...n}}).catch(()=>{})}var fa=t=>Bt.join(t,".bostrat","mocks");function Wt(t,e){let n=fa(t.cwd);return{html:Bt.join(n,`${t.id}.v${e}.html`),png:Bt.join(n,`${t.id}.v${e}.png`)}}function Ie(t,e=null){let n=t.versions||[];return e!=null?n.find(r=>r.v===Number(e))||null:n.length?n[n.length-1]:null}function Rc(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}var Rv=Buffer.from([137,80,78,71,13,10,26,10]);function Wp(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(Rv)}async function $v(t){let e=Buffer.alloc(24),n=await kt.open(t,"r");try{await n.read(e,0,24,0)}finally{await n.close()}return e}function Ov({imgW:t,imgH:e,bbox:n,pad:r,align:o=1}){if(!n||!(t>0)||!(e>0))return null;let s=n.right-n.left+1,a=n.bottom-n.top+1;if(s<=0||a<=0||s>=t*.9&&a>=e*.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 Cv(t,e){return t.evaluate(async n=>{let r=new Image;await new Promise((m,h)=>{r.onload=m,r.onerror=()=>h(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,f=s,d=-1,p=-1;for(let m=0;m<s;m++)for(let h=m*o*4,y=0;y<o;y++,h+=4)Math.abs(c[h]-l[0])+Math.abs(c[h+1]-l[1])+Math.abs(c[h+2]-l[2])>24&&(y<u&&(u=y),y>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}}},e.toString("base64"))}async function Mv(t,e,{bg:n,crop:r}){let o=await t.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:e.toString("base64"),bg:n,crop:r});return Buffer.from(o,"base64")}async function Nv({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await xv.launch({executablePath:vv,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new te("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:br})).newPage();a.setDefaultTimeout(kr),await a.goto(`file://${t}`,{waitUntil:"load",timeout:kr}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:kr});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await Cv(a,i),u=l&&Ov({...l,pad:Ev*br,align:br});u&&(i=await Mv(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Hp)throw new te(`capture produced a ${i.length}-byte image`);let c=Rc(i);if(!c)throw new te("output is not a PNG");return await kt.writeFile(e,i),{width:Math.round(c.width/br),height:Math.round(c.height/br),dpr:br,renderMs:Date.now()-r}}catch(s){throw s instanceof te?s:new te(/timeout/i.test(s?.message||"")?`render timed out after ${kr}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function jp({htmlPath:t,outPath:e,width:n}){let r=await kt.mkdtemp(Bt.join(Eo.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Lp(Tv,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:kr,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new te(c?.killed?`render timed out after ${kr}ms`:"firefox exited non-zero",s)}let a=await kt.stat(e).catch(()=>null);if(!a||a.size<Hp)throw new te(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Rc(await $v(e));if(!i)throw new te("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await kt.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function Pv(t){if(Sv==="firefox")return jp(t);try{return await Nv(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return jp(t);throw e}}var Bv=Pv;var Ic=Promise.resolve();function qp(t){return Ic=Ic.then(t,t),Ic}async function Kp(t,e){let n=await fe(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await de(),await ee(r,"rendering",{version:e});try{let o=await Iv();if(o<_v)throw new te(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Wt(r,e),a=await Bv({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===t)){await kt.rm(s.png,{force:!0}).catch(()=>{});return}Gp(r,{v:e,source:"html",htmlRel:Bt.relative(r.cwd,s.html),pngRel:Bt.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await de(),await ee(r,"ready",{version:e,width:a.width,height:a.height,dpr:a.dpr}),en(r),Nn(r,e,"png")}catch(o){if(!n.mocks.some(s=>s.id===t))return;r.state="error",r.error=o?.message||String(o),await de(),await ee(r,"error",{version:e,error:r.error}),en(r)}}}function Gp(t,e){t.versions=(t.versions||[]).filter(r=>r.v!==e.v).concat(e),t.versions.sort((r,o)=>r.v-o.v);let n=t.versions[t.versions.length-1];t.readyVersion=n.v,t.height=n.height,t.dpr=n.dpr,t.renderMs=n.renderMs??null,n.v===t.version&&(t.state="ready",t.error=null)}function Dv({trackId:t,name:e,cwd:n,width:r}){if(!t)throw new Error("trackId required");if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(!n)throw new Error("cwd required");let o=r==null?Av:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Pn({trackId:t,group:e}={}){let n=await fe(),r=t?n.mocks.filter(o=>o.trackId===t):n.mocks;return e&&(r=r.filter(o=>o.group===e)),r.map(o=>({...o}))}async function nn(t){let n=(await fe()).mocks.find(r=>r.id===t);return n?{...n}:null}async function Vp({trackId:t,name:e,html:n,image:r,cwd:o,width:s,group:a,box:i,agent:c,prompt:l}){let u=Dv({trackId:t,name:e,cwd:o,width:s}),f=typeof n=="string"&&n.trim();if(!f&&!r)throw new Error("html (string) or image (png) required");if(r&&!Wp(r))throw new Error("image must be a PNG");let d=await fe(),p={id:yv().slice(0,8),trackId:t,box:i||null,agent:c||null,name:e.trim(),prompt:l?String(l).slice(0,500):null,group:a?String(a).trim():null,cwd:o,width:u,height:null,dpr:null,state:"queued",source:f?"html":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await kt.mkdir(fa(o),{recursive:!0}),d.mocks.push(p),f?(await kt.writeFile(Wt(p,1).html,n),await de(),await ee(p,"created"),en(p),Nn(p,1,"html"),qp(()=>Kp(p.id,1))):(await Jp(p,1,r),await de(),await ee(p,"created"),await ee(p,"ready",{version:1,width:p.width,height:p.height,dpr:p.dpr}),en(p),Nn(p,1,"png")),{...p}}async function zp(t,{html:e,image:n,width:r}={}){let s=(await fe()).mocks.find(c=>c.id===t);if(!s)throw new Error("mock not found");let a=typeof e=="string"&&e.trim();if(!a&&!n)throw new Error("html (string) or image (png) required");if(n&&!Wp(n))throw new Error("image must be a PNG");if(r!=null){let c=Number(r);if(!Number.isInteger(c)||c<320||c>4e3)throw new Error("invalid width (320-4000)");s.width=c}let i=s.version+1;return s.version=i,s.updatedAt=Date.now(),s.source=a?"html":"image",await kt.mkdir(fa(s.cwd),{recursive:!0}),a?(s.state="queued",await kt.writeFile(Wt(s,i).html,e),await de(),await ee(s,"updated",{version:i}),en(s),Nn(s,i,"html"),qp(()=>Kp(s.id,i))):(await Jp(s,i,n),await de(),await ee(s,"updated",{version:i}),await ee(s,"ready",{version:i,width:s.width,height:s.height,dpr:s.dpr}),en(s),Nn(s,i,"png")),{...s}}async function Jp(t,e,n){let r=Rc(n);if(!r)throw new Error("image must be a PNG");let o=Wt(t,e);await kt.writeFile(o.png,n),Gp(t,{v:e,source:"image",htmlRel:null,pngRel:Bt.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function Xp(t){let e=await fe(),n=e.mocks.find(o=>o.id===t);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?e.mocks.filter(o=>o.trackId===n.trackId&&o.group===n.group&&o.id!==t&&o.readyVersion!=null).map(o=>({mockId:o.id,name:o.name})):[];return await ee(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Yp(t){let e=await fe(),n=e.mocks.findIndex(o=>o.id===t);if(n===-1)throw new Error("mock not found");let r=e.mocks[n];e.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=Wt(r,o);await kt.rm(s.html,{force:!0}).catch(()=>{}),await kt.rm(s.png,{force:!0}).catch(()=>{})}return await de(),await ee(r,"deleted"),Qt("mocks",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Zp(t){let n=(await fe()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=Bt.join(Fp,"mock-archive",t.id),o=fa(t.cwd),s=Bt.join(r,".bostrat","mocks");await kt.mkdir(s,{recursive:!0});let a=await kt.readdir(o).catch(()=>[]);for(let i of a)await kt.copyFile(Bt.join(o,i),Bt.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel]){if(!l)continue;if(!(await kt.stat(Bt.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=t.name||t.id,i.updatedAt=Date.now(),en(i)}return await de(),n.length}async function Qp(t){let e=await fe(),n=e.mocks.filter(r=>r.trackId===t);if(n.length){e.mocks=e.mocks.filter(r=>r.trackId!==t),await de();for(let r of n)Qt("mocks",r.id).catch(()=>{})}return n.length}async function tm(){if(!pt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await At("mocks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await fe();for(let r of n.mocks){await en(r);for(let o of r.versions||[])o.htmlRel&&await Nn(r,o.v,"html"),o.pngRel&&await Nn(r,o.v,"png")}return{pushed:n.mocks.length,source:"plane"}}import Io from"node:fs/promises";import Dt from"node:path";import jv from"node:os";import Lv from"node:net";import{randomUUID as Fv}from"node:crypto";import{execFile as Uv}from"node:child_process";import{promisify as Hv}from"node:util";var me=Hv(Uv),Wv=process.env.BOSTRAT_STATE_DIR||Dt.join(process.env.HOME||jv.tmpdir(),".bostrat"),sm=Dt.join(Wv,"views.json"),[em,nm]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),rn=Number(process.env.BOSTRAT_VIEW_MAX)||3,qv="bostrat-view-",xr=t=>`${qv}${t}`,rm=new Set(["bostrat","www","api","views","app"]),pa=t=>Object.assign(new Error(t),{status:400}),Kv=new Set(["npm","pnpm","yarn","bun"]),Cc="package.json",Gv=/[|;&`]|\$\(/,Vv=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),zv=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,am=t=>String(t).trim().split(/\s+/).filter(Boolean),Jv=/^[A-Za-z_][A-Za-z0-9_]*=/;function Xv(t){for(let e of am(t))if(!Jv.test(e))return e;return null}function im(t){let e=am(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function Yv(t,e){let n=im(e);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Dt.resolve(t,o[1]);if(n[r]==="--prefix"&&n[r+1])return Dt.resolve(t,n[r+1])}return t}var Zv=t=>Io.stat(t).then(e=>e.isDirectory(),()=>!1),cm=t=>Io.stat(t).then(e=>e.isFile(),()=>!1),ma=t=>Io.realpath(t).catch(()=>Dt.resolve(t));async function Qv(t,e){if(Dt.resolve(t)===Dt.resolve(e))return!0;let[n,r]=await Promise.all([ma(t),ma(e)]);return n===r}async function tT(t){let n=(await Io.readdir(t,{withFileTypes:!0}).catch(()=>[])).filter(o=>o.isDirectory()&&!o.name.startsWith(".")&&o.name!=="node_modules");return(await Promise.all(n.map(async o=>await cm(Dt.join(t,o.name,Cc))?`./${o.name}`:null))).filter(Boolean)}async function eT(t,e){let[n,r]=await Promise.all([ma(t),ma(e)]),o=Dt.relative(n,r);if(o&&(o.startsWith("..")||Dt.isAbsolute(o)))throw pa(`serve directory escapes the Track workspace: ${e}`)}async function nT(t,e){if(!await Zv(t))throw pa(`serve directory does not exist: ${t}`);let n=Xv(e);if(!n)return;if(!n.includes("/")&&!await cc(n,{loginShell:!0,failOpen:!0}))throw pa(`\`${n}\` is not on the box's PATH \u2014 install it in the box first`);if(Gv.test(e)||Vv.has(n)||!Kv.has(n)||!im(e).includes("run")||zv.test(e))return;let r=Yv(t,e);if(await cm(Dt.join(r,Cc)))return;let o=await tT(r),s=o.length?` \u2014 found one in ${o.join(", ")}; run bostrat-view from there`:"";throw pa(`no ${Cc} in ${r}${s}`)}var pe=null,$c=null;async function Oe(){return pe||($c||=(async()=>{try{pe=JSON.parse(await Io.readFile(sm,"utf8"))}catch{pe={views:[],adoptWatermarks:{}}}finally{$c=null}return pe.views||=[],pe.adoptWatermarks||={},pe})(),$c)}async function he(){await Nt(sm,pe)}function $e(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,payload:{ts:Date.now(),change:e,viewId:t.id,name:t.name,slug:t.slug,port:t.port,...n}}).catch(()=>{})}var _o=new Map,Pc=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,lm=12e3;function Bc(t){return _o.get(t)||null}function um(t,{name:e,box:n}={}){let r=_o.get(t);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o={trackId:t,name:e||"preview",box:n||null,step:"spinning-up",startedAt:Date.now(),finishedAt:null,viewId:null,error:null};return _o.set(t,o),o}function Mc(t,e){let n=_o.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function ha(t,{error:e=null}={}){let n=_o.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function dm(t){let e=[];for(let n of t?.events||[])n.type!=="view-spinup"||!n.box||!n.startedAt||!n.endedAt||e.push({box:n.box,from:n.startedAt,to:n.endedAt});return e}function Nc(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function rT(t,e){let n=Nc(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!rm.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!rm.has(s))return s}}function Ro(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=Lv.connect({port:t,host:"127.0.0.1"}),o=s=>{r.destroy(),n(s)};r.setTimeout(e,()=>o(!1)),r.once("connect",()=>o(!0)),r.once("error",()=>o(!1))})}async function Dc(t){if(process.platform!=="darwin"){await me("fuser",["-k",`${t}/tcp`]).catch(()=>{});return}let e="";try{({stdout:e}=await me("lsof",["-t","-sTCP:LISTEN","-i",`tcp:${t}`]))}catch{return}for(let n of e.split(/\s+/).filter(Boolean)){let r=Number(n);if(!(!r||r===process.pid))try{process.kill(r,"SIGKILL")}catch{}}}function fm(t=process.env){return new Set([Number(t.PORT)||3362,Number(t.BOSTRAT_TLS_PORT)||3443])}async function oT(t,e){let n=new Set(t.views.map(o=>o.port)),r=fm();if(e){let o=Number(e);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=em;o<=nm;o++)if(!(n.has(o)||r.has(o))&&!await Ro(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${em}-${nm}`)}async function sT(t){try{let{stdout:e}=await me(P,B("list-sessions","-F","#{session_name}"));return e.split(`
|
|
77
|
-
`).some(n=>n.trim()===
|
|
76
|
+
`)}`:e),this.name="MockRenderError"}},Fn=null,zc=null;async function ge(){return Fn||(zc||=(async()=>{try{Fn=JSON.parse(await At.readFile(wm,"utf8"))}catch{Fn={mocks:[]}}finally{zc=null}return Fn.mocks||=[],Fn})(),zc)}async function we(){await Lt(wm,Fn)}var un=t=>Dt("mocks",{...t},{trackId:t.trackId}).catch(()=>{}),Un=(t,e,n)=>rn("mocks",t.id,e,n,Jt(t,e)[n]).catch(()=>{});function se(t,e,n={}){return I({id:t.trackId,cwd:t.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,mockId:t.id,name:t.name,...t.group?{group:t.group}:{},...n}}).catch(()=>{})}var Ea=t=>Ht.join(t,".bostrat","mocks");function Jt(t,e){let n=Ea(t.cwd);return{html:Ht.join(n,`${t.id}.v${e}.html`),png:Ht.join(n,`${t.id}.v${e}.png`)}}function Ne(t,e=null){let n=t.versions||[];return e!=null?n.find(r=>r.v===Number(e))||null:n.length?n[n.length-1]:null}function Xc(t){return t.length<24||t.readUInt32BE(12)!==1229472850?null:{width:t.readUInt32BE(16),height:t.readUInt32BE(20)}}var NT=Buffer.from([137,80,78,71,13,10,26,10]);function ym(t){return Buffer.isBuffer(t)&&t.length>24&&t.subarray(0,8).equals(NT)}async function PT(t){let e=Buffer.alloc(24),n=await At.open(t,"r");try{await n.read(e,0,24,0)}finally{await n.close()}return e}function DT({imgW:t,imgH:e,bbox:n,pad:r,align:o=1}){if(!n||!(t>0)||!(e>0))return null;let s=n.right-n.left+1,a=n.bottom-n.top+1;if(s<=0||a<=0||s>=t*.9&&a>=e*.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 BT(t,e){return t.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}}},e.toString("base64"))}async function jT(t,e,{bg:n,crop:r}){let o=await t.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:e.toString("base64"),bg:n,crop:r});return Buffer.from(o,"base64")}async function LT({htmlPath:t,outPath:e,width:n}){let r=Date.now(),o;try{o=await ET.launch({executablePath:RT,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new oe("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:_r})).newPage();a.setDefaultTimeout(Ir),await a.goto(`file://${t}`,{waitUntil:"load",timeout:Ir}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:Ir});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await BT(a,i),u=l&&DT({...l,pad:MT*_r,align:_r});u&&(i=await jT(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<gm)throw new oe(`capture produced a ${i.length}-byte image`);let c=Xc(i);if(!c)throw new oe("output is not a PNG");return await At.writeFile(e,i),{width:Math.round(c.width/_r),height:Math.round(c.height/_r),dpr:_r,renderMs:Date.now()-r}}catch(s){throw s instanceof oe?s:new oe(/timeout/i.test(s?.message||"")?`render timed out after ${Ir}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function pm({htmlPath:t,outPath:e,width:n}){let r=await At.mkdtemp(Ht.join(mm.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await _T($T,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",e,`file://${t}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:Ir,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",new oe(c?.killed?`render timed out after ${Ir}ms`:"firefox exited non-zero",s)}let a=await At.stat(e).catch(()=>null);if(!a||a.size<gm)throw new oe(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Xc(await PT(e));if(!i)throw new oe("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await At.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function FT(t){if(IT==="firefox")return pm(t);try{return await LT(t)}catch(e){if(/failed to launch/i.test(e?.message||""))return pm(t);throw e}}var UT=FT;var Jc=Promise.resolve();function bm(t){return Jc=Jc.then(t,t),Jc}async function km(t,e){let n=await ge(),r=n.mocks.find(o=>o.id===t);if(r){r.state="rendering",await we(),await se(r,"rendering",{version:e});try{let o=await Aa();if(o<CT)throw new oe(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Jt(r,e),a=await UT({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===t)){await At.rm(s.png,{force:!0}).catch(()=>{});return}xm(r,{v:e,source:"html",htmlRel:Ht.relative(r.cwd,s.html),pngRel:Ht.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await we(),await se(r,"ready",{version:e,width:a.width,height:a.height,dpr:a.dpr}),un(r),Un(r,e,"png")}catch(o){if(!n.mocks.some(s=>s.id===t))return;r.state="error",r.error=o?.message||String(o),await we(),await se(r,"error",{version:e,error:r.error}),un(r)}}}function xm(t,e){t.versions=(t.versions||[]).filter(r=>r.v!==e.v).concat(e),t.versions.sort((r,o)=>r.v-o.v);let n=t.versions[t.versions.length-1];t.readyVersion=n.v,t.height=n.height,t.dpr=n.dpr,t.renderMs=n.renderMs??null,n.v===t.version&&(t.state="ready",t.error=null)}function HT({trackId:t,name:e,cwd:n,width:r}){if(!t)throw new Error("trackId required");if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(!n)throw new Error("cwd required");let o=r==null?OT:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Hn({trackId:t,group:e}={}){let n=await ge(),r=t?n.mocks.filter(o=>o.trackId===t):n.mocks;return e&&(r=r.filter(o=>o.group===e)),r.map(o=>({...o}))}async function dn(t){let n=(await ge()).mocks.find(r=>r.id===t);return n?{...n}:null}async function Sm({trackId:t,name:e,html:n,image:r,cwd:o,width:s,group:a,box:i,agent:c,prompt:l}){let u=HT({trackId:t,name:e,cwd:o,width:s}),f=typeof n=="string"&&n.trim();if(!f&&!r)throw new Error("html (string) or image (png) required");if(r&&!ym(r))throw new Error("image must be a PNG");let d=await ge(),p={id:vT().slice(0,8),trackId:t,box:i||null,agent:c||null,name:e.trim(),prompt:l?String(l).slice(0,500):null,group:a?String(a).trim():null,cwd:o,width:u,height:null,dpr:null,state:"queued",source:f?"html":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await At.mkdir(Ea(o),{recursive:!0}),d.mocks.push(p),f?(await At.writeFile(Jt(p,1).html,n),await we(),await se(p,"created"),un(p),Un(p,1,"html"),bm(()=>km(p.id,1))):(await Tm(p,1,r),await we(),await se(p,"created"),await se(p,"ready",{version:1,width:p.width,height:p.height,dpr:p.dpr}),un(p),Un(p,1,"png")),{...p}}async function vm(t,{html:e,image:n,width:r}={}){let s=(await ge()).mocks.find(c=>c.id===t);if(!s)throw new Error("mock not found");let a=typeof e=="string"&&e.trim();if(!a&&!n)throw new Error("html (string) or image (png) required");if(n&&!ym(n))throw new Error("image must be a PNG");if(r!=null){let c=Number(r);if(!Number.isInteger(c)||c<320||c>4e3)throw new Error("invalid width (320-4000)");s.width=c}let i=s.version+1;return s.version=i,s.updatedAt=Date.now(),s.source=a?"html":"image",await At.mkdir(Ea(s.cwd),{recursive:!0}),a?(s.state="queued",await At.writeFile(Jt(s,i).html,e),await we(),await se(s,"updated",{version:i}),un(s),Un(s,i,"html"),bm(()=>km(s.id,i))):(await Tm(s,i,n),await we(),await se(s,"updated",{version:i}),await se(s,"ready",{version:i,width:s.width,height:s.height,dpr:s.dpr}),un(s),Un(s,i,"png")),{...s}}async function Tm(t,e,n){let r=Xc(n);if(!r)throw new Error("image must be a PNG");let o=Jt(t,e);await At.writeFile(o.png,n),xm(t,{v:e,source:"image",htmlRel:null,pngRel:Ht.relative(t.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),t.width=r.width}async function Am(t){let e=await ge(),n=e.mocks.find(o=>o.id===t);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?e.mocks.filter(o=>o.trackId===n.trackId&&o.group===n.group&&o.id!==t&&o.readyVersion!=null).map(o=>({mockId:o.id,name:o.name})):[];return await se(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Em(t){let e=await ge(),n=e.mocks.findIndex(o=>o.id===t);if(n===-1)throw new Error("mock not found");let r=e.mocks[n];e.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=Jt(r,o);await At.rm(s.html,{force:!0}).catch(()=>{}),await At.rm(s.png,{force:!0}).catch(()=>{})}return await we(),await se(r,"deleted"),re("mocks",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function _m(t){let n=(await ge()).mocks.filter(i=>i.trackId===t.id);if(!n.length)return 0;let r=Ht.join(hm,"mock-archive",t.id),o=Ea(t.cwd),s=Ht.join(r,".bostrat","mocks");await At.mkdir(s,{recursive:!0});let a=await At.readdir(o).catch(()=>[]);for(let i of a)await At.copyFile(Ht.join(o,i),Ht.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel]){if(!l)continue;if(!(await At.stat(Ht.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=t.name||t.id,i.updatedAt=Date.now(),un(i)}return await we(),n.length}async function Im(t){let e=await ge(),n=e.mocks.filter(r=>r.trackId===t);if(n.length){e.mocks=e.mocks.filter(r=>r.trackId!==t),await we();for(let r of n)re("mocks",r.id).catch(()=>{})}return n.length}async function Rm(){if(!wt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ot("mocks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await ge();for(let r of n.mocks){await un(r);for(let o of r.versions||[])o.htmlRel&&await Un(r,o.v,"html"),o.pngRel&&await Un(r,o.v,"png")}return{pushed:n.mocks.length,source:"plane"}}import jo from"node:fs/promises";import Wt from"node:path";import WT from"node:os";import qT from"node:net";import{randomUUID as KT}from"node:crypto";import{execFile as GT}from"node:child_process";import{promisify as VT}from"node:util";var be=VT(GT),zT=process.env.BOSTRAT_STATE_DIR||Wt.join(process.env.HOME||WT.tmpdir(),".bostrat"),Nm=Wt.join(zT,"views.json"),[$m,Om]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(t=>Number(t)||0),fn=Number(process.env.BOSTRAT_VIEW_MAX)||3,JT="bostrat-view-",Rr=t=>`${JT}${t}`,Mm=new Set(["bostrat","www","api","views","app"]),_a=t=>Object.assign(new Error(t),{status:400}),XT=new Set(["npm","pnpm","yarn","bun"]),Qc="package.json",YT=/[|;&`]|\$\(/,ZT=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),QT=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Pm=t=>String(t).trim().split(/\s+/).filter(Boolean),tA=/^[A-Za-z_][A-Za-z0-9_]*=/;function eA(t){for(let e of Pm(t))if(!tA.test(e))return e;return null}function Dm(t){let e=Pm(t),n=e.indexOf("--");return n===-1?e:e.slice(0,n)}function nA(t,e){let n=Dm(e);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return Wt.resolve(t,o[1]);if(n[r]==="--prefix"&&n[r+1])return Wt.resolve(t,n[r+1])}return t}var rA=t=>jo.stat(t).then(e=>e.isDirectory(),()=>!1),Bm=t=>jo.stat(t).then(e=>e.isFile(),()=>!1),Ia=t=>jo.realpath(t).catch(()=>Wt.resolve(t));async function oA(t,e){if(Wt.resolve(t)===Wt.resolve(e))return!0;let[n,r]=await Promise.all([Ia(t),Ia(e)]);return n===r}async function sA(t){let n=(await jo.readdir(t,{withFileTypes:!0}).catch(()=>[])).filter(o=>o.isDirectory()&&!o.name.startsWith(".")&&o.name!=="node_modules");return(await Promise.all(n.map(async o=>await Bm(Wt.join(t,o.name,Qc))?`./${o.name}`:null))).filter(Boolean)}async function aA(t,e){let[n,r]=await Promise.all([Ia(t),Ia(e)]),o=Wt.relative(n,r);if(o&&(o.startsWith("..")||Wt.isAbsolute(o)))throw _a(`serve directory escapes the Track workspace: ${e}`)}async function iA(t,e){if(!await rA(t))throw _a(`serve directory does not exist: ${t}`);let n=eA(e);if(!n)return;if(!n.includes("/")&&!await tp(n,{loginShell:!0,failOpen:!0}))throw _a(`\`${n}\` is not on the box's PATH \u2014 install it in the box first`);if(YT.test(e)||ZT.has(n)||!XT.has(n)||!Dm(e).includes("run")||QT.test(e))return;let r=nA(t,e);if(await Bm(Wt.join(r,Qc)))return;let o=await sA(r),s=o.length?` \u2014 found one in ${o.join(", ")}; run bostrat-view from there`:"";throw _a(`no ${Qc} in ${r}${s}`)}var ye=null,Yc=null;async function Be(){return ye||(Yc||=(async()=>{try{ye=JSON.parse(await jo.readFile(Nm,"utf8"))}catch{ye={views:[],adoptWatermarks:{}}}finally{Yc=null}return ye.views||=[],ye.adoptWatermarks||={},ye})(),Yc)}async function ke(){await Lt(Nm,ye)}function De(t,e,n={}){return I({id:t.trackId,cwd:t.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,payload:{ts:Date.now(),change:e,viewId:t.id,name:t.name,slug:t.slug,port:t.port,...n}}).catch(()=>{})}var Bo=new Map,nl=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,jm=12e3;function rl(t){return Bo.get(t)||null}function Lm(t,{name:e,box:n}={}){let r=Bo.get(t);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o={trackId:t,name:e||"preview",box:n||null,step:"spinning-up",startedAt:Date.now(),finishedAt:null,viewId:null,error:null};return Bo.set(t,o),o}function tl(t,e){let n=Bo.get(t);n&&!n.finishedAt&&!n.viewId&&(n.viewId=e,n.step="booting")}function Ra(t,{error:e=null}={}){let n=Bo.get(t);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),e&&(n.error=e),n)}function Fm(t){let e=[];for(let n of t?.events||[])n.type!=="view-spinup"||!n.box||!n.startedAt||!n.endedAt||e.push({box:n.box,from:n.startedAt,to:n.endedAt});return e}function el(t){return String(t||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function cA(t,e){let n=el(e),r=new Set(t.views.map(o=>o.slug));if(!r.has(n)&&!Mm.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!Mm.has(s))return s}}function Lo(t,{timeoutMs:e=1200}={}){return new Promise(n=>{let r=qT.connect({port:t,host:"127.0.0.1"}),o=s=>{r.destroy(),n(s)};r.setTimeout(e,()=>o(!1)),r.once("connect",()=>o(!0)),r.once("error",()=>o(!1))})}async function ol(t){if(process.platform!=="darwin"){await be("fuser",["-k",`${t}/tcp`]).catch(()=>{});return}let e="";try{({stdout:e}=await be("lsof",["-t","-sTCP:LISTEN","-i",`tcp:${t}`]))}catch{return}for(let n of e.split(/\s+/).filter(Boolean)){let r=Number(n);if(!(!r||r===process.pid))try{process.kill(r,"SIGKILL")}catch{}}}function Um(t=process.env){return new Set([Number(t.PORT)||3362,Number(t.BOSTRAT_TLS_PORT)||3443])}async function lA(t,e){let n=new Set(t.views.map(o=>o.port)),r=Um();if(e){let o=Number(e);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=$m;o<=Om;o++)if(!(n.has(o)||r.has(o))&&!await Lo(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${$m}-${Om}`)}async function uA(t){try{let{stdout:e}=await be(M,N("list-sessions","-F","#{session_name}"));return e.split(`
|
|
77
|
+
`).some(n=>n.trim()===Rr(t))}catch{return!1}}async function Fo(t){try{await be(M,N("kill-session","-t",Rr(t)))}catch{}}async function sl(t){if(!await Ro())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let e=Rr(t.slug),n=t.serveCwd??t.cwd,r=Wt.dirname(process.execPath);await be(M,N("new-session","-d","-s",e,"-c",n)),await be(M,N("send-keys","-t",e,"-l",`export PORT=${t.port} PATH="${r}:$PATH"`)),await be(M,N("send-keys","-t",e,"Enter")),await be(M,N("send-keys","-t",e,"-l",t.cmd)),await be(M,N("send-keys","-t",e,"Enter"))}async function Cm(t,e=12){try{let{stdout:n}=await be(M,N("capture-pane","-t",Rr(t),"-p"));return String(n||"").trimEnd().split(`
|
|
78
78
|
`).filter(Boolean).slice(-e).join(`
|
|
79
|
-
`)}catch{return""}}var Re=new Map,aT=12e4;async function iT(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await sT(t.slug):!0;if(e&&!n){let s=t.state==="starting";return t.state=s?"error":"stopped",s&&(t.error=await om(t.slug)||"server exited before listening"),Re.delete(t.id),await $e(t,t.state,t.error?{error:t.error}:{}),!0}if(await Ro(t.port))return Re.delete(t.id),t.lastHealthyAt=Date.now(),t.state!=="live"?(t.state="live",t.error=null,await $e(t,"live"),!0):!1;if(t.state==="starting")return Date.now()-t.createdAt>aT?(t.state="error",t.error=await om(t.slug)||"never started listening",await $e(t,"error",{error:t.error}),!0):!1;let o=(Re.get(t.id)||0)+1;return Re.set(t.id,o),o>=3&&t.state==="live"?(t.state="unhealthy",!0):!1}var Oc=!1;async function wa(){if(!Oc){Oc=!0;try{let t=await Oe(),e=!1;for(let n of t.views)await iT(n)&&(e=!0);e&&await he()}finally{Oc=!1}}}async function ga({trackId:t}={}){await wa();let e=await Oe();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function pm(t){let n=(await Oe()).views.find(r=>r.id===t);return n?{...n}:null}function Lc(t){return pe&&pe.views.find(e=>e.slug===t)||null}async function Fc({trackId:t,name:e,cmd:n,cwd:r,serveCwd:o,port:s,box:a,origin:i="agent",branch:c=null}){if(!t)throw new Error("trackId required");if(typeof e!="string"||!e.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=Dt.resolve(r,o??"."),f=i==="detected"?null:async()=>{await eT(r,u),await nT(u,n)},d=await Oe();if(i!=="detected"){let h=Nc(e),y=String(n).trim(),b=d.views.find(S=>S.trackId===t&&(S.slug===h||Nc(S.name)===h||S.cmd===y&&Dt.resolve(S.serveCwd??S.cwd)===u));if(b)return cT(d,b,{cmd:y,box:a,branch:c,serveCwd:l?u:void 0,validate:f})}if(f&&await f(),d.views.filter(h=>h.state==="live"||h.state==="starting").length>=rn)throw new Error(`view cap reached (${rn} live) \u2014 stop one first`);let m={id:Fv().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:rT(d,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await oT(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await jc(m),d.views.push(m),await he(),await $e(m,"created",{origin:i}),Mc(t,m.id),i==="detected"&&await Ro(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await he(),await $e(m,"live")),{...m}}async function cT(t,e,{cmd:n,box:r,branch:o,serveCwd:s,validate:a}){r&&(e.box=r),o&&(e.branch=o);let i=e.cmd!==n,c=s!==void 0&&!await Qv(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await Ro(e.port))return await he(),Mc(e.trackId,e.id),{...e,reused:!0};if(a&&await a(),t.views.filter(f=>f.id!==e.id&&(f.state==="live"||f.state==="starting")).length>=rn)throw new Error(`view cap reached (${rn} live) \u2014 stop one first`);return e.origin==="detected"?(await Dc(e.port),e.origin="agent"):await $o(e.slug),e.cmd=n,s!==void 0&&(e.serveCwd=s),e.state="starting",e.error=null,e.createdAt=Date.now(),Re.delete(e.id),await jc(e),await he(),await $e(e,"created",{restarted:!0,reused:!0}),Mc(e.trackId,e.id),{...e,reused:!0}}async function mm(t){let n=(await Oe()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await Dc(n.port):await $o(n.slug),n.state!=="stopped"&&(n.state="stopped",Re.delete(n.id),await he(),await $e(n,"stopped")),{...n}}async function hm(t){let e=await Oe(),n=e.views.find(o=>o.id===t);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(e.views.filter(o=>o.id!==t&&(o.state==="live"||o.state==="starting")).length>=rn)throw new Error(`view cap reached (${rn} live) \u2014 stop one first`);return await $o(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Re.delete(n.id),await jc(n),await he(),await $e(n,"created",{restarted:!0}),{...n}}async function wm(t){let e=await Oe(),n=e.views.findIndex(o=>o.id===t);if(n===-1)throw new Error("view not found");let r=e.views[n];return r.origin==="detected"?await Dc(r.port):await $o(r.slug),e.views.splice(n,1),Re.delete(r.id),await he(),await $e(r,"deleted"),{ok:!0,id:t,slug:r.slug}}async function gm(t){let e=await Oe(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await $o(r.slug),Re.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await he()),n.length}var lT=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g;async function ym(t,e){if(!t?.id||!Array.isArray(e)||!e.length)return[];let n=await Oe(),r=n.adoptWatermarks[t.id]??null,o=e.filter(c=>c.role==="assistant"&&c.ts!=null&&(r==null?!1:c.ts>r)),s=e.reduce((c,l)=>l.ts>c?l.ts:c,r??0);if(n.adoptWatermarks[t.id]!==s&&(n.adoptWatermarks[t.id]=s,await he()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(lT)){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(!fm().has(c)&&!n.views.some(l=>l.port===c)&&await Ro(c))try{i.push(await Fc({trackId:t.id,name:`${t.name} preview`,cmd:null,cwd:t.cwd,port:c,origin:"detected",branch:t.workspace?.branch??null}))}catch{}return i}import Ne from"node:fs/promises";import Pe from"node:path";import uT from"node:os";import{randomUUID as dT}from"node:crypto";var Bn=process.env.BOSTRAT_STATE_DIR||Pe.join(process.env.HOME||uT.tmpdir(),".bostrat"),km=Pe.join(Bn,"runbooks.json"),ya=Pe.join(Bn,"runbooks"),bm=256*1024,fT=[{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/}];function Oo(t){return fT.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}var Ce=null,Uc=null;async function Be(){return Ce||(Uc||=(async()=>{let t=!0;try{Ce=JSON.parse(await Ne.readFile(km,"utf8"))}catch{Ce={runbooks:[]},t=!1}finally{Uc=null}return Ce.runbooks||=[],!t&&pt()&&await pT().catch(()=>{}),Ce})(),Uc)}async function Dn(){await Nt(km,Ce)}var Co=t=>Promise.resolve(t).catch(()=>{});function ba(t){return Co($t("runbooks",{...t},{trackId:t.trackId}))}async function pT(){let{records:t,source:e}=await At("runbooks");if(!(e!=="plane"||!t.length)){await Ne.mkdir(ya,{recursive:!0});for(let n of t){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 Ne.writeFile(Me(r,o.v),s).catch(()=>{})}Ce.runbooks.push(r)}Ce.runbooks.length&&await Dn()}}function Sr(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,runbookId:t.id,name:t.name,status:t.status,...n}}).catch(()=>{})}function Me(t,e){return Pe.join(ya,`${t.id}.v${e}.md`)}function xm(t,e=null){let n=t.versions||[];return e!=null?n.find(r=>r.v===Number(e))||null:n.length?n[n.length-1]:null}function Sm(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>bm)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${bm/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Oo(t);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 e}function vm(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function ka({trackId:t,status:e}={}){let n=await Be(),r=t?n.runbooks.filter(o=>o.trackId===t):n.runbooks;return e&&(r=r.filter(o=>o.status===e)),r.map(o=>({...o}))}async function Hc(t){let n=(await Be()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function Tm({trackId:t,trackName:e,name:n,md:r,cwd:o,box:s,agent:a}){if(!t)throw new Error("trackId required");if(typeof n!="string"||!n.trim())throw new Error("name (string) required");let i=Sm(r),c=await Be(),l={id:dT().slice(0,8),trackId:t,trackName:e||t,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 Ne.mkdir(ya,{recursive:!0}),await Ne.writeFile(Me(l,1),r),l.versions.push({v:1,mdRel:Pe.relative(Bn,Me(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await Dn(),await Sr(l,"created",{version:1}),ba(l),Co(Je("runbooks",l.id,1,"md",Me(l,1))),{...l}}async function Am(t,{md:e,note:n}={}){let o=(await Be()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");vm(o,"update");let s=Sm(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Ne.mkdir(ya,{recursive:!0}),await Ne.writeFile(Me(o,a),e),o.versions.push({v:a,mdRel:Pe.relative(Bn,Me(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await Dn(),await Sr(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),ba(o),Co(Je("runbooks",o.id,a,"md",Me(o,a))),{...o}}async function Em(t,e){if(e!=="open"&&e!=="done")throw new Error('status must be "open" or "done"');let r=(await Be()).runbooks.find(o=>o.id===t);if(!r)throw new Error("runbook not found");return vm(r,e==="done"?"complete":"reopen"),r.status===e?{...r}:(r.status=e,r.updatedAt=Date.now(),await Dn(),await Sr(r,e==="done"?"done":"reopened"),ba(r),{...r})}async function _m(t){let e=await Be(),n=e.runbooks.findIndex(o=>o.id===t);if(n===-1)throw new Error("runbook not found");let r=e.runbooks[n];e.runbooks.splice(n,1);for(let o of r.versions||[])await Ne.rm(Pe.join(Bn,o.mdRel),{force:!0}).catch(()=>{});return await Dn(),r.trackDeletedAt||await Sr(r,"deleted"),Co(Qt("runbooks",r.id)),{ok:!0,id:t,name:r.name}}async function Im(t){let n=(await Be()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await Sr(r,"orphaned"),r.cwd=null,ba(r);return n.length&&await Dn(),n.length}async function Rm(t){let e=await Be(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await Sr(r,"deleted");for(let o of r.versions||[])await Ne.rm(Pe.join(Bn,o.mdRel),{force:!0}).catch(()=>{});Co(Qt("runbooks",r.id))}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await Dn()),n.length}async function $m(){if(!pt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await At("runbooks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Be();for(let r of n.runbooks){await $t("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await Je("runbooks",r.id,o.v,"md",Pe.join(Bn,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}import Po from"node:fs/promises";import qm from"node:path";import WT from"node:os";import{randomUUID as Km}from"node:crypto";import mT from"node:fs/promises";import Om from"node:path";import hT from"node:os";var Cm=()=>Om.join(process.env.BOSTRAT_STATE_DIR||Om.join(process.env.HOME||hT.tmpdir(),".bostrat"),"integrations.json");async function De(){try{return JSON.parse(await mT.readFile(Cm(),"utf8"))||{}}catch{return{}}}var xa=["linked","prOpened","prMerged","create"],Wc=["prompt","auto","off"],wT={create:["prompt","off"]},Mm=t=>wT[t]??Wc;function Sa(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of xa){let s=Mm(o),a=s.includes(n)?n:"prompt",i=t.ticketFlow?.[o]??e.ticketFlow?.[o];r[o]=s.includes(i)?i:a}return r}async function jn(){let t=await De(),e=process.env.BOSTRAT_LINEAR_API_KEY||t.linear?.apiKey,n=process.env.BOSTRAT_LINEAR_DEFAULT_TEAM||t.linear?.defaultTeam||null;return e?{apiKey:e,ticketFlow:Sa(t),defaultTeam:n}:null}async function va(){let t=await De(),e=t.jira||{},n=process.env.BOSTRAT_JIRA_BASE_URL||e.baseUrl,r=process.env.BOSTRAT_JIRA_EMAIL||e.email,o=process.env.BOSTRAT_JIRA_API_TOKEN||e.apiToken;return!n||!r||!o?null:{baseUrl:String(n).replace(/\/$/,""),email:r,apiToken:o,tokenExpiresAt:e.tokenExpiresAt??null,ticketFlow:Sa(t)}}async function qc(){return Sa(await De())}async function Nm(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${xa.join(", ")}`);for(let[r,o]of e){if(!xa.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${xa.join(", ")}`);let s=Mm(r);if(!s.includes(o))throw new Error(s.length<Wc.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Wc.join(", ")}`)}let n=await De();return n.ticketFlow={...Sa(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Nt(Cm(),n,{mode:384}),n.ticketFlow}async function Mo(){let t=(await De()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function gT(){let t=(await De()).openai||{},e=process.env.BOSTRAT_OPENAI_API_KEY||t.apiKey;return e?{apiKey:e}:null}var yT={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function Ta(){let e=(await De()).voiceChat||{},n=e.provider||"openai",r=yT[n];if(!r)return null;let o=n==="openai"?await gT():null;return o?{provider:n,model:e.model||r.model,voice:e.voice||r.voice,apiKey:o.apiKey}:null}async function Pm(){let t=(await De()).cedar||{},e=process.env.BOSTRAT_CEDAR_INGEST_KEY||t.ingestKey,n=process.env.BOSTRAT_CEDAR_URL||t.url||"https://cedar-api.fly.dev";return e?{url:n,ingestKey:e}:null}async function Ln(){let t=(await De()).slack||{},e=process.env.BOSTRAT_SLACK_BOT_TOKEN||t.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||t.appToken;return!e||!n?null:{botToken:e,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||t.channel||null,maxConcurrent:Number.isInteger(t.maxConcurrent)&&t.maxConcurrent>0?t.maxConcurrent:2}}var Gc={};Nd(Gc,{createComment:()=>_T,findIssueUrl:()=>ST,issueStates:()=>$T,mdToAdf:()=>jm,moveIssue:()=>OT,parseIssueRef:()=>xT,resolveIssue:()=>vT,uploadImage:()=>IT,upsertCard:()=>TT,whoami:()=>Kc});var vr=t=>String(t.baseUrl||"").replace(/\/$/,""),Dm=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function Fn(t,e,n,r){let o=await fetch(`${vr(t)}${n}`,{method:e,headers:{authorization:Dm(t),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 bT=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,kT=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function xT(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(bT);return r?r[1].toUpperCase():null}let n=e.match(kT);return n?n[1]:null}function ST(t,e=null){let n=null;try{n=e?.baseUrl?new URL(e.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(t||"").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 vT(t,e){let n;try{n=await Fn(t,"GET",`/rest/api/3/issue/${encodeURIComponent(e)}?fields=summary,status`)}catch(r){throw r.status===404?new Error(`jira: issue "${e}" not found`):r}return{issueId:n.id,key:n.key,title:n.fields?.summary??null,url:`${vr(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function TT(t,e,{url:n,title:r,subtitle:o}){await Fn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/remotelink`,{globalId:n,application:{type:"com.bostrat.tracks",name:"Bostrat"},object:{url:n,title:r,summary:o,icon:{title:"Bostrat"}}})}var AT=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function ET(t){let e=[],n=0;for(let r of t.matchAll(AT)){r.index>n&&e.push({type:"text",text:t.slice(n,r.index)});let o=r[0];if(o.startsWith("![")){let[,s,a]=o.match(/^!\[([^\]]*)\]\(([^)\s]+)\)$/);e.push({type:"text",text:s||a,marks:[{type:"link",attrs:{href:a}}]})}else o.startsWith("**")?e.push({type:"text",text:o.slice(2,-2),marks:[{type:"strong"}]}):o.startsWith("`")?e.push({type:"text",text:o.slice(1,-1),marks:[{type:"code"}]}):o.startsWith("_")?e.push({type:"text",text:o.slice(1,-1),marks:[{type:"em"}]}):e.push({type:"text",text:o,marks:[{type:"link",attrs:{href:o}}]});n=r.index+o.length}return n<t.length&&e.push({type:"text",text:t.slice(n)}),e}function jm(t){let e=String(t??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
80
|
-
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...
|
|
79
|
+
`)}catch{return""}}var Pe=new Map,dA=12e4;async function fA(t){if(t.state==="stopped"||t.state==="error")return!1;let e=t.origin!=="detected",n=e?await uA(t.slug):!0;if(e&&!n){let s=t.state==="starting";return t.state=s?"error":"stopped",s&&(t.error=await Cm(t.slug)||"server exited before listening"),Pe.delete(t.id),await De(t,t.state,t.error?{error:t.error}:{}),!0}if(await Lo(t.port))return Pe.delete(t.id),t.lastHealthyAt=Date.now(),t.state!=="live"?(t.state="live",t.error=null,await De(t,"live"),!0):!1;if(t.state==="starting")return Date.now()-t.createdAt>dA?(t.state="error",t.error=await Cm(t.slug)||"never started listening",await De(t,"error",{error:t.error}),!0):!1;let o=(Pe.get(t.id)||0)+1;return Pe.set(t.id,o),o>=3&&t.state==="live"?(t.state="unhealthy",!0):!1}var Zc=!1;async function $a(){if(!Zc){Zc=!0;try{let t=await Be(),e=!1;for(let n of t.views)await fA(n)&&(e=!0);e&&await ke()}finally{Zc=!1}}}async function Oa({trackId:t}={}){await $a();let e=await Be();return(t?e.views.filter(r=>r.trackId===t):e.views).map(r=>({...r}))}async function Hm(t){let n=(await Be()).views.find(r=>r.id===t);return n?{...n}:null}function al(t){return ye&&ye.views.find(e=>e.slug===t)||null}async function il({trackId:t,name:e,cmd:n,cwd:r,serveCwd:o,port:s,box:a,origin:i="agent",branch:c=null}){if(!t)throw new Error("trackId required");if(typeof e!="string"||!e.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=Wt.resolve(r,o??"."),f=i==="detected"?null:async()=>{await aA(r,u),await iA(u,n)},d=await Be();if(i!=="detected"){let w=el(e),y=String(n).trim(),b=d.views.find(S=>S.trackId===t&&(S.slug===w||el(S.name)===w||S.cmd===y&&Wt.resolve(S.serveCwd??S.cwd)===u));if(b)return pA(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>=fn)throw new Error(`view cap reached (${fn} live) \u2014 stop one first`);let m={id:KT().slice(0,8),trackId:t,box:a||null,name:e.trim(),slug:cA(d,e),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await lA(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await sl(m),d.views.push(m),await ke(),await De(m,"created",{origin:i}),tl(t,m.id),i==="detected"&&await Lo(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await ke(),await De(m,"live")),{...m}}async function pA(t,e,{cmd:n,box:r,branch:o,serveCwd:s,validate:a}){r&&(e.box=r),o&&(e.branch=o);let i=e.cmd!==n,c=s!==void 0&&!await oA(e.serveCwd??e.cwd,s);if((e.state==="live"||e.state==="starting")&&!i&&!c&&await Lo(e.port))return await ke(),tl(e.trackId,e.id),{...e,reused:!0};if(a&&await a(),t.views.filter(f=>f.id!==e.id&&(f.state==="live"||f.state==="starting")).length>=fn)throw new Error(`view cap reached (${fn} live) \u2014 stop one first`);return e.origin==="detected"?(await ol(e.port),e.origin="agent"):await Fo(e.slug),e.cmd=n,s!==void 0&&(e.serveCwd=s),e.state="starting",e.error=null,e.createdAt=Date.now(),Pe.delete(e.id),await sl(e),await ke(),await De(e,"created",{restarted:!0,reused:!0}),tl(e.trackId,e.id),{...e,reused:!0}}async function Wm(t){let n=(await Be()).views.find(r=>r.id===t);if(!n)throw new Error("view not found");return n.origin==="detected"?await ol(n.port):await Fo(n.slug),n.state!=="stopped"&&(n.state="stopped",Pe.delete(n.id),await ke(),await De(n,"stopped")),{...n}}async function qm(t){let e=await Be(),n=e.views.find(o=>o.id===t);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(e.views.filter(o=>o.id!==t&&(o.state==="live"||o.state==="starting")).length>=fn)throw new Error(`view cap reached (${fn} live) \u2014 stop one first`);return await Fo(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Pe.delete(n.id),await sl(n),await ke(),await De(n,"created",{restarted:!0}),{...n}}async function Km(t){let e=await Be(),n=e.views.findIndex(o=>o.id===t);if(n===-1)throw new Error("view not found");let r=e.views[n];return r.origin==="detected"?await ol(r.port):await Fo(r.slug),e.views.splice(n,1),Pe.delete(r.id),await ke(),await De(r,"deleted"),{ok:!0,id:t,slug:r.slug}}async function Gm(t){let e=await Be(),n=e.views.filter(r=>r.trackId===t);for(let r of n)r.origin!=="detected"&&await Fo(r.slug),Pe.delete(r.id);return n.length&&(e.views=e.views.filter(r=>r.trackId!==t),await ke()),n.length}var mA=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g;async function Vm(t,e){if(!t?.id||!Array.isArray(e)||!e.length)return[];let n=await Be(),r=n.adoptWatermarks[t.id]??null,o=e.filter(c=>c.role==="assistant"&&c.ts!=null&&(r==null?!1:c.ts>r)),s=e.reduce((c,l)=>l.ts>c?l.ts:c,r??0);if(n.adoptWatermarks[t.id]!==s&&(n.adoptWatermarks[t.id]=s,await ke()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(mA)){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(!Um().has(c)&&!n.views.some(l=>l.port===c)&&await Lo(c))try{i.push(await il({trackId:t.id,name:`${t.name} preview`,cmd:null,cwd:t.cwd,port:c,origin:"detected",branch:t.workspace?.branch??null}))}catch{}return i}import Fe from"node:fs/promises";import Ue from"node:path";import hA from"node:os";import{randomUUID as wA}from"node:crypto";var Wn=process.env.BOSTRAT_STATE_DIR||Ue.join(process.env.HOME||hA.tmpdir(),".bostrat"),Jm=Ue.join(Wn,"runbooks.json"),Ma=Ue.join(Wn,"runbooks"),zm=256*1024,gA=[{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/}];function Uo(t){return gA.filter(({re:e})=>e.test(t)).map(({label:e})=>e)}var je=null,cl=null;async function He(){return je||(cl||=(async()=>{let t=!0;try{je=JSON.parse(await Fe.readFile(Jm,"utf8"))}catch{je={runbooks:[]},t=!1}finally{cl=null}return je.runbooks||=[],!t&&wt()&&await yA().catch(()=>{}),je})(),cl)}async function qn(){await Lt(Jm,je)}var Ho=t=>Promise.resolve(t).catch(()=>{});function Ca(t){return Ho(Dt("runbooks",{...t},{trackId:t.trackId}))}async function yA(){let{records:t,source:e}=await Ot("runbooks");if(!(e!=="plane"||!t.length)){await Fe.mkdir(Ma,{recursive:!0});for(let n of t){if(n.deletedAt||!n.data?.id)continue;let r=n.data;for(let o of r.versions||[]){let s=await ea("runbooks",r.id,o.v,"md");typeof s=="string"&&await Fe.writeFile(Le(r,o.v),s).catch(()=>{})}je.runbooks.push(r)}je.runbooks.length&&await qn()}}function $r(t,e,n={}){return I({id:t.trackId,cwd:t.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,runbookId:t.id,name:t.name,status:t.status,...n}}).catch(()=>{})}function Le(t,e){return Ue.join(Ma,`${t.id}.v${e}.md`)}function Xm(t,e=null){let n=t.versions||[];return e!=null?n.find(r=>r.v===Number(e))||null:n.length?n[n.length-1]:null}function Ym(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Buffer.byteLength(t,"utf8");if(e>zm)throw new Error(`runbook is ${Math.round(e/1024)} KB \u2014 over the ${zm/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=Uo(t);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 e}function Zm(t,e){if(t.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${e} it`)}async function Na({trackId:t,status:e}={}){let n=await He(),r=t?n.runbooks.filter(o=>o.trackId===t):n.runbooks;return e&&(r=r.filter(o=>o.status===e)),r.map(o=>({...o}))}async function ll(t){let n=(await He()).runbooks.find(r=>r.id===t);return n?{...n}:null}async function Qm({trackId:t,trackName:e,name:n,md:r,cwd:o,box:s,agent:a}){if(!t)throw new Error("trackId required");if(typeof n!="string"||!n.trim())throw new Error("name (string) required");let i=Ym(r),c=await He(),l={id:wA().slice(0,8),trackId:t,trackName:e||t,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 Fe.mkdir(Ma,{recursive:!0}),await Fe.writeFile(Le(l,1),r),l.versions.push({v:1,mdRel:Ue.relative(Wn,Le(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await qn(),await $r(l,"created",{version:1}),Ca(l),Ho(rn("runbooks",l.id,1,"md",Le(l,1))),{...l}}async function th(t,{md:e,note:n}={}){let o=(await He()).runbooks.find(i=>i.id===t);if(!o)throw new Error("runbook not found");Zm(o,"update");let s=Ym(e),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Fe.mkdir(Ma,{recursive:!0}),await Fe.writeFile(Le(o,a),e),o.versions.push({v:a,mdRel:Ue.relative(Wn,Le(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await qn(),await $r(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Ca(o),Ho(rn("runbooks",o.id,a,"md",Le(o,a))),{...o}}async function eh(t,e){if(e!=="open"&&e!=="done")throw new Error('status must be "open" or "done"');let r=(await He()).runbooks.find(o=>o.id===t);if(!r)throw new Error("runbook not found");return Zm(r,e==="done"?"complete":"reopen"),r.status===e?{...r}:(r.status=e,r.updatedAt=Date.now(),await qn(),await $r(r,e==="done"?"done":"reopened"),Ca(r),{...r})}async function nh(t){let e=await He(),n=e.runbooks.findIndex(o=>o.id===t);if(n===-1)throw new Error("runbook not found");let r=e.runbooks[n];e.runbooks.splice(n,1);for(let o of r.versions||[])await Fe.rm(Ue.join(Wn,o.mdRel),{force:!0}).catch(()=>{});return await qn(),r.trackDeletedAt||await $r(r,"deleted"),Ho(re("runbooks",r.id)),{ok:!0,id:t,name:r.name}}async function rh(t){let n=(await He()).runbooks.filter(r=>r.trackId===t&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await $r(r,"orphaned"),r.cwd=null,Ca(r);return n.length&&await qn(),n.length}async function oh(t){let e=await He(),n=e.runbooks.filter(r=>r.trackId===t);for(let r of n){await $r(r,"deleted");for(let o of r.versions||[])await Fe.rm(Ue.join(Wn,o.mdRel),{force:!0}).catch(()=>{});Ho(re("runbooks",r.id))}return n.length&&(e.runbooks=e.runbooks.filter(r=>r.trackId!==t),await qn()),n.length}async function sh(){if(!wt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ot("runbooks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await He();for(let r of n.runbooks){await Dt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await rn("runbooks",r.id,o.v,"md",Ue.join(Wn,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}import Ko from"node:fs/promises";import bh from"node:path";import zA from"node:os";import{randomUUID as kh}from"node:crypto";import bA from"node:fs/promises";import ah from"node:path";import kA from"node:os";var ih=()=>ah.join(process.env.BOSTRAT_STATE_DIR||ah.join(process.env.HOME||kA.tmpdir(),".bostrat"),"integrations.json");async function We(){try{return JSON.parse(await bA.readFile(ih(),"utf8"))||{}}catch{return{}}}var Pa=["linked","prOpened","prMerged","create"],ul=["prompt","auto","off"],xA={create:["prompt","off"]},ch=t=>xA[t]??ul;function Da(t){let e=t.linear||{},n=e.transitions===!0?"auto":"prompt",r={};for(let o of Pa){let s=ch(o),a=s.includes(n)?n:"prompt",i=t.ticketFlow?.[o]??e.ticketFlow?.[o];r[o]=s.includes(i)?i:a}return r}async function Kn(){let t=await We(),e=process.env.BOSTRAT_LINEAR_API_KEY||t.linear?.apiKey,n=process.env.BOSTRAT_LINEAR_DEFAULT_TEAM||t.linear?.defaultTeam||null;return e?{apiKey:e,ticketFlow:Da(t),defaultTeam:n}:null}async function Ba(){let t=await We(),e=t.jira||{},n=process.env.BOSTRAT_JIRA_BASE_URL||e.baseUrl,r=process.env.BOSTRAT_JIRA_EMAIL||e.email,o=process.env.BOSTRAT_JIRA_API_TOKEN||e.apiToken;return!n||!r||!o?null:{baseUrl:String(n).replace(/\/$/,""),email:r,apiToken:o,tokenExpiresAt:e.tokenExpiresAt??null,ticketFlow:Da(t)}}async function dl(){return Da(await We())}async function lh(t){let e=Object.entries(t||{});if(!e.length)throw new Error(`nothing to set \u2014 phases: ${Pa.join(", ")}`);for(let[r,o]of e){if(!Pa.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Pa.join(", ")}`);let s=ch(r);if(!s.includes(o))throw new Error(s.length<ul.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${ul.join(", ")}`)}let n=await We();return n.ticketFlow={...Da(n),...Object.fromEntries(e)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await Lt(ih(),n,{mode:384}),n.ticketFlow}async function Wo(){let t=(await We()).deepgram||{},e=process.env.DEEPGRAM_API_KEY||t.apiKey;return e?{apiKey:e}:null}async function SA(){let t=(await We()).openai||{},e=process.env.BOSTRAT_OPENAI_API_KEY||t.apiKey;return e?{apiKey:e}:null}var vA={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function ja(){let e=(await We()).voiceChat||{},n=e.provider||"openai",r=vA[n];if(!r)return null;let o=n==="openai"?await SA():null;return o?{provider:n,model:e.model||r.model,voice:e.voice||r.voice,apiKey:o.apiKey}:null}async function uh(){let t=(await We()).cedar||{},e=process.env.BOSTRAT_CEDAR_INGEST_KEY||t.ingestKey,n=process.env.BOSTRAT_CEDAR_URL||t.url||"https://cedar-api.fly.dev";return e?{url:n,ingestKey:e}:null}async function Gn(){let t=(await We()).slack||{},e=process.env.BOSTRAT_SLACK_BOT_TOKEN||t.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||t.appToken;return!e||!n?null:{botToken:e,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||t.channel||null,maxConcurrent:Number.isInteger(t.maxConcurrent)&&t.maxConcurrent>0?t.maxConcurrent:2}}var pl={};af(pl,{createComment:()=>MA,findIssueUrl:()=>_A,issueStates:()=>PA,mdToAdf:()=>ph,moveIssue:()=>DA,parseIssueRef:()=>EA,resolveIssue:()=>IA,uploadImage:()=>CA,upsertCard:()=>RA,whoami:()=>fl});var Or=t=>String(t.baseUrl||"").replace(/\/$/,""),fh=t=>`Basic ${Buffer.from(`${t.email}:${t.apiToken}`).toString("base64")}`;async function Vn(t,e,n,r){let o=await fetch(`${Or(t)}${n}`,{method:e,headers:{authorization:fh(t),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 TA=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,AA=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function EA(t){let e=String(t||"").trim();if(/^https?:\/\//i.test(e)){let r=e.match(TA);return r?r[1].toUpperCase():null}let n=e.match(AA);return n?n[1]:null}function _A(t,e=null){let n=null;try{n=e?.baseUrl?new URL(e.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(t||"").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 IA(t,e){let n;try{n=await Vn(t,"GET",`/rest/api/3/issue/${encodeURIComponent(e)}?fields=summary,status`)}catch(r){throw r.status===404?new Error(`jira: issue "${e}" not found`):r}return{issueId:n.id,key:n.key,title:n.fields?.summary??null,url:`${Or(t)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function RA(t,e,{url:n,title:r,subtitle:o}){await Vn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/remotelink`,{globalId:n,application:{type:"com.bostrat.tracks",name:"Bostrat"},object:{url:n,title:r,summary:o,icon:{title:"Bostrat"}}})}var $A=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function OA(t){let e=[],n=0;for(let r of t.matchAll($A)){r.index>n&&e.push({type:"text",text:t.slice(n,r.index)});let o=r[0];if(o.startsWith("![")){let[,s,a]=o.match(/^!\[([^\]]*)\]\(([^)\s]+)\)$/);e.push({type:"text",text:s||a,marks:[{type:"link",attrs:{href:a}}]})}else o.startsWith("**")?e.push({type:"text",text:o.slice(2,-2),marks:[{type:"strong"}]}):o.startsWith("`")?e.push({type:"text",text:o.slice(1,-1),marks:[{type:"code"}]}):o.startsWith("_")?e.push({type:"text",text:o.slice(1,-1),marks:[{type:"em"}]}):e.push({type:"text",text:o,marks:[{type:"link",attrs:{href:o}}]});n=r.index+o.length}return n<t.length&&e.push({type:"text",text:t.slice(n)}),e}function ph(t){let e=String(t??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
80
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...OA(o))}),{type:"paragraph",content:r.length?r:[{type:"text",text:" "}]}});return{type:"doc",version:1,content:e.length?e:[{type:"paragraph",content:[{type:"text",text:" "}]}]}}async function MA(t,e,n){let r=await Vn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/comment`,{body:ph(n)});return r?.id?`${Or(t)}/browse/${e.key}?focusedCommentId=${r.id}`:null}async function fl(t){let e=await Vn(t,"GET","/rest/api/3/myself"),n=null;try{n=new URL(Or(t)).host}catch{n=t.baseUrl??null}return{user:e?.displayName??e?.emailAddress??null,org:n}}async function CA(t,{issue:e,filename:n,contentType:r,buffer:o}){let s=new FormData;s.append("file",new Blob([o],{type:r}),n);let a=await fetch(`${Or(t)}/rest/api/3/issue/${encodeURIComponent(e)}/attachments`,{method:"POST",headers:{authorization:fh(t),"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??`${Or(t)}/rest/api/3/attachment/content/${c.id}`}var NA={new:"unstarted",indeterminate:"started",done:"completed"},dh=t=>NA[t?.key]??"started";async function PA(t,e){let n=encodeURIComponent(e.key),[r,o]=await Promise.all([Vn(t,"GET",`/rest/api/3/issue/${n}?fields=status`),Vn(t,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:dh(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:dh(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function DA(t,e,n){await Vn(t,"POST",`/rest/api/3/issue/${encodeURIComponent(e.key)}/transitions`,{transition:{id:String(n)}})}var gl={};af(gl,{createComment:()=>qA,createIssue:()=>wl,createProject:()=>hl,findIssueUrl:()=>UA,issueStates:()=>GA,moveIssue:()=>VA,parseIssueRef:()=>FA,resolveIssue:()=>HA,teams:()=>Mr,uploadImage:()=>KA,upsertCard:()=>WA,whoami:()=>ml});var BA=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function xe(t,e,n={}){let r=await fetch(BA(),{method:"POST",headers:{"content-type":"application/json",authorization:t.apiKey},body:JSON.stringify({query:e,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 jA=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,LA=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function FA(t){let e=String(t||"").trim(),n=e.match(jA);if(n)return n[1].toUpperCase();let r=e.match(LA);return r?r[1]:null}function UA(t){let e=String(t||"").match(/https?:\/\/linear\.app\/[^/\s]+\/issue\/[A-Za-z][A-Za-z0-9]{0,9}-\d+[^\s)]*/);return e?e[0]:null}async function HA(t,e){let r=(await xe(t,"query($id: String!) { issue(id: $id) { id identifier title url state { name } team { key } } }",{id:e})).issue;if(!r)throw new Error(`linear: issue "${e}" 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 WA(t,e,{url:n,title:r,subtitle:o}){let s=await xe(t,"mutation($input: AttachmentCreateInput!) { attachmentCreate(input: $input) { success attachment { id } } }",{input:{issueId:e.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 qA(t,e,n){let r=await xe(t,"mutation($input: CommentCreateInput!) { commentCreate(input: $input) { success comment { url } } }",{input:{issueId:e.issueId,body:n}});if(!r.commentCreate?.success)throw new Error("linear: commentCreate did not succeed");return r.commentCreate.comment?.url??null}async function ml(t){let e=await xe(t,"query { viewer { name email } organization { name urlKey } }");return{user:e.viewer?.name??e.viewer?.email??null,org:e.organization?.name??e.organization?.urlKey??null}}async function KA(t,{filename:e,contentType:n,buffer:r}){let o=await xe(t,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
81
81
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
82
82
|
success
|
|
83
83
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
84
84
|
}
|
|
85
|
-
}`,{contentType:n,filename:e,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 UT(t,e){let n=await we(t,"query($id: String!) { issue(id: $id) { state { id name type } team { states { nodes { id name type position } } } } }",{id:e.key});if(!n.issue)throw new Error(`linear: issue "${e.key}" not found`);return{current:n.issue.state??null,states:n.issue.team?.states?.nodes??[]}}async function HT(t,e,n){if(!(await we(t,"mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success } }",{id:e.issueId,input:{stateId:n}})).issueUpdate?.success)throw new Error("linear: issueUpdate did not succeed")}async function Tr(t){return((await we(t,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var Lm=255;async function zc(t,{teamId:e,name:n,description:r}){let o=r||null,s=!!o&&o.length>Lm,a=s?`${o.slice(0,Lm-1).trimEnd()}\u2026`:o,i=await we(t,"mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id url name } } }",{input:{teamIds:[e],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 Jc(t,{teamId:e,title:n,description:r,projectId:o,priority:s}){let a=await we(t,"mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier url title } } }",{input:{teamId:e,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 Gm={linear:{api:Xc,config:jn},jira:{api:Gc,config:va}};async function rl(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(Gm)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function Un(t){let e=Gm[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function ol(t,e){if(!t||t.name)return t;let n=e.find(r=>r.id===t.id);return n?{...t,name:n.name}:t}var Vm=()=>process.env.BOSTRAT_STATE_DIR||qm.join(process.env.HOME||WT.tmpdir(),".bostrat"),tl=()=>qm.join(Vm(),"tickets.json"),zm=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),Aa=t=>`${zm()}/track/${t}`,on=null,Yc=null,Zc=Promise.resolve();async function Do(){return on||(Yc??=Po.readFile(tl(),"utf8").then(t=>({tickets:JSON.parse(t)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:t,existed:e})=>{if(on=t,!e&&pt()){let{records:n,source:r}=await At("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;on[s]=i}Object.keys(on).length&&await el().catch(()=>{})}}return on}).finally(()=>Yc=null),Yc)}async function el(){let t=JSON.stringify({tickets:on},null,2),e=`${tl()}.${process.pid}.tmp`;await Po.mkdir(Vm(),{recursive:!0}),await Po.writeFile(e,t),await Po.rename(e,tl())}async function sn(t=null){if(Zc=Zc.then(el,el),await Zc.catch(()=>{}),!t)return;let e=on?.[t];e?$t("tickets",{id:t,updatedAt:Date.now(),...e},{trackId:t}).catch(()=>{}):Qt("tickets",t).catch(()=>{})}async function ge(t){return(await Do())[t]??null}async function Ea(){return{...await Do()}}async function Jm(){return(await rl()).length>0}async function Ar(t,e,{source:n="api"}={}){let r=await rl();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(e);if(f)try{s=await u.api.resolveIssue(u.cfg,f),o=u;break}catch(d){a=d}}if(!o)throw a||new Error(`"${e}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(t.boxes||[]).find(u=>u.name===t.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:t.name,branch:t.workspace?.branch??null,leadAgent:i?.agent??null,cwd:t.cwd??null,...t.workspace?.kind==="multi"?{repos:(t.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await Do())[t.id]=c,await sn(t.id),await R({id:t.id,cwd:t.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 Bo(t.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 No(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await Zm(t.id,c,"linked"),c}async function sl(t){let e=await Do();return e[t]?(delete e[t],await sn(t),!0):!1}var nl=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function Xm(t,e){if(!t.track?.cwd)return null;let n=ue(await L({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function qT(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${nl(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${nl(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function KT(t,e){let n=[];return t.track.leadAgent&&n.push(`lead ${t.track.leadAgent}`),t.track.branch&&n.push(t.track.branch),t.track.repos?.length&&n.push(t.track.repos.join(" + ")),t.pr?.number&&n.push(`PR #${t.pr.number} ${t.pr.change==="merged"?"merged":"open"}`),e&&n.push(nl(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function Bo(t,e){let n=await Un(e);if(!n)return;let r=await Xm(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:Aa(t),title:`Bostrat Track: ${e.track.name}`,subtitle:KT(e,r)})}function _a(t){let e=new URL(zm());return`${e.protocol}//${t}.${e.host}/`}var GT={linked:"linked",opened:"prOpened",merged:"prMerged"};function VT(t,e,n){let r=o=>e.filter(s=>s.type===o).sort((s,a)=>s.position-a.position);if(n==="linked"){if(["triage","backlog","unstarted"].includes(t?.type))return r("started")[0]??null}else if(n==="opened"){if(t?.type!=="completed")return r("started").find(o=>/review/i.test(o.name))??null}else if(n==="merged"&&t?.type!=="completed")return r("completed")[0]??null;return null}async function Ym(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await sn(t),await R({id:t,cwd:e.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:e.key,url:e.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function Zm(t,e,n){let r=await Un(e);if(!r)return;let o=r.cfg.ticketFlow?.[GT[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=ol(s,a),c=VT(i,a,n);if(!c||c.id===i?.id)return;let l=i?.name??e.state??null;if(o==="auto"){await r.api.moveIssue(r.cfg,e,c.id),await Ym(t,e,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await R({id:t,cwd:e.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:Km(),provider:e.provider,key:e.key,url:e.url,phase:n,from:l,to:{id:c.id,name:c.name,type:c.type},...e.pr?.number?{prNumber:e.pr.number}:{}}}).catch(()=>{})}async function Qm(t){let e=await ge(t);if(!e)throw new Error("no ticket linked to this Track");let n=await Un(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=ol(r,o);return s?.name&&e.state!==s.name&&(e.state=s.name,await sn(t)),{key:e.key,url:e.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 al(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await ge(t);if(!r)throw new Error("no ticket linked to this Track");let o=await Un(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=ol(s,a),c=a.find(u=>u.id===e);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 sn(t)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await Ym(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function th(t,e){let n=await ge(t);if(!n)throw new Error("no ticket linked to this Track");let r=await Un(n);if(!r)throw new Error("no ticketing integration configured");let o=String(e||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await R({id:t,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 eh(t,e,n){if(!e)throw new Error("offerId required");let r=await L({id:t.id,cwd:t.cwd}),o=r.find(a=>a.type==="ticket.move.offered"&&a.payload?.offerId===e);if(!o)throw new Error("unknown offer");if(r.some(a=>a.type==="ticket.move.answered"&&a.payload?.offerId===e))throw new Error("offer already answered");let s=null;return n&&(s=await al(t.id,o.payload.to?.id,{mode:"offer"})),await R({id:t.id,cwd:t.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:e,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:e,action:n?"moved":"dismiss",...s??{}}}var Fm=50;async function nh(){let t=await jn();if(!t)throw new Error("no ticketing integration configured");return Tr(t)}function zT(t){let e=t&&typeof t=="object"?t:{},n=(Array.isArray(e.issues)?e.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=e.epic&&String(e.epic.name||"").trim()?{name:String(e.epic.name).trim(),description:e.epic.description?String(e.epic.description):null}:null;if(!n.length&&!r)throw new Error("a plan needs an epic or at least one issue");if(n.length>Fm)throw new Error(`too many issues (${n.length}); cap is ${Fm} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function JT(t,e,n,r){let o=r??await Tr(t),s=i=>o.find(c=>c.id===i||c.key.toLowerCase()===String(i).toLowerCase()||c.name.toLowerCase()===String(i).toLowerCase())??null;if(e.team){let i=s(e.team);if(i)return i}let a=await ge(n.id);if(a?.key){let i=a.key.split("-")[0],c=o.find(l=>l.key===i);if(c)return c}if(t.defaultTeam){let i=s(t.defaultTeam);if(i)return i}return o.length===1?o[0]:null}async function rh(t,e){let n=await jn();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=zT(e),o=await Tr(n),s=await JT(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=Km();return await R({id:t.id,cwd:t.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 oh(t,e,n,{teamId:r=null}={}){if(!e)throw new Error("proposalId required");let o=await L({id:t.id,cwd:t.cwd}),s=o.find(m=>m.type==="ticket.create.offered"&&m.payload?.proposalId===e);if(!s)throw new Error("unknown proposal");if(o.some(m=>m.type==="ticket.create.answered"&&m.payload?.proposalId===e))throw new Error("proposal already answered");if(!n)return await R({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"dismiss"}}),{proposalId:e,action:"dismiss"};let a=await jn();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await Tr(a)).find(h=>h.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 zc(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 h=await Jc(a,{teamId:c.id,title:m.title,description:m.description,projectId:f,priority:m.priority});l.push({kind:"issue",key:h.key,title:h.title,url:h.url})}catch(h){u.push({title:m.title,message:h?.message||String(h)})}await R({id:t.id,cwd:t.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,team:{key:c.key,name:c.name},items:l,failed:u}}),await R({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,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 ge(t.id)&&(p=await Ar(t,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:p?p.key:null}}async function No(t,e){let n=await Un(t);n&&await n.api.createComment(n.cfg,t,e)}async function XT(t,e,n){let r=await Un(t);if(!r)return null;let o=await nn(e);if(!o)return null;let s=Ie(o,n);if(!s)return null;let a=await Po.readFile(Wt(o,s.v).png).catch(()=>null);return a?r.api.uploadImage(r.cfg,{issue:t.key,filename:`${o.name.replace(/[^\w.-]+/g,"-")}.v${s.v}.png`,contentType:"image/png",buffer:a}):null}var Um=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),Qc=new Map;function YT(t,e){if(Um()<=0)return Bo(t,e);if(Qc.has(t))return;let n=setTimeout(()=>{Qc.delete(t),ge(t).then(r=>r?Bo(t,r):null).catch(()=>{})},Um());n.unref?.(),Qc.set(t,n)}var Hm=new Map;async function Wm(t,e,n){let r=Hm.get(t)??0;Date.now()-r<600*1e3||(Hm.set(t,Date.now()),await R({id:t,cwd:e},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function ZT(t,{resolveTrack:e=null}={}){let n=await ge(t.trackId),r=t.payload||{};if(!n){if(t.type!=="turn.user"||t.actor!=="user")return;let o=null;for(let a of await rl())if(o=a.api.findIssueUrl(r.text,a.cfg),o)break;if(!o)return;let s=e?await e(t.trackId):null;if(!s)return;try{await Ar(s,o,{source:"chat"})}catch(a){await Wm(t.trackId,s.cwd,a)}return}if(!n.track.cwd&&e){let o=await e(t.trackId).catch(()=>null);o?.cwd&&(n.track.cwd=o.cwd,await sn(t.trackId))}try{switch(t.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await sn(t.trackId),await Bo(t.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 sn(t.trackId),await Bo(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await Xm(n,t.trackId).catch(()=>null):null;await No(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
86
|
-
|
|
87
|
-
Agent cost for this Track: ${
|
|
88
|
-
|
|
89
|
-
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await
|
|
90
|
-
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await YT(t.trackId,n);break;default:break}}catch(o){let s=e?await e(t.trackId).catch(()=>null):null;await Wm(t.trackId,s?.cwd??null,o)}}function sh({resolveTrack:t}){Jt.on("event",e=>{ZT(e,{resolveTrack:t}).catch(()=>{})})}async function ah(){if(!pt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await At("tickets");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Do(),r=0;for(let[o,s]of Object.entries(n))await $t("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}import QT from"node:crypto";import Ia from"node:fs/promises";import ih from"node:path";import tA from"node:os";var ch=process.env.BOSTRAT_STATE_DIR||ih.join(process.env.HOME||tA.tmpdir(),".bostrat"),ll=ih.join(ch,"pr-links.json"),K=(t,e)=>`${t}#${Number(e)}`,lh=t=>{let e=t.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=QT.createHash("sha256").update(t).digest("hex").slice(0,8);return`${e||"pr"}-${n}`},an=null,il=null,cl=Promise.resolve();async function yt(){return an||(il??=Ia.readFile(ll,"utf8").then(t=>({links:JSON.parse(t)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:t,existed:e})=>{if(an=t,!e&&pt()){let{records:n,source:r}=await At("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;an[K(i.repo,i.number)]=i}Object.keys(an).length&&await ul().catch(()=>{})}}return an}).finally(()=>il=null),il)}async function ul(){let t=JSON.stringify({links:an},null,2),e=`${ll}.${process.pid}.tmp`;await Ia.mkdir(ch,{recursive:!0}),await Ia.writeFile(e,t),await Ia.rename(e,ll)}async function vt(t){cl=cl.then(ul,ul),await cl.catch(()=>{});let e=an?.[t];e&&$t("prlinks",{id:lh(t),updatedAt:Date.now(),...e},{trackId:e.trackId??null}).catch(()=>{})}var eA=new Set(["api","observed","reconciled"]);async function qt({repo:t,number:e,trackId:n,url:r=null,headRef:o=null,title:s=null,source:a="matched"}){if(!t||!Number.isInteger(Number(e))||!n)return{link:null,created:!1};let i=await yt(),c=K(t,e);return i[c]?{link:i[c],created:!1}:(i[c]={repo:t,number:Number(e),trackId:n,url:r,headRef:o,title:s,source:a,announced:eA.has(a),firstSeen:Date.now()},await vt(K(t,e)),{link:i[c],created:!0})}async function jo(t,e){if(!t||e==null)return null;let n=(await yt())[K(t,e)];return!n||n.announced?null:(n.announced=!0,await vt(K(t,e)),n)}async function Ot(t,e){return!t||e==null?null:(await yt())[K(t,e)]??null}async function cn(t,e,n){if(!t||e==null)return null;let r=(await yt())[K(t,e)];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 vt(K(t,e)),r):null}async function uh(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await yt())[K(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await vt(K(t,e)),o):null}async function dh(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await yt())[K(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await vt(K(t,e)),o):null}async function Ra(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await yt())[K(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await vt(K(t,e)),r)}async function $a(t,e){if(!t||e==null)return null;let n=(await yt())[K(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await vt(K(t,e)),n)}async function Oa(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await yt())[K(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await vt(K(t,e)),r)}async function Ca(t,e){if(!t||e==null)return null;let n=(await yt())[K(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await vt(K(t,e)),n)}async function fh(t,e,{at:n=Date.now()}={}){if(!t||e==null)return null;let r=(await yt())[K(t,e)];return r?(r.baseFoldAt=n,await vt(K(t,e)),r):null}async function ph(t,e){if(!t||e==null)return null;let n=(await yt())[K(t,e)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await vt(K(t,e)),n)}async function mh(t,e,n){if(!t||e==null||!n)return null;let r=(await yt())[K(t,e)];return r?(r.lastHeadSha=n,await vt(K(t,e)),r):null}async function Lo(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await yt())[K(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await vt(K(t,e)),r):null}async function Hn(t,e,n=!0){if(!t||e==null)return null;let r=(await yt())[K(t,e)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await vt(K(t,e)),r):null}async function Fo(t,e,n=!0){if(!t||e==null)return null;let r=(await yt())[K(t,e)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await vt(K(t,e)),r):null}async function hh(t,e,n){if(!t||e==null)return null;let r=(await yt())[K(t,e)];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 vt(K(t,e)),r}async function Uo(t,e){if(!t||e==null)return null;let n=(await yt())[K(t,e)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,await vt(K(t,e)),n)}var nA=new Set(["merged","closed","lost"]);async function wh(){return Object.values(await yt()).filter(t=>!nA.has(t.state))}async function ye(){return Object.values(await yt()).sort((t,e)=>e.firstSeen-t.firstSeen)}async function gh(){if(!pt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await At("prlinks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await yt(),r=0;for(let[o,s]of Object.entries(n))await $t("prlinks",{id:lh(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var bh=t=>Math.round(t*100)/100;function rA(t){return!t||t.kind==="setup"?[]:jt(t.workspace).filter(e=>e&&(e.repo||e.base)).map(e=>({key:e.repo||e.base,slug:e.repo??null,base:e.base??null,name:e.repo?e.repo.split("/").pop():yh.basename(e.base)}))}function kh(t){let e=new Map;for(let n of t||[])for(let r of rA(n)){let o=e.get(r.key);o||e.set(r.key,o={...r,tracks:[]}),o.tracks.push(n)}return e}function Ma(t){let e=t.events?.length?t.events[t.events.length-1].at:0;return Math.max(t.createdAt||0,t.archivedAt||0,t.lastResponseAt||0,e||0)||null}var dl=(t,e)=>`${t} ${e}${t===1?"":"s"}`;function oA(t){if(!Number.isFinite(t)||t<=0)return null;let e=t/864e5;if(e>=1)return`${Math.round(e)}d`;let n=t/36e5;return n>=1?`${Math.round(n)}h`:`${Math.max(1,Math.round(t/6e4))}m`}function fl(t,e=[]){let n=[],r=t?.events||[],o=new Map,s=0;for(let u of e){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(dl(s,"PR")+" opened");let a=Object.keys(t?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(dl(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(`${dl(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=ue(e);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(t?.archivedAt)&&Number.isFinite(t?.createdAt)?oA(t.archivedAt-t.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}function sA(t){let e=[],n=[...t?.boxes||[]].sort((o,s)=>(o.createdAt||0)-(s.createdAt||0));for(let o of n){if(o.role!=="lead"&&o.role!=="handed-off")continue;let s=Array.isArray(o.epochs)&&o.epochs.length?o.epochs.map(a=>a.agent):[o.agent];for(let a of s)a&&e[e.length-1]!==a&&e.push(a)}let r=new Set(n.filter(o=>o.role==="subtask"&&o.agent&&!e.includes(o.agent)).map(o=>o.agent));return{lead:e,subtask:[...r]}}function xh(t,e,n){let r=new Set(t.tracks.map(d=>d.id)),o=0,s=0,a=0,i=null,c=null,l=0;for(let d of t.tracks){d.archivedAt?s++:o++,d.beat?.text&&a++;let p=Ma(d);p&&(!i||p>i)&&(i=p),d.createdAt&&(!c||d.createdAt<c)&&(c=d.createdAt),l+=e.get(d.id)||0}let u=0,f=0;for(let d of n)r.has(d.trackId)&&(t.slug&&d.repo!==t.slug||t.slug&&(d.state==="merged"?f++:d.state!=="closed"&&u++));return{key:t.key,slug:t.slug,base:t.base,name:t.name,tracks:{total:t.tracks.length,live:o,archived:s,withBeat:a},prs:{open:u,merged:f},usd:bh(l),firstActivityAt:c,lastActivityAt:i}}async function Sh(t){let e=kh(t),n=await ye().catch(()=>[]),r=new Map,o=new Set;for(let a of e.values())for(let i of a.tracks){if(o.has(i.id))continue;o.add(i.id);let c=await L(i).catch(()=>[]);r.set(i.id,ue(c).totalUsd)}let s=[...e.values()].map(a=>xh(a,r,n)).sort((a,i)=>(i.lastActivityAt??0)-(a.lastActivityAt??0));return{generatedAt:Date.now(),arcs:s}}async function vh(t,e){let r=kh(t).get(e);if(!r)return null;let[o,s,a,i,c]=await Promise.all([ye().catch(()=>[]),Pn().catch(()=>[]),ga().catch(()=>[]),ka().catch(()=>[]),Ea().catch(()=>({}))]),l=new Map,u=[],f=(d,p)=>(Ma(p)||p.createdAt||0)-(Ma(d)||d.createdAt||0);for(let d of[...r.tracks].sort(f)){let p=await L(d).catch(()=>[]),m=ue(p);l.set(d.id,m.totalUsd);let h=c[d.id]||null;u.push({trackId:d.id,name:d.name,createdAt:d.createdAt??null,archivedAt:d.archivedAt??null,live:!d.archivedAt,beat:d.beat??null,agents:sA(d),handoffs:(d.events||[]).filter(y=>y.type==="lead-handoff").length,subtasks:Object.keys(d.subtasks||{}).length,repos:jt(d.workspace).map(y=>y.repo||yh.basename(y.base||"")),prs:o.filter(y=>y.trackId===d.id&&(!r.slug||y.repo===r.slug)).map(y=>({repo:y.repo,number:y.number,state:y.state??null,title:y.title??null,url:y.url??null})),mocks:s.filter(y=>y.trackId===d.id).length,views:a.filter(y=>y.trackId===d.id).length,runbooks:i.filter(y=>y.trackId===d.id).map(y=>({name:y.name,status:y.status})),ticket:h?{provider:h.provider,key:h.key,url:h.url??null}:null,usd:bh(m.totalUsd),lastActivityAt:Ma(d)})}return{...xh(r,l,o),generatedAt:Date.now(),segments:u}}import{execFile as aA}from"node:child_process";import{promisify as iA}from"node:util";import{randomUUID as cA}from"node:crypto";var Er=iA(aA),Na=t=>new Promise(e=>setTimeout(e,t)),lA=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Th=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,uA=250,dA=400,Ah={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 pl(t,e){let n=Date.now()+e;for(;;){if(await fr(t))return!0;if(Date.now()>n)return!1;await Na(uA)}}async function fA(t,e){let n=Ah[e]||Ah.default;n.escape&&(await bt(t,["Escape"],{kind:"swap-exit"}),await Na(150)),n.wipe&&await zs(t,400,{kind:"swap-exit"}),await Js(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=_e(e).submitDelayMs||0;await Na(Math.max(r,250)),await bt(t,["Enter"],{kind:"swap-exit"})}async function pA(t,e){try{let{stdout:n}=await Er(P,B("show-environment","-t",t,e));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function Eh(t){await Er(P,B("respawn-pane","-k","-t",t))}async function ml(t,e={}){if(!gt(t))throw new Error("invalid session name");let n=String(e.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await Q(t);if(!r)throw new Error(`session "${t}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${t}"`);let o=r.agent||"agent",s=e.agent||Xe(n),a=Number(e.exitTimeoutMs)||lA;await q({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await fr(t)&&(await fA(t,o),!await pl(t,a)&&(i=!0,await q({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await Eh(t),!await pl(t,Th))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&Rn(n),l=c?Ms():null,u=await pA(t,"ANTHROPIC_API_KEY"),f=!1;try{l?(await Er(P,B("set-environment","-t",t,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await Er(P,B("set-environment","-u","-t",t,"ANTHROPIC_API_KEY")),f=!0)}catch(y){throw l?qs(y,l):y}if(f&&!i&&(await Eh(t),!await pl(t,Th)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let d=!!e.resumeSessionId,p=e.resumeSessionId||cA(),m=d?n:ic(n,p),h=d?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await Ge(r.cwd),s==="codex"&&await sr(r.cwd),s==="cursor"&&await ar(r.cwd),await Cn(t),await Er(P,B("set-option","-t",t,"@bostrat-sid",p)),await Er(P,B("set-option","-t",t,"@bostrat-agent",s)),await Na(dA),await St(t,m,{kind:"swap-launch"}),await q({flow:"swap",phase:"done",sessionName:t,from:o,to:s,sid:p,forced:i,envRespawned:f}),{name:t,cwd:r.cwd,sessionId:p,agent:s,pinned:h,forced:i,envRespawned:f}}import GA from"node:fs/promises";function mA(t){let e=[],n="",r=null;for(let o=0;o<t.length;o++){let s=t[o];if(r){n+=s,s===r&&t[o-1]!=="\\"&&(r=null);continue}if(s==="'"||s==='"'||s==="`"){r=s,n+=s;continue}if(s==="&"&&t[o+1]==="&"){e.push(n),n="",o++;continue}if(s==="|"&&t[o+1]==="|"){e.push(n),n="",o++;continue}if(s==="|"||s===";"||s===`
|
|
91
|
-
`){e.push(n),n="";continue}n+=s}return e.push(n),e.map(o=>o.trim()).filter(Boolean)}function
|
|
92
|
-
`,n);i!==-1&&(e=e.slice(0,i))}let r=
|
|
93
|
-
`).map(n=>n.trim()).find(Boolean)||t;return e.length>88?`${e.slice(0,88).trimEnd()}\u2026`:e}function
|
|
94
|
-
`)){let a=s.trim();if(!a||a[0]!=="{")continue;let i;try{i=JSON.parse(a)}catch{continue}let c=i.message;if(!c)continue;let l=[];if(
|
|
95
|
-
`)){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?
|
|
96
|
-
`).trim():"",
|
|
97
|
-
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=e)break}return o}finally{await n.close()}}async function
|
|
98
|
-
`).trim():"";async function
|
|
99
|
-
`)){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 S=m.payload?.type;S==="task_started"?(l=!0,u=!1,f=m.payload?.turn_id||null):S==="task_complete"&&l&&(l=!1,!u&&m.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:
|
|
100
|
-
`;else if(s==="t")n+=" ";else if(s==="r")n+="\r";else if(s==="u")if(t[r+1]==="{"){let a=t.indexOf("}",r+2);if(a===-1)return null;n+=String.fromCodePoint(parseInt(t.slice(r+2,a),16)),r=a}else n+=String.fromCharCode(parseInt(t.slice(r+1,r+5),16)),r+=4;else n+=s}else{if(o===e)return n;if(e==="`"&&o==="$"&&t[r+1]==="{")return null;n+=o}}return null}function
|
|
101
|
-
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type!=="response_item")continue;let u=l.payload;if(!u)continue;let f=[];if(u.type==="function_call"&&
|
|
102
|
-
`),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(
|
|
103
|
-
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!
|
|
104
|
-
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(
|
|
105
|
-
`),o=-1;for(let u=r.length-1;u>=0;u--)if(
|
|
106
|
-
`),o=
|
|
107
|
-
`),o=
|
|
108
|
-
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=
|
|
109
|
-
`)].reverse().find(a=>
|
|
110
|
-
`),o=
|
|
111
|
-
`),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(
|
|
112
|
-
`),o=
|
|
113
|
-
`).pop()||"")?!0:
|
|
114
|
-
`).pop()||""):t==="codex"&&/Sign in with ChatGPT|one-time code|Paste or type your API key|link to authenticate/i.test(n)?!0:t==="cursor"||t==="codex"?/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i.test(n):!1:!1}function
|
|
115
|
-
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(
|
|
85
|
+
}`,{contentType:n,filename:e,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 GA(t,e){let n=await xe(t,"query($id: String!) { issue(id: $id) { state { id name type } team { states { nodes { id name type position } } } } }",{id:e.key});if(!n.issue)throw new Error(`linear: issue "${e.key}" not found`);return{current:n.issue.state??null,states:n.issue.team?.states?.nodes??[]}}async function VA(t,e,n){if(!(await xe(t,"mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success } }",{id:e.issueId,input:{stateId:n}})).issueUpdate?.success)throw new Error("linear: issueUpdate did not succeed")}async function Mr(t){return((await xe(t,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var mh=255;async function hl(t,{teamId:e,name:n,description:r}){let o=r||null,s=!!o&&o.length>mh,a=s?`${o.slice(0,mh-1).trimEnd()}\u2026`:o,i=await xe(t,"mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id url name } } }",{input:{teamIds:[e],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 wl(t,{teamId:e,title:n,description:r,projectId:o,priority:s}){let a=await xe(t,"mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier url title } } }",{input:{teamId:e,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 xh={linear:{api:gl,config:Kn},jira:{api:pl,config:Ba}};async function Tl(){let t=[];for(let[e,{api:n,config:r}]of Object.entries(xh)){let o=await r();o&&t.push({name:e,api:n,cfg:o})}return t}async function zn(t){let e=xh[t?.provider];if(!e)return null;let n=await e.config();return n?{name:t.provider,api:e.api,cfg:n}:null}function Al(t,e){if(!t||t.name)return t;let n=e.find(r=>r.id===t.id);return n?{...t,name:n.name}:t}var Sh=()=>process.env.BOSTRAT_STATE_DIR||bh.join(process.env.HOME||zA.tmpdir(),".bostrat"),xl=()=>bh.join(Sh(),"tickets.json"),vh=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),La=t=>`${vh()}/track/${t}`,pn=null,yl=null,bl=Promise.resolve();async function Vo(){return pn||(yl??=Ko.readFile(xl(),"utf8").then(t=>({tickets:JSON.parse(t)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:t,existed:e})=>{if(pn=t,!e&&wt()){let{records:n,source:r}=await Ot("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;pn[s]=i}Object.keys(pn).length&&await Sl().catch(()=>{})}}return pn}).finally(()=>yl=null),yl)}async function Sl(){let t=JSON.stringify({tickets:pn},null,2),e=`${xl()}.${process.pid}.tmp`;await Ko.mkdir(Sh(),{recursive:!0}),await Ko.writeFile(e,t),await Ko.rename(e,xl())}async function mn(t=null){if(bl=bl.then(Sl,Sl),await bl.catch(()=>{}),!t)return;let e=pn?.[t];e?Dt("tickets",{id:t,updatedAt:Date.now(),...e},{trackId:t}).catch(()=>{}):re("tickets",t).catch(()=>{})}async function Se(t){return(await Vo())[t]??null}async function Fa(){return{...await Vo()}}async function Th(){return(await Tl()).length>0}async function Cr(t,e,{source:n="api"}={}){let r=await Tl();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(e);if(f)try{s=await u.api.resolveIssue(u.cfg,f),o=u;break}catch(d){a=d}}if(!o)throw a||new Error(`"${e}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(t.boxes||[]).find(u=>u.name===t.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:t.name,branch:t.workspace?.branch??null,leadAgent:i?.agent??null,cwd:t.cwd??null,...t.workspace?.kind==="multi"?{repos:(t.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await Vo())[t.id]=c,await mn(t.id),await I({id:t.id,cwd:t.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 Go(t.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 qo(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await _h(t.id,c,"linked"),c}async function El(t){let e=await Vo();return e[t]?(delete e[t],await mn(t),!0):!1}var vl=t=>`$${t>=100?Math.round(t):t.toFixed(2)}`;async function Ah(t,e){if(!t.track?.cwd)return null;let n=he(await L({id:e,cwd:t.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function JA(t){let e=Object.entries(t.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${vl(o.usd)}`).join(" \xB7 "),n=Object.keys(t.byBox).length;return`${vl(t.totalUsd)}${e?` (${e})`:""}${n>1?` across ${n} boxes`:""}`}function XA(t,e){let n=[];return t.track.leadAgent&&n.push(`lead ${t.track.leadAgent}`),t.track.branch&&n.push(t.track.branch),t.track.repos?.length&&n.push(t.track.repos.join(" + ")),t.pr?.number&&n.push(`PR #${t.pr.number} ${t.pr.change==="merged"?"merged":"open"}`),e&&n.push(vl(e.totalUsd)),n.join(" \xB7 ")||"linked"}async function Go(t,e){let n=await zn(e);if(!n)return;let r=await Ah(e,t).catch(()=>null);await n.api.upsertCard(n.cfg,e,{url:La(t),title:`Bostrat Track: ${e.track.name}`,subtitle:XA(e,r)})}function Ua(t){let e=new URL(vh());return`${e.protocol}//${t}.${e.host}/`}var YA={linked:"linked",opened:"prOpened",merged:"prMerged"};function ZA(t,e,n){let r=o=>e.filter(s=>s.type===o).sort((s,a)=>s.position-a.position);if(n==="linked"){if(["triage","backlog","unstarted"].includes(t?.type))return r("started")[0]??null}else if(n==="opened"){if(t?.type!=="completed")return r("started").find(o=>/review/i.test(o.name))??null}else if(n==="merged"&&t?.type!=="completed")return r("completed")[0]??null;return null}async function Eh(t,e,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){e.state=r,await mn(t),await I({id:t,cwd:e.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:e.key,url:e.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function _h(t,e,n){let r=await zn(e);if(!r)return;let o=r.cfg.ticketFlow?.[YA[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,e),i=Al(s,a),c=ZA(i,a,n);if(!c||c.id===i?.id)return;let l=i?.name??e.state??null;if(o==="auto"){await r.api.moveIssue(r.cfg,e,c.id),await Eh(t,e,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await I({id:t,cwd:e.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:kh(),provider:e.provider,key:e.key,url:e.url,phase:n,from:l,to:{id:c.id,name:c.name,type:c.type},...e.pr?.number?{prNumber:e.pr.number}:{}}}).catch(()=>{})}async function Ih(t){let e=await Se(t);if(!e)throw new Error("no ticket linked to this Track");let n=await zn(e);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,e),s=Al(r,o);return s?.name&&e.state!==s.name&&(e.state=s.name,await mn(t)),{key:e.key,url:e.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 _l(t,e,{mode:n="manual"}={}){if(!e)throw new Error("stateId required");let r=await Se(t);if(!r)throw new Error("no ticket linked to this Track");let o=await zn(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Al(s,a),c=a.find(u=>u.id===e);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 mn(t)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await Eh(t,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function Rh(t,e){let n=await Se(t);if(!n)throw new Error("no ticket linked to this Track");let r=await zn(n);if(!r)throw new Error("no ticketing integration configured");let o=String(e||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await I({id:t,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 $h(t,e,n){if(!e)throw new Error("offerId required");let r=await L({id:t.id,cwd:t.cwd}),o=r.find(a=>a.type==="ticket.move.offered"&&a.payload?.offerId===e);if(!o)throw new Error("unknown offer");if(r.some(a=>a.type==="ticket.move.answered"&&a.payload?.offerId===e))throw new Error("offer already answered");let s=null;return n&&(s=await _l(t.id,o.payload.to?.id,{mode:"offer"})),await I({id:t.id,cwd:t.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:e,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:e,action:n?"moved":"dismiss",...s??{}}}var hh=50;async function Oh(){let t=await Kn();if(!t)throw new Error("no ticketing integration configured");return Mr(t)}function QA(t){let e=t&&typeof t=="object"?t:{},n=(Array.isArray(e.issues)?e.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=e.epic&&String(e.epic.name||"").trim()?{name:String(e.epic.name).trim(),description:e.epic.description?String(e.epic.description):null}:null;if(!n.length&&!r)throw new Error("a plan needs an epic or at least one issue");if(n.length>hh)throw new Error(`too many issues (${n.length}); cap is ${hh} per proposal`);return{epic:r,issues:n,team:e.team?String(e.team).trim():null,linkCurrentTrack:e.linkCurrentTrack===!0}}async function tE(t,e,n,r){let o=r??await Mr(t),s=i=>o.find(c=>c.id===i||c.key.toLowerCase()===String(i).toLowerCase()||c.name.toLowerCase()===String(i).toLowerCase())??null;if(e.team){let i=s(e.team);if(i)return i}let a=await Se(n.id);if(a?.key){let i=a.key.split("-")[0],c=o.find(l=>l.key===i);if(c)return c}if(t.defaultTeam){let i=s(t.defaultTeam);if(i)return i}return o.length===1?o[0]:null}async function Mh(t,e){let n=await Kn();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=QA(e),o=await Mr(n),s=await tE(n,r,t,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=kh();return await I({id:t.id,cwd:t.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 Ch(t,e,n,{teamId:r=null}={}){if(!e)throw new Error("proposalId required");let o=await L({id:t.id,cwd:t.cwd}),s=o.find(m=>m.type==="ticket.create.offered"&&m.payload?.proposalId===e);if(!s)throw new Error("unknown proposal");if(o.some(m=>m.type==="ticket.create.answered"&&m.payload?.proposalId===e))throw new Error("proposal already answered");if(!n)return await I({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,action:"dismiss"}}),{proposalId:e,action:"dismiss"};let a=await Kn();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await Mr(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 hl(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 wl(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 I({id:t.id,cwd:t.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,team:{key:c.key,name:c.name},items:l,failed:u}}),await I({id:t.id,cwd:t.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:e,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 Se(t.id)&&(p=await Cr(t,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:e,action:"created",created:l,failed:u,linked:p?p.key:null}}async function qo(t,e){let n=await zn(t);n&&await n.api.createComment(n.cfg,t,e)}async function eE(t,e,n){let r=await zn(t);if(!r)return null;let o=await dn(e);if(!o)return null;let s=Ne(o,n);if(!s)return null;let a=await Ko.readFile(Jt(o,s.v).png).catch(()=>null);return a?r.api.uploadImage(r.cfg,{issue:t.key,filename:`${o.name.replace(/[^\w.-]+/g,"-")}.v${s.v}.png`,contentType:"image/png",buffer:a}):null}var wh=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),kl=new Map;function nE(t,e){if(wh()<=0)return Go(t,e);if(kl.has(t))return;let n=setTimeout(()=>{kl.delete(t),Se(t).then(r=>r?Go(t,r):null).catch(()=>{})},wh());n.unref?.(),kl.set(t,n)}var gh=new Map;async function yh(t,e,n){let r=gh.get(t)??0;Date.now()-r<600*1e3||(gh.set(t,Date.now()),await I({id:t,cwd:e},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function rE(t,{resolveTrack:e=null}={}){let n=await Se(t.trackId),r=t.payload||{};if(!n){if(t.type!=="turn.user"||t.actor!=="user")return;let o=null;for(let a of await Tl())if(o=a.api.findIssueUrl(r.text,a.cfg),o)break;if(!o)return;let s=e?await e(t.trackId):null;if(!s)return;try{await Cr(s,o,{source:"chat"})}catch(a){await yh(t.trackId,s.cwd,a)}return}if(!n.track.cwd&&e){let o=await e(t.trackId).catch(()=>null);o?.cwd&&(n.track.cwd=o.cwd,await mn(t.trackId))}try{switch(t.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await mn(t.trackId),await Go(t.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 mn(t.trackId),await Go(t.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await Ah(n,t.trackId).catch(()=>null):null;await qo(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
86
|
+
|
|
87
|
+
Agent cost for this Track: ${JA(o)}.`:"")),await _h(t.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await eE(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await qo(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
88
|
+
|
|
89
|
+
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await qo(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 qo(n,`**Bostrat** \u2014 live preview **${r.name}**: ${Ua(r.slug)}
|
|
90
|
+
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await nE(t.trackId,n);break;default:break}}catch(o){let s=e?await e(t.trackId).catch(()=>null):null;await yh(t.trackId,s?.cwd??null,o)}}function Nh({resolveTrack:t}){Ut.on("event",e=>{rE(e,{resolveTrack:t}).catch(()=>{})})}async function Ph(){if(!wt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ot("tickets");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Vo(),r=0;for(let[o,s]of Object.entries(n))await Dt("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}import oE from"node:crypto";import Ha from"node:fs/promises";import Dh from"node:path";import sE from"node:os";var Bh=process.env.BOSTRAT_STATE_DIR||Dh.join(process.env.HOME||sE.tmpdir(),".bostrat"),$l=Dh.join(Bh,"pr-links.json"),V=(t,e)=>`${t}#${Number(e)}`,jh=t=>{let e=t.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=oE.createHash("sha256").update(t).digest("hex").slice(0,8);return`${e||"pr"}-${n}`},hn=null,Il=null,Rl=Promise.resolve();async function kt(){return hn||(Il??=Ha.readFile($l,"utf8").then(t=>({links:JSON.parse(t)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:t,existed:e})=>{if(hn=t,!e&&wt()){let{records:n,source:r}=await Ot("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;hn[V(i.repo,i.number)]=i}Object.keys(hn).length&&await Ol().catch(()=>{})}}return hn}).finally(()=>Il=null),Il)}async function Ol(){let t=JSON.stringify({links:hn},null,2),e=`${$l}.${process.pid}.tmp`;await Ha.mkdir(Bh,{recursive:!0}),await Ha.writeFile(e,t),await Ha.rename(e,$l)}async function It(t){Rl=Rl.then(Ol,Ol),await Rl.catch(()=>{});let e=hn?.[t];e&&Dt("prlinks",{id:jh(t),updatedAt:Date.now(),...e},{trackId:e.trackId??null}).catch(()=>{})}var aE=new Set(["api","observed","reconciled"]);async function Xt({repo:t,number:e,trackId:n,url:r=null,headRef:o=null,title:s=null,source:a="matched"}){if(!t||!Number.isInteger(Number(e))||!n)return{link:null,created:!1};let i=await kt(),c=V(t,e);return i[c]?{link:i[c],created:!1}:(i[c]={repo:t,number:Number(e),trackId:n,url:r,headRef:o,title:s,source:a,announced:aE.has(a),firstSeen:Date.now()},await It(V(t,e)),{link:i[c],created:!0})}async function zo(t,e){if(!t||e==null)return null;let n=(await kt())[V(t,e)];return!n||n.announced?null:(n.announced=!0,await It(V(t,e)),n)}async function Bt(t,e){return!t||e==null?null:(await kt())[V(t,e)]??null}async function wn(t,e,n){if(!t||e==null)return null;let r=(await kt())[V(t,e)];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 It(V(t,e)),r):null}async function Lh(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await kt())[V(t,e)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await It(V(t,e)),o):null}async function Fh(t,e,{headSha:n=null,trackId:r=null}={}){if(!t||e==null)return null;let o=(await kt())[V(t,e)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await It(V(t,e)),o):null}async function Wa(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await kt())[V(t,e)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await It(V(t,e)),r)}async function qa(t,e){if(!t||e==null)return null;let n=(await kt())[V(t,e)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await It(V(t,e)),n)}async function Ka(t,e,{headSha:n=null}={}){if(!t||e==null)return null;let r=(await kt())[V(t,e)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await It(V(t,e)),r)}async function Ga(t,e){if(!t||e==null)return null;let n=(await kt())[V(t,e)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await It(V(t,e)),n)}async function Uh(t,e,{at:n=Date.now()}={}){if(!t||e==null)return null;let r=(await kt())[V(t,e)];return r?(r.baseFoldAt=n,await It(V(t,e)),r):null}async function Hh(t,e){if(!t||e==null)return null;let n=(await kt())[V(t,e)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await It(V(t,e)),n)}async function Wh(t,e,n){if(!t||e==null||!n)return null;let r=(await kt())[V(t,e)];return r?(r.lastHeadSha=n,await It(V(t,e)),r):null}async function Jo(t,e,{merge:n=null}={}){if(!t||e==null)return null;let r=(await kt())[V(t,e)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await It(V(t,e)),r):null}async function Jn(t,e,n=!0){if(!t||e==null)return null;let r=(await kt())[V(t,e)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await It(V(t,e)),r):null}async function Xo(t,e,n=!0){if(!t||e==null)return null;let r=(await kt())[V(t,e)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await It(V(t,e)),r):null}async function qh(t,e,n){if(!t||e==null)return null;let r=(await kt())[V(t,e)];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 It(V(t,e)),r}async function Yo(t,e){if(!t||e==null)return null;let n=(await kt())[V(t,e)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,await It(V(t,e)),n)}var iE=new Set(["merged","closed","lost"]);async function Kh(){return Object.values(await kt()).filter(t=>!iE.has(t.state))}async function ve(){return Object.values(await kt()).sort((t,e)=>e.firstSeen-t.firstSeen)}async function Gh(){if(!wt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ot("prlinks");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await kt(),r=0;for(let[o,s]of Object.entries(n))await Dt("prlinks",{id:jh(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var zh=t=>Math.round(t*100)/100;function cE(t){return!t||t.kind==="setup"?[]:qt(t.workspace).filter(e=>e&&(e.repo||e.base)).map(e=>({key:e.repo||e.base,slug:e.repo??null,base:e.base??null,name:e.repo?e.repo.split("/").pop():Vh.basename(e.base)}))}function Jh(t){let e=new Map;for(let n of t||[])for(let r of cE(n)){let o=e.get(r.key);o||e.set(r.key,o={...r,tracks:[]}),o.tracks.push(n)}return e}function Va(t){let e=t.events?.length?t.events[t.events.length-1].at:0;return Math.max(t.createdAt||0,t.archivedAt||0,t.lastResponseAt||0,e||0)||null}var Ml=(t,e)=>`${t} ${e}${t===1?"":"s"}`;function lE(t){if(!Number.isFinite(t)||t<=0)return null;let e=t/864e5;if(e>=1)return`${Math.round(e)}d`;let n=t/36e5;return n>=1?`${Math.round(n)}h`:`${Math.max(1,Math.round(t/6e4))}m`}function Cl(t,e=[]){let n=[],r=t?.events||[],o=new Map,s=0;for(let u of e){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(Ml(s,"PR")+" opened");let a=Object.keys(t?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Ml(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(`${Ml(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=he(e);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(t?.archivedAt)&&Number.isFinite(t?.createdAt)?lE(t.archivedAt-t.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}function uE(t){let e=[],n=[...t?.boxes||[]].sort((o,s)=>(o.createdAt||0)-(s.createdAt||0));for(let o of n){if(o.role!=="lead"&&o.role!=="handed-off")continue;let s=Array.isArray(o.epochs)&&o.epochs.length?o.epochs.map(a=>a.agent):[o.agent];for(let a of s)a&&e[e.length-1]!==a&&e.push(a)}let r=new Set(n.filter(o=>o.role==="subtask"&&o.agent&&!e.includes(o.agent)).map(o=>o.agent));return{lead:e,subtask:[...r]}}function Xh(t,e,n){let r=new Set(t.tracks.map(d=>d.id)),o=0,s=0,a=0,i=null,c=null,l=0;for(let d of t.tracks){d.archivedAt?s++:o++,d.beat?.text&&a++;let p=Va(d);p&&(!i||p>i)&&(i=p),d.createdAt&&(!c||d.createdAt<c)&&(c=d.createdAt),l+=e.get(d.id)||0}let u=0,f=0;for(let d of n)r.has(d.trackId)&&(t.slug&&d.repo!==t.slug||t.slug&&(d.state==="merged"?f++:d.state!=="closed"&&u++));return{key:t.key,slug:t.slug,base:t.base,name:t.name,tracks:{total:t.tracks.length,live:o,archived:s,withBeat:a},prs:{open:u,merged:f},usd:zh(l),firstActivityAt:c,lastActivityAt:i}}async function Yh(t){let e=Jh(t),n=await ve().catch(()=>[]),r=new Map,o=new Set;for(let a of e.values())for(let i of a.tracks){if(o.has(i.id))continue;o.add(i.id);let c=await L(i).catch(()=>[]);r.set(i.id,he(c).totalUsd)}let s=[...e.values()].map(a=>Xh(a,r,n)).sort((a,i)=>(i.lastActivityAt??0)-(a.lastActivityAt??0));return{generatedAt:Date.now(),arcs:s}}async function Zh(t,e){let r=Jh(t).get(e);if(!r)return null;let[o,s,a,i,c]=await Promise.all([ve().catch(()=>[]),Hn().catch(()=>[]),Oa().catch(()=>[]),Na().catch(()=>[]),Fa().catch(()=>({}))]),l=new Map,u=[],f=(d,p)=>(Va(p)||p.createdAt||0)-(Va(d)||d.createdAt||0);for(let d of[...r.tracks].sort(f)){let p=await L(d).catch(()=>[]),m=he(p);l.set(d.id,m.totalUsd);let w=c[d.id]||null;u.push({trackId:d.id,name:d.name,createdAt:d.createdAt??null,archivedAt:d.archivedAt??null,live:!d.archivedAt,beat:d.beat??null,agents:uE(d),handoffs:(d.events||[]).filter(y=>y.type==="lead-handoff").length,subtasks:Object.keys(d.subtasks||{}).length,repos:qt(d.workspace).map(y=>y.repo||Vh.basename(y.base||"")),prs:o.filter(y=>y.trackId===d.id&&(!r.slug||y.repo===r.slug)).map(y=>({repo:y.repo,number:y.number,state:y.state??null,title:y.title??null,url:y.url??null})),mocks:s.filter(y=>y.trackId===d.id).length,views:a.filter(y=>y.trackId===d.id).length,runbooks:i.filter(y=>y.trackId===d.id).map(y=>({name:y.name,status:y.status})),ticket:w?{provider:w.provider,key:w.key,url:w.url??null}:null,usd:zh(m.totalUsd),lastActivityAt:Va(d)})}return{...Xh(r,l,o),generatedAt:Date.now(),segments:u}}import{execFile as dE}from"node:child_process";import{promisify as fE}from"node:util";import{randomUUID as pE}from"node:crypto";var Nr=fE(dE),za=t=>new Promise(e=>setTimeout(e,t)),mE=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Qh=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,hE=250,wE=400,tw={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 Nl(t,e){let n=Date.now()+e;for(;;){if(await kr(t))return!0;if(Date.now()>n)return!1;await za(hE)}}async function gE(t,e){let n=tw[e]||tw.default;n.escape&&(await vt(t,["Escape"],{kind:"swap-exit"}),await za(150)),n.wipe&&await ca(t,400,{kind:"swap-exit"}),await la(t,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Ce(e).submitDelayMs||0;await za(Math.max(r,250)),await vt(t,["Enter"],{kind:"swap-exit"})}async function yE(t,e){try{let{stdout:n}=await Nr(M,N("show-environment","-t",t,e));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function ew(t){await Nr(M,N("respawn-pane","-k","-t",t))}async function Pl(t,e={}){if(!bt(t))throw new Error("invalid session name");let n=String(e.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await rt(t);if(!r)throw new Error(`session "${t}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${t}"`);let o=r.agent||"agent",s=e.agent||on(n),a=Number(e.exitTimeoutMs)||mE;await G({flow:"swap",phase:"start",sessionName:t,from:o,to:s,cwd:r.cwd});let i=!1;if(!await kr(t)&&(await gE(t,o),!await Nl(t,a)&&(i=!0,await G({flow:"swap",phase:"respawn-forced",sessionName:t,from:o}),await ew(t),!await Nl(t,Qh))))throw new Error(`pane shell did not come back after respawn-pane in "${t}"`);let c=s==="claude"&&Dn(n),l=c?Gs():null,u=await yE(t,"ANTHROPIC_API_KEY"),f=!1;try{l?(await Nr(M,N("set-environment","-t",t,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await Nr(M,N("set-environment","-u","-t",t,"ANTHROPIC_API_KEY")),f=!0)}catch(y){throw l?ra(y,l):y}if(f&&!i&&(await ew(t),!await Nl(t,Qh)))throw new Error(`pane shell did not come back after env respawn in "${t}"`);let d=!!e.resumeSessionId,p=e.resumeSessionId||pE(),m=d?n:_c(n,p),w=d?e.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await tn(r.cwd),s==="codex"&&await dr(r.cwd),s==="cursor"&&await fr(r.cwd),await jn(t),await Nr(M,N("set-option","-t",t,"@bostrat-sid",p)),await Nr(M,N("set-option","-t",t,"@bostrat-agent",s)),await za(wE),await _t(t,m,{kind:"swap-launch"}),await G({flow:"swap",phase:"done",sessionName:t,from:o,to:s,sid:p,forced:i,envRespawned:f}),{name:t,cwd:r.cwd,sessionId:p,agent:s,pinned:w,forced:i,envRespawned:f}}import YE from"node:fs/promises";function bE(t){let e=[],n="",r=null;for(let o=0;o<t.length;o++){let s=t[o];if(r){n+=s,s===r&&t[o-1]!=="\\"&&(r=null);continue}if(s==="'"||s==='"'||s==="`"){r=s,n+=s;continue}if(s==="&"&&t[o+1]==="&"){e.push(n),n="",o++;continue}if(s==="|"&&t[o+1]==="|"){e.push(n),n="",o++;continue}if(s==="|"||s===";"||s===`
|
|
91
|
+
`){e.push(n),n="";continue}n+=s}return e.push(n),e.map(o=>o.trim()).filter(Boolean)}function Zo(t){let e=[],n="",r=null,o=!1,s=()=>{if(o){if(/^\d*[<>]/.test(n)||n.startsWith(">")){n="",o=!1;return}e.push(n),n="",o=!1}};for(let a=0;a<t.length;a++){let i=t[a];if(r){i===r&&t[a-1]!=="\\"?r=null:n+=i,o=!0;continue}if(i==="'"||i==='"'){r=i,o=!0;continue}if(i==="<"&&t[a+1]==="<")return s(),e.push("<<"),e;if(/\s/.test(i)){s();continue}n+=i,o=!0}return s(),e}function Qo(t){let e=t;for(;e.length&&/^[A-Za-z_][A-Za-z0-9_]*=/.test(e[0]);)e=e.slice(1);for(;e.length&&(e[0]==="sudo"||e[0]==="time"||e[0]==="env");)e=e.slice(1);return e}var Dr=(t,e)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,Ct=t=>t&&!/[$`]/.test(t)?t:null,gt=t=>t.replace(/\/+$/,"").split("/").pop()||t,Pr=t=>{let e=t.replace(/\/+$/,"").split("/").filter(Boolean);return e.length>3?e.slice(-2).join("/"):t},ow=t=>t.startsWith("-"),nw=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);function ut(t,e=new Set){let n=[];for(let r=1;r<t.length;r++){let o=t[r];if(o==="<<")break;if(ow(o)){e.has(o)&&r++;continue}n.push(o)}return n}var kE={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:t=>t[1]?`Clone ${gt(t[1])}`:"Clone repository",checkout:()=>"Switch branches",switch:()=>"Switch branches",restore:()=>"Restore files",reset:()=>"Reset git state",branch:(t,e)=>e.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"},xE={pr:t=>({create:"Create a pull request",list:"List pull requests",view:"View a pull request",merge:"Merge a pull request",checks:"Check PR status"})[t[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"};function sw(t){let e=t.indexOf("-n"),n=e!==-1?t[e+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 SE(t){let e=sw(t),n=Ct(ut(t).find(r=>!/^\d+(,\d+)?p$/.test(r)));return e&&n?e.from===e.to?`Read line ${e.from} of ${gt(n)}`:`Read lines ${e.from}\u2013${e.to} of ${gt(n)}`:t.includes("-i")&&n?`Edit ${gt(n)}`:null}function Dl(t){if(t.includes("--files")){let s=Ct(ut(t,nw)[0]);return s&&s!=="."?`List files in ${Pr(s)}`:"List files"}let e=ut(t,nw),n=e[0];if(!n)return null;let r=Ct(e.slice(1).find(s=>s!==".")),o=Dr(n,40);return r?`Search for \u201C${o}\u201D in ${Pr(r)}`:`Search for \u201C${o}\u201D`}function Bl(t){let e=ut(t,new Set(["--prefix"])),n=e[0];return n?n==="test"||n==="t"?"Run tests":n==="run"||n==="run-script"?e[1]?`Run ${e[1]}`:null:n==="ci"||n==="install"||n==="i"||n==="add"?"Install dependencies":null:null}function jl(t,e){if(t.includes("-e")||t.includes("--eval")||t.includes("-c")||t[1]==="-"||t.includes("<<"))return`Run an inline ${e} script`;if(t.includes("--test"))return"Run tests";let n=ut(t)[0];return n?`Run ${gt(n)}`:null}function vE(t){let e=ut(t),n=e[0],r=e.find(a=>/\.(service|timer|socket|target)$/.test(a))||e[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 rw(t){return t.find(e=>/^https?:\/\//.test(e))||null}var TE={git:t=>{let e=ut(t,new Set(["-C","-c"])),n=kE[e[0]];return n?n(e,t):null},gh:t=>{let e=ut(t),n=xE[e[0]];return n?n(e):null},sed:SE,rg:Dl,grep:Dl,egrep:Dl,nl:t=>{let e=Ct(ut(t)[0]);return e?`Read ${gt(e)}`:null},cat:t=>{let e=Ct(ut(t)[0]);return e?`Read ${gt(e)}`:null},head:t=>{let e=Ct(ut(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the start of ${gt(e)}`:null},tail:t=>{let e=Ct(ut(t,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return e?`Read the end of ${gt(e)}`:null},wc:t=>{let e=Ct(ut(t)[0]);return e?`Count lines in ${gt(e)}`:"Count input lines"},ls:t=>{let e=Ct(ut(t)[0]);return e&&e!=="."?`List files in ${Pr(e)}`:"List files"},find:t=>Ct(t[1])&&!ow(t[1])&&t[1]!=="."?`Find files in ${Pr(t[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:t=>{let e=Ct(ut(t)[0]);return e?`Create directory ${Pr(e)}`:null},touch:t=>{let e=Ct(ut(t)[0]);return e?`Create ${gt(e)}`:null},rm:t=>{let e=Ct(ut(t)[0]);return e?`Delete ${Pr(e)}`:null},mv:t=>{let e=Ct(ut(t)[0]);return e?`Move ${gt(e)}`:null},cp:t=>{let e=Ct(ut(t)[0]);return e?`Copy ${gt(e)}`:null},chmod:t=>{let e=Ct(ut(t)[1]);return e?`Change permissions on ${gt(e)}`:null},diff:t=>{let e=ut(t);return e.length>=2?`Compare ${gt(e[0])} and ${gt(e[1])}`:null},curl:t=>{let e=rw(t);return e?`Fetch ${Dr(e,56)}`:"Fetch a URL"},wget:t=>{let e=rw(t);return e?`Fetch ${Dr(e,56)}`:"Fetch a URL"},npm:Bl,pnpm:Bl,yarn:Bl,npx:t=>{let e=ut(t)[0];return e?`Run ${e}`:null},node:t=>jl(t,"Node"),python:t=>jl(t,"Python"),python3:t=>jl(t,"Python"),make:t=>{let e=ut(t)[0];return e?`Run make ${e}`:"Run make"},systemctl:vE,journalctl:t=>{let e=t.findIndex(r=>r==="-u"||r==="--unit"||r==="--user-unit"),n=e!==-1?t[e+1]:null;return n?`Read ${n.replace(/\.service$/,"")} logs`:"Read system logs"},tmux:t=>{let e=ut(t,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"}[e]||null},ps:()=>"List processes",which:t=>{let e=ut(t)[0];return e?`Locate ${e}`:null},command:t=>t[1]==="-v"&&t[2]?`Locate ${t[2]}`:null,fly:t=>{let e=ut(t)[0];return e==="deploy"?"Deploy to Fly":e?`Run fly ${e}`:null}},AE=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"]);function EE(t){let e=Qo(Zo(t));if(!e.length)return null;if(/^(ba|z)?sh$/.test(gt(e[0]))&&(e[1]==="-lc"||e[1]==="-c")&&e[2])return Ll(e[2]);let n=gt(e[0]);if(AE.has(n))return null;if(e.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=TE[n];return r?r(e.map((o,s)=>s===0?n:o)):null}function Ll(t){let e=String(t||"").trim();if(!e)return null;let n=e.indexOf("<<");if(n!==-1){let i=e.indexOf(`
|
|
92
|
+
`,n);i!==-1&&(e=e.slice(0,i))}let r=bE(e);for(let i=0;i<r.length-1;i++){let c=Qo(Zo(r[i])),l=Qo(Zo(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(gt(c[0]))&>(l[0])==="sed"){let u=sw(l),f=ut(c)[0];if(u&&f)return Dr(u.from===u.to?`Read line ${u.from} of ${gt(f)}`:`Read lines ${u.from}\u2013${u.to} of ${gt(f)}`,88)}}let o=[];for(let i of r){let c=EE(i);c&&o.push({d:c,head:gt(Qo(Zo(i))[0]||""),sub:ut(Qo(Zo(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":Dr(s?s.d:o[0].d,88)}var Ul=new Set(["Bash"]);function Br(t){let e=t.split(`
|
|
93
|
+
`).map(n=>n.trim()).find(Boolean)||t;return e.length>88?`${e.slice(0,88).trimEnd()}\u2026`:e}function gn(t,e=""){let n=e.trim();return n||Ll(t)||Br(t)}function Fl(t,e){if(t){if(Array.isArray(t)){for(let n of t)Fl(n,e);return}if(typeof t=="object"){if(t.type==="tool_use"&&Ul.has(t.name)){let n=String(t.input?.command||"").trim();n&&e.push({id:t.id||null,tool:t.name,command:n,description:String(t.input?.description||"").trim()})}for(let n of Object.values(t))Fl(n,e)}}}async function aw(t,{maxBytes:e=1048576,limit:n=400}={}){let r;try{r=await Ft(t,e)}catch{return[]}let o=[];for(let s of r.split(`
|
|
94
|
+
`)){let a=s.trim();if(!a||a[0]!=="{")continue;let i;try{i=JSON.parse(a)}catch{continue}let c=i.message;if(!c)continue;let l=[];if(Fl(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let f of l)o.push({...f,summary:gn(f.command,f.description),ts:u})}return o.slice(-n)}function iw(t,e){let n=e.filter(l=>l.role==="assistant"&&(l.text||"").trim()).sort((l,u)=>(l.ts||0)-(u.ts||0)),r=[...t].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 _E=/^Async (?:agent|task) launched/i,IE=t=>{if(typeof t=="string")return t.trim();if(!Array.isArray(t))return"";let e=t.find(n=>n&&n.type==="text"&&typeof n.text=="string");return e?e.text.trim():""};async function cw(t,{maxBytes:e=2097152,limit:n=100}={}){let r;try{r=await Ft(t,e)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
|
|
95
|
+
`)){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?Br(p):null),detail:p?Br(p):null,ts:f,proven:!0})}}else d.type==="tool_result"&&d.tool_use_id&&_E.test(IE(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)}import ts from"node:fs";import $E from"node:fs/promises";import Xn from"node:path";import OE from"node:os";import ME from"node:crypto";import CE from"node:zlib";var Ja=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),ae=(t,e=88)=>t.length>e?`${t.slice(0,e).trimEnd()}\u2026`:t,jr=t=>String(t).replace(/\/+$/,"").split("/").pop()||String(t);function yn(t,e){if(!t)return null;let n=String(t);if(e){let o=e.endsWith("/")?e:`${e}/`;if(n===e)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}var Hl=t=>`"${String(t).replace(/"/g,'\\"')}"`;function RE(t){let e=typeof t=="string"?t:String(t?.patch??t?.input??""),n=[];for(let r of e.matchAll(/^\*{3} (?:Update|Add|Delete) File: (.+)$/gm))n.push(r[1].trim());return n}function lw(t,e,n){switch(t){case"Shell":{let r=String(e?.command||"").trim();return r?{command:r,description:String(e?.description||"").trim()}:null}case"Read":{let r=e?.path;if(!r)return null;let o=Number(e?.offset)||null,s=o&&Number(e?.limit)?o+Number(e.limit)-1:null;return{command:ae(`read ${yn(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${jr(r)}`:`Read ${jr(r)}`}}case"Grep":{let r=e?.pattern;if(!r)return null;let o=e?.path?yn(e.path,n):null,s=e?.glob?` --glob ${Hl(e.glob)}`:"",a=ae(String(r),40);return{command:ae(`grep ${Hl(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=e?.glob_pattern;if(!r)return null;let o=e?.target_directory?yn(e.target_directory,n):null;return{command:ae(`glob ${Hl(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${ae(String(r),40)}\u201D`}}case"StrReplace":return e?.path?{command:ae(`edit ${yn(e.path,n)}`,140),description:`Edit ${jr(e.path)}`}:null;case"Write":return e?.path?{command:ae(`write ${yn(e.path,n)}`,140),description:`Write ${jr(e.path)}`}:null;case"Delete":return e?.path?{command:ae(`delete ${yn(e.path,n)}`,140),description:`Delete ${jr(e.path)}`}:null;case"ReadLints":{let o=(Array.isArray(e?.paths)?e.paths:[]).map(s=>yn(s,n)).filter(Boolean);return{command:ae(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=RE(e);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:ae(`apply_patch ${yn(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${jr(r[0])}`}}case"Await":{let r=e?.shell_id??e?.task_id;return{command:ae(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var NE=process.env.BOSTRAT_CURSOR_CHATS||Xn.join(OE.homedir(),".cursor","chats");function fw(t){let e=ME.createHash("md5").update(String(t)).digest("hex");return Xn.join(NE,e)}function pw(t){try{let e=JSON.parse(ts.readFileSync(Xn.join(t,"meta.json"),"utf8"));return{createdAtMs:Number(e.createdAtMs)||null,updatedAtMs:Number(e.updatedAtMs)||Number(e.createdAtMs)||null,hasConversation:e.hasConversation===!0}}catch{return null}}function PE(t){let e=fw(t),n;try{n=ts.readdirSync(e,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=Xn.join(e,o.name),a=Xn.join(s,"store.db");if(!ts.existsSync(a))continue;let i=pw(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 Xa(t,e){if(!t)return null;if(e){let n=Xn.join(fw(t),e),r=Xn.join(n,"store.db");if(ts.existsSync(r))return{id:e,dir:n,store:r,...pw(n)||{createdAtMs:null,updatedAtMs:null}}}return PE(t)}function uw(t,e){let n=t.prepare("SELECT data FROM blobs WHERE id = ?").get(e);return n?Buffer.from(n.data):null}function DE(t){if(!t)return null;let e=t;if(e.length>1&&e[0]===31&&e[1]===139)try{e=CE.gunzipSync(e)}catch{}try{return JSON.parse(e.toString("utf8"))}catch{return null}}function BE(t){let e=[],n=0,r=t.length,o=()=>{let s=0,a=0;for(;n<r;){let i=t[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=t.subarray(n,n+c);n+=c,a===1&&c===32&&e.push(l.toString("hex"))}else if(i===0)o();else if(i===5)n+=4;else if(i===1)n+=8;else break}return e}function jE(t,e){if(!Array.isArray(t))return[];let n=[];for(let r of t){if(!r||r.type!=="tool-call"||!Ja.has(r.toolName))continue;let o=lw(r.toolName,r.args,e);!o||!o.command||n.push({id:r.toolCallId||null,tool:r.toolName,command:o.command,description:o.description||""})}return n}var LE=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&e.type==="text"&&typeof e.text=="string").map(e=>e.text).join(`
|
|
96
|
+
`).trim():"",FE=t=>Array.isArray(t)?t.filter(e=>e&&e.type==="tool-call"&&e.toolName).map(e=>e.toolName):[],dw=process.getBuiltinModule?.("node:sqlite"),Lr={turns:[],trailingCommands:[],model:null,requestIds:[]};async function Wl(t,e){if(!dw)return Lr;let{DatabaseSync:n}=dw,r;try{r=new n(t,{readOnly:!0})}catch{return Lr}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 Lr;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return Lr}let a=s.lastUsedModel||null,i=s.latestRootBlobId?uw(r,s.latestRootBlobId):null;if(!i)return{...Lr,model:a};let c=[],l=[],u=[],f=null,d=[];for(let p of BE(i)){let m=DE(uw(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 S of m.content){let _=S?.providerOptions?.cursor?.modelName;_&&(f=_)}l.push(...jE(m.content,e).map((S,_)=>({...S,id:S.id||`${p}:${_}`}))),u.push(...FE(m.content));let y=LE(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 Lr}finally{try{r.close()}catch{}}}function mw(t,e){let n=t.updatedAtMs;if(!n)try{n=Math.round(ts.statSync(t.store).mtimeMs)}catch{n=Date.now()}let r=t.createdAtMs;(!r||r>n)&&(r=n-e*1e3);let o=Math.max(0,n-r);return{end:n,tsAt:s=>Math.round(r+o*(s+1)/e)}}async function hw(t,e,{limit:n=80}={}){let r=Xa(t,e);if(!r)return[];let{turns:o,model:s}=await Wl(r.store,t);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=mw(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 ww(t,e){let n=Xa(t,e);if(!n)return null;let{turns:r,requestIds:o,model:s}=await Wl(n.store,t);return{chatId:n.id,requestIds:o,models:[...new Set(r.map(a=>a.model).filter(Boolean))],configuredModel:s,updatedAtMs:n.updatedAtMs||null}}async function gw(t,e,{limit:n=80}={}){let r=Xa(t,e);if(!r)return[];let{turns:o,trailingCommands:s}=await Wl(r.store,t);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=mw(r,a.length||1),l=[];a.forEach((u,f)=>{for(let d of u.commands)l.push({...d,summary:gn(d.command,d.description),ts:c(f)})});for(let u of s)l.push({...u,summary:gn(u.command,u.description),ts:i+1});return l}async function yw(t,e){let n=Xa(t,e);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await $E.stat(n.store)).mtimeMs)}catch{return null}}import bw from"node:fs";import Kl from"node:fs/promises";import ql from"node:path";import UE from"node:os";var HE=process.env.BOSTRAT_CODEX_SESSIONS||ql.join(process.env.CODEX_HOME||ql.join(UE.homedir(),".codex"),"sessions");async function kw(t){try{return await Kl.realpath(t)}catch{return t}}function WE(t=HE){let e=[],n=r=>{let o;try{o=bw.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=ql.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{e.push({id:i?i[1]:null,file:a,mtimeMs:bw.statSync(a).mtimeMs})}catch{}}}};return n(t),e.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function qE(t,e=1048576){let n;try{n=await Kl.open(t,"r")}catch{return""}try{let r=Buffer.alloc(Math.min(e,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(`
|
|
97
|
+
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=e)break}return o}finally{await n.close()}}async function KE(t){let e=await qE(t);if(!e)return null;let n;try{n=JSON.parse(e)}catch{return null}return n?.type==="session_meta"&&n?.payload?.cwd||null}async function Yn(t,e){if(!t&&!e)return null;let n=WE();if(e){let o=n.find(s=>s.id===e);return o||null}let r=await kw(t);for(let o of n){let s=await KE(o.file);if(s&&(s===t||await kw(s)===r))return o}return null}var GE=new Set(["output_text","text"]),VE=t=>typeof t=="string"?t.trim():Array.isArray(t)?t.filter(e=>e&&typeof e.text=="string"&&GE.has(e.type)).map(e=>e.text).join(`
|
|
98
|
+
`).trim():"";async function xw(t,e,{limit:n=80,maxBytes:r=524288}={}){let o=await Yn(t,e);if(!o)return[];let s;try{s=await Ft(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,f=null;for(let d of s.split(`
|
|
99
|
+
`)){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 S=m.payload?.type;S==="task_started"?(l=!0,u=!1,f=m.payload?.turn_id||null):S==="task_complete"&&l&&(l=!1,!u&&m.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:Vl,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,Gl.has(w.name)||c.push(w.name);continue}if(w.type!=="message"||w.role!=="assistant")continue;u=!0;let y=VE(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)}var es=new Set(["exec_command","shell","exec"]),Gl=new Set(["wait","write_stdin"]),Vl="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 zE(t){if(typeof t?.cmd=="string"&&t.cmd.trim())return t.cmd.trim();let e=t?.command;return typeof e=="string"&&e.trim()?e.trim():Array.isArray(e)&&e.length?e.length===3&&/^(ba)?sh$/.test(e[0])&&e[1]==="-lc"?String(e[2]).trim():e.map(String).join(" ").trim():null}function JE(t){let e=t[0];if(e!=='"'&&e!=="'"&&e!=="`")return null;let n="";for(let r=1;r<t.length;r++){let o=t[r];if(o==="\\"){let s=t[++r];if(s===void 0)return null;if(s==="n")n+=`
|
|
100
|
+
`;else if(s==="t")n+=" ";else if(s==="r")n+="\r";else if(s==="u")if(t[r+1]==="{"){let a=t.indexOf("}",r+2);if(a===-1)return null;n+=String.fromCodePoint(parseInt(t.slice(r+2,a),16)),r=a}else n+=String.fromCharCode(parseInt(t.slice(r+1,r+5),16)),r+=4;else n+=s}else{if(o===e)return n;if(e==="`"&&o==="$"&&t[r+1]==="{")return null;n+=o}}return null}function XE(t){let e=String(t||""),n=[],r=/exec_command\s*\(/g;for(let s;s=r.exec(e);)n.push(s.index+s[0].length);let o=[];return n.forEach((s,a)=>{let i=e.slice(s,a+1<n.length?n[a+1]:e.length),c=/(?:^|[{,\s])["'`]?cmd["'`]?\s*:\s*/.exec(i);if(!c)return;let l=JE(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function Sw(t,e,{limit:n=400,maxBytes:r=524288}={}){let o=await Yn(t,e);if(!o)return[];let s;try{s=await Ft(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
|
|
101
|
+
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type!=="response_item")continue;let u=l.payload;if(!u)continue;let f=[];if(u.type==="function_call"&&es.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let m=zE(p);m&&(f=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(f=XE(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:gn(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function vw(t,e){let n=await Yn(t,e);if(!n)return null;try{return Math.round((await Kl.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var Fr=class{constructor(e,n=new Set){this.agent=e,this.cliTools=n}async readMessages(){return[]}async readCommands(){return[]}async readJobs(){return[]}async updatedAt(){return null}},zl=class extends Fr{transcript(e){return!e?.cwd||!e?.sessionId?null:Me(e.cwd,e.sessionId)}async readMessages(e,n){let r=this.transcript(e);return r?Pc(r,n):[]}async readCommands(e,n){let r=this.transcript(e);return r?aw(r,n):[]}async readJobs(e,n){let r=this.transcript(e);return r?cw(r,n):[]}async updatedAt(e){let n=this.transcript(e);if(!n)return null;try{return Math.round((await YE.stat(n)).mtimeMs)}catch{return null}}},Jl=class extends Fr{async readMessages(e,n){return hw(e.cwd,e.sessionId,n)}async readCommands(e,n){return gw(e.cwd,e.sessionId,n)}async updatedAt(e){return yw(e.cwd,e.sessionId)}},Xl=class extends Fr{sessionId(e){return e?.pinned?e.sessionId:null}async readMessages(e,n){return xw(e.cwd,this.sessionId(e),n)}async readCommands(e,n){return Sw(e.cwd,this.sessionId(e),n)}async updatedAt(e){return vw(e.cwd,this.sessionId(e))}},ZE=new Fr("unknown"),Tw=new Map;function Yl(t){return Tw.set(t.agent,t),t}function ns(t){return Tw.get(t)||ZE}Yl(new zl("claude",Ul));Yl(new Jl("cursor",Ja));Yl(new Xl("codex",es));var Zl=t=>e=>({...e,box:t.name,agent:t.agent,boxRole:t.role});async function Ql(t,e={}){return(await ns(t.agent).readMessages(t,e)).map(Zl(t))}async function Aw(t,e={}){return(await ns(t.agent).readCommands(t,e)).map(Zl(t))}async function Ew(t,e={}){return(await ns(t.agent).readJobs(t,e)).map(Zl(t))}var Rw=/to navigate\b.*\bEsc to cancel|Enter to select\b/,nu=/Chat about this/i,Zn=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,_w=/^(type something|chat about this|submit)\b/i,Qn=/^[─—-]{6,}\s*$/,ru=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;function tr(t){let e=t.search(ru);return(e>=0?t.slice(0,e):t).trim()}function QE(t){if(!t)return"";let e=t.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!e||/hidden|\d+\s+lines?/i.test(e)?"":e}function t_(t){let e=String(t).trim();return!!(!e||Qn.test(t)||ru.test(e)||/hidden|\d+\s+lines?/i.test(e)||/notespress|press n to add/i.test(e)||/^\s*notes?\s*$/i.test(e))}function e_(t,e=""){let n=[],r=/([☐☒])\s+([^☐☒←→✔]+)/g,o;for(;(o=r.exec(t))!==null;)n.push({label:o[2].trim(),answered:o[1]==="\u2612",current:!1});if(!n.length)return null;let s=n_(n,e);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function n_(t,e){let n=String(e).toLowerCase(),r=t.findIndex(c=>!c.answered),o=r>=0?r:t.length-1;if(!n)return o;let s=t.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 Iw(t){return t.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}var $w=/^\s*Switch model\?\s*$/m;function r_(t,e=""){let n=String(t);if(!$w.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
102
|
+
`),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(Zn);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&&!Qn.test(r[u])&&a.push(d)}}if(s.length<2)return null;let i=s.map(u=>({label:tr(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=["Switch model?",...a].join(" ");return{provider:"claude",box:e,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}]}}var Ow=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,o_=/^\s*[─—▔-]{6,}\s*$/;function s_(t,e=""){let n=String(t).replace(/\r/g,"").split(`
|
|
103
|
+
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!Ow.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(Zn);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&&!(o_.test(n[c])||Qn.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:tr(c.label),description:""}));return{provider:"claude",box:e,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 a_(t,e=""){let n=String(t);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
104
|
+
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(Zn);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:e,multiSelect:!1,cursorIndex:i,review:!0,promptId:"claude:review:submit-answers",questions:[{question:"Ready to submit your answers?",header:"Review",multiSelect:!1,options:a}]}}var tu=/Resume from summary/i,i_=/^\s*This session is\b/;function c_(t,e=""){let n=String(t);if(!tu.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
105
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(i_.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(Zn);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&&!Qn.test(r[u])&&a.push(d)}}if(s.length<2||!s.some(u=>tu.test(u.label)))return null;let i=s.map(u=>({label:tr(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=a.join(" ");return{provider:"claude",box:e,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}]}}var er=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;function ou(t){let e=[];for(let r=0;r<t.length;r++){let o=t[r].match(Zn);o&&e.push({line:r,cursor:!!o[1],n:Number(o[2]),label:o[4].trim()})}let n=e.sort((r,o)=>r.n-o.n);return n.length<2||n.some((r,o)=>r.n!==o+1)?null:n}var Mw=/Choose the text style that looks best with your terminal/,Cw=/Let's get started\./;function l_(t,e=""){let n=String(t);if(!Mw.test(n)||!Cw.test(n)||er.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
106
|
+
`),o=ou(r);return o?{provider:"claude",box:e,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:tr(s.label),description:""}))}]}:null}var Nw=/Select login method:/;function u_(t,e=""){let n=String(t);if(!Nw.test(n)||er.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
107
|
+
`),o=ou(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:tr(i),description:c.join("\xB7").trim()}});return{provider:"claude",box:e,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}]}}var su=/Paste code here if prompted/,d_=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;function f_(t,e){let n=t[e].trim();for(let r=e+1;r<t.length;r++){let o=t[r].trim();if(!o||!d_.test(o)||su.test(t[r]))break;n+=o}return n}function p_(t,e=""){let n=String(t);if(!su.test(n)||er.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
108
|
+
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=f_(r,s);break}return o?{provider:"claude",box:e,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}var eu=/OAuth error:/,m_=/Press Enter to retry\./;function h_(t,e=""){let n=String(t);if(!eu.test(n)||!m_.test(n)||er.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
109
|
+
`)].reverse().find(a=>eu.test(a))||"OAuth error").trim();return{provider:"claude",box:e,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"}]}]}}var Pw=/Quick safety check: Is this a project you created or one you trust/;function w_(t,e=""){let n=String(t);if(!Pw.test(n)||er.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
110
|
+
`),o=ou(r);return o?{provider:"claude",box:e,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:tr(s.label),description:""}))}]}:null}var Dw=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/;function g_(t,e=""){let n=String(t);if(er.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
111
|
+
`),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(Dw);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(Qn.test(r[l])||ru.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:e,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 y_(t){let e=-1;for(let r=t.length-1;r>=0;r--)if(nu.test(t[r])){e=r;break}if(e<0)return null;let n=-1;for(let r=e-1;r>=0;r--)if(/[☐☒]/.test(t[r])){n=r;break}return n<0?null:{headerLine:n,chatLine:e,optStart:n+1,optEnd:e}}function b_(t,e=""){let n=String(t);if(!Rw.test(n)||!nu.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
112
|
+
`),o=y_(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let v=i;v<c;v++){let D=r[v].match(Zn);D&&(u<0&&(u=v),l.push({line:v,cursor:!!D[1],checked:D[3]!=null&&D[3]!==" ",hasCheckbox:D[3]!=null,label:D[4].trim(),desc:""}))}if(!l.length)return null;for(let v of l)for(let D=v.line+1;D<Math.min(r.length,v.line+4)&&!(Zn.test(r[D])||Qn.test(r[D]));D++){let B=r[D].trim();if(!(!B||t_(r[D])||_w.test(B))){v.desc=QE(B);break}}let f=l.filter(v=>!_w.test(v.label));if(!f.length)return null;let d=f.some(v=>v.hasCheckbox),p=Math.max(0,f.findIndex(v=>v.cursor)),m=s>=0?s+1:0,w=[];for(let v=m;v<u;v++){let D=r[v].trim();D&&!Qn.test(r[v])&&!/[☐☒]/.test(r[v])&&w.push(D)}let y=w.join(" ")||(s>=0?Iw(r[s]):"")||"The agent is asking a question",b=s>=0?e_(r[s],y):null,S=b?.currentTabLabel||(s>=0?Iw(r[s]):""),_=f.map(v=>({label:tr(v.label),description:v.desc})),O=_.map(v=>v.label).join("|"),R=`claude:${b?`q${b.questionIndex+1}/${b.questionTotal}`:"q1/1"}:${S}:${y}:${O}`.slice(0,240),C=b?`claude:ask:${b.tabs.map(v=>v.label).join("|")}`.slice(0,240):R;return{provider:"claude",box:e,multiSelect:d,cursorIndex:p,promptId:R,groupId:C,...b?{questionIndex:b.questionIndex,questionTotal:b.questionTotal,currentTabLabel:b.currentTabLabel,tabs:b.tabs}:{},questions:[{question:y,header:S,multiSelect:d,options:_}]}}function Ur(t,e){let n=String(e||"");return n?t==="claude"?Rw.test(n)&&nu.test(n)||$w.test(n)||tu.test(n)||Ow.test(n.trimEnd().split(`
|
|
113
|
+
`).pop()||"")?!0:er.test(n)?!1:Mw.test(n)&&Cw.test(n)||Nw.test(n)||su.test(n)||eu.test(n)||Pw.test(n)||Dw.test(n.trimEnd().split(`
|
|
114
|
+
`).pop()||""):t==="codex"&&/Sign in with ChatGPT|one-time code|Paste or type your API key|link to authenticate/i.test(n)?!0:t==="cursor"||t==="codex"?/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i.test(n):!1:!1}function nr(t,e){return!t||!e?null:t.agent==="claude"?a_(e,t.name)||b_(e,t.name)||r_(e,t.name)||c_(e,t.name)||h_(e,t.name)||p_(e,t.name)||u_(e,t.name)||l_(e,t.name)||w_(e,t.name)||g_(e,t.name)||s_(e,t.name):t.agent==="cursor"||t.agent==="codex"?Tp(t.agent,e,t.name):null}async function au(t){if(!t)return null;let e=await an(t.name).catch(()=>"");return nr(t,e)}function Bw(t,e){let n=Array.isArray(e?.[0])?e[0].filter(a=>Number.isInteger(a)&&a>=0):[],r=Number.isInteger(t?.cursorIndex)?t.cursorIndex:0,o=(a,i)=>{let c=i-a;return Array.from({length:Math.abs(c)},()=>c>0?"Down":"Up")},s=[];if(t?.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 jw(t,e){let n=Number.isInteger(t?.questionIndex)?t.questionIndex:0,o=(Number.isInteger(e)?e:n)-n;if(!o)return[];let s=o>0?"Tab":"BTab";return Array.from({length:Math.abs(o)},()=>s)}function Hr(t){return typeof t=="string"?t.trim():""}var Lw=/^\s*─{8,}\s*$/,k_=/^❯[ ]?(.*)$/;function rs(t,e){if(t!=="claude")return null;let n=String(e).replace(/\r/g,"").split(`
|
|
115
|
+
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(Lw.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(Lw.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(k_);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 St(t){return String(t||"").replace(/\s+/g," ").trim()}function Fw(t,e,{slackMs:n=2e4}={}){let r=St(e?.text);return!r||typeof e?.at!="number"?!1:(Array.isArray(t)?t:[]).some(o=>o?.role==="user"&&typeof o.ts=="number"&&o.ts>=e.at-n&&St(o.text).includes(r))}function Uw(t,e,n,{busy:r=!1}={}){let o=rs(t,n);return o&&iu(e,o)?"stuck":r||o?"submitted":"unknown"}function iu(t,e){let n=St(t),r=St(e).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}import x_ from"node:crypto";var cu=new Set(["cursor","codex"]),Ww=4,S_=4,v_=2,T_=24,Hw=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,os=t=>typeof t=="string"?t.trim():"";function A_(t){let e=Array.isArray(t)?t:Array.isArray(t?.questions)?t.questions:null;if(!e||!e.length)return null;let n=[];for(let r of e.slice(0,Ww)){let o=os(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,S_)){let c=os(i?.label);if(!c)return null;let l=os(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<v_)return null;n.push({question:o,header:os(r?.header).slice(0,T_),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function lu(t){if(typeof t!="string"||!t.includes("```bostrat-question"))return null;Hw.lastIndex=0;let e=null;for(let o;o=Hw.exec(t);)e=o;if(!e||e.index+e[0].length<t.trimEnd().length)return null;let n;try{n=JSON.parse(e[2])}catch{return null}let r=A_(n);return r?{text:t.slice(0,e.index).trimEnd(),questions:r}:null}function uu(t,e,n){let r=x_.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${t}:ask:${e||"turn"}:${r}`}function du(t,{leadBoxId:e,provider:n,isAnswered:r,maxQuestions:o=Ww}={}){if(!Array.isArray(t)||!e||!cu.has(n))return null;let s=null;for(let l=t.length-1;l>=0;l--){let u=t[l];if(u?.kind==="prompt")return null;if(u?.kind==="message"){if(u.role!=="assistant")return null;s=u;break}}if(!s||s.box!==e||s.agent!==n)return null;let a=s.ask?.questions||lu(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=uu(n,s.sourceId||s.id,a);return typeof r=="function"&&r(c)?null:{provider:n,box:e,promptId:c,groupId:c,answerMode:"say",source:"transcript",multiSelect:i[0].multiSelect,questionIndex:0,questionTotal:i.length,questions:i}}function qw(t,e,n){let r=[];(t?.questions||[]).forEach((a,i)=>{let c=(e?.[i]||[]).map(l=>a.options?.[l]?.label).filter(Boolean);c.length&&r.push({name:a.header||a.question,picks:c.join(", ")})});let o=os(n),s;return r.length?r.length===1?s=`${r[0].name}: ${r[0].picks}`:s=`Answers:
|
|
116
116
|
${r.map(a=>`- ${a.name}: ${a.picks}`).join(`
|
|
117
117
|
`)}`:s=o,r.length&&o&&(s+=`
|
|
118
|
-
${o}`),s||""}var
|
|
118
|
+
${o}`),s||""}var Kw=`
|
|
119
119
|
When you are BLOCKED on a decision only the operator can make \u2014 an ambiguous
|
|
120
120
|
requirement, an irreversible choice, several valid approaches \u2014 do not bury the
|
|
121
121
|
question in prose. End your message with exactly ONE fenced code block tagged
|
|
@@ -135,42 +135,42 @@ your turn. The operator sees the questions as an interactive card in their chat
|
|
|
135
135
|
answer. Never use the block for rhetorical questions, progress updates, or
|
|
136
136
|
anything you can decide yourself; keep asking in prose for questions without
|
|
137
137
|
enumerable options.
|
|
138
|
-
`;import{execFile as
|
|
139
|
-
`).filter(Boolean);return e&&(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
|
|
140
|
-
`).map(s=>s.trim()).filter(Boolean)}async function
|
|
141
|
-
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}import
|
|
142
|
-
`),n=e.findIndex(o=>/Select Model and Effort/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<e.length;o++){let s=e[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(e[o]))break}return r.length?r:null}function
|
|
143
|
-
`),n=e.findIndex(o=>/Select Reasoning Level/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<e.length;o++){let s=e[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(e[o]))break}return r.length?r:null}function
|
|
144
|
-
`);for(let n=e.length-1;n>=0;n--){let r=e[n].match(/Model changed to\s+(\S+)(?:\s+(\S+))?\s*$/i);if(r)return{model:r[1],effort:r[2]||null}}return null}function
|
|
145
|
-
`),n=e.findIndex(s=>/Available models|Models matching/i.test(s));if(n<0)return null;let r=/Models matching/i.test(e[n]),o=[];for(let s=n+1;s<e.length;s++){let a=e[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 $w(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim();if(t==="codex"){let[r,o]=n.split(/[\s:]+/);if(o&&!_w.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=Iw.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!$E.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function Xo(t,e,{timeoutMs:n=IE}={}){let r=Date.now()+n;for(;;){let o=await ft(t,{lines:80}).catch(()=>""),s=e(o);if(s)return s;if(Date.now()>r)return null;await La(RE)}}async function ME(t,e){return await St(t,`/model ${e.model}`,_e("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function NE(t,e){await Vs(t),await St(t,"/model",{enter:!1}),await La(250),await bt(t,["Enter"],{kind:"model-switch"});let n=await Xo(t,vw);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===e.model);if(!r)throw await bt(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${e.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=CE(i);return!c||c.model!==e.model||e.effort&&c.effort&&c.effort.toLowerCase()!==e.effort.toLowerCase()?null:c};await St(t,String(r.n),{enter:!1,kind:"model-switch"});let s=await Xo(t,i=>Tw(i)||!vw(i)&&o(i)&&{done:o(i)});if(!s)throw await bt(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=e.effort?_w.get(e.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await bt(t,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${e.effort}" for ${e.model}`);await St(t,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Xo(t,i=>!Tw(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 PE(){let t=Sw.join(process.env.CURSOR_CONFIG_DIR||Sw.join(_E.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await EE.readFile(t,"utf8"))?.model||null}catch{return null}}async function BE(t,e){await Vs(t),await Js(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await Xo(t,Aw),r=c=>String(c||"").toLowerCase(),o=c=>r(c.label).startsWith(r(e.filter)),s=n?.rows.find(c=>c.active)||null;if(n&&!(s&&o(s)))for(let c=0;c<12;c++){let l=await Xo(t,Aw,{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 bt(t,["Down"],{kind:"model-switch"}),await La(250)}if(!n||!s||!o(s)){await bt(t,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${e.filter}`.length).fill("BSpace");throw await bt(t,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${e.filter}"`:"cursor model picker didn't open")}await bt(t,["Enter"],{kind:"model-switch"}),await La(800);let a=await PE();return{verified:!!a&&(a.modelId===e.model||a.displayModelId===e.model||r(a.displayName).startsWith(r(e.filter))),model:a?.modelId||null,note:"cursor applies this as the global default for new sessions too"}}async function Ow(t,e,n){return t==="codex"?NE(e,n):t==="cursor"?BE(e,n):ME(e,n)}var Kt=t=>t||null;function Ua(t){let e=t||{},n=DE(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function DE(t){switch(t.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:t.box,provider:Kt(t.agent),payload:{text:t.text,boxRole:t.boxRole,...t.kind?{kind:t.kind}:{},...t.attachments?.length?{attachments:t.attachments}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:t.box,provider:Kt(t.agent),payload:{saidTs:t.saidTs,text:t.text,boxRole:t.boxRole}};case"orchestration-prompt":return{type:"turn.user",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Kt(t.agent),payload:{text:t.prompt,kind:t.kind,boxRole:t.boxRole}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:t.to,provider:Kt(t.toAgent),payload:{from:t.from,fromAgent:t.fromAgent,to:t.to,toAgent:t.toAgent,reason:t.reason,handoffStartedAt:t.handoffStartedAt,fromSessionId:t.fromSessionId,toSessionId:t.toSessionId,epoch:t.epoch,singleBox:t.singleBox,forced:t.forced,recovered:t.recovered}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Kt(t.toAgent),payload:{box:t.box,fromAgent:t.fromAgent,toAgent:t.toAgent,reason:t.reason,error:t.error,rolledBack:t.rolledBack,handoffStartedAt:t.handoffStartedAt}};case"subtask-delegated":return{type:"subtask.delegated",actor:"orchestrator",source:"orchestrator",boxId:t.to,provider:Kt(t.toAgent),payload:{task:t.task,subtaskId:t.subtaskId,reason:t.reason}};case"subtask-merged":return{type:"subtask.merged",actor:"orchestrator",source:"orchestrator",boxId:t.box||t.from,provider:Kt(t.agent),payload:{...Br(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:Kt(t.agent||t.toAgent),payload:{kind:"subtask-failed",...Br(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Kt(t.agent),payload:{change:"subtask-resumed",...Br(t)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:t.box,payload:{change:"model",model:t.to,from:t.from??null,verified:t.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:"codex",payload:{change:"cli-update",from:t.from??null,to:t.to??null,status:t.status,...t.error?{error:t.error}:{}}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:t.box,payload:{change:"opened",...Br(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...Br(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Kt(t.agent),payload:{change:"rehydrated"}};case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Br(t)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:Kt(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}var Fa=Promise.resolve(),eu=t=>(Fa=Fa.then(t).catch(()=>{}),Fa),Mw=()=>Fa;function Nw(t,e){let n=Ua(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;eu(()=>R(r,n))}var Cw=new Set;async function Pw(t){if(!(!t?.id||Cw.has(t.id))){Cw.add(t.id);try{let e=await L(t),n=new Set(e.filter(r=>r.source==="orchestrator").map(r=>`${r.type}|${r.payload?.ts??"?"}`));for(let r of t.events||[]){let o=Ua(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await R(t,o)}}catch{}}}function Br(t){let{type:e,at:n,...r}=t;return r}var tu=new Map;function Bw(t,e,n){!n?.promptId||tu.get(t.id)===n.promptId||(tu.set(t.id,n.promptId),eu(()=>R(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:Kt(n.provider||e?.agent),payload:{promptId:n.promptId,question:n.question||n.title||null,options:(n.options||[]).map(r=>r?.label??r).slice(0,12)}})))}function nu(t,e,n,r={}){tu.delete(t.id),eu(()=>R(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:Kt(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}import{randomUUID as h_}from"node:crypto";var jw=/(?<!\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,Lw=/(?<!\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,Fw=/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,Uw=/(?<!\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,jE=new RegExp(`\\b(?:${jw}|${Lw}|${Fw}|${Uw})\\b`,"i"),LE=/\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,FE=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,UE=90;function HE(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function Hw(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of HE(n).reverse()){if(!jE.test(r))continue;let o=r.endsWith("?")&&r.length<=UE&&FE.test(r);if(LE.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}var Ww=["commit","rebase","push","pr"],WE=new RegExp(`\\b(?:${jw})\\b`,"i"),qE=new RegExp(`\\b(?:${Uw})\\b`,"i"),KE=new RegExp(`\\b(?:${Lw})\\b`,"i"),GE=new RegExp(`\\b(?:${Fw})\\b`,"i"),VE=/\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;function qw(t){let e=String(t||""),n=[];WE.test(e)&&n.push("commit"),qE.test(e)&&n.push("rebase"),KE.test(e)&&n.push("push");let r=VE.test(e);!r&&GE.test(e)&&n.push("pr");let o=null;if(r){let s=e.match(/(?:pr|pull\s+request)\s*\(?\s*#(\d+)\)?/i)||e.match(/#(\d+)/);o={number:s?Number(s[1]):null}}return{offered:n.length?n:null,toPr:o}}function Ha(t,{branch:e=null,base:n=null,toPr:r=null,repos:o=null,offeredRebase:s=!1}={}){Array.isArray(t)||(t=t==="push"?["commit","push"]:["commit","push","pr"]);let a=new Set(t),i=Array.isArray(o)&&o.length>0,c=i?o.map(p=>`${p.name}/`).join(", "):null,l=e?` on this branch (${e})`:"",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${e?` (${e})`:""}`;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${e?` (${e})`:""} 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}var zE=3e4;function Kw(t,e=Date.now()){let n=[],r=null;for(let o of t||[]){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}!r||a==null||a<r.from||(o.type==="pr.offer.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&&e-r.lastActivity<=zE&&n.push({box:r.box,from:r.from,to:e+1e12}),n}function Gw(t,e){let n=new Set;for(let r of e||[]){if(!r.settled)continue;let o=null;for(let s of t||[])s.kind!=="message"||s.role!=="assistant"||s.box!==r.box||s.ts==null||s.ts<r.from||s.ts>r.to||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}var Dw={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"};function Vw(t){let e=(Array.isArray(t)?t:[]).filter(r=>Dw[r]).map(r=>Dw[r]);return`The ship you were given the go-ahead for${e.length?` (${e.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.`}import{execFile as QE}from"node:child_process";import{promisify as t_}from"node:util";import JE from"node:fs";import ru from"node:fs/promises";import Yw from"node:path";import XE from"node:os";var Zw=process.env.BOSTRAT_STATE_DIR||Yw.join(process.env.HOME||XE.tmpdir(),".bostrat"),Wa=Yw.join(Zw,"github-metrics.json"),Qw=60*6e4,zw=5*6e4,YE=6e4,Dr=Date.now(),je=new Map,dn=null,Xn=0,Jw=0,qa=t=>[...t.values()].reduce((e,n)=>e+n,0);try{let t=JSON.parse(JE.readFileSync(Wa,"utf8"));t?.windowStart&&(Date.now()-t.windowStart<Qw?(Dr=t.windowStart,je=new Map(Object.entries(t.byCaller||{})),dn=t.lastHour||null):qa(new Map(Object.entries(t.byCaller||{})))&&(dn={start:t.windowStart,total:qa(new Map(Object.entries(t.byCaller||{}))),byCaller:t.byCaller||{}}))}catch{}var Xw=Promise.resolve();function tg(t,e=!1){if(!e&&t-Jw<YE)return;Jw=t;let n=JSON.stringify({windowStart:Dr,byCaller:Object.fromEntries(je),lastHour:dn});Xw=Xw.then(()=>ru.mkdir(Zw,{recursive:!0})).then(()=>ru.writeFile(`${Wa}.tmp`,n)).then(()=>ru.rename(`${Wa}.tmp`,Wa)).catch(()=>{})}function eg(t){t-Dr<Qw||(dn={start:Dr,total:qa(je),byCaller:Object.fromEntries(je)},dn.total&&console.log(`[github] ${dn.total} calls last hour`,dn.byCaller),Dr=t,je=new Map,tg(t,!0))}function Le(t="other",e=Date.now()){eg(e),je.set(t,(je.get(t)||0)+1),tg(e)}function ng(t=Date.now()){return eg(t),{windowStart:Dr,total:qa(je),byCaller:Object.fromEntries(je),lastHour:dn,backoffUntil:Xn>t?Xn:null}}var ZE=/rate limit|too quickly|abuse detection|HTTP 429/i;function fn(t){return ZE.test(String(t?.message||t||""))}function pn(t,e=Date.now()){return Xn<=e&&console.warn(`[github] rate limited \u2014 backing off ${zw/6e4} min:`,String(t?.message||t||"").slice(0,200)),Xn=Math.max(Xn,e+zw),Xn}function Ka(t=Date.now()){return Math.max(0,Xn-t)}async function ou(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await ft(t,{lines:xt}).catch(()=>"");if(!Ee(e,r))return tp(e,r)?{working:!0,prompted:!1}:n;if(Mr(e,r)){let o=await Ze(t).catch(()=>r);if(Jn({name:t,agent:e},o))return{working:!1,prompted:!0}}return{working:!0,prompted:!1}}async function Ga(t,e){return(await ou(t,e)).working}var rg=t_(QE),og=[4e3,15e3,45e3],e_=2500,n_=5*6e4;async function su(t,e,{midTurn:n=Ga,pollMs:r=e_,capMs:o=n_}={}){let s=Date.now()+o;for(;await n(t,e).catch(()=>!1);){if(Date.now()>=s)return!1;await new Promise(a=>setTimeout(a,r))}return!0}var r_=6e4,o_=9e4,s_=2e3,a_=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,i_=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,c_=/\s--web\b/,sg=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/;function au(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function ag(t){let e=String(t||""),n=e.match(a_);if(!n)return null;let r=au(e,n.index);return c_.test(r)?null:{repo:r.match(sg)?.[1]??null}}var l_=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);function ig(t){let e=String(t||""),n=e.match(i_);if(!n)return null;let r=au(e,n.index),o=au(e,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("-")){l_.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(sg)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}var u_="number,url,title,headRefName,createdAt";async function d_(t,e){let n=async a=>{Le("observer");let{stdout:i}=await rg("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",u_],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...a}}).catch(c=>{throw fn(c)&&pn(c),c});return JSON.parse(i||"[]")},r=a=>(a||[]).filter(i=>Date.parse(i.createdAt)>=e-r_),o=await ae({slug:t}),s=r(await n(o));return!s.length&&o.GH_TOKEN&&(s=r(await n({}).catch(()=>[]))),s.sort((a,i)=>Date.parse(i.createdAt)-Date.parse(a.createdAt))}async function f_(t,e){if(e)return[e];let n=[];for(let r of cg(t)){let o=await et(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await ve().catch(()=>[])){let o=await et(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function cg(t){let e=t.workspace;return e?.kind==="multi"?(e.repos??[]).map(n=>n?.base).filter(Boolean):[e?.base||t.cwd].filter(Boolean)}async function p_(t,e,n){let r=t.workspace?.branch,o=[];for(let s of await f_(t,e)){let a;try{a=await d_(s,n)}catch{continue}for(let i of a){let c=await Ot(s,i.number);if(!(c&&(c.trackId!==t.id||c.announced))){if(r&&i.headRefName===r)return{slug:s,pr:i};o.push({slug:s,pr:i})}}}return o.length?(o.sort((s,a)=>Date.parse(a.pr.createdAt)-Date.parse(s.pr.createdAt)),o[0]):null}var mn=new Set;function lg(t,e,{delays:n=og,turnEnd:r}={}){let o=ag(e.command);if(!o)return null;let s=!!ig(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(mn.has(a))return null;mn.add(a);let i=e.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(h=>setTimeout(h,l));let u=await p_(t,o.repo,i).catch(()=>null);if(!u)continue;let{slug:f,pr:d}=u,{link:p,created:m}=await qt({repo:f,number:d.number,trackId:t.id,url:d.url,headRef:d.headRefName,title:d.title,source:"observed"});return!m&&!(p?.trackId===t.id&&await jo(f,d.number))?null:(await su(e.box,e.agent,r),await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.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{mn.delete(a)}})();return c.catch(()=>mn.delete(a)),c}var m_="number,url,title,headRefName,state";function ug(t,e,{delays:n=og,turnEnd:r,openedWait:o}={}){let s=ig(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(mn.has(a))return null;mn.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=cg(t);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let h of m){let y=await et(h).catch(()=>null);y&&!f.includes(y)&&f.push(y)}}if(l||=t.workspace?.branch,!f.length||!l)return null;c=f[0];let d=async(m,h)=>{let y=await Ot(c,m.number);return y&&y.trackId!==t.id?null:(y||await qt({repo:c,number:m.number,trackId:t.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await Lo(c,m.number),h&&((await L(t).catch(()=>[])).some(I=>I.type==="pr.state.changed"&&(I.payload?.change==="auto-armed"||I.payload?.change==="merged")&&I.payload?.number===m.number)||(await su(e.box,e.agent,r),await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.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($=>setTimeout($,m));let h=[];for(let $ of f)try{Le("observer");let{stdout:F}=await rg("gh",["pr","view",String(l),"--repo",$,"--json",m_],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await ae({slug:$})}}),T=JSON.parse(F||"null");T?.number&&h.push({slug:$,pr:T})}catch(F){fn(F)&&pn(F)}let y=h.find($=>$.pr.state==="MERGED")??(h.length===1?h[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 S=await Ot(c,b.number);if(S&&S.trackId!==t.id)return null;S||await qt({repo:c,number:b.number,trackId:t.id,url:b.url,headRef:b.headRefName,title:b.title,source:"merge-observed"}),await su(e.box,e.agent,r);let I=await L(t).catch(()=>[]);if(I.some($=>$.type==="pr.state.changed"&&$.payload?.change==="merged"&&$.payload?.number===b.number))return null;let M=$=>$.some(F=>F.type==="pr.state.changed"&&F.payload?.change==="opened"&&F.payload?.number===b.number),z=0;if(!M(I)){if(ag(e.command)&&await jo(c,b.number)){let $=Date.now();z=$+1,await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:$,change:"opened",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c,selfMerge:!0}})}else if((await Ot(c,b.number))?.announced){let $=o?.capMs??o_,F=o?.pollMs??s_,T=Date.now()+$;for(;Date.now()<T&&(await new Promise(C=>setTimeout(C,F)),!M(await L(t).catch(()=>[]))););}}return await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Math.max(Date.now(),z),change:"merged",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c}}),await wn(c,b.number).catch(()=>{}),await hn(c,b.number).catch(()=>{}),await cn(c,b.number,"merged"),b}return p?d(p,!1):null}finally{mn.delete(a)}})();return i.catch(()=>mn.delete(a)),i}function cu(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function dg(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function fg(t){return`job|${t.box}|id:${t.id}`}var iu=new Map,Va=new Map;async function lu(t){return iu.has(t.id)?iu.get(t.id):(Va.has(t.id)||Va.set(t.id,L(t).then(e=>{let n={keys:new Set,cmdN:0};for(let r of e)if((r.type==="turn.user"||r.type==="turn.agent")&&r.payload?.text!=null){let o=r.type==="turn.user"?"user":"assistant";r.payload.sourceId&&n.keys.add(cu({role:o,sourceId:r.payload.sourceId})),n.keys.add(cu({role:o,ts:r.payload.ts,text:r.payload.text}))}else if(r.type==="tool.called"&&r.source==="transcript"&&r.payload?.command){let o=r.payload;n.keys.add(dg({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(fg({id:r.payload.id,box:r.boxId}));return iu.set(t.id,n),n}).finally(()=>Va.delete(t.id))),Va.get(t.id))}async function pg(t,e){try{let n=(await lu(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=cu(r);if(!n.has(o)&&(n.add(o),await R(t,{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===t.leadBox&&t.workspace?.branch)){let s=Hw(r.text);if(s){let{offered:a,toPr:i}=qw(s),c;if(t.workspace?.kind==="multi"){let l=await Yo(t).catch(()=>[]);c=l.length?l.map(u=>({name:u.name,slug:u.repo??null})):void 0}await R(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:h_(),ts:r.ts,excerpt:s,branch:t.workspace.branch,base:t.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:r.boxRole,offered:a||void 0,toPr:i||void 0,repos:c}})}}}}catch{}}async function mg(t,e){try{let n=await lu(t),r=[...e||[]].filter(o=>o&&String(o.command||"").trim()).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=dg(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await R(t,{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}}),lg(t,o),ug(t,o))}}catch{}}async function hg(t,e){try{let n=await lu(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=fg(o);n.keys.has(s)||(n.keys.add(s),await R(t,{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 w_=/^Base directory for this skill:\s*(\S+)/,g_=/^Run the "([^"]+)" workflow\./,y_=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,b_=/^<local-command-(?:stdout|caveat)>/i,k_=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,x_=/^Continue from where you left off\.$/,S_=/^<task-notification>/i,v_=/<summary>([\s\S]*?)<\/summary>/i,T_=/<task-id>([\s\S]*?)<\/task-id>/i,A_=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,E_=/<status>([\s\S]*?)<\/status>/i,__=/<result>([\s\S]*?)(?:<\/result>|$)/i;var uu=new Set(["model","clear","compact","login","exit","quit"]);function wg(t){return uu.has(String(t||"").replace(/^\//,"").toLowerCase())}var I_=/^\/([a-z0-9:_-]+)$/i;function za(t){let e=String(t||"").trim();if(!e)return null;let n=e.match(w_);if(n)return{kind:"skill",name:n[1].split("/").filter(Boolean).pop()||"skill"};let r=e.match(g_);if(r)return{kind:"skill",name:r[1]};let o=e.match(y_);if(o){let a=o[1].toLowerCase();return uu.has(a)?{kind:"suppress"}:{kind:"command",command:`/${a}`}}let s=e.match(I_);if(s&&uu.has(s[1].toLowerCase()))return{kind:"suppress"};if(b_.test(e))return{kind:"suppress"};if(x_.test(e))return{kind:"suppress"};if(k_.test(e))return{kind:"suppress"};if(S_.test(e)){let a=c=>R_(e.match(c)?.[1].trim()||"")||null,i=a(__);return{kind:"task",summary:a(v_)||"",taskId:a(T_),toolUseId:a(A_),status:a(E_)?.toLowerCase()||null,resultHead:i?i.length>600?`${i.slice(0,600).trimEnd()}\u2026`:i:null}}return null}function R_(t){return t.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function gg(t){return t?.kind==="skill"?`Ran the ${t.name} skill`:t?.kind==="command"?`Ran ${t.command}`:t?.kind==="task"?t.summary||"A background task finished":""}function yg(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":"command-run"}var st=t=>t.payload?.ts??t.ts;function $_(t){return{id:t.id,kind:"prompt",groupId:t.groupId,provider:t.provider,box:t.box,agent:t.provider,questionTotal:t.questions.length,answeredVia:"chat",questions:t.questions.map((e,n)=>({index:n,header:e.header||"",question:e.question,multiSelect:!!e.multiSelect,chosen:[]})),ts:t.ts}}var O_=new Set(["opened","ci-fixed","reconciled","pushed"]),bg=t=>`${t.repo??""}#${t.number}`;function C_(t,e,n){let r=bg(n),o=t.has(r)?r:null;if(!o&&n.number!=null)for(let[i,c]of t){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?t.get(o):t.get(s);if(o&&o!==s){t.delete(o);let i=e.get(o);if(e.delete(o),i?.length){let c=e.get(s)??[];e.set(s,c.concat(i))}}return t.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 Zo(t){let e=[],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 t||[]){if(g.type!=="tool.called"||g.source!=="transcript")continue;let w=String(g.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(w))for(let _ of w.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let v=_[0].split("/").pop().toLowerCase();f.has(v)||f.set(v,[]),f.get(v).push(g.payload?.ts??g.ts)}}let d=3*6e4,p=20*6e4,m=(g,w)=>!!(g?.name&&f.get(String(g.name).toLowerCase()))?.some(v=>v>=w-d&&v<=w+p);for(let g of t||[])if(g.type==="turn.retracted"){r.add(`${g.boxId}|${g.payload?.saidTs}`);let w=Tt(g.payload?.text);w&&(o.has(g.boxId)||o.set(g.boxId,[]),o.get(g.boxId).push(w))}else if(g.type==="turn.user"&&g.source==="orchestrator"){let w=Tt(g.payload?.text);w&&(s.has(g.boxId)||s.set(g.boxId,new Set),s.get(g.boxId).add(w))}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 w=g.payload.note,_={status:w.status||null,summary:w.summary||null,resultHead:w.resultHead??null,ts:st(g)};w.toolUseId&&i.set(w.toolUseId,_);let v=String(w.summary||"").match(u);v&&l.set(v[1],_)}let h=new Map,y=new Map,b=null,S=new Map,I=new Map,M=new Map,z=new Map,$=new Map,F=new Map,T=new Map,C=()=>{for(let g of T.values())g.questions.length&&e.push(g);T.clear()},D=null,it=6e4,ot=new Map,A=(g,w)=>{let _=o.get(g);if(!_?.length)return!1;let v=Tt(w),O=s.get(g);return _.some(E=>{if(!v.startsWith(E))return!1;let N=Tt(v.slice(E.length));return!!N&&!!O?.has(N)})};for(let g of t||[]){let w=g.payload||{};switch(g.type){case"turn.user":{if(g.source==="hook"||!String(w.text||"").trim()&&!w.images?.length)break;{let _=w.note||za(w.text);if(_){if(_.kind==="task"){if(_.toolUseId&&a.has(_.toolUseId))break;let v=String(_.summary||"").match(u);if(!_.toolUseId&&v&&c.has(v[1]))break}if(_.kind==="command"&&wg(_.command))break;_.kind!=="suppress"&&e.push({id:g.id,kind:"event",type:yg(_),name:_.name??null,command:_.command??null,summary:_.summary??null,ts:st(g),box:g.boxId,agent:g.provider});break}}if(r.has(`${g.boxId}|${st(g)}`)||g.source==="transcript"&&A(g.boxId,w.text))break;if(w.kind==="merge-notify"||w.kind==="ship-notify"){e.push({id:g.id,kind:"event",type:w.kind==="ship-notify"?"ship-notified":"merge-notified",text:w.text,ts:st(g),box:g.boxId,agent:g.provider});break}if(w.kind==="mock-picked"||w.kind==="ci-fix"||w.kind==="reconcile"||w.kind==="pr-go"||w.kind==="push-go"||w.kind==="spin-up-view"||w.kind==="prompt-answer"){w.kind==="prompt-answer"&&(D=null);break}for(let _ of S.values())_.status="stale";S.clear(),D&&D.box===g.boxId&&(e.push($_(D)),D=null),e.push({id:g.id,kind:"message",role:"user",text:w.text,ts:st(g),box:g.boxId,agent:g.provider,boxRole:w.boxRole,...g.actor==="orchestrator"?{orchestration:w.kind||!0}:{},...w.sourceId?{sourceId:w.sourceId}:{},...w.attachments?.length?{attachments:w.attachments}:{},...w.images?.length?{images:w.images}:{}});break}case"turn.agent":{if(w.model==="<synthetic>"&&/^No response requested\.$/.test(String(w.text||"").trim()))break;C();let _=(w.images||[]).filter(N=>!m(N,st(g)));if(!String(w.text||"").trim()&&!_.length)break;if(g.provider==="cursor"){let N=`${g.boxId}|${w.text}`;if(n.has(N))break;n.add(N)}(!D||D.box===g.boxId)&&(D=null);let v=g.provider==="codex"?(w.tools||[]).filter(N=>!Al.has(N)&&!Ko.has(N)):w.tools||[],O=w.text,E=null;if(Ul.has(g.provider)){let N=Hl(String(w.text||""));N&&(O=N.text,E={questions:N.questions})}e.push({id:g.id,kind:"message",role:"assistant",text:O,ts:st(g),box:g.boxId,agent:g.provider,boxRole:w.boxRole,...E?{ask:E}:{},...v.length?{tools:v}:{},...w.model?{model:w.model}:{},...w.sourceId?{sourceId:w.sourceId}:{},..._.length?{images:_}:{}}),E&&(D={id:g.id,groupId:Wl(g.provider,w.sourceId||g.id,E.questions),provider:g.provider,box:g.boxId,questions:E.questions,ts:st(g)});break}case"job.started":{if(!w.id)break;let _=st(g),v=i.get(w.id);if(!v&&(w.kind||"agent")==="agent"&&w.name){let Z=l.get(w.name);Z&&Z.ts>=_&&(v=Z)}let O={jobId:w.id,jobKind:w.kind||"agent",name:w.name||null,detail:w.detail||null,status:v?v.status&&v.status!=="completed"?"failed":"done":"running",startedTs:_,finishedTs:v?.ts??null,summary:v?.summary??null,resultHead:v?.resultHead??null},E=ot.get(g.boxId),N=E?.jobs[E.jobs.length-1]?.startedTs;if(E&&N!=null&&_-N<=it)E.jobs.push(O);else{let Z={id:g.id,kind:"job-dock",dockId:w.id,ts:_,box:g.boxId,agent:g.provider,jobs:[O]};ot.set(g.boxId,Z),e.push(Z)}break}case"handoff.done":e.push(gn(g,"lead-handoff"));break;case"subtask.delegated":e.push({...gn(g,"subtask-delegated"),to:g.boxId,toAgent:g.provider});break;case"subtask.merged":e.push(gn(g,"subtask-merged"));break;case"pr.state.changed":{if(!w.change)break;if(w.change==="opened"){for(let v of S.values())v.status="stale";S.clear()}let _=gn(g,`pr-${w.change}`);if(O_.has(w.change)&&w.number!=null){let v=C_(h,y,w);if(h.size>=2){for(let E of y.values())for(let N of E)N.grouped=!0;_.groupPrs=[...h.values()];let O=[_];for(let E of h.keys())y.set(E,O)}else{let O=y.get(v)??[];O.push(_),y.set(v,O)}}else if(w.change==="merged"||w.change==="closed"||w.change==="lost"){let v=bg(w),O=b&&!b.complete||h.size>=2;if(h.has(v))h.delete(v),y.delete(v);else if(w.number!=null)for(let E of[...h.keys()])E.endsWith(`#${w.number}`)&&(h.delete(E),y.delete(E));if(O&&w.number!=null){(!b||b.complete)&&(b={id:g.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:st(g),ts:st(g),box:g.boxId,agent:g.provider},e.push(b)),b.prs.push({number:w.number,url:w.url??null,title:w.title??null,repo:w.repo??null,branch:w.branch??null,change:w.change,ts:st(g)}),b.remaining=[...h.values()],b.complete=h.size===0;break}}e.push(_);break}case"pr.offered":{if(!w.offerId)break;for(let v of S.values())v.status="stale";S.clear();let _={id:g.id,kind:"pr-offer",offerId:w.offerId,excerpt:w.excerpt||null,branch:w.branch||null,base:w.base||null,offered:Array.isArray(w.offered)&&w.offered.length?w.offered:null,toPr:w.toPr&&typeof w.toPr=="object"?{number:w.toPr.number??null}:null,repos:Array.isArray(w.repos)&&w.repos.length?w.repos:null,status:"open",ts:st(g),box:g.boxId,agent:g.provider};S.set(w.offerId,_),I.set(w.offerId,_),e.push(_);break}case"pr.offer.answered":{let _=I.get(w.offerId);_&&(_.status=w.action||"dismiss",_.actions=Array.isArray(w.actions)?w.actions:null,_.notify=w.notify===!0,S.delete(w.offerId));break}case"box.state.changed":{let _=w.change==="model"?"model-change":w.change;if(!_)break;let v=gn(g,_);_==="model-change"&&(v.to=w.model),e.push(v);break}case"view.state.changed":{if(w.change!=="created"||w.restarted)break;e.push({id:g.id,kind:"view-card",viewId:w.viewId,name:w.name,slug:w.slug,port:w.port,origin:w.origin,ts:st(g),box:g.boxId,agent:g.provider});break}case"view.spinup.failed":{e.push({id:g.id,kind:"view-spinup-failed",name:w.name||null,reason:w.reason||"no-view",ts:st(g),box:g.boxId,agent:g.provider});break}case"mock.state.changed":{if(w.change==="picked"){e.push({id:g.id,kind:"mock-picked",mockId:w.mockId,name:w.name,group:w.group??null,version:w.version??null,siblings:w.siblings??[],ts:st(g),box:g.boxId,agent:g.provider});break}if(w.change!=="created")break;e.push({id:g.id,kind:"mock-card",mockId:w.mockId,name:w.name,group:w.group??null,ts:st(g),box:g.boxId,agent:g.provider});break}case"flow.state.changed":{if(w.change!=="created")break;e.push({id:g.id,kind:"flow-card",flowId:w.flowId,name:w.name,stepCount:w.stepCount??null,mockIds:w.mockIds??[],ts:st(g),box:g.boxId,agent:g.provider});break}case"runbook.state.changed":{if(w.change!=="created")break;e.push({id:g.id,kind:"runbook-card",runbookId:w.runbookId,name:w.name,ts:st(g),box:g.boxId,agent:g.provider});break}case"ticket.linked":{e.push({id:g.id,kind:"ticket-card",provider:w.provider||"linear",key:w.key,url:w.url,title:w.title??null,linkSource:w.linkSource??null,ts:st(g),box:g.boxId,agent:g.provider});break}case"ticket.move.offered":{if(!w.offerId)break;for(let v of M.values())v.key===w.key&&(v.status="stale",M.delete(v.offerId));let _={id:g.id,kind:"ticket-offer",offerId:w.offerId,provider:w.provider||"linear",key:w.key,url:w.url??null,phase:w.phase??null,from:w.from??null,to:w.to?.name??null,prNumber:w.prNumber??null,status:"open",ts:st(g),box:g.boxId,agent:g.provider};M.set(w.offerId,_),z.set(w.offerId,_),e.push(_);break}case"ticket.move.answered":{let _=z.get(w.offerId);_&&(_.status=w.action==="moved"?"moved":"dismiss",M.delete(w.offerId));break}case"ticket.state.changed":{for(let _ of M.values())_.key===w.key&&_.to===w.to&&(_.status="stale",M.delete(_.offerId));if(w.mode==="offer")break;e.push(gn(g,"ticket-state"));break}case"ticket.commented":{e.push({id:g.id,kind:"ticket-comment",key:w.key,url:w.url??null,excerpt:w.excerpt??null,ts:st(g),box:g.boxId,agent:g.provider});break}case"ticket.flow.changed":e.push(gn(g,"ticket-flow"));break;case"ticket.create.offered":{if(!w.proposalId)break;for(let v of $.values())v.status="stale";$.clear();let _={id:g.id,kind:"ticket-create-offer",proposalId:w.proposalId,provider:w.provider||"linear",epic:w.epic??null,issues:Array.isArray(w.issues)?w.issues:[],team:w.team??null,needsTeam:!!w.needsTeam,linkCurrentTrack:!!w.linkCurrentTrack,status:"open",ts:st(g),box:g.boxId,agent:g.provider};$.set(w.proposalId,_),F.set(w.proposalId,_),e.push(_);break}case"ticket.create.answered":{let _=F.get(w.proposalId);_&&(_.status=w.action==="created"?"created":"dismiss",_.createdCount=w.createdCount??null,_.failedCount=w.failedCount??null,$.delete(w.proposalId));break}case"ticket.created":{let _={items:Array.isArray(w.items)?w.items:[],failed:Array.isArray(w.failed)?w.failed:[],team:w.team??null},v=w.proposalId?F.get(w.proposalId):null;if(v){v.created=_;break}e.push({id:g.id,kind:"ticket-created",..._,ts:st(g),box:g.boxId,agent:g.provider});break}case"error":{if(!w.kind)break;e.push(gn(g,w.kind));break}case"provider.connected":{e.push({id:g.id,kind:"event",type:"provider-connected",provider:w.provider||g.provider,ts:st(g),at:st(g),box:g.boxId,agent:g.provider});break}case"prompt.answered":{if(D=null,w.control||!w.question)break;let _=w.groupId||w.promptId;if(!_)break;let v=T.get(_);v||(v={id:g.id,kind:"prompt",groupId:_,provider:g.provider,box:g.boxId,agent:g.provider,questionTotal:w.questionTotal??1,questions:[],ts:st(g)},T.set(_,v));let O=w.questionIndex??0,E={index:O,header:w.header||"",question:w.question,multiSelect:!!w.multiSelect,chosen:Array.isArray(w.chosen)?w.chosen:[]},N=v.questions.findIndex(Z=>Z.index===O&&Tt(Z.question)===Tt(E.question));N>=0?v.questions[N]=E:v.questions.push(E),v.questions.sort((Z,wt)=>Z.index-wt.index),v.questionTotal=Math.max(v.questionTotal,w.questionTotal??1),v.questions.length>=v.questionTotal&&(e.push(v),T.delete(_));break}default:break}}return N_(e.sort((g,w)=>(g.ts??0)-(w.ts??0)))}var M_=2*6e4;function N_(t){let e=new Map,n=[];for(let r of t){if(r.kind!=="message"||!r.box){n.push(r);continue}let o=r.role==="assistant"&&!String(r.text||"").trim()&&r.images?.length,s=e.get(r.box);if(o&&s&&(r.ts??0)-s.lastTs<=M_){s.item.images.push(...r.images),s.lastTs=r.ts??s.lastTs;continue}if(o){let a={...r,images:[...r.images]};e.set(r.box,{item:a,lastTs:r.ts??0}),n.push(a)}else e.delete(r.box),n.push(r)}return n}function du(t,e=null){let n=[];for(let o of t||[]){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:P_(s),ts:s.ts??o.ts,n:s.n,box:o.boxId,agent:o.provider,boxRole:s.boxRole})}if(!n.length)return[];let r=(e??Zo(t)).filter(o=>o.kind==="message");return Ch(n,r)}function P_(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==Rr(t.command)?e:ln(t.command,t.description||"")}function gn(t,e){let{ts:n,change:r,...o}=t.payload||{};return{id:t.id,kind:"event",type:e,...o,at:st(t),ts:st(t)}}function B_(t){let e=[],n=null;for(let r of String(t||"").split(`
|
|
146
|
-
`)){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;e.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 e}var
|
|
147
|
-
`)}catch{}}function Ig(){let t={};for(let[e,n]of wu)t[e]={count:n.count,measured:n.measured,avgMs:n.measured?Math.round(n.totalMs/n.measured):null,maxMs:n.measured?n.maxMs:null};return{byChange:t,recent:Ya.slice(-20)}}async function tI(t){let e=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(t||"")?.[1];if(e)try{let[{stdout:n},{stdout:r}]=await Promise.all([Lr("git",["-C",t,"rev-parse","HEAD"]),Lr("git",["-C",t,"rev-parse","--abbrev-ref","HEAD"])]);await R({id:e,cwd:t},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}var Lr=Q_(Z_),eI={timeout:3e4,maxBuffer:8*1024*1024};async function Yt(t,e,n={}){let{caller:r="prs",...o}=n,s=await ae({cwd:t});Le(r);try{let{stdout:a}=await Lr("gh",e,{cwd:t,...eI,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw fn(a)&&pn(a),a}}var Qa=null;async function bu(){if(Qa&&Date.now()-Qa.at<6e4)return Qa.data;let t;try{Le("auth");let e=await Lr("gh",["auth","status"],{timeout:1e4});t={installed:!0,authenticated:!0,login:`${e.stdout||""}${e.stderr||""}`.match(/account\s+(\S+)/)?.[1]??null}}catch(e){t=e?.code==="ENOENT"||e?.code===127?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(t.installed&&!t.authenticated)try{let e=await uo();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return Qa={at:Date.now(),data:t},t}async function nI(t){try{let{stdout:e}=await Lr("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function $g(t){let e=t.name||t.context||"check",n=t.detailsUrl||t.targetUrl||null,r=t.completedAt||t.createdAt||null,o;return t.__typename==="StatusContext"?o=t.state==="SUCCESS"?"pass":t.state==="PENDING"||t.state==="EXPECTED"?"pending":"fail":t.status&&t.status!=="COMPLETED"?o="pending":["SUCCESS","NEUTRAL","SKIPPED"].includes(t.conclusion)?o="pass":o="fail",{name:e,state:o,url:n,completedAt:r}}function gu(t){let e=(t||[]).map($g),n=e.filter(s=>s.state==="pending").length,r=e.map(s=>Date.parse(s.completedAt||"")).filter(s=>s>0),o=e.length&&!n&&r.length?Math.max(...r):null;return{total:e.length,passed:e.filter(s=>s.state==="pass").length,failed:e.filter(s=>s.state==="fail").length,pending:n,failing:e.filter(s=>s.state==="fail").map(({name:s,url:a})=>({name:s,url:a})),settledAt:o}}var ku="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",Og=ku.replace("statusCheckRollup,","");function xu(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function Cg(t,e){if(!e)return null;let n=await et(t);if(!n)return null;try{let r=await Yt(t,["api",`repos/${n}/actions/runs?head_sha=${encodeURIComponent(e)}&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 Mg(t){return(t||[]).map(e=>({name:e.name,detailsUrl:e.html_url||null,status:String(e.status||"").toUpperCase(),conclusion:e.conclusion?String(e.conclusion).toUpperCase():null,completedAt:e.status==="completed"&&e.updated_at||null}))}async function Ng(t,e){let n=await Cg(t,e);return n===null?{...gu([]),unavailable:!0}:gu(Mg(n))}function ti(t,e=gu(t.statusCheckRollup)){return{number:t.number,title:t.title,url:t.url,author:t.author?.login??null,headRefName:t.headRefName,headSha:t.headRefOid??null,baseRefName:t.baseRefName,isDraft:!!t.isDraft,reviewDecision:t.reviewDecision||null,mergeable:t.mergeable||"UNKNOWN",mergeStateStatus:t.mergeStateStatus||"UNKNOWN",additions:t.additions??0,deletions:t.deletions??0,createdAt:t.createdAt,updatedAt:t.updatedAt,checks:e,green:e.failed===0&&e.pending===0&&t.mergeStateStatus==="CLEAN",autoMergeArmed:!!t.autoMergeRequest,mergeabilityPending:!1}}var rI=9e4,jr=new Map;function oI(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){jr.delete(s);continue}r.add(s),jr.has(s)||jr.set(s,n),o.mergeabilityPending=n-jr.get(s)<rI}for(let o of jr.keys())o.startsWith(`${t}#`)&&!r.has(o)&&jr.delete(o);return e}async function Fr(t,e){let n=await Fe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await Yt(n.path,["pr","view",String(r),"--json",ku]);return ti(JSON.parse(o))}catch(o){if(!xu(o))throw o;let s=await Yt(n.path,["pr","view",String(r),"--json",Og]),a=JSON.parse(s);return ti(a,await Ng(n.path,a.headRefOid))}}var Rg=(t,e)=>t.number-e.number;async function ts(t,{caller:e="prs"}={}){try{let n=await Yt(t,["pr","list","--json",ku,"--limit","50"],{caller:e});return JSON.parse(n||"[]").map(r=>ti(r)).sort(Rg)}catch(n){if(!xu(n))throw n;let r=await Yt(t,["pr","list","--json",Og,"--limit","50"],{caller:e});return(await Promise.all(JSON.parse(r||"[]").map(async o=>ti(o,await Ng(t,o.headRefOid))))).sort(Rg)}}var sI=3e4,aI=1500,Qo=new Map;function ke(){Qo.clear()}async function iI(t,e){if(!t)return e;for(let n of e){let r=await Ot(t,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 Ra(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Oa(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function Pg(){let t=await bu();if(!t.authenticated)return{auth:t,repos:[]};let e=[];for(let a of await ve())await nI(a.path)&&e.push(a);let n=Date.now(),r=Ka(n)>0,o=await Promise.all(e.map(async a=>{let i=Qo.get(a.path),c=i?.pending?aI:sI;if(i&&(r||n-i.at<c))return i.entry;try{let l=await et(a.path).catch(()=>null),u=oI(a.path,await ts(a.path,{caller:"tab"}),n);if(await iI(l,u),i?.entry?.prs)for(let p of u)i.entry.prs.find(h=>h.number===p.number)?.checks?.pending>0&&p.checks.total>0&&p.checks.pending===0&&Za({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 Qo.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:be(l)}}})),s=new Set(e.map(a=>a.path));for(let a of Qo.keys())s.has(a)||Qo.delete(a);return{auth:t,repos:o}}async function Fe(t){let e=(await ve()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function Yn(t){return co(t)}var es=new Set(["squash","merge","rebase"]);async function Ur(t,e,{method:n="squash"}={}){let r=await Fe(t);if(!es.has(n))throw new Error(`invalid merge method: ${n}`);let o=Number(e);if(!Number.isInteger(o)||o<=0)throw new Error("invalid PR number");let s=["pr","merge",String(o),`--${n}`];if(n!=="rebase"&&(await ae({cwd:r.path})).GH_TOKEN){let a=await qi().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await Yt(r.path,s)}catch(a){throw new Error(be(a))}finally{ke()}return{ok:!0,number:o}}async function ns(t,e){let n=await Fe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Yt(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(be(s))}finally{ke()}let o=await et(n.path).catch(()=>null);return o&&await fh(o,r).catch(()=>{}),{ok:!0,number:r}}async function Su(t,e){let n=await Fe(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await Yt(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(be(o))}finally{ke()}return{ok:!0,number:r}}var cI=2500,lI=9e4,uI=t=>new Promise(e=>setTimeout(e,t));function vu(t){return t.green&&!t.isDraft}function dI(t){return t.isDraft?"draft":t.mergeable==="CONFLICTING"?`conflicts with ${t.baseRefName}`:t.checks.failed>0?"checks failing":t.checks.pending>0?"checks running":t.mergeable==="UNKNOWN"||t.mergeStateStatus==="UNKNOWN"?"mergeability unresolved":t.mergeStateStatus==="BEHIND"?`behind ${t.baseRefName}`:t.mergeStateStatus==="BLOCKED"?"blocked by branch protection":`not clean (${t.mergeStateStatus})`}function Bg(t,{update:e=!0}={}){return t.isDraft?{action:"skip",reason:"draft"}:vu(t)?{action:"merge"}:e&&t.mergeStateStatus==="BEHIND"&&t.mergeable!=="CONFLICTING"&&t.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function Dg(t){return[...t.filter(e=>e.plan.action!=="merge"),...t.filter(e=>e.plan.action==="merge")]}var yu=new Map;function jg(){let t={};for(let[e,n]of yu)t[e]={...n};return t}async function Lg(t,{method:e="merge",onMerged:n=null}={}){let r=await Fe(t);if(!es.has(e))throw new Error(`invalid merge method: ${e}`);if(yu.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await ts(r.path)).filter(vu).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:e,total:o.length,current:o[0].number,queue:o.map(a=>({number:a.number,title:a.title})),merged:[],skipped:[],failed:[]};return yu.set(r.path,s),pI(s,o,{listPrs:()=>ts(r.path),merge:a=>Ur(r.path,a,{method:e}),sleep:uI,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function fI(t,e){let n=Date.now()+(e.settleMaxMs??lI),r=null;for(;;){let o=null;try{if(o=await e.listPrs(),r=null,!o.some(a=>t.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 e.sleep(cI)}}async function pI(t,e,n){try{let r=e;for(let o=0;o<e.length;o++){let s=e[o];if(t.current=s.number,o>0)try{r=await fI(e.slice(o).map(i=>i.number),n)}catch(i){for(let c of e.slice(o))t.failed.push({number:c.number,title:c.title,reason:`couldn't re-check PR state: ${be(i)}`});break}let a=r.find(i=>i.number===s.number);if(!a){t.skipped.push({number:s.number,title:s.title,reason:"no longer open"});continue}if(!vu(a)){t.skipped.push({number:a.number,title:a.title,reason:dI(a)});continue}try{await n.merge(a.number),t.merged.push({number:a.number,title:a.title});try{await n.onMerged?.(a)}catch{}}catch(i){t.failed.push({number:a.number,title:a.title,reason:be(i)})}}}finally{t.current=null,t.running=!1,t.finishedAt=Date.now(),ke()}}async function Tu(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await Lr("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${be(l)}`)}await tI(t);let o=n||"";if((await ae({cwd:t})).GH_TOKEN){let l=await qi().catch(()=>null);l&&(o=`${o?`${o}
|
|
148
|
-
|
|
149
|
-
`:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",e.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await
|
|
138
|
+
`;import{execFile as E_}from"node:child_process";import{promisify as __}from"node:util";var pu=__(E_),Gw=64*1024*1024;async function yt(t,e,{input:n}={}){let r=pu("git",["-C",t,...e],{maxBuffer:Gw});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Ya(t){if(!t)return!1;try{let{stdout:e}=await yt(t,["rev-parse","--is-inside-work-tree"]);return e.trim()==="true"}catch{return!1}}async function Vw(t){let{stdout:e}=await yt(t,["rev-parse","--show-toplevel"]);return e.trim()}async function I_(t){try{return await yt(t,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function ss(t,e="bostrat: subtask snapshot"){let{stdout:n}=await yt(t,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>pu("git",["-C",t,...a],{maxBuffer:Gw,env:o,...i});try{let a=await I_(t);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",e]:["commit-tree",c,"-m",e],{stdout:u}=await s(l);return u.trim()}finally{try{await pu("rm",["-f",r])}catch{}}}async function mu(t,e){let{stdout:n}=await yt(t,["status","--porcelain"]);return n.trim()?(await yt(t,["add","-A"]),await yt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",e]),!0):!1}async function hu(t,{ignorePlumbing:e=!1}={}){let{stdout:n}=await yt(t,["status","--porcelain"]),r=n.split(`
|
|
139
|
+
`).filter(Boolean);return e&&(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 wu(t,e){if(!e)return 0;try{let{stdout:n}=await yt(t,["rev-list","--count",`${e}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function Wr(t,e,n,r){return await yt(t,["worktree","add","--no-track","-b",n,e,r]),e}async function zw(t,e){try{return await yt(t,["rev-parse","--verify","--quiet",`refs/heads/${e}`]),!0}catch{return!1}}async function Jw(t,e,n){return await yt(t,["worktree","add",e,n]),e}async function Za(t){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:e}=await yt(t,["symbolic-ref","--short","refs/remotes/origin/HEAD"]);if(e.trim())return e.trim()}catch{}for(let[e,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 yt(t,["rev-parse","--verify","--quiet",e]),n}catch{}return"HEAD"}async function Xw(t){await yt(t,["init","-q"]),await yt(t,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function ee(t,e){try{await yt(t,["worktree","remove","--force",e])}catch{try{await yt(t,["worktree","prune"])}catch{}}}function R_(t){let e=/(\d+) files? changed/.exec(t),n=/(\d+) insertions?\(\+\)/.exec(t),r=/(\d+) deletions?\(-\)/.exec(t);return{files:e?Number(e[1]):0,insertions:n?Number(n[1]):0,deletions:r?Number(r[1]):0}}function gu(t,e){return!e||!e.length?t:[...t,"--",".",...e.map(n=>`:(exclude)${n}`)]}async function yu(t,e,n,r){let{stdout:o}=await yt(t,gu(["diff","--shortstat",e,n],r));return R_(o)}async function bu(t,e,n,r){let{stdout:o}=await yt(t,gu(["diff","--name-only",e,n],r));return o.split(`
|
|
140
|
+
`).map(s=>s.trim()).filter(Boolean)}async function ku(t,e,n,r){let{stdout:o}=await yt(t,gu(["diff","--binary",e,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await fu(t,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await fu(t,o,["--3way","--whitespace=nowarn"]),a=await $_(t);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await fu(t,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function fu(t,e,n){try{return await yt(t,["apply",...n,"-"],{input:e}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function $_(t){try{let{stdout:e}=await yt(t,["diff","--name-only","--diff-filter=U"]);return e.split(`
|
|
141
|
+
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}import xu from"node:fs/promises";import Zw from"node:path";import O_ from"node:os";var Qw=process.env.BOSTRAT_STATE_DIR||Zw.join(process.env.HOME||O_.tmpdir(),".bostrat"),tg=()=>Zw.join(Qw,"usage-caps.json"),eg=.8;function ng(t){let e=new Date(t);return e.setHours(0,0,0,0),e.setDate(e.getDate()-(e.getDay()+6)%7),e.getTime()}function rg(t){let e=new Date(t);return e.setHours(0,0,0,0),e.setDate(1),e.getTime()}function og(t){let e=new Date(ng(t));return e.setDate(e.getDate()+7),e.getTime()}function sg(t){let e=new Date(rg(t));return e.setMonth(e.getMonth()+1),e.getTime()}var qr={weekly:{startOf:ng,nextStart:og,capKey:"weeklyUsd"},monthly:{startOf:rg,nextStart:sg,capKey:"monthlyUsd"}},ie=null,vu=!1,qe={weekly:0,monthly:0},rr=new Map,Kr=new Set,Su=null,M_=t=>{let e=rr.get(t);return e||(e={weekly:0,monthly:0},rr.set(t,e)),e};function ti(t=Date.now()){for(let[e,n]of Object.entries(qr)){let r=n.startOf(t);if(r!==qe[e]){qe[e]=r;for(let o of rr.values())o[e]=0;for(let o of Kr)o.includes(`:${e}:`)&&!o.endsWith(`:${r}`)&&Kr.delete(o)}}}function ag(t,e){let n=ie?.caps?.[t]?.[qr[e].capKey];return Number.isFinite(n)&&n>0?n:null}function as(t,e=Date.now()){if(!t||!vu||!ie?.caps)return null;ti(e);let n=rr.get(t);for(let r of Object.keys(qr)){let o=ag(t,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:t,window:r,limitUsd:o,spentUsd:Qa(s),resetsAt:qr[r].nextStart(e)}}return null}function ig(t,e){let n=`hit:${t}:${e.provider}:${e.window}:${qe[e.window]}`;return Kr.has(n)?!1:(Kr.add(n),!0)}function Yw(t,e,n,r=Date.now()){if(!t||!e)return;ti(r);let o=M_(t);for(let[s,a]of Object.entries(qr))n>=qe[s]&&n<a.nextStart(r)&&(o[s]+=e)}async function C_(t,e,n=Date.now()){if(!ie?.caps||!Su)return;let r=rr.get(t);if(r)for(let[o,s]of Object.entries(qr)){let a=ag(t,o);if(a==null)continue;let i=r[o]||0;if(i<a*eg||i>=a)continue;let c=`warn:${t}:${o}:${qe[o]}`;if(!Kr.has(c)){Kr.add(c);try{let l=await Su(e);if(!l)continue;await I(l,{type:"usage.cap.warning",actor:"system",provider:t,payload:{provider:t,window:o,limitUsd:a,spentUsd:Qa(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function cg(t){let e={};for(let[n,r]of Object.entries(t||{})){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&&(e[n]=o)}return e}async function ei(t,{source:e="local",updatedAt:n=Date.now()}={}){ie={caps:cg(t),updatedAt:n,source:e};try{await xu.mkdir(Qw,{recursive:!0}),await xu.writeFile(tg(),JSON.stringify(ie,null,2))}catch{}return is()}function is(t=Date.now()){ti(t);let e=new Set([...rr.keys(),...Object.keys(ie?.caps||{})]),n={};for(let r of e){let o=rr.get(r);n[r]={weeklyUsd:Qa(o?.weekly||0),monthlyUsd:Qa(o?.monthly||0)}}return{caps:ie?.caps||{},configured:!!ie,seeded:vu,updatedAt:ie?.updatedAt??null,spent:n,windows:{weekly:{start:qe.weekly,resetsAt:og(t)},monthly:{start:qe.monthly,resetsAt:sg(t)}},warnPct:eg}}async function lg({listTracks:t,getTrack:e}={}){Su=e||null,ti();try{let o=JSON.parse(await xu.readFile(tg(),"utf8"));ie={caps:cg(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{ie=null}let n=Date.now();Ut.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=Ln(o);Yw(o.provider||"unknown",s,o.ts??Date.now()),C_(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(qe.weekly,qe.monthly);for(let o of await t?.()||[]){let s;try{s=await L(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||Yw(a.provider||"unknown",Ln(a).usd,i,n)}}return vu=!0,is()}var Qa=t=>Math.round(t*1e4)/1e4;import N_ from"node:fs/promises";import P_ from"node:os";import ug from"node:path";var ni=t=>new Promise(e=>setTimeout(e,t)),D_=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,B_=300,mg=[{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"}],j_=new Set([...mg.map(t=>t.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"]),L_=[{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"}],hg=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),wg=[{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 gg(t){return t==="codex"?L_:t==="cursor"?wg:mg}function dg(t){let e=String(t||"").split(`
|
|
142
|
+
`),n=e.findIndex(o=>/Select Model and Effort/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<e.length;o++){let s=e[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(e[o]))break}return r.length?r:null}function fg(t){let e=String(t||"").split(`
|
|
143
|
+
`),n=e.findIndex(o=>/Select Reasoning Level/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<e.length;o++){let s=e[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(e[o]))break}return r.length?r:null}function F_(t){let e=String(t||"").split(`
|
|
144
|
+
`);for(let n=e.length-1;n>=0;n--){let r=e[n].match(/Model changed to\s+(\S+)(?:\s+(\S+))?\s*$/i);if(r)return{model:r[1],effort:r[2]||null}}return null}function pg(t){let e=String(t||"").split(`
|
|
145
|
+
`),n=e.findIndex(s=>/Available models|Models matching/i.test(s));if(n<0)return null;let r=/Models matching/i.test(e[n]),o=[];for(let s=n+1;s<e.length;s++){let a=e[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 yg(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim();if(t==="codex"){let[r,o]=n.split(/[\s:]+/);if(o&&!hg.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(t==="cursor"){let r=wg.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!j_.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function cs(t,e,{timeoutMs:n=D_}={}){let r=Date.now()+n;for(;;){let o=await mt(t,{lines:80}).catch(()=>""),s=e(o);if(s)return s;if(Date.now()>r)return null;await ni(B_)}}async function U_(t,e){return await _t(t,`/model ${e.model}`,Ce("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function H_(t,e){await ia(t),await _t(t,"/model",{enter:!1}),await ni(250),await vt(t,["Enter"],{kind:"model-switch"});let n=await cs(t,dg);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===e.model);if(!r)throw await vt(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${e.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=F_(i);return!c||c.model!==e.model||e.effort&&c.effort&&c.effort.toLowerCase()!==e.effort.toLowerCase()?null:c};await _t(t,String(r.n),{enter:!1,kind:"model-switch"});let s=await cs(t,i=>fg(i)||!dg(i)&&o(i)&&{done:o(i)});if(!s)throw await vt(t,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=e.effort?hg.get(e.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await vt(t,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${e.effort}" for ${e.model}`);await _t(t,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await cs(t,i=>!fg(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 W_(){let t=ug.join(process.env.CURSOR_CONFIG_DIR||ug.join(P_.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await N_.readFile(t,"utf8"))?.model||null}catch{return null}}async function q_(t,e){await ia(t),await la(t,`/model ${e.filter}`,{kind:"model-switch"});let n=await cs(t,pg),r=c=>String(c||"").toLowerCase(),o=c=>r(c.label).startsWith(r(e.filter)),s=n?.rows.find(c=>c.active)||null;if(n&&!(s&&o(s)))for(let c=0;c<12;c++){let l=await cs(t,pg,{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 vt(t,["Down"],{kind:"model-switch"}),await ni(250)}if(!n||!s||!o(s)){await vt(t,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${e.filter}`.length).fill("BSpace");throw await vt(t,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${e.filter}"`:"cursor model picker didn't open")}await vt(t,["Enter"],{kind:"model-switch"}),await ni(800);let a=await W_();return{verified:!!a&&(a.modelId===e.model||a.displayModelId===e.model||r(a.displayName).startsWith(r(e.filter))),model:a?.modelId||null,note:"cursor applies this as the global default for new sessions too"}}async function bg(t,e,n){return t==="codex"?H_(e,n):t==="cursor"?q_(e,n):U_(e,n)}var Yt=t=>t||null;function oi(t){let e=t||{},n=K_(e);return n&&(n.payload={ts:e.at,...n.payload}),n}function K_(t){switch(t.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:t.box,provider:Yt(t.agent),payload:{text:t.text,boxRole:t.boxRole,...t.kind?{kind:t.kind}:{},...t.attachments?.length?{attachments:t.attachments}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:t.box,provider:Yt(t.agent),payload:{saidTs:t.saidTs,text:t.text,boxRole:t.boxRole}};case"orchestration-prompt":return{type:"turn.user",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Yt(t.agent),payload:{text:t.prompt,kind:t.kind,boxRole:t.boxRole}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:t.to,provider:Yt(t.toAgent),payload:{from:t.from,fromAgent:t.fromAgent,to:t.to,toAgent:t.toAgent,reason:t.reason,handoffStartedAt:t.handoffStartedAt,fromSessionId:t.fromSessionId,toSessionId:t.toSessionId,epoch:t.epoch,singleBox:t.singleBox,forced:t.forced,recovered:t.recovered}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Yt(t.toAgent),payload:{box:t.box,fromAgent:t.fromAgent,toAgent:t.toAgent,reason:t.reason,error:t.error,rolledBack:t.rolledBack,handoffStartedAt:t.handoffStartedAt}};case"subtask-delegated":return{type:"subtask.delegated",actor:"orchestrator",source:"orchestrator",boxId:t.to,provider:Yt(t.toAgent),payload:{task:t.task,subtaskId:t.subtaskId,reason:t.reason}};case"subtask-merged":return{type:"subtask.merged",actor:"orchestrator",source:"orchestrator",boxId:t.box||t.from,provider:Yt(t.agent),payload:{...Gr(t)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box||t.to,provider:Yt(t.agent||t.toAgent),payload:{kind:"subtask-failed",...Gr(t)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Yt(t.agent),payload:{change:"subtask-resumed",...Gr(t)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:t.box,payload:{change:"model",model:t.to,from:t.from??null,verified:t.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:"codex",payload:{change:"cli-update",from:t.from??null,to:t.to??null,status:t.status,...t.error?{error:t.error}:{}}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:t.box,payload:{change:"opened",...Gr(t)}};case"archived":case"unarchived":case"track-deleted":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:t.type,...Gr(t)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box,provider:Yt(t.agent),payload:{change:"rehydrated"}};case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...Gr(t)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:t.box,provider:Yt(t.agent),payload:{kind:"rehydrate-failed",reason:t.reason}};default:return null}}var ri=Promise.resolve(),Au=t=>(ri=ri.then(t).catch(()=>{}),ri),xg=()=>ri;function Sg(t,e){let n=oi(e);if(!n)return;let r=e?.type==="track-deleted"?{id:t.id}:t;Au(()=>I(r,n))}var kg=new Set;async function vg(t){if(!(!t?.id||kg.has(t.id))){kg.add(t.id);try{let e=await L(t),n=new Set(e.filter(r=>r.source==="orchestrator").map(r=>`${r.type}|${r.payload?.ts??"?"}`));for(let r of t.events||[]){let o=oi(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await I(t,o)}}catch{}}}function Gr(t){let{type:e,at:n,...r}=t;return r}var Tu=new Map;function Tg(t,e,n){!n?.promptId||Tu.get(t.id)===n.promptId||(Tu.set(t.id,n.promptId),Au(()=>I(t,{type:"prompt.raised",actor:"agent",source:"pane",boxId:e?.name,provider:Yt(n.provider||e?.agent),payload:{promptId:n.promptId,question:n.question||n.title||null,options:(n.options||[]).map(r=>r?.label??r).slice(0,12)}})))}function Eu(t,e,n,r={}){Tu.delete(t.id),Au(()=>I(t,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:e?.name,provider:Yt(n?.provider||e?.agent),payload:{promptId:n?.promptId,...r}}))}import{randomUUID as vI}from"node:crypto";var Eg=/(?<!\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,_g=/(?<!\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,Ig=/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,Rg=/(?<!\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,G_=new RegExp(`\\b(?:${Eg}|${_g}|${Ig}|${Rg})\\b`,"i"),V_=/\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,z_=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,J_=90;function X_(t){return String(t).split(/(?<=[.?!])\s+|\n+/).map(e=>e.trim()).filter(Boolean)}function $g(t){let e=String(t||"").trim();if(!e)return null;let n=e.slice(-600);for(let r of X_(n).reverse()){if(!G_.test(r))continue;let o=r.endsWith("?")&&r.length<=J_&&z_.test(r);if(V_.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}var Og=["commit","rebase","push","pr"],Y_=new RegExp(`\\b(?:${Eg})\\b`,"i"),Z_=new RegExp(`\\b(?:${Rg})\\b`,"i"),Q_=new RegExp(`\\b(?:${_g})\\b`,"i"),tI=new RegExp(`\\b(?:${Ig})\\b`,"i"),eI=/\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;function Mg(t){let e=String(t||""),n=[];Y_.test(e)&&n.push("commit"),Z_.test(e)&&n.push("rebase"),Q_.test(e)&&n.push("push");let r=eI.test(e);!r&&tI.test(e)&&n.push("pr");let o=null;if(r){let s=e.match(/(?:pr|pull\s+request)\s*\(?\s*#(\d+)\)?/i)||e.match(/#(\d+)/);o={number:s?Number(s[1]):null}}return{offered:n.length?n:null,toPr:o}}function si(t,{branch:e=null,base:n=null,toPr:r=null,repos:o=null,offeredRebase:s=!1}={}){Array.isArray(t)||(t=t==="push"?["commit","push"]:["commit","push","pr"]);let a=new Set(t),i=Array.isArray(o)&&o.length>0,c=i?o.map(p=>`${p.name}/`).join(", "):null,l=e?` on this branch (${e})`:"",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${e?` (${e})`:""}`;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${e?` (${e})`:""} 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}var nI=3e4;function Cg(t,e=Date.now()){let n=[],r=null;for(let o of t||[]){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}!r||a==null||a<r.from||(o.type==="pr.offer.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&&e-r.lastActivity<=nI&&n.push({box:r.box,from:r.from,to:e+1e12}),n}function Ng(t,e){let n=new Set;for(let r of e||[]){if(!r.settled)continue;let o=null;for(let s of t||[])s.kind!=="message"||s.role!=="assistant"||s.box!==r.box||s.ts==null||s.ts<r.from||s.ts>r.to||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}var Ag={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"};function Pg(t){let e=(Array.isArray(t)?t:[]).filter(r=>Ag[r]).map(r=>Ag[r]);return`The ship you were given the go-ahead for${e.length?` (${e.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.`}import{execFile as iI}from"node:child_process";import{promisify as cI}from"node:util";import rI from"node:fs";import _u from"node:fs/promises";import Lg from"node:path";import oI from"node:os";var Fg=process.env.BOSTRAT_STATE_DIR||Lg.join(process.env.HOME||oI.tmpdir(),".bostrat"),ai=Lg.join(Fg,"github-metrics.json"),Ug=60*6e4,Dg=5*6e4,sI=6e4,Vr=Date.now(),Ke=new Map,bn=null,or=0,Bg=0,ii=t=>[...t.values()].reduce((e,n)=>e+n,0);try{let t=JSON.parse(rI.readFileSync(ai,"utf8"));t?.windowStart&&(Date.now()-t.windowStart<Ug?(Vr=t.windowStart,Ke=new Map(Object.entries(t.byCaller||{})),bn=t.lastHour||null):ii(new Map(Object.entries(t.byCaller||{})))&&(bn={start:t.windowStart,total:ii(new Map(Object.entries(t.byCaller||{}))),byCaller:t.byCaller||{}}))}catch{}var jg=Promise.resolve();function Hg(t,e=!1){if(!e&&t-Bg<sI)return;Bg=t;let n=JSON.stringify({windowStart:Vr,byCaller:Object.fromEntries(Ke),lastHour:bn});jg=jg.then(()=>_u.mkdir(Fg,{recursive:!0})).then(()=>_u.writeFile(`${ai}.tmp`,n)).then(()=>_u.rename(`${ai}.tmp`,ai)).catch(()=>{})}function Wg(t){t-Vr<Ug||(bn={start:Vr,total:ii(Ke),byCaller:Object.fromEntries(Ke)},bn.total&&console.log(`[github] ${bn.total} calls last hour`,bn.byCaller),Vr=t,Ke=new Map,Hg(t,!0))}function Ge(t="other",e=Date.now()){Wg(e),Ke.set(t,(Ke.get(t)||0)+1),Hg(e)}function qg(t=Date.now()){return Wg(t),{windowStart:Vr,total:ii(Ke),byCaller:Object.fromEntries(Ke),lastHour:bn,backoffUntil:or>t?or:null}}var aI=/rate limit|too quickly|abuse detection|HTTP 429/i;function kn(t){return aI.test(String(t?.message||t||""))}function xn(t,e=Date.now()){return or<=e&&console.warn(`[github] rate limited \u2014 backing off ${Dg/6e4} min:`,String(t?.message||t||"").slice(0,200)),or=Math.max(or,e+Dg),or}function ci(t=Date.now()){return Math.max(0,or-t)}var Kg=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,ls=new Map;function li(t,e,n){let{state:r,reason:o}=Fc(e,n);if(r!=="unknown"){if(t){let a=Date.now();for(let[i,c]of ls)a-c.at>=Kg()&&ls.delete(i);ls.set(t,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!t)return!1;let s=ls.get(t);return s?Date.now()-s.at>=Kg()?(ls.delete(t),!1):s.busy:!1}async function Iu(t,e){let n={working:!1,prompted:!1};if(!t)return n;let r=await mt(t,{lines:Et}).catch(()=>"");if(!li(t,e,r))return Ip(e,r)?{working:!0,prompted:!1}:n;if(Ur(e,r)){let o=await an(t).catch(()=>r);if(nr({name:t,agent:e},o))return{working:!1,prompted:!0}}return{working:!0,prompted:!1}}async function ui(t,e){return(await Iu(t,e)).working}var Gg=cI(iI),Vg=[4e3,15e3,45e3],lI=2500,uI=5*6e4;async function Ru(t,e,{midTurn:n=ui,pollMs:r=lI,capMs:o=uI}={}){let s=Date.now()+o;for(;await n(t,e).catch(()=>!1);){if(Date.now()>=s)return!1;await new Promise(a=>setTimeout(a,r))}return!0}var dI=6e4,fI=9e4,pI=2e3,mI=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+create\b/,hI=/(?:^|[;&|]\s*|\)\s*)gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+merge\b/,wI=/\s--web\b/,zg=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/;function $u(t,e){return t.slice(e).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|]/)[0]}function Jg(t){let e=String(t||""),n=e.match(mI);if(!n)return null;let r=$u(e,n.index);return wI.test(r)?null:{repo:r.match(zg)?.[1]??null}}var gI=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);function Xg(t){let e=String(t||""),n=e.match(hI);if(!n)return null;let r=$u(e,n.index),o=$u(e,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("-")){gI.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(zg)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}var yI="number,url,title,headRefName,createdAt";async function bI(t,e){let n=async a=>{Ge("observer");let{stdout:i}=await Gg("gh",["pr","list","--repo",t,"--author","@me","--state","all","--limit","10","--json",yI],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...a}}).catch(c=>{throw kn(c)&&xn(c),c});return JSON.parse(i||"[]")},r=a=>(a||[]).filter(i=>Date.parse(i.createdAt)>=e-dI),o=await de({slug:t}),s=r(await n(o));return!s.length&&o.GH_TOKEN&&(s=r(await n({}).catch(()=>[]))),s.sort((a,i)=>Date.parse(i.createdAt)-Date.parse(a.createdAt))}async function kI(t,e){if(e)return[e];let n=[];for(let r of Yg(t)){let o=await ot(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await Re().catch(()=>[])){let o=await ot(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function Yg(t){let e=t.workspace;return e?.kind==="multi"?(e.repos??[]).map(n=>n?.base).filter(Boolean):[e?.base||t.cwd].filter(Boolean)}async function xI(t,e,n){let r=t.workspace?.branch,o=[];for(let s of await kI(t,e)){let a;try{a=await bI(s,n)}catch{continue}for(let i of a){let c=await Bt(s,i.number);if(!(c&&(c.trackId!==t.id||c.announced))){if(r&&i.headRefName===r)return{slug:s,pr:i};o.push({slug:s,pr:i})}}}return o.length?(o.sort((s,a)=>Date.parse(a.pr.createdAt)-Date.parse(s.pr.createdAt)),o[0]):null}var Sn=new Set;function Zg(t,e,{delays:n=Vg,turnEnd:r}={}){let o=Jg(e.command);if(!o)return null;let s=!!Xg(e.command),a=`${t.id}|${e.id??e.ts??e.command}`;if(Sn.has(a))return null;Sn.add(a);let i=e.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(w=>setTimeout(w,l));let u=await xI(t,o.repo,i).catch(()=>null);if(!u)continue;let{slug:f,pr:d}=u,{link:p,created:m}=await Xt({repo:f,number:d.number,trackId:t.id,url:d.url,headRef:d.headRefName,title:d.title,source:"observed"});return!m&&!(p?.trackId===t.id&&await zo(f,d.number))?null:(await Ru(e.box,e.agent,r),await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.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{Sn.delete(a)}})();return c.catch(()=>Sn.delete(a)),c}var SI="number,url,title,headRefName,state";function Qg(t,e,{delays:n=Vg,turnEnd:r,openedWait:o}={}){let s=Xg(e.command);if(!s)return null;let a=`merge|${t.id}|${e.id??e.ts??e.command}`;if(Sn.has(a))return null;Sn.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=Yg(t);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of m){let y=await ot(w).catch(()=>null);y&&!f.includes(y)&&f.push(y)}}if(l||=t.workspace?.branch,!f.length||!l)return null;c=f[0];let d=async(m,w)=>{let y=await Bt(c,m.number);return y&&y.trackId!==t.id?null:(y||await Xt({repo:c,number:m.number,trackId:t.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await Jo(c,m.number),w&&((await L(t).catch(()=>[])).some(_=>_.type==="pr.state.changed"&&(_.payload?.change==="auto-armed"||_.payload?.change==="merged")&&_.payload?.number===m.number)||(await Ru(e.box,e.agent,r),await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.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(R=>setTimeout(R,m));let w=[];for(let R of f)try{Ge("observer");let{stdout:C}=await Gg("gh",["pr","view",String(l),"--repo",R,"--json",SI],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await de({slug:R})}}),v=JSON.parse(C||"null");v?.number&&w.push({slug:R,pr:v})}catch(C){kn(C)&&xn(C)}let y=w.find(R=>R.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 S=await Bt(c,b.number);if(S&&S.trackId!==t.id)return null;S||await Xt({repo:c,number:b.number,trackId:t.id,url:b.url,headRef:b.headRefName,title:b.title,source:"merge-observed"}),await Ru(e.box,e.agent,r);let _=await L(t).catch(()=>[]);if(_.some(R=>R.type==="pr.state.changed"&&R.payload?.change==="merged"&&R.payload?.number===b.number))return null;let O=R=>R.some(C=>C.type==="pr.state.changed"&&C.payload?.change==="opened"&&C.payload?.number===b.number),nt=0;if(!O(_)){if(Jg(e.command)&&await zo(c,b.number)){let R=Date.now();nt=R+1,await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:R,change:"opened",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c,selfMerge:!0}})}else if((await Bt(c,b.number))?.announced){let R=o?.capMs??fI,C=o?.pollMs??pI,v=Date.now()+R;for(;Date.now()<v&&(await new Promise(D=>setTimeout(D,C)),!O(await L(t).catch(()=>[]))););}}return await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:e.box??null,provider:e.agent??null,payload:{ts:Math.max(Date.now(),nt),change:"merged",number:b.number,url:b.url,title:b.title,branch:b.headRefName,repo:c}}),await Tn(c,b.number).catch(()=>{}),await vn(c,b.number).catch(()=>{}),await wn(c,b.number,"merged"),b}return p?d(p,!1):null}finally{Sn.delete(a)}})();return i.catch(()=>Sn.delete(a)),i}function Mu(t){return t.sourceId?`${t.role}|id:${t.sourceId}`:`${t.role}|${t.ts??"?"}|${String(t.text||"").slice(0,40)}`}function ty(t){return t.id?`cmd|${t.box}|id:${t.id}`:`cmd|${t.box}|${t.ts??"?"}|${String(t.command||"").slice(0,40)}`}function ey(t){return`job|${t.box}|id:${t.id}`}var Ou=new Map,di=new Map;async function Cu(t){return Ou.has(t.id)?Ou.get(t.id):(di.has(t.id)||di.set(t.id,L(t).then(e=>{let n={keys:new Set,cmdN:0};for(let r of e)if((r.type==="turn.user"||r.type==="turn.agent")&&r.payload?.text!=null){let o=r.type==="turn.user"?"user":"assistant";r.payload.sourceId&&n.keys.add(Mu({role:o,sourceId:r.payload.sourceId})),n.keys.add(Mu({role:o,ts:r.payload.ts,text:r.payload.text}))}else if(r.type==="tool.called"&&r.source==="transcript"&&r.payload?.command){let o=r.payload;n.keys.add(ty({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(ey({id:r.payload.id,box:r.boxId}));return Ou.set(t.id,n),n}).finally(()=>di.delete(t.id))),di.get(t.id))}async function ny(t,e){try{let n=(await Cu(t)).keys;for(let r of e||[]){if(!r||r.ts==null||!String(r.text||"").trim()&&!r.images?.length)continue;let o=Mu(r);if(!n.has(o)&&(n.add(o),await I(t,{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===t.leadBox&&t.workspace?.branch)){let s=$g(r.text);if(s){let{offered:a,toPr:i}=Mg(s),c;if(t.workspace?.kind==="multi"){let l=await us(t).catch(()=>[]);c=l.length?l.map(u=>({name:u.name,slug:u.repo??null})):void 0}await I(t,{type:"pr.offered",actor:"agent",source:"transcript",boxId:r.box,provider:r.agent||null,payload:{offerId:vI(),ts:r.ts,excerpt:s,branch:t.workspace.branch,base:t.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:r.boxRole,offered:a||void 0,toPr:i||void 0,repos:c}})}}}}catch{}}async function ry(t,e){try{let n=await Cu(t),r=[...e||[]].filter(o=>o&&String(o.command||"").trim()).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=ty(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await I(t,{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}}),Zg(t,o),Qg(t,o))}}catch{}}async function oy(t,e){try{let n=await Cu(t),r=[...e||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=ey(o);n.keys.has(s)||(n.keys.add(s),await I(t,{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 TI=/^Base directory for this skill:\s*(\S+)/,AI=/^Run the "([^"]+)" workflow\./,EI=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,_I=/^<local-command-(?:stdout|caveat)>/i,II=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,RI=/^Continue from where you left off\.$/,$I=/^<task-notification>/i,OI=/<summary>([\s\S]*?)<\/summary>/i,MI=/<task-id>([\s\S]*?)<\/task-id>/i,CI=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,NI=/<status>([\s\S]*?)<\/status>/i,PI=/<result>([\s\S]*?)(?:<\/result>|$)/i;var Nu=new Set(["model","clear","compact","login","exit","quit"]);function sy(t){return Nu.has(String(t||"").replace(/^\//,"").toLowerCase())}var DI=/^\/([a-z0-9:_-]+)$/i;function fi(t){let e=String(t||"").trim();if(!e)return null;let n=e.match(TI);if(n)return{kind:"skill",name:n[1].split("/").filter(Boolean).pop()||"skill"};let r=e.match(AI);if(r)return{kind:"skill",name:r[1]};let o=e.match(EI);if(o){let a=o[1].toLowerCase();return Nu.has(a)?{kind:"suppress"}:{kind:"command",command:`/${a}`}}let s=e.match(DI);if(s&&Nu.has(s[1].toLowerCase()))return{kind:"suppress"};if(_I.test(e))return{kind:"suppress"};if(RI.test(e))return{kind:"suppress"};if(II.test(e))return{kind:"suppress"};if($I.test(e)){let a=c=>BI(e.match(c)?.[1].trim()||"")||null,i=a(PI);return{kind:"task",summary:a(OI)||"",taskId:a(MI),toolUseId:a(CI),status:a(NI)?.toLowerCase()||null,resultHead:i?i.length>600?`${i.slice(0,600).trimEnd()}\u2026`:i:null}}return null}function BI(t){return t.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function ay(t){return t?.kind==="skill"?`Ran the ${t.name} skill`:t?.kind==="command"?`Ran ${t.command}`:t?.kind==="task"?t.summary||"A background task finished":""}function iy(t){return t?.kind==="skill"?"skill-run":t?.kind==="task"?"task-done":"command-run"}var ct=t=>t.payload?.ts??t.ts;function jI(t){return{id:t.id,kind:"prompt",groupId:t.groupId,provider:t.provider,box:t.box,agent:t.provider,questionTotal:t.questions.length,answeredVia:"chat",questions:t.questions.map((e,n)=>({index:n,header:e.header||"",question:e.question,multiSelect:!!e.multiSelect,chosen:[]})),ts:t.ts}}var LI=new Set(["opened","ci-fixed","reconciled","pushed"]),cy=t=>`${t.repo??""}#${t.number}`;function FI(t,e,n){let r=cy(n),o=t.has(r)?r:null;if(!o&&n.number!=null)for(let[i,c]of t){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?t.get(o):t.get(s);if(o&&o!==s){t.delete(o);let i=e.get(o);if(e.delete(o),i?.length){let c=e.get(s)??[];e.set(s,c.concat(i))}}return t.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 ds(t){let e=[],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 t||[]){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 A of h.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let E=A[0].split("/").pop().toLowerCase();f.has(E)||f.set(E,[]),f.get(E).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(E=>E>=h-d&&E<=h+p);for(let g of t||[])if(g.type==="turn.retracted"){r.add(`${g.boxId}|${g.payload?.saidTs}`);let h=St(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=St(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,A={status:h.status||null,summary:h.summary||null,resultHead:h.resultHead??null,ts:ct(g)};h.toolUseId&&i.set(h.toolUseId,A);let E=String(h.summary||"").match(u);E&&l.set(E[1],A)}let w=new Map,y=new Map,b=null,S=new Map,_=new Map,O=new Map,nt=new Map,R=new Map,C=new Map,v=new Map,D=()=>{for(let g of v.values())g.questions.length&&e.push(g);v.clear()},B=null,lt=6e4,q=new Map,T=(g,h)=>{let A=o.get(g);if(!A?.length)return!1;let E=St(h),J=s.get(g);return A.some(W=>{if(!E.startsWith(W))return!1;let it=St(E.slice(W.length));return!!it&&!!J?.has(it)})};for(let g of t||[]){let h=g.payload||{};switch(g.type){case"turn.user":{if(g.source==="hook"||!String(h.text||"").trim()&&!h.images?.length)break;{let A=h.note||fi(h.text);if(A){if(A.kind==="task"){if(A.toolUseId&&a.has(A.toolUseId))break;let E=String(A.summary||"").match(u);if(!A.toolUseId&&E&&c.has(E[1]))break}if(A.kind==="command"&&sy(A.command))break;A.kind!=="suppress"&&e.push({id:g.id,kind:"event",type:iy(A),name:A.name??null,command:A.command??null,summary:A.summary??null,ts:ct(g),box:g.boxId,agent:g.provider});break}}if(r.has(`${g.boxId}|${ct(g)}`)||g.source==="transcript"&&T(g.boxId,h.text))break;if(h.kind==="merge-notify"||h.kind==="ship-notify"){e.push({id:g.id,kind:"event",type:h.kind==="ship-notify"?"ship-notified":"merge-notified",text:h.text,ts:ct(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"&&(B=null);break}for(let A of S.values())A.status="stale";S.clear(),B&&B.box===g.boxId&&(e.push(jI(B)),B=null),e.push({id:g.id,kind:"message",role:"user",text:h.text,ts:ct(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"turn.agent":{if(h.model==="<synthetic>"&&/^No response requested\.$/.test(String(h.text||"").trim()))break;D();let A=(h.images||[]).filter(it=>!m(it,ct(g)));if(!String(h.text||"").trim()&&!A.length)break;if(g.provider==="cursor"){let it=`${g.boxId}|${h.text}`;if(n.has(it))break;n.add(it)}(!B||B.box===g.boxId)&&(B=null);let E=g.provider==="codex"?(h.tools||[]).filter(it=>!Gl.has(it)&&!es.has(it)):h.tools||[],J=h.text,W=null;if(cu.has(g.provider)){let it=lu(String(h.text||""));it&&(J=it.text,W={questions:it.questions})}e.push({id:g.id,kind:"message",role:"assistant",text:J,ts:ct(g),box:g.boxId,agent:g.provider,boxRole:h.boxRole,...W?{ask:W}:{},...E.length?{tools:E}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},...A.length?{images:A}:{}}),W&&(B={id:g.id,groupId:uu(g.provider,h.sourceId||g.id,W.questions),provider:g.provider,box:g.boxId,questions:W.questions,ts:ct(g)});break}case"job.started":{if(!h.id)break;let A=ct(g),E=i.get(h.id);if(!E&&(h.kind||"agent")==="agent"&&h.name){let K=l.get(h.name);K&&K.ts>=A&&(E=K)}let J={jobId:h.id,jobKind:h.kind||"agent",name:h.name||null,detail:h.detail||null,status:E?E.status&&E.status!=="completed"?"failed":"done":"running",startedTs:A,finishedTs:E?.ts??null,summary:E?.summary??null,resultHead:E?.resultHead??null},W=q.get(g.boxId),it=W?.jobs[W.jobs.length-1]?.startedTs;if(W&&it!=null&&A-it<=lt)W.jobs.push(J);else{let K={id:g.id,kind:"job-dock",dockId:h.id,ts:A,box:g.boxId,agent:g.provider,jobs:[J]};q.set(g.boxId,K),e.push(K)}break}case"handoff.done":e.push(Te(g,"lead-handoff"));break;case"subtask.delegated":e.push({...Te(g,"subtask-delegated"),to:g.boxId,toAgent:g.provider});break;case"subtask.merged":e.push(Te(g,"subtask-merged"));break;case"pr.state.changed":{if(!h.change)break;if(h.change==="opened"){for(let E of S.values())E.status="stale";S.clear()}let A=Te(g,`pr-${h.change}`);if(LI.has(h.change)&&h.number!=null){let E=FI(w,y,h);if(w.size>=2){for(let W of y.values())for(let it of W)it.grouped=!0;A.groupPrs=[...w.values()];let J=[A];for(let W of w.keys())y.set(W,J)}else{let J=y.get(E)??[];J.push(A),y.set(E,J)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let E=cy(h),J=b&&!b.complete||w.size>=2;if(w.has(E))w.delete(E),y.delete(E);else if(h.number!=null)for(let W of[...w.keys()])W.endsWith(`#${h.number}`)&&(w.delete(W),y.delete(W));if(J&&h.number!=null){(!b||b.complete)&&(b={id:g.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:ct(g),ts:ct(g),box:g.boxId,agent:g.provider},e.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:ct(g)}),b.remaining=[...w.values()],b.complete=w.size===0;break}}e.push(A);break}case"pr.offered":{if(!h.offerId)break;for(let E of S.values())E.status="stale";S.clear();let A={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,repos:Array.isArray(h.repos)&&h.repos.length?h.repos:null,status:"open",ts:ct(g),box:g.boxId,agent:g.provider};S.set(h.offerId,A),_.set(h.offerId,A),e.push(A);break}case"pr.offer.answered":{let A=_.get(h.offerId);A&&(A.status=h.action||"dismiss",A.actions=Array.isArray(h.actions)?h.actions:null,A.notify=h.notify===!0,S.delete(h.offerId));break}case"box.state.changed":{let A=h.change==="model"?"model-change":h.change;if(!A)break;let E=Te(g,A);A==="model-change"&&(E.to=h.model),e.push(E);break}case"view.state.changed":{if(h.change!=="created"||h.restarted)break;e.push({id:g.id,kind:"view-card",viewId:h.viewId,name:h.name,slug:h.slug,port:h.port,origin:h.origin,ts:ct(g),box:g.boxId,agent:g.provider});break}case"view.spinup.failed":{e.push({id:g.id,kind:"view-spinup-failed",name:h.name||null,reason:h.reason||"no-view",ts:ct(g),box:g.boxId,agent:g.provider});break}case"mock.state.changed":{if(h.change==="picked"){e.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:ct(g),box:g.boxId,agent:g.provider});break}if(h.change!=="created")break;e.push({id:g.id,kind:"mock-card",mockId:h.mockId,name:h.name,group:h.group??null,ts:ct(g),box:g.boxId,agent:g.provider});break}case"flow.state.changed":{if(h.change!=="created")break;e.push({id:g.id,kind:"flow-card",flowId:h.flowId,name:h.name,stepCount:h.stepCount??null,mockIds:h.mockIds??[],ts:ct(g),box:g.boxId,agent:g.provider});break}case"runbook.state.changed":{if(h.change!=="created")break;e.push({id:g.id,kind:"runbook-card",runbookId:h.runbookId,name:h.name,ts:ct(g),box:g.boxId,agent:g.provider});break}case"ticket.linked":{e.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:ct(g),box:g.boxId,agent:g.provider});break}case"ticket.move.offered":{if(!h.offerId)break;for(let E of O.values())E.key===h.key&&(E.status="stale",O.delete(E.offerId));let A={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:ct(g),box:g.boxId,agent:g.provider};O.set(h.offerId,A),nt.set(h.offerId,A),e.push(A);break}case"ticket.move.answered":{let A=nt.get(h.offerId);A&&(A.status=h.action==="moved"?"moved":"dismiss",O.delete(h.offerId));break}case"ticket.state.changed":{for(let A of O.values())A.key===h.key&&A.to===h.to&&(A.status="stale",O.delete(A.offerId));if(h.mode==="offer")break;e.push(Te(g,"ticket-state"));break}case"ticket.commented":{e.push({id:g.id,kind:"ticket-comment",key:h.key,url:h.url??null,excerpt:h.excerpt??null,ts:ct(g),box:g.boxId,agent:g.provider});break}case"ticket.flow.changed":e.push(Te(g,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let E of R.values())E.status="stale";R.clear();let A={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:ct(g),box:g.boxId,agent:g.provider};R.set(h.proposalId,A),C.set(h.proposalId,A),e.push(A);break}case"ticket.create.answered":{let A=C.get(h.proposalId);A&&(A.status=h.action==="created"?"created":"dismiss",A.createdCount=h.createdCount??null,A.failedCount=h.failedCount??null,R.delete(h.proposalId));break}case"ticket.created":{let A={items:Array.isArray(h.items)?h.items:[],failed:Array.isArray(h.failed)?h.failed:[],team:h.team??null},E=h.proposalId?C.get(h.proposalId):null;if(E){E.created=A;break}e.push({id:g.id,kind:"ticket-created",...A,ts:ct(g),box:g.boxId,agent:g.provider});break}case"error":{if(!h.kind)break;e.push(Te(g,h.kind));break}case"usage.cap.hit":e.push(Te(g,"usage-cap-hit"));break;case"usage.cap.warning":e.push(Te(g,"usage-cap-warning"));break;case"provider.connected":{e.push({id:g.id,kind:"event",type:"provider-connected",provider:h.provider||g.provider,ts:ct(g),at:ct(g),box:g.boxId,agent:g.provider});break}case"prompt.answered":{if(B=null,h.control||!h.question)break;let A=h.groupId||h.promptId;if(!A)break;let E=v.get(A);E||(E={id:g.id,kind:"prompt",groupId:A,provider:g.provider,box:g.boxId,agent:g.provider,questionTotal:h.questionTotal??1,questions:[],ts:ct(g)},v.set(A,E));let J=h.questionIndex??0,W={index:J,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},it=E.questions.findIndex(K=>K.index===J&&St(K.question)===St(W.question));it>=0?E.questions[it]=W:E.questions.push(W),E.questions.sort((K,Y)=>K.index-Y.index),E.questionTotal=Math.max(E.questionTotal,h.questionTotal??1),E.questions.length>=E.questionTotal&&(e.push(E),v.delete(A));break}default:break}}return HI(e.sort((g,h)=>(g.ts??0)-(h.ts??0)))}var UI=2*6e4;function HI(t){let e=new Map,n=[];for(let r of t){if(r.kind!=="message"||!r.box){n.push(r);continue}let o=r.role==="assistant"&&!String(r.text||"").trim()&&r.images?.length,s=e.get(r.box);if(o&&s&&(r.ts??0)-s.lastTs<=UI){s.item.images.push(...r.images),s.lastTs=r.ts??s.lastTs;continue}if(o){let a={...r,images:[...r.images]};e.set(r.box,{item:a,lastTs:r.ts??0}),n.push(a)}else e.delete(r.box),n.push(r)}return n}function Pu(t,e=null){let n=[];for(let o of t||[]){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:WI(s),ts:s.ts??o.ts,n:s.n,box:o.boxId,agent:o.provider,boxRole:s.boxRole})}if(!n.length)return[];let r=(e??ds(t)).filter(o=>o.kind==="message");return iw(n,r)}function WI(t){let e=String(t.summary||"").trim();return e&&e!==t.command&&e!==Br(t.command)?e:gn(t.command,t.description||"")}function Te(t,e){let{ts:n,change:r,...o}=t.payload||{};return{id:t.id,kind:"event",type:e,...o,at:ct(t),ts:ct(t)}}var qI=12e4,KI=new Set(["archived","unarchived","opened"]);function ly(t){let e=[...t];for(let n=0;n<e.length;n++){let r=e[n];if(r?.kind!=="event"||r.type!=="archived")continue;let o=(r.ts??0)+qI,s=n;for(;s+1<e.length;){let a=e[s+1];if((a?.ts??0)>o||a?.kind==="event"&&KI.has(a.type))break;s++}s>n&&e.splice(s,0,...e.splice(n,1)),n=s}return e}function GI(t){let e=[],n=null;for(let r of String(t||"").split(`
|
|
146
|
+
`)){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;e.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 e}var Du=new Map;async function VI(t,e){let n=`${t.id}:${e.name}`;if(!Du.has(n)){let r=await L(t),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===e.name&&s.payload?.codexTs!=null&&o.add(s.payload.codexTs);Du.set(n,o)}return Du.get(n)}async function uy(t,e){try{if(e?.agent!=="codex"||!e.cwd)return;let n=await Yn(e.cwd,e.pinned?e.sessionId:null);if(!n)return;let r=await VI(t,e),o=GI(await Ft(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||e.model||"gpt-5.5";await I(t,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:e.name,provider:"codex",payload:{usd:me(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await I(t,{type:"context.usage",actor:"agent",source:"transcript",boxId:e.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{}}import pi from"node:fs";import ju from"node:path";import zI from"node:os";var JI=process.env.BOSTRAT_STATE_DIR||ju.join(process.env.HOME||zI.tmpdir(),".bostrat"),fy=(t=process.env)=>!!t.BOSTRAT_CURSOR_USAGE_COOKIE,XI="https://cursor.com/api/dashboard/get-filtered-usage-events",dy=100;async function YI({sinceMs:t,cookie:e,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(XI,{method:"POST",headers:{"content-type":"application/json",origin:"https://cursor.com",cookie:`WorkosCursorSessionToken=${e}`},body:JSON.stringify({teamId:0,startDate:String(t),endDate:String(Date.now()),page:o,pageSize:dy})});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<dy)break}return r}function ZI(t){if(!t)return null;let e=Number(t.timestamp)||null,n=t.tokenUsage||null,r=n?{input:n.inputTokens||0,output:n.outputTokens||0,cacheRead:n.cacheReadTokens||0,cacheWrite:n.cacheWriteTokens||0}:null,o=t.conversationId||null;return{id:[o||"?",e??"?",r?`${r.input}.${r.output}.${r.cacheRead}.${r.cacheWrite}`:"?"].join(":"),conversationId:o,ts:e,model:t.model||t.modelIntent||null,kind:t.kindLabel||t.kind||null,isHeadless:t.isHeadless===!0,tokens:r,vendorCents:n?.totalCents??null,chargedCents:t.chargedCents??null}}var QI=5*6e4,An={at:0,rows:null,error:null},mi=()=>ju.join(JI,"cursor-usage.json");function py(){try{return JSON.parse(pi.readFileSync(mi(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function tR(t){try{pi.mkdirSync(ju.dirname(mi()),{recursive:!0});let e=mi()+".tmp";pi.writeFileSync(e,JSON.stringify(t)),pi.renameSync(e,mi())}catch{}}var eR=5e3;async function nR({fetchImpl:t,now:e=Date.now()}={}){if(An.rows&&e-An.at<QI)return An.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=py(),o=Math.max(r.sinceMs-6e4,e-24*36e5);try{let s=await YI({sinceMs:o,cookie:n,fetchImpl:t});An={at:e,rows:s.map(ZI).filter(Boolean),error:null}}catch(s){An={at:e,rows:null,error:String(s?.message||s)}}return An.rows}var Lu=()=>({enabled:fy(),lastPollAt:An.at||null,error:An.error}),Bu=new Map;async function rR(t,e){let n=`${t.id}:${e.name}`;if(!Bu.has(n)){let r=await L(t),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===e.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);Bu.set(n,o)}return Bu.get(n)}async function my(t,e,{fetchImpl:n}={}){try{if(e?.agent!=="cursor"||!e.cwd||!fy())return;let r=await nR({fetchImpl:n});if(!r||!r.length)return;let o=await ww(e.cwd,e.pinned?e.sessionId:null);if(!o?.chatId)return;let s=await rR(t,e),a=py(),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(`${t.id}:${e.name}:${l.id}`))continue;s.add(l.id),i.add(`${t.id}:${e.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=me(u,f)),!d&&l.chargedCents!=null&&(d=l.chargedCents/100),await I(t,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:e.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}})}tR({sinceMs:c,seen:[...i].slice(-eR)})}catch{}}import{execFile as cR}from"node:child_process";import{promisify as lR}from"node:util";import hy from"node:fs/promises";import wy from"node:path";import oR from"node:os";var gy=process.env.BOSTRAT_STATE_DIR||wy.join(process.env.HOME||oR.tmpdir(),".bostrat"),sR=wy.join(gy,"github-latency.ndjson"),aR=100,hi=[],Fu=new Map;function wi({change:t,repo:e=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:t,repo:e,number:n,githubAt:r,observedAt:o,latencyMs:c,lane:s,intervalMs:a,source:i};hi.push(l),hi.length>aR&&hi.shift();let u=Fu.get(t)||{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)),Fu.set(t,u),iR(l),l}async function iR(t){try{await hy.mkdir(gy,{recursive:!0}),await hy.appendFile(sR,JSON.stringify(t)+`
|
|
147
|
+
`)}catch{}}function yy(){let t={};for(let[e,n]of Fu)t[e]={count:n.count,measured:n.measured,avgMs:n.measured?Math.round(n.totalMs/n.measured):null,maxMs:n.measured?n.maxMs:null};return{byChange:t,recent:hi.slice(-20)}}async function uR(t){let e=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(t||"")?.[1];if(e)try{let[{stdout:n},{stdout:r}]=await Promise.all([Jr("git",["-C",t,"rev-parse","HEAD"]),Jr("git",["-C",t,"rev-parse","--abbrev-ref","HEAD"])]);await I({id:e,cwd:t},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}var Jr=lR(cR),dR={timeout:3e4,maxBuffer:8*1024*1024};async function ne(t,e,n={}){let{caller:r="prs",...o}=n,s=await de({cwd:t});Ge(r);try{let{stdout:a}=await Jr("gh",e,{cwd:t,...dR,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw kn(a)&&xn(a),a}}var gi=null;async function Wu(){if(gi&&Date.now()-gi.at<6e4)return gi.data;let t;try{Ge("auth");let e=await Jr("gh",["auth","status"],{timeout:1e4});t={installed:!0,authenticated:!0,login:`${e.stdout||""}${e.stderr||""}`.match(/account\s+(\S+)/)?.[1]??null}}catch(e){t=e?.code==="ENOENT"||e?.code===127?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(t.installed&&!t.authenticated)try{let e=await xo();e.configured&&(t={installed:!0,authenticated:!0,login:e.username,via:"broker"})}catch{}return gi={at:Date.now(),data:t},t}async function fR(t){try{let{stdout:e}=await Jr("git",["-C",t,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(e)}catch{return!1}}function ky(t){let e=t.name||t.context||"check",n=t.detailsUrl||t.targetUrl||null,r=t.completedAt||t.createdAt||null,o;return t.__typename==="StatusContext"?o=t.state==="SUCCESS"?"pass":t.state==="PENDING"||t.state==="EXPECTED"?"pending":"fail":t.status&&t.status!=="COMPLETED"?o="pending":["SUCCESS","NEUTRAL","SKIPPED"].includes(t.conclusion)?o="pass":o="fail",{name:e,state:o,url:n,completedAt:r}}function Uu(t){let e=(t||[]).map(ky),n=e.filter(s=>s.state==="pending").length,r=e.map(s=>Date.parse(s.completedAt||"")).filter(s=>s>0),o=e.length&&!n&&r.length?Math.max(...r):null;return{total:e.length,passed:e.filter(s=>s.state==="pass").length,failed:e.filter(s=>s.state==="fail").length,pending:n,failing:e.filter(s=>s.state==="fail").map(({name:s,url:a})=>({name:s,url:a})),settledAt:o}}var qu="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",xy=qu.replace("statusCheckRollup,","");function Ku(t){return/Resource not accessible by personal access token/.test(String(t?.stderr||t?.message||""))}async function Sy(t,e){if(!e)return null;let n=await ot(t);if(!n)return null;try{let r=await ne(t,["api",`repos/${n}/actions/runs?head_sha=${encodeURIComponent(e)}&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 vy(t){return(t||[]).map(e=>({name:e.name,detailsUrl:e.html_url||null,status:String(e.status||"").toUpperCase(),conclusion:e.conclusion?String(e.conclusion).toUpperCase():null,completedAt:e.status==="completed"&&e.updated_at||null}))}async function Ty(t,e){let n=await Sy(t,e);return n===null?{...Uu([]),unavailable:!0}:Uu(vy(n))}function yi(t,e=Uu(t.statusCheckRollup)){return{number:t.number,title:t.title,url:t.url,author:t.author?.login??null,headRefName:t.headRefName,headSha:t.headRefOid??null,baseRefName:t.baseRefName,isDraft:!!t.isDraft,reviewDecision:t.reviewDecision||null,mergeable:t.mergeable||"UNKNOWN",mergeStateStatus:t.mergeStateStatus||"UNKNOWN",additions:t.additions??0,deletions:t.deletions??0,createdAt:t.createdAt,updatedAt:t.updatedAt,checks:e,green:e.failed===0&&e.pending===0&&t.mergeStateStatus==="CLEAN",autoMergeArmed:!!t.autoMergeRequest,mergeabilityPending:!1}}var pR=9e4,zr=new Map;function mR(t,e,n){let r=new Set;for(let o of e){let s=`${t}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){zr.delete(s);continue}r.add(s),zr.has(s)||zr.set(s,n),o.mergeabilityPending=n-zr.get(s)<pR}for(let o of zr.keys())o.startsWith(`${t}#`)&&!r.has(o)&&zr.delete(o);return e}async function Xr(t,e){let n=await Ve(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await ne(n.path,["pr","view",String(r),"--json",qu]);return yi(JSON.parse(o))}catch(o){if(!Ku(o))throw o;let s=await ne(n.path,["pr","view",String(r),"--json",xy]),a=JSON.parse(s);return yi(a,await Ty(n.path,a.headRefOid))}}var by=(t,e)=>t.number-e.number;async function ps(t,{caller:e="prs"}={}){try{let n=await ne(t,["pr","list","--json",qu,"--limit","50"],{caller:e});return JSON.parse(n||"[]").map(r=>yi(r)).sort(by)}catch(n){if(!Ku(n))throw n;let r=await ne(t,["pr","list","--json",xy,"--limit","50"],{caller:e});return(await Promise.all(JSON.parse(r||"[]").map(async o=>yi(o,await Ty(t,o.headRefOid))))).sort(by)}}var hR=3e4,wR=1500,fs=new Map;function Ee(){fs.clear()}async function gR(t,e){if(!t)return e;for(let n of e){let r=await Bt(t,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 Wa(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Ka(t,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return e}async function Ay(){let t=await Wu();if(!t.authenticated)return{auth:t,repos:[]};let e=[];for(let a of await Re())await fR(a.path)&&e.push(a);let n=Date.now(),r=ci(n)>0,o=await Promise.all(e.map(async a=>{let i=fs.get(a.path),c=i?.pending?wR:hR;if(i&&(r||n-i.at<c))return i.entry;try{let l=await ot(a.path).catch(()=>null),u=mR(a.path,await ps(a.path,{caller:"tab"}),n);if(await gR(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&&wi({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 fs.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:Ae(l)}}})),s=new Set(e.map(a=>a.path));for(let a of fs.keys())s.has(a)||fs.delete(a);return{auth:t,repos:o}}async function Ve(t){let e=(await Re()).find(n=>n.path===t);if(!e)throw new Error(`unknown repo: ${t}`);return e}async function sr(t){return bo(t)}var ms=new Set(["squash","merge","rebase"]);async function Yr(t,e,{method:n="squash"}={}){let r=await Ve(t);if(!ms.has(n))throw new Error(`invalid merge method: ${n}`);let o=Number(e);if(!Number.isInteger(o)||o<=0)throw new Error("invalid PR number");let s=["pr","merge",String(o),`--${n}`];if(n!=="rebase"&&(await de({cwd:r.path})).GH_TOKEN){let a=await lc().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await ne(r.path,s)}catch(a){throw new Error(Ae(a))}finally{Ee()}return{ok:!0,number:o}}async function hs(t,e){let n=await Ve(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await ne(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Ae(s))}finally{Ee()}let o=await ot(n.path).catch(()=>null);return o&&await Uh(o,r).catch(()=>{}),{ok:!0,number:r}}async function Gu(t,e){let n=await Ve(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await ne(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Ae(o))}finally{Ee()}return{ok:!0,number:r}}var yR=2500,bR=9e4,kR=t=>new Promise(e=>setTimeout(e,t));function Vu(t){return t.green&&!t.isDraft}function xR(t){return t.isDraft?"draft":t.mergeable==="CONFLICTING"?`conflicts with ${t.baseRefName}`:t.checks.failed>0?"checks failing":t.checks.pending>0?"checks running":t.mergeable==="UNKNOWN"||t.mergeStateStatus==="UNKNOWN"?"mergeability unresolved":t.mergeStateStatus==="BEHIND"?`behind ${t.baseRefName}`:t.mergeStateStatus==="BLOCKED"?"blocked by branch protection":`not clean (${t.mergeStateStatus})`}function Ey(t,{update:e=!0}={}){return t.isDraft?{action:"skip",reason:"draft"}:Vu(t)?{action:"merge"}:e&&t.mergeStateStatus==="BEHIND"&&t.mergeable!=="CONFLICTING"&&t.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function _y(t){return[...t.filter(e=>e.plan.action!=="merge"),...t.filter(e=>e.plan.action==="merge")]}var Hu=new Map;function Iy(){let t={};for(let[e,n]of Hu)t[e]={...n};return t}async function Ry(t,{method:e="merge",onMerged:n=null}={}){let r=await Ve(t);if(!ms.has(e))throw new Error(`invalid merge method: ${e}`);if(Hu.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await ps(r.path)).filter(Vu).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:e,total:o.length,current:o[0].number,queue:o.map(a=>({number:a.number,title:a.title})),merged:[],skipped:[],failed:[]};return Hu.set(r.path,s),vR(s,o,{listPrs:()=>ps(r.path),merge:a=>Yr(r.path,a,{method:e}),sleep:kR,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function SR(t,e){let n=Date.now()+(e.settleMaxMs??bR),r=null;for(;;){let o=null;try{if(o=await e.listPrs(),r=null,!o.some(a=>t.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 e.sleep(yR)}}async function vR(t,e,n){try{let r=e;for(let o=0;o<e.length;o++){let s=e[o];if(t.current=s.number,o>0)try{r=await SR(e.slice(o).map(i=>i.number),n)}catch(i){for(let c of e.slice(o))t.failed.push({number:c.number,title:c.title,reason:`couldn't re-check PR state: ${Ae(i)}`});break}let a=r.find(i=>i.number===s.number);if(!a){t.skipped.push({number:s.number,title:s.title,reason:"no longer open"});continue}if(!Vu(a)){t.skipped.push({number:a.number,title:a.title,reason:xR(a)});continue}try{await n.merge(a.number),t.merged.push({number:a.number,title:a.title});try{await n.onMerged?.(a)}catch{}}catch(i){t.failed.push({number:a.number,title:a.title,reason:Ae(i)})}}}finally{t.current=null,t.running=!1,t.finishedAt=Date.now(),Ee()}}async function zu(t,{title:e,body:n="",base:r=null}={}){if(typeof e!="string"||!e.trim())throw new Error("title required");try{await Jr("git",["-C",t,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Ae(l)}`)}await uR(t);let o=n||"";if((await de({cwd:t})).GH_TOKEN){let l=await lc().catch(()=>null);l&&(o=`${o?`${o}
|
|
148
|
+
|
|
149
|
+
`:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",e.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await ne(t,s)}catch(l){throw new Error(Ae(l))}finally{Ee()}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 $y(t,e){let n=await Ve(t),r=Number(e);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await ne(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(Ae(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!Ku(i))throw i;let c=await ne(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await Sy(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(Ae(i));o=vy(l).map(ky).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=TR(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await ne(n.path,["run","view",c,"--log-failed"]).then(u=>AR(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function TR(t){return String(t||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function AR(t,{maxLines:e=400,maxBytes:n=64*1024}={}){let r=String(t||""),o=r.split(`
|
|
150
150
|
`);return o.length>e&&(r=`\u2026 (${o.length-e} earlier lines truncated)
|
|
151
151
|
`+o.slice(-e).join(`
|
|
152
152
|
`)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
|
|
153
|
-
`+r.slice(-n)),r}function
|
|
154
|
-
`).find(n=>n.trim())||"gh failed"}var wI=6e3,ei=new Map;function Au(t){let e=null;for(let n of t||[]){let r=n.payload?.ts??n.ts;n.type==="pr.offer.answered"?e=n.payload?.action==="pr"?{ts:r,offerId:n.payload?.offerId??null,box:n.boxId??null}:null:n.type==="pr.state.changed"&&n.payload?.change==="opened"&&e!=null&&r>=e.ts&&(e=null)}return e}async function Ug(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await L(t)}catch{return}if(Au(n)==null)return;let r=(t.boxes||[]).find(c=>c.name===t.leadBox);if(await Ga(t.leadBox,r?.agent).catch(()=>!1))return;let o=Date.now();if(o-(ei.get(t.id)||0)<wI)return;ei.set(t.id,o);let s;try{s=await ts(t.cwd,{caller:"reconcile"})}catch{return}let a=(s||[]).find(c=>c.headRefName===e);if(!a)return;let i=await et(t.cwd).catch(()=>null);if(i){let{link:c,created:l}=await qt({repo:i,number:a.number,trackId:t.id,url:a.url,headRef:e,title:a.title,source:"reconciled"});if(!l&&!(c?.trackId===t.id&&await jo(i,a.number))){ei.delete(t.id);return}}await R(t,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:t.leadBox,payload:{ts:Date.now(),change:"opened",number:a.number,url:a.url,title:a.title,branch:e,...i?{repo:i}:{}}}).catch(()=>{}),ei.delete(t.id)}import gI from"node:fs/promises";import Gg from"node:path";import yI from"node:os";import{randomUUID as Vg}from"node:crypto";var bI=process.env.BOSTRAT_STATE_DIR||Gg.join(process.env.HOME||yI.tmpdir(),".bostrat"),zg=Gg.join(bI,"flows.json"),Hg=20,Wg=80,qg=500,Kg=80,Ue=null,Eu=null;async function He(){return Ue||(Eu||=(async()=>{let t=!0;try{Ue=JSON.parse(await gI.readFile(zg,"utf8"))}catch{Ue={flows:[]},t=!1}finally{Eu=null}if(Ue.flows||=[],!t&&pt()){let{records:e,source:n}=await At("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of e)!r.deletedAt&&r.data?.id&&Ue.flows.push(r.data);Ue.flows.length&&await Hr().catch(()=>{})}}return Ue})(),Eu)}async function Hr(){await Nt(zg,Ue)}var ni=t=>$t("flows",{...t},{trackId:t.trackId}).catch(()=>{});function _u(t,e,n={}){return R({id:t.trackId,cwd:t.cwd},{type:"flow.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,flowId:t.id,name:t.name,stepCount:t.steps.length,mockIds:[...new Set(t.steps.map(r=>r.mockId))],...n}}).catch(()=>{})}async function Jg({trackId:t,steps:e,prevSteps:n=[]}){if(!Array.isArray(e)||e.length<1||e.length>Hg)throw new Error(`steps must be an array of 1-${Hg}`);let r=await Pn({trackId:t}),o=[...n],s=[];for(let i=0;i<e.length;i++){let c=e[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>qg)throw new Error(`${l}: caption over ${qg} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>Kg)throw new Error(`${l}: title over ${Kg} chars`);let d=String(c.mock??"").trim().replace(/^mock:/,"");if(!d)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(S=>S.id===d);if(p.length||(p=r.filter(S=>S.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(S=>`mock:${S.id}`).join(", ")}) \u2014 use the id`);let m=p[0],h=null;if(c.v!=null&&(h=Number(c.v),!Number.isInteger(h)||!(m.versions||[]).some(S=>S.v===h)))throw new Error(`${l}: "${m.name}" has no captured v${c.v} to pin`);let y=o.findIndex(S=>S.mockId===m.id),b=y>=0?o.splice(y,1)[0].id:Vg().slice(0,8);s.push({id:b,mockId:m.id,caption:u,title:f,v:h})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function kI(t){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(t.trim().length>Wg)throw new Error(`name over ${Wg} chars`);return t.trim()}async function Xg({trackId:t}={}){let e=await He();return(t?e.flows.filter(r=>r.trackId===t):e.flows).map(r=>({...r}))}async function xI(t){let n=(await He()).flows.find(r=>r.id===t);return n?{...n}:null}async function Yg(t){let e=await xI(t);if(!e)return null;let n=new Map((await Pn({trackId:e.trackId})).map(r=>[r.id,r]));return e.steps=e.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=Ie(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}}),e}async function Zg({trackId:t,name:e,steps:n,cwd:r,box:o,agent:s}){if(!t)throw new Error("trackId required");if(!r)throw new Error("cwd required");let a=kI(e),i=await He(),c=i.flows.find(p=>p.trackId===t&&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 Jg({trackId:t,steps:n}),f=Date.now(),d={id:Vg().slice(0,8),trackId:t,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 Hr(),await _u(d,"created"),ni(d),{...d}}async function Qg(t,{steps:e,note:n}={}){let o=(await He()).flows.find(i=>i.id===t);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await Jg({trackId:o.trackId,steps:e,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 Hr(),await _u(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),ni(o),{...o}}async function ty(t){let e=await He(),n=e.flows.findIndex(o=>o.id===t);if(n===-1)throw new Error("flow not found");let r=e.flows[n];return e.flows.splice(n,1),await Hr(),await _u(r,"deleted"),Qt("flows",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function ey(t){return(await He()).flows.filter(n=>n.steps.some(r=>r.mockId===t)).map(n=>({...n}))}async function ny(t){let n=(await He()).flows.filter(r=>r.trackId===t.id);for(let r of n)r.trackDeletedAt=Date.now(),r.trackName=t.name||t.id,r.updatedAt=Date.now(),ni(r);return n.length&&await Hr(),n.length}async function ry(t){let e=await He(),n=e.flows.filter(r=>r.trackId===t);if(n.length){e.flows=e.flows.filter(r=>r.trackId!==t),await Hr();for(let r of n)Qt("flows",r.id).catch(()=>{})}return n.length}async function oy(){if(!pt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await At("flows");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await He();for(let r of n.flows)await ni(r);return{pushed:n.flows.length,source:"plane"}}import Iu from"node:fs/promises";import rs from"node:path";import{randomUUID as SI}from"node:crypto";var ay=".bostrat/uploads",Ru=t=>rs.join(t,ay),sy=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,vI=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),TI=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"]);function $u(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return vI.has(e)?"image":TI.has(e)?"doc":null}function AI(t){let r=rs.basename(String(t||"")).replace(/[\x00-\x1f]/g,"").replace(/[^A-Za-z0-9._-]+/g,"_").replace(/^_+|_+$/g,"").slice(-80)||"file";return r.startsWith(".")?`f${r}`:r}async function iy(t,{filename:e,buffer:n}){if(!t)throw new Error("cwd required");if(!Buffer.isBuffer(n)||n.length===0)throw new Error("empty upload");if(n.length>sy)throw new Error(`file too large \u2014 max ${Math.floor(sy/(1024*1024))} MB`);let r=AI(e),o=rs.extname(r).slice(1),s=$u(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=SI().slice(0,8),i=Ru(t);await Iu.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Iu.writeFile(rs.join(i,c),n,{mode:384}),await Iu.chmod(rs.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${ay}/${c}`,kind:s,size:n.length}}function cy(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}var EI=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/;function Ou(t){return t.role==="user"&&EI.test((t.text||"").trim())}function Wr(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var RI=II(_I),We=process.env.BOSTRAT_STATE_DIR||H.join(process.env.HOME||"/tmp",".bostrat"),gy=H.join(We,"tracks.json"),$I=H.join(We,"events.jsonl"),ju=H.join(We,"workspaces"),OI=process.env.BOSTRAT_DEFAULT_REPO||null,si={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"}},ai=Object.keys(si);function CI(t,{skipPermissions:e=!0}={}){let n=si[t];if(!n)throw new Error(`unknown agent "${t}"`);let r=process.env[n.envVar];return r||(e&&n.skipFlag?`${n.base} ${n.skipFlag}`:n.base)}function yy(t,e,{skipPermissions:n=!0}={}){let r=si[t];if(!r)throw new Error(`unknown agent "${t}"`);let o=process.env[r.envVar];if(o)return o;let s=n&&r.skipFlag?` ${r.skipFlag}`:"";switch(t){case"claude":return`${r.base} --resume ${e}${s}`;case"cursor":return`${r.base} --continue${s}`;case"codex":return`${r.base} resume --last${s}`;default:return`${r.base}${s}`}}function ps(t){let e=si[t];if(e)return process.env[e.envVar]||e.base}var Zn={at:0,value:null};async function Fu({maxAgeMs:t=6e4}={}){if(Zn.value&&Date.now()-Zn.at<t)return Zn.value;let e=await Promise.all(ai.map(async n=>[n,await Gs(ps(n))]));return Zn.value=Object.fromEntries(e),Zn.at=Date.now(),Zn.value}async function ii(t,e=!0){let n=CI(t,{skipPermissions:e});if(!await Gs(n)){Zn.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.":lc[r]?`Install it on this node: ${lc[r]}`:"Its CLI must be installed and on PATH first.";throw new Error(`agent "${t}" isn't available on this box (command not found: ${r}). ${o}`)}return n}var ci=".bostrat/handoff/latest.md";function MI(t,e=ci){return`${t.trim()}
|
|
153
|
+
`+r.slice(-n)),r}function Ae(t){return String(t?.stderr||t?.message||t||"").trim().split(`
|
|
154
|
+
`).find(n=>n.trim())||"gh failed"}var ER=6e3,bi=new Map;function Ju(t){let e=null;for(let n of t||[]){let r=n.payload?.ts??n.ts;n.type==="pr.offer.answered"?e=n.payload?.action==="pr"?{ts:r,offerId:n.payload?.offerId??null,box:n.boxId??null}:null:n.type==="pr.state.changed"&&n.payload?.change==="opened"&&e!=null&&r>=e.ts&&(e=null)}return e}async function Oy(t){let e=t?.workspace?.branch;if(!e)return;let n;try{n=await L(t)}catch{return}if(Ju(n)==null)return;let r=(t.boxes||[]).find(c=>c.name===t.leadBox);if(await ui(t.leadBox,r?.agent).catch(()=>!1))return;let o=Date.now();if(o-(bi.get(t.id)||0)<ER)return;bi.set(t.id,o);let s;try{s=await ps(t.cwd,{caller:"reconcile"})}catch{return}let a=(s||[]).find(c=>c.headRefName===e);if(!a)return;let i=await ot(t.cwd).catch(()=>null);if(i){let{link:c,created:l}=await Xt({repo:i,number:a.number,trackId:t.id,url:a.url,headRef:e,title:a.title,source:"reconciled"});if(!l&&!(c?.trackId===t.id&&await zo(i,a.number))){bi.delete(t.id);return}}await I(t,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:t.leadBox,payload:{ts:Date.now(),change:"opened",number:a.number,url:a.url,title:a.title,branch:e,...i?{repo:i}:{}}}).catch(()=>{}),bi.delete(t.id)}import _R from"node:fs/promises";import Dy from"node:path";import IR from"node:os";import{randomUUID as By}from"node:crypto";var RR=process.env.BOSTRAT_STATE_DIR||Dy.join(process.env.HOME||IR.tmpdir(),".bostrat"),jy=Dy.join(RR,"flows.json"),My=20,Cy=80,Ny=500,Py=80,ze=null,Xu=null;async function Je(){return ze||(Xu||=(async()=>{let t=!0;try{ze=JSON.parse(await _R.readFile(jy,"utf8"))}catch{ze={flows:[]},t=!1}finally{Xu=null}if(ze.flows||=[],!t&&wt()){let{records:e,source:n}=await Ot("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of e)!r.deletedAt&&r.data?.id&&ze.flows.push(r.data);ze.flows.length&&await Zr().catch(()=>{})}}return ze})(),Xu)}async function Zr(){await Lt(jy,ze)}var ki=t=>Dt("flows",{...t},{trackId:t.trackId}).catch(()=>{});function Yu(t,e,n={}){return I({id:t.trackId,cwd:t.cwd},{type:"flow.state.changed",actor:"orchestrator",source:"orchestrator",boxId:t.box||null,provider:t.agent||null,payload:{ts:Date.now(),change:e,flowId:t.id,name:t.name,stepCount:t.steps.length,mockIds:[...new Set(t.steps.map(r=>r.mockId))],...n}}).catch(()=>{})}async function Ly({trackId:t,steps:e,prevSteps:n=[]}){if(!Array.isArray(e)||e.length<1||e.length>My)throw new Error(`steps must be an array of 1-${My}`);let r=await Hn({trackId:t}),o=[...n],s=[];for(let i=0;i<e.length;i++){let c=e[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>Ny)throw new Error(`${l}: caption over ${Ny} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>Py)throw new Error(`${l}: title over ${Py} chars`);let d=String(c.mock??"").trim().replace(/^mock:/,"");if(!d)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(S=>S.id===d);if(p.length||(p=r.filter(S=>S.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(S=>`mock:${S.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(S=>S.v===w)))throw new Error(`${l}: "${m.name}" has no captured v${c.v} to pin`);let y=o.findIndex(S=>S.mockId===m.id),b=y>=0?o.splice(y,1)[0].id:By().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 $R(t){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(t.trim().length>Cy)throw new Error(`name over ${Cy} chars`);return t.trim()}async function Fy({trackId:t}={}){let e=await Je();return(t?e.flows.filter(r=>r.trackId===t):e.flows).map(r=>({...r}))}async function OR(t){let n=(await Je()).flows.find(r=>r.id===t);return n?{...n}:null}async function Uy(t){let e=await OR(t);if(!e)return null;let n=new Map((await Hn({trackId:e.trackId})).map(r=>[r.id,r]));return e.steps=e.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=Ne(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}}),e}async function Hy({trackId:t,name:e,steps:n,cwd:r,box:o,agent:s}){if(!t)throw new Error("trackId required");if(!r)throw new Error("cwd required");let a=$R(e),i=await Je(),c=i.flows.find(p=>p.trackId===t&&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 Ly({trackId:t,steps:n}),f=Date.now(),d={id:By().slice(0,8),trackId:t,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 Zr(),await Yu(d,"created"),ki(d),{...d}}async function Wy(t,{steps:e,note:n}={}){let o=(await Je()).flows.find(i=>i.id===t);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await Ly({trackId:o.trackId,steps:e,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 Zr(),await Yu(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),ki(o),{...o}}async function qy(t){let e=await Je(),n=e.flows.findIndex(o=>o.id===t);if(n===-1)throw new Error("flow not found");let r=e.flows[n];return e.flows.splice(n,1),await Zr(),await Yu(r,"deleted"),re("flows",r.id).catch(()=>{}),{ok:!0,id:t,name:r.name}}async function Ky(t){return(await Je()).flows.filter(n=>n.steps.some(r=>r.mockId===t)).map(n=>({...n}))}async function Gy(t){let n=(await Je()).flows.filter(r=>r.trackId===t.id);for(let r of n)r.trackDeletedAt=Date.now(),r.trackName=t.name||t.id,r.updatedAt=Date.now(),ki(r);return n.length&&await Zr(),n.length}async function Vy(t){let e=await Je(),n=e.flows.filter(r=>r.trackId===t);if(n.length){e.flows=e.flows.filter(r=>r.trackId!==t),await Zr();for(let r of n)re("flows",r.id).catch(()=>{})}return n.length}async function zy(){if(!wt())return{pushed:0,source:"unconfigured"};let{records:t,source:e}=await Ot("flows");if(e!=="plane"||t.length)return{pushed:0,source:e};let n=await Je();for(let r of n.flows)await ki(r);return{pushed:n.flows.length,source:"plane"}}import Zu from"node:fs/promises";import ws from"node:path";import{randomUUID as MR}from"node:crypto";var Xy=".bostrat/uploads",Qu=t=>ws.join(t,Xy),Jy=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,CR=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),NR=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"]);function td(t){let e=String(t||"").toLowerCase().replace(/^\./,"");return CR.has(e)?"image":NR.has(e)?"doc":null}function PR(t){let r=ws.basename(String(t||"")).replace(/[\x00-\x1f]/g,"").replace(/[^A-Za-z0-9._-]+/g,"_").replace(/^_+|_+$/g,"").slice(-80)||"file";return r.startsWith(".")?`f${r}`:r}async function Yy(t,{filename:e,buffer:n}){if(!t)throw new Error("cwd required");if(!Buffer.isBuffer(n)||n.length===0)throw new Error("empty upload");if(n.length>Jy)throw new Error(`file too large \u2014 max ${Math.floor(Jy/(1024*1024))} MB`);let r=PR(e),o=ws.extname(r).slice(1),s=td(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=MR().slice(0,8),i=Qu(t);await Zu.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Zu.writeFile(ws.join(i,c),n,{mode:384}),await Zu.chmod(ws.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${Xy}/${c}`,kind:s,size:n.length}}function Zy(t){let e=t.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${t.length===1?"this file":`these ${t.length} files`}: ${e}]`}var DR=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/;function ed(t){return t.role==="user"&&DR.test((t.text||"").trim())}function ar(t,e,n=Date.now()){return e?t.idleSince==null?(t.idleSince=n,!1):n-t.idleSince>=6500:(t.idleSince=null,!1)}var LR=jR(BR),Xe=process.env.BOSTRAT_STATE_DIR||U.join(process.env.HOME||"/tmp",".bostrat"),ab=U.join(Xe,"tracks.json"),FR=U.join(Xe,"events.jsonl"),cd=U.join(Xe,"workspaces"),UR=process.env.BOSTRAT_DEFAULT_REPO||null,Ti={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"}},Ai=Object.keys(Ti);function HR(t,{skipPermissions:e=!0}={}){let n=Ti[t];if(!n)throw new Error(`unknown agent "${t}"`);let r=process.env[n.envVar];return r||(e&&n.skipFlag?`${n.base} ${n.skipFlag}`:n.base)}function ib(t,e,{skipPermissions:n=!0}={}){let r=Ti[t];if(!r)throw new Error(`unknown agent "${t}"`);let o=process.env[r.envVar];if(o)return o;let s=n&&r.skipFlag?` ${r.skipFlag}`:"";switch(t){case"claude":return`${r.base} --resume ${e}${s}`;case"cursor":return`${r.base} --continue${s}`;case"codex":return`${r.base} resume --last${s}`;default:return`${r.base}${s}`}}function As(t){let e=Ti[t];if(e)return process.env[e.envVar]||e.base}var En={at:0,value:null};async function dd({maxAgeMs:t=6e4}={}){if(En.value&&Date.now()-En.at<t)return En.value;let e=En.value,n=await Promise.all(Ai.map(async r=>{let o=await aa(As(r),{retries:[]});return o===yr?[r,e?.[r]??!0]:[r,o===sa]}));return En.value=Object.fromEntries(n),En.at=Date.now(),En.value}async function Ei(t,e=!0){let n=HR(t,{skipPermissions:e});if(await aa(n)===gr){En.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.":Ic[r]?`Install it on this node: ${Ic[r]}`:"Its CLI must be installed and on PATH first.";throw new Error(`agent "${t}" isn't available on this box (command not found: ${r}). ${o}`)}return n}var _i=".bostrat/handoff/latest.md";function WR(t,e=_i){return`${t.trim()}
|
|
155
155
|
|
|
156
|
-
(Read ${e} 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.)`}var
|
|
156
|
+
(Read ${e} 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.)`}var qR=/\(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\.\)$/;function Qy(t){return t.role==="user"&&qR.test((t.text||"").trim())}var xi=".bostrat/handoff/subtask-result.md",ys=".bostrat/handoff/lead-context.md",KR=(t,e=!1)=>e?`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 ${ys} \u2014 read that first for context. Your scoped task:
|
|
157
157
|
|
|
158
158
|
${t}
|
|
159
159
|
|
|
160
|
-
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 ${
|
|
160
|
+
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 ${xi} (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 ${ys} \u2014 read that first for context. Your scoped task:
|
|
161
161
|
|
|
162
162
|
${t}
|
|
163
163
|
|
|
164
|
-
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 ${
|
|
165
|
-
`;return Mu=Mu.then(()=>U.mkdir(We,{recursive:!0})).then(()=>U.appendFile($I,n)).catch(()=>{}),Mu}var lt=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),DI(t.id,r),Nw(t,r),r};qf(async({sessionName:t,from:e,to:n,version:r,status:o,error:s})=>{let i=(await W()).tracks.find(c=>c.leadBox===t||(c.boxes||[]).some(l=>l.name===t));i&&(lt(i,"codex-update",{box:t,from:e,to:r||n,status:o,...s?{error:s}:{}}),await V().catch(()=>{}))});var Nu=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),jI=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);function by(t){if(!t)return null;let e=typeof t=="string"?[t]:Array.isArray(t?.codes)&&t.codes.length?t.codes:t?.code?[t.code]:[],n=[...new Set(e.filter(s=>jI.has(s)))];if(!n.length)return null;let r=typeof t=="object"?t?.note:null,o=r&&String(r).slice(0,500).trim()||null;return{code:n[0],...n.length>1?{codes:n}:{},note:o}}async function ky(t,e,n,{kind:r}={}){let o=await Q(e);if(!o)throw new Error(`box "${e}" is not running`);let s=ps(o.agent);await tn({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await St(e,n,{..._e(o.agent),kind:r||"orchestration"}),await q({flow:"orchestration",phase:"sent",trackId:t,sessionName:e,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await W(),i=Y(a,t);if(!i)return;let c=i.boxes.find(l=>l.name===e);lt(i,"orchestration-prompt",{box:e,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await V()}var tr=new Set(["merged","conflict","canceled","failed","handed-off"]),uy=new Map;async function LI(t){let e=H.join(sa(t),`${t.id}.ndjson`),n;try{n=await U.stat(e)}catch{return null}let r=uy.get(t.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await L(t)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return uy.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function FI(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:ou(n.name,n.agent)}async function bn(t,e=null){let n=e??new Map((await $n()).map(i=>[i.name,i])),r=t.boxes.map(i=>{let c=n.get(i.name),l=tr.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([LI(t),FI(t,r)]);return{...t,boxes:r,lastResponseAt:o,working:s,prompted:a}}var UI=5e3;function HI(t,e,n){let r=[...t].sort((l,u)=>l.createdAt-u.createdAt),o=[...e].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-UI,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 xy(t){let e=new Map;for(let r of t.tracks)for(let o of r.boxes)o.agent==="claude"&&o.cwd&&(e.has(o.cwd)||e.set(o.cwd,[]),e.get(o.cwd).push({track:r,box:o}));let n=!1;for(let[r,o]of e){let s=o.map(d=>d.box.sessionId),a=jf(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=HI(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 Rf(d.box,d.sessionId),n=!0}}return n}async function at(){let t=await W();await xy(t)&&await V();let e=new Map((await $n()).map(n=>[n.name,n]));return Promise.all(t.tracks.map(n=>bn(n,e)))}async function Sy(t,e){let n=await W();if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===t);if(o)return{track:r,box:o}}if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===e);if(o)return{track:r,box:o}}return null}async function vy(t){let e=ao(t);if(!e)return null;let n=await W();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Bd(o.hookTokenHash,e))return{track:r,box:o};return null}async function tt(t){let e=await W(),n=Y(e,t);return n?(await xy(e)&&await V(),bn(n)):null}var WI={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};function dy(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(WI[t.agent]||[]).some(n=>n.test(e))}var qI=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];function fy(t){let e=(t.text||"").trim();return e?qI.some(n=>n.test(e)):!1}var KI=10*6e4;async function Uu(t,e={}){let n=await tt(t);if(!n)return null;let r=new Set([...n.events.filter(k=>k.type==="orchestration-prompt"&&k.prompt).map(k=>String(k.prompt).trim()),...n.events.filter(k=>k.type==="said"&&k.text).map(k=>String(k.text).trim())]),o=ms(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=Bc(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...dm(n),...l],d={windows:[...Ep(n),..._p(n,ca),...a,...u]},m=(await Promise.all(n.boxes.map(k=>Cl(k,e)))).flat(),h=m.map(k=>{let G={kind:"message",...k};if(G.role==="assistant"&&(G.text||"").trim()&&G.tools?.length){let rr=Go(G.agent).cliTools,Rs=G.tools.filter(or=>!rr.has(or));return{...G,tools:Rs.length?Rs:void 0}}return G}).map(k=>{if(k.role!=="user")return k;let G=za(k.text);return G?G.kind==="suppress"?null:{...k,note:G,text:gg(G)}:k}).filter(Boolean).filter(k=>!(k.role==="user"&&r.has((k.text||"").trim()))).filter(k=>!dy(k)).filter(k=>!fy(k)).filter(k=>!ly(k)).filter(k=>!Ou(k)).filter(k=>k.role!=="assistant"||!!(k.text||"").trim()||!!k.images?.length).filter(k=>!Ip(k,d)),y=h.filter(k=>k.failed&&k.text===El);if(y.length){let k=new Map;for(let G of y){if(!k.has(G.box)){let or=[...(await ft(G.box,{lines:xt}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(Di=>Di[1]);k.set(G.box,or.length?or[or.length-1]:null)}let rr=k.get(G.box);rr&&(G.text=`\u25A0 ${rr}`)}}let b=await Promise.all(n.boxes.map(k=>Yh(k,e)));await hg(n,b.flat()),await pg(n,h),await Pw(n),await ym(n,h).catch(()=>{});for(let k of n.boxes)k.agent==="codex"&&k.state==="live"&&await kg(n,k),k.agent==="cursor"&&k.state==="live"&&await Tg(n,k);let S=await Promise.all(n.boxes.map(k=>Xh(k,e)));await mg(n,S.flat().filter(k=>!Nu(k.box,k.ts,u)));let I=await L(n),M=c&&i.viewId?i.viewId:null,z=Kw(I),$=Zo(I).filter(k=>!(k.kind==="message"&&(dy(k)||fy(k)||ly(k)||Ou(k)))&&!(k.kind==="view-card"&&M&&k.viewId===M)),F=Gw($,z),T=$.filter(k=>!(k.kind==="message"&&k.role==="assistant"&&!F.has(k)&&Nu(k.box,k.ts,z))),C=du(I,T).map(k=>({kind:"command-batch",id:k.id,ts:k.pending?k.ts:(k.anchorTs??k.ts)-1,anchorTs:k.anchorTs,anchorBox:k.anchorBox,agent:k.agent,boxRole:k.boxRole,commands:k.commands,pending:k.pending})).filter(k=>!Nu(k.anchorBox,k.anchorTs??k.ts,u)),D=[...T,...C].filter(k=>k.ts!=null).sort((k,G)=>k.ts-G.ts),it=GI(n),ot=it.size?D.filter(k=>k.kind==="event"||!it.has(k.box??k.anchorBox)):D,A=n.boxes.find(k=>k.name===n.leadBox)||null,g=A?await xo(A.cwd,A.sessionId,{fallbackNewest:!1}):null;if(!g&&A){let k=[...I].reverse().find(G=>G.type==="context.usage"&&G.boxId===A.name);k?.payload?.window&&(g={pct:k.payload.pct??(k.payload.occupancy!=null?k.payload.occupancy/k.payload.window:null),occupancy:k.payload.occupancy??null,window:k.payload.window,windowKnown:!0,model:k.payload.model||null})}let w=[...m].reverse().find(k=>k.role==="assistant"&&k.model),_=w?.model||null,v=[...n.events||[]].reverse().find(k=>k.type==="model-change"),O=g?.model||_;v&&(!w?.ts||v.at>=w.ts)&&(O=v.to);let E=A?await ft(A.name,{lines:xt}).catch(()=>""):"",N=A&&Mr(A.agent,E)?await Ze(A.name).catch(()=>E):E,nt=A?Jn(A,N):null;!nt&&A&&!Ee(A.agent,E)&&!py(n.id).length&&(nt=ql(ot,{leadBoxId:A.name,provider:A.agent,isAnswered:k=>I.some(G=>G.type==="prompt.answered"&&G.payload?.promptId===k)})),nt&&Bw(n,A,nt);let Z=ot.filter(k=>!(k.kind==="message"&&k.role==="assistant"&&k.ask&&!String(k.text||"").trim()&&!k.images?.length)),wt=!!A&&!nt&&Ee(A.agent,E);A&&!A.readyAt&&(wt||nt||E&&ta(A.agent,E))&&(A.readyAt=Date.now(),await V());let Ct=!!A&&!A.readyAt&&Date.now()-(n.createdAt||0)<KI;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&A&&ta(A.agent,E)&&!nt){let k=await W(),G=Y(k,n.id);G?.setup&&!G.setup.connectedAt&&(G.setup.connectedAt=Date.now(),n.setup.connectedAt=G.setup.connectedAt,await R(G,{type:"provider.connected",actor:"system",source:"pane",boxId:A.name,provider:A.agent,payload:{provider:A.agent,ts:G.setup.connectedAt}}).catch(()=>{}),await V())}let Vt=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,Zt=null;if(c){let k=Date.now()-i.startedAt;if(i.viewId&&!wt){let G=ha(n.id);G&&(lt(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:G.finishedAt}),await V())}else if(nt||k>lm&&!wt&&!i.viewId||k>Pc){let G=nt?"needs-input":k>Pc?"timeout":"no-view";ha(n.id,{error:G})&&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:G}}).catch(()=>{})}else Zt={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:Z,context:g?{pct:g.pct,occupancy:g.occupancy,window:g.window,windowKnown:g.windowKnown,model:g.model}:null,models:{current:O,options:Rw(A?.agent)},pendingPrompt:nt,working:wt,stopping:eR(n.id),leadStarting:Ct,handoff:Vt,view:Zt,queued:py(n.id).filter(k=>k.kind!=="ship-notify"&&k.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function GI(t){let e=new Set;for(let n of t.boxes||[])n.role==="subtask"&&n.name&&e.add(n.name);for(let n of Object.values(t.subtasks||{}))n?.box&&e.add(n.box);return e}async function Ty(t,e){let n=await tt(t);if(!n)return null;let r=Xr(t).find(f=>f.id===e)||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 L(n),a=Zo(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=du(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 ft(o,{lines:xt}).catch(()=>"");u=Ee(l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Hu(t,{promptId:e,answers:n,text:r}={}){let o=Y(await W(),t);if(!o)throw new Error(`track "${t}" not found`);ui(t,"prompt-answer");let s=o.boxes.find(d=>d.name===o.leadBox);if(!s)throw new Error(`track "${t}" has no lead box`);if(!await Q(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Ll(s);if(i||(i=await VI(o,s)),!i)throw new Error("no pending prompt to answer");if(e&&e!==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 d=typeof r=="string"?r.trim():"";if(!d)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(d)||d.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await St(o.leadBox,d,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await bt(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let d=hw(i,n||[],typeof r=="string"?r:"");if(!d)throw new Error("no answer selected \u2014 pick an option or reply in chat");await oe(o.id,d,{kind:"prompt-answer"}),c={mode:"say",value:d,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let d=cw(i,n||[]);await bt(o.leadBox,d,{kind:"answer-prompt"}),c={mode:"keys",value:d.join(" "),count:d.length}}else{let d=Yf(i,n||[]);await St(o.leadBox,d,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:d,count:1}}if(i.answerMode==="say"){let d=i.questions||[];for(let p=0;p<d.length;p++){let m=d[p],h=(Array.isArray(n?.[p])?n[p]:[]).map(y=>m.options?.[y]).filter(Boolean).map(y=>({label:y.label,description:y.description||""}));nu(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:p,questionTotal:i.questionTotal??d.length,header:m.header||"",question:m.question||"",multiSelect:!!m.multiSelect,chosen:h})}return await q({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=(Array.isArray(n?.[0])?n[0]:[]).map(d=>l.options?.[d]).filter(Boolean).map(d=>({label:d.label,description:d.description||""})),f=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:u,...f?{control:f}:{}},await q({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),nu(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function VI(t,e){if(!e)return null;let n=await L(t).catch(()=>[]);return ql(Zo(n),{leadBoxId:e.name,provider:e.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function Ay(t,{promptId:e,questionIndex:n}={}){let r=Y(await W(),t);if(!r)throw new Error(`track "${t}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${t}" has no lead box`);if(!await Q(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Ll(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(e&&e!==a.promptId)throw new Error("prompt changed since it was shown \u2014 reload and try again");let c=lw(a,n);return c.length?(await bt(r.leadBox,c,{kind:"navigate-prompt"}),await q({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}}var zI=1500,as=new Map;function Wu(t){let e=as.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},as.set(t,e)),e}function py(t){return(as.get(t)?.items??[]).map(e=>({...e}))}function li(t){let e=as.get(t);if(!e)return[];let n=e.items.splice(0);return e.idleSince=null,e.timer&&(clearInterval(e.timer),e.timer=null),n}async function Ey(t){let e=li(t),n=e.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=Y(await W(),t);if(o)return r=cs(o,n),await V(),{removed:e,restored:r,draftRev:o.draftRev||0}}return{removed:e,restored:r,draftRev:null}}function _y(t){let e=Wu(t);e.timer||(e.timer=setInterval(()=>JI(t).catch(()=>{}),zI))}async function JI(t){let e=as.get(t);if(!(!e||e.flushing)){if(!e.items.length){e.timer&&(clearInterval(e.timer),e.timer=null);return}e.flushing=!0;try{let n=Y(await W(),t);if(!n||n.archivedAt){li(t);return}let r=ms(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){Wr(e,!1);return}let o=await Q(n.leadBox);if(!o){Wr(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox),a=await ft(n.leadBox,{lines:xt}).catch(()=>"");if(Qs(s?.agent,a)){Wr(e,!1);return}if(Mr(s?.agent,a)){let c=await Ze(n.leadBox).catch(()=>a);if(Jn(s,c)){Wr(e,!1);return}}if(!Wr(e,!0))return;let i=e.items.shift();e.idleSince=null,await Iy(n,s,o,i.text,i.kind??null,i.attachments??null),await V()}finally{e.flushing=!1}}}async function Iy(t,e,n,r,o=null,s=null){let a=is.get(t.id)||0,i=r,c=!1;t.pendingHandoffHydrate&&(i=MI(r,t.pendingHandoffHydrate.artifact||ci),delete t.pendingHandoffHydrate,c=!0);let l=Array.isArray(s)?s.filter(f=>f&&f.rel):[];if(l.length){let f=cy(l.map(d=>d.rel));i=i.trim()?`${i.trim()}
|
|
164
|
+
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 ${xi} 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.`,GR=(t,e,n,r)=>`A subtask box you delegated has finished: "${t}". Its changes are now merged into your working tree (${e.files} file${e.files===1?"":"s"}, +${e.insertions} \u2212${e.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."),_e=null,nd=null;async function H(){return _e||(nd||=(async()=>{try{_e=JSON.parse(await F.readFile(ab,"utf8"))}catch{_e={tracks:[]}}finally{nd=null}return _e})(),nd)}async function X(){await Lt(ab,_e)}var tt=(t,e)=>t.tracks.find(n=>n.id===e)||null,rd=Promise.resolve();function VR(t,e){let n=JSON.stringify({trackId:t,...e})+`
|
|
165
|
+
`;return rd=rd.then(()=>F.mkdir(Xe,{recursive:!0})).then(()=>F.appendFile(FR,n)).catch(()=>{}),rd}var dt=(t,e,n={})=>{let r={at:Date.now(),type:e,...n};return t.events.push(r),VR(t.id,r),Sg(t,r),r};yp(async({sessionName:t,from:e,to:n,version:r,status:o,error:s})=>{let i=(await H()).tracks.find(c=>c.leadBox===t||(c.boxes||[]).some(l=>l.name===t));i&&(dt(i,"codex-update",{box:t,from:e,to:r||n,status:o,...s?{error:s}:{}}),await X().catch(()=>{}))});var od=(t,e,n)=>e!=null&&n.some(r=>t===r.box&&e>=r.from&&e<=r.to),zR=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);function cb(t){if(!t)return null;let e=typeof t=="string"?[t]:Array.isArray(t?.codes)&&t.codes.length?t.codes:t?.code?[t.code]:[],n=[...new Set(e.filter(s=>zR.has(s)))];if(!n.length)return null;let r=typeof t=="object"?t?.note:null,o=r&&String(r).slice(0,500).trim()||null;return{code:n[0],...n.length>1?{codes:n}:{},note:o}}async function lb(t,e,n,{kind:r}={}){let o=await rt(e);if(!o)throw new Error(`box "${e}" is not running`);let s=As(o.agent);await ln({cwd:o.cwd,sessionId:o.sessionId,sessionName:e,agent:o.agent,agentCmd:s}),await _t(e,n,{...Ce(o.agent),kind:r||"orchestration"}),await G({flow:"orchestration",phase:"sent",trackId:t,sessionName:e,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await H(),i=tt(a,t);if(!i)return;let c=i.boxes.find(l=>l.name===e);dt(i,"orchestration-prompt",{box:e,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await X()}var cr=new Set(["merged","conflict","canceled","failed","handed-off"]),tb=new Map;async function JR(t){let e=U.join(ba(t),`${t.id}.ndjson`),n;try{n=await F.stat(e)}catch{return null}let r=tb.get(t.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await L(t)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return tb.set(t.id,{mtimeMs:n.mtimeMs,at:o}),o}async function XR(t,e){let n=e.find(r=>r.name===t.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Iu(n.name,n.agent)}async function Ye(t,e=null){let n=e??new Map((await Oe()).map(i=>[i.name,i])),r=t.boxes.map(i=>{let c=n.get(i.name),l=cr.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([JR(t),XR(t,r)]);return{...t,boxes:r,lastResponseAt:o,working:s,prompted:a,archiving:Si.has(t.id)}}var YR=5e3;function ZR(t,e,n){let r=[...t].sort((l,u)=>l.createdAt-u.createdAt),o=[...e].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-YR,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 ub(t){let e=new Map;for(let r of t.tracks)for(let o of r.boxes)o.agent==="claude"&&o.cwd&&(e.has(o.cwd)||e.set(o.cwd,[]),e.get(o.cwd).push({track:r,box:o}));let n=!1;for(let[r,o]of e){let s=o.map(d=>d.box.sessionId),a=fp(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=ZR(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 rp(d.box,d.sessionId),n=!0}}return n}async function at(){let t=await H();await ub(t)&&await X();let e=new Map((await Oe()).map(n=>[n.name,n]));return Promise.all(t.tracks.map(n=>Ye(n,e)))}async function db(t,e){let n=await H();if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===t);if(o)return{track:r,box:o}}if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===e);if(o)return{track:r,box:o}}return null}async function fb(t){let e=go(t);if(!e)return null;let n=await H();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&lf(o.hookTokenHash,e))return{track:r,box:o};return null}async function et(t){let e=await H(),n=tt(e,t);return n?(await ub(e)&&await X(),Ye(n)):null}var QR={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};function eb(t){if(t.role!=="user")return!1;let e=(t.text||"").trim();return(QR[t.agent]||[]).some(n=>n.test(e))}var t$=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];function nb(t){let e=(t.text||"").trim();return e?t$.some(n=>n.test(e)):!1}var e$=10*6e4;async function fd(t,e={}){let n=await et(t);if(!n)return null;let r=new Set([...n.events.filter(k=>k.type==="orchestration-prompt"&&k.prompt).map(k=>String(k.prompt).trim()),...n.events.filter(k=>k.type==="said"&&k.text).map(k=>String(k.text).trim())]),o=Es(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=rl(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...Fm(n),...l],d={windows:[...tm(n),...em(n,Sa),...a,...u]},m=(await Promise.all(n.boxes.map(k=>Ql(k,e)))).flat(),w=m.map(k=>{let P={kind:"message",...k};if(P.role==="assistant"&&(P.text||"").trim()&&P.tools?.length){let $t=ns(P.agent).cliTools,Qt=P.tools.filter(Tt=>!$t.has(Tt));return{...P,tools:Qt.length?Qt:void 0}}return P}).map(k=>{if(k.role!=="user")return k;let P=fi(k.text);return P?P.kind==="suppress"?null:{...k,note:P,text:ay(P)}:k}).filter(Boolean).filter(k=>!(k.role==="user"&&r.has((k.text||"").trim()))).filter(k=>!eb(k)).filter(k=>!nb(k)).filter(k=>!Qy(k)).filter(k=>!ed(k)).filter(k=>k.role!=="assistant"||!!(k.text||"").trim()||!!k.images?.length).filter(k=>!nm(k,d)),y=w.filter(k=>k.failed&&k.text===Vl);if(y.length){let k=new Map;for(let P of y){if(!k.has(P.box)){let Tt=[...(await mt(P.box,{lines:Et}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(sf=>sf[1]);k.set(P.box,Tt.length?Tt[Tt.length-1]:null)}let $t=k.get(P.box);$t&&(P.text=`\u25A0 ${$t}`)}}let b=await Promise.all(n.boxes.map(k=>Ew(k,e)));await oy(n,b.flat()),await ny(n,w),await vg(n),await Vm(n,w).catch(()=>{});for(let k of n.boxes)k.agent==="codex"&&k.state==="live"&&await uy(n,k),k.agent==="cursor"&&k.state==="live"&&await my(n,k);let S=await Promise.all(n.boxes.map(k=>Aw(k,e)));await ry(n,S.flat().filter(k=>!od(k.box,k.ts,u)));let _=await L(n),O=c&&i.viewId?i.viewId:null,nt=Cg(_),R=ds(_).filter(k=>!(k.kind==="message"&&(eb(k)||nb(k)||Qy(k)||ed(k)))&&!(k.kind==="view-card"&&O&&k.viewId===O)),C=Ng(R,nt),v=R.filter(k=>!(k.kind==="message"&&k.role==="assistant"&&!C.has(k)&&od(k.box,k.ts,nt))),D=Pu(_,v).map(k=>({kind:"command-batch",id:k.id,ts:k.pending?k.ts:(k.anchorTs??k.ts)-1,anchorTs:k.anchorTs,anchorBox:k.anchorBox,agent:k.agent,boxRole:k.boxRole,commands:k.commands,pending:k.pending})).filter(k=>!od(k.anchorBox,k.anchorTs??k.ts,u)),B=ly([...v,...D].filter(k=>k.ts!=null).sort((k,P)=>k.ts-P.ts)),lt=n$(n),q=lt.size?B.filter(k=>k.kind==="event"||!lt.has(k.box??k.anchorBox)):B,T=n.boxes.find(k=>k.name===n.leadBox)||null,g=T?await Oo(T.cwd,T.sessionId,{fallbackNewest:!1}):null;if(!g&&T){let k=[..._].reverse().find(P=>P.type==="context.usage"&&P.boxId===T.name);k?.payload?.window&&(g={pct:k.payload.pct??(k.payload.occupancy!=null?k.payload.occupancy/k.payload.window:null),occupancy:k.payload.occupancy??null,window:k.payload.window,windowKnown:!0,model:k.payload.model||null})}let h=[...m].reverse().find(k=>k.role==="assistant"&&k.model),A=h?.model||null,E=[...n.events||[]].reverse().find(k=>k.type==="model-change"),J=g?.model||A;E&&(!h?.ts||E.at>=h.ts)&&(J=E.to);let W=T?await mt(T.name,{lines:Et}).catch(()=>""):"",it=T&&Ur(T.agent,W)?await an(T.name).catch(()=>W):W,Pt=T?nr(T,it):null;!Pt&&T&&!Co(T.agent,W)&&!ld(n.id).length&&(Pt=du(q,{leadBoxId:T.name,provider:T.agent,isAnswered:k=>_.some(P=>P.type==="prompt.answered"&&P.payload?.promptId===k)})),Pt&&Tg(n,T,Pt);let K=q.filter(k=>!(k.kind==="message"&&k.role==="assistant"&&k.ask&&!String(k.text||"").trim()&&!k.images?.length)),$=!!T&&li(T.name,T.agent,W)&&!Pt;T&&!T.readyAt&&($||Pt||W&&ma(T.agent,W))&&(T.readyAt=Date.now(),await X());let z=!!T&&!T.readyAt&&Date.now()-(n.createdAt||0)<e$;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&T&&ma(T.agent,W)&&!Pt){let k=await H(),P=tt(k,n.id);P?.setup&&!P.setup.connectedAt&&(P.setup.connectedAt=Date.now(),n.setup.connectedAt=P.setup.connectedAt,await I(P,{type:"provider.connected",actor:"system",source:"pane",boxId:T.name,provider:T.agent,payload:{provider:T.agent,ts:P.setup.connectedAt}}).catch(()=>{}),await X())}let xt=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,ht=null;if(c){let k=Date.now()-i.startedAt;if(i.viewId&&!$){let P=Ra(n.id);P&&(dt(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:P.finishedAt}),await X())}else if(Pt||k>jm&&!$&&!i.viewId||k>nl){let P=Pt?"needs-input":k>nl?"timeout":"no-view";Ra(n.id,{error:P})&&await I(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:T?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:P}}).catch(()=>{})}else ht={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:K,context:g?{pct:g.pct,occupancy:g.occupancy,window:g.window,windowKnown:g.windowKnown,model:g.model}:null,models:{current:J,options:gg(T?.agent)},pendingPrompt:Pt,working:$,stopping:d$(n.id),leadStarting:z,handoff:xt,view:ht,queued:ld(n.id).filter(k=>k.kind!=="ship-notify"&&k.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function n$(t){let e=new Set;for(let n of t.boxes||[])n.role==="subtask"&&n.name&&e.add(n.name);for(let n of Object.values(t.subtasks||{}))n?.box&&e.add(n.box);return e}async function pb(t,e){let n=await et(t);if(!n)return null;let r=ao(t).find(f=>f.id===e)||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 L(n),a=ds(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=Pu(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 mt(o,{lines:Et}).catch(()=>"");u=li(o,l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function pd(t,{promptId:e,answers:n,text:r}={}){let o=tt(await H(),t);if(!o)throw new Error(`track "${t}" not found`);Ri(t,"prompt-answer");let s=o.boxes.find(d=>d.name===o.leadBox);if(!s)throw new Error(`track "${t}" has no lead box`);if(!await rt(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await au(s);if(i||(i=await r$(o,s)),!i)throw new Error("no pending prompt to answer");if(e&&e!==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 d=typeof r=="string"?r.trim():"";if(!d)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(d)||d.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await _t(o.leadBox,d,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await vt(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let d=qw(i,n||[],typeof r=="string"?r:"");if(!d)throw new Error("no answer selected \u2014 pick an option or reply in chat");await le(o.id,d,{kind:"prompt-answer"}),c={mode:"say",value:d,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let d=Bw(i,n||[]);await vt(o.leadBox,d,{kind:"answer-prompt"}),c={mode:"keys",value:d.join(" "),count:d.length}}else{let d=Ap(i,n||[]);await _t(o.leadBox,d,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:d,count:1}}if(i.answerMode==="say"){let d=i.questions||[];for(let p=0;p<d.length;p++){let m=d[p],w=(Array.isArray(n?.[p])?n[p]:[]).map(y=>m.options?.[y]).filter(Boolean).map(y=>({label:y.label,description:y.description||""}));Eu(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:p,questionTotal:i.questionTotal??d.length,header:m.header||"",question:m.question||"",multiSelect:!!m.multiSelect,chosen:w})}return await G({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=(Array.isArray(n?.[0])?n[0]:[]).map(d=>l.options?.[d]).filter(Boolean).map(d=>({label:d.label,description:d.description||""})),f=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:u,...f?{control:f}:{}},await G({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),Eu(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function r$(t,e){if(!e)return null;let n=await L(t).catch(()=>[]);return du(ds(n),{leadBoxId:e.name,provider:e.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function mb(t,{promptId:e,questionIndex:n}={}){let r=tt(await H(),t);if(!r)throw new Error(`track "${t}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${t}" has no lead box`);if(!await rt(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await au(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(e&&e!==a.promptId)throw new Error("prompt changed since it was shown \u2014 reload and try again");let c=jw(a,n);return c.length?(await vt(r.leadBox,c,{kind:"navigate-prompt"}),await G({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}}var o$=1500,bs=new Map;function md(t){let e=bs.get(t);return e||(e={items:[],timer:null,flushing:!1,idleSince:null},bs.set(t,e)),e}function ld(t){return(bs.get(t)?.items??[]).map(e=>({...e}))}function Ii(t){let e=bs.get(t);if(!e)return[];let n=e.items.splice(0);return e.idleSince=null,e.timer&&(clearInterval(e.timer),e.timer=null),n}async function hb(t){let e=Ii(t),n=e.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=tt(await H(),t);if(o)return r=no(o,n),await X(),{removed:e,restored:r,draftRev:o.draftRev||0}}return{removed:e,restored:r,draftRev:null}}function wb(t){let e=md(t);e.timer||(e.timer=setInterval(()=>s$(t).catch(()=>{}),o$))}async function s$(t){let e=bs.get(t);if(!(!e||e.flushing)){if(!e.items.length){e.timer&&(clearInterval(e.timer),e.timer=null);return}e.flushing=!0;try{let n=tt(await H(),t);if(!n||n.archivedAt){Ii(t);return}let r=Es(t);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){ar(e,!1);return}let o=await rt(n.leadBox);if(!o){ar(e,!1);return}let s=n.boxes.find(c=>c.name===n.leadBox);if(as(s?.agent)){ar(e,!1);return}let a=await mt(n.leadBox,{lines:Et}).catch(()=>"");if(pa(s?.agent,a)){ar(e,!1);return}if(Ur(s?.agent,a)){let c=await an(n.leadBox).catch(()=>a);if(nr(s,c)){ar(e,!1);return}}if(!ar(e,!0))return;let i=e.items.shift();e.idleSince=null,await yb(n,s,o,i.text,i.kind??null,i.attachments??null),await X()}finally{e.flushing=!1}}}async function gb(t,e,n,r){let o=as(e?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(no(t,[n]),await X()),ig(t.id,o))try{await I(t,{type:"usage.cap.hit",actor:"system",provider:e?.agent??null,boxId:t.leadBox,payload:{...o,blockedKind:r??null}})}catch{}let s=new Error(`${e?.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 yb(t,e,n,r,o=null,s=null){await gb(t,e,r,o);let a=ks.get(t.id)||0,i=r,c=!1;t.pendingHandoffHydrate&&(i=WR(r,t.pendingHandoffHydrate.artifact||_i),delete t.pendingHandoffHydrate,c=!0);let l=Array.isArray(s)?s.filter(f=>f&&f.rel):[];if(l.length){let f=Zy(l.map(d=>d.rel));i=i.trim()?`${i.trim()}
|
|
166
166
|
|
|
167
|
-
${f}`:f}let u=
|
|
167
|
+
${f}`:f}let u=dt(t,"said",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,text:r,...o?{kind:o}:{},...l.length?{attachments:l}:{}});ir(t,null);try{if(await ln({cwd:n.cwd,sessionId:n.sessionId,sessionName:t.leadBox,agent:n.agent,agentCmd:As(n.agent)}),(ks.get(t.id)||0)!==a){!o&&r.trim()&&(no(t,[r]),await X());let f=new Error("delivery stopped \u2014 the message was handed back as the draft");throw f.interrupted=!0,f}await _t(t.leadBox,i,{...Ce(e?.agent),kind:c?"lead-handoff-first-prompt":"say"})}catch(f){throw dt(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:u.at,text:r,...f?.interrupted?{reason:"interrupted"}:{}}),f}await x$(t,e,i)||(await Sb(t.leadBox,e?.agent,i.length+8).catch(()=>{}),dt(t,"unsaid",{box:t.leadBox,agent:e?.agent,boxRole:e?.role,saidTs:u.at,text:r,reason:"unsubmitted"}),o||no(t,[r])),c&&await G({flow:"lead-handoff",phase:"first-prompt",trackId:t.id,sessionName:t.leadBox,agent:e?.agent,userTextLen:r.length})}function bb(t,{actions:e}={}){md(t).items.push({id:Ts(),text:Pg(e),ts:Date.now(),kind:"ship-notify"}),wb(t)}async function le(t,e,{kind:n=null,attachments:r=null}={}){let o=Array.isArray(r)?r.filter(d=>d&&typeof d.rel=="string"):[];if((typeof e!="string"||!Hr(e))&&!o.length)throw new Error("text (string) or an attachment required");let s=tt(await H(),t);if(!s)throw new Error(`track "${t}" not found`);if(s.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it before driving it`);let a=s.boxes.find(d=>d.name===s.leadBox),i=Hr(e);await gb(s,a,i,n),Ri(t,"say");let c=await rt(s.leadBox);if(!c&&await yd(s)&&(await X(),c=await rt(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=md(t),u=await mt(s.leadBox,{lines:Et}).catch(()=>""),f=l.items.length>0||pa(a?.agent,u);if(!f&&Ur(a?.agent,u)){let d=await an(s.leadBox).catch(()=>u);f=!!nr(a,d)}if(f){let d={id:Ts(),text:i,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(d),wb(t),ir(s,null),await X(),{ok:!0,leadBox:s.leadBox,queued:!0,id:d.id,position:l.items.length}}return await yb(s,a,c,i,n,o),await X(),{ok:!0,leadBox:s.leadBox}}var rb="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.";async function kb(t,{hint:e}={}){let n=tt(await H(),t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it first`);let r=rl(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(e||"").trim()?String(e).trim().slice(0,40):"preview";Lm(n.id,{name:s,box:n.leadBox});let a=String(e||"").trim()?`${rb}
|
|
168
168
|
|
|
169
|
-
What to preview: ${String(e).trim()}`:
|
|
169
|
+
What to preview: ${String(e).trim()}`:rb;try{await le(t,a,{kind:"spin-up-view"})}catch(i){throw Ra(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function hd(t){let e=tt(await H(),t);if(!e)throw new Error(`track "${t}" not found`);if(e.archivedAt)throw new Error(`track "${t}" is archived`);ks.set(t,(ks.get(t)||0)+1);let n=Ii(t),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=eo.get(t);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=no(e,r),await X()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:e.draftRev||0}}let s=f$(t,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,_n(a$)]);let a=tt(await H(),t)||e;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}}var a$=4e3,i$=3,c$=[300,350,400],l$=1e3,u$=12,eo=new Map,ks=new Map;function d$(t){let e=eo.get(t);return!!e&&!e.canceled}function Ri(t,e){let n=eo.get(t);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=e||null)}function xb(t){Ri(t,"track-ended"),ks.delete(t),Ii(t)}function f$(t,{droppedCount:e,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}),eo.set(t,r),(async()=>{let s=tt(await H(),t),a=s?.boxes.find(y=>y.name===s.leadBox),i=s?await rt(s.leadBox):null,c=!1,l=!1;if(i){t:for(let y=0;y<i$&&!r.canceled;y++){await vt(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let b of c$){await _n(b);let S=await mt(s.leadBox,{lines:Et}).catch(()=>"");if(!Co(a?.agent,S)){c=!0;break t}}}if(!c){l=!0;for(let y=0;y<u$&&!r.canceled&&(await _n(l$),!r.canceled);y++){let b=await mt(s.leadBox,{lines:Et}).catch(()=>"");if(!Co(a?.agent,b)){c=!0;break}await vt(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=tt(await H(),t)||s,f=u?.boxes.find(y=>y.name===u.leadBox)||a,d=null,p=null;u&&f&&i&&c&&!r.canceled&&(d=await w$(u,f).catch(()=>null),d||(p=await b$(u,f).catch(()=>null)));let m=u&&i&&!r.canceled?await m$(u).catch(()=>null):null,w=[d||p,...r.queuedTexts].filter(Boolean);u&&w.length&&(r.restored=no(u,w),await X()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await p$(u||s,f,{ts:r.startedAt,result:r.result,dropped:e,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 G({flow:"interrupt",phase:r.result==="gave-up"?"failed":"done",trackId:t,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"),eo.get(t)===r&&eo.delete(t),o()}),r}async function p$(t,e,n){await I(t,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:t.leadBox,provider:e?.agent||null,payload:n}).catch(()=>{})}async function m$(t){let e=Ju(await L(t));return e?.offerId?(await I(t,{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:e.box,payload:{ts:Date.now(),offerId:e.offerId,action:"dismiss",actions:null,reason:"interrupt"}}),e.offerId):null}var h$=[250,400,500,600];async function w$(t,e){if(e?.agent!=="claude")return null;let n=[...t.events||[]].reverse().find(r=>r.type==="said"&&r.text);if(!n)return null;for(let r of h$){await _n(r);let o=await mt(t.leadBox,{lines:Et}).catch(()=>""),s=rs(e.agent,o);if(s&&iu(n.text,s))return await Sb(t.leadBox,e.agent,n.text.length+8),dt(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:n.at,text:n.text}),n.text}return null}var g$=3e4,y$=[350,650,900];async function b$(t,e){if(e?.agent!=="claude")return null;let n=t.events||[],r=[...n].reverse().find(s=>s.type==="said"&&s.text);if(!r||r.kind||Date.now()-r.at>g$||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!St(r.text))return null;for(let s of y$){await _n(s);let a;try{a=await Ql(e,{})}catch{return null}if(Fw(a,r))return null;let i=await mt(t.leadBox,{lines:Et}).catch(()=>"");if(rs(e.agent,i))return null}return dt(t,"unsaid",{box:t.leadBox,agent:e.agent,boxRole:e.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}var k$=[250,450,700],sd=2;async function x$(t,e,n){if(e?.agent!=="claude")return!0;for(let r=0;r<=sd;r++){let o=!1;for(let s of k$){await _n(s);let a=await mt(t.leadBox,{lines:Et}).catch(()=>""),i=Uw(e.agent,n,a,{busy:Co(e.agent,a)});if(i==="stuck"){o=!0;break}if(i==="submitted")return!0}if(!o)return!0;r<sd&&(await vt(t.leadBox,["Enter"],{kind:"submit-retry"}),G({flow:"say",phase:"submit-retry",trackId:t.id,sessionName:t.leadBox,agent:e.agent,attempt:r+1,textHead:n.slice(0,120)}).catch(()=>{}))}return G({flow:"say",phase:"submit-failed",trackId:t.id,sessionName:t.leadBox,agent:e.agent,retries:sd,textHead:n.slice(0,120)}).catch(()=>{}),!1}async function Sb(t,e,n){for(let r=0;r<3;r++){await ca(t,n,{kind:"unsend-wipe"}).catch(()=>{}),await _n(200);let o=await mt(t,{lines:Et}).catch(()=>""),s=rs(e,o);if(!s)return!0;n=s.length+8}return!1}function ir(t,e,{source:n}={}){t.draftRev=(t.draftRev||0)+1;let r=typeof e=="string"?e:"";return r.trim()?t.draft={text:r,updatedAt:Date.now(),...n?{source:n}:{}}:delete t.draft,t.draft||null}function no(t,e){let n=[],r=o=>n.some(s=>St(s).includes(St(o)));for(let o of[t.draft?.text,...e])o&&o.trim()&&!r(o)&&n.push(o);return n.length?ir(t,n.join(`
|
|
170
170
|
|
|
171
|
-
`),{source:"restore"})?.text??null:null}async function
|
|
171
|
+
`),{source:"restore"})?.text??null:null}var S$=12e4;function v$(t,e){let n=St(e);if(!n)return!1;if(ld(t.id).some(o=>St(o.text)===n))return!0;let r=t.events||[];for(let o=r.length-1;o>=0;o--){let s=r[o];if(typeof s.at=="number"&&Date.now()-s.at>S$)break;if(!(s.type!=="said"||!s.text||St(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function vb(t,e,{rev:n=null}={}){if(typeof e!="string")throw new Error("text (string) required");let r=tt(await H(),t);if(!r)throw new Error(`track "${t}" not found`);let o=Hr(e);if(typeof n=="number"&&n<(r.draftRev||0)&&o&&v$(r,o))return{ok:!0,draft:r.draft||null,rev:null,ignored:"already-sent"};if(typeof n=="number"&&n<(r.draftRev||0)&&r.draft?.source==="restore"){let a=r.draft.text;o?St(o).includes(St(a))?ir(r,o):ir(r,`${a}
|
|
172
172
|
|
|
173
|
-
${o}`,{source:"restore"}):
|
|
173
|
+
${o}`,{source:"restore"}):ir(r,a,{source:"restore"})}else ir(r,o);return await X(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Tb(t,e){if(typeof e!="string"||!e.trim())throw new Error("model (string) required");let n=e.trim(),r=tt(await H(),t);if(!r)throw new Error(`track "${t}" not found`);let o=await rt(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=yg(a,n),l=[...r.events||[]].reverse().find(f=>f.type==="model-change")?.to??null;l||(l=(await Oo(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await ln({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:As(o.agent)});let u=await bg(a,r.leadBox,i);return dt(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await X(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function Ab(t){for(let e of t){if(!e||!await Ya(e))continue;let n=await Vw(e).catch(()=>null);if(!n)continue;if((await F.stat(U.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function qt(t){let e=t;return e?e.kind==="multi"?Array.isArray(e.repos)?e.repos.filter(Boolean):[]:e.base?[{name:U.basename(e.base),dir:e.dir??null,base:e.base,baseRef:e.baseRef??null,repo:e.repo??null}]:[]:[]}async function us(t){let e=[];for(let n of qt(t?.workspace)){if(!n?.dir)continue;let r=await hu(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await wu(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&e.push({...n,dirty:r,ahead:o})}return e}async function Eb(t){let e=t;typeof e=="string"&&!U.isAbsolute(e)&&/^[^/\s]+\/[^/\s]+$/.test(e)&&(e=await bo(e)??await dc(e));let n=await Ab([e]);if(!n)throw new Error(`tagged repo "${t}" is not a usable base (need a primary checkout or a synced owner/name)`);return n}async function T$(t,e,n,r){let o=`track/${xd(e)}-${t}`,s=[];try{await F.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await Eb(i);if(s.some(p=>p.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await ot(c).catch(()=>null),u=U.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let p=2;a.has(u);p++)u=`${U.basename(c)}-${p}`;a.add(u);let f=U.join(n,u),d=await Za(c);await Wr(c,f,o,d),s.push({name:u,dir:f,base:c,baseRef:d,repo:l})}return await $i(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await ee(i.base,i.dir).catch(()=>{});throw await F.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}var _b=t=>({branch:t.branch,kind:t.kind,repos:(t.repos??[]).filter(Boolean).map(({name:e,base:n,baseRef:r,repo:o})=>({name:e,base:n,baseRef:r,repo:o}))});async function Ib(t,e){let n=await H(),r=tt(n,t);if(!r)throw new Error(`track "${t}" 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 Eb(typeof e=="string"?e.trim():e);if((o.repos??[]).some(d=>d?.base===s))throw new Error(`repo already tagged: ${e}`);let a=await ot(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(d=>d?.name).filter(Boolean)]),c=U.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let d=2;i.has(c);d++)c=`${U.basename(s)}-${d}`;let l=U.join(o.dir,c),u=await Za(s);await zw(s,o.branch)?await Jw(s,l,o.branch):await Wr(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 $i(o.dir,o.repos,o.branch),dt(r,"retagged",{action:"add",repo:a??s,name:c,workspace:_b(o)}),await X(),f}async function Rb(t,e){let n=await H(),r=tt(n,t);if(!r)throw new Error(`track "${t}" 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===e||c.repo===e||c.base===e));if(!s)throw new Error(`repo "${e}" 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 ce.values())if(c.trackId===t&&!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 hu(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await wu(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 ee(s.base,s.dir).catch(()=>{}),await F.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await $i(o.dir,o.repos,o.branch),dt(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:_b(o)}),await X(),{removed:s.name}}async function A$(t,e,{repo:n,repos:r,cwd:o}={}){await F.mkdir(cd,{recursive:!0});let s=U.join(cd,t),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return T$(t,e,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!U.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await bo(n)??await dc(n));let i=await Ab([n,o,UR]);if(i){let c=`track/${xd(e)}-${t}`;try{let l=await Za(i);await Wr(i,s,c,l),await ud(s);let u=await ot(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await F.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await F.mkdir(s,{recursive:!0}),await Xw(s),await ud(s),{dir:s,base:null,branch:null,kind:"repo"}}var xs="# bostrat workspace",$b=`${xs}
|
|
174
174
|
|
|
175
175
|
TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
|
|
176
176
|
before substantive work \u2014 it is the shared cross-provider memory for this repo
|
|
@@ -337,9 +337,9 @@ which steps to run (and which to skip). An offer buried mid-message, phrased as
|
|
|
337
337
|
statement, or omitted entirely renders no controls \u2014 so close EVERY completed
|
|
338
338
|
implementation turn this way, every time. When the operator has already asked you
|
|
339
339
|
to commit / push / open the PR, just do it; do not re-offer.
|
|
340
|
-
`,
|
|
341
|
-
`)),
|
|
342
|
-
`);return`${
|
|
340
|
+
`,E$=$b,gs=$b+Kw;async function ud(t){try{await F.writeFile(U.join(t,"AGENTS.md"),gs,{flag:"wx"})}catch{return}await R$(t,["/AGENTS.md"])}var ad=t=>typeof t=="string"&&(t===xs||t.startsWith(`${xs}
|
|
341
|
+
`)),_$="# bostrat multi-repo workspace";function I$(t,e){let n=t.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
|
|
342
|
+
`);return`${_$}
|
|
343
343
|
|
|
344
344
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
345
345
|
git worktree per tagged repo, ALL on the same branch \`${e}\`:
|
|
@@ -351,25 +351,25 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
351
351
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
352
352
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
353
353
|
or preview an app, run it from that app's own repo directory.
|
|
354
|
-
`}async function
|
|
355
|
-
`,`${
|
|
354
|
+
`}async function $i(t,e,n){let r=I$(e,n);for(let o of["CLAUDE.md","AGENTS.md"])await F.writeFile(U.join(t,o),r).catch(()=>{})}var id=gs.replace(`${xs}
|
|
355
|
+
`,`${xs}
|
|
356
356
|
|
|
357
357
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
358
358
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
359
359
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
360
360
|
When working anywhere else, ignore everything below.
|
|
361
|
-
`);async function
|
|
362
|
-
`).includes(s));o.length&&await
|
|
361
|
+
`);async function Ob(t){let{stdout:e}=await LR("git",["-C",t,"rev-parse","--git-path","info/exclude"]);return U.resolve(t,e.trim())}async function R$(t,e){try{let n=await Ob(t);await F.mkdir(U.dirname(n),{recursive:!0});let r=await F.readFile(n,"utf8").catch(()=>""),o=e.filter(s=>!r.split(`
|
|
362
|
+
`).includes(s));o.length&&await F.appendFile(n,(r.endsWith(`
|
|
363
363
|
`)||!r?"":`
|
|
364
364
|
`)+o.join(`
|
|
365
365
|
`)+`
|
|
366
|
-
`)}catch{}}async function
|
|
366
|
+
`)}catch{}}async function $$(t,e){try{let n=await Ob(t),r=await F.readFile(n,"utf8"),o=r.split(`
|
|
367
367
|
`).filter(s=>s!==e);o.length!==r.split(`
|
|
368
|
-
`).length&&await
|
|
369
|
-
`))}catch{}}async function
|
|
368
|
+
`).length&&await F.writeFile(n,o.join(`
|
|
369
|
+
`))}catch{}}async function Mb(){let t={refreshed:0,removed:0,codexGlobal:null};try{await F.mkdir(Xe,{recursive:!0}),await F.writeFile(U.join(Xe,"CLAUDE.md"),E$).catch(()=>{}),await F.writeFile(U.join(Xe,"AGENTS.md"),gs).catch(()=>{});let e=process.env.CODEX_HOME||U.join(process.env.HOME||"/tmp",".codex");if((await F.stat(e).catch(()=>null))?.isDirectory()){let r=U.join(e,"AGENTS.md"),o=await F.readFile(r,"utf8").catch(()=>null);o===null?(await F.writeFile(r,id,{flag:"wx"}).catch(()=>{}),t.codexGlobal="created"):ad(o)&&o!==id&&(await F.writeFile(r,id).catch(()=>{}),t.codexGlobal="refreshed")}for(let r of[cd,ro]){let o=await F.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=U.join(r,s.name),i=U.join(a,"AGENTS.md"),c=await F.readFile(i,"utf8").catch(()=>null);c!==null&&ad(c)&&c!==gs&&(await F.writeFile(i,gs).catch(()=>{}),t.refreshed++);let l=U.join(a,"CLAUDE.md"),u=await F.readFile(l,"utf8").catch(()=>null);u!==null&&ad(u)&&(await F.rm(l,{force:!0}).catch(()=>{}),await $$(a,"/CLAUDE.md"),t.removed++)}}}catch{}return t}async function O$(t){let e=t.workspace;if(!(!e||!e.dir))try{if(e.kind==="multi"){for(let n of qt(e))n.base&&n.dir&&await ee(n.base,n.dir).catch(()=>{});await F.rm(e.dir,{recursive:!0,force:!0})}else e.kind==="worktree"&&e.base?await ee(e.base,e.dir):await F.rm(e.dir,{recursive:!0,force:!0})}catch{}}async function wd({name:t,cwd:e,repo:n,repos:r,agent:o="claude",skipPermissions:s=!0}){if(typeof t!="string"||!t.trim())throw new Error("name (string) required");let a=await Ei(o,s),i=await H(),c=Ts().slice(0,8),l=await A$(c,t,{repo:n,repos:r,cwd:e}),u=new Set(i.tracks.flatMap(m=>(m.boxes||[]).map(w=>w.name))),f=`${t.trim()} lead`;u.has(Rc(f))&&(f=`${t.trim().slice(0,50).trim()} ${c} lead`);let d=await Bn(f,l.dir,{agentCmd:a,agent:o});ha({cwd:d.cwd,sessionName:d.name,agent:o,agentCmd:a});let p={id:c,name:t.trim(),cwd:d.cwd,workspace:l,leadBox:d.name,boxes:[Hb(d,"lead","live")],events:[],createdAt:Date.now()};return dt(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 X(),Ye(p)}var M$=new Set(["claude","codex","cursor"]);async function Cb({provider:t="claude",fresh:e=!1}={}){if(!M$.has(t))throw new Error(`unknown setup provider "${t}" (claude | codex | cursor)`);let r=(await H()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===t&&!u.archivedAt);if(r){if(!e){let u=await Ye(r);if(u.boxes.find(d=>d.name===u.leadBox)?.state==="live")return u}await so(r.id).catch(()=>{})}let o=(await Ei(t,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=Ts().slice(0,8),a=U.join(Xe,"setup",s);await F.mkdir(a,{recursive:!0});let i=await Bn(`Connect ${t} ${s}`,a,{agentCmd:o,agent:t,firstRun:!0});t==="codex"&&ha({cwd:i.cwd,sessionName:i.name,agent:t,agentCmd:o});let c={id:s,name:`Connect ${t}`,kind:"setup",setup:{provider:t,startedAt:Date.now(),connectedAt:null},cwd:i.cwd,workspace:{dir:a,base:null,branch:null,kind:"scratch"},leadBox:i.name,boxes:[Hb(i,"lead","live")],events:[],createdAt:Date.now()};return dt(c,"opened",{agent:t,box:i.name,skipPermissions:!1,trackKind:"setup",name:c.name,cwd:c.cwd,workspace:{base:null,branch:null,kind:"scratch"}}),(await H()).tracks.push(c),await X(),Ye(c)}async function Nb(t,e,n){await Gm(t).catch(()=>{});let r=Ss.get(t);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),Ss.delete(t),e.handoff&&!e.handoff.finishedAt&&(e.handoff.step="error",e.handoff.error=n,e.handoff.finishedAt=Date.now());for(let a of ao(t))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await vd(a).catch(()=>{}),ce.delete(a.id);for(let a of Object.values(e.subtasks||{}))a.finishedAt||(a.step="error",a.error=n,a.canceled=!0,a.finishedAt=Date.now());let o=await H(),s=new Set((o.tracks||[]).filter(a=>a.id!==t&&!a.archivedAt).flatMap(a=>(a.boxes||[]).map(i=>i.name)));for(let a of e.boxes)s.has(a.name)||await pe(a.name).catch(()=>{})}async function Pb(t,{cascadeMocks:e=!0,cascadeRunbooks:n=!1}={}){let r=await H(),o=r.tracks.findIndex(a=>a.id===t);if(o===-1)throw new Error(`track "${t}" not found`);let s=r.tracks[o];return e||await _m(s),xb(t),await Nb(t,s,"track deleted"),n?await oh(t).catch(()=>{}):await rh(t).catch(()=>{}),await xg(),await O$(s),e&&await Im(t).catch(()=>{}),e?await Vy(t).catch(()=>{}):await Gy(s).catch(()=>{}),dt(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await X(),{ok:!0,id:t,name:s.name,boxes:s.boxes.length}}var Si=new Set;async function so(t){let e=await H(),n=tt(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is already archived`);return n.archivedAt=Date.now(),xb(t),dt(n,"archived",{boxes:n.boxes.length}),await X(),n.kind==="setup"?(await Db(t),Ye(n)):(Si.add(t),N$(t).catch(()=>{}).finally(()=>Si.delete(t)),{...await Ye(n),archiving:!0})}var C$=9e4;async function N$(t){try{await P$(t)}catch{}await Db(t).catch(()=>{})}async function Db(t){let e=await H(),n=tt(e,t);if(!(!n||!n.archivedAt)){await Nb(t,n,"track archived");for(let r of n.boxes)cr.has(r.state)||(r.state="stopped");await X()}}async function P$(t){let e=await H(),n=tt(e,t);if(!n||!n.archivedAt)return;let r=null,o=[],s="derived",a=(n.events||[]).some(l=>l.type==="said"),i=n.leadBox?await rt(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await Zp(n.leadBox,i.cwd,{timeoutMs:C$})),s="lead-cli"}catch{}if(r||(r=Cl(n,await L(n).catch(()=>[]))),!r)return;let c={text:r,source:s,at:Date.now()};o.length&&(c.items=o),n.beat=c,await I(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 X()}async function Bb(){let t=await H(),e=0,n=0,r=0;for(let o of t.tracks){if(!o.archivedAt||o.kind==="setup"||o.beat?.text){n++;continue}let s=Cl(o,await L(o).catch(()=>[]));if(!s){r++;continue}o.beat={text:s,source:"derived",at:Date.now()},await I(o,{type:"track.summary.recorded",actor:"orchestrator",source:"orchestrator",payload:{text:s,source:"derived",backfill:!0}}).catch(()=>{}),e++}return await X(),{recorded:e,skipped:n,empty:r}}async function vn(t,e){let n=await Bt(t,e).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await jb(n);if(r.length){for(let o of r)await Jn(o.repo,o.number,!0).catch(()=>{});return!1}try{return await so(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function jb(t){return(await ve().catch(()=>[])).filter(n=>n.trackId===t.trackId&&n.state!=="merged"&&n.state!=="closed"&&n.state!=="lost"&&!(n.repo===t.repo&&n.number===t.number))}async function Tn(t,e){return gd(t,e,"merged")}async function gd(t,e,n="merged"){let r=await Bt(t,e).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 jb(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await qh(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await le(r.trackId,D$(a),{kind:"merge-notify"}),!0}catch{return!1}}var ob={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};function D$(t){let e=i=>`${i.repo?`${i.repo}`:""}#${i.number}${i.title?` "${i.title}"`:""}${i.url?` (${i.url})`:""}`;if(t.length===1){let i=t[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 ${ob[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=t.filter(i=>i.outcome==="merged"),r=t.filter(i=>i.outcome!=="merged"),o=r.length?`${n.length} of the ${t.length} PRs on this Track merged; ${r.length} did not land:`:`All ${t.length} PRs on this Track have merged:`,s=t.map(i=>`- ${e(i)} \u2014 ${ob[i.outcome]??i.outcome}`).join(`
|
|
370
370
|
`),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}
|
|
371
371
|
${s}
|
|
372
|
-
${a}`}async function Vu(t){if(t.archivedAt)return!1;let e=t.boxes.find(r=>r.name===t.leadBox);if(!e||tr.has(e.state)||await Q(e.name))return!1;let n=yy(e.agent,e.sessionId);if(!await Gs(n))return lt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:`agent "${e.agent}" not available on this box`}),!1;try{let r=await On(e.name,e.cwd,{agentCmd:n,agent:e.agent,resumeSessionId:e.sessionId,pinned:e.pinned===!0});return e.hookTokenHash=ao(r.hookToken),ea({cwd:e.cwd,sessionName:e.name,agent:e.agent,agentCmd:n}),e.state="live",lt(t,"rehydrated",{box:e.name,agent:e.agent}),!0}catch(r){return lt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:String(r?.message||r)}),!1}}async function Jy(t){let e=await W(),n=Y(e,t);if(!n)throw new Error(`track "${t}" not found`);if(!n.archivedAt)throw new Error(`track "${t}" is not archived`);return delete n.archivedAt,lt(n,"unarchived",{}),await Vu(n),await V(),bn(n)}async function Xy(){let t=await W(),e=new Set((await $n()).map(s=>s.name)),n=new Set((t.tracks||[]).filter(s=>!s.archivedAt).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of t.tracks||[])if(s.archivedAt)for(let a of s.boxes)e.has(a.name)&&!n.has(a.name)&&(await ce(a.name).catch(()=>{}),lt(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!tr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await V(),r}async function Yy(t){let e=await W(),n=Y(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it before restarting the lead`);let r=n.boxes.find(a=>a.name===n.leadBox);if(!r)throw new Error(`track "${t}" has no lead box`);if(tr.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await Q(n.leadBox)){let a=await ft(n.leadBox,{lines:xt}).catch(()=>"");if(Qs(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await ce(n.leadBox),r.state="stopped"}let s=await Vu(n);if(await V(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return bn(n)}var Zy=(t,e,n)=>({name:t.name,agent:t.agent,role:e,state:n,cwd:t.cwd,sessionId:t.sessionId,pinned:t.pinned===!0,hookTokenHash:ao(t.hookToken),createdAt:Date.now()});function Qy(t){return(!Array.isArray(t.epochs)||t.epochs.length===0)&&(t.epochs=[{agent:t.agent??null,sessionId:t.sessionId??null,pinned:t.pinned===!0,startedAt:t.createdAt??null,endedAt:null}]),t.epochs}var us=new Map,ms=t=>us.get(t)||(xe?Y(xe,t)?.handoff:null)||null;async function oi(t,e){let n=await W(),r=Y(n,t);r&&(r.handoff={...r.handoff||{},...e},await V())}function AR(t,e=Date.now()){let n=t?.handoff;if(!n||n.finishedAt)return null;let r=n.step,o=!!n.toBox&&n.toBox===n.fromBox,s=t.boxes?.find?.(i=>i.name===t.leadBox)||null;return(o?t.leadBox===n.fromBox&&!!n.startedAt&&Array.isArray(s?.epochs)&&s.epochs.some(i=>i.startedAt!=null&&i.startedAt>=n.startedAt):!!n.toBox&&t.leadBox===n.toBox)?{...n,step:"done",finishedAt:e,recovered:`committed before restart (was "${r}")`}:{...n,step:"error",finishedAt:e,error:`interrupted by an agent restart during "${r}"; lead "${t.leadBox}" retained \u2014 retry the handoff`,recovered:"rolled-back"}}async function tb(){let t=await W(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=AR(n);if(!o)continue;e=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await Q(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=Qy(l),d=f[f.length-1],p=d.agent,m=d.sessionId??null;d.endedAt=u,r.reason&&(d.endReason=r.reason);let h=c.agent==="claude"&&!!c.sessionId;f.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:h,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=h,l.state="live",n.pendingHandoffHydrate={artifact:ci,at:u},lt(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 q({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 q({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await ce(i).catch(()=>{}),a&&await ce(a).catch(()=>{})}return e&&await V(),e}async function zu(t,e,n=!0,r=null){let o=us.get(t);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=Y(await W(),t);if(!s)throw new Error(`track "${t}" not found`);ui(t,"handoff");let a=await ii(e,n),i=await Q(s.leadBox);if(!i)throw new Error(`lead box "${s.leadBox}" is not running`);let c={trackId:t,step:"checkpoint",fromBox:s.leadBox,toAgent:e,skipPermissions:n,reason:by(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return us.set(t,c),await oi(t,{step:c.step,fromBox:c.fromBox,toAgent:e,toBox:c.toBox,skipPermissions:n,startedAt:c.startedAt,finishedAt:null,error:null}),ER(c,s,i,a,e).catch(async l=>{c.error=l?.message||String(l),c.finishedAt=Date.now(),console.error(`[lead-handoff ${t}] FAILED at step "${c.step}": ${c.error}`),await oi(t,{step:c.step,error:c.error,finishedAt:c.finishedAt}).catch(()=>{}),q({flow:"lead-handoff",phase:"failed",trackId:t,step:c.step,error:c.error,fromBox:c.fromBox,toAgent:c.toAgent,toBox:c.toBox}).catch(()=>{})}),q({flow:"lead-handoff",phase:"started",trackId:t,fromBox:c.fromBox,toAgent:e,cwd:i.cwd}).catch(()=>{}),c}async function ER(t,e,n,r,o){let s=async(I,M={})=>{t.step=I,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${I}`),await oi(t.trackId,{step:I,toBox:t.toBox}),await q({flow:"lead-handoff",phase:I,trackId:t.trackId,fromBox:t.fromBox,toBox:t.toBox,toAgent:o,sessionName:M.sessionName,...M})};console.log(`[lead-handoff ${t.trackId}] ${t.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await W(),i=Y(a,t.trackId)?.boxes.find(I=>I.name===t.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;await s("checkpoint",{sessionName:t.fromBox}),await Op(t.fromBox),await s("swap",{cwd:n.cwd,agentCmd:r});let f=await ml(t.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:t.fromBox}),await tn({cwd:n.cwd,sessionId:f.sessionId,sessionName:t.fromBox,agent:o,agentCmd:ps(o)});let d=await ft(t.fromBox,{lines:xt}).catch(()=>"");if(!ta(o,d)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let I=yy(c,l,{skipPermissions:t.skipPermissions});await ml(t.fromBox,{agentCmd:I,agent:c,resumeSessionId:l,pinned:u});let M=await W(),z=Y(M,t.trackId);if(z){let $=z.boxes.find(F=>F.name===t.fromBox);$&&($.state="live"),lt(z,"lead-handoff-failed",{box:t.fromBox,fromAgent:c,toAgent:o,reason:t.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:t.startedAt}),await V()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let p=await W(),m=Y(p,t.trackId);if(!m)throw new Error(`track "${t.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let h=m.boxes.find(I=>I.name===t.fromBox);if(!h)throw new Error(`lead box row "${t.fromBox}" missing during handoff commit`);let y=Date.now(),b=Qy(h),S=b[b.length-1];S.endedAt=y,t.reason&&(S.endReason=t.reason),b.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,startedAt:y,endedAt:null}),h.agent=o,h.sessionId=f.sessionId,h.pinned=f.pinned,h.state="live",h.role="lead",m.pendingHandoffHydrate={artifact:ci,at:y},lt(m,"lead-handoff",{from:t.fromBox,to:t.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:b.length-1,singleBox:!0,forced:f.forced||void 0,skipPermissions:t.skipPermissions,reason:t.reason,handoffStartedAt:t.startedAt}),await V(),await s("done",{sessionName:t.fromBox}),await oi(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}var _R=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,IR=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,Vr=H.join(We,"worktrees"),qr=[ss,ri],re=new Map;var Xr=t=>{let e=new Map([...re.values()].filter(r=>r.trackId===t).map(r=>[r.id,r])),n=xe?Y(xe,t):null;if(n?.subtasks)for(let[r,o]of Object.entries(n.subtasks))e.has(r)||e.set(r,o);return[...e.values()].sort((r,o)=>r.startedAt-o.startedAt)},Ju=t=>({id:t.id,trackId:t.trackId,trackName:t.trackName,agent:t.agent,task:t.task,skipPermissions:t.skipPermissions,reason:t.reason||null,step:t.step,failedStep:t.failedStep||null,box:t.box,branch:t.branch,base:t.base,repos:t.repos||null,dir:t.dir,stat:t.stat,files:t.files||null,conflicts:t.conflicts||[],applied:t.applied,error:t.error,canceled:t.canceled,startedAt:t.startedAt,finishedAt:t.finishedAt});async function RR(t,e,n){let r=await W(),o=Y(r,t);o&&(o.subtasks=o.subtasks||{},o.subtasks[e]={...o.subtasks[e]||{},...n},await V())}var zr=t=>RR(t.trackId,t.id,Ju(t));async function Kr(t,e){t.step=e,await zr(t)}function $R(t,e=Date.now()){if(!t||t.finishedAt)return null;let n=t.step;return["snapshot","spawn","working"].includes(n)?{...t,step:"error",finishedAt:e,error:`interrupted by an agent restart during "${n}"; no changes were applied to the lead \u2014 retry the delegation`,recovered:"rolled-back"}:{...t,step:"error",finishedAt:e,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 ${t.branch}`,recovered:"partial-merge"}}function OR(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function CR(t,e,n){if(!OR(n)||re.has(e)||!await Q(n.box).catch(()=>null))return!1;try{await U.access(n.dir)}catch{return!1}let o=await Q(t.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:e,leadBox:t.leadBox,step:"working",canceled:!1,forceMerge:!1};return re.set(e,s),sb(s,o).catch(a=>rb(s,a)),!0}async function eb(){let t=await W(),e=!1;for(let n of t.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(re.has(r))continue;if(await CR(n,r,o)){e=!0,lt(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await q({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=$R(o);if(!a)continue;e=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!tr.has(i.state)&&(i.state="canceled"),lt(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 q({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await wy(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||tr.has(r.state)||re.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;e=!0,r.state="canceled";let o=r.subtaskId?H.join(Vr,`${n.id}-${r.subtaskId}`):null;await q({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await wy(n,r.name,o)}}return e=await MR(t)||e,e&&await V(),e}async function wy(t,e,n){if(e&&await ce(e).catch(()=>{}),!n)return;if(t.workspace?.kind==="multi"){for(let o of jt(t.workspace)){let s=H.join(n,o.name);await Xt(o.base,s).then(()=>!0,()=>!1)||await Xt(o.dir,s).catch(()=>{})}await U.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await Q(t.leadBox).catch(()=>null);r?.cwd&&await Xt(r.cwd,n).catch(()=>{})}async function MR(t){let e;try{e=await U.readdir(Vr)}catch{return!1}let n=new Set;for(let o of t.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 re.values())o.dir&&n.add(H.basename(o.dir));let r=!1;for(let o of e){if(n.has(o))continue;let s=H.join(Vr,o),a=(t.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of jt(a.workspace))await Xt(i.base,H.join(s,i.name)).catch(()=>{});await U.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await Q(a.leadBox).catch(()=>null):null;i?.cwd?await Xt(i.cwd,s).catch(()=>{}):await U.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await q({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}var Xu=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",ds=class extends Error{};async function nb(t,{agent:e,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Nr(n))throw new Error("task (string) required");let s=await ii(e,r),a=Y(await W(),t);if(!a)throw new Error(`track "${t}" not found`);let i=await Q(a.leadBox);if(!i)throw new Error(`lead box "${a.leadBox}" is not running`);if(!i.cwd)throw new Error("can't resolve the lead's working directory");let c=a.workspace?.kind==="multi",l=null;if(c){let d=jt(a.workspace);if(!d.length)throw new Error("this multi-repo Track has no tagged repos to delegate into");for(let p of d)if(!p?.dir||!await Da(p.dir))throw new Error(`tagged checkout "${p?.name??"?"}" is missing from the lead workspace (${p?.dir??"no dir"})`);l=d.map(p=>({name:p.name,leadDir:p.dir,base:null}))}else if(!await Da(i.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${i.cwd})`);let u=fs().slice(0,8),f={id:u,trackId:t,trackName:a.name,agent:e,task:Nr(n),skipPermissions:r,reason:by(o),step:"snapshot",leadBox:a.leadBox,box:null,branch:`bostrat/${Xu(a.name)}-${u}`,base:null,repos:l,dir:H.join(Vr,`${t}-${u}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return re.set(u,f),await zr(f),NR(f,i,s).catch(d=>rb(f,d)),f}async function rb(t,e){t.failedStep=t.step,t.error=e instanceof ds?"canceled":e?.message||String(e),t.step=e instanceof ds?"canceled":"error",t.finishedAt=Date.now(),await Zu(t).catch(()=>{}),await zr(t).catch(()=>{}),await jR(t).catch(()=>{})}function Yu(t){let e=re.get(t);if(!e)throw new Error(`subtask "${t}" not found`);if(e.finishedAt)throw new Error("subtask already finished");return e.canceled=!0,e}function ob(t){let e=re.get(t);if(!e)throw new Error(`subtask "${t}" not found`);if(e.finishedAt)throw new Error("subtask already finished");if(e.step!=="working")throw new Error(`subtask is not waiting to merge (step: ${e.step})`);return e.forceMerge=!0,e}async function NR(t,e,n){if(await Kr(t,"snapshot"),t.repos){for(let s of t.repos)s.base=await Jo(s.leadDir,`bostrat: snapshot for subtask ${t.id}`);await zr(t)}else t.base=await Jo(e.cwd,`bostrat: snapshot for subtask ${t.id}`);if(await Kr(t,"spawn"),await U.mkdir(Vr,{recursive:!0}),t.repos){await U.mkdir(t.dir,{recursive:!0});for(let a of t.repos)await Pr(a.leadDir,H.join(t.dir,a.name),t.branch,a.base);let s=jt((Y(await W(),t.trackId)||{}).workspace);await di(t.dir,t.repos.map(a=>{let i=s.find(c=>c?.name===a.name);return{name:a.name,repo:i?.repo??null,baseRef:null}}),t.branch)}else await Pr(e.cwd,t.dir,t.branch,t.base),await Lu(t.dir);await PR(t,e);let r=t.trackName.slice(0,38).trim(),o=await On(`${r} ${t.agent} sub ${t.id}`,t.dir,{agentCmd:n,agent:t.agent});t.box=o.name,await BR(t,o),await ky(t.trackId,o.name,PI(t.task,!!t.repos),{kind:"subtask"}),await sb(t,e)}async function sb(t,e){await Kr(t,"working");let n=H.join(t.dir,ri);if(await FR(n,t),t.canceled)throw new ds;if(await Kr(t,"merge"),t.repos){let o={files:0,insertions:0,deletions:0},s=[],a=[],i=!0;for(let c of t.repos){let l=H.join(t.dir,c.name);await Vl(l,`bostrat: subtask ${t.id} result (auto-committed)`),c.stat=await Yl(c.leadDir,c.base,t.branch,qr),c.files=await Zl(c.leadDir,c.base,t.branch,qr);let u=await Ql(c.leadDir,c.base,t.branch,qr);c.applied=u.applied,c.conflicts=u.conflicts??[],o.files+=c.stat.files,o.insertions+=c.stat.insertions,o.deletions+=c.stat.deletions,s.push(...c.files.map(f=>H.join(c.name,f))),a.push(...c.conflicts.map(f=>f.startsWith("patch did not apply cleanly")?`${c.name}: ${f}`:H.join(c.name,f))),u.applied||(i=!1)}t.stat=o,t.files=s,t.conflicts=a,t.applied=i}else{await Vl(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await Yl(e.cwd,t.base,t.branch,qr),t.files=await Zl(e.cwd,t.base,t.branch,qr);let o=await Ql(e.cwd,t.base,t.branch,qr);t.applied=o.applied,t.conflicts=o.conflicts}await zr(t);let r=await LR(t,e,n);await Kr(t,"resume"),await ky(t.trackId,t.leadBox,BI(t.task,t.stat,t.conflicts,r),{kind:"subtask-resume"}),await Kr(t,"reap"),await Zu(t),t.step="done",t.finishedAt=Date.now(),await DR(t),await zr(t)}async function PR(t,e){let n=H.join(t.dir,H.dirname(ss));await U.mkdir(n,{recursive:!0});let r="";try{r=await Ff(e.cwd,e.sessionId)}catch{}if(!r)try{r=await U.readFile(H.join(e.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 ${t.id}
|
|
372
|
+
${a}`}async function yd(t){if(t.archivedAt)return!1;let e=t.boxes.find(o=>o.name===t.leadBox);if(!e||cr.has(e.state)||await rt(e.name))return!1;let n=ib(e.agent,e.sessionId),r=await aa(n);if(r===gr)return dt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:`agent "${e.agent}" is not installed on this box`}),!1;r===yr&&console.warn(`[track] ${e.name}: could not confirm "${e.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);try{let o=await Bn(e.name,e.cwd,{agentCmd:n,agent:e.agent,resumeSessionId:e.sessionId,pinned:e.pinned===!0});return e.hookTokenHash=go(o.hookToken),ha({cwd:e.cwd,sessionName:e.name,agent:e.agent,agentCmd:n}),e.state="live",dt(t,"rehydrated",{box:e.name,agent:e.agent}),!0}catch(o){return dt(t,"rehydrate-failed",{box:e.name,agent:e.agent,reason:String(o?.message||o)}),!1}}async function Lb(t){let e=await H(),n=tt(e,t);if(!n)throw new Error(`track "${t}" not found`);if(!n.archivedAt)throw new Error(`track "${t}" is not archived`);return delete n.archivedAt,Si.delete(t),dt(n,"unarchived",{}),await yd(n),await X(),Ye(n)}async function Fb(){let t=await H(),e=new Set((await Oe()).map(s=>s.name)),n=new Set((t.tracks||[]).filter(s=>!s.archivedAt).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of t.tracks||[])if(s.archivedAt)for(let a of s.boxes)e.has(a.name)&&!n.has(a.name)&&(await pe(a.name).catch(()=>{}),dt(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!cr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await X(),r}async function Ub(t){let e=await H(),n=tt(e,t);if(!n)throw new Error(`track "${t}" not found`);if(n.archivedAt)throw new Error(`track "${t}" is archived \u2014 restore it before restarting the lead`);let r=n.boxes.find(a=>a.name===n.leadBox);if(!r)throw new Error(`track "${t}" has no lead box`);if(cr.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await rt(n.leadBox)){let a=await mt(n.leadBox,{lines:Et}).catch(()=>"");if(pa(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await pe(n.leadBox),r.state="stopped"}let s=await yd(n);if(await X(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return Ye(n)}var Hb=(t,e,n)=>({name:t.name,agent:t.agent,role:e,state:n,cwd:t.cwd,sessionId:t.sessionId,pinned:t.pinned===!0,hookTokenHash:go(t.hookToken),createdAt:Date.now()});function Wb(t){return(!Array.isArray(t.epochs)||t.epochs.length===0)&&(t.epochs=[{agent:t.agent??null,sessionId:t.sessionId??null,pinned:t.pinned===!0,startedAt:t.createdAt??null,endedAt:null}]),t.epochs}var Ss=new Map,Es=t=>Ss.get(t)||(_e?tt(_e,t)?.handoff:null)||null;async function vi(t,e){let n=await H(),r=tt(n,t);r&&(r.handoff={...r.handoff||{},...e},await X())}function B$(t,e=Date.now()){let n=t?.handoff;if(!n||n.finishedAt)return null;let r=n.step,o=!!n.toBox&&n.toBox===n.fromBox,s=t.boxes?.find?.(i=>i.name===t.leadBox)||null;return(o?t.leadBox===n.fromBox&&!!n.startedAt&&Array.isArray(s?.epochs)&&s.epochs.some(i=>i.startedAt!=null&&i.startedAt>=n.startedAt):!!n.toBox&&t.leadBox===n.toBox)?{...n,step:"done",finishedAt:e,recovered:`committed before restart (was "${r}")`}:{...n,step:"error",finishedAt:e,error:`interrupted by an agent restart during "${r}"; lead "${t.leadBox}" retained \u2014 retry the handoff`,recovered:"rolled-back"}}async function qb(){let t=await H(),e=!1;for(let n of t.tracks||[]){let r=n.handoff,o=B$(n);if(!o)continue;e=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await rt(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=Wb(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:_i,at:u},dt(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 G({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 G({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await pe(i).catch(()=>{}),a&&await pe(a).catch(()=>{})}return e&&await X(),e}async function bd(t,e,n=!0,r=null){let o=Ss.get(t);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=tt(await H(),t);if(!s)throw new Error(`track "${t}" not found`);Ri(t,"handoff");let a=await Ei(e,n),i=as(e);if(i){let u=new Error(`can't hand off to ${e} \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 rt(s.leadBox);if(!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l={trackId:t,step:"checkpoint",fromBox:s.leadBox,toAgent:e,skipPermissions:n,reason:cb(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return Ss.set(t,l),await vi(t,{step:l.step,fromBox:l.fromBox,toAgent:e,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),j$(l,s,c,a,e).catch(async u=>{l.error=u?.message||String(u),l.finishedAt=Date.now(),console.error(`[lead-handoff ${t}] FAILED at step "${l.step}": ${l.error}`),await vi(t,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),G({flow:"lead-handoff",phase:"failed",trackId:t,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),G({flow:"lead-handoff",phase:"started",trackId:t,fromBox:l.fromBox,toAgent:e,cwd:c.cwd}).catch(()=>{}),l}async function j$(t,e,n,r,o){let s=async(_,O={})=>{t.step=_,console.log(`[lead-handoff ${t.trackId}] step \u2192 ${_}`),await vi(t.trackId,{step:_,toBox:t.toBox}),await G({flow:"lead-handoff",phase:_,trackId:t.trackId,fromBox:t.fromBox,toBox:t.toBox,toAgent:o,sessionName:O.sessionName,...O})};console.log(`[lead-handoff ${t.trackId}] ${t.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await H(),i=tt(a,t.trackId)?.boxes.find(_=>_.name===t.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;await s("checkpoint",{sessionName:t.fromBox}),await sm(t.fromBox),await s("swap",{cwd:n.cwd,agentCmd:r});let f=await Pl(t.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:t.fromBox}),await ln({cwd:n.cwd,sessionId:f.sessionId,sessionName:t.fromBox,agent:o,agentCmd:As(o)});let d=await mt(t.fromBox,{lines:Et}).catch(()=>"");if(!ma(o,d)){await s("rollback",{sessionName:t.fromBox,fromAgent:c});let _=ib(c,l,{skipPermissions:t.skipPermissions});await Pl(t.fromBox,{agentCmd:_,agent:c,resumeSessionId:l,pinned:u});let O=await H(),nt=tt(O,t.trackId);if(nt){let R=nt.boxes.find(C=>C.name===t.fromBox);R&&(R.state="live"),dt(nt,"lead-handoff-failed",{box:t.fromBox,fromAgent:c,toAgent:o,reason:t.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:t.startedAt}),await X()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let p=await H(),m=tt(p,t.trackId);if(!m)throw new Error(`track "${t.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let w=m.boxes.find(_=>_.name===t.fromBox);if(!w)throw new Error(`lead box row "${t.fromBox}" missing during handoff commit`);let y=Date.now(),b=Wb(w),S=b[b.length-1];S.endedAt=y,t.reason&&(S.endReason=t.reason),b.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,startedAt:y,endedAt:null}),w.agent=o,w.sessionId=f.sessionId,w.pinned=f.pinned,w.state="live",w.role="lead",m.pendingHandoffHydrate={artifact:_i,at:y},dt(m,"lead-handoff",{from:t.fromBox,to:t.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:b.length-1,singleBox:!0,forced:f.forced||void 0,skipPermissions:t.skipPermissions,reason:t.reason,handoffStartedAt:t.startedAt}),await X(),await s("done",{sessionName:t.fromBox}),await vi(t.trackId,{finishedAt:Date.now(),error:null}),t.finishedAt=Date.now()}var L$=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,F$=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,ro=U.join(Xe,"worktrees"),Qr=[ys,xi],ce=new Map;var ao=t=>{let e=new Map([...ce.values()].filter(r=>r.trackId===t).map(r=>[r.id,r])),n=_e?tt(_e,t):null;if(n?.subtasks)for(let[r,o]of Object.entries(n.subtasks))e.has(r)||e.set(r,o);return[...e.values()].sort((r,o)=>r.startedAt-o.startedAt)},kd=t=>({id:t.id,trackId:t.trackId,trackName:t.trackName,agent:t.agent,task:t.task,skipPermissions:t.skipPermissions,reason:t.reason||null,step:t.step,failedStep:t.failedStep||null,box:t.box,branch:t.branch,base:t.base,repos:t.repos||null,dir:t.dir,stat:t.stat,files:t.files||null,conflicts:t.conflicts||[],applied:t.applied,error:t.error,canceled:t.canceled,startedAt:t.startedAt,finishedAt:t.finishedAt});async function U$(t,e,n){let r=await H(),o=tt(r,t);o&&(o.subtasks=o.subtasks||{},o.subtasks[e]={...o.subtasks[e]||{},...n},await X())}var oo=t=>U$(t.trackId,t.id,kd(t));async function to(t,e){t.step=e,await oo(t)}function H$(t,e=Date.now()){if(!t||t.finishedAt)return null;let n=t.step;return["snapshot","spawn","working"].includes(n)?{...t,step:"error",finishedAt:e,error:`interrupted by an agent restart during "${n}"; no changes were applied to the lead \u2014 retry the delegation`,recovered:"rolled-back"}:{...t,step:"error",finishedAt:e,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 ${t.branch}`,recovered:"partial-merge"}}function W$(t){return!!t&&!t.finishedAt&&!t.canceled&&t.step==="working"&&!!t.box&&!!t.dir&&!!t.base&&!!t.branch}async function q$(t,e,n){if(!W$(n)||ce.has(e)||!await rt(n.box).catch(()=>null))return!1;try{await F.access(n.dir)}catch{return!1}let o=await rt(t.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:e,leadBox:t.leadBox,step:"working",canceled:!1,forceMerge:!1};return ce.set(e,s),Jb(s,o).catch(a=>Vb(s,a)),!0}async function Kb(){let t=await H(),e=!1;for(let n of t.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(ce.has(r))continue;if(await q$(n,r,o)){e=!0,dt(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await G({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=H$(o);if(!a)continue;e=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!cr.has(i.state)&&(i.state="canceled"),dt(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 G({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await sb(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||cr.has(r.state)||ce.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;e=!0,r.state="canceled";let o=r.subtaskId?U.join(ro,`${n.id}-${r.subtaskId}`):null;await G({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await sb(n,r.name,o)}}return e=await K$(t)||e,e&&await X(),e}async function sb(t,e,n){if(e&&await pe(e).catch(()=>{}),!n)return;if(t.workspace?.kind==="multi"){for(let o of qt(t.workspace)){let s=U.join(n,o.name);await ee(o.base,s).then(()=>!0,()=>!1)||await ee(o.dir,s).catch(()=>{})}await F.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await rt(t.leadBox).catch(()=>null);r?.cwd&&await ee(r.cwd,n).catch(()=>{})}async function K$(t){let e;try{e=await F.readdir(ro)}catch{return!1}let n=new Set;for(let o of t.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 ce.values())o.dir&&n.add(U.basename(o.dir));let r=!1;for(let o of e){if(n.has(o))continue;let s=U.join(ro,o),a=(t.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of qt(a.workspace))await ee(i.base,U.join(s,i.name)).catch(()=>{});await F.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await rt(a.leadBox).catch(()=>null):null;i?.cwd?await ee(i.cwd,s).catch(()=>{}):await F.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await G({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}var xd=t=>t.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",vs=class extends Error{};async function Gb(t,{agent:e,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!Hr(n))throw new Error("task (string) required");let s=await Ei(e,r),a=as(e);if(a){let p=new Error(`can't delegate to ${e} \u2014 its ${a.window} spend limit is reached ($${a.spentUsd.toFixed(2)} of $${a.limitUsd.toFixed(2)})`);throw p.usageCap=a,p}let i=tt(await H(),t);if(!i)throw new Error(`track "${t}" not found`);let c=await rt(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=qt(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 Ya(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 Ya(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let f=Ts().slice(0,8),d={id:f,trackId:t,trackName:i.name,agent:e,task:Hr(n),skipPermissions:r,reason:cb(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${xd(i.name)}-${f}`,base:null,repos:u,dir:U.join(ro,`${t}-${f}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return ce.set(f,d),await oo(d),G$(d,c,s).catch(p=>Vb(d,p)),d}async function Vb(t,e){t.failedStep=t.step,t.error=e instanceof vs?"canceled":e?.message||String(e),t.step=e instanceof vs?"canceled":"error",t.finishedAt=Date.now(),await vd(t).catch(()=>{}),await oo(t).catch(()=>{}),await X$(t).catch(()=>{})}function Sd(t){let e=ce.get(t);if(!e)throw new Error(`subtask "${t}" not found`);if(e.finishedAt)throw new Error("subtask already finished");return e.canceled=!0,e}function zb(t){let e=ce.get(t);if(!e)throw new Error(`subtask "${t}" not found`);if(e.finishedAt)throw new Error("subtask already finished");if(e.step!=="working")throw new Error(`subtask is not waiting to merge (step: ${e.step})`);return e.forceMerge=!0,e}async function G$(t,e,n){if(await to(t,"snapshot"),t.repos){for(let s of t.repos)s.base=await ss(s.leadDir,`bostrat: snapshot for subtask ${t.id}`);await oo(t)}else t.base=await ss(e.cwd,`bostrat: snapshot for subtask ${t.id}`);if(await to(t,"spawn"),await F.mkdir(ro,{recursive:!0}),t.repos){await F.mkdir(t.dir,{recursive:!0});for(let a of t.repos)await Wr(a.leadDir,U.join(t.dir,a.name),t.branch,a.base);let s=qt((tt(await H(),t.trackId)||{}).workspace);await $i(t.dir,t.repos.map(a=>{let i=s.find(c=>c?.name===a.name);return{name:a.name,repo:i?.repo??null,baseRef:null}}),t.branch)}else await Wr(e.cwd,t.dir,t.branch,t.base),await ud(t.dir);await V$(t,e);let r=t.trackName.slice(0,38).trim(),o=await Bn(`${r} ${t.agent} sub ${t.id}`,t.dir,{agentCmd:n,agent:t.agent});t.box=o.name,await z$(t,o),await lb(t.trackId,o.name,KR(t.task,!!t.repos),{kind:"subtask"}),await Jb(t,e)}async function Jb(t,e){await to(t,"working");let n=U.join(t.dir,xi);if(await Z$(n,t),t.canceled)throw new vs;if(await to(t,"merge"),t.repos){let o={files:0,insertions:0,deletions:0},s=[],a=[],i=!0;for(let c of t.repos){let l=U.join(t.dir,c.name);await mu(l,`bostrat: subtask ${t.id} result (auto-committed)`),c.stat=await yu(c.leadDir,c.base,t.branch,Qr),c.files=await bu(c.leadDir,c.base,t.branch,Qr);let u=await ku(c.leadDir,c.base,t.branch,Qr);c.applied=u.applied,c.conflicts=u.conflicts??[],o.files+=c.stat.files,o.insertions+=c.stat.insertions,o.deletions+=c.stat.deletions,s.push(...c.files.map(f=>U.join(c.name,f))),a.push(...c.conflicts.map(f=>f.startsWith("patch did not apply cleanly")?`${c.name}: ${f}`:U.join(c.name,f))),u.applied||(i=!1)}t.stat=o,t.files=s,t.conflicts=a,t.applied=i}else{await mu(t.dir,`bostrat: subtask ${t.id} result (auto-committed)`),t.stat=await yu(e.cwd,t.base,t.branch,Qr),t.files=await bu(e.cwd,t.base,t.branch,Qr);let o=await ku(e.cwd,t.base,t.branch,Qr);t.applied=o.applied,t.conflicts=o.conflicts}await oo(t);let r=await Y$(t,e,n);await to(t,"resume"),await lb(t.trackId,t.leadBox,GR(t.task,t.stat,t.conflicts,r),{kind:"subtask-resume"}),await to(t,"reap"),await vd(t),t.step="done",t.finishedAt=Date.now(),await J$(t),await oo(t)}async function V$(t,e){let n=U.join(t.dir,U.dirname(ys));await F.mkdir(n,{recursive:!0});let r="";try{r=await mp(e.cwd,e.sessionId)}catch{}if(!r)try{r=await F.readFile(U.join(e.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 ${t.id}
|
|
373
373
|
|
|
374
374
|
**Delegated task:** ${t.task}
|
|
375
375
|
|
|
@@ -380,50 +380,50 @@ ${a}`}async function Vu(t){if(t.archivedAt)return!1;let e=t.boxes.find(r=>r.name
|
|
|
380
380
|
## Conversation so far (lead \u2192 you)
|
|
381
381
|
|
|
382
382
|
${r}
|
|
383
|
-
`;await
|
|
384
|
-
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${
|
|
383
|
+
`;await F.writeFile(U.join(t.dir,ys),o)}async function z$(t,e){let n=await H(),r=tt(n,t.trackId);r&&(r.boxes.push({name:e.name,agent:e.agent,role:"subtask",state:"live",cwd:e.cwd,sessionId:e.sessionId,hookTokenHash:go(e.hookToken),createdAt:Date.now(),subtaskId:t.id,branch:t.branch,task:t.task}),dt(r,"subtask-delegated",{to:e.name,toAgent:t.agent,task:t.task,subtaskId:t.id,skipPermissions:t.skipPermissions,reason:t.reason}),r.subtasks=r.subtasks||{},r.subtasks[t.id]={...r.subtasks[t.id]||{},...kd(t)},await X())}async function J$(t){let e=await H(),n=tt(e,t.trackId);if(!n)return;let r=n.boxes.find(o=>o.subtaskId===t.id);r&&(r.state=t.conflicts.length?"conflict":"merged",r.stat=t.stat),dt(n,"subtask-merged",{from:t.box,fromAgent:t.agent,task:t.task,subtaskId:t.id,stat:t.stat,conflicts:t.conflicts.length,files:t.files,durationMs:t.finishedAt?t.finishedAt-t.startedAt:null}),await X()}async function X$(t){let e=await H(),n=tt(e,t.trackId);if(!n)return;let r=t.canceled||t.error==="canceled",o=n.boxes.find(s=>s.subtaskId===t.id);o&&(o.state=r?"canceled":"failed"),dt(n,r?"subtask-canceled":"subtask-failed",{from:t.box,fromAgent:t.agent,task:t.task,subtaskId:t.id,failedStep:t.failedStep||t.step,error:t.error,durationMs:t.finishedAt?t.finishedAt-t.startedAt:null}),n.subtasks=n.subtasks||{},n.subtasks[t.id]={...n.subtasks[t.id]||{},...kd(t)},await X()}async function Y$(t,e,n){let r=`.bostrat/handoff/subtask-${t.id}.md`;try{let o=U.join(e.cwd,r);await F.mkdir(U.dirname(o),{recursive:!0}),await F.copyFile(n,o)}catch{}return r}async function vd(t){if(t.box&&await pe(t.box).catch(()=>{}),t.repos&&t.dir){for(let e of t.repos)await ee(e.leadDir,U.join(t.dir,e.name)).catch(()=>{});await F.rm(t.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(t.dir){let n=(await rt(t.leadBox).catch(()=>null))?.cwd;n&&await ee(n,t.dir).catch(()=>{})}}async function Z$(t,e){let n=(e.startedAt||Date.now())+L$;for(;;){if(e.canceled||e.forceMerge)return;try{await F.access(t);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await _n(F$)}}function _n(t){return new Promise(e=>setTimeout(e,t))}import _s from"node:fs/promises";import Ci from"node:path";import rO from"node:os";import Q$ from"ws";var Xb=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function Oi(t,e,n={}){let r=await fetch(`${Xb()}/${t}`,{method:"POST",headers:{authorization:`Bearer ${e}`,"content-type":"application/json; charset=utf-8"},body:JSON.stringify(n)}),o=await r.json().catch(()=>null);if(!o?.ok)throw new Error(`slack ${t}: ${o?.error||`http ${r.status}`}`);return o}var Td=(t,e)=>Oi("chat.postMessage",t,e),io=(t,e)=>Oi("chat.update",t,e);async function Kt(t,e){await fetch(t,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(e)}).catch(()=>{})}async function tO(t,e,n={}){let r=await fetch(`${Xb()}/${t}`,{method:"POST",headers:{authorization:`Bearer ${e}`,"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 ${t}: ${o?.error||`http ${r.status}`}`);return o}async function Yb(t,{filename:e,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await tO("files.getUploadURLExternal",t,{filename:e,length:String(n.length)}),l=await fetch(i,{method:"POST",body:n});if(!l.ok)throw new Error(`slack upload: http ${l.status}`);return Oi("files.completeUploadExternal",t,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var eO=2200;function Zb({getToken:t,onEnvelope:e,log:n=()=>{}}){let r=!1,o=null,s=null,a=1e3;async function i(){if(r)return;let l;try{let u=await t();if(!u)throw new Error("app token missing");({url:l}=await Oi("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new Q$(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()},eO);Promise.resolve().then(()=>e(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 ek=()=>process.env.BOSTRAT_STATE_DIR||Ci.join(process.env.HOME||rO.tmpdir(),".bostrat"),Rd=()=>Ci.join(ek(),"slack.json"),Mi=null,Ad=null,Ed=Promise.resolve();async function Od(){return Mi||(Ad??=_s.readFile(Rd(),"utf8").then(t=>JSON.parse(t)?.threads??{}).catch(()=>({})).then(t=>Mi=t).finally(()=>Ad=null),Ad)}async function Qb(){let t=JSON.stringify({threads:Mi},null,2),e=`${Rd()}.${process.pid}.tmp`;await _s.mkdir(ek(),{recursive:!0}),await _s.writeFile(e,t),await _s.rename(e,Rd())}async function Ze(){Ed=Ed.then(Qb,Qb),await Ed.catch(()=>{})}async function Md(t){return(await Od())[t]??null}async function oO(t,e){let n=await Od();for(let[r,o]of Object.entries(n))if(o.channel===t&&o.ts===e)return{trackId:r,rec:o};return null}var Q=t=>String(t??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");function Is(t,e){let n=String(t??"").trim();return n.length<=e?n:`${n.slice(0,e-1).trimEnd()}\u2026`}function sO(t,e=500){let n=String(t??"").trim().split(/\n\s*\n/)[0]??"";return Is(n.replace(/\n+/g," "),e)}function nk(t){let e=`*${Q(t.name)}*${t.archivedAt?" \xB7 _archived_":""}`,n=`${Q(t.repoLabel)} \xB7 \`${Q(t.branch||"?")}\` \xB7 lead *${Q(t.lead)}*`,r=t.kickoff?`> ${Q(Is(t.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${e}
|
|
384
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${La(t.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function $d(t,e){await io(t.botToken,{channel:e.channel,ts:e.ts,text:`Track: ${e.name}`,blocks:nk(e)})}async function ur(t,e,n,r=void 0){return Td(t.botToken,{channel:e.channel,thread_ts:e.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var tk=new Set;function lr(t){tk.add(t),t.catch(()=>{}).finally(()=>tk.delete(t))}var pt=t=>({response_type:"ephemeral",text:t}),aO="*/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._",iO=["claude","cursor","codex"];function _d(t,e){let n=(e||"").toLowerCase(),r=t.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 Id=(t,e)=>pt(`No live Track matches \u201C${Q(e)}\u201D. Live: ${t.map(n=>`\`${Q(n.name)}\``).join(" \xB7 ")||"none"}`);async function rk(t,e,{name:n,repo:r,responseUrl:o}){let s=t.deps;try{let a=(await s.listTracks()).filter(f=>!f.archivedAt);if(a.length>=e.maxConcurrent){await Kt(o,{...pt(`At capacity \u2014 ${a.length} live Tracks (limit ${e.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!e.channel){await Kt(o,{...pt("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:e.channel,name:i.name,branch:c.branch||null,repoLabel:c.repo||(c.base?Ci.basename(c.base):"fresh workspace"),lead:"claude"},u=await Td(e.botToken,{channel:e.channel,text:`Track started: ${l.name}`,blocks:nk(l),unfurl_links:!1});l.ts=u.ts,(await Od())[i.id]=l,await Ze(),await Kt(o,{...pt(`\u2713 *${Q(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await Kt(o,{...pt(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function cO(t,e,{track:n,responseUrl:r}){let o=t.deps;try{await o.archiveTrack(n.id);let s=await Md(n.id);s&&(s.archivedAt=Date.now(),await Ze(),await $d(e,s).catch(()=>{})),await Kt(r,{...pt(`\u2713 *${Q(n.name)}* archived.`),replace_original:!0})}catch(s){await Kt(r,{...pt(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function lO(t,e,{track:n,agent:r,responseUrl:o}){try{await t.deps.startLeadHandoff(n.id,r),await Kt(o,{...pt(`\u2713 Handing *${Q(n.name)}* to ${Q(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await Kt(o,{...pt(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function uO(t,e,{track:n,responseUrl:r}){let o=t.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 Kt(r,{...pt(`\u2713 Stopped *${Q(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 Kt(r,{...pt(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function dO(t,e){let n=[...e.slice(0,99).map(r=>({text:{type:"plain_text",text:Is(`${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:t}),text:{type:"mrkdwn",text:`Repo for *${Q(t)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function fO(t){let e=await t.listTracks(),n=e.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?Ci.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 *${Q(s.name)}* \u2014 ${Q(i)}${a.branch?` \`${Q(a.branch)}\``:""} \xB7 lead ${Q(c)} \xB7 ${l}`}),o=e.length-n.length;return r.join(`
|
|
385
385
|
`)+(o?`
|
|
386
|
-
_${o} archived_`:"")}async function
|
|
387
|
-
\u2713 *${
|
|
388
|
-
\u2713 _answered_`}}]})}async function
|
|
389
|
-
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return
|
|
390
|
-
`)){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&&e.set(i,{messageId:i,tokens:a,model:o.message?.model||o.model||null})}return[...e.values()]}function y$(t){let e=new Set;for(let n of t)if(n.type==="cost.recorded"){n.payload?.messageId&&e.add(n.payload.messageId);for(let r of n.payload?.messageIds||[])e.add(r)}return e}function b$(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?Ae(e.cwd,e.sessionId):e?.cwd?mc(e.cwd):null}async function hb(t,e){let n=await e(t?.session_id,t?.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(t?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:t.session_id}});break;case"UserPromptSubmit":await i({type:"turn.user",actor:"user",payload:{promptHead:String(t.prompt??t.user_prompt??"").slice(0,160)||null}});break;case"PostToolUse":await i({type:"tool.called",actor:"agent",payload:{tool:t.tool_name??null}});break;case"Stop":await k$(r,o,t,i);break;default:break}return a}async function k$(t,e,n,r){let o=[],s=b$(n,e);if(s)try{o=g$(await w$.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??e?.model??null,messageId:a?.messageId??null}}),!o.length)return;let i=y$(await L(t)),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:le(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}})}import v$ from"node:fs";import cd from"node:fs/promises";import T$ from"node:http";import Zr from"node:path";import wi from"node:os";import{createRequire as A$}from"node:module";import ld from"ws";import x$ from"node:path";import S$ from"node:os";var wb=process.env.BOSTRAT_STATE_DIR||x$.join(process.env.HOME||S$.tmpdir(),".bostrat");function gb(t=process.env){let e=(t.BOSTRAT_HOST||"").trim();if(e!=="all")return e||(t.BOSTRAT_PLANE_URL?"127.0.0.1":void 0)}function yb(t){let e=String(t||"").trim();if(!e)return"";let n=/^[a-z][a-z0-9+.-]*:\/\//i.test(e)?e:`wss://${e}`,r;try{r=new URL(n)}catch{return e}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()):e}var hi=(()=>{try{return A$(import.meta.url)("../package.json").version||null}catch{return null}})(),E$=process.env.BOSTRAT_STATE_DIR||Zr.join(process.env.HOME||wi.tmpdir(),".bostrat");function _$(t,e){let n=String(t).split(".").map(Number),r=String(e).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 I$({home:t=process.env.HOME||wi.homedir(),env:e=process.env}={}){let n=r=>{try{return v$.statSync(r),!0}catch{return!1}};return{claude:n(Zr.join(t,".claude",".credentials.json")),codex:n(Zr.join(e.CODEX_HOME||Zr.join(t,".codex"),"auth.json"))}}var It=1,bb=1e3,R$=3e4,$$=6e4,O$=1500,C$=500,kb=100,M$=2e3,N$=25e3;function xb({url:t,token:e,nodeId:n=wi.hostname(),name:r=wi.hostname(),listTracks:o,detectAgents:s=null,selfUrl:a=`http://127.0.0.1:${process.env.PORT||3362}`,log:i=console}){if(!t||!e)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");t=yb(t);let c=a.replace(/^http/,"ws"),l=new Map,u=new Map,f=Zr.join(E$,"plane-link","cursor.json"),d={},p=new Map,m=null,h=!1,y=!1,b=bb,S=null,I=null,M=null,z=null,$=!1;async function F(){try{d=JSON.parse(await cd.readFile(f,"utf8"))}catch{d={}}}function T(){$=!0,!M&&(M=setTimeout(async()=>{if(M=null,!!$){$=!1;try{await cd.mkdir(Zr.dirname(f),{recursive:!0}),await cd.writeFile(f,JSON.stringify(d))}catch(v){i.error("[plane-link] cursor flush failed:",v?.message||v)}}},M$),M.unref?.())}function C(v){return!m||m.readyState!==ld.OPEN?!1:(m.send(JSON.stringify(v)),!0)}function D(v){for(let{trackId:O,event:E}of v)p.set(E.id,{trackId:O,seq:E.seq});return C({v:It,ch:"events",type:"append",events:v})}async function it(){let v;try{v=await o()}catch(O){i.error("[plane-link] backfill listTracks failed:",O?.message||O);return}C({v:It,ch:"ctl",type:"tracks",ids:v.map(O=>O.id).filter(O=>typeof O=="string")});for(let O of v){if(!h)return;let E;try{E=await L(O)}catch{continue}let N=d[O.id]??0,nt=E.filter(Z=>Z.seq>N&&typeof Z.id=="string");for(let Z=0;Z<nt.length;Z+=kb){let wt=nt.slice(Z,Z+kb).map(Ct=>({trackId:O.id,event:Ct}));if(!D(wt))return}}}function ot(v){!h||!v?.trackId||typeof v.id!="string"||D([{trackId:v.trackId,event:v}])}let A=new Map;async function g(v){let{reqId:O,method:E,path:N,headers:nt={},body:Z}=v,wt=Object.fromEntries(Object.entries(nt).filter(([,Vt])=>Vt!=null)),Ct=new AbortController;A.set(O,Ct);try{if(v.host){await w(v,wt,Ct);return}let Vt=await fetch(a+N,{method:E,headers:wt,body:Z?Buffer.from(Z,"base64"):void 0,signal:Ct.signal}),Zt=Object.fromEntries(Vt.headers);for(let k of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Zt[k];if(Zt["content-type"]||(Zt["content-type"]="application/octet-stream"),C({v:It,ch:"control",type:"http_head",reqId:O,status:Vt.status,headers:Zt}),Vt.body){for await(let k of Vt.body)if(!C({v:It,ch:"control",type:"http_data",reqId:O,data:Buffer.from(k).toString("base64")}))break}C({v:It,ch:"control",type:"http_end",reqId:O,error:null})}finally{A.delete(O)}}function w(v,O,E){let{reqId:N,method:nt,path:Z,body:wt}=v,Ct=new URL(a);return new Promise(Vt=>{let Zt=T$.request({host:Ct.hostname,port:Ct.port,method:nt,path:Z,headers:{...O,host:v.host},signal:E.signal},k=>{let{connection:G,"keep-alive":rr,"transfer-encoding":Rs,upgrade:or,...Di}=k.headers;C({v:It,ch:"control",type:"http_head",reqId:N,status:k.statusCode||502,headers:Di}),k.on("data",sx=>{C({v:It,ch:"control",type:"http_data",reqId:N,data:Buffer.from(sx).toString("base64")})||k.destroy()}),k.on("end",()=>{C({v:It,ch:"control",type:"http_end",reqId:N,error:null}),Vt()}),k.on("error",()=>{C({v:It,ch:"control",type:"http_end",reqId:N,error:"view upstream error"}),Vt()})});Zt.on("error",k=>{C({v:It,ch:"control",type:"http_end",reqId:N,error:k?.message||"view request failed"}),Vt()}),wt&&Zt.write(Buffer.from(wt,"base64")),Zt.end()})}function _(){if(y)return;m=new ld(t,{headers:{authorization:`Bearer ${e}`}}),m.on("open",async()=>{let O=null;try{O=await s?.()||null}catch{O=null}C({v:It,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:hi,providers:I$(),...O?{agents:O}:{}})}),m.on("message",O=>{let E;try{E=JSON.parse(O)}catch{return}if(E.ch==="ctl"&&E.type==="hello_ok"){h=!0,b=bb,S=null,i.log(`[plane-link] connected to plane as ${n}`),E.minNodeVersion&&hi&&_$(hi,E.minNodeVersion)&&i.error(`[plane-link] this node is bostrat-node ${hi}; the plane asks for \u2265${E.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),z&&clearInterval(z),z=setInterval(()=>C({v:It,ch:"ctl",type:"ping"}),N$),z.unref?.(),it().catch(N=>i.error("[plane-link] backfill failed:",N?.message||N));return}if(E.ch==="events"&&E.type==="ack"&&Array.isArray(E.ids)){for(let N of E.ids){let nt=p.get(N);nt&&(p.delete(N),(d[nt.trackId]??0)<nt.seq&&(d[nt.trackId]=nt.seq))}T();return}if(E.ch==="control"&&E.type==="http_open"){g(E).catch(N=>C({v:It,ch:"control",type:"http_end",reqId:E.reqId,error:N?.message||"tunnel error"}));return}if(E.ch==="control"&&E.type==="http_cancel"){A.get(E.reqId)?.abort();return}if(E.ch==="attach"&&E.type==="open"){let N=new ld(c+E.path,Array.isArray(E.protocols)?E.protocols:[],E.host?{headers:{host:E.host}}:void 0);l.set(E.streamId,N),u.set(E.streamId,[]),N.on("open",()=>{let Z=u.get(E.streamId)||[];u.delete(E.streamId);for(let{buf:wt,bin:Ct}of Z)N.send(wt,{binary:Ct})}),N.on("message",(Z,wt)=>{let Ct=wt?Z:Buffer.from(String(Z));C({v:It,ch:"attach",type:"data",streamId:E.streamId,data:Ct.toString("base64"),...wt?{bin:!0}:{}})});let nt=()=>{u.delete(E.streamId),l.delete(E.streamId)&&C({v:It,ch:"attach",type:"close",streamId:E.streamId})};N.on("close",nt),N.on("error",nt);return}if(E.ch==="attach"&&E.type==="data"){let N=l.get(E.streamId);if(!N)return;let nt=Buffer.from(E.data,"base64"),Z=E.bin===!0,wt=u.get(E.streamId);wt?wt.push({buf:nt,bin:Z}):N.send(nt,{binary:Z});return}if(E.ch==="attach"&&E.type==="close"){let N=l.get(E.streamId);l.delete(E.streamId),u.delete(E.streamId),N?.close();return}});let v=()=>{h&&i.log("[plane-link] disconnected from plane"),h=!1,z&&(clearInterval(z),z=null),p.clear();for(let N of l.values())N.close();l.clear(),u.clear();for(let N of A.values())N.abort();if(A.clear(),y||I)return;S==null&&(S=Date.now());let O=Date.now()-S<$$,E=O?O$+Math.random()*C$:b;I=setTimeout(()=>{I=null,_()},E),I.unref?.(),O||(b=Math.min(b*2,R$))};m.on("close",v),m.on("error",v)}return Jt.on("event",ot),F().then(_),{stop:()=>{y=!0,Jt.off("event",ot),I&&clearTimeout(I),M&&clearTimeout(M),z&&clearInterval(z),C({v:It,ch:"ctl",type:"bye",reason:"shutdown"}),m?.close()},status:()=>({connected:h,nodeId:n,cursor:{...d},inflight:p.size})}}function dd(t){let e={};for(let n of t.boxes||[]){let r=n.role&&n.role!=="lead"?` \xB7 ${n.role}`:"";e[n.name]=`${n.name} (${n.agent||"?"}${r})`}return e}var Sb=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),vb=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},ud=t=>Math.round(t*1e4)/1e4;async function Tb(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=new Set,l=Sb(),u=0,f=[];for(let d of t||[]){let p=await L(d),m=ue(p,{boxLabels:dd(d)});if(m.turns===0){d.archivedAt?u++:f.push(d.name||d.id);continue}o+=m.totalUsd,s+=m.turns,a+=m.repricedTurns||0,i+=m.unpricedTurns||0;for(let h of m.unpricedModels||[])c.add(h);vb(l,m.totalTokens);for(let[h,y]of Object.entries(m.byModel))n[h]||={usd:0,events:0,tokens:Sb()},n[h].usd=ud(n[h].usd+y.usd),n[h].events+=y.events,vb(n[h].tokens,y.tokens);for(let[h,y]of Object.entries(m.byProvider))r[h]||={usd:0,events:0},r[h].usd=ud(r[h].usd+y.usd),r[h].events+=y.events;e.push({id:d.id,name:d.name,usd:m.totalUsd,rollup:m})}return e.sort((d,p)=>p.usd-d.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:ud(o),turns:s,repricedTurns:a,unpricedTurns:i,unpricedModels:[...c],totalTokens:l,byModel:n,byProvider:r,tracksWithCost:e.length,tracksSilent:u+f.length,tracksSilentArchived:u,tracksSilentLive:f},tracks:e}}var gs=t=>`$${t.toFixed(4)}`,Qr=(t,e)=>String(t).padEnd(e);function Ab(t){let{fleet:e,tracks:n}=t,r=[];r.push(`BOSTRAT COST READ-OUT \xB7 ${t.generatedAt}`),t.pricingBasis&&r.push(`basis: ${t.pricingBasis}`);let o=e.tracksSilentLive||[];r.push(`fleet total ${gs(e.totalUsd)} over ${e.turns} turn(s) \xB7 ${e.tracksWithCost} track(s) with cost, ${e.tracksSilentArchived??e.tracksSilent} silent (archived)`+(o.length?`, ${o.length} LIVE with no cost data: ${o.join(", ")}`:"")),e.repricedTurns&&r.push(`${e.repricedTurns} turn(s) imputed at current rates (recorded before their model was priced)`),e.unpricedTurns&&r.push(`\u26A0 ${e.unpricedTurns} turn(s) UNPRICED \u2014 total under-reports. Add PER_MTOK rate(s) for: ${(e.unpricedModels||[]).join(", ")}`),r.push(""),r.push("BY MODEL");let s=Object.entries(e.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(` ${Qr(a,26)} ${Qr(gs(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(` ${Qr(a.name||a.id,30)} ${gs(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${Qr(c.label,40)} ${Qr(gs(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))}${Qr(gs(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
391
|
-
`)}import L$ from"node:fs/promises";import F$ from"node:path";var gi=t=>t.payload?.ts??t.ts,P$=12e4,xn=t=>Math.round(t*1e4)/1e4,B$=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function D$(t,{since:e}){let{track:n,events:r,brief:o}=t,s=S=>gi(S)>=e,a=hr([vo(n?.name),vo(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let S of r)if(S.type==="subtask.delegated"&&S.boxId&&S.payload?.task){let I=hr(S.payload.task);I&&i.set(S.boxId,I)}let c=new Map,l=S=>{let I=S||"unattributed";return c.has(I)||c.set(I,{segments:[u(null)],lastTs:null,provider:null}),c.get(I)},u=S=>({textCat:S,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),f=[...r].sort((S,I)=>gi(S)-gi(I)),d=0,p=0,m=0,h={turns:0};for(let S of f){let I=l(S.boxId),M=I.segments[I.segments.length-1],z=gi(S);switch(I.lastTs!=null&&z>I.lastTs&&s(S)&&(M.activeMs+=Math.min(z-I.lastTs,P$)),I.lastTs=z,S.provider&&(I.provider=S.provider),S.type){case"turn.user":{let $=S.payload?.text;if(S.source==="hook"||!String($||"").trim())break;let F=u(hr($));I.segments.push(F),s(S)&&(F.turns+=1);break}case"tool.called":{if(S.source==="transcript")break;let $=S.payload?.tool;$&&(M.tools[$]=(M.tools[$]||0)+1),s(S)&&(M.toolCalls+=1);break}case"cost.recorded":{if(!s(S))break;let $=S.payload||{},F={input:$.tokens?.input||0,output:$.tokens?.output||0,cacheRead:$.tokens?.cacheRead||0,cacheWrite:$.tokens?.cacheWrite??$.tokens?.cacheCreation??0},T=Number($.usd)||0;!T&&$.tokens&&(T=le($.model,F)),!T&&$.tokens&&!la($.model)&&(h.turns+=1),M.usd+=T,M.tokens.input+=F.input,M.tokens.output+=F.output,M.tokens.cacheRead+=F.cacheRead,M.tokens.cacheWrite+=F.cacheWrite;let C=$.model||"unknown";M.models[C]=(M.models[C]||0)+T,S.provider&&(M.provider=S.provider);break}case"handoff.done":s(S)&&(d+=1);break;case"subtask.merged":s(S)&&(p+=1);break;case"error":s(S)&&S.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let y=new Map,b=!1;for(let[S,I]of c)for(let M of I.segments){if(!(M.usd>0||M.activeMs>0||M.turns>0||M.toolCalls>0))continue;let $=M.textCat?.category??i.get(S)?.category??np(M.tools)?.category??a?.category??"uncategorized";y.has($)||y.set($,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let F=y.get($);F.usd+=M.usd,F.activeMs+=M.activeMs,F.turns+=M.turns,F.toolCalls+=M.toolCalls;for(let[C,D]of Object.entries(M.models))F.models[C]=(F.models[C]||0)+D;let T=M.provider||I.provider;T&&(F.providers[T]=(F.providers[T]||0)+M.usd),T==="cursor"&&M.usd===0&&(b=!0)}return{perCategory:y,handoffs:d,merged:p,failed:m,sawCursorActivity:b,unpricedTurns:h.turns}}function _b(t,{windowMs:e=null,now:n=Date.now()}={}){let r=e==null?0:n-e,o=new Map,s=[],a=0,i=!1,c=0;for(let h of t||[]){let{track:y,events:b}=h;if(!b?.length){a+=1;continue}let{perCategory:S,handoffs:I,merged:M,failed:z,sawCursorActivity:$,unpricedTurns:F}=D$(h,{since:r});$&&(i=!0),c+=F;let T={trackId:y?.id,name:y?.name||y?.id,archived:!!y?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:I,subtasksMerged:M,subtasksFailed:z};for(let[C,D]of S){T.usd+=D.usd,T.activeMs+=D.activeMs,T.turns+=D.turns,T.categories[C]={usd:xn(D.usd),activeMs:D.activeMs};for(let[A,g]of Object.entries(D.models))T.models[A]=xn((T.models[A]||0)+g);for(let[A,g]of Object.entries(D.providers))T.providers[A]=xn((T.providers[A]||0)+g);o.has(C)||o.set(C,B$());let it=o.get(C);it.usd+=D.usd,it.activeMs+=D.activeMs,it.turns+=D.turns,it.toolCalls+=D.toolCalls;for(let[A,g]of Object.entries(D.models))it.models[A]=(it.models[A]||0)+g;for(let[A,g]of Object.entries(D.providers))it.providers[A]=(it.providers[A]||0)+g;let ot=it.byTrack.get(y?.id)||{usd:0,activeMs:0};ot.usd+=D.usd,ot.activeMs+=D.activeMs,it.byTrack.set(y?.id,ot)}(T.usd>0||T.activeMs>0||T.turns>0)&&(T.usd=xn(T.usd),T.dominantCategory=j$(T.categories),s.push(T))}let l=[...o.values()].reduce((h,y)=>h+y.usd,0),u=[...o.values()].reduce((h,y)=>h+y.activeMs,0),f=l>0?"usd":"time",d=f==="usd"?l:u,p=new Map((t||[]).map(h=>[h.track?.id,h.track?.name||h.track?.id])),m=[...o.entries()].map(([h,y])=>({category:h,sharePct:d>0?Math.round((f==="usd"?y.usd:y.activeMs)/d*100):0,shareBasis:f,usd:xn(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:Eb(y.models),providers:Eb(y.providers),topWorkstreams:[...y.byTrack.entries()].sort((b,S)=>S[1].usd-b[1].usd||S[1].activeMs-b[1].activeMs).slice(0,2).map(([b,S])=>({trackId:b,name:p.get(b),usd:xn(S.usd),activeMs:S.activeMs}))})).sort((h,y)=>y.usd-h.usd||y.activeMs-h.activeMs);return s.sort((h,y)=>y.usd-h.usd||y.activeMs-h.activeMs),{since:r,capturedAt:n,totalUsd:xn(l),activeMs:u,turns:[...o.values()].reduce((h,y)=>h+y.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var j$=t=>{let e=null;for(let[n,r]of Object.entries(t))(!e||r.usd>e.v.usd||r.usd===e.v.usd&&r.activeMs>e.v.activeMs)&&(e={cat:n,v:r});return e?.cat??null},Eb=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,xn(n)]));var to={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function U$(t){let e=String(t||"7d");return e in to?{key:e,ms:to[e]}:{key:"7d",ms:to["7d"]}}function H$(t,e){let n=new Set((e||[]).filter(o=>o.source==="orchestrator").map(o=>`${o.type}|${o.payload?.ts??"?"}`)),r=[...e||[]];for(let o of t?.events||[]){let s=Ua(o);!s||n.has(`${s.type}|${s.payload?.ts??"?"}`)||r.push({trackId:t.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 W$(t){if(!t?.cwd)return null;try{let n=(await L$.readFile(F$.join(t.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 Ib(t,{window:e="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}:U$(e),f=[];for(let d of t||[]){let p=await L(d),m=H$(d,p);i&&c<o&&(m=m.filter(h=>(h.ts??0)<=c)),f.push({track:d,events:m,brief:await W$(d)})}return{window:l,..._b(f,{windowMs:u,now:c})}}var Sn=36e5,Cb=24*Sn,q$=400,K$=12,eo=t=>Math.round(t*1e4)/1e4;function G$({window:t,from:e,to:n,now:r=Date.now()}={}){let o=Number(e),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(t||"7d")in to?String(t||"7d"):"7d",i=to[a];return{key:a,since:i==null?null:r-i,until:r}}var V$=(t,e)=>t!=null&&e-t<=48*Sn?Sn:Cb;function Rb(t,e){if(e===Sn)return Math.floor(t/Sn)*Sn;let n=new Date(t);return n.setHours(0,0,0,0),n.getTime()}function z$(t,e){if(e===Sn)return t+Sn;let n=new Date(t);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var $b=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),Ob=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},J$=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,eo(n)]));async function Mb(t,e={}){let n=e.now??Date.now(),r=G$({...e,now:n}),o=r.since==null?Cb:V$(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=$b(),f=0,d=0,p=0,m=0,h=0,y=new Set,b=new Map,S=[],I=[],M=[],z=null;for(let T of t||[]){let D=(await L(T)).filter(A=>A.type==="cost.recorded");if(!D.length){T.archivedAt||M.push(T.name||T.id);continue}let it=[];for(let A of D){let g=A.ts??0;s(g)?it.push(A):i(g)&&(h+=da(A).usd)}if(!it.length)continue;let ot=ue(it,{boxLabels:dd(T)});f+=ot.totalUsd,d+=ot.turns,p+=ot.repricedTurns||0,m+=ot.unpricedTurns||0;for(let A of ot.unpricedModels||[])y.add(A);Ob(u,ot.totalTokens);for(let[A,g]of Object.entries(ot.byModel))c[A]||={usd:0,events:0,tokens:$b()},c[A].usd=eo(c[A].usd+g.usd),c[A].events+=g.events,Ob(c[A].tokens,g.tokens);for(let[A,g]of Object.entries(ot.byProvider))l[A]||={usd:0,events:0},l[A].usd=eo(l[A].usd+g.usd),l[A].events+=g.events;for(let A of it){let{usd:g}=da(A);if(!g)continue;let w=Rb(A.ts??0,o),_=b.get(w)??{t:w,total:0,byProvider:{}};_.total+=g;let v=A.provider||"unknown";_.byProvider[v]=(_.byProvider[v]||0)+g,b.set(w,_),(z==null||w<z)&&(z=w)}S.push({id:T.id,name:T.name||T.id,archived:!!T.archivedAt,usd:ot.totalUsd,turns:ot.turns,providers:Object.keys(ot.byProvider)});for(let[A,g]of Object.entries(ot.byBox))I.push({trackId:T.id,trackName:T.name||T.id,boxId:A,...g})}let $=r.since!=null?Rb(r.since,o):z,F=[];for(let T=$,C=0;T!=null&&T<r.until&&C<q$;T=z$(T,o),C++){let D=b.get(T);F.push(D?{t:T,total:eo(D.total),byProvider:J$(D.byProvider)}:{t:T,total:0,byProvider:{}})}return S.sort((T,C)=>C.usd-T.usd),I.sort((T,C)=>C.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:F,fleet:{totalUsd:eo(f),turns:d,repricedTurns:p,unpricedTurns:m,unpricedModels:[...y],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:S.length,tracksSilentLive:M},prior:a!=null?{totalUsd:eo(h),since:r.since-a,until:r.since}:null,tracks:S.slice(0,50),boxes:I.slice(0,K$)}}import no from"node:fs/promises";import X$ from"node:fs";import Rt from"node:path";import Y$ from"node:os";import{execFile as Z$}from"node:child_process";import{promisify as Q$}from"node:util";var tO=Q$(Z$),Nb=process.env.BOSTRAT_STATE_DIR||Rt.join(process.env.HOME||Y$.tmpdir(),".bostrat"),yi=!1,fd=null;async function pd(t,e){let n=await no.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await no.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await no.mkdir(Rt.dirname(e),{recursive:!0}),await no.copyFile(t,e),!0)}var eO=new Set([".claude",".codex",".credentials.json","auth.json",".claude.json","integrations.json"]);async function bi(t,e,{skip:n=null}={}){let r=0,o=await no.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(eO.has(s.name)||n?.has(s.name))continue;let a=Rt.join(t,s.name);s.isDirectory()?r+=await bi(a,Rt.join(e,s.name)):s.isFile()&&(r+=await pd(a,Rt.join(e,s.name))?1:0)}return r}async function Pb({stateDir:t=Nb,dest:e=process.env.BOSTRAT_SYNC_DIR||`${Nb}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await no.mkdir(e,{recursive:!0});let o=0;o+=await pd(Rt.join(t,"tracks.json"),Rt.join(e,"tracks.json"))?1:0,o+=await pd(Rt.join(t,"events.jsonl"),Rt.join(e,"events.jsonl"))?1:0,o+=await bi(Rt.join(t,"events"),Rt.join(e,"events")),o+=await bi(Rt.join(t,"memory"),Rt.join(e,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await bi(Rt.join(u.cwd,".bostrat"),Rt.join(e,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>tO("git",["-C",e,...u]);X$.existsSync(Rt.join(e,".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 Ki(e).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||yi))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,yi&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),yi=!1,fd=null}catch(u){yi=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
392
|
-
`).slice(-3).join(" | ").slice(0,300);f!==
|
|
393
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function
|
|
394
|
-
`).map(s=>s.replace(/\s+$/,"").trim()),n=s=>{let a=null,i=null;for(let c=s+1;c<Math.min(s+5,e.length)&&!/^Current\s/i.test(e[c]);c++){let l=e[c].match(/(\d+)%\s*used/i);l&&a===null&&(a=Number(l[1]));let u=e[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=e.findIndex(i=>s.test(i));return a<0?null:n(a)},o=[];return e.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
|
|
395
|
-
`).map(s=>s.replace(/\s+$/,"")),n=e.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<e.length&&!r.test(e[s]);s++)e[s].trim()&&o.push(e[s].trim());return o.length?o:null}async function
|
|
396
|
-
`),n=!1;for(let r=e.length-1;r>=0;r--){let o=e[r].trim();if(!o||o[0]!=="{"||!o.includes("token_count"))continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s?.payload;if(a?.type!=="token_count")continue;n=!0;let i=a.rate_limits;if(!i)return{session:null,week:null,sawTokenCount:n};if(i.primary||i.secondary)return{session:
|
|
397
|
-
`,{mode:384}),
|
|
386
|
+
_${o} archived_`:"")}async function pO(t,e){let n=await Gn();if(!n)return pt("Slack integration not configured.");let r=t.deps,o=(e.text||"").trim().split(/\s+/).filter(Boolean),s=(o[0]||"help").toLowerCase(),a=o.slice(1);switch(s){case"start":{if(!a.length)return pt("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?(lr(rk(t,n,{name:u,repo:l.path,responseUrl:e.response_url})),pt(`Starting *${Q(u)}* on ${Q(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:dO(u,i)}}case"archive":{if(!a.length)return pt("Usage: `/bostrat archive <track>`");let{match:i,live:c}=_d(await r.listTracks(),a.join(" "));return i?(lr(cO(t,n,{track:i,responseUrl:e.response_url})),pt(`Archiving *${Q(i.name)}*\u2026`)):Id(c,a.join(" "))}case"handoff":{let i=r.knownAgents??iO,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return pt(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:f}=_d(await r.listTracks(),l);return u?(lr(lO(t,n,{track:u,agent:c,responseUrl:e.response_url})),pt(`Handing *${Q(u.name)}* to ${Q(c)}\u2026`)):Id(f,l)}case"stop":{if(!a.length)return pt("Usage: `/bostrat stop <track>`");let{match:i,live:c}=_d(await r.listTracks(),a.join(" "));return i?(lr(uO(t,n,{track:i,responseUrl:e.response_url})),pt(`Stopping *${Q(i.name)}*\u2026`)):Id(c,a.join(" "))}case"list":return pt(await fO(r));default:return pt(aO)}}async function mO(t,e,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Md(o);try{if(await t.deps.answerPrompt(o,{promptId:s,answers:[a]}),i?.prompt?.promptId===s){let{ts:c,question:l}=i.prompt;delete i.prompt,await Ze(),await io(e.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Q(l||"question")}~
|
|
387
|
+
\u2713 *${Q(a)}*`}}]}).catch(()=>{})}}catch(c){await Kt(n.response_url,pt(`Couldn't answer: ${c?.message||c}`))}}async function hO(t,e,n,r){let{trackId:o,offerId:s,action:a}=r,i=t.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=a==="pr"?["commit","push","pr"]:a==="push"?["commit","push"]:null;f&&await i.sayToLead(c.id,si(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 Kt(n.response_url,pt(`Couldn't act on the offer: ${c?.message||c}`))}}async function wO(t,e){let n=e.actions?.[0];if(!n?.action_id)return;let r=await Gn();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 Kt(e.response_url,{...pt(`Starting *${Q(s)}*\u2026`),replace_original:!0}),lr(rk(t,r,{name:s,repo:a==="__fresh__"?null:a,responseUrl:e.response_url}));return}let o;try{o=JSON.parse(n.value)}catch{return}n.action_id==="bostrat-prompt"?lr(mO(t,r,e,o)):n.action_id==="bostrat-ship"&&lr(hO(t,r,e,o))}async function gO(t,e){if(!e||e.type!=="message"||e.bot_id||e.subtype||!e.thread_ts||e.thread_ts===e.ts||!e.text?.trim())return;let n=await oO(e.channel,e.thread_ts);if(!n)return;let r=await Gn();if(r)try{await t.deps.sayToLead(n.trackId,e.text.trim())}catch(o){await ur(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function yO(t){return async e=>{if(e.type==="slash_commands")return pO(t,e.payload);if(e.type==="interactive"){await wO(t,e.payload);return}if(e.type==="events_api"){await gO(t,e.payload?.event);return}}}var bO=new Set(["turn.user","turn.agent","prompt.raised","prompt.answered","pr.offered","pr.offer.answered","pr.state.changed","handoff.done","view.state.changed","mock.state.changed"]);function kO(t,e){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${Q(e.question||"The lead needs an answer")}`}}],r=(e.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:Is(String(o),75)},value:JSON.stringify({trackId:t,promptId:e.promptId,label:String(o)})}))}):n.push({type:"context",elements:[{type:"mrkdwn",text:"_reply in this thread to answer_"}]}),n}async function xO(t,e,n){let r=n.payload||{};if(!r.promptId||e.prompt?.promptId===r.promptId)return;e.prompt&&await io(t.botToken,{channel:e.channel,ts:e.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Q(e.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await ur(t,e,r.question||"The lead needs an answer",kO(n.trackId,r));e.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await Ze()}async function SO(t,e,n){let r=n.payload||{};if(!e.prompt||r.promptId&&e.prompt.promptId!==r.promptId)return;let{ts:o,question:s}=e.prompt;delete e.prompt,await Ze(),await io(t.botToken,{channel:e.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${Q(s||"question")}~
|
|
388
|
+
\u2713 _answered_`}}]})}async function vO(t,e,n){let r=n.payload||{};if(!r.offerId||e.offers?.[r.offerId])return;let o=(i,c,l)=>({type:"button",action_id:"bostrat-ship",text:{type:"plain_text",text:c},value:JSON.stringify({trackId:n.trackId,offerId:r.offerId,action:i}),...l?{style:l}:{}}),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")],a=await ur(t,e,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${Q(Is(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);e.offers=e.offers||{},e.offers[r.offerId]=a.ts,await Ze()}async function TO(t,e,n){let r=n.payload||{},o=e.offers?.[r.offerId];if(!o)return;delete e.offers[r.offerId],await Ze();let s=r.action==="dismiss"?"dismissed":r.action==="pr"?"\u2713 opening a PR":"\u2713 pushing";await io(t.botToken,{channel:e.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function AO(t,e,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??EO)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await Yb(t.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:e.channel,threadTs:e.ts,initialComment:`\u{1F3A8} mock *${Q(o.name)}*${a}${o.group?` (group \`${Q(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${Q(a.name)}*`).join(", ");await ur(t,e,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${Q(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function EO(t,e){let n=await dn(t);if(!n)return null;let r=Ne(n,e);if(!r)return null;let o=await _s.readFile(Jt(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function _O(t,e){if(!bO.has(e.type))return;let n=await Md(e.trackId);if(!n)return;let r=await Gn();if(!r)return;let o=e.payload||{};switch(e.type){case"turn.user":if(n.kickoff||e.actor!=="user"||!o.text)return;n.kickoff=o.text,await Ze(),await $d(r,n);return;case"turn.agent":{if(!o.text)return;let s=sO(o.text);if(!s)return;await ur(r,n,s,[{type:"section",text:{type:"mrkdwn",text:Q(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${La(e.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return xO(r,n,e);case"prompt.answered":return SO(r,n,e);case"pr.offered":return vO(r,n,e);case"pr.offer.answered":return TO(r,n,e);case"pr.state.changed":{if(o.change!=="opened"&&o.change!=="merged")return;let s=o.change==="opened"?"PR opened":"PR merged";await ur(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 Ze(),await $d(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await ur(r,n,`Live preview ${o.name}: ${Ua(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${Q(o.name)}*: ${Ua(o.slug)}
|
|
389
|
+
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return AO(r,n,e,t.deps)}}function ok(t){let e={deps:t};Ut.on("event",s=>{_O(e,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await Gn()||(r&&clearInterval(r),r=null,n=Zb({getToken:async()=>(await Gn())?.appToken,onEnvelope:yO(e),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}import IO from"node:fs/promises";function RO(t){let e=new Map;for(let n of String(t||"").split(`
|
|
390
|
+
`)){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&&e.set(i,{messageId:i,tokens:a,model:o.message?.model||o.model||null})}return[...e.values()]}function $O(t){let e=new Set;for(let n of t)if(n.type==="cost.recorded"){n.payload?.messageId&&e.add(n.payload.messageId);for(let r of n.payload?.messageIds||[])e.add(r)}return e}function OO(t,e){return t?.transcript_path?t.transcript_path:e?.cwd&&e?.sessionId?Me(e.cwd,e.sessionId):e?.cwd?Cc(e.cwd):null}async function sk(t,e){let n=await e(t?.session_id,t?.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(t?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:t.session_id}});break;case"UserPromptSubmit":await i({type:"turn.user",actor:"user",payload:{promptHead:String(t.prompt??t.user_prompt??"").slice(0,160)||null}});break;case"PostToolUse":await i({type:"tool.called",actor:"agent",payload:{tool:t.tool_name??null}});break;case"Stop":await MO(r,o,t,i);break;default:break}return a}async function MO(t,e,n,r){let o=[],s=OO(n,e);if(s)try{o=RO(await IO.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??e?.model??null,messageId:a?.messageId??null}}),!o.length)return;let i=$O(await L(t)),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:me(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}})}import PO from"node:fs";import Cd from"node:fs/promises";import DO from"node:http";import co from"node:path";import Pi from"node:os";import{createRequire as BO}from"node:module";import Nd from"ws";import CO from"node:path";import NO from"node:os";var Rs=process.env.BOSTRAT_STATE_DIR||CO.join(process.env.HOME||NO.tmpdir(),".bostrat");function ak(t=process.env){let e=(t.BOSTRAT_HOST||"").trim();if(e!=="all")return e||(t.BOSTRAT_PLANE_URL?"127.0.0.1":void 0)}function ik(t){let e=String(t||"").trim();if(!e)return"";let n=/^[a-z][a-z0-9+.-]*:\/\//i.test(e)?e:`wss://${e}`,r;try{r=new URL(n)}catch{return e}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()):e}var Ni=(()=>{try{return BO(import.meta.url)("../package.json").version||null}catch{return null}})(),jO=process.env.BOSTRAT_STATE_DIR||co.join(process.env.HOME||Pi.tmpdir(),".bostrat");function LO(t,e){let n=String(t).split(".").map(Number),r=String(e).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 FO({home:t=process.env.HOME||Pi.homedir(),env:e=process.env}={}){let n=r=>{try{return PO.statSync(r),!0}catch{return!1}};return{claude:n(co.join(t,".claude",".credentials.json")),codex:n(co.join(e.CODEX_HOME||co.join(t,".codex"),"auth.json"))}}var Rt=1,ck=1e3,UO=3e4,HO=6e4,WO=1500,qO=500,lk=100,KO=2e3,GO=25e3,VO=6e4;function uk({url:t,token:e,nodeId:n=Pi.hostname(),name:r=Pi.hostname(),listTracks:o,detectAgents:s=null,sampleMetrics:a=null,metricsMs:i=VO,selfUrl:c=`http://127.0.0.1:${process.env.PORT||3362}`,log:l=console}){if(!t||!e)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");t=ik(t);let u=c.replace(/^http/,"ws"),f=new Map,d=new Map,p=co.join(jO,"plane-link","cursor.json"),m={},w=new Map,y=null,b=!1,S=!1,_=ck,O=null,nt=null,R=null,C=null,v=null,D=!1;async function B(){try{m=JSON.parse(await Cd.readFile(p,"utf8"))}catch{m={}}}function lt(){D=!0,!R&&(R=setTimeout(async()=>{if(R=null,!!D){D=!1;try{await Cd.mkdir(co.dirname(p),{recursive:!0}),await Cd.writeFile(p,JSON.stringify(m))}catch(K){l.error("[plane-link] cursor flush failed:",K?.message||K)}}},KO),R.unref?.())}function q(K){return!y||y.readyState!==Nd.OPEN?!1:(y.send(JSON.stringify(K)),!0)}function T(K){for(let{trackId:Y,event:$}of K)w.set($.id,{trackId:Y,seq:$.seq});return q({v:Rt,ch:"events",type:"append",events:K})}async function g(){if(!a)return;let K;try{K=await a()}catch(Y){l.error("[plane-link] metrics sample failed:",Y?.message||Y);return}K&&q({v:Rt,ch:"ctl",type:"metrics",metrics:K})}function h(){a&&(v&&clearInterval(v),g(),v=setInterval(g,i),v.unref?.())}async function A(){let K;try{K=await o()}catch(Y){l.error("[plane-link] backfill listTracks failed:",Y?.message||Y);return}q({v:Rt,ch:"ctl",type:"tracks",ids:K.map(Y=>Y.id).filter(Y=>typeof Y=="string")});for(let Y of K){if(!b)return;let $;try{$=await L(Y)}catch{continue}let z=m[Y.id]??0,xt=$.filter(ht=>ht.seq>z&&typeof ht.id=="string");for(let ht=0;ht<xt.length;ht+=lk){let k=xt.slice(ht,ht+lk).map(P=>({trackId:Y.id,event:P}));if(!T(k))return}}}function E(K){!b||!K?.trackId||typeof K.id!="string"||T([{trackId:K.trackId,event:K}])}let J=new Map;async function W(K){let{reqId:Y,method:$,path:z,headers:xt={},body:ht}=K,k=Object.fromEntries(Object.entries(xt).filter(([,$t])=>$t!=null)),P=new AbortController;J.set(Y,P);try{if(K.host){await it(K,k,P);return}let $t=await fetch(c+z,{method:$,headers:k,body:ht?Buffer.from(ht,"base64"):void 0,signal:P.signal}),Qt=Object.fromEntries($t.headers);for(let Tt of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Qt[Tt];if(Qt["content-type"]||(Qt["content-type"]="application/octet-stream"),q({v:Rt,ch:"control",type:"http_head",reqId:Y,status:$t.status,headers:Qt}),$t.body){for await(let Tt of $t.body)if(!q({v:Rt,ch:"control",type:"http_data",reqId:Y,data:Buffer.from(Tt).toString("base64")}))break}q({v:Rt,ch:"control",type:"http_end",reqId:Y,error:null})}finally{J.delete(Y)}}function it(K,Y,$){let{reqId:z,method:xt,path:ht,body:k}=K,P=new URL(c);return new Promise($t=>{let Qt=DO.request({host:P.hostname,port:P.port,method:xt,path:ht,headers:{...Y,host:K.host},signal:$.signal},Tt=>{let{connection:sf,"keep-alive":LN,"transfer-encoding":FN,upgrade:UN,...X0}=Tt.headers;q({v:Rt,ch:"control",type:"http_head",reqId:z,status:Tt.statusCode||502,headers:X0}),Tt.on("data",Y0=>{q({v:Rt,ch:"control",type:"http_data",reqId:z,data:Buffer.from(Y0).toString("base64")})||Tt.destroy()}),Tt.on("end",()=>{q({v:Rt,ch:"control",type:"http_end",reqId:z,error:null}),$t()}),Tt.on("error",()=>{q({v:Rt,ch:"control",type:"http_end",reqId:z,error:"view upstream error"}),$t()})});Qt.on("error",Tt=>{q({v:Rt,ch:"control",type:"http_end",reqId:z,error:Tt?.message||"view request failed"}),$t()}),k&&Qt.write(Buffer.from(k,"base64")),Qt.end()})}function Pt(){if(S)return;y=new Nd(t,{headers:{authorization:`Bearer ${e}`}}),y.on("open",async()=>{let Y=null;try{Y=await s?.()||null}catch{Y=null}q({v:Rt,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:Ni,providers:FO(),...Y?{agents:Y}:{}})}),y.on("message",Y=>{let $;try{$=JSON.parse(Y)}catch{return}if($.ch==="ctl"&&$.type==="hello_ok"){b=!0,_=ck,O=null,l.log(`[plane-link] connected to plane as ${n}`),$.minNodeVersion&&Ni&&LO(Ni,$.minNodeVersion)&&l.error(`[plane-link] this node is bostrat-node ${Ni}; the plane asks for \u2265${$.minNodeVersion} \u2014 update with: npm install -g bostrat-node`),C&&clearInterval(C),C=setInterval(()=>q({v:Rt,ch:"ctl",type:"ping"}),GO),C.unref?.(),h(),A().catch(z=>l.error("[plane-link] backfill failed:",z?.message||z));return}if($.ch==="ctl"&&$.type==="usage_caps"){ei($.caps??{},{source:"plane",updatedAt:$.updatedAt??Date.now()}).then(z=>l.log(`[plane-link] spend caps ${Object.keys(z.caps).length?`updated (${Object.keys(z.caps).join(", ")})`:"cleared"} from plane`)).catch(z=>l.error("[plane-link] spend-caps push rejected:",z?.message||z));return}if($.ch==="events"&&$.type==="ack"&&Array.isArray($.ids)){for(let z of $.ids){let xt=w.get(z);xt&&(w.delete(z),(m[xt.trackId]??0)<xt.seq&&(m[xt.trackId]=xt.seq))}lt();return}if($.ch==="control"&&$.type==="http_open"){W($).catch(z=>q({v:Rt,ch:"control",type:"http_end",reqId:$.reqId,error:z?.message||"tunnel error"}));return}if($.ch==="control"&&$.type==="http_cancel"){J.get($.reqId)?.abort();return}if($.ch==="attach"&&$.type==="open"){let z=new Nd(u+$.path,Array.isArray($.protocols)?$.protocols:[],$.host?{headers:{host:$.host}}:void 0);f.set($.streamId,z),d.set($.streamId,[]),z.on("open",()=>{let ht=d.get($.streamId)||[];d.delete($.streamId);for(let{buf:k,bin:P}of ht)z.send(k,{binary:P})}),z.on("message",(ht,k)=>{let P=k?ht:Buffer.from(String(ht));q({v:Rt,ch:"attach",type:"data",streamId:$.streamId,data:P.toString("base64"),...k?{bin:!0}:{}})});let xt=()=>{d.delete($.streamId),f.delete($.streamId)&&q({v:Rt,ch:"attach",type:"close",streamId:$.streamId})};z.on("close",xt),z.on("error",xt);return}if($.ch==="attach"&&$.type==="data"){let z=f.get($.streamId);if(!z)return;let xt=Buffer.from($.data,"base64"),ht=$.bin===!0,k=d.get($.streamId);k?k.push({buf:xt,bin:ht}):z.send(xt,{binary:ht});return}if($.ch==="attach"&&$.type==="close"){let z=f.get($.streamId);f.delete($.streamId),d.delete($.streamId),z?.close();return}});let K=()=>{b&&l.log("[plane-link] disconnected from plane"),b=!1,C&&(clearInterval(C),C=null),v&&(clearInterval(v),v=null),w.clear();for(let z of f.values())z.close();f.clear(),d.clear();for(let z of J.values())z.abort();if(J.clear(),S||nt)return;O==null&&(O=Date.now());let Y=Date.now()-O<HO,$=Y?WO+Math.random()*qO:_;nt=setTimeout(()=>{nt=null,Pt()},$),nt.unref?.(),Y||(_=Math.min(_*2,UO))};y.on("close",K),y.on("error",K)}return Ut.on("event",E),B().then(Pt),{stop:()=>{S=!0,Ut.off("event",E),nt&&clearTimeout(nt),R&&clearTimeout(R),C&&clearInterval(C),v&&clearInterval(v),q({v:Rt,ch:"ctl",type:"bye",reason:"shutdown"}),y?.close()},status:()=>({connected:b,nodeId:n,cursor:{...m},inflight:w.size})}}import zO from"node:fs";import dk from"node:fs/promises";import $s from"node:os";import JO from"node:path";var XO=1024*1024,Di=t=>Math.round(t/XO);async function YO(t){try{let e=await dk.statfs(t),n=e.blocks*e.bsize,r=n-e.bavail*e.bsize;return{diskUsedMb:Di(r),diskTotalMb:Di(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function ZO(){try{return(await Oe()).filter(e=>e.isBostrat).length}catch{return null}}async function QO(){let t=0,e=!1;for(let n of["workspaces","worktrees"])try{let r=await dk.readdir(JO.join(Rs,n),{withFileTypes:!0});t+=r.filter(o=>o.isDirectory()).length,e=!0}catch{}return e?t:null}async function fk({stateDir:t=Rs,now:e=Date.now}={}){let n=$s.totalmem(),[r,o,s,a]=await Promise.all([Aa().catch(()=>null),YO(zO.existsSync(t)?t:$s.tmpdir()),ZO(),QO()]),i=$s.cpus()?.length||null,c=$s.loadavg()?.[0]??null;return{ts:e(),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?Di(n-r):null,memTotalMb:Di(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round($s.uptime())}}function Dd(t){let e={};for(let n of t.boxes||[]){let r=n.role&&n.role!=="lead"?` \xB7 ${n.role}`:"";e[n.name]=`${n.name} (${n.agent||"?"}${r})`}return e}var pk=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),mk=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},Pd=t=>Math.round(t*1e4)/1e4;async function hk(t){let e=[],n={},r={},o=0,s=0,a=0,i=0,c=new Set,l=pk(),u=0,f=[];for(let d of t||[]){let p=await L(d),m=he(p,{boxLabels:Dd(d)});if(m.turns===0){d.archivedAt?u++:f.push(d.name||d.id);continue}o+=m.totalUsd,s+=m.turns,a+=m.repricedTurns||0,i+=m.unpricedTurns||0;for(let w of m.unpricedModels||[])c.add(w);mk(l,m.totalTokens);for(let[w,y]of Object.entries(m.byModel))n[w]||={usd:0,events:0,tokens:pk()},n[w].usd=Pd(n[w].usd+y.usd),n[w].events+=y.events,mk(n[w].tokens,y.tokens);for(let[w,y]of Object.entries(m.byProvider))r[w]||={usd:0,events:0},r[w].usd=Pd(r[w].usd+y.usd),r[w].events+=y.events;e.push({id:d.id,name:d.name,usd:m.totalUsd,rollup:m})}return e.sort((d,p)=>p.usd-d.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",fleet:{totalUsd:Pd(o),turns:s,repricedTurns:a,unpricedTurns:i,unpricedModels:[...c],totalTokens:l,byModel:n,byProvider:r,tracksWithCost:e.length,tracksSilent:u+f.length,tracksSilentArchived:u,tracksSilentLive:f},tracks:e}}var Os=t=>`$${t.toFixed(4)}`,lo=(t,e)=>String(t).padEnd(e);function wk(t){let{fleet:e,tracks:n}=t,r=[];r.push(`BOSTRAT COST READ-OUT \xB7 ${t.generatedAt}`),t.pricingBasis&&r.push(`basis: ${t.pricingBasis}`);let o=e.tracksSilentLive||[];r.push(`fleet total ${Os(e.totalUsd)} over ${e.turns} turn(s) \xB7 ${e.tracksWithCost} track(s) with cost, ${e.tracksSilentArchived??e.tracksSilent} silent (archived)`+(o.length?`, ${o.length} LIVE with no cost data: ${o.join(", ")}`:"")),e.repricedTurns&&r.push(`${e.repricedTurns} turn(s) imputed at current rates (recorded before their model was priced)`),e.unpricedTurns&&r.push(`\u26A0 ${e.unpricedTurns} turn(s) UNPRICED \u2014 total under-reports. Add PER_MTOK rate(s) for: ${(e.unpricedModels||[]).join(", ")}`),r.push(""),r.push("BY MODEL");let s=Object.entries(e.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(` ${lo(a,26)} ${lo(Os(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(` ${lo(a.name||a.id,30)} ${Os(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${lo(c.label,40)} ${lo(Os(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))}${lo(Os(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
391
|
+
`)}import oM from"node:fs/promises";import sM from"node:path";var Bi=t=>t.payload?.ts??t.ts,tM=12e4,In=t=>Math.round(t*1e4)/1e4,eM=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function nM(t,{since:e}){let{track:n,events:r,brief:o}=t,s=S=>Bi(S)>=e,a=vr([No(n?.name),No(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let S of r)if(S.type==="subtask.delegated"&&S.boxId&&S.payload?.task){let _=vr(S.payload.task);_&&i.set(S.boxId,_)}let c=new Map,l=S=>{let _=S||"unattributed";return c.has(_)||c.set(_,{segments:[u(null)],lastTs:null,provider:null}),c.get(_)},u=S=>({textCat:S,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),f=[...r].sort((S,_)=>Bi(S)-Bi(_)),d=0,p=0,m=0,w={turns:0};for(let S of f){let _=l(S.boxId),O=_.segments[_.segments.length-1],nt=Bi(S);switch(_.lastTs!=null&&nt>_.lastTs&&s(S)&&(O.activeMs+=Math.min(nt-_.lastTs,tM)),_.lastTs=nt,S.provider&&(_.provider=S.provider),S.type){case"turn.user":{let R=S.payload?.text;if(S.source==="hook"||!String(R||"").trim())break;let C=u(vr(R));_.segments.push(C),s(S)&&(C.turns+=1);break}case"tool.called":{if(S.source==="transcript")break;let R=S.payload?.tool;R&&(O.tools[R]=(O.tools[R]||0)+1),s(S)&&(O.toolCalls+=1);break}case"cost.recorded":{if(!s(S))break;let R=S.payload||{},C={input:R.tokens?.input||0,output:R.tokens?.output||0,cacheRead:R.tokens?.cacheRead||0,cacheWrite:R.tokens?.cacheWrite??R.tokens?.cacheCreation??0},v=Number(R.usd)||0;!v&&R.tokens&&(v=me(R.model,C)),!v&&R.tokens&&!va(R.model)&&(w.turns+=1),O.usd+=v,O.tokens.input+=C.input,O.tokens.output+=C.output,O.tokens.cacheRead+=C.cacheRead,O.tokens.cacheWrite+=C.cacheWrite;let D=R.model||"unknown";O.models[D]=(O.models[D]||0)+v,S.provider&&(O.provider=S.provider);break}case"handoff.done":s(S)&&(d+=1);break;case"subtask.merged":s(S)&&(p+=1);break;case"error":s(S)&&S.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let y=new Map,b=!1;for(let[S,_]of c)for(let O of _.segments){if(!(O.usd>0||O.activeMs>0||O.turns>0||O.toolCalls>0))continue;let R=O.textCat?.category??i.get(S)?.category??$p(O.tools)?.category??a?.category??"uncategorized";y.has(R)||y.set(R,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let C=y.get(R);C.usd+=O.usd,C.activeMs+=O.activeMs,C.turns+=O.turns,C.toolCalls+=O.toolCalls;for(let[D,B]of Object.entries(O.models))C.models[D]=(C.models[D]||0)+B;let v=O.provider||_.provider;v&&(C.providers[v]=(C.providers[v]||0)+O.usd),v==="cursor"&&O.usd===0&&(b=!0)}return{perCategory:y,handoffs:d,merged:p,failed:m,sawCursorActivity:b,unpricedTurns:w.turns}}function yk(t,{windowMs:e=null,now:n=Date.now()}={}){let r=e==null?0:n-e,o=new Map,s=[],a=0,i=!1,c=0;for(let w of t||[]){let{track:y,events:b}=w;if(!b?.length){a+=1;continue}let{perCategory:S,handoffs:_,merged:O,failed:nt,sawCursorActivity:R,unpricedTurns:C}=nM(w,{since:r});R&&(i=!0),c+=C;let v={trackId:y?.id,name:y?.name||y?.id,archived:!!y?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:_,subtasksMerged:O,subtasksFailed:nt};for(let[D,B]of S){v.usd+=B.usd,v.activeMs+=B.activeMs,v.turns+=B.turns,v.categories[D]={usd:In(B.usd),activeMs:B.activeMs};for(let[T,g]of Object.entries(B.models))v.models[T]=In((v.models[T]||0)+g);for(let[T,g]of Object.entries(B.providers))v.providers[T]=In((v.providers[T]||0)+g);o.has(D)||o.set(D,eM());let lt=o.get(D);lt.usd+=B.usd,lt.activeMs+=B.activeMs,lt.turns+=B.turns,lt.toolCalls+=B.toolCalls;for(let[T,g]of Object.entries(B.models))lt.models[T]=(lt.models[T]||0)+g;for(let[T,g]of Object.entries(B.providers))lt.providers[T]=(lt.providers[T]||0)+g;let q=lt.byTrack.get(y?.id)||{usd:0,activeMs:0};q.usd+=B.usd,q.activeMs+=B.activeMs,lt.byTrack.set(y?.id,q)}(v.usd>0||v.activeMs>0||v.turns>0)&&(v.usd=In(v.usd),v.dominantCategory=rM(v.categories),s.push(v))}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((t||[]).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:In(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:gk(y.models),providers:gk(y.providers),topWorkstreams:[...y.byTrack.entries()].sort((b,S)=>S[1].usd-b[1].usd||S[1].activeMs-b[1].activeMs).slice(0,2).map(([b,S])=>({trackId:b,name:p.get(b),usd:In(S.usd),activeMs:S.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:In(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 rM=t=>{let e=null;for(let[n,r]of Object.entries(t))(!e||r.usd>e.v.usd||r.usd===e.v.usd&&r.activeMs>e.v.activeMs)&&(e={cat:n,v:r});return e?.cat??null},gk=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,In(n)]));var uo={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function aM(t){let e=String(t||"7d");return e in uo?{key:e,ms:uo[e]}:{key:"7d",ms:uo["7d"]}}function iM(t,e){let n=new Set((e||[]).filter(o=>o.source==="orchestrator").map(o=>`${o.type}|${o.payload?.ts??"?"}`)),r=[...e||[]];for(let o of t?.events||[]){let s=oi(o);!s||n.has(`${s.type}|${s.payload?.ts??"?"}`)||r.push({trackId:t.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 cM(t){if(!t?.cwd)return null;try{let n=(await oM.readFile(sM.join(t.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 bk(t,{window:e="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}:aM(e),f=[];for(let d of t||[]){let p=await L(d),m=iM(d,p);i&&c<o&&(m=m.filter(w=>(w.ts??0)<=c)),f.push({track:d,events:m,brief:await cM(d)})}return{window:l,...yk(f,{windowMs:u,now:c})}}var Rn=36e5,vk=24*Rn,lM=400,uM=12,fo=t=>Math.round(t*1e4)/1e4;function dM({window:t,from:e,to:n,now:r=Date.now()}={}){let o=Number(e),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(t||"7d")in uo?String(t||"7d"):"7d",i=uo[a];return{key:a,since:i==null?null:r-i,until:r}}var fM=(t,e)=>t!=null&&e-t<=48*Rn?Rn:vk;function kk(t,e){if(e===Rn)return Math.floor(t/Rn)*Rn;let n=new Date(t);return n.setHours(0,0,0,0),n.getTime()}function pM(t,e){if(e===Rn)return t+Rn;let n=new Date(t);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var xk=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),Sk=(t,e)=>{for(let n of Object.keys(t))t[n]+=e?.[n]||0},mM=t=>Object.fromEntries(Object.entries(t).map(([e,n])=>[e,fo(n)]));async function Tk(t,e={}){let n=e.now??Date.now(),r=dM({...e,now:n}),o=r.since==null?vk:fM(r.since,r.until),s=v=>(r.since==null||v>=r.since)&&v<r.until,a=r.since!=null?r.until-r.since:null,i=v=>a!=null&&v>=r.since-a&&v<r.since,c={},l={},u=xk(),f=0,d=0,p=0,m=0,w=0,y=new Set,b=new Map,S=[],_=[],O=[],nt=null;for(let v of t||[]){let B=(await L(v)).filter(T=>T.type==="cost.recorded");if(!B.length){v.archivedAt||O.push(v.name||v.id);continue}let lt=[];for(let T of B){let g=T.ts??0;s(g)?lt.push(T):i(g)&&(w+=Ln(T).usd)}if(!lt.length)continue;let q=he(lt,{boxLabels:Dd(v)});f+=q.totalUsd,d+=q.turns,p+=q.repricedTurns||0,m+=q.unpricedTurns||0;for(let T of q.unpricedModels||[])y.add(T);Sk(u,q.totalTokens);for(let[T,g]of Object.entries(q.byModel))c[T]||={usd:0,events:0,tokens:xk()},c[T].usd=fo(c[T].usd+g.usd),c[T].events+=g.events,Sk(c[T].tokens,g.tokens);for(let[T,g]of Object.entries(q.byProvider))l[T]||={usd:0,events:0},l[T].usd=fo(l[T].usd+g.usd),l[T].events+=g.events;for(let T of lt){let{usd:g}=Ln(T);if(!g)continue;let h=kk(T.ts??0,o),A=b.get(h)??{t:h,total:0,byProvider:{}};A.total+=g;let E=T.provider||"unknown";A.byProvider[E]=(A.byProvider[E]||0)+g,b.set(h,A),(nt==null||h<nt)&&(nt=h)}S.push({id:v.id,name:v.name||v.id,archived:!!v.archivedAt,usd:q.totalUsd,turns:q.turns,providers:Object.keys(q.byProvider)});for(let[T,g]of Object.entries(q.byBox))_.push({trackId:v.id,trackName:v.name||v.id,boxId:T,...g})}let R=r.since!=null?kk(r.since,o):nt,C=[];for(let v=R,D=0;v!=null&&v<r.until&&D<lM;v=pM(v,o),D++){let B=b.get(v);C.push(B?{t:v,total:fo(B.total),byProvider:mM(B.byProvider)}:{t:v,total:0,byProvider:{}})}return S.sort((v,D)=>D.usd-v.usd),_.sort((v,D)=>D.usd-v.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:C,fleet:{totalUsd:fo(f),turns:d,repricedTurns:p,unpricedTurns:m,unpricedModels:[...y],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:S.length,tracksSilentLive:O},prior:a!=null?{totalUsd:fo(w),since:r.since-a,until:r.since}:null,tracks:S.slice(0,50),boxes:_.slice(0,uM)}}import po from"node:fs/promises";import hM from"node:fs";import Nt from"node:path";import wM from"node:os";import{execFile as gM}from"node:child_process";import{promisify as yM}from"node:util";var bM=yM(gM),Ak=process.env.BOSTRAT_STATE_DIR||Nt.join(process.env.HOME||wM.tmpdir(),".bostrat"),ji=!1,Bd=null;async function jd(t,e){let n=await po.stat(t).catch(()=>null);if(!n?.isFile())return!1;let r=await po.stat(e).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await po.mkdir(Nt.dirname(e),{recursive:!0}),await po.copyFile(t,e),!0)}var kM=new Set([".claude",".codex",".credentials.json","auth.json",".claude.json","integrations.json"]);async function Li(t,e,{skip:n=null}={}){let r=0,o=await po.readdir(t,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(kM.has(s.name)||n?.has(s.name))continue;let a=Nt.join(t,s.name);s.isDirectory()?r+=await Li(a,Nt.join(e,s.name)):s.isFile()&&(r+=await jd(a,Nt.join(e,s.name))?1:0)}return r}async function Ek({stateDir:t=Ak,dest:e=process.env.BOSTRAT_SYNC_DIR||`${Ak}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await po.mkdir(e,{recursive:!0});let o=0;o+=await jd(Nt.join(t,"tracks.json"),Nt.join(e,"tracks.json"))?1:0,o+=await jd(Nt.join(t,"events.jsonl"),Nt.join(e,"events.jsonl"))?1:0,o+=await Li(Nt.join(t,"events"),Nt.join(e,"events")),o+=await Li(Nt.join(t,"memory"),Nt.join(e,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await Li(Nt.join(u.cwd,".bostrat"),Nt.join(e,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>bM("git",["-C",e,...u]);hM.existsSync(Nt.join(e,".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 uc(e).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||ji))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,ji&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),ji=!1,Bd=null}catch(u){ji=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
392
|
+
`).slice(-3).join(" | ").slice(0,300);f!==Bd&&(Bd=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)}}}import{execFile as xM}from"node:child_process";import{promisify as SM}from"node:util";var vM=SM(xM),TM=12e4,AM=6e4;function EM(t){return`refs/bostrat/wip/${t}`}function Rk(t=process.env){let e=String(t.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return e!=="0"&&e!=="off"&&e!=="false"}function _M(t=process.env){let e=Number(t.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(e)&&e>0?e:TM}var Fi=(t,e,n=15e3)=>vM("git",["-C",t,...e],{timeout:n,maxBuffer:4*1024*1024}),_k=new Map,Ik=new Set;async function IM(t,{log:e=console}={}){let{id:n,cwd:r}=t??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await Fi(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await Fi(r,["remote","get-url","origin"])}catch{return Ik.has(r)||(Ik.add(r),e.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 ss(r,"bostrat: wip snapshot"),{stdout:s}=await Fi(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(_k.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=EM(n);return await Fi(r,["push","--force","origin",`${o}:${i}`],AM),_k.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(()=>{}),e.log?.(`[wip-sync] ${n}: pushed ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return e.log?.(`[wip-sync] ${n}: push failed (will retry): ${String(o?.message||o).split(`
|
|
393
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function $k({listTracks:t,sweepMs:e=_M(),log:n=console}){let r=!1,o=async()=>{if(!r){r=!0;try{let a=await t()??[];for(let i of a)i?.archivedAt||i?.kind==="setup"||await IM(i,{log:n})}catch(a){n.log?.(`[wip-sync] sweep failed: ${a?.message||a}`)}finally{r=!1}}},s=setInterval(o,e);return s.unref?.(),o(),{stop:()=>clearInterval(s),cycle:o}}import{execFile as RM}from"node:child_process";import{promisify as $M}from"node:util";import{randomUUID as OM}from"node:crypto";import MM from"node:fs";import CM from"node:os";import NM from"node:path";var On=$M(RM),Ui=t=>new Promise(e=>setTimeout(e,t)),Mn=Ec,Ld=process.env.BOSTRAT_USAGE_PROBE_CWD||NM.join(CM.homedir(),".bostrat","usage-probe"),PM=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,DM=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,BM=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,Ok=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,$n=null,Ms=null,Cs=null;async function jM(){try{return await On(M,N("has-session","-t",Mn)),!0}catch{return!1}}function LM(){$n&&clearTimeout($n),$n=setTimeout(()=>FM().catch(()=>{}),PM),$n.unref&&$n.unref()}async function FM(){$n&&(clearTimeout($n),$n=null);try{await On(M,N("kill-session","-t",Mn))}catch{}}async function UM(){let t=Date.now()+BM;for(;;){let e=await Fd().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(e)){await Ui(500);return}if(Date.now()>t)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await Ui(500)}}async function HM(){if(await jM())return;if(!await sn())throw new Error("host tmux server not running \u2014 start globo-tmux.service");MM.mkdirSync(Ld,{recursive:!0}),await tn(Ld);let t=OM();await On(M,N("new-session","-d","-s",Mn,"-c",Ld,"-x","200","-y","60",...Ac()));let e=`${Tc} --session-id ${t}`;await On(M,N("send-keys","-t",Mn,e,"Enter")),await UM()}async function Fd(){let{stdout:t}=await On(M,N("capture-pane","-t",Mn,"-p"));return t}async function WM(){await HM(),await On(M,N("send-keys","-t",Mn,"-l","/usage")),await On(M,N("send-keys","-t",Mn,"Enter"));let t=Date.now()+DM,e="";for(;await Ui(400),e=await Fd(),!(/Current\s+(session|week)/i.test(e)||/\d+%\s*used/i.test(e)||Date.now()>t););return await Ui(900),e=await Fd(),await On(M,N("send-keys","-t",Mn,"Escape")).catch(()=>{}),LM(),e}function qM(t){let e=String(t||"").split(`
|
|
394
|
+
`).map(s=>s.replace(/\s+$/,"").trim()),n=s=>{let a=null,i=null;for(let c=s+1;c<Math.min(s+5,e.length)&&!/^Current\s/i.test(e[c]);c++){let l=e[c].match(/(\d+)%\s*used/i);l&&a===null&&(a=Number(l[1]));let u=e[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=e.findIndex(i=>s.test(i));return a<0?null:n(a)},o=[];return e.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 KM(t){let e=String(t||"").split(`
|
|
395
|
+
`).map(s=>s.replace(/\s+$/,"")),n=e.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<e.length&&!r.test(e[s]);s++)e[s].trim()&&o.push(e[s].trim());return o.length?o:null}async function Mk({fresh:t=!1}={}){return!t&&Ms&&Date.now()-Ms.at<Ok?Ms.data:Cs||(Cs=(async()=>{try{let e=await WM(),n=qM(e),r={...n,contributors:KM(e),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Ms={at:Date.now(),data:r},r}catch(e){let n={session:null,week:null,contributors:null,subscription:!1,error:e?.message||String(e),capturedAt:Date.now()};return Ms={at:Date.now()-(Ok-3e3),data:n},n}finally{Cs=null}})(),Cs)}var GM=262144;function VM(t){let e=Number(t);if(!Number.isFinite(e)||e<0)return null;let n=Math.floor(e/3600),r=Math.round(e%3600/60);return n>=48?`in ${Math.round(n/24)}d`:n>0?`in ${n}h ${r}m`:`in ${Math.max(1,r)}m`}function Ck(t){if(!t||typeof t!="object")return null;let e=Number(t.used_percent);return Number.isFinite(e)?{pct:Math.round(e),resets:VM(t.resets_in_seconds)}:null}function zM(t){let e=String(t||"").split(`
|
|
396
|
+
`),n=!1;for(let r=e.length-1;r>=0;r--){let o=e[r].trim();if(!o||o[0]!=="{"||!o.includes("token_count"))continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s?.payload;if(a?.type!=="token_count")continue;n=!0;let i=a.rate_limits;if(!i)return{session:null,week:null,sawTokenCount:n};if(i.primary||i.secondary)return{session:Ck(i.primary),week:Ck(i.secondary),sawTokenCount:n};if(i.primary_used_percent!=null||i.secondary_used_percent!=null){let c=l=>Number.isFinite(Number(l))?{pct:Math.round(Number(l)),resets:null}:null;return{session:c(i.primary_used_percent),week:c(i.secondary_used_percent),sawTokenCount:n}}return{session:null,week:null,sawTokenCount:n}}return{session:null,week:null,sawTokenCount:n}}async function Ud(t){let e={provider:"codex",supported:!0,contributors:null};try{let n=await Yn(t?.cwd,t?.sessionId);if(!n)return{...e,session:null,week:null,subscription:!1,error:"no codex rollout found for this box yet",capturedAt:Date.now()};let r=await Ft(n.file,GM),o=zM(r);return{...e,session:o.session,week:o.week,subscription:!!(o.session||o.week),capturedAt:n.mtimeMs||Date.now()}}catch(n){return{...e,session:null,week:null,subscription:!1,error:n?.message||String(n),capturedAt:Date.now()}}}async function Nk(t,{fresh:e=!1}={}){let n=new Set;for(let o of t||[])for(let s of o.boxes||[])s.agent&&n.add(s.agent);let r=[];if((n.has("claude")||n.size===0)&&r.push(await Hi({agent:"claude"},{fresh:e})),n.has("codex")){let o=[],s=[...t||[]].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 Ud(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await Ud(null))}return n.has("cursor")&&r.push(await Hi({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function Hi(t,{fresh:e=!1}={}){let n=t?.agent||"claude";return n==="codex"?Ud(t):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await Mk({fresh:e})}}function JM(t=process.env){return!!(te(t)&&t.BOSTRAT_PLANE_TOKEN)}var XM=t=>({authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`}),Wi=(t,e)=>Object.assign(new Error(t),{status:e});async function Pk(t,e){if(!JM(e))throw Wi("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${te(e)}${t}`,{headers:XM(e)})}catch(o){throw Wi(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw Wi(r?.error||`plane replied ${n.status}`,n.status);return r}async function Dk(t,{max:e=10,env:n=process.env}={}){let r=new URLSearchParams({q:t||""});return e&&r.set("max",String(e)),(await Pk(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function Bk(t,{env:e=process.env}={}){if(!t)throw Wi("message id required",400);return(await Pk(`/v1/gmail/messages/${encodeURIComponent(t)}`,e)).message}function Hd(t){let e=t.split(".");if(e.length!==4)return null;let n=0;for(let r of e){let o=Number(r);if(!Number.isInteger(o)||o<0||o>255)return null;n=n*256+o}return n>>>0}function YM(t){let[e,n]=t.split("/"),r=Hd(e);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 jk(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(YM).filter(Boolean)}function ZM(t){let e=t.headers["fly-client-ip"];return typeof e=="string"&&e.trim()?e.trim():(t.socket&&t.socket.remoteAddress||"").replace(/^::ffff:/,"")}function Lk(t){let e=jk(t),n=String(t||"").trim().toLowerCase()==="open";return{open:n,clientIp:ZM,allowed(r){if(n)return!0;let o=Hd((r||"").replace(/^::ffff:/,""));return o==null?!1:e.some(s=>s(o))}}}function Fk(t,e){let n=jk(t);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=Hd(o);if(s==null||!n.some(i=>i(s)))return!1;let a=e(r);return!!(a&&a.view)}}}import Uk from"node:crypto";import qi from"node:fs";import QM from"node:path";function Hk(t=process.env,{stateDir:e=Rs,log:n=console}={}){let r=String(t.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=QM.join(e,"api-token");try{let a=qi.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=Uk.randomBytes(32).toString("base64url");try{qi.mkdirSync(e,{recursive:!0}),qi.writeFileSync(o,s+`
|
|
397
|
+
`,{mode:384}),qi.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function Ns(t){let e=t?.socket?.remoteAddress||"";if(!e)return!1;let n=e.replace(/^::ffff:/,"");return n==="::1"||n==="127.0.0.1"||/^127\./.test(n)}function Wk(t,e){if(!t||!e)return!1;let n=Buffer.from(String(t)),r=Buffer.from(String(e));return n.length!==r.length?!1:Uk.timingSafeEqual(n,r)}function qk(t,e=null){let n=t?.headers?.["x-bostrat-token"];if(typeof n=="string"&&n.trim())return n.trim();let r=t?.headers?.authorization||"",o=/^Bearer\s+(.+)$/i.exec(r)?.[1];if(o)return o.trim();try{return(e??new URL(t.url,"http://node")).searchParams.get("token")||""}catch{return""}}var tC=[/^\/api\/github\/token\b/],eC=t=>tC.some(e=>e.test(t));function Kk({token:t,enforce:e=!0,log:n=console}={}){return function(o,s,a){let i=o.path||o.url||"";if(!i.startsWith("/api"))return a();let c=qk(o);return Wk(c,t)?a():eC(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"})):!e||Ns(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 Gk(t,e,{enforce:n=!0}={}){return!n||Wk(qk(t),e)?!0:Ns(t)}import nC from"node:http";import rC from"node:net";function Vk(t){if(!t)return null;let e=String(t).replace(/:\d+$/,"").toLowerCase();if(/^[\d.]+$/.test(e)||e.includes(":"))return null;let n=e.split(".");if(n.length<3)return null;let r=n[0];return/^[a-z0-9-]+$/.test(r)?r:null}function Ki(t){let e=Vk(t.headers.host);return e?{slug:e,view:al(e)}:null}var oC=t=>`<!doctype html><meta charset="utf-8">
|
|
398
398
|
<meta http-equiv="refresh" content="3">
|
|
399
399
|
<title>${t.name} \u2014 starting</title>
|
|
400
400
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
401
401
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
402
|
-
<p><b>${t.name}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`;function
|
|
402
|
+
<p><b>${t.name}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`;function zk(t,e,n){let r=Ki(t);if(!r)return n();let{view:o,slug:s}=r;if(!o)return e.statusCode=404,e.setHeader("content-type","text/plain"),e.end(`bostrat: no view named "${s}"`);if(o.state==="starting")return e.statusCode=503,e.setHeader("content-type","text/html"),e.end(oC(o));if(o.state!=="live"&&o.state!=="unhealthy")return e.statusCode=404,e.setHeader("content-type","text/plain"),e.end(`bostrat: view "${s}" is ${o.state} \u2014 restart it from the Views tab`);let a={...t.headers,host:`127.0.0.1:${o.port}`,"x-forwarded-host":t.headers.host,"x-forwarded-proto":t.headers["x-forwarded-proto"]||"http"},i=nC.request({host:"127.0.0.1",port:o.port,method:t.method,path:t.url,headers:a},c=>{e.writeHead(c.statusCode||502,c.headers),c.pipe(e)});i.on("error",()=>{e.headersSent?e.destroy():(e.statusCode=502,e.setHeader("content-type","text/plain"),e.end(`bostrat: view "${s}" did not answer on :${o.port}`))}),t.pipe(i),t.on("aborted",()=>i.destroy())}function Jk(t,e,n){let r=Ki(t);if(!r)return!1;let{view:o,slug:s}=r;if(!o||o.state!=="live"&&o.state!=="unhealthy")return e.write(`HTTP/1.1 404 Not Found\r
|
|
403
403
|
\r
|
|
404
404
|
bostrat: no live view "${s}"\r
|
|
405
|
-
`),e.destroy(),!0;let a=
|
|
405
|
+
`),e.destroy(),!0;let a=rC.connect(o.port,"127.0.0.1",()=>{let c=[`${t.method} ${t.url} HTTP/1.1`];for(let[l,u]of Object.entries(t.headers))l!=="host"&&c.push(`${l}: ${Array.isArray(u)?u.join(", "):u}`);c.push(`host: 127.0.0.1:${o.port}`),a.write(c.join(`\r
|
|
406
406
|
`)+`\r
|
|
407
407
|
\r
|
|
408
|
-
`),n?.length&&a.write(n),e.pipe(a),a.pipe(e)}),i=()=>{a.destroy(),e.destroy()};return a.on("error",i),e.on("error",i),!0}function
|
|
409
|
-
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,UO)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${HO}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!!i){if(i.length>ik)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${ik}-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>ak)throw new Error(`${r.length} bullets \u2014 over the ${ak}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function lk(t,{repos:e=yd()}={}){let n=new Set(e);return(await ye()).filter(r=>r.state==="merged"&&(r.resolvedAt??0)>=t&&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 uk(t,{md:e,day:n=null}){let r=KO(e),o=n==null||n===""?qO():String(n);if(!WO.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await R({id:t.id,cwd:t.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:t.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}}import{execFile as GO}from"node:child_process";import{promisify as VO}from"node:util";var kd=VO(GO),dk=3e4,zO=30*6e4,JO=dk,XO=10*6e4,YO=10080*6e4,ZO=60*6e4,QO=8,tC=1440*6e4,eC=3,fk="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",bd=60*6e4,nC=15*6e4,Ei=new Map,_i=new Map,rC=/could not resolve|not found|no pull requests?/i,oC=t=>rC.test(String(t?.message||t||""));function sC(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<bd)}var pk={fast:JO,slow:XO,decay:ZO};function mk(t,e){return t.armedAt&&e-t.armedAt<zO||sC(t,e)?"fast":e-(t.firstSeen||0)>YO?"decay":"slow"}function aC(t,e){return pk[mk(t,e)]}function Ts(t,e,n,r){let o=t.lanes?.get(K(e.repo,e.number))||{};return Za({change:n,repo:e.repo,number:Number(e.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function iC(t,e){Le("watch");try{let{stdout:n}=await kd("gh",["pr","view",String(e),"--repo",t,"--json",fk],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await ae({slug:t})}});return JSON.parse(n||"null")}catch(n){throw fn(n)&&pn(n),n}}async function cC(t){Le("watch");try{let{stdout:e}=await kd("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",fk],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await ae({slug:t})}}),n=new Map;for(let r of JSON.parse(e||"[]"))n.set(Number(r.number),r);return n}catch(e){throw fn(e)&&pn(e),e}}async function lC(){let t=new Map;for(let e of await at().catch(()=>[]))t.set(e.id,e);return t}async function uC(t,e){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...e}})}async function dC(t,e){let n=await Yn(t);return n?Fr(n,e):null}async function fC(t,e,n){let r=await Yn(t);if(!r)throw new Error(`no local checkout for ${t}`);return Ur(r,e,{method:n})}async function pC(t,e){let n=await Yn(t);if(!n)throw new Error(`no local checkout for ${t}`);return ns(n,e)}async function mC(t,e){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...e}})}async function ro(t,e,n,r,o){if(!await r.disarm(t.repo,t.number).catch(()=>null))return;let a=n.get(t.trackId);a&&await r.emitDisarmed(a,{number:Number(t.number),url:e?.url||t.url,title:e?.title||t.title,branch:e?.headRefName||t.headRef,repo:t.repo,reason:o}).catch(()=>{})}var hC=10*6e4;async function wC(t,e,n){let r=t.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>tC){await ro(t,null,e,n,"auto-merge expired \u2014 checks never settled within 24h");return}let o=await n.detail(t.repo,t.number).catch(()=>null);if(!o)return;if(o.checks.failed>0){let a=o.checks.failing.map(i=>i.name).join(", ");await ro(t,o,e,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await ro(t,o,e,n,`conflicts with ${o.baseRefName}`);return}if(!o.checks.total){let a=Math.max(t.autoMerge?.armedAt||0,Date.parse(o.updatedAt||"")||0);if((n.now??Date.now())-a<hC)return;await ro(t,o,e,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(t.repo,t.number).catch(()=>null);if(s?.autoMerge){if(o.mergeStateStatus==="BEHIND"){try{await n.updateBranch(t.repo,t.number)}catch(a){let i=String(a?.message||a).trim();/no new commits|already up.to.date/i.test(i)||await ro(t,o,e,n,i||`couldn't update with ${o.baseRefName}`)}return}try{await n.merge(t.repo,t.number,s.autoMerge.method||"merge")}catch(a){if(/already merged|not open|is closed/i.test(String(a?.message||a)))return;await ro(t,o,e,n,String(a?.message||a).trim()||"merge failed");return}await hk(t,{...o,state:"MERGED"},e,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function xs(t,e,n){await R({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:e,...n}})}async function gC(t,e){try{return await kd("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Ss(t,e,n,r=()=>!0){return(await L(t).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===e&&s.payload?.number===Number(n)&&r(s))}var vs=(t,e)=>({number:Number(t.number),url:e?.url||t.url,title:e?.title||t.title,branch:e?.headRefName||t.headRef,repo:t.repo});async function yC(t,e,n,r){let o=r.now??Date.now(),s=e.headRefOid||null,a=n.get(t.trackId)||null;if(s&&(t.ciFix?.headSha&&s!==t.ciFix.headSha&&await Ra(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await Oa(t.repo,t.number,{headSha:s}).catch(()=>{})),s&&s!==t.lastHeadSha){let i=!t.lastHeadSha,c=!!(t.ciFix||t.reconcile||t.ciFixAwait||t.reconcileAwait),l=!!t.baseFoldAt&&o-t.baseFoldAt<nC;await mh(t.repo,t.number,s).catch(()=>{}),t.baseFoldAt&&await ph(t.repo,t.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Ss(a,"pushed",t.number,f=>f.payload?.sha===s)&&await xs(a,"pushed",{...vs(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>bd)await $a(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i?.checks){if(i.checks.failed>0)await $a(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Ts(r,t,"ci-fixed",i.checks.settledAt||null),l=t.ciFixAwait.since||0;a&&!await Ss(a,"ci-fixed",t.number,u=>(u.payload?.ts||0)>=l)&&await xs(a,"ci-fixed",{...vs(t,e),passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await $a(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>bd)await Ca(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Ca(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Ts(r,t,"reconciled",null);let c=t.reconcileAwait.since||0;a&&!await Ss(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await xs(a,"reconciled",{...vs(t,e),base:i.baseRefName||null}).catch(()=>{}),await Ca(t.repo,t.number).catch(()=>{})}}}}async function bC(t,e,n,r){let o=Ts(r,t,"closed",Date.parse(e?.closedAt||"")||null),s=n.get(t.trackId);s&&!await Ss(s,"closed",t.number)&&await xs(s,"closed",{...vs(t,e),githubAt:o.githubAt,latencyMs:o.latencyMs,lane:o.lane}).catch(()=>{}),await r.notifyIfResolved(t.repo,t.number,"closed").catch(()=>{}),await r.mark(t.repo,t.number,"closed").catch(()=>{})}async function kC(t,e,n){Ts(n,t,"lost",null);let r=e.get(t.trackId);r&&!await Ss(r,"lost",t.number)&&await xs(r,"lost",vs(t,null)).catch(()=>{}),await n.notifyIfResolved(t.repo,t.number,"lost").catch(()=>{}),await n.mark(t.repo,t.number,"lost").catch(()=>{})}async function hk(t,e,n,r,o={}){let s=Ts(r,t,o.change||"merged",o.githubAt??(Date.parse(e.mergedAt||"")||null)),a=n.get(t.trackId);a&&((await L(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(t.number))||await r.emitMerged(a,{number:Number(t.number),url:e.url||t.url,title:e.title||t.title,branch:e.headRefName||t.headRef,repo:t.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(t.repo,t.number).catch(()=>{}),await r.archiveIfArmed(t.repo,t.number).catch(()=>{}),await r.mark(t.repo,t.number,"merged").catch(()=>{})}async function xC(t={}){let{now:e=Date.now(),auth:n=bu,view:r=iC,list:o=cC,links:s=wh,trackMap:a=lC,emitMerged:i=uC,mark:c=cn,archiveIfArmed:l=hn,notifyIfArmed:u=wn,notifyIfResolved:f=Gu,batchThreshold:d=eC,detail:p=dC,merge:m=fC,updateBranch:h=pC,lookup:y=Ot,disarm:b=Uo,emitDisarmed:S=mC,localHasCommit:I=gC}=t;if(!(await n().catch(()=>null))?.authenticated||Ka()>0)return 0;let $=(await s().catch(()=>[])).filter(A=>e-(Ei.get(K(A.repo,A.number))||0)>=aC(A,e));if(!$.length)return 0;let F=Date.now(),T=new Map,C=new Map;for(let A of $){let g=mk(A,e);T.set(K(A.repo,A.number),{lane:g,intervalMs:pk[g]}),Ei.set(K(A.repo,A.number),e),C.has(A.repo)||C.set(A.repo,[]),C.get(A.repo).push(A)}let D=await a().catch(()=>new Map),it={now:e,lanes:T,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,detail:p,merge:m,updateBranch:h,lookup:y,disarm:b,emitDisarmed:S,localHasCommit:I};for(let[A,g]of C){let w,_=new Set;if(g.length>d)w=await o(A).catch(()=>null);else{w=new Map;for(let v of g)try{let O=await r(A,v.number);O?.number!=null&&w.set(Number(O.number),O)}catch(O){oC(O)&&_.add(K(v.repo,v.number))}}if(w)for(let v of g){let O=K(v.repo,v.number),E=w.get(Number(v.number));if(!E){if(_.has(O)){let N=(_i.get(O)||0)+1;_i.set(O,N),N>=QO&&(await kC(v,D,it),_i.delete(O),Ei.delete(O))}continue}_i.delete(O),E.state==="MERGED"||E.state==="CLOSED"?(E.state==="MERGED"?await hk(v,E,D,it):await bC(v,E,D,it),Ei.delete(O)):(await yC(v,E,D,it).catch(()=>{}),v.autoMerge&&await wC(v,D,it).catch(()=>{}))}}let ot={};for(let{lane:A}of T.values())ot[A]=(ot[A]||0)+1;return console.log(`[pr-watch] probed ${$.length} link(s) (${Object.entries(ot).map(([A,g])=>`${A} ${g}`).join(", ")}) in ${Date.now()-F}ms`),$.length}function wk(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await xC()}catch{}finally{t=!1}}},dk)}import Ke from"node:fs/promises";import oo from"node:path";import vk from"node:os";import gk from"node:fs/promises";import Ii from"node:path";import{execFile as SC}from"node:child_process";import{promisify as vC}from"node:util";import{fileURLToPath as TC}from"node:url";var AC=vC(SC),bk=Ii.dirname(TC(import.meta.url)),EC=Ii.resolve(bk,"..","..","client","dist"),_C="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",IC=new Map([..._C].map((t,e)=>[t,e]));function RC(t){let e=[],n=0,r=0;for(let o of t){let s=IC.get(o);if(s===void 0)return e;n+=(s&31)<<r,s&32?r+=5:(e.push(n&1?-(n>>>1):n>>>1),n=0,r=0)}return e}function $C(t){let e=[],n=0,r=0,o=0;for(let s of t.split(";")){let a=[],i=0;if(s)for(let c of s.split(",")){let l=RC(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])))}e.push(a)}return e}function OC(t,e){if(!t||t.length===0||t[0][0]>e)return null;let n=0,r=t.length-1;for(;n<r;){let o=n+r+1>>1;t[o][0]<=e?n=o:r=o-1}return t[n]}var yk=new Map;async function CC(t){let e;try{e=await gk.stat(t)}catch{return null}let n=yk.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await gk.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:$C(r.mappings||"")};return yk.set(t,o),o}catch{return null}}var xd=null;function MC(){return xd||(xd=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):AC("git",["rev-parse","--short","HEAD"],{cwd:Ii.resolve(bk,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),xd}var NC=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function PC(t,{distDir:e=EC}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(NC)){let[a,i,c,l]=s,u=await CC(Ii.join(e,"assets",`${i}.map`)),f=u?OC(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(t.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(t.slice(r)),{stack:n.join(""),symbolicated:o}}async function kk(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await MC();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await PC(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var BC=()=>process.env.BOSTRAT_STATE_DIR||oo.join(process.env.HOME||vk.tmpdir(),".bostrat"),En=()=>oo.join(BC(),"telemetry"),Tk=()=>oo.join(En(),".relay-offsets.json"),DC=50,jC=14,LC=3e4,xk=500,Gt=(t,e)=>typeof t=="string"?t.slice(0,e):"";function FC(t,e){if(!t||typeof t!="object")return null;let n=Gt(t.name,64);if(!n)return null;let r=Number.isFinite(t.ts)?t.ts:e,o={name:n,ts:Math.abs(r-e)<168*3600*1e3?r:e,receivedAt:e,anonId:Gt(t.anonId,64),sessionId:Gt(t.sessionId,64),userId:Gt(t.userId,64),release:Gt(t.release,64),appVersion:Gt(t.appVersion,32),route:Gt(t.route,128),deviceClass:t.deviceClass==="phone"||t.deviceClass==="desktop"?t.deviceClass:""};return t.props&&typeof t.props=="object"&&!Array.isArray(t.props)&&(JSON.stringify(t.props).length<=8192?o.props=t.props:o.props={clamped:!0}),t.error&&typeof t.error=="object"&&(o.error={kind:Gt(t.error.kind,256),message:Gt(t.error.message,8192),stack:Gt(t.error.stack,32768),fingerprint:Gt(t.error.fingerprint,64)}),Array.isArray(t.breadcrumbs)&&(o.breadcrumbs=t.breadcrumbs.slice(-50).map(s=>({t:Number.isFinite(s?.t)?s.t:0,n:Gt(s?.n,64),r:Gt(s?.r,128)}))),o}var Ak=t=>new Date(t).toISOString().slice(0,10),Sk=0;async function Ek(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,DC):[]).map(s=>FC(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await kk(s);await Ke.mkdir(En(),{recursive:!0});let o=oo.join(En(),`${Ak(e)}.ndjson`);return await Ke.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
410
|
-
`).join(""),"utf8"),e-
|
|
411
|
-
`).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=xk){let p=f.slice(d,d+xk).map(h=>HC(h)),m=await t(`${e.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.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 Rk(r)}return{sent:o,disabled:!1}}var Sd=null,vd=!1;function $k(){Sd||(Sd=setInterval(async()=>{if(!vd){vd=!0;try{await WC()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{vd=!1}}},LC),Sd.unref?.())}import Oi from"node:fs/promises";import Pk from"node:path";import qC from"node:os";var Bk=()=>process.env.BOSTRAT_STATE_DIR||Pk.join(process.env.HOME||qC.tmpdir(),".bostrat"),Dk=()=>Pk.join(Bk(),"dictation-glossary.json"),KC=100,GC=500,VC=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Ci=null,Td=null,Ad=Promise.resolve();function Ok(){let t={},e=Date.now();for(let n of VC)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function jk(){return Ci||(Td??=Oi.readFile(Dk(),"utf8").then(t=>JSON.parse(t)?.terms??Ok()).catch(()=>Ok()).then(t=>Ci=t).finally(()=>Td=null),Td)}async function Ck(){let t=JSON.stringify({terms:Ci},null,2),e=Dk(),n=`${e}.${process.pid}.tmp`;await Oi.mkdir(Bk(),{recursive:!0}),await Oi.writeFile(n,t),await Oi.rename(n,e)}function zC(){return Ad=Ad.then(Ck,Ck),Ad.catch(()=>{})}async function Ed(){return Object.values(await jk()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var JC=t=>Math.max(1,Math.ceil(t.length/4));async function XC(){let t=[],e=0;for(let{term:n}of await Ed()){let r=JC(n);if(!(e+r>GC)&&(t.push(n),e+=r,t.length>=KC))break}return t}async function YC(t,e){let n=await jk(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var Mk=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),$i=t=>t.toLowerCase();function Nk(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function Lk(t,e){let n=Mk(t),r=Mk(e);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]=$i(n[u])===$i(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;)$i(n[c])===$i(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(Nk(r[l])&&i.add(r[l]),l++);for(;l<s;l++)Nk(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await YC(u,"learned");return await zC(),[...i]}var As=null;function ZC(t,e){let n=null;try{n=JSON.parse(e)}catch{}let r=n?.err_code||n?.error_code||n?.category||"";return t===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."}:t===401||t===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 ${t}: ${e.slice(0,300)}`}}async function Fk(){if(!As)return null;let t=await Mo();if(!t||t.apiKey.slice(-6)!==As.keyTail)return null;let{keyTail:e,...n}=As;return n}async function Uk(t,e){let n=await Mo();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 XC())r.searchParams.append("keyterm",i);let o=await fetch(r,{method:"POST",headers:{Authorization:`Token ${n.apiKey}`,"Content-Type":e||"application/octet-stream"},body:t});if(!o.ok){let i=await o.text().catch(()=>""),c=ZC(o.status,i);c.persistent&&(As={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 As=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}var _d={"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 QC(t){if(!t)return null;let e=String(t).trim();if(_d[e])return e;let n=null;for(let r of Object.keys(_d))e.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function tM(t,e={}){let n=_d[QC(t)];if(!n)return 0;let r=0;for(let o of Object.keys(n))r+=(Number(e[o])||0)*n[o];return r/1e6}function eM(t,e){let n=null;try{n=JSON.parse(e)}catch{}let r=n?.error?.code||n?.error?.type||"";return t===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."}:t===401||t===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 (${t}): ${e.slice(0,300)}`}}var Es=null;async function nM(){if(!Es)return null;let t=await Ta();if(!t||t.apiKey.slice(-6)!==Es.keyTail)return null;let{keyTail:e,...n}=Es;return n}async function rM(t){let e=await fetch("https://api.openai.com/v1/realtime/client_secrets",{method:"POST",headers:{Authorization:`Bearer ${t.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({session:{type:"realtime",model:t.model,audio:{output:{voice:t.voice}}}})});if(!e.ok){let o=await e.text().catch(()=>""),s=eM(e.status,o);s.persistent&&(Es={code:s.code,message:s.message,at:Date.now(),keyTail:t.apiKey.slice(-6)});let a=new Error(s.message);throw a.status=s.status,a.code=s.code,a}Es=null;let n=await e.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 oM={openai:rM};async function Hk(){let t=await Ta();return t?{configured:!0,provider:t.provider,model:t.model,voice:t.voice,problem:await nM()}:{configured:!1}}async function Wk(){let t=await Ta();if(!t){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 e=await oM[t.provider](t);return{provider:t.provider,model:t.model,voice:t.voice,...e}}async function qk(t,{model:e,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=tM(e,s),i=await R(t,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:e||"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 Xk=ut.dirname(aM(import.meta.url)),Kk=Number(process.env.PORT)||3362,Mi=gb(process.env),Yk=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,so=zb(process.env.BOSTRAT_ALLOW_IPS),Cd=Jb(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Ai),Zk=Yb(),Qk=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",x=Se();x.disable("x-powered-by");x.use((t,e,n)=>{if(ks(t)||so.allowed(so.clientIp(t))||Cd.allows(t))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});x.use(rk);x.use(tk({token:Zk,enforce:Qk}));x.use("/api/mocks",Se.json({limit:"1mb"}));x.use("/api/mocks",Se.raw({type:"image/png",limit:"24mb"}));x.use("/api/runbooks",Se.json({limit:"1mb"}));x.use("/api/telemetry",Se.json({limit:"256kb"}));x.use("/api/hook",Se.json({limit:"2mb"}));x.use("/api/dictation",Se.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));x.use("/api/tracks/:id/upload",Se.raw({type:()=>!0,limit:"25mb"}));x.use(Se.json({limit:"64kb"}));x.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:Ht,hostTmuxAlive:await Ye(),autoTriggerPct:Yk})});x.get("/api/sessions",async(t,e)=>{e.json({sessions:await $n()})});x.post("/api/sessions",async(t,e)=>{let{displayName:n,cwd:r}=t.body||{};if(typeof n!="string")return e.status(400).json({error:"displayName (string) required"});try{let o=await On(n,r||void 0);e.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;e.status(a).json({error:s})}});x.delete("/api/sessions/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!gt(n))return e.status(400).json({error:"invalid session name"});if(!await Q(n))return e.status(404).json({error:"session not found"});try{await ce(n),e.json({ok:!0})}catch(r){e.status(500).json({error:`failed to kill: ${r?.message||r}`})}});x.get("/api/context/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!gt(n))return e.status(400).json({error:"invalid session name"});let r=await Q(n);if(!r)return e.status(404).json({error:"session not found"});let o=await xo(r.cwd,r.sessionId);if(!o)return e.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});e.json({available:!0,...o})});x.post("/api/handoff/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!gt(n))return e.status(400).json({error:"invalid session name"});try{let r=await Rp(n);e.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){e.status(409).json({error:r?.message||String(r)})}});x.get("/api/handoff/:name",(t,e)=>{let n=decodeURIComponent(t.params.name),r=ca(n);if(!r)return e.json({running:!1});e.json({running:!r.finishedAt,step:r.step,error:r.error,file:r.file,startedAt:r.startedAt,finishedAt:r.finishedAt})});var Gk=10;x.get("/api/tracks",async(t,e)=>{let n=await at(),r=n.filter(s=>s.archivedAt),o=n;if(t.query.archived!=="all"&&r.length>Gk){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,Gk).map(a=>a.id));o=n.filter(a=>!a.archivedAt||s.has(a.id))}e.json({tracks:o,archivedTotal:r.length,agents:ai,agentAvailability:await Fu(),tickets:await Ea()})});x.get("/api/cost",async(t,e)=>{try{let n=await at();t.query.track&&(n=n.filter(o=>o.id===t.query.track));let r=await Tb(n);if(r.cursorUsage=hu(),t.query.format==="text")return e.type("text/plain").send(Ab(r));e.json(r)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/roundup",async(t,e)=>{try{e.json(await Ib(await at(),{window:t.query.window,from:t.query.from,to:t.query.to}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/spend",async(t,e)=>{try{let n=await Mb(await at(),{window:t.query.window,from:t.query.from,to:t.query.to});n.cursorUsage=hu(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/limits",async(t,e)=>{try{let n=t.query.fresh==="1"||t.query.fresh==="true";e.json(await Wb(await at(),{fresh:n}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/arcs",async(t,e)=>{try{e.json(await Sh(await at()))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/arcs/:key",async(t,e)=>{try{let n=await vh(await at(),t.params.key);if(!n)return e.status(404).json({error:"unknown arc"});e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/arcs/backfill-beats",async(t,e)=>{try{e.json({ok:!0,...await Vy()})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/hook",async(t,e)=>{try{let{mode:n,target:r}=await Dd(t.get(Os),{byToken:vy,legacy:()=>Sy(t.body?.session_id,t.body?.cwd)});if(n==="refused")return Cs("refused",{reason:"unknown-token",hook:t.body?.hook_event_name}),e.json({ok:!1,error:"unknown hook token"});n==="token"&&t.body?.session_id&&r.box?.sessionId&&t.body.session_id!==r.box.sessionId&&Cs("mismatch",{track:r.track.id,box:r.box.name}),Cs(n,{track:r?.track?.id,hook:t.body?.hook_event_name});let o=await hb(t.body||{},()=>r);e.json({ok:!0,events:o.length})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});x.post("/api/telemetry",async(t,e)=>{try{if(!_k(so.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await Ek(t.body||{});e.json({ok:!0,events:n})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});x.get("/api/repos",async(t,e)=>{try{let n=await ve(),r=new Set((await Promise.all(n.map(a=>et(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await uo()).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}));e.json({repos:[...n,...s]})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/github/token",async(t,e)=>{try{e.json(await Bs(String(t.query.repo||"")))}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});x.get("/api/github/status",(t,e)=>{e.json({configured:Ps(),calls:ng(),latency:Ig()})});x.post("/api/github/refresh",(t,e)=>{Vd(),e.json({ok:!0})});x.get("/api/gmail/search",async(t,e)=>{try{let n=await Kb(String(t.query.q||""),{max:Number(t.query.max)||10});e.json({messages:n})}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});x.get("/api/gmail/messages/:id",async(t,e)=>{try{e.json({message:await Gb(String(t.params.id))})}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});function Md(t,e,n){let r=t.workspace;return r?.kind==="multi"?(r.repos??[]).some(o=>e&&o?.base===e||n&&o?.repo===n):!r?.base||!e||r.base===e||n&&r.repo===n}async function Ni({repo:t=null,number:e=null,branch:n=null}){let r=t?await et(t):null;if(r&&e!=null){let s=await Ot(r,e),a=s&&await tt(s.trackId);if(a)return a}return n?(await at()).find(s=>s.workspace?.branch===n&&Md(s,t,r))??null:null}async function tx({repo:t=null,number:e=null,branch:n=null}){let r=await Ni({repo:t,number:e,branch:n}).catch(()=>null);if(r&&!r.archivedAt)return r;if(!n)return null;let o=t?await et(t).catch(()=>null):null;return(await at()).find(a=>!a.archivedAt&&a.workspace?.branch===n&&Md(a,t,o))??null}async function se(t,{number:e,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Ni({repo:s,number:e,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await et(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:t,number:e,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function uM(t){let e=(await at()).filter(n=>!n.archivedAt);for(let n of t??[]){if(!n.prs?.length)continue;let r=await et(n.path);if(r)for(let o of n.prs){if(await Ot(r,o.number))continue;let s=e.find(a=>a.workspace?.branch===o.headRefName&&Md(a,n.path,r));s&&await qt({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}x.get("/api/git/prs",async(t,e)=>{try{let n=await Pg();await uM(n.repos).catch(()=>{}),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/git/prs/merge",async(t,e)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:f}=t.body||{};try{if(s){let h=o||"merge";if(!es.has(h))throw new Error(`invalid merge method: ${h}`);let y=Number(r);if(!Number.isInteger(y)||y<=0)throw new Error("invalid PR number");let b=await et((await Fe(n)).path);if(!b)throw new Error("repo has no GitHub remote");let S=await Ni({repo:n,number:y,branch:l}).catch(()=>null);if(!S)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await ns(n,y),await qt({repo:b,number:y,trackId:S.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Lo(b,y,{merge:{method:h}}))throw new Error("couldn't record the auto-merge arm");return await Hn(b,y,i===!0),await Fo(b,y,c===!0&&i!==!0),ke(),await se("auto-armed",{number:y,url:f,title:u,branch:l,repo:n,track:S,...a?{extra:{updated:!0}}:{}}),e.json({ok:!0,number:y,auto:!0})}let d=await Ur(n,r,{method:o||"squash"});await se("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let p=await et(n).catch(()=>null),m=!1;if(p&&(i===!0&&await Hn(p,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await Fo(p,d.number,!0).catch(()=>{}),await wn(p,d.number).catch(()=>{}),m=await hn(p,d.number).catch(()=>!1),await cn(p,d.number,"merged").catch(()=>{})),i===!0&&!m){let h=await Ni({repo:n,number:d.number,branch:l}).catch(()=>null);h&&!h.archivedAt&&await Jr(h.id).catch(()=>{})}e.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;e.status(m).json({error:p})}});x.post("/api/git/prs/disarm-auto",async(t,e)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=t.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await et((await Fe(n)).path).catch(()=>null),l=c?await Uo(c,i).catch(()=>null):null;try{await Su(n,i)}catch(u){if(!l)throw u}ke(),await se("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),e.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;e.status(l).json({error:c})}});x.post("/api/git/prs/merge-all",async(t,e)=>{let{repo:n,method:r}=t.body||{};try{let o=await et(n).catch(()=>null),s=await Lg(n,{method:r||"merge",onMerged:a=>{se("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&wn(o,a.number).catch(()=>{}).then(()=>hn(o,a.number)).catch(()=>{}).then(()=>cn(o,a.number,"merged")).catch(()=>{})}});e.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;e.status(a).json({error:s})}});x.get("/api/git/prs/merge-all",(t,e)=>{e.json({jobs:jg()})});x.post("/api/git/prs/merge-track",async(t,e)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=t.body||{};try{let i=a||"merge";if(!es.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return e.status(400).json({error:"trackId required"});let c=await tt(n);if(!c)return e.status(404).json({error:`Track ${n} isn't on this node`});let l=(await ye()).filter(d=>d.trackId===c.id&&d.state!=="merged"&&d.state!=="closed");if(!l.length)return e.status(409).json({error:"no open PRs on this Track"});for(let d of l)await Hn(d.repo,d.number,o===!0).catch(()=>{}),await Fo(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 Yn(d.repo);if(!m)throw new Error(`no local checkout for ${d.repo}`);let h=await Fr(m,d.number);f.push({r:p,l:d,repoPath:m,pr:h,plan:Bg(h,{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:h,plan:y}of Dg(f))try{if(y.action==="skip")d.action="skipped",d.reason=y.reason;else if(y.action==="merge")await Ur(m,p.number,{method:i}),await se("merged",{number:p.number,url:h.url,title:h.title,branch:h.headRefName,repo:m,track:c}),await wn(p.repo,p.number).catch(()=>{}),await hn(p.repo,p.number).catch(()=>{}),await cn(p.repo,p.number,"merged").catch(()=>{}),d.action="merged";else{if(y.action==="update-arm"&&(await ns(m,p.number).catch(()=>{}),d.action="updated-armed"),!await Lo(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await se("auto-armed",{number:p.number,url:h.url,title:h.title,branch:h.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(ke(),!u.some(d=>d.ok)){if(o===!0)for(let d of l)await Hn(d.repo,d.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let d of l)await Fo(d.repo,d.number,!1).catch(()=>{});return e.status(502).json({error:u.map(d=>`${d.repo}#${d.number}: ${d.reason}`).join("; "),results:u})}e.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;e.status(l).json({error:c})}});x.post("/api/git/prs/disarm-track",async(t,e)=>{let{trackId:n}=t.body||{};try{if(!n)return e.status(400).json({error:"trackId required"});let r=await tt(n);if(!r)return e.status(404).json({error:`Track ${n} isn't on this node`});let o=(await ye()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await Uo(a.repo,a.number).catch(()=>null),c=await Yn(a.repo).catch(()=>null);c&&await Su(c,a.number).catch(()=>{}),i&&(await se("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}))}ke(),e.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;e.status(s).json({error:o})}});x.post("/api/git/prs/open",async(t,e)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=t.body||{};try{if(!n)return e.status(400).json({error:"trackId required"});let i=await tt(n);if(!i)return e.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=jt(i.workspace),f,d=Array.isArray(a)?a.filter(Boolean):[];if(d.length)f=d.map(h=>{let y=u.find(b=>b.name===h||b.repo===h||b.base===h||b.dir===h);if(!y)throw new Error(`invalid repo "${h}" \u2014 not tagged on this Track`);return y});else if(f=await Yo(i),!f.length)return e.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let h of f)try{let y=await Tu(h.dir,{title:r,body:o,base:s}),b=h.repo||await et(h.dir).catch(()=>null);b&&y.number&&await qt({repo:b,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await se("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:b??h.name}}),p.push({repo:h.name,slug:b??null,ok:!0,url:y.url,number:y.number})}catch(y){p.push({repo:h.name,slug:h.repo??null,ok:!1,error:y?.message||String(y)})}let m=p.filter(h=>h.ok);return m.length?e.json({ok:!0,multi:!0,results:p,url:m[0].url,number:m[0].number}):e.status(502).json({error:p.map(h=>`${h.repo}: ${h.error}`).join("; "),results:p})}let c=await Tu(i.cwd,{title:r,body:o,base:s}),l=await et(i.cwd).catch(()=>null);l&&c.number&&await qt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await se("opened",{number:c.number,url:c.url,title:r,track:i,branch:i.workspace?.branch,extra:{...l?{repo:l}:{}}}),e.json(c)}catch(i){let c=i?.message||String(i),l=/required|invalid/i.test(c)?400:502;e.status(l).json({error:c})}});x.get("/api/tracks/:id/repos",async(t,e)=>{try{let n=await tt(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await Yo(n),o=new Map(r.map(a=>[a.base,a])),s=jt(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}});e.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tracks/:id/repos",async(t,e)=>{let{repo:n}=t.body||{};try{if(!n||typeof n!="string")return e.status(400).json({error:"repo (path or owner/name) required"});let r=await jy(t.params.id,n);e.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;e.status(s).json({error:o})}});x.delete("/api/tracks/:id/repos/:name",async(t,e)=>{try{let n=await Ly(t.params.id,t.params.name);e.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;e.status(o).json({error:r})}});x.post("/api/git/prs/fix",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await Fr(n,r),s=await et(n).catch(()=>null),a=await tx({repo:n,number:r,branch:o.headRefName});if(!a)return e.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=jt(a.workspace).find(b=>b.base===n||s&&b.repo===s)??null,c=a.workspace?.kind==="multi",l=await Fg(n,o.number);if(!l.length)return e.status(409).json({error:"no failing checks on this PR"});let u=c&&i?ut.join("ci",i.name):"ci",f=ut.join(a.cwd,".bostrat",u),d=ut.join(".bostrat",u,`pr-${o.number}-fail.log`);await Ft.promises.mkdir(f,{recursive:!0}),await Ft.promises.writeFile(ut.join(a.cwd,d),l.map(b=>`\u2550\u2550\u2550\u2550 ${b.name}${b.link?` \xB7 ${b.link}`:""} \u2550\u2550\u2550\u2550
|
|
408
|
+
`),n?.length&&a.write(n),e.pipe(a),a.pipe(e)}),i=()=>{a.destroy(),e.destroy()};return a.on("error",i),e.on("error",i),!0}function Xk(t,e){let n=typeof e=="string"?{host:e}:e||{},r=n["x-bostrat-view-base"];return typeof r=="string"&&r?`${n["x-forwarded-proto"]==="https"?"https":"http"}://${t.slug}.${r.replace(/:\d+$/,"")}/`:`http://${t.slug}.${sC(n.host)}/`}function sC(t){if(process.env.BOSTRAT_PUBLIC_HOST)return String(process.env.BOSTRAT_PUBLIC_HOST).replace(/:\d+$/,"");let e=String(t||"").replace(/:\d+$/,"").toLowerCase();if(!e||!e.includes(".")||/^[\d.]+$/.test(e))return"bostrat.globopi";let n=e.split(".");return Vk(e)&&al(n[0])?n.slice(1).join("."):e}var Yk=10,Zk=200,aC=120,iC=24,cC=/^\d{4}-\d{2}-\d{2}$/;function Wd(t=process.env){return(t.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(e=>e.trim()).filter(Boolean)}var lC=(t=Date.now())=>new Date(t).toISOString().slice(0,10);function Qk(t,e=Date.now()){if(!t)return e-24*36e5;let n=/^(\d+)\s*([hd])$/.exec(String(t).trim());if(n)return e-Number(n[1])*(n[2]==="h"?36e5:24*36e5);let r=Date.parse(t);if(!Number.isNaN(r))return r;throw new Error(`unparseable since "${t}" \u2014 use 24h, 7d, or an ISO date`)}function uC(t){if(typeof t!="string"||!t.trim())throw new Error("md (markdown string) required");let e=Uo(t);if(e.length)throw new Error(`changelog entry appears to contain a secret VALUE (${e.join(", ")}) \u2014 entries ship off-box; never put a credential in one. Replace it and retry.`);let n=null,r=[];for(let o of t.split(`
|
|
409
|
+
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,aC)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${iC}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!!i){if(i.length>Zk)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${Zk}-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>Yk)throw new Error(`${r.length} bullets \u2014 over the ${Yk}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function t0(t,{repos:e=Wd()}={}){let n=new Set(e);return(await ve()).filter(r=>r.state==="merged"&&(r.resolvedAt??0)>=t&&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 e0(t,{md:e,day:n=null}){let r=uC(e),o=n==null||n===""?lC():String(n);if(!cC.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await I({id:t.id,cwd:t.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:t.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}}import{execFile as dC}from"node:child_process";import{promisify as fC}from"node:util";var Kd=fC(dC),n0=3e4,pC=30*6e4,mC=n0,hC=10*6e4,wC=10080*6e4,gC=60*6e4,yC=8,bC=1440*6e4,kC=3,r0="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",qd=60*6e4,xC=15*6e4,Gi=new Map,Vi=new Map,SC=/could not resolve|not found|no pull requests?/i,vC=t=>SC.test(String(t?.message||t||""));function TC(t,e){return[t.ciFix?.delegatedAt,t.reconcile?.delegatedAt,t.ciFixAwait?.since,t.reconcileAwait?.since].some(n=>n&&e-n<qd)}var o0={fast:mC,slow:hC,decay:gC};function s0(t,e){return t.armedAt&&e-t.armedAt<pC||TC(t,e)?"fast":e-(t.firstSeen||0)>wC?"decay":"slow"}function AC(t,e){return o0[s0(t,e)]}function js(t,e,n,r){let o=t.lanes?.get(V(e.repo,e.number))||{};return wi({change:n,repo:e.repo,number:Number(e.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function EC(t,e){Ge("watch");try{let{stdout:n}=await Kd("gh",["pr","view",String(e),"--repo",t,"--json",r0],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await de({slug:t})}});return JSON.parse(n||"null")}catch(n){throw kn(n)&&xn(n),n}}async function _C(t){Ge("watch");try{let{stdout:e}=await Kd("gh",["pr","list","--repo",t,"--state","all","--limit","100","--json",r0],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await de({slug:t})}}),n=new Map;for(let r of JSON.parse(e||"[]"))n.set(Number(r.number),r);return n}catch(e){throw kn(e)&&xn(e),e}}async function IC(){let t=new Map;for(let e of await at().catch(()=>[]))t.set(e.id,e);return t}async function RC(t,e){await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...e}})}async function $C(t,e){let n=await sr(t);return n?Xr(n,e):null}async function OC(t,e,n){let r=await sr(t);if(!r)throw new Error(`no local checkout for ${t}`);return Yr(r,e,{method:n})}async function MC(t,e){let n=await sr(t);if(!n)throw new Error(`no local checkout for ${t}`);return hs(n,e)}async function CC(t,e){await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...e}})}async function mo(t,e,n,r,o){if(!await r.disarm(t.repo,t.number).catch(()=>null))return;let a=n.get(t.trackId);a&&await r.emitDisarmed(a,{number:Number(t.number),url:e?.url||t.url,title:e?.title||t.title,branch:e?.headRefName||t.headRef,repo:t.repo,reason:o}).catch(()=>{})}var NC=10*6e4;async function PC(t,e,n){let r=t.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>bC){await mo(t,null,e,n,"auto-merge expired \u2014 checks never settled within 24h");return}let o=await n.detail(t.repo,t.number).catch(()=>null);if(!o)return;if(o.checks.failed>0){let a=o.checks.failing.map(i=>i.name).join(", ");await mo(t,o,e,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await mo(t,o,e,n,`conflicts with ${o.baseRefName}`);return}if(!o.checks.total){let a=Math.max(t.autoMerge?.armedAt||0,Date.parse(o.updatedAt||"")||0);if((n.now??Date.now())-a<NC)return;await mo(t,o,e,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(t.repo,t.number).catch(()=>null);if(s?.autoMerge){if(o.mergeStateStatus==="BEHIND"){try{await n.updateBranch(t.repo,t.number)}catch(a){let i=String(a?.message||a).trim();/no new commits|already up.to.date/i.test(i)||await mo(t,o,e,n,i||`couldn't update with ${o.baseRefName}`)}return}try{await n.merge(t.repo,t.number,s.autoMerge.method||"merge")}catch(a){if(/already merged|not open|is closed/i.test(String(a?.message||a)))return;await mo(t,o,e,n,String(a?.message||a).trim()||"merge failed");return}await a0(t,{...o,state:"MERGED"},e,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function Ps(t,e,n){await I({id:t.id,cwd:t.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:e,...n}})}async function DC(t,e){try{return await Kd("git",["-C",t.cwd,"cat-file","-e",`${e}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Ds(t,e,n,r=()=>!0){return(await L(t).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===e&&s.payload?.number===Number(n)&&r(s))}var Bs=(t,e)=>({number:Number(t.number),url:e?.url||t.url,title:e?.title||t.title,branch:e?.headRefName||t.headRef,repo:t.repo});async function BC(t,e,n,r){let o=r.now??Date.now(),s=e.headRefOid||null,a=n.get(t.trackId)||null;if(s&&(t.ciFix?.headSha&&s!==t.ciFix.headSha&&await Wa(t.repo,t.number,{headSha:s}).catch(()=>{}),t.reconcile?.headSha&&s!==t.reconcile.headSha&&await Ka(t.repo,t.number,{headSha:s}).catch(()=>{})),s&&s!==t.lastHeadSha){let i=!t.lastHeadSha,c=!!(t.ciFix||t.reconcile||t.ciFixAwait||t.reconcileAwait),l=!!t.baseFoldAt&&o-t.baseFoldAt<xC;await Wh(t.repo,t.number,s).catch(()=>{}),t.baseFoldAt&&await Hh(t.repo,t.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Ds(a,"pushed",t.number,f=>f.payload?.sha===s)&&await Ps(a,"pushed",{...Bs(t,e),sha:s}).catch(()=>{})}if(t.ciFixAwait)if(o-(t.ciFixAwait.since||0)>qd)await qa(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i?.checks){if(i.checks.failed>0)await qa(t.repo,t.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=js(r,t,"ci-fixed",i.checks.settledAt||null),l=t.ciFixAwait.since||0;a&&!await Ds(a,"ci-fixed",t.number,u=>(u.payload?.ts||0)>=l)&&await Ps(a,"ci-fixed",{...Bs(t,e),passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await qa(t.repo,t.number).catch(()=>{})}}}if(t.reconcileAwait)if(o-(t.reconcileAwait.since||0)>qd)await Ga(t.repo,t.number).catch(()=>{});else{let i=await r.detail(t.repo,t.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Ga(t.repo,t.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){js(r,t,"reconciled",null);let c=t.reconcileAwait.since||0;a&&!await Ds(a,"reconciled",t.number,l=>(l.payload?.ts||0)>=c)&&await Ps(a,"reconciled",{...Bs(t,e),base:i.baseRefName||null}).catch(()=>{}),await Ga(t.repo,t.number).catch(()=>{})}}}}async function jC(t,e,n,r){let o=js(r,t,"closed",Date.parse(e?.closedAt||"")||null),s=n.get(t.trackId);s&&!await Ds(s,"closed",t.number)&&await Ps(s,"closed",{...Bs(t,e),githubAt:o.githubAt,latencyMs:o.latencyMs,lane:o.lane}).catch(()=>{}),await r.notifyIfResolved(t.repo,t.number,"closed").catch(()=>{}),await r.mark(t.repo,t.number,"closed").catch(()=>{})}async function LC(t,e,n){js(n,t,"lost",null);let r=e.get(t.trackId);r&&!await Ds(r,"lost",t.number)&&await Ps(r,"lost",Bs(t,null)).catch(()=>{}),await n.notifyIfResolved(t.repo,t.number,"lost").catch(()=>{}),await n.mark(t.repo,t.number,"lost").catch(()=>{})}async function a0(t,e,n,r,o={}){let s=js(r,t,o.change||"merged",o.githubAt??(Date.parse(e.mergedAt||"")||null)),a=n.get(t.trackId);a&&((await L(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(t.number))||await r.emitMerged(a,{number:Number(t.number),url:e.url||t.url,title:e.title||t.title,branch:e.headRefName||t.headRef,repo:t.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(t.repo,t.number).catch(()=>{}),await r.archiveIfArmed(t.repo,t.number).catch(()=>{}),await r.mark(t.repo,t.number,"merged").catch(()=>{})}async function FC(t={}){let{now:e=Date.now(),auth:n=Wu,view:r=EC,list:o=_C,links:s=Kh,trackMap:a=IC,emitMerged:i=RC,mark:c=wn,archiveIfArmed:l=vn,notifyIfArmed:u=Tn,notifyIfResolved:f=gd,batchThreshold:d=kC,detail:p=$C,merge:m=OC,updateBranch:w=MC,lookup:y=Bt,disarm:b=Yo,emitDisarmed:S=CC,localHasCommit:_=DC}=t;if(!(await n().catch(()=>null))?.authenticated||ci()>0)return 0;let R=(await s().catch(()=>[])).filter(T=>e-(Gi.get(V(T.repo,T.number))||0)>=AC(T,e));if(!R.length)return 0;let C=Date.now(),v=new Map,D=new Map;for(let T of R){let g=s0(T,e);v.set(V(T.repo,T.number),{lane:g,intervalMs:o0[g]}),Gi.set(V(T.repo,T.number),e),D.has(T.repo)||D.set(T.repo,[]),D.get(T.repo).push(T)}let B=await a().catch(()=>new Map),lt={now:e,lanes:v,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,detail:p,merge:m,updateBranch:w,lookup:y,disarm:b,emitDisarmed:S,localHasCommit:_};for(let[T,g]of D){let h,A=new Set;if(g.length>d)h=await o(T).catch(()=>null);else{h=new Map;for(let E of g)try{let J=await r(T,E.number);J?.number!=null&&h.set(Number(J.number),J)}catch(J){vC(J)&&A.add(V(E.repo,E.number))}}if(h)for(let E of g){let J=V(E.repo,E.number),W=h.get(Number(E.number));if(!W){if(A.has(J)){let it=(Vi.get(J)||0)+1;Vi.set(J,it),it>=yC&&(await LC(E,B,lt),Vi.delete(J),Gi.delete(J))}continue}Vi.delete(J),W.state==="MERGED"||W.state==="CLOSED"?(W.state==="MERGED"?await a0(E,W,B,lt):await jC(E,W,B,lt),Gi.delete(J)):(await BC(E,W,B,lt).catch(()=>{}),E.autoMerge&&await PC(E,B,lt).catch(()=>{}))}}let q={};for(let{lane:T}of v.values())q[T]=(q[T]||0)+1;return console.log(`[pr-watch] probed ${R.length} link(s) (${Object.entries(q).map(([T,g])=>`${T} ${g}`).join(", ")}) in ${Date.now()-C}ms`),R.length}function i0(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{await FC()}catch{}finally{t=!1}}},n0)}import Qe from"node:fs/promises";import ho from"node:path";import m0 from"node:os";import c0 from"node:fs/promises";import zi from"node:path";import{execFile as UC}from"node:child_process";import{promisify as HC}from"node:util";import{fileURLToPath as WC}from"node:url";var qC=HC(UC),u0=zi.dirname(WC(import.meta.url)),KC=zi.resolve(u0,"..","..","client","dist"),GC="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",VC=new Map([...GC].map((t,e)=>[t,e]));function zC(t){let e=[],n=0,r=0;for(let o of t){let s=VC.get(o);if(s===void 0)return e;n+=(s&31)<<r,s&32?r+=5:(e.push(n&1?-(n>>>1):n>>>1),n=0,r=0)}return e}function JC(t){let e=[],n=0,r=0,o=0;for(let s of t.split(";")){let a=[],i=0;if(s)for(let c of s.split(",")){let l=zC(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])))}e.push(a)}return e}function XC(t,e){if(!t||t.length===0||t[0][0]>e)return null;let n=0,r=t.length-1;for(;n<r;){let o=n+r+1>>1;t[o][0]<=e?n=o:r=o-1}return t[n]}var l0=new Map;async function YC(t){let e;try{e=await c0.stat(t)}catch{return null}let n=l0.get(t);if(n&&n.mtimeMs===e.mtimeMs)return n;try{let r=JSON.parse(await c0.readFile(t,"utf8")),o={mtimeMs:e.mtimeMs,sources:r.sources||[],lines:JC(r.mappings||"")};return l0.set(t,o),o}catch{return null}}var Gd=null;function ZC(){return Gd||(Gd=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):qC("git",["rev-parse","--short","HEAD"],{cwd:zi.resolve(u0,"..","..")}).then(t=>t.stdout.trim()).catch(()=>null)),Gd}var QC=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function tN(t,{distDir:e=KC}={}){let n=[],r=0,o=!1;for(let s of t.matchAll(QC)){let[a,i,c,l]=s,u=await YC(zi.join(e,"assets",`${i}.map`)),f=u?XC(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(t.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(t.slice(r)),{stack:n.join(""),symbolicated:o}}async function d0(t,e={}){try{if(!t?.error?.stack||!t.release||t.release==="unknown")return t;let n=e.release??await ZC();if(!n||t.release!==n)return t;let{stack:r,symbolicated:o}=await tN(t.error.stack,e);o&&(t.error.stack=r,t.error.symbolicated=!0)}catch{}return t}var eN=()=>process.env.BOSTRAT_STATE_DIR||ho.join(process.env.HOME||m0.tmpdir(),".bostrat"),Cn=()=>ho.join(eN(),"telemetry"),h0=()=>ho.join(Cn(),".relay-offsets.json"),nN=50,rN=14,oN=3e4,f0=500,Zt=(t,e)=>typeof t=="string"?t.slice(0,e):"";function sN(t,e){if(!t||typeof t!="object")return null;let n=Zt(t.name,64);if(!n)return null;let r=Number.isFinite(t.ts)?t.ts:e,o={name:n,ts:Math.abs(r-e)<168*3600*1e3?r:e,receivedAt:e,anonId:Zt(t.anonId,64),sessionId:Zt(t.sessionId,64),userId:Zt(t.userId,64),release:Zt(t.release,64),appVersion:Zt(t.appVersion,32),route:Zt(t.route,128),deviceClass:t.deviceClass==="phone"||t.deviceClass==="desktop"?t.deviceClass:""};return t.props&&typeof t.props=="object"&&!Array.isArray(t.props)&&(JSON.stringify(t.props).length<=8192?o.props=t.props:o.props={clamped:!0}),t.error&&typeof t.error=="object"&&(o.error={kind:Zt(t.error.kind,256),message:Zt(t.error.message,8192),stack:Zt(t.error.stack,32768),fingerprint:Zt(t.error.fingerprint,64)}),Array.isArray(t.breadcrumbs)&&(o.breadcrumbs=t.breadcrumbs.slice(-50).map(s=>({t:Number.isFinite(s?.t)?s.t:0,n:Zt(s?.n,64),r:Zt(s?.r,128)}))),o}var w0=t=>new Date(t).toISOString().slice(0,10),p0=0;async function g0(t,e=Date.now()){let r=(Array.isArray(t?.events)?t.events.slice(0,nN):[]).map(s=>sN(s,e)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await d0(s);await Qe.mkdir(Cn(),{recursive:!0});let o=ho.join(Cn(),`${w0(e)}.ndjson`);return await Qe.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
410
|
+
`).join(""),"utf8"),e-p0>6*3600*1e3&&(p0=e,aN(e).catch(()=>{})),r.length}async function aN(t=Date.now()){let e=w0(t-rN*24*3600*1e3),n=[];try{n=await Qe.readdir(Cn())}catch{return}let r=n.filter(s=>s.endsWith(".ndjson")&&s.slice(0,10)<e);if(!r.length)return;for(let s of r)await Qe.rm(ho.join(Cn(),s),{force:!0});let o=await b0();for(let s of r)delete o[s];await k0(o)}var Ji=new Map;function y0(t,e=Date.now()){let n=t||"unknown",r=Ji.get(n);return r||(Ji.size>512&&Ji.clear(),r={tokens:40,at:e},Ji.set(n,r)),r.tokens=Math.min(40,r.tokens+(e-r.at)/6e4*20),r.at=e,r.tokens<1?!1:(r.tokens-=1,!0)}async function b0(){try{return JSON.parse(await Qe.readFile(h0(),"utf8"))||{}}catch{return{}}}async function k0(t){await Qe.mkdir(Cn(),{recursive:!0}),await Qe.writeFile(h0(),JSON.stringify(t),"utf8")}function iN(t,{env:e,host:n}={}){let r=t.name==="app.error"&&t.error,o={};for(let[a,i]of[["event",t.name],["route",t.route],["session",t.sessionId],["anon",t.anonId],["user",t.userId],["release",t.release],["appVersion",t.appVersion],["device",t.deviceClass]])i&&(o[a]=i);let s={timestamp:new Date(t.ts).toISOString(),service:"bostrat-client",source:"treetop-js",env:e||process.env.BOSTRAT_ENV||"pi",host:n||m0.hostname(),level:r?"error":"info",message:r?`${t.error.kind||"Error"}: ${t.error.message||"(no message)"}`.slice(0,8192):t.name,tags:o,attributes:{...t.props?{props:t.props}:{},receivedAt:t.receivedAt}};return r&&(s.error={kind:t.error.kind,message:t.error.message,stack:t.error.stack},t.error.fingerprint&&(s.fingerprint=t.error.fingerprint),t.error.symbolicated&&(s.attributes.symbolicated=!0),t.breadcrumbs&&(s.attributes.breadcrumbs=t.breadcrumbs)),s}async function cN({fetchImpl:t=fetch}={}){let e=await uh();if(!e)return{sent:0,disabled:!0};let n;try{n=(await Qe.readdir(Cn())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await b0(),o=0;for(let s of n){let a=ho.join(Cn(),s),i=r[s]||0,c;try{let d=await Qe.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(`
|
|
411
|
+
`).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=f0){let p=f.slice(d,d+f0).map(w=>iN(w)),m=await t(`${e.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e.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 k0(r)}return{sent:o,disabled:!1}}var Vd=null,zd=!1;function x0(){Vd||(Vd=setInterval(async()=>{if(!zd){zd=!0;try{await cN()}catch(t){console.error("[telemetry] relay failed:",t?.message||t)}finally{zd=!1}}},oN),Vd.unref?.())}import Yi from"node:fs/promises";import E0 from"node:path";import lN from"node:os";var _0=()=>process.env.BOSTRAT_STATE_DIR||E0.join(process.env.HOME||lN.tmpdir(),".bostrat"),I0=()=>E0.join(_0(),"dictation-glossary.json"),uN=100,dN=500,fN=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Zi=null,Jd=null,Xd=Promise.resolve();function S0(){let t={},e=Date.now();for(let n of fN)t[n.toLowerCase()]={term:n,count:1,lastUsed:e,source:"seed"};return t}async function R0(){return Zi||(Jd??=Yi.readFile(I0(),"utf8").then(t=>JSON.parse(t)?.terms??S0()).catch(()=>S0()).then(t=>Zi=t).finally(()=>Jd=null),Jd)}async function v0(){let t=JSON.stringify({terms:Zi},null,2),e=I0(),n=`${e}.${process.pid}.tmp`;await Yi.mkdir(_0(),{recursive:!0}),await Yi.writeFile(n,t),await Yi.rename(n,e)}function pN(){return Xd=Xd.then(v0,v0),Xd.catch(()=>{})}async function Yd(){return Object.values(await R0()).sort((e,n)=>n.count-e.count||n.lastUsed-e.lastUsed)}var mN=t=>Math.max(1,Math.ceil(t.length/4));async function hN(){let t=[],e=0;for(let{term:n}of await Yd()){let r=mN(n);if(!(e+r>dN)&&(t.push(n),e+=r,t.length>=uN))break}return t}async function wN(t,e){let n=await R0(),r=t.toLowerCase(),o=n[r];n[r]={term:t,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??e}}var T0=t=>(t||"").split(/\s+/).map(e=>e.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Xi=t=>t.toLowerCase();function A0(t){return!new RegExp("\\p{L}","u").test(t)||t.length<3?!1:/^[a-z]+$/.test(t)?t.length>=4:!0}async function $0(t,e){let n=T0(t),r=T0(e);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]=Xi(n[u])===Xi(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;)Xi(n[c])===Xi(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(A0(r[l])&&i.add(r[l]),l++);for(;l<s;l++)A0(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await wN(u,"learned");return await pN(),[...i]}var Ls=null;function gN(t,e){let n=null;try{n=JSON.parse(e)}catch{}let r=n?.err_code||n?.error_code||n?.category||"";return t===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."}:t===401||t===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 ${t}: ${e.slice(0,300)}`}}async function O0(){if(!Ls)return null;let t=await Wo();if(!t||t.apiKey.slice(-6)!==Ls.keyTail)return null;let{keyTail:e,...n}=Ls;return n}async function M0(t,e){let n=await Wo();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 hN())r.searchParams.append("keyterm",i);let o=await fetch(r,{method:"POST",headers:{Authorization:`Token ${n.apiKey}`,"Content-Type":e||"application/octet-stream"},body:t});if(!o.ok){let i=await o.text().catch(()=>""),c=gN(o.status,i);c.persistent&&(Ls={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 Ls=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}var Zd={"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 yN(t){if(!t)return null;let e=String(t).trim();if(Zd[e])return e;let n=null;for(let r of Object.keys(Zd))e.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function bN(t,e={}){let n=Zd[yN(t)];if(!n)return 0;let r=0;for(let o of Object.keys(n))r+=(Number(e[o])||0)*n[o];return r/1e6}function kN(t,e){let n=null;try{n=JSON.parse(e)}catch{}let r=n?.error?.code||n?.error?.type||"";return t===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."}:t===401||t===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 (${t}): ${e.slice(0,300)}`}}var Fs=null;async function xN(){if(!Fs)return null;let t=await ja();if(!t||t.apiKey.slice(-6)!==Fs.keyTail)return null;let{keyTail:e,...n}=Fs;return n}async function SN(t){let e=await fetch("https://api.openai.com/v1/realtime/client_secrets",{method:"POST",headers:{Authorization:`Bearer ${t.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({session:{type:"realtime",model:t.model,audio:{output:{voice:t.voice}}}})});if(!e.ok){let o=await e.text().catch(()=>""),s=kN(e.status,o);s.persistent&&(Fs={code:s.code,message:s.message,at:Date.now(),keyTail:t.apiKey.slice(-6)});let a=new Error(s.message);throw a.status=s.status,a.code=s.code,a}Fs=null;let n=await e.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 vN={openai:SN};async function C0(){let t=await ja();return t?{configured:!0,provider:t.provider,model:t.model,voice:t.voice,problem:await xN()}:{configured:!1}}async function N0(){let t=await ja();if(!t){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 e=await vN[t.provider](t);return{provider:t.provider,model:t.model,voice:t.voice,...e}}async function P0(t,{model:e,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=bN(e,s),i=await I(t,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:e||"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 U0=ft.dirname(AN(import.meta.url)),D0=Number(process.env.PORT)||3362,Qi=ak(process.env),H0=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,wo=Lk(process.env.BOSTRAT_ALLOW_IPS),rf=Fk(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Ki),W0=Hk(),q0=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",x=Ie();x.disable("x-powered-by");x.use((t,e,n)=>{if(Ns(t)||wo.allowed(wo.clientIp(t))||rf.allows(t))return n();e.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});x.use(zk);x.use(Kk({token:W0,enforce:q0}));x.use("/api/mocks",Ie.json({limit:"1mb"}));x.use("/api/mocks",Ie.raw({type:"image/png",limit:"24mb"}));x.use("/api/runbooks",Ie.json({limit:"1mb"}));x.use("/api/telemetry",Ie.json({limit:"256kb"}));x.use("/api/hook",Ie.json({limit:"2mb"}));x.use("/api/dictation",Ie.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));x.use("/api/tracks/:id/upload",Ie.raw({type:()=>!0,limit:"25mb"}));x.use(Ie.json({limit:"64kb"}));x.get("/api/health",async(t,e)=>{e.json({ok:!0,tmuxSocket:zt,hostTmuxAlive:await sn(),autoTriggerPct:H0})});x.get("/api/sessions",async(t,e)=>{e.json({sessions:await Oe()})});x.post("/api/sessions",async(t,e)=>{let{displayName:n,cwd:r}=t.body||{};if(typeof n!="string")return e.status(400).json({error:"displayName (string) required"});try{let o=await Bn(n,r||void 0);e.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;e.status(a).json({error:s})}});x.delete("/api/sessions/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!bt(n))return e.status(400).json({error:"invalid session name"});if(!await rt(n))return e.status(404).json({error:"session not found"});try{await pe(n),e.json({ok:!0})}catch(r){e.status(500).json({error:`failed to kill: ${r?.message||r}`})}});x.get("/api/context/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!bt(n))return e.status(400).json({error:"invalid session name"});let r=await rt(n);if(!r)return e.status(404).json({error:"session not found"});let o=await Oo(r.cwd,r.sessionId);if(!o)return e.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});e.json({available:!0,...o})});x.post("/api/handoff/:name",async(t,e)=>{let n=decodeURIComponent(t.params.name);if(!bt(n))return e.status(400).json({error:"invalid session name"});try{let r=await rm(n);e.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){e.status(409).json({error:r?.message||String(r)})}});x.get("/api/handoff/:name",(t,e)=>{let n=decodeURIComponent(t.params.name),r=Sa(n);if(!r)return e.json({running:!1});e.json({running:!r.finishedAt,step:r.step,error:r.error,file:r.file,startedAt:r.startedAt,finishedAt:r.finishedAt})});var B0=10;x.get("/api/tracks",async(t,e)=>{let n=await at(),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(t.query.archived!=="all"&&r.length>B0){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,B0).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}e.json({tracks:o,archivedTotal:r.length,agents:Ai,agentAvailability:await dd(),tickets:await Fa()})});x.get("/api/cost",async(t,e)=>{try{let n=await at();t.query.track&&(n=n.filter(o=>o.id===t.query.track));let r=await hk(n);if(r.cursorUsage=Lu(),t.query.format==="text")return e.type("text/plain").send(wk(r));e.json(r)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/roundup",async(t,e)=>{try{e.json(await bk(await at(),{window:t.query.window,from:t.query.from,to:t.query.to}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/spend",async(t,e)=>{try{let n=await Tk(await at(),{window:t.query.window,from:t.query.from,to:t.query.to});n.cursorUsage=Lu(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=is(),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/usage/caps",(t,e)=>{e.json(is())});x.post("/api/usage/caps",async(t,e)=>{try{e.json(await ei(t.body?.caps??{},{source:"local"}))}catch(n){e.status(400).json({error:n?.message||String(n)})}});x.get("/api/usage/limits",async(t,e)=>{try{let n=t.query.fresh==="1"||t.query.fresh==="true";e.json(await Nk(await at(),{fresh:n}))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/arcs",async(t,e)=>{try{e.json(await Yh(await at()))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/arcs/:key",async(t,e)=>{try{let n=await Zh(await at(),t.params.key);if(!n)return e.status(404).json({error:"unknown arc"});e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/arcs/backfill-beats",async(t,e)=>{try{e.json({ok:!0,...await Bb()})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/hook",async(t,e)=>{try{let{mode:n,target:r}=await uf(t.get(qs),{byToken:fb,legacy:()=>db(t.body?.session_id,t.body?.cwd)});if(n==="refused")return Ks("refused",{reason:"unknown-token",hook:t.body?.hook_event_name}),e.json({ok:!1,error:"unknown hook token"});n==="token"&&t.body?.session_id&&r.box?.sessionId&&t.body.session_id!==r.box.sessionId&&Ks("mismatch",{track:r.track.id,box:r.box.name}),Ks(n,{track:r?.track?.id,hook:t.body?.hook_event_name});let o=await sk(t.body||{},()=>r);e.json({ok:!0,events:o.length})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});x.post("/api/telemetry",async(t,e)=>{try{if(!y0(wo.clientIp(t)))return e.json({ok:!0,dropped:!0});let n=await g0(t.body||{});e.json({ok:!0,events:n})}catch(n){e.json({ok:!1,error:n?.message||String(n)})}});x.get("/api/repos",async(t,e)=>{try{let n=await Re(),r=new Set((await Promise.all(n.map(a=>ot(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await xo()).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}));e.json({repos:[...n,...s]})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/github/token",async(t,e)=>{try{e.json(await Js(String(t.query.repo||"")))}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});x.get("/api/github/status",(t,e)=>{e.json({configured:zs(),calls:qg(),latency:yy()})});x.post("/api/github/refresh",(t,e)=>{kf(),e.json({ok:!0})});x.get("/api/gmail/search",async(t,e)=>{try{let n=await Dk(String(t.query.q||""),{max:Number(t.query.max)||10});e.json({messages:n})}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});x.get("/api/gmail/messages/:id",async(t,e)=>{try{e.json({message:await Bk(String(t.params.id))})}catch(n){e.status(n?.status||502).json({error:n?.message||String(n)})}});function of(t,e,n){let r=t.workspace;return r?.kind==="multi"?(r.repos??[]).some(o=>e&&o?.base===e||n&&o?.repo===n):!r?.base||!e||r.base===e||n&&r.repo===n}async function tc({repo:t=null,number:e=null,branch:n=null}){let r=t?await ot(t):null;if(r&&e!=null){let s=await Bt(r,e),a=s&&await et(s.trackId);if(a)return a}return n?(await at()).find(s=>s.workspace?.branch===n&&of(s,t,r))??null:null}async function K0({repo:t=null,number:e=null,branch:n=null}){let r=await tc({repo:t,number:e,branch:n}).catch(()=>null);if(r&&!r.archivedAt)return r;if(!n)return null;let o=t?await ot(t).catch(()=>null):null;return(await at()).find(a=>!a.archivedAt&&a.workspace?.branch===n&&of(a,t,o))??null}async function ue(t,{number:e,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await tc({repo:s,number:e,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await ot(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:t,number:e,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function RN(t){let e=(await at()).filter(n=>!n.archivedAt);for(let n of t??[]){if(!n.prs?.length)continue;let r=await ot(n.path);if(r)for(let o of n.prs){if(await Bt(r,o.number))continue;let s=e.find(a=>a.workspace?.branch===o.headRefName&&of(a,n.path,r));s&&await Xt({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}x.get("/api/git/prs",async(t,e)=>{try{let n=await Ay();await RN(n.repos).catch(()=>{}),e.json(n)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/git/prs/merge",async(t,e)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:f}=t.body||{};try{if(s){let w=o||"merge";if(!ms.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 ot((await Ve(n)).path);if(!b)throw new Error("repo has no GitHub remote");let S=await tc({repo:n,number:y,branch:l}).catch(()=>null);if(!S)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await hs(n,y),await Xt({repo:b,number:y,trackId:S.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Jo(b,y,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await Jn(b,y,i===!0),await Xo(b,y,c===!0&&i!==!0),Ee(),await ue("auto-armed",{number:y,url:f,title:u,branch:l,repo:n,track:S,...a?{extra:{updated:!0}}:{}}),e.json({ok:!0,number:y,auto:!0})}let d=await Yr(n,r,{method:o||"squash"});await ue("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let p=await ot(n).catch(()=>null),m=!1;if(p&&(i===!0&&await Jn(p,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await Xo(p,d.number,!0).catch(()=>{}),await Tn(p,d.number).catch(()=>{}),m=await vn(p,d.number).catch(()=>!1),await wn(p,d.number,"merged").catch(()=>{})),i===!0&&!m){let w=await tc({repo:n,number:d.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await so(w.id).catch(()=>{})}e.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;e.status(m).json({error:p})}});x.post("/api/git/prs/disarm-auto",async(t,e)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=t.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await ot((await Ve(n)).path).catch(()=>null),l=c?await Yo(c,i).catch(()=>null):null;try{await Gu(n,i)}catch(u){if(!l)throw u}Ee(),await ue("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),e.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;e.status(l).json({error:c})}});x.post("/api/git/prs/merge-all",async(t,e)=>{let{repo:n,method:r}=t.body||{};try{let o=await ot(n).catch(()=>null),s=await Ry(n,{method:r||"merge",onMerged:a=>{ue("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&Tn(o,a.number).catch(()=>{}).then(()=>vn(o,a.number)).catch(()=>{}).then(()=>wn(o,a.number,"merged")).catch(()=>{})}});e.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;e.status(a).json({error:s})}});x.get("/api/git/prs/merge-all",(t,e)=>{e.json({jobs:Iy()})});x.post("/api/git/prs/merge-track",async(t,e)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=t.body||{};try{let i=a||"merge";if(!ms.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return e.status(400).json({error:"trackId required"});let c=await et(n);if(!c)return e.status(404).json({error:`Track ${n} isn't on this node`});let l=(await ve()).filter(d=>d.trackId===c.id&&d.state!=="merged"&&d.state!=="closed");if(!l.length)return e.status(409).json({error:"no open PRs on this Track"});for(let d of l)await Jn(d.repo,d.number,o===!0).catch(()=>{}),await Xo(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 sr(d.repo);if(!m)throw new Error(`no local checkout for ${d.repo}`);let w=await Xr(m,d.number);f.push({r:p,l:d,repoPath:m,pr:w,plan:Ey(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 _y(f))try{if(y.action==="skip")d.action="skipped",d.reason=y.reason;else if(y.action==="merge")await Yr(m,p.number,{method:i}),await ue("merged",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:m,track:c}),await Tn(p.repo,p.number).catch(()=>{}),await vn(p.repo,p.number).catch(()=>{}),await wn(p.repo,p.number,"merged").catch(()=>{}),d.action="merged";else{if(y.action==="update-arm"&&(await hs(m,p.number).catch(()=>{}),d.action="updated-armed"),!await Jo(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await ue("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(Ee(),!u.some(d=>d.ok)){if(o===!0)for(let d of l)await Jn(d.repo,d.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let d of l)await Xo(d.repo,d.number,!1).catch(()=>{});return e.status(502).json({error:u.map(d=>`${d.repo}#${d.number}: ${d.reason}`).join("; "),results:u})}e.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;e.status(l).json({error:c})}});x.post("/api/git/prs/disarm-track",async(t,e)=>{let{trackId:n}=t.body||{};try{if(!n)return e.status(400).json({error:"trackId required"});let r=await et(n);if(!r)return e.status(404).json({error:`Track ${n} isn't on this node`});let o=(await ve()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await Yo(a.repo,a.number).catch(()=>null),c=await sr(a.repo).catch(()=>null);c&&await Gu(c,a.number).catch(()=>{}),i&&(await ue("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}))}Ee(),e.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;e.status(s).json({error:o})}});x.post("/api/git/prs/open",async(t,e)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=t.body||{};try{if(!n)return e.status(400).json({error:"trackId required"});let i=await et(n);if(!i)return e.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=qt(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 us(i),!f.length)return e.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let w of f)try{let y=await zu(w.dir,{title:r,body:o,base:s}),b=w.repo||await ot(w.dir).catch(()=>null);b&&y.number&&await Xt({repo:b,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await ue("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?e.json({ok:!0,multi:!0,results:p,url:m[0].url,number:m[0].number}):e.status(502).json({error:p.map(w=>`${w.repo}: ${w.error}`).join("; "),results:p})}let c=await zu(i.cwd,{title:r,body:o,base:s}),l=await ot(i.cwd).catch(()=>null);l&&c.number&&await Xt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await ue("opened",{number:c.number,url:c.url,title:r,track:i,branch:i.workspace?.branch,extra:{...l?{repo:l}:{}}}),e.json(c)}catch(i){let c=i?.message||String(i),l=/required|invalid/i.test(c)?400:502;e.status(l).json({error:c})}});x.get("/api/tracks/:id/repos",async(t,e)=>{try{let n=await et(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await us(n),o=new Map(r.map(a=>[a.base,a])),s=qt(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}});e.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tracks/:id/repos",async(t,e)=>{let{repo:n}=t.body||{};try{if(!n||typeof n!="string")return e.status(400).json({error:"repo (path or owner/name) required"});let r=await Ib(t.params.id,n);e.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;e.status(s).json({error:o})}});x.delete("/api/tracks/:id/repos/:name",async(t,e)=>{try{let n=await Rb(t.params.id,t.params.name);e.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;e.status(o).json({error:r})}});x.post("/api/git/prs/fix",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await Xr(n,r),s=await ot(n).catch(()=>null),a=await K0({repo:n,number:r,branch:o.headRefName});if(!a)return e.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=qt(a.workspace).find(b=>b.base===n||s&&b.repo===s)??null,c=a.workspace?.kind==="multi",l=await $y(n,o.number);if(!l.length)return e.status(409).json({error:"no failing checks on this PR"});let u=c&&i?ft.join("ci",i.name):"ci",f=ft.join(a.cwd,".bostrat",u),d=ft.join(".bostrat",u,`pr-${o.number}-fail.log`);await Gt.promises.mkdir(f,{recursive:!0}),await Gt.promises.writeFile(ft.join(a.cwd,d),l.map(b=>`\u2550\u2550\u2550\u2550 ${b.name}${b.link?` \xB7 ${b.link}`:""} \u2550\u2550\u2550\u2550
|
|
412
412
|
${b.log??"(no fetchable log \u2014 see link)"}
|
|
413
413
|
`).join(`
|
|
414
|
-
`));let p=l.map(b=>b.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",h=await oe(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 qt({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await uh(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await se("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)}}),e.json({ok:!0,leadBox:h.leadBox,queued:!!h.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;e.status(a).json({error:s})}});x.post("/api/git/prs/reconcile",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await Fr(n,r);if(o.mergeable!=="CONFLICTING")return e.status(409).json({error:"this PR does not conflict with its base branch"});let s=await et(n).catch(()=>null),a=await tx({repo:n,number:r,branch:o.headRefName});if(!a)return e.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=jt(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 oe(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 qt({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await dh(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await se("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),e.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;e.status(a).json({error:s})}});x.post("/api/tracks/:id/pr-offer",async(t,e)=>{let{offerId:n,action:r}=t.body||{};try{let o=null;if(Array.isArray(t.body?.actions)){if(o=Ww.filter(f=>t.body.actions.includes(f)),!o.length)return e.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 e.status(400).json({error:'offerId and actions ["commit"|"rebase"|"push"|"pr"] (or action "dismiss") required'});if(!n)return e.status(400).json({error:"offerId required"});let s=o!=null&&t.body?.notify===!0,a=await tt(t.params.id);if(!a)return e.status(404).json({error:"track not found"});let i=await L(a),c=i.find(f=>f.type==="pr.offered"&&f.payload?.offerId===n);if(!c)return e.status(404).json({error:"unknown offer"});if(i.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===n))return e.status(409).json({error:"offer already answered"});let l=null;o&&(l=await oe(a.id,Ha(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&&Ry(a.id,{actions:o}),e.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;e.status(a).json({error:s})}});var _s=t=>e=>({...e,url:sk(e,t.headers)});x.get("/api/views",async(t,e)=>{try{let n=await ga({trackId:t.query.track||void 0});e.json({views:n.map(_s(t)),maxLive:rn})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/views/:id",async(t,e)=>{let n=await pm(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:_s(t)(n),session:xr(n.slug)})});x.post("/api/views",async(t,e)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=t.body||{};try{let i=n?await tt(n):null;if(!i&&s&&(i=(await at()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ut.sep)))||null),!i)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await Fc({trackId:i.id,name:r,cmd:o,cwd:i.cwd,serveCwd:s,port:a,box:i.leadBox,branch:i.workspace?.branch??null});e.json({ok:!0,view:_s(t)(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);e.status(l).json({error:c})}});x.post("/api/views/:id/stop",async(t,e)=>{try{e.json({ok:!0,view:_s(t)(await mm(t.params.id))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.post("/api/views/:id/restart",async(t,e)=>{try{e.json({ok:!0,view:_s(t)(await hm(t.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;e.status(o).json({error:r})}});x.delete("/api/views/:id",async(t,e)=>{try{e.json(await wm(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var dM=[["linear",jn,Vc],["jira",va,Kc]],Id=new Map;x.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await qc(),r={};for(let[o,s,a]of dM){let i=await s();if(!i){Id.delete(o),r[o]={configured:!1};continue}let c=Id.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},Id.set(o,c)}r[o]={...c.body,flow:n},o==="jira"&&i.tokenExpiresAt&&(r[o].tokenExpiresAt=i.tokenExpiresAt)}e.json(r)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/tickets/flow",async(t,e)=>{try{e.json({flow:await qc()})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tickets/flow",async(t,e)=>{let{trackId:n,cwd:r,set:o,actor:s}=t.body||{};try{let a=await Nm(o),i=n?await tt(n):null;!i&&r&&(i=(await at()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ut.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(()=>{}),e.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);e.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});x.post("/api/tickets/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=n?await tt(n):null;if(!s&&r&&(s=(await at()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+ut.sep)))||null),!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return e.status(400).json({error:"ref (issue URL or key) required"});let a=await Ar(s,o,{source:n?"api":"cli"});e.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);e.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});x.get("/api/tickets/status",async(t,e)=>{let{cwd:n,track:r}=t.query;try{let o=r?await tt(r):null;if(!o&&n&&(o=(await at()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+ut.sep)))||null),!o)return e.status(400).json({error:"track (or a cwd inside a Track workspace) required"});e.json({trackId:o.id,configured:await Jm(),ticket:await ge(o.id)})}catch(o){e.status(500).json({error:o?.message||String(o)})}});x.get("/api/tracks/:id/ticket",async(t,e)=>{e.json({ticket:await ge(t.params.id)})});x.post("/api/tracks/:id/ticket",async(t,e)=>{try{let n=await tt(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await Ar(n,(t.body||{}).ref,{source:"api"});e.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);e.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});x.delete("/api/tracks/:id/ticket",async(t,e)=>{e.json({ok:!0,removed:await sl(t.params.id)})});var Bi=t=>/no ticket linked|unknown offer|unknown state/i.test(t)?404:/already answered/i.test(t)?409:/required|no ticketing/i.test(t)?400:502;x.get("/api/tracks/:id/ticket/states",async(t,e)=>{try{e.json(await Qm(t.params.id))}catch(n){let r=n?.message||String(n);e.status(Bi(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await al(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(Bi(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await th(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(Bi(r)).json({error:r})}});x.post("/api/tracks/:id/ticket-offer",async(t,e)=>{let{offerId:n,accept:r}=t.body||{};try{let o=await tt(t.params.id);if(!o)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await eh(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(Bi(s)).json({error:s})}});x.post("/api/tickets/propose",async(t,e)=>{let{trackId:n,cwd:r,plan:o}=t.body||{};try{let s=n?await tt(n):null;if(!s&&r&&(s=(await at()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ut.sep)))||null),!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,trackId:s.id,proposal:await rh(s,o)})}catch(s){let a=s?.message||String(s);e.status(/no ticketing|are off|needs an epic|too many|required/i.test(a)?400:502).json({error:a})}});x.get("/api/tickets/teams",async(t,e)=>{try{e.json({teams:await nh()})}catch(n){let r=n?.message||String(n);e.status(/no ticketing/i.test(r)?400:502).json({error:r})}});x.post("/api/tracks/:id/ticket-create-offer",async(t,e)=>{let{proposalId:n,accept:r,teamId:o}=t.body||{};try{let s=await tt(t.params.id);if(!s)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await oh(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);e.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})}});x.get("/api/mocks",async(t,e)=>{try{e.json({mocks:await Pn({trackId:t.query.track||void 0,group:t.query.group||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/mocks/:id",async(t,e)=>{let n=await nn(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});e.json({mock:n})});x.get("/api/mocks/:id/png",async(t,e)=>{let n=await nn(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=Ie(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});e.set("cache-control","private, max-age=31536000, immutable"),e.sendFile(Wt(n,r.v).png,o=>{o&&!e.headersSent&&e.status(404).json({error:"rendered artifact missing"})})});x.get("/api/mocks/:id/html",async(t,e)=>{let n=await nn(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=Ie(n,t.query.v??null);if(!r?.htmlRel)return e.status(404).json({error:"this version has no HTML source (image-sourced)"});e.set("content-security-policy","sandbox"),e.sendFile(Wt(n,r.v).html,o=>{o&&!e.headersSent&&e.status(404).json({error:"source artifact missing"})})});x.post("/api/mocks",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null,r=n?t.query:t.body||{},{trackId:o,name:s,cwd:a,width:i,group:c}=r;try{let l=o?await tt(o):null;if(!l&&a&&(l=(await at()).find(p=>p.cwd&&(a===p.cwd||a.startsWith(p.cwd+ut.sep)))||null),!l)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(d=>d.name===l.leadBox),f=await Vp({trackId:l.id,name:s,html:n?null:r.html,image:n,cwd:l.cwd,width:i,group:c,box:l.leadBox,agent:u?.agent||null});e.json({ok:!0,mock:f})}catch(l){let u=l?.message||String(l);e.status(/required|invalid|must be/i.test(u)?400:500).json({error:u})}});x.post("/api/mocks/:id/update",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null;try{let r=await zp(t.params.id,{html:n?null:(t.body||{}).html,image:n,width:n?t.query.width:(t.body||{}).width});e.json({ok:!0,mock:r})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required|invalid|must be/i.test(o)?400:500;e.status(s).json({error:o})}});x.post("/api/mocks/:id/pick",async(t,e)=>{try{let{mock:n,siblings:r}=await Xp(t.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await oe(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),e.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;e.status(o).json({error:r})}});x.delete("/api/mocks/:id",async(t,e)=>{try{let n=(await ey(t.params.id)).map(r=>({id:r.id,name:r.name}));e.json({...await Yp(t.params.id),...n.length?{danglingFlows:n}:{}})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.get("/api/flows",async(t,e)=>{try{e.json({flows:await Xg({trackId:t.query.track||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/flows/:id",async(t,e)=>{let n=await Yg(t.params.id);if(!n)return e.status(404).json({error:"flow not found"});e.json({flow:n})});x.post("/api/flows",async(t,e)=>{let{trackId:n,name:r,steps:o,cwd:s}=t.body||{};try{let a=n?await tt(n):null;if(!a&&s&&(a=(await at()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ut.sep)))||null),!a)return e.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 Zg({trackId:a.id,name:r,steps:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});e.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;e.status(c).json({error:i})}});x.post("/api/flows/:id/update",async(t,e)=>{try{let{steps:n,note:r}=t.body||{};e.json({ok:!0,flow:await Qg(t.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;e.status(o).json({error:r})}});x.delete("/api/flows/:id",async(t,e)=>{try{e.json(await ty(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.get("/api/runbooks",async(t,e)=>{try{let n=t.query.track||void 0;if(!n&&t.query.cwd){let r=await at(),o=String(t.query.cwd);if(n=r.find(s=>s.cwd&&(o===s.cwd||o.startsWith(s.cwd+ut.sep)))?.id,!n)return e.json({runbooks:[]})}e.json({runbooks:await ka({trackId:n,status:t.query.status||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/runbooks/:id",async(t,e)=>{let n=await Hc(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});e.json({runbook:n})});x.get("/api/runbooks/:id/md",async(t,e)=>{let n=await Hc(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=xm(n,t.query.v??null);if(!r)return e.status(404).json({error:"no such version"});e.set("content-security-policy","sandbox"),e.set("cache-control","private, max-age=31536000, immutable"),e.type("text/markdown; charset=utf-8"),e.sendFile(Me(n,r.v),o=>{o&&!e.headersSent&&e.status(404).json({error:"runbook artifact missing"})})});x.post("/api/runbooks",async(t,e)=>{let{trackId:n,name:r,md:o,cwd:s}=t.body||{};try{let a=n?await tt(n):null;if(!a&&s&&(a=(await at()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ut.sep)))||null),!a)return e.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 Tm({trackId:a.id,trackName:a.name,name:r,md:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});e.json({ok:!0,runbook:c})}catch(a){let i=a?.message||String(a);e.status(/required|invalid|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});x.post("/api/runbooks/:id/update",async(t,e)=>{try{let{md:n,note:r}=t.body||{};e.json({ok:!0,runbook:await Am(t.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;e.status(o).json({error:r})}});x.post("/api/runbooks/:id/status",async(t,e)=>{try{e.json({ok:!0,runbook:await Em(t.params.id,(t.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;e.status(o).json({error:r})}});x.delete("/api/runbooks/:id",async(t,e)=>{try{e.json(await _m(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.get("/api/changelog/draft",async(t,e)=>{try{let n=ck(t.query.since||null);e.json({since:n,repos:yd(),prs:await lk(n)})}catch(n){let r=n?.message||String(n);e.status(/unparseable/i.test(r)?400:500).json({error:r})}});x.post("/api/changelog/publish",async(t,e)=>{let{md:n,day:r,trackId:o,cwd:s}=t.body||{};try{let a=o?await tt(o):null;if(!a&&s&&(a=(await at()).find(c=>c.cwd&&(s===c.cwd||String(s).startsWith(c.cwd+ut.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,entry:await uk(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);e.status(/required|invalid|no `- `|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});var fM=120,Rd=400,Pi=null;x.post("/api/notify",async(t,e)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a}=t.body||{};try{let i=typeof r=="string"?r.trim():"";if(!i)return e.status(400).json({error:"body (the message) required"});if(i.length>Rd)return e.status(400).json({error:`message is ${i.length} chars \u2014 over the ${Rd} cap; a notification is a nudge, put the detail in chat`});let c=Oo(`${n||""}
|
|
415
|
-
${i}`);if(c.length)return e.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
|
|
414
|
+
`));let p=l.map(b=>b.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",w=await le(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 Xt({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Lh(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await ue("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)}}),e.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;e.status(a).json({error:s})}});x.post("/api/git/prs/reconcile",async(t,e)=>{let{repo:n,number:r}=t.body||{};try{let o=await Xr(n,r);if(o.mergeable!=="CONFLICTING")return e.status(409).json({error:"this PR does not conflict with its base branch"});let s=await ot(n).catch(()=>null),a=await K0({repo:n,number:r,branch:o.headRefName});if(!a)return e.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=qt(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 le(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 Xt({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Fh(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await ue("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),e.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;e.status(a).json({error:s})}});x.post("/api/tracks/:id/pr-offer",async(t,e)=>{let{offerId:n,action:r}=t.body||{};try{let o=null;if(Array.isArray(t.body?.actions)){if(o=Og.filter(f=>t.body.actions.includes(f)),!o.length)return e.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 e.status(400).json({error:'offerId and actions ["commit"|"rebase"|"push"|"pr"] (or action "dismiss") required'});if(!n)return e.status(400).json({error:"offerId required"});let s=o!=null&&t.body?.notify===!0,a=await et(t.params.id);if(!a)return e.status(404).json({error:"track not found"});let i=await L(a),c=i.find(f=>f.type==="pr.offered"&&f.payload?.offerId===n);if(!c)return e.status(404).json({error:"unknown offer"});if(i.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===n))return e.status(409).json({error:"offer already answered"});let l=null;o&&(l=await le(a.id,si(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&&bb(a.id,{actions:o}),e.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;e.status(a).json({error:s})}});var Us=t=>e=>({...e,url:Xk(e,t.headers)});x.get("/api/views",async(t,e)=>{try{let n=await Oa({trackId:t.query.track||void 0});e.json({views:n.map(Us(t)),maxLive:fn})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/views/:id",async(t,e)=>{let n=await Hm(t.params.id);if(!n)return e.status(404).json({error:"view not found"});e.json({view:Us(t)(n),session:Rr(n.slug)})});x.post("/api/views",async(t,e)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=t.body||{};try{let i=n?await et(n):null;if(!i&&s&&(i=(await at()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ft.sep)))||null),!i)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await il({trackId:i.id,name:r,cmd:o,cwd:i.cwd,serveCwd:s,port:a,box:i.leadBox,branch:i.workspace?.branch??null});e.json({ok:!0,view:Us(t)(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);e.status(l).json({error:c})}});x.post("/api/views/:id/stop",async(t,e)=>{try{e.json({ok:!0,view:Us(t)(await Wm(t.params.id))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.post("/api/views/:id/restart",async(t,e)=>{try{e.json({ok:!0,view:Us(t)(await qm(t.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;e.status(o).json({error:r})}});x.delete("/api/views/:id",async(t,e)=>{try{e.json(await Km(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});var $N=[["linear",Kn,ml],["jira",Ba,fl]],Qd=new Map;x.get("/api/integrations/ticketing",async(t,e)=>{try{let n=await dl(),r={};for(let[o,s,a]of $N){let i=await s();if(!i){Qd.delete(o),r[o]={configured:!1};continue}let c=Qd.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},Qd.set(o,c)}r[o]={...c.body,flow:n},o==="jira"&&i.tokenExpiresAt&&(r[o].tokenExpiresAt=i.tokenExpiresAt)}e.json(r)}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/tickets/flow",async(t,e)=>{try{e.json({flow:await dl()})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tickets/flow",async(t,e)=>{let{trackId:n,cwd:r,set:o,actor:s}=t.body||{};try{let a=await lh(o),i=n?await et(n):null;!i&&r&&(i=(await at()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ft.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(()=>{}),e.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);e.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});x.post("/api/tickets/link",async(t,e)=>{let{trackId:n,cwd:r,ref:o}=t.body||{};try{let s=n?await et(n):null;if(!s&&r&&(s=(await at()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+ft.sep)))||null),!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return e.status(400).json({error:"ref (issue URL or key) required"});let a=await Cr(s,o,{source:n?"api":"cli"});e.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);e.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});x.get("/api/tickets/status",async(t,e)=>{let{cwd:n,track:r}=t.query;try{let o=r?await et(r):null;if(!o&&n&&(o=(await at()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+ft.sep)))||null),!o)return e.status(400).json({error:"track (or a cwd inside a Track workspace) required"});e.json({trackId:o.id,configured:await Th(),ticket:await Se(o.id)})}catch(o){e.status(500).json({error:o?.message||String(o)})}});x.get("/api/tracks/:id/ticket",async(t,e)=>{e.json({ticket:await Se(t.params.id)})});x.post("/api/tracks/:id/ticket",async(t,e)=>{try{let n=await et(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=await Cr(n,(t.body||{}).ref,{source:"api"});e.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);e.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});x.delete("/api/tracks/:id/ticket",async(t,e)=>{e.json({ok:!0,removed:await El(t.params.id)})});var nc=t=>/no ticket linked|unknown offer|unknown state/i.test(t)?404:/already answered/i.test(t)?409:/required|no ticketing/i.test(t)?400:502;x.get("/api/tracks/:id/ticket/states",async(t,e)=>{try{e.json(await Ih(t.params.id))}catch(n){let r=n?.message||String(n);e.status(nc(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/state",async(t,e)=>{try{e.json({ok:!0,...await _l(t.params.id,(t.body||{}).stateId)})}catch(n){let r=n?.message||String(n);e.status(nc(r)).json({error:r})}});x.post("/api/tracks/:id/ticket/comment",async(t,e)=>{try{e.json({ok:!0,...await Rh(t.params.id,(t.body||{}).body)})}catch(n){let r=n?.message||String(n);e.status(nc(r)).json({error:r})}});x.post("/api/tracks/:id/ticket-offer",async(t,e)=>{let{offerId:n,accept:r}=t.body||{};try{let o=await et(t.params.id);if(!o)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await $h(o,n,r===!0)})}catch(o){let s=o?.message||String(o);e.status(nc(s)).json({error:s})}});x.post("/api/tickets/propose",async(t,e)=>{let{trackId:n,cwd:r,plan:o}=t.body||{};try{let s=n?await et(n):null;if(!s&&r&&(s=(await at()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ft.sep)))||null),!s)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,trackId:s.id,proposal:await Mh(s,o)})}catch(s){let a=s?.message||String(s);e.status(/no ticketing|are off|needs an epic|too many|required/i.test(a)?400:502).json({error:a})}});x.get("/api/tickets/teams",async(t,e)=>{try{e.json({teams:await Oh()})}catch(n){let r=n?.message||String(n);e.status(/no ticketing/i.test(r)?400:502).json({error:r})}});x.post("/api/tracks/:id/ticket-create-offer",async(t,e)=>{let{proposalId:n,accept:r,teamId:o}=t.body||{};try{let s=await et(t.params.id);if(!s)return e.status(404).json({error:"track not found"});e.json({ok:!0,...await Ch(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);e.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})}});x.get("/api/mocks",async(t,e)=>{try{e.json({mocks:await Hn({trackId:t.query.track||void 0,group:t.query.group||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/mocks/:id",async(t,e)=>{let n=await dn(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});e.json({mock:n})});x.get("/api/mocks/:id/png",async(t,e)=>{let n=await dn(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=Ne(n,t.query.v??null);if(!r)return e.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});e.set("cache-control","private, max-age=31536000, immutable"),e.sendFile(Jt(n,r.v).png,o=>{o&&!e.headersSent&&e.status(404).json({error:"rendered artifact missing"})})});x.get("/api/mocks/:id/html",async(t,e)=>{let n=await dn(t.params.id);if(!n)return e.status(404).json({error:"mock not found"});let r=Ne(n,t.query.v??null);if(!r?.htmlRel)return e.status(404).json({error:"this version has no HTML source (image-sourced)"});e.set("content-security-policy","sandbox"),e.sendFile(Jt(n,r.v).html,o=>{o&&!e.headersSent&&e.status(404).json({error:"source artifact missing"})})});x.post("/api/mocks",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null,r=n?t.query:t.body||{},{trackId:o,name:s,cwd:a,width:i,group:c}=r;try{let l=o?await et(o):null;if(!l&&a&&(l=(await at()).find(p=>p.cwd&&(a===p.cwd||a.startsWith(p.cwd+ft.sep)))||null),!l)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(d=>d.name===l.leadBox),f=await Sm({trackId:l.id,name:s,html:n?null:r.html,image:n,cwd:l.cwd,width:i,group:c,box:l.leadBox,agent:u?.agent||null});e.json({ok:!0,mock:f})}catch(l){let u=l?.message||String(l);e.status(/required|invalid|must be/i.test(u)?400:500).json({error:u})}});x.post("/api/mocks/:id/update",async(t,e)=>{let n=Buffer.isBuffer(t.body)?t.body:null;try{let r=await vm(t.params.id,{html:n?null:(t.body||{}).html,image:n,width:n?t.query.width:(t.body||{}).width});e.json({ok:!0,mock:r})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required|invalid|must be/i.test(o)?400:500;e.status(s).json({error:o})}});x.post("/api/mocks/:id/pick",async(t,e)=>{try{let{mock:n,siblings:r}=await Am(t.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await le(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),e.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;e.status(o).json({error:r})}});x.delete("/api/mocks/:id",async(t,e)=>{try{let n=(await Ky(t.params.id)).map(r=>({id:r.id,name:r.name}));e.json({...await Em(t.params.id),...n.length?{danglingFlows:n}:{}})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.get("/api/flows",async(t,e)=>{try{e.json({flows:await Fy({trackId:t.query.track||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/flows/:id",async(t,e)=>{let n=await Uy(t.params.id);if(!n)return e.status(404).json({error:"flow not found"});e.json({flow:n})});x.post("/api/flows",async(t,e)=>{let{trackId:n,name:r,steps:o,cwd:s}=t.body||{};try{let a=n?await et(n):null;if(!a&&s&&(a=(await at()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ft.sep)))||null),!a)return e.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 Hy({trackId:a.id,name:r,steps:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});e.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;e.status(c).json({error:i})}});x.post("/api/flows/:id/update",async(t,e)=>{try{let{steps:n,note:r}=t.body||{};e.json({ok:!0,flow:await Wy(t.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;e.status(o).json({error:r})}});x.delete("/api/flows/:id",async(t,e)=>{try{e.json(await qy(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.get("/api/runbooks",async(t,e)=>{try{let n=t.query.track||void 0;if(!n&&t.query.cwd){let r=await at(),o=String(t.query.cwd);if(n=r.find(s=>s.cwd&&(o===s.cwd||o.startsWith(s.cwd+ft.sep)))?.id,!n)return e.json({runbooks:[]})}e.json({runbooks:await Na({trackId:n,status:t.query.status||void 0})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.get("/api/runbooks/:id",async(t,e)=>{let n=await ll(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});e.json({runbook:n})});x.get("/api/runbooks/:id/md",async(t,e)=>{let n=await ll(t.params.id);if(!n)return e.status(404).json({error:"runbook not found"});let r=Xm(n,t.query.v??null);if(!r)return e.status(404).json({error:"no such version"});e.set("content-security-policy","sandbox"),e.set("cache-control","private, max-age=31536000, immutable"),e.type("text/markdown; charset=utf-8"),e.sendFile(Le(n,r.v),o=>{o&&!e.headersSent&&e.status(404).json({error:"runbook artifact missing"})})});x.post("/api/runbooks",async(t,e)=>{let{trackId:n,name:r,md:o,cwd:s}=t.body||{};try{let a=n?await et(n):null;if(!a&&s&&(a=(await at()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ft.sep)))||null),!a)return e.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 Qm({trackId:a.id,trackName:a.name,name:r,md:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});e.json({ok:!0,runbook:c})}catch(a){let i=a?.message||String(a);e.status(/required|invalid|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});x.post("/api/runbooks/:id/update",async(t,e)=>{try{let{md:n,note:r}=t.body||{};e.json({ok:!0,runbook:await th(t.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;e.status(o).json({error:r})}});x.post("/api/runbooks/:id/status",async(t,e)=>{try{e.json({ok:!0,runbook:await eh(t.params.id,(t.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;e.status(o).json({error:r})}});x.delete("/api/runbooks/:id",async(t,e)=>{try{e.json(await nh(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.get("/api/changelog/draft",async(t,e)=>{try{let n=Qk(t.query.since||null);e.json({since:n,repos:Wd(),prs:await t0(n)})}catch(n){let r=n?.message||String(n);e.status(/unparseable/i.test(r)?400:500).json({error:r})}});x.post("/api/changelog/publish",async(t,e)=>{let{md:n,day:r,trackId:o,cwd:s}=t.body||{};try{let a=o?await et(o):null;if(!a&&s&&(a=(await at()).find(c=>c.cwd&&(s===c.cwd||String(s).startsWith(c.cwd+ft.sep)))||null),!a)return e.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});e.json({ok:!0,entry:await e0(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);e.status(/required|invalid|no `- `|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});var ON=120,tf=400,ec=null;x.post("/api/notify",async(t,e)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a}=t.body||{};try{let i=typeof r=="string"?r.trim():"";if(!i)return e.status(400).json({error:"body (the message) required"});if(i.length>tf)return e.status(400).json({error:`message is ${i.length} chars \u2014 over the ${tf} cap; a notification is a nudge, put the detail in chat`});let c=Uo(`${n||""}
|
|
415
|
+
${i}`);if(c.length)return e.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 et(a):null;if(!l&&s&&(l=(await at()).find(m=>m.cwd&&(s===m.cwd||s.startsWith(m.cwd+ft.sep)))||null),!l)return e.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,ON):l.name,body:f(i,tf),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await I(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:d}),e.json({ok:!0,notification:d,delivered:ec?ec.status().connected:null})}catch(i){e.status(500).json({error:i?.message||String(i)})}});x.post("/api/dictation",async(t,e)=>{if(!Buffer.isBuffer(t.body)||t.body.length===0)return e.status(400).json({error:"expected a raw audio body"});try{e.json(await M0(t.body,t.headers["content-type"]))}catch(n){e.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});x.get("/api/dictation/glossary",async(t,e)=>{e.json({configured:!!await Wo(),terms:await Yd(),problem:await O0()})});x.post("/api/dictation/learn",async(t,e)=>{let{transcript:n,finalText:r}=t.body||{};if(typeof n!="string"||typeof r!="string")return e.status(400).json({error:"transcript and finalText are required"});e.json({learned:await $0(n,r)})});x.get("/api/tracks/:id/voice-chat",async(t,e)=>{if(!await et(t.params.id))return e.status(404).json({error:"track not found"});e.json(await C0())});x.post("/api/tracks/:id/voice-chat/session",async(t,e)=>{try{let n=await et(t.params.id);if(!n)return e.status(404).json({error:"track not found"});if(n.archivedAt)return e.status(409).json({error:"track is archived"});e.json(await N0())}catch(n){e.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});x.post("/api/tracks/:id/voice-chat/usage",async(t,e)=>{try{let n=await et(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let{model:r,usage:o,seconds:s}=t.body||{};e.json({ok:!0,...await P0(n,{model:r,usage:o,seconds:s})})}catch(n){e.status(n?.status||500).json({error:n?.message||String(n)})}});x.post("/api/tracks",async(t,e)=>{let{name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i,ticket:c}=t.body||{};try{let l=await wd({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),u=null;if(c)try{await Cr(l,c,{source:"create"})}catch(f){u=f?.message||String(f)}e.json({ok:!0,track:l,...u?{ticketWarning:u}:{}})}catch(l){let u=l?.message||String(l);e.status(/required|unknown agent|invalid|must be/i.test(u)?400:500).json({error:u})}});x.get("/api/tracks/:id",async(t,e)=>{let n=await et(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json({track:n,handoff:G0(Es(t.params.id)),subtasks:ao(t.params.id).map(Hs)})});x.delete("/api/tracks/:id",async(t,e)=>{try{let n=await Pb(t.params.id,{cascadeMocks:t.query.mocks!=="0",cascadeRunbooks:t.query.runbooks==="1"});await El(t.params.id).catch(()=>{}),e.json(n)}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:500).json({error:r})}});x.post("/api/tracks/:id/archive",async(t,e)=>{try{e.json({ok:!0,track:await so(t.params.id)})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/already archived/i.test(r)?409:500).json({error:r})}});x.post("/api/tracks/:id/unarchive",async(t,e)=>{try{e.json({ok:!0,track:await Lb(t.params.id)})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/not archived/i.test(r)?409:500).json({error:r})}});x.get("/api/tracks/:id/messages",async(t,e)=>{let n=await fd(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.json(n)});x.get("/api/tracks/:id/events",async(t,e)=>{let n=await et(t.params.id);if(!n)return e.status(404).json({error:"track not found"});e.set({"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive","x-accel-buffering":"no"}),e.flushHeaders();let r=i=>e.write(`id: ${i.seq}
|
|
416
416
|
data: ${JSON.stringify(i)}
|
|
417
417
|
|
|
418
|
-
`),o=Number(t.query.since??t.headers["last-event-id"])||0;for(let i of await L(n))i.seq>o&&r(i);let s=
|
|
418
|
+
`),o=Number(t.query.since??t.headers["last-event-id"])||0;for(let i of await L(n))i.seq>o&&r(i);let s=Lp(n.id,r),a=setInterval(()=>e.write(`: hb
|
|
419
419
|
|
|
420
|
-
`),25e3);t.on("close",()=>{clearInterval(a),s()})});x.get("/api/tracks/:id/search",async(t,e)=>{let n=await
|
|
421
|
-
`);x.post("/api/tracks/:id/memory/consolidate",async(t,e)=>{try{e.json({ok:!0,...await
|
|
420
|
+
`),25e3);t.on("close",()=>{clearInterval(a),s()})});x.get("/api/tracks/:id/search",async(t,e)=>{let n=await et(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=String(t.query.q||"").trim();if(!r)return e.status(400).json({error:"q (query) is required"});let o=Math.min(Math.max(Number(t.query.k)||8,1),50),s=Po(await L(n)),a=wa(s,r,{k:o,role:t.query.role||null,tag:t.query.tag||null});e.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}))})});x.get("/api/memory",async(t,e)=>{e.json({stores:await Nf()})});x.delete("/api/memory/:key/:file",async(t,e)=>{let n=String(t.params.key||"");if(!/^[a-z0-9][a-z0-9._-]*$/.test(n))return e.status(400).json({error:"bad store key"});try{e.json({ok:!0,...await Pf(n,String(t.params.file||""))})}catch(r){let o=r?.message||String(r);e.status(/not found/i.test(o)?404:/bad fact/i.test(o)?400:500).json({error:o})}});var MN=["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(`
|
|
421
|
+
`);x.post("/api/tracks/:id/memory/consolidate",async(t,e)=>{try{e.json({ok:!0,...await le(t.params.id,MN)})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/archived/i.test(r)?409:/not running/i.test(r)?400:500).json({error:r})}});x.post("/api/tracks/:id/lead/restart",async(t,e)=>{try{e.json({ok:!0,track:await Ub(t.params.id)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/mid-turn|terminal|archived/i.test(r)?409:500;e.status(o).json({error:r})}});x.post("/api/tracks/:id/say",async(t,e)=>{let{text:n,attachments:r}=t.body||{};try{e.json(await le(t.params.id,n,{attachments:r}))}catch(o){let s=o?.message||String(o);if(o?.usageCap)return e.status(429).json({error:"usage_cap",message:s,cap:o.usageCap});e.status(/not found/i.test(s)?404:/archived/i.test(s)?409:/required|not running/i.test(s)?400:500).json({error:s})}});x.post("/api/tracks/:id/upload",async(t,e)=>{try{let n=await et(t.params.id);if(!n)return e.status(404).json({error:`track "${t.params.id}" not found`});if(n.archivedAt)return e.status(409).json({error:"track is archived"});if(!n.cwd)return e.status(409).json({error:"track has no workspace yet"});let r=Buffer.isBuffer(t.body)?t.body:null,o=await Yy(n.cwd,{filename:t.query.filename,buffer:r});e.json({ok:!0,attachment:o})}catch(n){let r=n?.message||String(n);e.status(/too large|unsupported|empty|required/i.test(r)?400:500).json({error:r})}});x.get("/api/tracks/:id/uploads/:file",async(t,e)=>{try{let n=await et(t.params.id);if(!n)return e.status(404).json({error:`track "${t.params.id}" not found`});if(!n.cwd)return e.status(404).json({error:"track has no workspace"});let r=ft.basename(String(t.params.file||""));if(!r||!td(ft.extname(r).slice(1)))return e.status(400).json({error:"not an upload path"});e.set("cache-control","private, max-age=31536000, immutable"),e.set("content-security-policy","sandbox"),e.sendFile(ft.join(Qu(n.cwd),r),o=>{o&&!e.headersSent&&e.status(404).json({error:"upload not found"})})}catch(n){e.status(500).json({error:n?.message||String(n)})}});var CN=/^[0-9a-fA-F-]{8,64}$/;x.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(t,e)=>{try{let n=await et(t.params.id);if(!n)return e.status(404).json({error:`track "${t.params.id}" not found`});let{box:r,uuid:o}=t.params,s=Number(t.params.idx);if(!CN.test(o)||!Number.isInteger(s)||s<0)return e.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 e.status(404).json({error:"image source not available"});let i=Me(a.cwd,a.sessionId);if(!i)return e.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=TN.createInterface({input:Gt.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?Nc(l)[s]:null;if(!f||!/^image\//.test(f.mediaType))return e.status(404).json({error:"image not found"});e.set("content-type",f.mediaType),e.set("cache-control","private, max-age=31536000, immutable"),e.set("content-security-policy","sandbox"),e.send(Buffer.from(f.data,"base64"))}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tracks/:id/interrupt",async(t,e)=>{try{e.json(await hd(t.params.id))}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:/archived/i.test(r)?409:500).json({error:r})}});x.delete("/api/tracks/:id/queue",async(t,e)=>{try{let{removed:n,restored:r,draftRev:o}=await hb(t.params.id);e.json({ok:!0,removed:n.filter(s=>s.kind!=="ship-notify"),restored:r,draftRev:o})}catch(n){e.status(500).json({error:n?.message||String(n)})}});x.post("/api/tracks/:id/answer-prompt",async(t,e)=>{let{promptId:n,answers:r,text:o}=t.body||{};try{e.json(await pd(t.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;e.status(i).json({error:a})}});x.post("/api/setup/:provider/start",async(t,e)=>{try{let n=await Cb({provider:t.params.provider,fresh:t.body?.fresh===!0});e.json({ok:!0,track:n})}catch(n){let r=n?.message||String(n);e.status(/unknown setup provider|isn't available/i.test(r)?400:500).json({error:r})}});x.post("/api/tracks/:id/navigate-prompt",async(t,e)=>{let{promptId:n,questionIndex:r}=t.body||{};try{e.json(await mb(t.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;e.status(a).json({error:s})}});x.put("/api/tracks/:id/draft",async(t,e)=>{let{text:n,rev:r}=t.body||{};try{e.json(await vb(t.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;e.status(a).json({error:s})}});x.get("/api/tracks/:id/usage",async(t,e)=>{let n=await et(t.params.id);if(!n)return e.status(404).json({error:"track not found"});let r=n.boxes?.find(s=>s.name===n.leadBox)||null,o=t.query.fresh==="1"||t.query.fresh==="true";e.json(await Hi(r,{fresh:o}))});x.post("/api/tracks/:id/model",async(t,e)=>{let{model:n}=t.body||{};try{e.json(await Tb(t.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;e.status(s).json({error:o})}});x.post("/api/tracks/:id/subtask",async(t,e)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=t.body||{};try{let a=await Gb(t.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});e.json({ok:!0,subtask:Hs(a)})}catch(a){let i=a?.message||String(a);if(a?.usageCap)return e.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;e.status(c).json({error:i})}});x.get("/api/tracks/:id/subtasks",(t,e)=>{e.json({subtasks:ao(t.params.id).map(Hs)})});x.get("/api/tracks/:id/subtask/:sid/messages",async(t,e)=>{let n=await pb(t.params.id,t.params.sid);if(!n)return e.status(404).json({error:"subtask not found"});e.json(n)});x.post("/api/tracks/:id/subtask/:sid/cancel",(t,e)=>{try{e.json({ok:!0,subtask:Hs(Sd(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});x.post("/api/tracks/:id/subtask/:sid/merge-now",(t,e)=>{try{e.json({ok:!0,subtask:Hs(zb(t.params.sid))})}catch(n){let r=n?.message||String(n);e.status(/not found/i.test(r)?404:409).json({error:r})}});function Hs(t){return t?{id:t.id,agent:t.agent,task:t.task,skipPermissions:t.skipPermissions,step:t.step,running:!t.finishedAt,box:t.box,branch:t.branch,stat:t.stat,files:t.files||null,conflicts:t.conflicts||[],applied:t.applied,error:t.error,canceled:t.canceled,startedAt:t.startedAt,finishedAt:t.finishedAt}:null}x.post("/api/tracks/:id/handoff-lead",async(t,e)=>{let{toAgent:n,skipPermissions:r,reason:o}=t.body||{};try{let s=await bd(t.params.id,n,r!==!1,o);e.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 e.status(429).json({error:"usage_cap",message:a,cap:s.usageCap});e.status(/already running/i.test(a)?409:/not found/i.test(a)?404:400).json({error:a})}});x.post("/api/tracks/:id/spin-up-view",async(t,e)=>{try{e.json(await kb(t.params.id,t.body||{}))}catch(n){let r=n?.message||String(n);e.status(/already being spun up/i.test(r)?409:/not found/i.test(r)?404:400).json({error:r})}});x.get("/api/tracks/:id/handoff-lead",(t,e)=>{e.json(G0(Es(t.params.id))??{running:!1})});function G0(t){return t?{running:!t.finishedAt,step:t.step,error:t.error,fromBox:t.fromBox,toBox:t.toBox,toAgent:t.toAgent,skipPermissions:t.skipPermissions,startedAt:t.startedAt,finishedAt:t.finishedAt}:null}x.get("/api/debug/handoff",async(t,e)=>{let n=Math.min(Number(t.query.limit)||80,500);e.json({verbose:na(),paths:Yf,trace:await Xf({limit:n})})});var ef=ft.resolve(U0,"..","..","client","dist");Gt.existsSync(ef)&&(x.use(Ie.static(ef)),x.get(/^(?!\/api\/).*/,(t,e)=>e.sendFile(ft.join(ef,"index.html"))));var V0=EN.createServer(x),NN=new IN({noServer:!0}),z0=(t,e,n)=>{if(!(Ns(t)||wo.allowed(wo.clientIp(t))||rf.allows(t))){e.write(`HTTP/1.1 403 Forbidden\r
|
|
422
422
|
\r
|
|
423
|
-
`),e.destroy();return}if(
|
|
423
|
+
`),e.destroy();return}if(Jk(t,e,n))return;if(!Gk(t,W0,{enforce:q0})){e.write(`HTTP/1.1 401 Unauthorized\r
|
|
424
424
|
\r
|
|
425
|
-
`),e.destroy();return}let r=
|
|
425
|
+
`),e.destroy();return}let r=up(t.url);if(!r.ok){e.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
426
426
|
\r
|
|
427
|
-
`),e.destroy();return}let o=r.sessionName;if(!
|
|
427
|
+
`),e.destroy();return}let o=r.sessionName;if(!bt(o)){e.write(`HTTP/1.1 400 Bad Request\r
|
|
428
428
|
\r
|
|
429
|
-
`),e.destroy();return}let{cols:s,rows:a}=r;
|
|
429
|
+
`),e.destroy();return}let{cols:s,rows:a}=r;NN.handleUpgrade(t,e,n,i=>ip(i,o,{cols:s,rows:a}))};V0.on("upgrade",z0);var J0=process.env.BOSTRAT_CERT_DIR||ft.join(process.env.HOME||nf.tmpdir(),".bostrat","certs"),j0=Number(process.env.BOSTRAT_TLS_PORT)||3443,L0=ft.join(J0,"bostrat.globopi.key"),F0=ft.join(J0,"bostrat.globopi.crt");if(Gt.existsSync(L0)&&Gt.existsSync(F0)){let t=_N.createServer({key:Gt.readFileSync(L0),cert:Gt.readFileSync(F0)},x);t.on("upgrade",z0),t.on("error",e=>{console.error(`[bostrat-agent] https listener failed (${e?.code||e?.message}) \u2014 continuing HTTP-only`)}),t.listen(j0,Qi,()=>{console.log(`[bostrat-agent] https listening on ${Qi||"0.0.0.0"}:${j0}`)})}V0.listen(D0,Qi,()=>{console.log(`[bostrat-agent] listening on ${Qi||"0.0.0.0"}:${D0}`),console.log(`[bostrat-agent] tmux socket: ${zt}`),console.log(`[bostrat-agent] auto-trigger default: ${H0}%`),console.log(`[bostrat-agent] ip allowlist: ${wo.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(rf.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),qb().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),Kb().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Fb().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)),Mb().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)),jN(),BN(),lg({listTracks:at,getTrack:et}).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)),Rk()&&$k({listTracks:at}),i0(),Nh({resolveTrack:et}),ok({listTracks:at,createTrack:wd,archiveTrack:so,listRepos:Re,resolveTrack:et,sayToLead:le,answerPrompt:pd,startLeadHandoff:bd,interruptLead:hd,listSubtaskJobs:ao,cancelSubtask:Sd,readEvents:L,appendEvent:I,knownAgents:Ai}),PN(),Ro().then(t=>{t||console.warn(`[boot] no host tmux server on ${zt} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),x0(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(ec=uk({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||nf.hostname(),name:process.env.BOSTRAT_NODE_NAME||nf.hostname(),listTracks:at,detectAgents:dd,sampleMetrics:fk}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),hr().catch(()=>{}),sh().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),Ph().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),Rm().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),zy().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),Gh().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{ec?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function PN(){DN(),$a().catch(()=>{}),setInterval(()=>$a().catch(()=>{}),1e4)}function DN(){for(let t of["bostrat-view","bostrat-mock","bostrat-flow","bostrat-ticket","bostrat-runbook","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let e=ft.resolve(U0,"..","bin",t),n=ft.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!Gt.existsSync(e))continue;Gt.mkdirSync(n,{recursive:!0});let r=ft.join(n,t);(Gt.existsSync(r)?Gt.readlinkSync(r):null)!==e&&(Gt.rmSync(r,{force:!0}),Gt.symlinkSync(e,r)),console.log(`[bostrat-agent] ${t} CLI linked at ${r}`)}catch(e){console.error(`[bostrat-agent] ${t} CLI link failed:`,e?.message||e)}}function BN(){let t=!1,e=async()=>{if(!t){t=!0;try{let n=await at(),r=await Ek({tracks:n});r.committed&&console.log(`[state-sync] committed (${r.staged} file(s) staged${r.pushed?", pushed":""})`)}catch{}finally{t=!1}}};setInterval(e,6e4),e()}function jN(){let t=!1;setInterval(async()=>{if(!t){t=!0;try{let e=await at();for(let n of e)n.archivedAt||!n.boxes?.some(r=>r.state==="live")||(await fd(n.id).catch(()=>{}),await Oy(n).catch(()=>{}))}catch{}finally{t=!1}}},4e3)}
|