bostrat-node 1.7.9 → 1.7.11
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/bin/bostrat-decision +4 -1
- package/dist/cli.js +38 -38
- package/dist/index.js +186 -178
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,93 +1,96 @@
|
|
|
1
|
-
var
|
|
2
|
-
`).includes(a));if(!s.length)return;await dt.mkdir(
|
|
1
|
+
var _E=Object.defineProperty;var C=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var ju=(e,t)=>{for(var n in t)_E(e,n,{get:t[n],enumerable:!0})};import{createHash as IE,randomBytes as RE,timingSafeEqual as $E}from"node:crypto";function pw(){return RE(32).toString("hex")}function Gs(e){let t=typeof e=="string"?e.trim():"";return t?IE("sha256").update(t).digest("hex"):null}function mw(e,t){return typeof e!="string"||typeof t!="string"||e.length!==t.length||!e?!1:$E(Buffer.from(e),Buffer.from(t))}function Vs(e,t={},{now:n=Date.now(),log:r=console.warn}={}){if(Lu.set(e,(Lu.get(e)||0)+1),Fu.has(e)&&n-Fu.get(e)<OE)return!1;Fu.set(e,n);let o=Object.entries(t).filter(([,s])=>s!=null&&s!=="").map(([s,a])=>`${s}=${a}`).join(" ");return r(`[hook-audit] ${e} n=${Lu.get(e)}${o?` ${o}`:""}`),!0}async function hw(e,{byToken:t,legacy:n,env:r=process.env}={}){let o=typeof e=="string"?e.trim():"";if(!o)return r.BOSTRAT_HOOK_LEGACY_DENY==="1"?{mode:"legacy-denied",target:null}:{mode:"legacy",target:await n()};let s=await t(o);return s?.track?{mode:"token",target:s}:{mode:"refused",target:null}}var Gi,Vi,Lu,Fu,OE,zs=C(()=>{Gi="BOSTRAT_HOOK_TOKEN",Vi="x-bostrat-hook";Lu=new Map,Fu=new Map,OE=6e4});import dt from"node:fs/promises";import ww from"node:os";import st from"node:path";import{execFile as CE}from"node:child_process";import{promisify as ME}from"node:util";function zi(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function NE(){return process.env.CLAUDE_CONFIG_DIR?st.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):st.join(ww.homedir(),".claude.json")}function PE(){return process.env.CLAUDE_CONFIG_DIR?st.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):st.join(ww.homedir(),".claude","settings.json")}async function Fn(e){if(!e||typeof e!="string")return;let t=st.resolve(e),n=NE(),r=PE(),o={};try{o=JSON.parse(await dt.readFile(n,"utf8"))}catch{}o.hasCompletedOnboarding=!0,o.theme||(o.theme="dark"),o.projects=o.projects||{},o.projects[t]=o.projects[t]||{};let s=o.projects[t];Array.isArray(s.allowedTools)||(s.allowedTools=[]),s.hasTrustDialogAccepted=!0,s.hasCompletedProjectOnboarding=!0;let a=process.env.ANTHROPIC_API_KEY;if(a){let c=a.slice(-20);o.customApiKeyResponses=o.customApiKeyResponses||{},o.customApiKeyResponses.approved=o.customApiKeyResponses.approved||[],o.customApiKeyResponses.rejected=o.customApiKeyResponses.rejected||[],o.customApiKeyResponses.approved.includes(c)||o.customApiKeyResponses.approved.push(c)}await dt.mkdir(st.dirname(n),{recursive:!0}),await dt.writeFile(n,JSON.stringify(o,null,2));let i={};try{i=JSON.parse(await dt.readFile(r,"utf8"))}catch{}if(i.skipDangerousModePermissionPrompt=!0,i.tui===void 0&&(i.tui="default"),process.env.BOSTRAT_HOOKS!=="off"){let l=`curl -sS -m 3 -X POST ${process.env.BOSTRAT_HOOK_URL||`http://127.0.0.1:${process.env.PORT||3362}/api/hook`} -H 'content-type: application/json' -H "${Vi}: $${Gi}" --data-binary @- >/dev/null 2>&1 || true`,u=[{hooks:[{type:"command",command:l}]}];i.hooks=i.hooks||{};for(let d of["SessionStart","UserPromptSubmit","Stop"])i.hooks[d]=u;i.hooks.PostToolUse=[{matcher:"*",hooks:[{type:"command",command:l}]}]}await dt.mkdir(st.dirname(r),{recursive:!0}),await dt.writeFile(r,JSON.stringify(i,null,2)),await DE(t)}async function DE(e){try{let t=st.resolve(e),n=st.join(t,".bostrat","memory","claude"),r=st.join(t,".claude","settings.local.json"),o={};try{o=JSON.parse(await dt.readFile(r,"utf8"))}catch{}let s=o.autoMemoryDirectory,a=typeof s=="string"&&s.includes(st.join(".bostrat","memory"));if(s&&!a||s===n)return;o.autoMemoryDirectory=n,await dt.mkdir(n,{recursive:!0}),await dt.mkdir(st.dirname(r),{recursive:!0}),await dt.writeFile(r,JSON.stringify(o,null,2)),await BE(t,["/.claude/settings.local.json"])}catch{}}async function BE(e,t){try{let{stdout:n}=await ME(CE)("git",["-C",e,"rev-parse","--git-path","info/exclude"]),r=st.resolve(e,n.trim()),o=await dt.readFile(r,"utf8").catch(()=>""),s=t.filter(a=>!o.split(`
|
|
2
|
+
`).includes(a));if(!s.length)return;await dt.mkdir(st.dirname(r),{recursive:!0}),await dt.appendFile(r,(o&&!o.endsWith(`
|
|
3
3
|
`)?`
|
|
4
4
|
`:"")+s.join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`)}catch{}}var
|
|
6
|
+
`)}catch{}}var Js=C(()=>{zs()});import Uu from"node:fs/promises";import jE from"node:os";import Ji from"node:path";function LE(){let e=process.env.CODEX_HOME||Ji.join(jE.homedir(),".codex");return Ji.join(e,"config.toml")}function gw(e){return`[projects."${e}"]`}function FE(e,t){let n=gw(t),r=e.indexOf(n);if(r===-1)return!1;let o=e.slice(r+n.length),s=o.search(/^\s*\[/m),a=s===-1?o:o.slice(0,s);return/trust_level\s*=\s*"trusted"/.test(a)}async function ko(e){if(!e||typeof e!="string")return;let t=Ji.resolve(e),n=LE(),r="";try{r=await Uu.readFile(n,"utf8")}catch{}if(FE(r,t))return;let o=gw(t);if(r.includes(o)){let a=r.indexOf(o)+o.length,c=r.slice(a).search(/^\s*\[/m),l=c===-1?r.length:a+c,u=r.slice(a,l),d=/trust_level\s*=/.test(u)?u.replace(/trust_level\s*=\s*"[^"]*"/,'trust_level = "trusted"'):`${u.replace(/\s*$/,"")}
|
|
7
7
|
trust_level = "trusted"
|
|
8
|
-
`;r=r.slice(0,a)+
|
|
8
|
+
`;r=r.slice(0,a)+d+r.slice(l)}else{let s=`
|
|
9
9
|
${o}
|
|
10
10
|
trust_level = "trusted"
|
|
11
11
|
`;r=r.trimEnd()+s+(r.length&&!r.endsWith(`
|
|
12
12
|
`)?`
|
|
13
|
-
`:"")}try{await
|
|
14
|
-
`,"utf8")}var
|
|
15
|
-
`),
|
|
16
|
-
`))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function
|
|
13
|
+
`:"")}try{await Uu.mkdir(Ji.dirname(n),{recursive:!0}),await Uu.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var Yi=C(()=>{});import yw from"node:fs/promises";import UE from"node:os";import Ys from"node:path";function HE(e){let n=Ys.resolve(e).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||Ys.join(UE.homedir(),".cursor");return Ys.join(r,"projects",n)}async function xo(e){if(!e||typeof e!="string")return;let t=Ys.resolve(e),n=HE(t),r=Ys.join(n,".workspace-trusted");await yw.mkdir(n,{recursive:!0}),await yw.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:t},null,2)}
|
|
14
|
+
`,"utf8")}var Xi=C(()=>{});import Wu from"node:fs/promises";import Un from"node:path";import{execFile as WE}from"node:child_process";import{promisify as qE}from"node:util";async function YE(e){try{return(await Wu.stat(Un.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function XE(e){let t=i=>Wu.readFile(Un.join(e,".git",i),"utf8").then(c=>c.trim()).catch(()=>null),r=(await t("refs/remotes/origin/HEAD"))?.match(/^ref:\s*refs\/remotes\/origin\/(.+)/);if(r)return r[1].trim();let o;for(let i of["main","master"])if(await t(`refs/heads/${i}`)||(o??=await t("packed-refs")||"",new RegExp(`^[0-9a-f]{40} refs/heads/${i}$`,"m").test(o)))return i;let a=(await t("HEAD"))?.match(/^ref:\s*refs\/heads\/(.+)/);return a?a[1].trim():null}async function un(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await YE(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:Un.basename(o),branch:await XE(o)});return}if(s>=zE)return;let i;try{i=await Wu.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||JE.has(c.name)||await t(Un.join(o,c.name),s+1)}for(let o of VE)await t(Un.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=(await me(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 ZE(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function me(e){if(!e)return null;if(Hu.has(e))return Hu.get(e);let t=null;try{let{stdout:n}=await KE("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=ZE(n)}catch{t=null}return Hu.set(e,t),t}async function So(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await un())if((await me(n.path))?.toLowerCase()===t)return n.path;return null}var KE,bw,GE,VE,zE,JE,Hu,Hn=C(()=>{KE=qE(WE),bw=process.env.HOME||"/home/globo",GE=Un.join(process.env.BOSTRAT_STATE_DIR||Un.join(bw,".bostrat"),"repos"),VE=[...(process.env.BOSTRAT_REPOS_ROOT||Un.join(bw,"Documents")).split(","),GE].map(e=>e.trim()).filter(Boolean),zE=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,JE=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);Hu=new Map});import QE from"node:fs";import kw from"node:fs/promises";import Xs from"node:path";import e_ from"node:os";import{fileURLToPath as t_}from"node:url";import{execFile as n_}from"node:child_process";import{promisify as r_}from"node:util";function ke(e=process.env){let t=e.BOSTRAT_PLANE_URL||"";if(!t)return null;try{let n=new URL(t);return n.protocol=n.protocol==="ws:"?"http:":"https:",n.origin}catch{return null}}function Zi(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Qi(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!Sw.test(n))throw vo("repo must be owner/name",400);let r=qu.get(n);if(r&&r.expiresAt-Date.now()>a_)return r;if(!Zi(t))throw vo("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await vw(`${ke(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:Tw(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw vo(`plane unreachable \u2014 GitHub auth unavailable (${i?.message||i})`,503)}let s=await o.json().catch(()=>({}));if(!o.ok){if(o.status>=500&&r&&r.expiresAt>Date.now())return r;throw vo(s?.error||`plane replied ${o.status}`,o.status)}let a={token:s.token,expiresAt:Number(s.expiresAt)||Date.now()+55*6e4,username:s.username??null,installationId:s.installationId??null};return qu.set(n,a),a}async function Zs({env:e=process.env,force:t=!1}={}){if(!Zi(e))return{configured:!1,repos:[],username:null};if(!t&&Cr&&Date.now()-Cr.at<i_)return Cr.data;try{let n=await vw(`${ke(e)}/v1/github/repos`,{headers:Tw(e)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Cr={at:Date.now(),data:r},r}catch(n){return Cr?Cr.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Aw(){qu.clear(),Cr=null}async function qt({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await me(e):null);if(!r)return{};let{token:o}=await Qi(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Ku({env:e=process.env}={}){return(await Zs({env:e})).username??null}function d_(e){let[t,n]=String(e).split("/");return Xs.join(s_,t,n)}async function Gu(e,{env:t=process.env}={}){let n=(...r)=>xw("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${l_()}" ${u_(t)}`),await n("config","credential.useHttpPath","true")}async function ec(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!Sw.test(String(e||"")))throw vo(`bad repo slug "${e}"`,400);let r=d_(e);if(QE.existsSync(Xs.join(r,".git")))return r;let{token:o}=await Qi(e,{env:t});await kw.mkdir(Xs.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await xw("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:c_,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await kw.rm(r,{recursive:!0,force:!0}).catch(()=>{}),vo(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Gu(r,{env:t}),r}var xw,o_,s_,Sw,a_,i_,c_,vw,qu,Cr,Tw,vo,l_,u_,et=C(()=>{Hn();xw=r_(n_),o_=process.env.BOSTRAT_STATE_DIR||Xs.join(process.env.HOME||e_.tmpdir(),".bostrat"),s_=Xs.join(o_,"repos"),Sw=/^[^/\s]+\/[^/\s]+$/,a_=5*6e4,i_=6e4,c_=10*6e4,vw=fetch,qu=new Map,Cr=null;Tw=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),vo=(e,t)=>Object.assign(new Error(e),{status:t});l_=()=>t_(new URL("../bin/bostrat-git-credential",import.meta.url)),u_=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function _w(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Rw(e,t,n,r=Ew){let o=ke(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await p_(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function $w(e,{env:t=process.env,force:n=!1,timeoutMs:r=Ew}={}){if(!_w(t))return{facts:[],source:"unconfigured"};let o=Vu.get(e);if(!n&&o&&Date.now()-o.at<f_)return{facts:o.facts,source:"cache"};try{let s=await Rw(`/v1/memory/${encodeURIComponent(e)}`,{headers:Iw(t)},t,r);if(!s.ok)throw new Error(`plane ${s.status}`);let a=await s.json(),i=Array.isArray(a?.facts)?a.facts:[];return Vu.set(e,{at:Date.now(),facts:i}),{facts:i,source:"plane"}}catch(s){return o?{facts:o.facts,source:"stale",error:s?.message}:{facts:[],source:"unreachable",error:s?.message}}}async function tc(e,t,{env:n=process.env}={}){if(!_w(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await Rw(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...Iw(n),"content-type":"application/json"},body:JSON.stringify({facts:t})},n);if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json(),s=Array.isArray(o?.results)?o.results:[];return Vu.delete(e),{pushed:s.filter(a=>a.outcome==="accepted").length,rejected:s.filter(a=>a.outcome==="rejected"),source:"plane"}}catch(r){return{pushed:0,source:"unreachable",error:r?.message}}}var f_,Ew,Vu,p_,Iw,Ow=C(()=>{et();f_=6e4,Ew=5e3,Vu=new Map,p_=(...e)=>fetch(...e);Iw=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import zu from"node:fs/promises";import To from"node:path";import m_ from"node:os";import{EventEmitter as h_}from"node:events";import{randomUUID as w_}from"node:crypto";function Ao(e){return e?.cwd?To.join(e.cwd,".bostrat","events"):To.join(Ju,"events")}async function k_(e,t){if(!nc.has(t)){let o=(await L(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);nc.set(t,o)}let n=nc.get(t)+1;return nc.set(t,n),n}function x_(e,t){let n=Array.isArray(e?.epochs)?e.epochs:null;if(!(!n||n.length===0)){if(!Number.isFinite(t))return n.length-1;for(let r=n.length-1;r>=0;r--){let o=n[r].startedAt;if(o==null||o<=t)return r}return 0}}async function _(e,t){let n=typeof e=="string"?e:e?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!t||!g_.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!y_.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!b_.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?To.join(Ju,"events"):Ao(e),o=t.epoch;if(o==null&&typeof e=="object"&&t.boxId&&Array.isArray(e?.boxes)){let c=e.boxes.find(l=>l.name===t.boxId);o=x_(c,Number(t.payload?.ts??t.ts)||Date.now())}let s=await k_(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||w_(),trackId:n,boxId:t.boxId??null,...o!=null?{epoch:o}:{},...a?{userId:a}:{},seq:s,ts:t.ts??Date.now(),type:t.type,actor:t.actor??"system",provider:t.provider??null,source:t.source??"orchestrator",payload:t.payload??{},...t.causationId?{causationId:t.causationId}:{},...t.correlationId?{correlationId:t.correlationId}:{}};return await zu.mkdir(r,{recursive:!0}),await zu.appendFile(Cw(r,n),JSON.stringify(i)+`
|
|
15
|
+
`),at.emit("event",i),at.emit(`track:${n}`,i),i}async function L(e,t){let n,r;typeof e=="string"&&t?(n=e,r=t):(r=typeof e=="string"?e:e?.id,n=typeof e=="string"?To.join(Ju,"events"):Ao(e));let o;try{o=await zu.readFile(Cw(n,r),"utf8")}catch{return[]}let s=[];for(let a of o.split(`
|
|
16
|
+
`))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function Mw(e,t){let n=`track:${e}`;return at.on(n,t),()=>at.off(n,t)}var g_,y_,b_,at,Ju,Cw,nc,ue=C(()=>{g_=new Set(["session.ready","turn.user","turn.agent","turn.retracted","peer.message","provider.error","turn.interrupted","tool.called","tool.result","prompt.raised","prompt.answered","prompt.autoRejected","box.fullscreen.detected","box.fullscreen.answered","context.usage","cost.recorded","memory.seed","memory.harvested","handoff.artifact","code.pushed","handoff.started","handoff.step","handoff.done","handoff.failed","subtask.delegated","subtask.merged","job.started","pr.state.changed","pr.offered","pr.offer.answered","pr.exists.offered","pr.exists.answered","box.state.changed","view.state.changed","view.spinup.failed","mock.state.changed","flow.state.changed","runbook.state.changed","ticket.linked","ticket.sync.failed","ticket.move.offered","ticket.move.answered","ticket.state.changed","ticket.commented","ticket.flow.changed","ticket.create.offered","ticket.create.answered","ticket.created","loop.create.offered","loop.create.answered","loop.created","notify.raised","usage.cap.hit","usage.cap.warning","provider.connected","track.summary.recorded","track.transferred","track.recovered","track.circuit.tagged","blueprint.item.bound","decision.recorded","changelog.published","stall.detected","error"]),y_=new Set(["user","agent","orchestrator","system","peer"]),b_=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),at=new h_;at.setMaxListeners(0);Ju=process.env.BOSTRAT_STATE_DIR||To.join(process.env.HOME||m_.tmpdir(),".bostrat");Cw=(e,t)=>To.join(e,`${t}.ndjson`),nc=new Map});import K from"node:fs/promises";import S_ from"node:os";import se from"node:path";import{execFile as v_}from"node:child_process";import{promisify as T_}from"node:util";function oc(){return se.join(sc,"memory","repos")}function Mr(e){return e===dn?se.join(sc,"memory",dn):se.join(oc(),e)}async function ea(e){try{return JSON.parse(await K.readFile(se.join(e,Fw),"utf8"))}catch{return{}}}async function Xu(e,t){let n=await ea(e);for(let[r,o]of t)n[r]=Math.max(Number(n[r])||0,o);await K.mkdir(e,{recursive:!0}),await K.writeFile(se.join(e,Fw),JSON.stringify(n))}function Pw(e){let t=String(e||"").trim();if(!t)return null;let r=(t.replace(/\/+$/,"").split(/[/:]/).pop()||"").replace(/\.git$/i,"").toLowerCase();return/^[a-z0-9][a-z0-9._-]*$/.test(r)?r:null}async function Dw(e){try{let{stdout:t}=await A_("git",["-C",e,"config","--get","remote.origin.url"]);return t.trim()||null}catch{return null}}async function Zu(e){if(!e)return[];let t=Pw(await Dw(e));if(t)return[t];let n=[],r=[];try{r=await K.readdir(e,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=Pw(await Dw(se.join(e,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function ac(e){let t=/^---\n([\s\S]*?)\n---\n?/.exec(e);return t?{fm:t[1],body:e.slice(t[0].length)}:{fm:null,body:e}}function Wn(e,t){let n=new RegExp(`^\\s*${t}:\\s*(.+?)\\s*$`,"m").exec(e||"");return n?n[1].replace(/^"|"$/g,""):null}async function Kt(e,t=null){let n=t??await K.readFile(e,"utf8").catch(()=>""),{fm:r}=ac(n),o=Wn(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await K.stat(e)).mtimeMs}catch{return 0}}function Uw(e,t){let n=Wn(e,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===dn){let o=Wn(e,"trust");if(!o||/^operator/i.test(o))return dn}return t}function Qu(e,t,n,r=new Map){let o=(e||"").split(`
|
|
17
17
|
`),s=new Map;for(let c of(t||"").split(`
|
|
18
|
-
`)){let l=
|
|
18
|
+
`)){let l=_o(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=_o(c);return l?(a.add(l),n.has(l)&&s.has(l)?s.get(l):c):c});for(let[c,l]of s)a.has(c)||!n.has(c)||(a.add(c),i.push(l));for(let c of n){if(a.has(c))continue;let l=r.get(c);l&&i.push(l)}return i.join(`
|
|
19
19
|
`).replace(/\n{3,}/g,`
|
|
20
20
|
|
|
21
|
-
`)}function
|
|
22
|
-
`),o);if(a.length){let
|
|
23
|
-
`).filter(d
|
|
24
|
-
`)}await K.mkdir(n,{recursive:!0}),await K.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function
|
|
25
|
-
`);t.length>
|
|
26
|
-
`);for(;Buffer.byteLength(n,"utf8")>
|
|
27
|
-
`);return n}async function
|
|
21
|
+
`)}function Hw(e,t){let n=Wn(t,"name")||e.replace(/\.md$/,""),r=Wn(t,"description")||"";return`- [${n}](${e})${r?` \u2014 ${r}`:""}`}async function ta(e){let t=[];try{t=await K.readdir(e,{withFileTypes:!0})}catch{return[]}return t.filter(n=>n.isFile()&&n.name.endsWith(".md")&&n.name!=="MEMORY.md").map(n=>n.name)}async function Ww(e,t){let n=se.join(e,".bostrat","memory"),r=await ta(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=Bw.get(e)??0,s=Date.now(),a=new Map,i=new Map,c=0,l=new Map;for(let d of r){let f=se.join(n,d);if(o)try{if((await K.stat(f)).mtimeMs<o-__)continue}catch{continue}let p;try{p=await K.readFile(f,"utf8")}catch{continue}let{fm:m}=ac(p),h=Uw(m,t);if(!h)continue;let g=Mr(h),x=se.join(g,d),k=await Kt(f,p);if(l.has(g)||l.set(g,await ea(g)),(l.get(g)[d]||0)>=k)continue;let A=await Kt(x).catch(()=>0);if(await K.access(x).then(()=>!0).catch(()=>!1)&&A>=k)continue;await K.mkdir(g,{recursive:!0}),await K.writeFile(x,p),c++;let M=a.get(g);M||a.set(g,M={won:new Set,generated:new Map}),M.won.add(d),M.generated.set(d,Hw(d,m)),i.has(h)||i.set(h,new Set),i.get(h).add(d)}let u=await K.readFile(se.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[d,f]of a){let p=se.join(d,"MEMORY.md"),m=await K.readFile(p,"utf8").catch(()=>"");await K.writeFile(p,Qu(m,u,f.won,f.generated))}return Bw.set(e,s),{harvested:c,changedByKey:i}}async function qw(e){let t=0,n=new Map;for(let r of e||[]){if(!r||!await K.access(se.join(r,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[s]=await Zu(r),a=await Ww(r,s||null);t+=a.harvested;for(let[i,c]of a.changedByKey){n.has(i)||n.set(i,new Set);for(let l of c)n.get(i).add(l)}}return{harvested:t,changedByKey:n}}async function I_(){let e=se.join(sc,"workspaces"),t=[];try{t=await K.readdir(e,{withFileTypes:!0})}catch{return{harvested:0,changedByKey:new Map}}return qw(t.filter(n=>n.isDirectory()).map(n=>se.join(e,n.name)))}async function O_(e,t){let n=Mr(e),r=await ea(n),o=new Set,s=new Map,a=[],i=0;for(let c of t||[]){let l=String(c?.file||"");if(!R_.test(l)||l==="MEMORY.md")continue;let u=se.join(n,l),d=await K.access(u).then(()=>!0).catch(()=>!1),f=d?await Kt(u):0,p=Number(c.modified)||0;if(c.deleted){p>f&&(d&&await K.rm(u,{force:!0}),await Xu(n,[[l,p]]),d&&(a.push(l),i++));continue}d&&f>=p||(r[l]||0)>=p||(await K.mkdir(n,{recursive:!0}),await K.writeFile(u,String(c.content??"")),o.add(l),typeof c.indexLine=="string"&&c.indexLine.trim()&&s.set(l,c.indexLine),i++)}if(o.size||a.length){let c=se.join(n,"MEMORY.md"),l=await K.readFile(c,"utf8").catch(()=>""),u=Qu(l,[...s.values()].join(`
|
|
22
|
+
`),o);if(a.length){let d=new Set(a);u=u.split(`
|
|
23
|
+
`).filter(f=>!d.has(_o(f))).join(`
|
|
24
|
+
`)}await K.mkdir(n,{recursive:!0}),await K.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function ic(e,t=null){let n=Mr(e),r=t?.length?t:await ta(n);if(!r.length)return{pushed:0,source:"empty"};let o=$_(await K.readFile(se.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=se.join(n,a),c;try{c=await K.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await Kt(i,c),indexLine:o.get(a)??null})}return tc(e,s)}async function C_(e,{timeoutMs:t}={}){let{facts:n,source:r}=await $w(e,t?{timeoutMs:t}:{});n.length?await O_(e,n):r==="plane"&&await ic(e)}function M_(e){let t=e.split(`
|
|
25
|
+
`);t.length>Nw&&(t=t.slice(0,Nw));let n=t.join(`
|
|
26
|
+
`);for(;Buffer.byteLength(n,"utf8")>E_&&t.length;)t.pop(),n=t.join(`
|
|
27
|
+
`);return n}async function P_(e,t){if(!e||!t?.length)return{materialized:!1,reason:"no-repo-key"};let n=se.join(e,".bostrat","memory"),r=0,o=0,s=[];for(let u of t){let d=Mr(u),f=await ea(d);for(let[g,x]of Object.entries(f)){let k=se.join(n,g);await K.access(k).then(()=>!0).catch(()=>!1)&&await Kt(k)<=x&&(await K.rm(k,{force:!0}),o++)}let p=await ta(d);if(!p.length)continue;await K.mkdir(n,{recursive:!0});for(let g of p){let x=se.join(d,g),k=se.join(n,g),A=await Kt(x),I=await Kt(k).catch(()=>0);await K.access(k).then(()=>!0).catch(()=>!1)&&I>=A||(await K.copyFile(x,k),r++)}let m=await K.readFile(se.join(d,"MEMORY.md"),"utf8").catch(()=>""),h=u===dn?"## operator memory":`## repo: ${u}`;m.trim()&&s.push(t.length>1?`${h}
|
|
28
28
|
|
|
29
29
|
${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(`
|
|
30
30
|
|
|
31
31
|
`)+`
|
|
32
|
-
`,c=
|
|
33
|
-
`).filter(p=>!s.has(
|
|
34
|
-
`);await K.writeFile(c,u).catch(()=>{});let
|
|
35
|
-
`).filter(l=>
|
|
36
|
-
`)).catch(()=>{});let c=await
|
|
37
|
-
`)){let r=
|
|
32
|
+
`,c=M_(i);await K.mkdir(n,{recursive:!0}),await K.writeFile(se.join(n,"MEMORY.md"),c);let l=se.join(n,"claude");return await K.mkdir(l,{recursive:!0}),await K.writeFile(se.join(l,"MEMORY.md"),N_,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function Kw(e){try{let t=await Zu(e);if(!t.length)return{materialized:!1,reason:"no-repo-key"};let n=[...t,dn];return await Promise.all(n.map(r=>C_(r,{timeoutMs:D_}))),await P_(e,n)}catch(t){return{materialized:!1,reason:"error",error:t?.message||String(t)}}}function Gw(){return{...Be,pending:Eo.size,queued:Yu.size}}function Vw(e){if(rc.has(e))return rc.get(e);Yu.add(e);let t=jw.then(async()=>{Yu.delete(e),rc.delete(e);let n=await td(e);return Be.runs++,Be.lastRunAt=Date.now(),n.error?(Be.errors++,Be.lastError=n.error):Be.harvested+=n.harvested||0,n}).catch(n=>({harvested:0,error:n?.message||String(n)}));return rc.set(e,t),jw=t.then(()=>{}),t}function cc(e,{delayMs:t=B_}={}){if(!e||typeof e!="string")return;Be.scheduled++;let n=Eo.get(e);n&&clearTimeout(n);let r=setTimeout(()=>{Eo.delete(e),Vw(e)},t);r.unref?.(),Eo.set(e,r)}async function ed(e,{timeoutMs:t=3e3}={}){if(!e)return{harvested:0,skipped:"no-cwd"};if(!await K.access(se.join(e,".bostrat","memory")).then(()=>!0,()=>!1))return{harvested:0,skipped:"no-memory-dir"};let r=Eo.get(e);r&&(clearTimeout(r),Eo.delete(e));let o,s=new Promise(a=>{o=setTimeout(()=>a({harvested:0,timedOut:!0}),t),o.unref?.()});try{return await Promise.race([Vw(e),s])}finally{clearTimeout(o)}}async function zw(e){return Qs||(Qs=(async()=>{try{Lw||(Lw=!0,await L_());let{harvested:t,changedByKey:n}=await qw(e);for(let[r,o]of n)await ic(r,[...o]);return Be.reconciles++,Be.lastReconcileAt=Date.now(),Be.harvested+=t,{harvested:t,keys:[...n.keys()]}}catch(t){return Be.errors++,Be.lastError=t?.message||String(t),{harvested:0,error:Be.lastError}}finally{Qs=null}})(),Qs)}function j_(){return se.join(sc,"memory",".full-sweep-done")}async function Jw(){let e=j_();try{let t=JSON.parse(await K.readFile(e,"utf8"));return Be.backfill={at:Number(t?.at)||0,harvested:Number(t?.harvested)||0,prior:!0},{ran:!1,...Be.backfill}}catch{}try{let{harvested:t,changedByKey:n}=await I_();for(let[o,s]of n)await ic(o,[...s]);let r={at:Date.now(),harvested:t};return await K.mkdir(se.dirname(e),{recursive:!0}),await K.writeFile(e,JSON.stringify(r)),Be.backfill=r,{ran:!0,...r}}catch(t){return Be.errors++,Be.lastError=t?.message||String(t),{ran:!1,error:Be.lastError}}}async function L_(){let e=0,t=[];try{t=await K.readdir(oc(),{withFileTypes:!0})}catch{return{moved:e}}for(let n of t){if(!n.isDirectory())continue;let r=se.join(oc(),n.name),o=Mr(dn),s=new Set,a=new Map;for(let p of await ta(r)){let m=se.join(r,p),h;try{h=await K.readFile(m,"utf8")}catch{continue}let{fm:g}=ac(h);if(Uw(g,n.name)!==dn)continue;let x=se.join(o,p),k=await Kt(x).catch(()=>0),A=await Kt(m,h);await K.mkdir(o,{recursive:!0}),A>k&&await K.writeFile(x,h),await K.rm(m,{force:!0}),s.add(p),a.set(p,Hw(p,g)),e++}if(!s.size)continue;let i=Date.now();await Xu(r,[...s].map(p=>[p,i])),await tc(n.name,[...s].map(p=>({file:p,deleted:!0,modified:i})));let c=se.join(r,"MEMORY.md"),l=await K.readFile(c,"utf8").catch(()=>""),u=l.split(`
|
|
33
|
+
`).filter(p=>!s.has(_o(p))).join(`
|
|
34
|
+
`);await K.writeFile(c,u).catch(()=>{});let d=se.join(o,"MEMORY.md"),f=await K.readFile(d,"utf8").catch(()=>"");await K.writeFile(d,Qu(f,l,s,a))}return{moved:e}}async function Yw(){let e=[],t=[dn];try{for(let n of await K.readdir(oc(),{withFileTypes:!0}))n.isDirectory()&&t.push(n.name)}catch{}for(let n of t){let r=Mr(n),o=await ta(r),s=await ea(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await K.readFile(se.join(r,i),"utf8").catch(()=>""),{fm:l}=ac(c);a.push({file:i,name:Wn(l,"name")||i.replace(/\.md$/,""),description:Wn(l,"description")||"",scope:Wn(l,"scope")||null,modified:await Kt(se.join(r,i),c)})}e.push({key:n,facts:a,tombstones:Object.keys(s).length})}return e}async function Xw(e,t){if(!/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/.test(t)||t==="MEMORY.md")throw new Error("bad fact filename");let n=Mr(e),r=se.join(n,t);if(!await K.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${t} not found in ${e}`);let s=Date.now();await K.rm(r,{force:!0}),await Xu(n,[[t,s]]);let a=se.join(n,"MEMORY.md"),i=await K.readFile(a,"utf8").catch(()=>"");await K.writeFile(a,i.split(`
|
|
35
|
+
`).filter(l=>_o(l)!==t).join(`
|
|
36
|
+
`)).catch(()=>{});let c=await tc(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function td(e){try{let t=await Zu(e),{harvested:n,changedByKey:r}=await Ww(e,t[0]||null);for(let[o,s]of r)await ic(o,[...s]);return n>0&&await U_(e,r).catch(()=>{}),{harvested:n}}catch(t){return{harvested:0,error:t?.message||String(t)}}}async function U_(e,t){let n=/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1];if(!n)return;let r={},o=0;for(let[s,a]of t){let i=[...a].sort();o+=i.length,r[s]={count:i.length,files:i.slice(0,F_)}}await _({id:n,cwd:e},{type:"memory.harvested",actor:"orchestrator",payload:{harvested:o,keys:r,ts:Date.now()}})}var A_,sc,Nw,E_,dn,Fw,_o,Bw,__,R_,$_,N_,D_,B_,Eo,Yu,jw,Be,rc,Lw,Qs,F_,na=C(()=>{Ow();ue();A_=T_(v_),sc=process.env.BOSTRAT_STATE_DIR||se.join(process.env.HOME||S_.tmpdir(),".bostrat"),Nw=200,E_=25*1024;dn="operator";Fw=".tombstones.json";_o=e=>/\]\(([^)]+\.md)\)/.exec(e)?.[1]||null;Bw=new Map,__=2e3;R_=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,$_=e=>{let t=new Map;for(let n of(e||"").split(`
|
|
37
|
+
`)){let r=_o(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};N_=`# Track memory
|
|
38
38
|
|
|
39
39
|
Shared Track memory (all providers) lives one level up: read \`../MEMORY.md\` for the
|
|
40
40
|
index and the fact files beside it. Facts you save here stay Claude-local but ride the
|
|
41
41
|
same sync; prefer adding durable cross-provider facts to \`../\` following its format.
|
|
42
|
-
`;
|
|
43
|
-
`,n!=null?{mode:n}:void 0),await
|
|
44
|
-
`,"utf8");let n=(await
|
|
45
|
-
`).filter(Boolean);n.length>
|
|
42
|
+
`;D_=Number(process.env.BOSTRAT_MEMORY_SPAWN_PULL_MS)||1500;B_=Number(process.env.BOSTRAT_MEMORY_HARVEST_DEBOUNCE_MS)||2e3,Eo=new Map,Yu=new Set,jw=Promise.resolve(),Be={scheduled:0,runs:0,harvested:0,errors:0,lastRunAt:null,lastError:null,reconciles:0,lastReconcileAt:null,backfill:null};rc=new Map;Lw=!1,Qs=null;F_=20});import nd from"node:fs/promises";import H_ from"node:path";async function it(e,t,{mode:n}={}){await nd.mkdir(H_.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${++W_}.tmp`;await nd.writeFile(r,JSON.stringify(t,null,2)+`
|
|
43
|
+
`,n!=null?{mode:n}:void 0),await nd.rename(r,e)}var W_,qn=C(()=>{W_=0});import ng from"node:fs/promises";import rg from"node:os";import og from"node:path";function Ee(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function sa(e,t={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),q_);try{return await Y_(`${ke(n)}${e}`,{...t,signal:r.signal})}finally{clearTimeout(o)}}async function ag(){return uc||(rd||=ng.readFile(sg(),"utf8").then(e=>JSON.parse(e)).catch(()=>({ops:[]})).then(e=>uc={ops:Array.isArray(e?.ops)?e.ops:[],parked:Array.isArray(e?.parked)?e.parked:[]}).finally(()=>rd=null),rd)}async function dc(){await it(sg(),uc).catch(()=>{})}function eg(e){return e.op==="blob"?`blob ${e.family} ${e.id} ${e.v} ${e.kind}`:`${e.op} ${e.family} ${e.id}`}async function sd(e){let t=await ag(),n=eg(e);if(e.op==="delete")t.ops=t.ops.filter(r=>!(r.family===e.family&&r.id===e.id)),t.ops.push(e);else{t.ops=t.ops.filter(o=>!(o.op==="delete"&&o.family===e.family&&o.id===e.id));let r=t.ops.findIndex(o=>eg(o)===n);r>=0?t.ops[r]=e:t.ops.push(e)}t.ops.length>Zw&&(t.ops=t.ops.slice(-Zw)),await dc()}function tg(){lc||(lc=setTimeout(()=>{lc=null,Io().catch(()=>{})},X_),lc.unref?.())}function od(e){let t=new Error(`plane ${e}`);return t.refused=!0,t}async function Q_(e,t=process.env){if(e.op==="record"){let n=await sa(`/v1/content/${encodeURIComponent(e.family)}`,{method:"POST",headers:{...oa(t),"content-type":"application/json"},body:JSON.stringify({records:[{id:e.id,trackId:e.trackId??null,data:e.data,updatedAt:e.updatedAt,originNode:J_()}]})},t);if(!n.ok)throw od(n.status);let o=(await n.json().catch(()=>null))?.results?.[0];return o?.outcome==="rejected"?{delivered:!1,rejected:o.reason}:{delivered:!0}}if(e.op==="blob"){let n=await ng.readFile(e.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await sa(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}/blob?v=${e.v}&kind=${encodeURIComponent(e.kind)}`,{method:"POST",headers:{...oa(t),"content-type":"application/octet-stream"},body:n},t);if(!r.ok&&r.status!==400)throw od(r.status);return r.status===400?{delivered:!1,rejected:(await r.json().catch(()=>null))?.error||"rejected"}:{delivered:!0}}if(e.op==="delete"){let n=await sa(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}?deletedAt=${e.deletedAt}`,{method:"DELETE",headers:oa(t)},t);if(!n.ok)throw od(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function Io({env:e=process.env,log:t=console,now:n=Date.now()}={}){return Ee(e)?ra||(ra=(async()=>{let r=await ag();eI(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await Q_(a,e)}catch(c){if(!c?.refused)return tg(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<G_)return tg(),await dc(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};t.error?.(`[content-plane] ALARM parking ${a.op} ${a.family}/${a.id} after ${a.refusals} refusals (${c.message}) \u2014 draining the ${r.ops.length-1} ops behind it`),r.ops.shift(),r.parked.push({...a,parkedAt:n,error:c.message}),r.parked.length>Qw&&(r.parked=r.parked.slice(-Qw)),s++,await dc();continue}i.rejected&&t.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await dc()}return{flushed:o,parked:s,pending:0}})().finally(()=>{ra=null}),ra):{flushed:0,source:"unconfigured"}}function eI(e,t){if(!e.parked.length||e.ops.length)return;let n=e.parked.filter(r=>t-(r.parkedAt||0)>=Z_);if(n.length){e.parked=e.parked.filter(r=>!n.includes(r));for(let r of n)e.ops.push({...r,refusals:0,parkedAt:void 0,error:void 0})}}async function tt(e,t,{trackId:n=null,env:r=process.env}={}){return Ee(r)?(await sd({op:"record",family:e,id:t.id,trackId:n??t.trackId??null,data:t,updatedAt:Number(t.updatedAt)||Date.now()}),Io({env:r})):{queued:!1,source:"unconfigured"}}async function Kn(e,t,n,r,o,{env:s=process.env}={}){return Ee(s)?(await sd({op:"blob",family:e,id:t,v:Number(n),kind:r,path:o}),Io({env:s})):{queued:!1,source:"unconfigured"}}async function Ot(e,t,{deletedAt:n=Date.now(),env:r=process.env}={}){return Ee(r)?(await sd({op:"delete",family:e,id:t,deletedAt:n}),Io({env:r})):{queued:!1,source:"unconfigured"}}async function Fe(e,{env:t=process.env}={}){if(!Ee(t))return{records:[],source:"unconfigured"};try{let n=await sa(`/v1/content/${encodeURIComponent(e)}?tombstones=1`,{headers:oa(t)},t);if(!n.ok)throw new Error(`plane ${n.status}`);let r=await n.json();return{records:Array.isArray(r?.records)?r.records:[],source:"plane"}}catch(n){return{records:[],source:"unreachable",error:n?.message}}}async function aa(e,t,n,r,{env:o=process.env}={}){if(!Ee(o))return null;try{let s=await sa(`/v1/content/${encodeURIComponent(e)}/${encodeURIComponent(t)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:oa(o)},o);if(!s.ok)return null;let a=Buffer.from(await s.arrayBuffer());return r==="md"||r==="html"?a.toString("utf8"):a}catch{return null}}var q_,K_,Zw,G_,Qw,V_,z_,sg,J_,Y_,X_,Z_,oa,uc,rd,lc,ra,Gn=C(()=>{et();qn();q_=1e4,K_=3e4,Zw=2e3,G_=5,Qw=100,V_=60*6e4,z_=()=>process.env.BOSTRAT_STATE_DIR||og.join(process.env.HOME||rg.tmpdir(),".bostrat"),sg=()=>og.join(z_(),"content-outbox.json"),J_=()=>process.env.BOSTRAT_NODE_ID||rg.hostname(),Y_=(...e)=>fetch(...e),X_=K_,Z_=V_;oa=(e=process.env)=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`});uc=null,rd=null,lc=null,ra=null});import ad from"node:fs/promises";import ig from"node:path";async function cg(e,t,{box:n=null,agent:r=null}={}){if(!e||!Ee())return{pushed:!1};try{let o=Date.now();return await tt("checkpoints",{id:e,trackId:e,v:o,box:n,agent:r,updatedAt:o},{trackId:e}),await Kn("checkpoints",e,o,"md",t),ia=null,{pushed:!0,v:o}}catch{return{pushed:!1}}}async function sI(){if(ia&&Date.now()-ia.at<oI)return{records:ia.records,source:"plane"};let e=await Fe("checkpoints");return e.source==="plane"&&(ia={at:Date.now(),records:e.records}),e}async function fc(e){try{let t=rI(e);if(!t||!Ee())return{restored:!1};let n=ig.join(e,tI,nI);if(await ad.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await sI();if(o!=="plane")return{restored:!1};let s=r.find(i=>!i.deletedAt&&i.data?.id===t);if(!s?.data?.v)return{restored:!1};let a=await aa("checkpoints",t,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await ad.mkdir(ig.dirname(n),{recursive:!0}),await ad.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var tI,nI,rI,oI,ia,pc=C(()=>{Gn();tI=".bostrat/handoff",nI="latest.md",rI=e=>/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1]??null;oI=6e4,ia=null});import ca from"node:fs/promises";import aI from"node:os";import ug from"node:path";function Nr(e){let t=String(e??"");for(let n of iI)t=t.replace(n,"\u2039redacted\u203A");return t}function mc(){let e=process.env.BOSTRAT_HANDOFF_DEBUG;return e==="1"||e==="true"}async function cI(e){try{await ca.mkdir(id,{recursive:!0}),await ca.appendFile(Ro,`${JSON.stringify(e)}
|
|
44
|
+
`,"utf8");let n=(await ca.readFile(Ro,"utf8")).split(`
|
|
45
|
+
`).filter(Boolean);n.length>lg&&await ca.writeFile(Ro,`${n.slice(-lg).join(`
|
|
46
46
|
`)}
|
|
47
|
-
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${
|
|
48
|
-
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=
|
|
49
|
-
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,f,
|
|
50
|
-
`).map(n=>n.trim()).includes(e)}catch{return!1}}function
|
|
51
|
-
`).filter(Boolean).length}catch{return 0}}async function
|
|
52
|
-
`);for(let n=t.length-1;n>=0;n--){let r=t[n].trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a=(s.input_tokens||0)+(s.cache_creation_input_tokens||0)+(s.cache_read_input_tokens||0);if(a>0)return{occupancy:a,model:o.message.model||o.model||null}}return null}async function
|
|
53
|
-
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function
|
|
54
|
-
`,n);i!==-1&&(t=t.slice(0,i))}let r=
|
|
55
|
-
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function
|
|
56
|
-
`)){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(
|
|
57
|
-
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=t)break}return o}finally{await n.close()}}async function
|
|
58
|
-
`)){let p=
|
|
59
|
-
`;else if(s==="t")n+=" ";else if(s==="r")n+="\r";else if(s==="u")if(e[r+1]==="{"){let a=e.indexOf("}",r+2);if(a===-1)return null;n+=String.fromCodePoint(parseInt(e.slice(r+2,a),16)),r=a}else n+=String.fromCharCode(parseInt(e.slice(r+1,r+5),16)),r+=4;else n+=s}else{if(o===t)return n;if(t==="`"&&o==="$"&&e[r+1]==="{")return null;n+=o}}return null}function
|
|
60
|
-
`)){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
|
|
61
|
-
`).trim():"";
|
|
62
|
-
`).trim():""}function
|
|
63
|
-
`)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let h=c.attachment;h?.type==="queued_command"&&h.commandMode==="task-notification"&&typeof h.prompt=="string"&&h.prompt.trim()&&o.push({role:"user",text:h.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 h of l.content){let g=h?.type==="tool_use"?h.input?.file_path||h.input?.path:null;typeof g=="string"&&
|
|
64
|
-
|
|
65
|
-
`):""}var
|
|
66
|
-
`);if(!
|
|
67
|
-
`);for(let r=n.length-1;r>=0;r--)if(
|
|
68
|
-
`);return t}function
|
|
69
|
-
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(
|
|
70
|
-
`).pop()||"";return
|
|
71
|
-
`),o=r.findIndex(i=>
|
|
72
|
-
`).map(a=>a.trim()),o=r.findIndex(a=>/Press any key to log in/.test(a)),s=r.slice(0,o<0?r.length:o).filter(a=>a&&!/^Cursor Agent$/i.test(a)&&!/^v?\d{4}\.\d{2}\.\d{2}/.test(a)).join(" ");return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,answerMode:"keys",promptId:"cursor:signin-start",questions:[{question:(s?`${s} `:"")+"Cursor isn't signed in on this box \u2014 start the sign-in to get a login link you can open from any device.",header:"Sign in",multiSelect:!1,options:[{label:"Start sign-in",description:"Shows a login link (and a QR option) \u2014 your login goes to cursor.com, never through bostrat"}]}]}}function
|
|
73
|
-
`),o="",s=r.findIndex(a=>/https:\/\//.test(a));if(s>=0){o=r[s].slice(r[s].indexOf("https://")).trim();for(let a=s+1;a<r.length;a++){let i=r[a].trim();if(!i||!/^[A-Za-z0-9~%&=?/_.-]+$/.test(i))break;o+=i}}return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,promptId:`cursor:signin-link:${(o.match(/[?&]challenge=([\w-]+)/)||[])[1]?.slice(0,16)||o.slice(-24)||"pending"}`,questions:[{question:o?`Cursor is waiting for you to finish signing in: open ${o} on any device and log in. Your login goes straight to cursor.com \u2014 bostrat never sees it. This card clears once sign-in completes.`:"Cursor is waiting for you to finish signing in \u2014 open the login link shown in the Terminal tab. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function
|
|
74
|
-
`)
|
|
75
|
-
`).slice(
|
|
76
|
-
`)
|
|
47
|
+
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${Ro}: ${t?.message||t}`)}}async function Y(e={}){let t={at:new Date().toISOString(),ts:Date.now(),...e},n=[t.flow||"handoff",t.phase||t.step||"?",t.trackId?`track=${t.trackId}`:"",t.sessionName?`box=${t.sessionName}`:"",t.error?`ERR=${t.error}`:""].filter(Boolean).join(" \xB7 ");if(console.log(`[handoff-debug] ${n}`),t.sessionName&&mc())try{t.paneTail=Nr((await Te(t.sessionName,{lines:35})).slice(-4e3))}catch{t.paneTail=null}(mc()||t.error||/^(checkpoint|spawn|hydrate|commit|done|summarize|clear|resume|ready|send|failed|launch|submit-|gate-accept|blocked|timeout|dialog)/.test(String(t.phase||t.step||"")))&&await cI(t)}async function dg({limit:e=80}={}){try{return(await ca.readFile(Ro,"utf8")).split(`
|
|
48
|
+
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Nr(o.paneTail)),o}catch{return{raw:Nr(r)}}})}catch{return[]}}var iI,id,Ro,lg,fg,Gt=C(()=>{Ve();iI=[/[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];id=process.env.BOSTRAT_DEBUG_DIR||ug.join(aI.homedir(),".bostrat","debug"),Ro=ug.join(id,"handoff.jsonl"),lg=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;fg={DEBUG_DIR:id,LOG_FILE:Ro}});import{execFile as mg}from"node:child_process";import{promisify as lI}from"node:util";import wc from"node:fs";import{randomUUID as hg}from"node:crypto";function Co(e){return new Promise(t=>setTimeout(t,e))}function hc(e,t){if(!t)return e;let n=o=>typeof o=="string"?o.split(t).join("<redacted>"):o,r=new Error(n(e?.message)||"command failed");return e?.code!==void 0&&(r.code=e.code),e?.stderr!==void 0&&(r.stderr=n(e.stderr)),r}function wg(e){return`/tmp/tmux-${e}/default`}function uI({explicit:e,legacyExists:t,runtimeDir:n,uid:r}){if(e)return e;let o=wg(r);return t?o:n?`${n}/bostrat/tmux-default`:o}function ua(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${la}=1`]}function dd(e,t,n){let r=Pr(e),o=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(e);if(!r||o)return e;let s=`${e} --session-id ${t}`,a=/(^|\s)(--name|-n)(\s|=)/.test(e);return n&&!a&&_e(n)&&(s+=` --name ${n}`),s}function Pr(e){return/(^|\/|\s)claude(\s|$)/.test(String(e||""))}function Vn(e){return/(^|\/|\s)claude(\s|$)/.test(e)?"claude":/cursor/.test(e)?"cursor":/codex/.test(e)?"codex":/gemini/.test(e)?"gemini":"agent"}function pI(e){let t=String(e||"").trim();return!t||/fake-|\.sh(\s|$)/.test(t)?!1:Pr(t)||/(^|\/|\s)codex(\s|$)/.test(t)||/cursor-agent/.test(t)||/(^|\/|\s)gemini(\s|$)/.test(t)}async function gg(e,{loginShell:t=!1,failOpen:n=!1}={}){let r=await yg(e,{loginShell:t});return r===gc?!0:r===$o?!1:n}async function yg(e,{loginShell:t=!1,timeoutMs:n=cd}={}){if(!e)return $o;let r=t?process.env.SHELL||"/bin/sh":"sh",o=t?"-lic":"-c";try{return await we(r,[o,'command -v "$1" >/dev/null 2>&1',"_",e],{timeout:n}),gc}catch(s){return s?.killed||s?.signal?Oo:s?.code===1||s?.code===127?$o:Oo}}async function yc(e,{retries:t=mI,sleep:n=hI,probe:r=yg}={}){let o=String(e||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:cd});for(let a=0;s===Oo&&a<t.length;a++)await n(t[a]),s=await r(o,{timeoutMs:cd*2});return s}function H(...e){return["-S",ft,...e]}async function zn(){if(!wc.existsSync(ft))return!1;try{return await we(F,H("list-sessions")),!0}catch(e){let t=e&&e.stderr?String(e.stderr):"";return!/no server running/i.test(t)}}async function da(){if(await zn())return!0;try{wc.mkdirSync(ft.replace(/\/[^/]+$/,""),{recursive:!0}),await we(F,H("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await zn()}function _e(e){return typeof e=="string"&&wI.test(e)}async function Ct(){if(!wc.existsSync(ft))return[];try{let e="#{session_name}|#{session_windows}|#{session_attached}|#{session_activity}|#{pane_current_path}|#{@bostrat-session}|#{@bostrat-title}|#{@bostrat-sid}|#{@bostrat-agent}|#{alternate_on}|#{session_created}",{stdout:t}=await we(F,H("list-sessions","-F",e)),n=Date.now();return t.trim().split(`
|
|
49
|
+
`).filter(Boolean).map(r=>{let[o,s,a,i,c,l,u,d,f,p,m]=r.split("|"),h=Number(i),g=Number(m);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(h)?n-h*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:d||null,agent:f||(l==="1"?"claude":null),alternateOn:p==="1",createdMs:Number.isFinite(g)&&g>0?g*1e3:null}}).filter(r=>!fI.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function ae(e){return(await Ct()).find(n=>n.name===e)||null}function bI(e){if(typeof e!="string")return!1;let t=e.trim();return t.length>=1&&t.length<=yI&&gI.test(t)}function bc(e){return e.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function bg(e){try{let{stdout:t}=await we(F,H("list-sessions","-F","#{session_name}"));return t.split(`
|
|
50
|
+
`).map(n=>n.trim()).includes(e)}catch{return!1}}function SI(e){let t=String(e||"").trim().split(/\s+/)[0]||"";if(t.includes("/"))return null;let n=[];for(let o=1;o<=t.length-3;o++)n.push(t.slice(o));if(!n.length)return null;let r=n.map(o=>o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return new RegExp(`\\b(?:${r.join("|")})`)}async function vI(e,t){let n=Date.now()+fa(),r=SI(t),o=!1,s=0;for(;;){if(await Co(kI),s+=1,!await bg(e))return;let a=await Te(e,{lines:15}),i=!o&&xI.test(a)&&!!r?.test(a);if(!i&&s>1&&!await fn(e))return;if(i){o=!0,await Y({flow:"spawn",phase:"launch-retry",sessionName:e,agent:Vn(t),promptHead:t.slice(0,120)}).catch(()=>{}),await we(F,H("send-keys","-t",e,"C-u")),await we(F,H("send-keys","-t",e,"-l",t)),await we(F,H("send-keys","-t",e,"Enter"));continue}if(Date.now()>n){await Y({flow:"spawn",phase:"launch-failed",sessionName:e,agent:Vn(t),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function Jn(e,t=dI,n={}){if(!bI(e))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=e.trim(),o=bc(r);if(!_e(o))throw new Error("resulting tmux session name is invalid");if(!await da())throw new Error(`host tmux server not running on ${ft} and auto-start failed \u2014 start one: ${F} -S ${ft} new-session -d -s keeper "sleep 2147483647"`);if(await bg(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||ld,a=n.agent||Vn(s),i=!!n.resumeSessionId,c=n.resumeSessionId||hg(),l=i?s:dd(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),d=a==="claude"&&Pr(s),f=n.firstRun===!0;f||(d&&await Fn(t),a==="codex"&&await ko(t),a==="cursor"&&await xo(t),await Kw(t),n.freshWorkspace!==!0&&await fc(t));let p=d&&!f?zi():null,m=pw(),h=["-e",`${Gi}=${m}`];p&&h.push("-e",`ANTHROPIC_API_KEY=${p}`),d&&h.push(...ua());try{await we(F,H("new-session","-d","-s",o,"-c",t,...h))}catch(g){throw hc(p?hc(g,p):g,m)}return await Dr(o),await we(F,H("set-option","-t",o,"@bostrat-session","1")),await we(F,H("set-option","-t",o,"@bostrat-title",r)),await we(F,H("set-option","-t",o,"@bostrat-sid",c)),await we(F,H("set-option","-t",o,"@bostrat-agent",a)),await we(F,H("send-keys","-t",o,l,"Enter")),pI(l)&&vI(o,l).catch(()=>{}),{name:o,title:r,cwd:t,sessionId:c,agent:a,pinned:u,hookToken:m}}async function Mt(e){if(!_e(e))throw new Error("invalid session name");await we(F,H("kill-session","-t",e))}async function kg(e,t){if(_e(e))try{let n=t?H("set-option","-t",e,"@bostrat-sid",t):H("set-option","-u","-t",e,"@bostrat-sid");await we(F,n)}catch{}}async function Te(e,{lines:t=40}={}){if(!_e(e))return"";try{let n=H("capture-pane","-t",e,"-p");Number.isFinite(t)&&t>0&&n.push("-S",`-${Math.floor(t)}`);let{stdout:r}=await we(F,n);return String(r||"")}catch{return""}}async function xg(e){if(!_e(e))return 0;try{let{stdout:t}=await we(F,H("list-clients","-t",e,"-F","1"));return String(t||"").trim().split(`
|
|
51
|
+
`).filter(Boolean).length}catch{return 0}}async function Dr(e,{cols:t=EI,rows:n=_I}={}){if(!_e(e))return!0;if(await xg(e))return!1;try{let r=await Sg(e);if(r.cols===t&&r.rows===n)return!0;await we(F,H("resize-window","-t",e,"-x",String(t),"-y",String(n))),await we(F,H("set-window-option","-t",e,"window-size","latest")),await Co(80)}catch{}return!0}async function II(e){if(!_e(e))return 0;try{let{stdout:t}=await we(F,H("display-message","-t",e,"-p","#{pane_pid}"));return Number(String(t||"").trim())||0}catch{return 0}}async function fn(e){let t=await II(e);if(!t)return!1;try{return await we("pgrep",["-P",String(t)]),!1}catch(n){return n?.code===1}}async function Sg(e){if(!_e(e))return{cols:0,rows:0};try{let{stdout:t}=await we(F,H("display-message","-t",e,"-p","#{window_width} #{window_height}")),[n,r]=String(t||"").trim().split(/\s+/).map(o=>Number(o)||0);return{cols:n,rows:r}}catch{return{cols:0,rows:0}}}async function kc(e,{cols:t=TI,rows:n=AI}={}){if(_e(e)&&!await xg(e))try{let r=await Sg(e),o=Math.max(r.cols,t),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await we(F,H("resize-window","-t",e,"-x",String(o),"-y",String(s))),await we(F,H("set-window-option","-t",e,"window-size","latest")),await Co(80))}catch{}}async function pn(e){return await kc(e),Te(e,{lines:je})}async function Ie(e,t,{settleMs:n=40,kind:r}={}){if(!_e(e))throw new Error("invalid session name");let o=(Array.isArray(t)?t:[t]).filter(s=>RI.has(s));for(let s=0;s<o.length;s++)await we(F,H("send-keys","-t",e,o[s])),n>0&&s<o.length-1&&await Co(n);return Y({flow:"tmux",phase:"send-keys",sessionName:e,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function Mo(e,t,{kind:n}={}){if(!_e(e))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(t)||0));return r>0&&await we(F,H("send-keys","-t",e,"-N",String(r),"BSpace")),Y({flow:"tmux",phase:"send-keys",sessionName:e,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function Sc(e,t,{perCharDelayMs:n=120,kind:r}={}){if(!_e(e))throw new Error("invalid session name");let o=[...String(t)];for(let s of o)await we(F,H("send-keys","-t",e,"-l",s)),n>0&&await Co(n);Y({flow:"tmux",phase:"type",sessionName:e,kind:r||"type",textLen:o.length,textHead:String(t).slice(0,120)}).catch(()=>{})}function $I(e,t){return new Promise((n,r)=>{let o=mg(F,H(...e),(s,a,i)=>{s?r(s):n({stdout:a,stderr:i})});o.stdin.on("error",()=>{}),o.stdin.end(t)})}async function OI(e,t){let n=`bostrat-${hg().slice(0,8)}`;await $I(["load-buffer","-b",n,"-"],t),await we(F,H("paste-buffer","-p","-d","-b",n,"-t",e))}async function Le(e,t,{enter:n=!0,submitDelayMs:r=0,kind:o,paste:s=!1}={}){if(!_e(e))throw new Error("invalid session name");if(typeof t=="string"&&t.length>0&&(s?await OI(e,t):await we(F,H("send-keys","-t",e,"-l",t))),n){let a=Number(r);Number.isFinite(a)&&a>0&&await Co(a),await we(F,H("send-keys","-t",e,"Enter"))}Y({flow:"tmux",phase:"send",sessionName:e,kind:o||"text",textLen:typeof t=="string"?t.length:0,textHead:typeof t=="string"?t.slice(0,120):"",enter:n,submitDelayMs:r,...s?{paste:!0}:{}}).catch(()=>{})}var we,F,pg,ft,dI,ld,la,ud,fI,gc,$o,Oo,cd,mI,hI,fd,wI,gI,yI,fa,kI,xI,TI,AI,je,EI,_I,RI,xc,Ve=C(()=>{Js();Yi();Xi();na();pc();Gt();zs();we=lI(mg);F=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";pg=typeof process.getuid=="function"?process.getuid():0,ft=uI({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return wc.existsSync(wg(pg))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:pg}),dI=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",ld=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",la="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";ud="bostrat-usage-probe",fI=new Set([ud]);gc="present",$o="absent",Oo="unknown",cd=5e3,mI=[250,1e3,3e3],hI=e=>new Promise(t=>setTimeout(t,e));fd={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"};wI=/^[A-Za-z0-9_.-]{1,64}$/;gI=/^[A-Za-z0-9 ._↻-]+$/,yI=64;fa=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,kI=1e3,xI=/not found|Unknown command/i;TI=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,AI=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,je=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,EI=Number(process.env.BOSTRAT_BOX_COLS)||120,_I=Number(process.env.BOSTRAT_BOX_ROWS)||40;RI=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);xc=1600});import pa from"node:fs";import BI from"node:fs/promises";import No from"node:path";import jI from"node:os";function hd(e){return e.replace(/[^A-Za-z0-9]/g,"-")}function wd(e){let t=No.join(md,hd(e)),n;try{n=pa.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=No.join(t,o);try{let{mtimeMs:a}=pa.statSync(s);(!r||a>r.mtimeMs)&&(r={full:s,mtimeMs:a})}catch{}}return r?r.full:null}function Nt(e,t){if(!t)return null;let n=No.join(md,hd(e),`${t}.jsonl`);return pa.existsSync(n)?n:null}function Rg(e){if(!e)return[];let t=No.join(md,hd(e)),n;try{n=pa.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=pa.statSync(No.join(t,o));r.push({id:o.slice(0,-6),birthMs:s.birthtimeMs||s.ctimeMs||s.mtimeMs})}catch{}return r}async function ct(e,t=131072){let n=await BI.open(e,"r");try{let{size:r}=await n.stat(),o=Math.max(0,r-t),s=r-o;if(s===0)return"";let a=Buffer.alloc(s);return await n.read(a,0,s,o),a.toString("utf8")}finally{await n.close()}}function FI(e){let t=e.split(`
|
|
52
|
+
`);for(let n=t.length-1;n>=0;n--){let r=t[n].trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a=(s.input_tokens||0)+(s.cache_creation_input_tokens||0)+(s.cache_read_input_tokens||0);if(a>0)return{occupancy:a,model:o.message.model||o.model||null}}return null}async function ma(e,t=null,{fallbackNewest:n=!0}={}){if(!e)return null;let r,o;if(t){if(r=Nt(e,t),o=!0,!r)return null}else if(n){if(r=wd(e),o=!1,!r)return null}else return null;let s;try{s=FI(await ct(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(Ig,s.model),i=Ig[s.model]||LI;return{source:"transcript",exact:o,model:s.model,windowKnown:a,window:i,occupancy:s.occupancy,pct:Math.min(1,s.occupancy/i),transcript:r}}var Ig,LI,md,Pt=C(()=>{Ig={"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},LI=2e5,md=process.env.BOSTRAT_CLAUDE_PROJECTS||No.join(jI.homedir(),".claude","projects")});function UI(e){let t=[],n="",r=null;for(let o=0;o<e.length;o++){let s=e[o];if(r){n+=s,s===r&&e[o-1]!=="\\"&&(r=null);continue}if(s==="'"||s==='"'||s==="`"){r=s,n+=s;continue}if(s==="&"&&e[o+1]==="&"){t.push(n),n="",o++;continue}if(s==="|"&&e[o+1]==="|"){t.push(n),n="",o++;continue}if(s==="|"||s===";"||s===`
|
|
53
|
+
`){t.push(n),n="";continue}n+=s}return t.push(n),t.map(o=>o.trim()).filter(Boolean)}function ha(e){let t=[],n="",r=null,o=!1,s=()=>{if(o){if(/^\d*[<>]/.test(n)||n.startsWith(">")){n="",o=!1;return}t.push(n),n="",o=!1}};for(let a=0;a<e.length;a++){let i=e[a];if(r){i===r&&e[a-1]!=="\\"?r=null:n+=i,o=!0;continue}if(i==="'"||i==='"'){r=i,o=!0;continue}if(i==="<"&&e[a+1]==="<")return s(),t.push("<<"),t;if(/\s/.test(i)){s();continue}n+=i,o=!0}return s(),t}function wa(e){let t=e;for(;t.length&&/^[A-Za-z_][A-Za-z0-9_]*=/.test(t[0]);)t=t.slice(1);for(;t.length&&(t[0]==="sudo"||t[0]==="time"||t[0]==="env");)t=t.slice(1);return t}function xe(e,t=new Set){let n=[];for(let r=1;r<e.length;r++){let o=e[r];if(o==="<<")break;if(Cg(o)){t.has(o)&&r++;continue}n.push(o)}return n}function Mg(e){let t=e.indexOf("-n"),n=t!==-1?e[t+1]:null;if(!n)return null;let r=/^(\d+),(\d+)p$/.exec(n)||/^(\d+)p$/.exec(n);return r?r[2]?{from:r[1],to:r[2]}:{from:r[1],to:r[1]}:null}function qI(e){let t=Mg(e),n=ze(xe(e).find(r=>!/^\d+(,\d+)?p$/.test(r)));return t&&n?t.from===t.to?`Read line ${t.from} of ${Re(n)}`:`Read lines ${t.from}\u2013${t.to} of ${Re(n)}`:e.includes("-i")&&n?`Edit ${Re(n)}`:null}function gd(e){if(e.includes("--files")){let s=ze(xe(e,$g)[0]);return s&&s!=="."?`List files in ${Po(s)}`:"List files"}let t=xe(e,$g),n=t[0];if(!n)return null;let r=ze(t.slice(1).find(s=>s!==".")),o=Do(n,40);return r?`Search for \u201C${o}\u201D in ${Po(r)}`:`Search for \u201C${o}\u201D`}function yd(e){let t=xe(e,new Set(["--prefix"])),n=t[0];return n?n==="test"||n==="t"?"Run tests":n==="run"||n==="run-script"?t[1]?`Run ${t[1]}`:null:n==="ci"||n==="install"||n==="i"||n==="add"?"Install dependencies":null:null}function bd(e,t){if(e.includes("-e")||e.includes("--eval")||e.includes("-c")||e[1]==="-"||e.includes("<<"))return`Run an inline ${t} script`;if(e.includes("--test"))return"Run tests";let n=xe(e)[0];return n?`Run ${Re(n)}`:null}function KI(e){let t=xe(e),n=t[0],r=t.find(a=>/\.(service|timer|socket|target)$/.test(a))||t[1],o=r?r.replace(/\.service$/,""):"a service";return{status:`Check ${o} status`,show:`Inspect ${o}`,restart:`Restart ${o}`,start:`Start ${o}`,stop:`Stop ${o}`,"daemon-reload":"Reload systemd"}[n]||null}function Og(e){return e.find(t=>/^https?:\/\//.test(t))||null}function zI(e){let t=wa(ha(e));if(!t.length)return null;if(/^(ba|z)?sh$/.test(Re(t[0]))&&(t[1]==="-lc"||t[1]==="-c")&&t[2])return kd(t[2]);let n=Re(t[0]);if(VI.has(n))return null;if(t.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=GI[n];return r?r(t.map((o,s)=>s===0?n:o)):null}function kd(e){let t=String(e||"").trim();if(!t)return null;let n=t.indexOf("<<");if(n!==-1){let i=t.indexOf(`
|
|
54
|
+
`,n);i!==-1&&(t=t.slice(0,i))}let r=UI(t);for(let i=0;i<r.length-1;i++){let c=wa(ha(r[i])),l=wa(ha(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(Re(c[0]))&&Re(l[0])==="sed"){let u=Mg(l),d=xe(c)[0];if(u&&d)return Do(u.from===u.to?`Read line ${u.from} of ${Re(d)}`:`Read lines ${u.from}\u2013${u.to} of ${Re(d)}`,88)}}let o=[];for(let i of r){let c=zI(i);c&&o.push({d:c,head:Re(wa(ha(i))[0]||""),sub:xe(wa(ha(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":Do(s?s.d:o[0].d,88)}var Do,ze,Re,Po,Cg,$g,HI,WI,GI,VI,Ng=C(()=>{Do=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,ze=e=>e&&!/[$`]/.test(e)?e:null,Re=e=>e.replace(/\/+$/,"").split("/").pop()||e,Po=e=>{let t=e.replace(/\/+$/,"").split("/").filter(Boolean);return t.length>3?t.slice(-2).join("/"):e},Cg=e=>e.startsWith("-"),$g=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);HI={status:()=>"Check git status",diff:()=>"Review changes",log:()=>"Review commit history",show:()=>"Inspect a commit",add:()=>"Stage files",commit:()=>"Commit changes",push:()=>"Push to remote",pull:()=>"Pull from remote",fetch:()=>"Fetch from remote",merge:()=>"Merge branches",rebase:()=>"Rebase branch",stash:()=>"Stash changes",clone:e=>e[1]?`Clone ${Re(e[1])}`:"Clone repository",checkout:()=>"Switch branches",switch:()=>"Switch branches",restore:()=>"Restore files",reset:()=>"Reset git state",branch:(e,t)=>t.includes("--show-current")?"Check current branch":"List branches",remote:()=>"Check git remotes","rev-parse":()=>"Resolve a git ref",worktree:()=>"Manage git worktrees","merge-base":()=>"Find the merge base","ls-files":()=>"List tracked files"},WI={pr:e=>({create:"Create a pull request",list:"List pull requests",view:"View a pull request",merge:"Merge a pull request",checks:"Check PR status"})[e[1]]||"Run a gh pr command",auth:()=>"Authenticate GitHub CLI",api:()=>"Call the GitHub API",repo:()=>"Inspect the GitHub repo",issue:()=>"Work with GitHub issues"};GI={git:e=>{let t=xe(e,new Set(["-C","-c"])),n=HI[t[0]];return n?n(t,e):null},gh:e=>{let t=xe(e),n=WI[t[0]];return n?n(t):null},sed:qI,rg:gd,grep:gd,egrep:gd,nl:e=>{let t=ze(xe(e)[0]);return t?`Read ${Re(t)}`:null},cat:e=>{let t=ze(xe(e)[0]);return t?`Read ${Re(t)}`:null},head:e=>{let t=ze(xe(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the start of ${Re(t)}`:null},tail:e=>{let t=ze(xe(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the end of ${Re(t)}`:null},wc:e=>{let t=ze(xe(e)[0]);return t?`Count lines in ${Re(t)}`:"Count input lines"},ls:e=>{let t=ze(xe(e)[0]);return t&&t!=="."?`List files in ${Po(t)}`:"List files"},find:e=>ze(e[1])&&!Cg(e[1])&&e[1]!=="."?`Find files in ${Po(e[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:e=>{let t=ze(xe(e)[0]);return t?`Create directory ${Po(t)}`:null},touch:e=>{let t=ze(xe(e)[0]);return t?`Create ${Re(t)}`:null},rm:e=>{let t=ze(xe(e)[0]);return t?`Delete ${Po(t)}`:null},mv:e=>{let t=ze(xe(e)[0]);return t?`Move ${Re(t)}`:null},cp:e=>{let t=ze(xe(e)[0]);return t?`Copy ${Re(t)}`:null},chmod:e=>{let t=ze(xe(e)[1]);return t?`Change permissions on ${Re(t)}`:null},diff:e=>{let t=xe(e);return t.length>=2?`Compare ${Re(t[0])} and ${Re(t[1])}`:null},curl:e=>{let t=Og(e);return t?`Fetch ${Do(t,56)}`:"Fetch a URL"},wget:e=>{let t=Og(e);return t?`Fetch ${Do(t,56)}`:"Fetch a URL"},npm:yd,pnpm:yd,yarn:yd,npx:e=>{let t=xe(e)[0];return t?`Run ${t}`:null},node:e=>bd(e,"Node"),python:e=>bd(e,"Python"),python3:e=>bd(e,"Python"),make:e=>{let t=xe(e)[0];return t?`Run make ${t}`:"Run make"},systemctl:KI,journalctl:e=>{let t=e.findIndex(r=>r==="-u"||r==="--unit"||r==="--user-unit"),n=t!==-1?e[t+1]:null;return n?`Read ${n.replace(/\.service$/,"")} logs`:"Read system logs"},tmux:e=>{let t=xe(e,new Set(["-S","-L","-t","-f"]))[0];return{ls:"List tmux sessions","list-sessions":"List tmux sessions","kill-session":"Kill a tmux session","send-keys":"Send keys to a tmux session","capture-pane":"Capture a tmux pane","new-session":"Start a tmux session"}[t]||null},ps:()=>"List processes",which:e=>{let t=xe(e)[0];return t?`Locate ${t}`:null},command:e=>e[1]==="-v"&&e[2]?`Locate ${e[2]}`:null,fly:e=>{let t=xe(e)[0];return t==="deploy"?"Deploy to Fly":t?`Run fly ${t}`:null}},VI=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"])});function Bo(e){let t=e.split(`
|
|
55
|
+
`).map(n=>n.trim()).find(Boolean)||e;return t.length>88?`${t.slice(0,88).trimEnd()}\u2026`:t}function Yn(e,t=""){let n=t.trim();return n||kd(e)||Bo(e)}function xd(e,t){if(e){if(Array.isArray(e)){for(let n of e)xd(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&Sd.has(e.name)){let n=String(e.input?.command||"").trim();n&&t.push({id:e.id||null,tool:e.name,command:n,description:String(e.input?.description||"").trim()})}for(let n of Object.values(e))xd(n,t)}}}async function Pg(e,{maxBytes:t=1048576,limit:n=400}={}){let r;try{r=await ct(e,t)}catch{return[]}let o=[];for(let s of r.split(`
|
|
56
|
+
`)){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(xd(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:Yn(d.command,d.description),ts:u})}return o.slice(-n)}function Dg(e,t){let n=t.filter(l=>l.role==="assistant"&&(l.text||"").trim()).sort((l,u)=>(l.ts||0)-(u.ts||0)),r=[...e].sort((l,u)=>(l.ts||0)-(u.ts||0)),o=[],s=0,a=0,i=l=>{let u=l.n??a+1;return u>a&&(a=u),u};for(let l of n){let u=r.findIndex((m,h)=>h>=s&&(m.ts||0)>(l.ts||0)),d=u===-1?r.length:u,f=r.slice(s,d);if(s=d,!f.length)continue;let p=f.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 Sd,jo=C(()=>{Pt();Ng();Sd=new Set(["Bash"])});import Bg from"node:fs";import Td from"node:fs/promises";import vd from"node:path";import JI from"node:os";async function jg(e){try{return await Td.realpath(e)}catch{return e}}function XI(e=YI){let t=[],n=r=>{let o;try{o=Bg.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=vd.join(r,s.name);if(s.isDirectory())n(a);else if(s.isFile()&&s.name.startsWith("rollout-")&&s.name.endsWith(".jsonl")){let i=/-([0-9a-f-]{36})\.jsonl$/i.exec(s.name);try{t.push({id:i?i[1]:null,file:a,mtimeMs:Bg.statSync(a).mtimeMs})}catch{}}}};return n(e),t.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function ZI(e,t=1048576){let n;try{n=await Td.open(e,"r")}catch{return""}try{let r=Buffer.alloc(Math.min(t,65536)),o="",s=0;for(;;){let{bytesRead:a}=await n.read(r,0,r.length,s);if(!a)break;o+=r.toString("utf8",0,a);let i=o.indexOf(`
|
|
57
|
+
`);if(i!==-1)return o.slice(0,i);if(s+=a,o.length>=t)break}return o}finally{await n.close()}}async function QI(e){let t=await ZI(e);if(!t)return null;let n;try{n=JSON.parse(t)}catch{return null}return n?.type==="session_meta"&&n?.payload?.cwd||null}async function Br(e,t){if(!e&&!t)return null;let n=XI();if(t){let o=n.find(s=>s.id===t);return o||null}let r=await jg(e);for(let o of n){let s=await QI(o.file);if(s&&(s===e||await jg(s)===r))return o}return null}async function Lg(e,t,{limit:n=80,maxBytes:r=524288}={}){let o=await Br(e,t);if(!o)return[];let s;try{s=await ct(o.file,r)}catch{return[]}let a=[],i=null,c=[],l=!1,u=!1,d=null;for(let f of s.split(`
|
|
58
|
+
`)){let p=f.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 k=m.payload?.type;k==="task_started"?(l=!0,u=!1,d=m.payload?.turn_id||null):k==="task_complete"&&l&&(l=!1,!u&&m.payload?.last_agent_message==null&&a.push({role:"assistant",failed:!0,text:jr,sourceId:m.payload?.turn_id||d||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i}));continue}if(m.type!=="response_item")continue;let h=m.payload;if(!h)continue;if((h.type==="function_call"||h.type==="custom_tool_call")&&h.name){u=!0,Ad.has(h.name)||c.push(h.name);continue}if(h.type!=="message"||h.role!=="assistant")continue;u=!0;let g=tR(h.content),x=c.length?c.slice():null;c=[],!(!g&&!x)&&a.push({role:"assistant",text:g,tools:x||void 0,ts:m.timestamp?Date.parse(m.timestamp):null,model:i})}if(i)for(let f of a)f.model||(f.model=i);return a.slice(-n)}function nR(e){if(typeof e?.cmd=="string"&&e.cmd.trim())return e.cmd.trim();let t=e?.command;return typeof t=="string"&&t.trim()?t.trim():Array.isArray(t)&&t.length?t.length===3&&/^(ba)?sh$/.test(t[0])&&t[1]==="-lc"?String(t[2]).trim():t.map(String).join(" ").trim():null}function rR(e){let t=e[0];if(t!=='"'&&t!=="'"&&t!=="`")return null;let n="";for(let r=1;r<e.length;r++){let o=e[r];if(o==="\\"){let s=e[++r];if(s===void 0)return null;if(s==="n")n+=`
|
|
59
|
+
`;else if(s==="t")n+=" ";else if(s==="r")n+="\r";else if(s==="u")if(e[r+1]==="{"){let a=e.indexOf("}",r+2);if(a===-1)return null;n+=String.fromCodePoint(parseInt(e.slice(r+2,a),16)),r=a}else n+=String.fromCharCode(parseInt(e.slice(r+1,r+5),16)),r+=4;else n+=s}else{if(o===t)return n;if(t==="`"&&o==="$"&&e[r+1]==="{")return null;n+=o}}return null}function oR(e){let t=String(e||""),n=[],r=/exec_command\s*\(/g;for(let s;s=r.exec(t);)n.push(s.index+s[0].length);let o=[];return n.forEach((s,a)=>{let i=t.slice(s,a+1<n.length?n[a+1]:t.length),c=/(?:^|[{,\s])["'`]?cmd["'`]?\s*:\s*/.exec(i);if(!c)return;let l=rR(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function Fg(e,t,{limit:n=400,maxBytes:r=524288}={}){let o=await Br(e,t);if(!o)return[];let s;try{s=await ct(o.file,r)}catch{return[]}let a=[];for(let i of s.split(`
|
|
60
|
+
`)){let c=i.trim();if(!c||c[0]!=="{")continue;let l;try{l=JSON.parse(c)}catch{continue}if(l.type!=="response_item")continue;let u=l.payload;if(!u)continue;let d=[];if(u.type==="function_call"&&ga.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let m=nR(p);m&&(d=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=oR(u.input));if(!d.length)continue;let f=u.call_id||u.id||null;d.forEach((p,m)=>{a.push({id:f?m?`${f}:${m}`:f:null,tool:u.name,command:p,description:"",summary:Yn(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function Ug(e,t){let n=await Br(e,t);if(!n)return null;try{return Math.round((await Td.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var YI,eR,tR,ga,Ad,jr,Lr=C(()=>{Pt();jo();YI=process.env.BOSTRAT_CODEX_SESSIONS||vd.join(process.env.CODEX_HOME||vd.join(JI.homedir(),".codex"),"sessions");eR=new Set(["output_text","text"]),tR=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&typeof t.text=="string"&&eR.has(t.type)).map(t=>t.text).join(`
|
|
61
|
+
`).trim():"";ga=new Set(["exec_command","shell","exec"]),Ad=new Set(["wait","write_stdin"]),jr="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 ya({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&Hg.has(r))return Hg.get(r);let o=Number(t)||0;if(o===401)return"auth";if(o===403)return"entitlement";if(o===429)return"rate_limit";if(o===529)return"overloaded";if(o>=500)return"server";let s=String(n||"").toLowerCase();return/overloaded/.test(s)?"overloaded":/rate limit|too many requests|quota|usage limit/.test(s)?"rate_limit":/billing|subscription|not allowed|disabled .* access|upgrade your plan|no credits/.test(s)?"entitlement":/log ?in|authenticat|expired|invalid.*key|unauthorized/.test(s)?"auth":/server|internal error|5\d\d/.test(s)?"server":"other"}function Wg(e){if(!e||e.role!=="assistant")return null;if(e.apiError){let t=e.apiError;return{cls:t.cls||ya({error:t.code,status:t.status,text:e.text}),code:t.code||null,status:t.status||null,requestId:t.requestId||null}}if(e.failed){let t=String(e.text||"");return{cls:t===jr?"other":ya({text:t}),code:null,status:null,requestId:null}}return null}function qg(e,{codexFailedText:t=null}={}){let n=String(e?.text||"").trim();return n?e?.model==="<synthetic>"?/^No response requested\.$/.test(n)?null:{cls:ya({text:n}),code:null,status:sR(n),requestId:null,text:n}:t&&n===t?{cls:"other",code:null,status:null,requestId:null,text:n}:null:null}function sR(e){let t=/^API Error:\s*(\d{3})\b/.exec(e);return t?Number(t[1]):null}var j2,Hg,Tc=C(()=>{Lr();j2=Object.freeze(["entitlement","auth","rate_limit","overloaded","server","other"]),Hg=new Map([["oauth_org_not_allowed","entitlement"],["permission_error","entitlement"],["billing_error","entitlement"],["authentication_error","auth"],["oauth_token_expired","auth"],["invalid_api_key","auth"],["rate_limit_error","rate_limit"],["overloaded_error","overloaded"],["server_error","server"],["api_error","server"],["internal_server_error","server"]])});function aR(e,t){return!e||!t?null:Nt(e,t)}function iR(e){return typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&t.type==="text"&&typeof t.text=="string").map(t=>t.text).join(`
|
|
62
|
+
`).trim():""}function cR(e){if(!Array.isArray(e))return null;let t=e.filter(n=>n&&n.type==="tool_use").map(n=>n.name).filter(Boolean);return t.length?t:null}function lR(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&t.type==="tool_result")}function Ed(e){let t=[],n=e?.message?.content;if(!Array.isArray(n))return t;for(let r of n)if(r){if(r.type==="image"&&r.source?.type==="base64"&&r.source.data)t.push({mediaType:r.source.media_type||"image/png",data:r.source.data,toolUseId:null});else if(r.type==="tool_result"&&Array.isArray(r.content))for(let o of r.content)o?.type==="image"&&o.source?.type==="base64"&&o.source.data&&t.push({mediaType:o.source.media_type||"image/png",data:o.source.data,toolUseId:r.tool_use_id||null})}return t}function Kg(e,t){if(!e.uuid)return null;let n=Ed(e).map((r,o)=>({uuid:e.uuid,index:o,mediaType:r.mediaType,name:r.toolUseId&&t.get(r.toolUseId)||null}));return n.length?n:null}async function _d(e,{maxBytes:t=524288,limit:n=80}={}){let r;try{r=await ct(e,t)}catch{return[]}let o=[],s=new Map;for(let a of r.split(`
|
|
63
|
+
`)){let i=a.trim();if(!i||i[0]!=="{")continue;let c;try{c=JSON.parse(i)}catch{continue}if(c.type==="attachment"){let h=c.attachment;h?.type==="queued_command"&&h.commandMode==="task-notification"&&typeof h.prompt=="string"&&h.prompt.trim()&&o.push({role:"user",text:h.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 h of l.content){let g=h?.type==="tool_use"?h.input?.file_path||h.input?.path:null;typeof g=="string"&&uR.test(g)&&s.set(h.id,g.split("/").pop())}if(lR(l.content)){let h=Kg(c,s);h&&o.push({role:"assistant",text:"",images:h,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let d=iR(l.content),f=u==="assistant"?cR(l.content):null,p=u==="user"?Kg(c,s):null;if(!d&&!f&&!p)continue;let m=u==="assistant"&&c.isApiErrorMessage?{code:typeof c.error=="string"?c.error:null,status:Number(c.apiErrorStatus)||null,requestId:c.requestId||null,cls:ya({error:c.error,status:c.apiErrorStatus,text:d})}:null;o.push({role:u,text:d,tools:f||void 0,images:p||void 0,ts:c.timestamp?Date.parse(c.timestamp):null,model:l.model||null,apiError:m||void 0})}return o.slice(-n)}async function Gg(e,t,{turns:n=16}={}){let r=aR(e,t);if(!r)return"";let o=await _d(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}:** ${dR(s.text||c,700)} ${i}`.trim()}).join(`
|
|
64
|
+
|
|
65
|
+
`):""}var uR,dR,Ac=C(()=>{Tc();Pt();uR=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;dR=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e});import fR from"node:fs/promises";import{execFile as pR}from"node:child_process";import{promisify as mR}from"node:util";function Yg(e){Rd=typeof e=="function"?e:null}function $d(e){let t=String(e||""),n=t.indexOf("/lib/node_modules/");return n>0?t.slice(0,n):null}function Xg({sessionName:e,from:t,to:n}={}){let r=String(n||"").trim();!r||Vg.has(r)||(Vg.add(r),zg=zg.then(()=>wR({sessionName:e,from:t,to:r})).catch(()=>{}))}async function wR({sessionName:e,from:t,to:n}){await Y({flow:"codex-update",phase:"start",sessionName:e,from:t,to:n}).catch(()=>{});try{let r=(await Id("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await fR.realpath(r),s=$d(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await Id("npm",["install","-g","@openai/codex"],{env:a,timeout:hR});let i=(await Id(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await Y({flow:"codex-update",phase:"done",sessionName:e,from:t,to:n,version:i}).catch(()=>{}),await Jg({sessionName:e,from:t,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await Y({flow:"codex-update",phase:"failed",sessionName:e,from:t,to:n,error:o}).catch(()=>{}),await Jg({sessionName:e,from:t,to:n,status:"failed",error:o})}}async function Jg(e){if(Rd)try{await Rd(e)}catch{}}var Id,hR,Vg,zg,Rd,Ec=C(()=>{Gt();Id=mR(pR),hR=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,Vg=new Set,zg=Promise.resolve(),Rd=null});function yR(e,{provider:t,box:n}){let r=String(e).replace(/\r/g,"").split(`
|
|
66
|
+
`);if(!Zg.test(e))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(gR);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"&&Zg.test(a)?a:"The agent is asking for approval";return{provider:t,box:n,promptId:`${t}:${c}:${i.map(l=>l.label).join("|")}`.slice(0,200),questions:[{question:c,header:"Approval",multiSelect:!1,options:i.map(l=>({label:l.label,description:""}))}]}}function Fr(e){let t=String(e??"").replace(/\r/g,""),n=t.split(`
|
|
67
|
+
`);for(let r=n.length-1;r>=0;r--)if(ty.test(n[r]))return n.slice(r).join(`
|
|
68
|
+
`);return t}function bR(e){let t=String(e).trim();if(!t)return!1;let n=(t.match(/[A-Za-z]/g)||[]).length;return n>=3&&n/t.length>=.5}function kR(e){return/device code/i.test(e)?"works from the device you're reading this on":/sign in with chatgpt/i.test(e)?"opens a browser link on the node itself \u2014 can't finish from another device":""}function xR(e,t=""){let n=Fr(e);if(ba.test(n)||!/Sign in with ChatGPT/.test(n)||!/Press enter to continue/.test(n))return null;let r=n.split(`
|
|
69
|
+
`),o=[],s=-1;for(let l=0;l<r.length;l++){let u=r[l].match(Qg);if(!u)continue;s<0&&(s=l);let d="";for(let f=l+1;f<Math.min(l+3,r.length);f++){let p=r[f].trim();if(p){if(Qg.test(r[f])||/^Press enter/.test(p))break;d=p;break}}o.push({n:Number(u[2]),cursor:!!u[1],label:u[3].trim(),desc:d})}let a=o.sort((l,u)=>l.n-u.n);if(a.length<2||a.some((l,u)=>l.n!==u+1)||!a.some(l=>/Sign in with ChatGPT/.test(l.label)))return null;let i=[];for(let l=0;l<s;l++){let u=r[l].trim();u&&!ty.test(r[l])&&bR(u)&&i.push(u)}let c=i.join(" ").slice(0,400)||"Codex isn't signed in on this box \u2014 choose a sign-in method";return{provider:"codex",box:t,multiSelect:!1,cursorIndex:Math.max(0,a.findIndex(l=>l.cursor)),answerMode:"keys",promptId:`codex:signin:${a.map(l=>l.label).join("|")}`.slice(0,200),questions:[{question:c,header:"Sign in",multiSelect:!1,options:a.map(l=>{let u=kR(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function TR(e,t=""){let n=Fr(e);if(ba.test(n)||!SR.test(n))return null;let r=n.trimEnd().split(`
|
|
70
|
+
`).pop()||"";return vR.test(r)?{provider:"codex",box:t,multiSelect:!1,cursorIndex:0,answerMode:"keys",promptId:"codex:signin-complete",questions:[{question:"Codex successfully signed in and is waiting to open the session.",header:"Signed in",multiSelect:!1,options:[{label:"Continue",description:""}]}]}:null}function ER(e,t=""){let n=Fr(e);if(ba.test(n)||!ey.test(n))return null;let r=n.split(`
|
|
71
|
+
`),o=r.findIndex(i=>ey.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(AR);if(c){s=c[1];break}}if(!s)return null;let a=(n.match(/https:\/\/\S+/)||[])[0]||"https://auth.openai.com/codex/device";return{provider:"codex",box:t,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:`codex:device-code:${s}`,questions:[{question:`Codex is waiting for you to finish signing in: open ${a} and enter code ${s} (expires in 15 minutes). The code goes straight to OpenAI \u2014 bostrat never sees your login. This card clears once OpenAI confirms.`,header:"Sign in",multiSelect:!1,options:[{label:"Cancel sign-in",description:"Go back to the sign-in options"}]}]}}function _R(e,t=""){let n=Fr(e);return ba.test(n)||!/open the following link to authenticate/.test(n)?null:{provider:"codex",box:t,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:"codex:signin-browser",questions:[{question:"Codex started its browser sign-in, which opens on the box itself and can't complete from another device. Go back and choose \u201CSign in with Device Code\u201D instead.",header:"Sign in",multiSelect:!1,options:[{label:"Go back to sign-in options",description:""}]}]}}function IR(e,t=""){let n=Fr(e);return ba.test(n)||!/Paste or type your API key/.test(n)?null:{provider:"codex",box:t,multiSelect:!1,cursorIndex:0,answerMode:"esc",promptId:"codex:signin-apikey",questions:[{question:"Codex is asking for an OpenAI API key. For safety a key can only be typed in the Terminal tab, never through chat. To use your ChatGPT subscription instead, go back and pick a sign-in option.",header:"Sign in",multiSelect:!1,options:[{label:"Go back to sign-in options",description:""}]}]}}function RR(e,t=""){let n=String(e);if(!/Press any key to log in/.test(n)||Od.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
72
|
+
`).map(a=>a.trim()),o=r.findIndex(a=>/Press any key to log in/.test(a)),s=r.slice(0,o<0?r.length:o).filter(a=>a&&!/^Cursor Agent$/i.test(a)&&!/^v?\d{4}\.\d{2}\.\d{2}/.test(a)).join(" ");return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,answerMode:"keys",promptId:"cursor:signin-start",questions:[{question:(s?`${s} `:"")+"Cursor isn't signed in on this box \u2014 start the sign-in to get a login link you can open from any device.",header:"Sign in",multiSelect:!1,options:[{label:"Start sign-in",description:"Shows a login link (and a QR option) \u2014 your login goes to cursor.com, never through bostrat"}]}]}}function OR(e,t=""){let n=String(e);if(!$R.test(n)||Od.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
73
|
+
`),o="",s=r.findIndex(a=>/https:\/\//.test(a));if(s>=0){o=r[s].slice(r[s].indexOf("https://")).trim();for(let a=s+1;a<r.length;a++){let i=r[a].trim();if(!i||!/^[A-Za-z0-9~%&=?/_.-]+$/.test(i))break;o+=i}}return{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,promptId:`cursor:signin-link:${(o.match(/[?&]challenge=([\w-]+)/)||[])[1]?.slice(0,16)||o.slice(-24)||"pending"}`,questions:[{question:o?`Cursor is waiting for you to finish signing in: open ${o} on any device and log in. Your login goes straight to cursor.com \u2014 bostrat never sees it. This card clears once sign-in completes.`:"Cursor is waiting for you to finish signing in \u2014 open the login link shown in the Terminal tab. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function CR(e,t=""){let n=String(e);return!/Scan this QR code to log in/.test(n)||Od.test(n)?null:{provider:"cursor",box:t,multiSelect:!1,cursorIndex:0,promptId:"cursor:signin-qr",questions:[{question:"Cursor is showing a QR sign-in code in the Terminal tab \u2014 scan it from another device to log in. This card clears once sign-in completes.",header:"Sign in",multiSelect:!1,options:[]}]}}function ny(e,t,n=""){if(e!=="cursor"&&e!=="codex")return null;if(e==="codex"){let r=TR(t,n)||ER(t,n)||_R(t,n)||IR(t,n)||xR(t,n);if(r)return r}if(e==="cursor"){let r=OR(t,n)||CR(t,n)||RR(t,n);if(r)return r}return yR(t,{provider:e,box:n})}function ry(e,t){let n=Array.isArray(t?.[0])?t[0]:[],o=(Number.isInteger(n[0])&&n[0]>=0?n[0]:0)+1;return String(o)}var gR,Zg,ba,ty,Qg,SR,vR,ey,AR,Od,$R,_c=C(()=>{gR=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,Zg=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;ba=/(^|\n)\s*›/,ty=/^\s*Welcome to Codex\b/;Qg=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;SR=/Successfully logged in\./,vR=/^\s*Press Enter to continue\.\s*$/;ey=/Enter this one-time code/,AR=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;Od=/Run Everything|Add a follow-up|Plan, search, build anything/;$R=/click this link to log in|Signing in with the browser/});function DR(e){let t=String(e||"").replace(/\r/g,"").split(`
|
|
74
|
+
`),n=-1;for(let s=t.length-1;s>=0;s--)if(t[s].trim()){n=s;break}if(n<0)return!1;let r=t[n];if(NR.test(r)||PR.test(r)){for(let s=n-1;s>=0&&n-s<=64;s--)if(/^\s*›/.test(t[s]))return!0}return/esc to interrupt|\bWorking \(/.test(String(e||""))}function UR(e){let t=String(e||"");if(!Ne.codex.block.test(t))return null;let n=Fr(t);if(!Ne.codex.block.test(n))return null;let r=n.match(FR);return{from:r?.[1]||null,to:r?.[2]||null}}function Vt(e){return new Promise(t=>setTimeout(t,e))}function Cd(e,t){let n=Date.now(),r=HR();for(let[s,a]of Rc)n-a>=r&&Rc.delete(s);let o=`${e}::${t}`;return Rc.has(o)?!1:(Rc.set(o,n),!0)}function iy(e,t){if(e?.busyScope!=="footer")return t;let n=String(t||"").split(`
|
|
75
|
+
`),r=-1;for(let o=n.length-1;o>=0;o--)if(ay.test(n[o])){r=o;break}return r<0?t:n.slice(r+1).join(`
|
|
76
|
+
`)}function qR(e){let t=0;for(let n of String(e).split(`
|
|
77
|
+
`))ay.test(n)&&(t=Math.max(t,n.trimEnd().length));return t}function Md(e,t){let n=Ne[e];if(!n?.busy)return{state:"idle",reason:null};if(Pd(e,t))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(t))return{state:"idle",reason:null};if(n.busy.test(iy(n,t)))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(t))return{state:"busy",reason:null};let r=qR(t);return e==="claude"&&r>0&&r<WR?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function Nd(e,t){return Md(e,t).state}function cy(e,t){if(e!=="claude")return!1;let n=String(t||"").split(`
|
|
78
|
+
`).slice(-GR).join(`
|
|
79
|
+
`);return KR.test(n)}function Fo(e,t){return Nd(e,t)==="busy"}function $c(e,t){return Nd(e,t)!=="idle"}function Uo(e,t){let n=Ne[e];return n?.up?n.up.test(t):!0}function VR(e,t){let n=Ne[e];return n?Pd(e,t)?!1:n.up.test(t)&&Nd(e,t)==="idle":!0}function Pd(e,t){let n=Ne[e];return n?n.auth?.test(t)&&!n.up.test(t)?!0:n.block?.test(t)?e==="codex"?!0:!n.up.test(t):!1:!1}function Dd(e,t){let n=String(t||"").trim();if(!n)return e;let r=String(e||"").split(`
|
|
77
80
|
`);for(let o=r.length-1;o>=0;o--)if(r[o].includes(n))return r.slice(o+1).join(`
|
|
78
|
-
`);return e}function
|
|
79
|
-
`)?!0:Buffer.byteLength(e,"utf8")>=
|
|
80
|
-
`)
|
|
81
|
-
|
|
82
|
-
`)
|
|
81
|
+
`);return e}function ly(e){let t=String(e||"").trim();return t?t.slice(0,32):null}function oy(e){let t=new Error(`${e} isn't running in this box \u2014 its pane is sitting at a bare shell (the launch command was likely swallowed at shell startup, or the CLI exited). Relaunch it from the Terminal tab, or stop and reopen the box, then send again.`);return t.code="cli-not-running",t}async function sy(e,t,n,{bailOnDialog:r=!1,launchEcho:o=null}={}){let s=Date.now()+n,a=0,i=null;for(await Dr(e).catch(()=>{});;){let c=Dd(await Te(e),o);if(VR(t,c)){await Vt(Lo);return}if(Ne[t]?.up&&!Ne[t].up.test(c))if(await fn(e)){if(i??=Date.now(),Date.now()-i>=fa()){let l=oy(t);throw Y({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:a,idleMs:Date.now()-i,error:l.message}).catch(()=>{}),l}}else i=null;else i=null;if(r&&t==="claude"&&LR.test(c)&&!Ne.claude.up.test(c)){Y({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"resume-confirm"}).catch(()=>{});return}if(t==="codex"&&jR.test(c)&&!Ne.codex.up.test(c)&&a<3){if(!Cd(e,"codex-trust")){await Vt(Ic);continue}a+=1,await Y({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"codex-trust",attempt:a}).catch(()=>{}),await Le(e,"",{enter:!0,kind:"codex-trust-accept"}),await Vt(Lo);continue}if(t==="cursor"&&BR.test(c)&&!Ne.cursor.up.test(c)&&a<3){if(!Cd(e,"cursor-trust")){await Vt(Ic);continue}a+=1,await Y({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:a}).catch(()=>{}),await Le(e,"a",{enter:!1,kind:"cursor-trust-accept"}),await Vt(Lo);continue}if(t==="codex"&&a<3){let l=UR(c);if(l){if(!Cd(e,`codex-update:${l.from}->${l.to}`)){await Vt(Ic);continue}a+=1,await Y({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:a,update:`${l.from} -> ${l.to}`}).catch(()=>{}),await Le(e,"3",{enter:!1,kind:"codex-update-skip"}),Xg({sessionName:e,from:l.from,to:l.to}),await Vt(Lo);continue}}if(Date.now()>s){if(Pd(t,c)){let l=`${t} is blocked on an interactive prompt`;throw t==="codex"?l=Ne.codex.auth.test(c)?"codex isn't signed in on this box \u2014 answer the Sign in card in chat (Device Code works from any device) or finish signing in from the Terminal tab, then send again":"codex is stuck on its update prompt (auto-skip didn't clear it) \u2014 open the terminal and choose Skip, then send again":t==="cursor"&&Ne.cursor.auth.test(c)?l="cursor isn't signed in on this box \u2014 answer the Sign in card in chat (the login link works from any device) or finish signing in from the Terminal tab, then send again":t==="claude"&&Ne.claude.auth.test(c)?l="claude isn't signed in on this box \u2014 answer the setup cards in chat (or finish signing in from the Terminal tab), then send again":t==="claude"&&Ne.claude.block.test(c)&&(l='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'),Y({flow:"agent-ready",phase:"blocked",sessionName:e,agent:t,timeoutMs:n,gateAccepts:a,error:l}).catch(()=>{}),new Error(l)}if(await fn(e)){let l=oy(t);throw Y({flow:"agent-ready",phase:"blocked",reason:"cli-not-running",sessionName:e,agent:t,timeoutMs:n,gateAccepts:a,error:l.message}).catch(()=>{}),l}console.warn(`[agent-ready] ${t} readiness unconfirmed in ${e} after ${n}ms \u2014 driving the box anyway.`),Y({flow:"agent-ready",phase:"timeout",sessionName:e,agent:t,timeoutMs:n,gateAccepts:a,paneBlocked:!!Ne[t]?.block?.test(c),paneUp:!!Ne[t]?.up?.test(c),paneBusy:!!Ne[t]?.busy?.test(iy(Ne[t],c))}).catch(()=>{}),await Vt(Lo);return}await Vt(Ic)}}function Bd({agent:e,agentCmd:t}={}){let n=t?Vn(t):Vn(e||"");return{label:n,realClaude:t?Pr(t):n==="claude",realCodex:t?/codex/.test(t)&&!/fake-codex|fake-lead/.test(t):n==="codex",realCursor:t?/cursor/.test(t)&&!/fake-cursor|fake-box/.test(t):n==="cursor"}}function uy({agent:e,agentCmd:t}={}){let{realClaude:n,realCodex:r,realCursor:o}=Bd({agent:e,agentCmd:t});return n||r||o||process.env.BOSTRAT_FAKE_POLL_READY==="1"}function ka({cwd:e,sessionName:t,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Bd({agent:n,agentCmd:r});return!t||!o&&!s?null:Xn({cwd:e,sessionName:t,agent:n,agentCmd:r}).catch(()=>{})}async function Xn({cwd:e,sessionId:t,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=MR(),bailOnDialog:a=!1,launchEcho:i=null}={}){let{label:c,realClaude:l,realCodex:u,realCursor:d}=Bd({agent:r,agentCmd:o});if(l&&e&&await Fn(e),u&&e&&await ko(e),d&&e&&await xo(e),(l||u||d)&&n){await sy(n,c,s,{bailOnDialog:a,launchEcho:i});return}let f=Ne[r]?r:Ne[c]?c:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&f){await sy(n,f,s,{launchEcho:i});return}await Vt(Lo)}var MR,Ic,Lo,NR,PR,Ne,BR,jR,LR,FR,HR,Rc,WR,ay,KR,GR,xa=C(()=>{Ve();Js();Yi();Xi();Ec();_c();Gt();MR=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Ic=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,Lo=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,NR=/^\s{0,4}[^\s›•■◦╭╰│]\S*(?: \S+){0,3} · \S/,PR=/^\s*tab to queue message\b/;Ne={claude:{up:/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/,busy:/esc to interrupt/,busyScope:"footer",busyAlt:/^[·✢✳✶✻✽✺✹✸✷]\s+[A-Z][\w '’-]{1,40}…/m,auth:/Select login method:|Paste code here if prompted|OAuth error:|Choose the text style that looks best|Quick safety check: Is this a project you created/,block:/Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/m},codex:{up:{test:DR},busy:/esc to interrupt|\bWorking \(/,block:/Update available![\s\S]*\b(?:Update now|Skip until next version)\b[\s\S]*Press enter to continue/,auth:/Sign in with ChatGPT[\s\S]*Press enter to continue|Finish signing in via your browser|Paste or type your API key|Successfully logged in\.[\s\S]*Press Enter to continue\./},cursor:{up:/Run Everything|Add a follow-up|Plan, search, build anything/,busy:/ctrl\+c to stop|^\s*[⠁-⣿]+\s+\S/m,block:/Workspace Trust Required|Trust this workspace|\[a\]\s*Trust|Trusting workspace/,auth:/Press any key to log in|click this link to log in|Scan this QR code to log in|Signing in with the browser/}},BR=/Trust this workspace|\[a\]\s*Trust/,jR=/Do you trust the contents of this directory\?[\s\S]*›\s*1\. Yes, continue[\s\S]*Press enter to continue/,LR=/Resume from summary/i,FR=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;HR=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,Rc=new Map;WR=68,ay=/^\s*[─━]{10,}\s*$/;KR=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,GR=15});function XR(e){return typeof e!="string"||!e?!1:e.includes(`
|
|
82
|
+
`)?!0:Buffer.byteLength(e,"utf8")>=YR}function zt(e,t){return e==="codex"?{enter:!0,submitDelayMs:zR,...XR(t)?{paste:!0}:{}}:e==="claude"?{enter:!0,submitDelayMs:JR}:{enter:!0}}var zR,JR,YR,Sa=C(()=>{zR=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),JR=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),YR=(()=>{let e=Number(process.env.BOSTRAT_CODEX_PASTE_MIN_BYTES??512);return Number.isFinite(e)&&e>=0?e:512})()});function va(e,t){if(e==="codex")return n$(t);if(e!=="claude")return null;let n=String(t).replace(/\r/g,"").split(`
|
|
83
|
+
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(dy.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(dy.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(ZR);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 n$(e){let t=String(e).replace(/\r/g,"").split(`
|
|
84
|
+
`).map(o=>o.replace(/\s+$/,"")),n=-1;for(let o=t.length-1;o>=0;o--)if(t[o]){(QR.test(t[o])||e$.test(t[o]))&&!jd.test(t[o])?n=o:jd.test(t[o])&&/\[Pasted Content \d+ chars\]/.test(t[o])&&(n=o+1);break}if(n<=0)return null;let r=[];for(let o=n-1;o>=0&&n-o<=60;o--){let s=t[o],a=s.match(jd);if(a){r.unshift(a[1]);let i=r.map(c=>c.trim()).filter(Boolean);return i.length?i.join(" "):null}if(t$.test(s))return null;r.unshift(s)}return null}function Ld(e,t){let n=String(t||"");if(!/\[Pasted Content \d+ chars\]/.test(n))return!1;let r=0,o=Oe(n.replace(r$,(i,c)=>(r+=Number(c)," "))),s=[...String(e||"")].length;return o&&!Oe(e).includes(o)?!1:r+o.length<=s+Math.max(4,Math.round(s*.05))}function Oe(e){return String(e||"").replace(/\s+/g," ").trim()}function fy(e,t,{slackMs:n=2e4}={}){let r=Oe(t?.text);return!r||typeof t?.at!="number"?!1:(Array.isArray(e)?e:[]).some(o=>o?.role==="user"&&typeof o.ts=="number"&&o.ts>=t.at-n&&Oe(o.text).includes(r))}function py(e,t,n,{busy:r=!1}={}){let o=va(e,n);return o&&Oc(t,o)||o&&e==="codex"&&Ld(t,o)?"stuck":r&&e!=="codex"||o?"submitted":"unknown"}function Oc(e,t){let n=Oe(e),r=Oe(t).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var dy,ZR,jd,QR,e$,t$,r$,Cc=C(()=>{dy=/^\s*─{8,}\s*$/,ZR=/^❯[ ]?(.*)$/,jd=/^\s*›[ ]?(.*)$/,QR=/\S+ · \S/,e$=/^\s*(?:\? for shortcuts|tab to queue message)\b/,t$=/^\s*(?:[•■◦✶✻⚠]|╭|╰|│)/;r$=/\[Pasted Content (\d+) chars\](?: #\d+)?/g});async function Aa({sessionName:e,agent:t,agentCmd:n=null,text:r,trackId:o=null,flow:s="say"},a={}){if(!o$.has(t))return!0;let i=a.waitForUp??uy({agent:t,agentCmd:n}),c=a.capture||(I=>Te(I,{lines:je})),l=a.pressEnter||(I=>Ie(I,["Enter"],{kind:"submit-retry"})),u=a.sleep||c$,d=a.now||Date.now,f=a.log||(I=>Y(I).catch(()=>{})),p=a.maxMs??i$(),m=d()+p,h={flow:s,trackId:o,sessionName:e,agent:t,textHead:String(r).slice(0,120)},g=0,x=0,k=0,A=!1;for(await u(Ta[0]);;){let I=await c(e).catch(()=>""),M=Uo(t,I),N=Fo(t,I),O=py(t,r,I,{busy:N});if(O==="submitted")return!0;if(i&&(!M||O==="stuck"&&N)){if(d()>=m)return O==="stuck"?(f({...h,phase:"submit-failed",reason:"stuck-at-deadline",retries:g}),!1):(f({...h,phase:"submit-unverified",reason:A?"cli-down":"cli-never-up",retries:g,waitedMs:p}),!0);await u(s$);continue}if(A=!0,O==="stuck"){if(g>=a$)return f({...h,phase:"submit-failed",retries:g}),!1;g+=1,x=0,k=0,await l(e),f({...h,phase:"submit-retry",attempt:g}),await u(Ta[0]);continue}if(x+=1,x>=Ta.length||d()>=m)return!0;k+=1,await u(Ta[Math.min(k,Ta.length-1)])}}var o$,Ta,s$,a$,i$,c$,Fd=C(()=>{Ve();xa();Cc();Gt();o$=new Set(["claude","codex"]),Ta=[250,450,700],s$=500,a$=2,i$=()=>{let e=Number(process.env.BOSTRAT_SUBMIT_VERIFY_MAX_MS);return Number.isFinite(e)&&e>0?e:3e4},c$=e=>new Promise(t=>setTimeout(t,e))});function Ur(e){let t=String(e||"").toLowerCase();if(!t.trim())return null;let n=null;for(let r of l$){let o=0;for(let s of u$[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function my(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))d$.has(r)&&(t+=o||0),f$.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function Ea(e){return String(e||"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/[-_/]+/g," ")}var l$,u$,d$,f$,Ud=C(()=>{l$=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],u$={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/]};d$=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),f$=new Set(["Edit","Write","MultiEdit","NotebookEdit"])});function yy(e){let t=Ea(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||p$.has(r)||n.push(r);return n}function m$(e){let t=[];for(let n of e||[]){let r=n.payload||{},o=String(r.text||"").trim();if(n.type==="turn.user"){if(n.source==="hook"||!o)continue;t.push(gy(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(gy(n,"assistant",o))}}return t}function gy(e,t,n){let r=Ur(n);return{id:e.id,seq:e.seq,ts:e.payload?.ts??e.ts,role:t,boxId:e.boxId??null,provider:e.provider??null,text:n,tags:r?[r.category]:[]}}function _a(e){let t=m$(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=yy(a.text);r[i]=c.length,o+=c.length;let l=new Map;for(let u of c)l.set(u,(l.get(u)||0)+1);for(let[u,d]of l){let f=n.get(u);f||n.set(u,f=new Map),f.set(i,d)}});let s=t.length;return{chunks:t,postings:n,lengths:r,N:s,avgdl:s?o/s:0}}function Mc(e,t,n={}){let{k:r=8,role:o=null,tag:s=null,boxId:a=null}=n;if(!e||!e.N)return[];let i=[...new Set(yy(t))];if(!i.length)return[];let c=new Map;for(let u of i){let d=e.postings.get(u);if(!d)continue;let f=d.size,p=Math.log(1+(e.N-f+.5)/(f+.5));for(let[m,h]of d){let g=e.lengths[m],x=h+hy*(1-wy+wy*(g/(e.avgdl||1))),k=p*(h*(hy+1)/x);c.set(m,(c.get(m)||0)+k)}}let l=[];for(let[u,d]of c){let f=e.chunks[u];o&&f.role!==o||s&&!f.tags.includes(s)||a&&f.boxId!==a||l.push({chunk:f,score:d})}return l.sort((u,d)=>d.score-u.score||d.chunk.seq-u.chunk.seq),l.slice(0,r)}var hy,wy,p$,Hd=C(()=>{Ud();hy=1.5,wy=.75,p$=new Set("a an and are as at be been but by for from had has have he her his i if in into is it its of on or our so than that the their them then there these they this those to was were will with would you your we".split(" "))});import Nc from"node:fs/promises";import by from"node:path";async function h$(e){let t=by.join(e,".bostrat","events"),n;try{n=await Nc.readdir(t)}catch{return _a([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await Nc.readFile(by.join(t,o),"utf8");for(let a of s.split(`
|
|
85
|
+
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return _a(r)}function w$(e){let t=String(e||""),n=a=>{let i=t.match(a);return i?i[1].trim():""},r=n(/#+\s*Objective\s*\n([\s\S]*?)(?:\n#+\s|$)/i),o=n(/#+\s*Next steps?[^\n]*\n([\s\S]*?)(?:\n#+\s|$)/i);return`${r}
|
|
86
|
+
${o}`.trim()||t.slice(0,2e3)}function g$(e,t,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=Mc(e,t,{k:n*2+4}),a=[],i=0;for(let{chunk:c}of s){if(o&&o(c))continue;let l=c.role==="user"?"Operator":`Agent${c.provider?` (${c.provider})`:""}`,u=c.text.trim(),d=u.length>800?u.slice(0,800)+"\u2026":u,f=`- **${l}:** ${d}`;if(i+f.length>r||(a.push(f),i+=f.length,a.length>=n))break}return a.length?["",`## ${ky}, 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(`
|
|
87
|
+
`):""}async function xy(e,t,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await Nc.readFile(t,"utf8");if(a.includes(ky))return{appended:!1,reason:"already-enriched"};let i=await h$(e);if(!i.N)return{appended:!1,reason:"empty-index"};let c=g$(i,w$(a),{k:r,maxChars:o,exclude:s});return c?(await Nc.appendFile(t,`
|
|
83
88
|
`+c),{appended:!0,chars:c.length,chunks:c.split(`
|
|
84
|
-
- `).length-1,indexed:i.N}):{appended:!1,reason:"no-hits",indexed:i.N}}catch(a){return{appended:!1,reason:"error",error:a?.message||String(a)}}}var
|
|
85
|
-
`).map(
|
|
86
|
-
`).map(o=>o.replace(/\s+$/,"")),n=-1;for(let o=t.length-1;o>=0;o--)if(t[o]){WR.test(t[o])&&!iy.test(t[o])&&(n=o);break}if(n<=0)return null;let r=[];for(let o=n-1;o>=0&&n-o<=60;o--){let s=t[o],a=s.match(iy);if(a){r.unshift(a[1]);let i=r.map(c=>c.trim()).filter(Boolean);return i.length?i.join(" "):null}if(qR.test(s))return null;r.unshift(s)}return null}function Od(e,t){let n=String(t||"");if(!/\[Pasted Content \d+ chars\]/.test(n))return!1;let r=0,o=Oe(n.replace(GR,(i,c)=>(r+=Number(c)," "))),s=[...String(e||"")].length;return o&&!Oe(e).includes(o)?!1:r+o.length<=s+Math.max(4,Math.round(s*.05))}function Oe(e){return String(e||"").replace(/\s+/g," ").trim()}function cy(e,t,{slackMs:n=2e4}={}){let r=Oe(t?.text);return!r||typeof t?.at!="number"?!1:(Array.isArray(e)?e:[]).some(o=>o?.role==="user"&&typeof o.ts=="number"&&o.ts>=t.at-n&&Oe(o.text).includes(r))}function ly(e,t,n,{busy:r=!1}={}){let o=ya(e,n);return o&&_c(t,o)||o&&e==="codex"&&Od(t,o)?"stuck":r||o?"submitted":"unknown"}function _c(e,t){let n=Oe(e),r=Oe(t).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var ay,HR,iy,WR,qR,GR,Cd=O(()=>{ay=/^\s*─{8,}\s*$/,HR=/^❯[ ]?(.*)$/,iy=/^\s*›[ ]?(.*)$/,WR=/\S+ · \S/,qR=/^\s*(?:[•■◦✶✻⚠]|╭|╰|│)/;GR=/\[Pasted Content (\d+) chars\](?: #\d+)?/g});function ZR(e){return VR.test(String(e||"").trimStart())}function fy(e){let t=String(e||"");if(!ZR(t.trimStart()))return null;let n=t.replace(zR,"").trim(),r=QR(n),o=[];for(let u of YR){let f=u.from.map(d=>Do(r.get(d))).find(d=>d&&!uy(d));f&&o.push({h:u.h,text:ba(f,JR)})}let s=Do(r.get("Current Work"))||Do(r.get("Primary Request and Intent"))||t$(n),a=Rc(s),i=ba(Ic(Pd(a[0]||"")),XR)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(ba(Ic(Pd(l)),Nd));for(let u of["Pending Tasks","Optional Next Step"]){let f=Rc(Do(r.get(u)))[0]||"";if(!(!f||uy(f))){c.push(ba(`Left to do: ${Ic(Pd(f))}`,Nd));break}}if(!c.length){let u=Rc(Do(r.get("Problem Solving"))||"")[0];u&&c.push(ba(Ic(u),Nd))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function QR(e){let t=new Map,n=[];Md.lastIndex=0;for(let r=Md.exec(e);r;r=Md.exec(e))n.push({name:e$(r[1]),start:r.index,end:r.index+r[0].length});for(let r=0;r<n.length;r++){let o=n[r+1]?.start??e.length;t.has(n[r].name)||t.set(n[r].name,e.slice(n[r].end,o).trim())}return t}function e$(e){return dy.find(n=>n.toLowerCase()===e.toLowerCase())||e}function Do(e){return e?String(e).replace(/```[\s\S]*?(?:```|$)/g," ").split(`
|
|
87
|
-
`).map(t=>t.replace(/^[ \t]*(?:[-*•]|\d+[.)]|[a-z][.)])[ \t]+/i,"").trim()).filter(t=>t&&!/^#{1,6}[ \t]/.test(t)).join(" ").replace(/\*\*(.+?)\*\*/g,"$1").replace(/`([^`]+)`/g,"$1").replace(/\s+/g," ").trim():""}function t$(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return Do(t.split(/\n{2,}/).slice(0,2).join(" "))}function Rc(e){let t=String(e||"").trim();if(!t)return[];let r=t.replace(/\b([A-Za-z0-9_-]+)\.(js|ts|tsx|mjs|json|md|css|jsonl|sh|yml|yaml|py|html)\b/g,"$1<DOT>$2").replace(/\b(e\.g|i\.e|etc|vs|approx|no|fig|Dr|Mr|Ms|St)\./gi,"$1<DOT>").split(/(?<=[.!?])["')\]]?\s+(?=[A-Z"“'(\[]|\d)/).map(s=>s.replace(/<DOT>/g,".").trim()).filter(Boolean),o=[];for(let s of r){let a=o[o.length-1];a&&(a.match(/["“”]/g)?.length??0)%2===1?o[o.length-1]=`${a} ${s}`:o.push(s)}return o}function Pd(e){return String(e||"").replace(/^(?:Immediately before (?:this|the) summary(?: request)?[,:]?\s*|At the (?:time|point) of (?:this )?compaction[,:]?\s*|Currently[,:]?\s*|The (?:most recent|immediately preceding|current) work (?:was|is)\s*)/i,"").replace(/^(?:I(?:'m|'d| am| was| had| have)?\s+(?:currently\s+|just\s+)?(?:been\s+)?(?:working on|in the middle of|completing|finishing)\s+|The user(?:'s)?\s+(?:most recent\s+)?(?:request|ask) (?:was|is)\s+(?:to\s+)?)/i,"").replace(/^I(?:'m|'d| am| was| had| have)?\s+(?:just\s+|then\s+)?(?=(?:[a-z]+(?:ed|ing)|ran|wrote|built|made|began|found|left|sent|took|went|did|got|kept|held|brought|caught|chose|came|saw|said|gave|told|set|put|read)\b)/,"").replace(/^["“]|["”]$/g,"").replace(/^(\w)/,t=>t.toUpperCase()).trim()}function Ic(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function uy(e){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(e||"").trim())}function ba(e,t){let n=String(e||"").trim();if(n.length<=t)return n;let r=Rc(n),o="";for(let i of r){let c=o?`${o} ${i}`:i;if(c.length>t)break;o=c}if(o.length>=Math.min(60,t*.4))return o;let s=n.slice(0,t),a=s.lastIndexOf(" ");return`${(a>t*.6?s.slice(0,a):s).replace(/[,;:.\s]+$/,"")}\u2026`}var VR,dy,Md,zR,JR,YR,XR,Nd,py=O(()=>{VR=/^This session is being continued from a previous conversation that ran out of context\./,dy=["Primary Request and Intent","Key Technical Concepts","Files and Code Sections","Errors and Fixes","Problem Solving","All user messages","Pending Tasks","Current Work","Optional Next Step"],Md=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${dy.join("|")})[:*\t ]*$`,"gim"),zR=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,JR=420,YR=[{h:"What we were doing",from:["Primary Request and Intent"]},{h:"Key decisions",from:["Key Technical Concepts"]},{h:"Where it got to",from:["Current Work","Problem Solving"]},{h:"Left to do",from:["Pending Tasks","Optional Next Step"]}],XR=116,Nd=168});function hy(e){return Dd.has(String(e||"").replace(/^\//,"").toLowerCase())}function $c(e){let t=String(e||"").trim();if(!t)return null;let n=fy(t);if(n)return n;let r=t.match(n$);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(r$);if(o)return{kind:"skill",name:o[1]};let s=t.match(o$);if(s){let i=s[1].toLowerCase();return Dd.has(i)?{kind:"suppress"}:{kind:"command",command:`/${i}`}}let a=t.match(m$);if(a&&Dd.has(a[1].toLowerCase()))return{kind:"suppress"};if(s$.test(t))return{kind:"suppress"};if(i$.test(t))return{kind:"suppress"};if(a$.test(t))return{kind:"suppress"};if(c$.test(t)){let i=l=>h$(t.match(l)?.[1].trim()||"")||null,c=i(p$);return{kind:"task",summary:i(l$)||"",taskId:i(u$),toolUseId:i(d$),status:i(f$)?.toLowerCase()||null,resultHead:c?c.length>my?`${c.slice(0,my).trimEnd()}\u2026`:c:null}}return null}function h$(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function wy(e){return e?.kind==="skill"?`Ran the ${e.name} skill`:e?.kind==="command"?`Ran ${e.command}`:e?.kind==="task"?e.summary||"A background task finished":e?.kind==="resume-summary"?e.title?`Context resumed from summary: ${e.title}`:"Context resumed from summary":""}function gy(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var n$,r$,o$,s$,a$,i$,c$,l$,u$,d$,f$,p$,my,Dd,m$,Bd=O(()=>{py();n$=/^Base directory for this skill:\s*(\S+)/,r$=/^Run the "([^"]+)" workflow\./,o$=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,s$=/^<local-command-(?:stdout|caveat)>/i,a$=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,i$=/^Continue from where you left off\.$/,c$=/^<task-notification>/i,l$=/<summary>([\s\S]*?)<\/summary>/i,u$=/<task-id>([\s\S]*?)<\/task-id>/i,d$=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,f$=/<status>([\s\S]*?)<\/status>/i,p$=/<result>([\s\S]*?)(?:<\/result>|$)/i,my=600,Dd=new Set(["model","clear","compact","login","exit","quit"]);m$=/^\/([a-z0-9:_-]+)$/i});import w$ from"node:crypto";function k$(e){let t=Array.isArray(e)?e:Array.isArray(e?.questions)?e.questions:null;if(!t||!t.length)return null;let n=[];for(let r of t.slice(0,by)){let o=ka(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,g$)){let c=ka(i?.label);if(!c)return null;let l=ka(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<y$)return null;n.push({question:o,header:ka(r?.header).slice(0,b$),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function Ld(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;yy.lastIndex=0;let t=null;for(let o;o=yy.exec(e);)t=o;if(!t||t.index+t[0].length<e.trimEnd().length)return null;let n;try{n=JSON.parse(t[2])}catch{return null}let r=k$(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function Fd(e,t,n){let r=w$.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${e}:ask:${t||"turn"}:${r}`}function Ud(e,{leadBoxId:t,provider:n,isAnswered:r,maxQuestions:o=by}={}){if(!Array.isArray(e)||!t||!jd.has(n))return null;let s=null;for(let l=e.length-1;l>=0;l--){let u=e[l];if(u?.kind==="prompt")return null;if(u?.kind==="message"){if(u.role!=="assistant")return null;s=u;break}}if(!s||s.box!==t||s.agent!==n)return null;let a=s.ask?.questions||Ld(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=Fd(n,s.sourceId||s.id,a);return typeof r=="function"&&r(c)?null:{provider:n,box:t,promptId:c,groupId:c,answerMode:"say",source:"transcript",multiSelect:i[0].multiSelect,questionIndex:0,questionTotal:i.length,questions:i}}function ky(e,t,n){let r=[];(e?.questions||[]).forEach((a,i)=>{let c=(t?.[i]||[]).map(l=>a.options?.[l]?.label).filter(Boolean);c.length&&r.push({name:a.header||a.question,picks:c.join(", ")})});let o=ka(n),s;return r.length?r.length===1?s=`${r[0].name}: ${r[0].picks}`:s=`Answers:
|
|
89
|
+
- `).length-1,indexed:i.N}):{appended:!1,reason:"no-hits",indexed:i.N}}catch(a){return{appended:!1,reason:"error",error:a?.message||String(a)}}}var ky,Sy=C(()=>{Hd();ky="Relevant prior discussion (verbatim, retrieved"});function v$(e){return y$.test(String(e||"").trimStart())}function Ay(e){let t=String(e||"");if(!v$(t.trimStart()))return null;let n=t.replace(b$,"").trim(),r=T$(n),o=[];for(let u of x$){let d=u.from.map(f=>Ho(r.get(f))).find(f=>f&&!vy(f));d&&o.push({h:u.h,text:Ia(d,k$)})}let s=Ho(r.get("Current Work"))||Ho(r.get("Primary Request and Intent"))||E$(n),a=Dc(s),i=Ia(Pc(Kd(a[0]||"")),S$)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(Ia(Pc(Kd(l)),qd));for(let u of["Pending Tasks","Optional Next Step"]){let d=Dc(Ho(r.get(u)))[0]||"";if(!(!d||vy(d))){c.push(Ia(`Left to do: ${Pc(Kd(d))}`,qd));break}}if(!c.length){let u=Dc(Ho(r.get("Problem Solving"))||"")[0];u&&c.push(Ia(Pc(u),qd))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function T$(e){let t=new Map,n=[];Wd.lastIndex=0;for(let r=Wd.exec(e);r;r=Wd.exec(e))n.push({name:A$(r[1]),start:r.index,end:r.index+r[0].length});for(let r=0;r<n.length;r++){let o=n[r+1]?.start??e.length;t.has(n[r].name)||t.set(n[r].name,e.slice(n[r].end,o).trim())}return t}function A$(e){return Ty.find(n=>n.toLowerCase()===e.toLowerCase())||e}function Ho(e){return e?String(e).replace(/```[\s\S]*?(?:```|$)/g," ").split(`
|
|
90
|
+
`).map(t=>t.replace(/^[ \t]*(?:[-*•]|\d+[.)]|[a-z][.)])[ \t]+/i,"").trim()).filter(t=>t&&!/^#{1,6}[ \t]/.test(t)).join(" ").replace(/\*\*(.+?)\*\*/g,"$1").replace(/`([^`]+)`/g,"$1").replace(/\s+/g," ").trim():""}function E$(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return Ho(t.split(/\n{2,}/).slice(0,2).join(" "))}function Dc(e){let t=String(e||"").trim();if(!t)return[];let r=t.replace(/\b([A-Za-z0-9_-]+)\.(js|ts|tsx|mjs|json|md|css|jsonl|sh|yml|yaml|py|html)\b/g,"$1<DOT>$2").replace(/\b(e\.g|i\.e|etc|vs|approx|no|fig|Dr|Mr|Ms|St)\./gi,"$1<DOT>").split(/(?<=[.!?])["')\]]?\s+(?=[A-Z"“'(\[]|\d)/).map(s=>s.replace(/<DOT>/g,".").trim()).filter(Boolean),o=[];for(let s of r){let a=o[o.length-1];a&&(a.match(/["“”]/g)?.length??0)%2===1?o[o.length-1]=`${a} ${s}`:o.push(s)}return o}function Kd(e){return String(e||"").replace(/^(?:Immediately before (?:this|the) summary(?: request)?[,:]?\s*|At the (?:time|point) of (?:this )?compaction[,:]?\s*|Currently[,:]?\s*|The (?:most recent|immediately preceding|current) work (?:was|is)\s*)/i,"").replace(/^(?:I(?:'m|'d| am| was| had| have)?\s+(?:currently\s+|just\s+)?(?:been\s+)?(?:working on|in the middle of|completing|finishing)\s+|The user(?:'s)?\s+(?:most recent\s+)?(?:request|ask) (?:was|is)\s+(?:to\s+)?)/i,"").replace(/^I(?:'m|'d| am| was| had| have)?\s+(?:just\s+|then\s+)?(?=(?:[a-z]+(?:ed|ing)|ran|wrote|built|made|began|found|left|sent|took|went|did|got|kept|held|brought|caught|chose|came|saw|said|gave|told|set|put|read)\b)/,"").replace(/^["“]|["”]$/g,"").replace(/^(\w)/,t=>t.toUpperCase()).trim()}function Pc(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function vy(e){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(e||"").trim())}function Ia(e,t){let n=String(e||"").trim();if(n.length<=t)return n;let r=Dc(n),o="";for(let i of r){let c=o?`${o} ${i}`:i;if(c.length>t)break;o=c}if(o.length>=Math.min(60,t*.4))return o;let s=n.slice(0,t),a=s.lastIndexOf(" ");return`${(a>t*.6?s.slice(0,a):s).replace(/[,;:.\s]+$/,"")}\u2026`}var y$,Ty,Wd,b$,k$,x$,S$,qd,Ey=C(()=>{y$=/^This session is being continued from a previous conversation that ran out of context\./,Ty=["Primary Request and Intent","Key Technical Concepts","Files and Code Sections","Errors and Fixes","Problem Solving","All user messages","Pending Tasks","Current Work","Optional Next Step"],Wd=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${Ty.join("|")})[:*\t ]*$`,"gim"),b$=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,k$=420,x$=[{h:"What we were doing",from:["Primary Request and Intent"]},{h:"Key decisions",from:["Key Technical Concepts"]},{h:"Where it got to",from:["Current Work","Problem Solving"]},{h:"Left to do",from:["Pending Tasks","Optional Next Step"]}],S$=116,qd=168});function Iy(e){return Gd.has(String(e||"").replace(/^\//,"").toLowerCase())}function Bc(e){let t=String(e||"").trim();if(!t)return null;let n=Ay(t);if(n)return n;let r=t.match(_$);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(I$);if(o)return{kind:"skill",name:o[1]};let s=t.match(R$);if(s){let i=s[1].toLowerCase();return Gd.has(i)?{kind:"suppress"}:{kind:"command",command:`/${i}`}}let a=t.match(L$);if(a&&Gd.has(a[1].toLowerCase()))return{kind:"suppress"};if($$.test(t))return{kind:"suppress"};if(C$.test(t))return{kind:"suppress"};if(O$.test(t))return{kind:"suppress"};if(M$.test(t)){let i=l=>F$(t.match(l)?.[1].trim()||"")||null,c=i(j$);return{kind:"task",summary:i(N$)||"",taskId:i(P$),toolUseId:i(D$),status:i(B$)?.toLowerCase()||null,resultHead:c?c.length>_y?`${c.slice(0,_y).trimEnd()}\u2026`:c:null}}return null}function F$(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function Ry(e){return e?.kind==="skill"?`Ran the ${e.name} skill`:e?.kind==="command"?`Ran ${e.command}`:e?.kind==="task"?e.summary||"A background task finished":e?.kind==="resume-summary"?e.title?`Context resumed from summary: ${e.title}`:"Context resumed from summary":""}function $y(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var _$,I$,R$,$$,O$,C$,M$,N$,P$,D$,B$,j$,_y,Gd,L$,Vd=C(()=>{Ey();_$=/^Base directory for this skill:\s*(\S+)/,I$=/^Run the "([^"]+)" workflow\./,R$=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,$$=/^<local-command-(?:stdout|caveat)>/i,O$=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,C$=/^Continue from where you left off\.$/,M$=/^<task-notification>/i,N$=/<summary>([\s\S]*?)<\/summary>/i,P$=/<task-id>([\s\S]*?)<\/task-id>/i,D$=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,B$=/<status>([\s\S]*?)<\/status>/i,j$=/<result>([\s\S]*?)(?:<\/result>|$)/i,_y=600,Gd=new Set(["model","clear","compact","login","exit","quit"]);L$=/^\/([a-z0-9:_-]+)$/i});import U$ from"node:crypto";function K$(e){let t=Array.isArray(e)?e:Array.isArray(e?.questions)?e.questions:null;if(!t||!t.length)return null;let n=[];for(let r of t.slice(0,Cy)){let o=Ra(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,H$)){let c=Ra(i?.label);if(!c)return null;let l=Ra(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<W$)return null;n.push({question:o,header:Ra(r?.header).slice(0,q$),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function Jd(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;Oy.lastIndex=0;let t=null;for(let o;o=Oy.exec(e);)t=o;if(!t||t.index+t[0].length<e.trimEnd().length)return null;let n;try{n=JSON.parse(t[2])}catch{return null}let r=K$(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function Yd(e,t,n){let r=U$.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${e}:ask:${t||"turn"}:${r}`}function Xd(e,{leadBoxId:t,provider:n,isAnswered:r,maxQuestions:o=Cy}={}){if(!Array.isArray(e)||!t||!zd.has(n))return null;let s=null;for(let l=e.length-1;l>=0;l--){let u=e[l];if(u?.kind==="prompt")return null;if(u?.kind==="message"){if(u.role!=="assistant")return null;s=u;break}}if(!s||s.box!==t||s.agent!==n)return null;let a=s.ask?.questions||Jd(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=Yd(n,s.sourceId||s.id,a);return typeof r=="function"&&r(c)?null:{provider:n,box:t,promptId:c,groupId:c,answerMode:"say",source:"transcript",multiSelect:i[0].multiSelect,questionIndex:0,questionTotal:i.length,questions:i}}function My(e,t,n){let r=[];(e?.questions||[]).forEach((a,i)=>{let c=(t?.[i]||[]).map(l=>a.options?.[l]?.label).filter(Boolean);c.length&&r.push({name:a.header||a.question,picks:c.join(", ")})});let o=Ra(n),s;return r.length?r.length===1?s=`${r[0].name}: ${r[0].picks}`:s=`Answers:
|
|
88
91
|
${r.map(a=>`- ${a.name}: ${a.picks}`).join(`
|
|
89
92
|
`)}`:s=o,r.length&&o&&(s+=`
|
|
90
|
-
${o}`),s||""}var
|
|
93
|
+
${o}`),s||""}var zd,Cy,H$,W$,q$,Oy,Ra,Ny,Zd=C(()=>{zd=new Set(["cursor","codex"]),Cy=4,H$=4,W$=2,q$=24,Oy=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,Ra=e=>typeof e=="string"?e.trim():"";Ny=`
|
|
91
94
|
When you are BLOCKED on a decision only the operator can make \u2014 an ambiguous
|
|
92
95
|
requirement, an irreversible choice, several valid approaches \u2014 do not bury the
|
|
93
96
|
question in prose. End your message with exactly ONE fenced code block tagged
|
|
@@ -107,67 +110,67 @@ your turn. The operator sees the questions as an interactive card in their chat
|
|
|
107
110
|
answer. Never use the block for rhetorical questions, progress updates, or
|
|
108
111
|
anything you can decide yourself; keep asking in prose for questions without
|
|
109
112
|
enumerable options.
|
|
110
|
-
`});function x$(e){return{id:e.id,kind:"prompt",groupId:e.groupId,provider:e.provider,box:e.box,agent:e.provider,questionTotal:e.questions.length,answeredVia:"chat",questions:e.questions.map((t,n)=>({index:n,header:t.header||"",question:t.question,multiSelect:!!t.multiSelect,chosen:[]})),ts:e.ts}}function v$(e,t,n){let r=Sy(n),o=e.has(r)?r:null;if(!o&&n.number!=null)for(let[i,c]of e){if(Number(c.number)!==Number(n.number))continue;let l=!!(n.url&&c.url&&n.url===c.url),u=!c.repo&&!!n.repo||!!c.repo&&!n.repo;if(l||u&&(!c.url||!n.url||c.url===n.url)){o=i;break}}let s=n.repo||!o?r:o,a=o?e.get(o):e.get(s);if(o&&o!==s){e.delete(o);let i=t.get(o);if(t.delete(o),i?.length){let c=t.get(s)??[];t.set(s,c.concat(i))}}return e.set(s,{number:n.number,url:n.url??a?.url??null,title:n.title??a?.title??null,repo:n.repo??a?.repo??null,branch:n.branch??a?.branch??null}),s}function Zn(e){let t=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=new Map,f=(y,w)=>{u.set(y,w),t.push(w)},d=y=>{let w=u.get(y);w&&(w.retired=!0)},p=/^Agent "([\s\S]+)" finished$/,m=new Map;for(let y of e||[]){if(y.type!=="tool.called"||y.source!=="transcript")continue;let w=String(y.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(w))for(let v of w.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let $=v[0].split("/").pop().toLowerCase();m.has($)||m.set($,[]),m.get($).push(y.payload?.ts??y.ts)}}let h=3*6e4,g=20*6e4,x=(y,w)=>!!(y?.name&&m.get(String(y.name).toLowerCase()))?.some($=>$>=w-h&&$<=w+g);for(let y of e||[])if(y.type==="turn.retracted"){r.add(`${y.boxId}|${y.payload?.saidTs}`);let w=Oe(y.payload?.text);w&&(o.has(y.boxId)||o.set(y.boxId,[]),o.get(y.boxId).push(w))}else if(y.type==="turn.user"&&y.source==="orchestrator"){let w=Oe(y.payload?.text);w&&(s.has(y.boxId)||s.set(y.boxId,new Set),s.get(y.boxId).add(w))}else if(y.type==="job.started"&&y.payload?.id)a.add(y.payload.id),(y.payload.kind||"agent")==="agent"&&y.payload.name&&c.add(y.payload.name);else if(y.type==="turn.user"&&y.payload?.note?.kind==="task"){let w=y.payload.note,v={status:w.status||null,summary:w.summary||null,resultHead:w.resultHead??null,ts:de(y)};w.toolUseId&&i.set(w.toolUseId,v);let $=String(w.summary||"").match(p);$&&l.set($[1],v)}let k=new Map,E=new Map,I=null,M=new Map,N=new Map,C=new Map,T=new Map,j=new Map,Z=new Map,fe=new Map,ie=new Map,D=new Map,R=new Map,U=new Map,pe=new Map,Se=new Map,He=()=>{for(let y of Se.values())y.questions.length&&t.push(y);Se.clear()},le=null,q=6e4,J=new Map,ee=(y,w)=>{let v=o.get(y);if(!v?.length)return!1;let $=Oe(w),ve=s.get(y);return v.some(b=>{if(!$.startsWith(b))return!1;let P=Oe($.slice(b.length));return!!P&&!!ve?.has(P)})};for(let y of e||[]){let w=y.payload||{};switch(y.type){case"turn.user":{if(y.source==="hook"||!String(w.text||"").trim()&&!w.images?.length&&!w.attachments?.length)break;{let v=w.note||$c(w.text);if(v){if(v.kind==="task"){if(v.toolUseId&&a.has(v.toolUseId))break;let $=String(v.summary||"").match(p);if(!v.toolUseId&&$&&c.has($[1]))break}if(v.kind==="command"&&hy(v.command))break;if(v.kind==="resume-summary"){t.push({id:y.id,kind:"resume-summary",title:v.title||"Context resumed from summary",bullets:v.bullets||[],sections:v.sections||[],rawChars:v.rawChars??null,ts:de(y),box:y.boxId,agent:y.provider});break}v.kind!=="suppress"&&t.push({id:y.id,kind:"event",type:gy(v),name:v.name??null,command:v.command??null,summary:v.summary??null,status:v.status??null,ts:de(y),box:y.boxId,agent:y.provider});break}}if(r.has(`${y.boxId}|${de(y)}`)||y.source==="transcript"&&ee(y.boxId,w.text))break;if(w.kind==="merge-notify"||w.kind==="ship-notify"){t.push({id:y.id,kind:"event",type:w.kind==="ship-notify"?"ship-notified":"merge-notified",text:w.text,ts:de(y),box:y.boxId,agent:y.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"&&(le=null);break}for(let v of M.values())v.status="stale";M.clear();for(let v of C.values())v.status="stale";C.clear(),le&&le.box===y.boxId&&(t.push(x$(le)),le=null),t.push({id:y.id,kind:"message",role:"user",text:w.text,ts:de(y),box:y.boxId,agent:y.provider,boxRole:w.boxRole,...y.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"peer.message":{let v=w.from||{};t.push({id:y.id,kind:"event",type:"peer-message",text:w.text,from:v.trackName||v.boxId||v.name||null,fromTrackId:v.trackId||null,ts:de(y),box:y.boxId,agent:y.provider});break}case"turn.agent":{if(w.model==="<synthetic>"&&/^No response requested\.$/.test(String(w.text||"").trim()))break;let v=Og(w,{codexFailedText:Dr});if(v){t.push({id:y.id,kind:"event",type:"provider-error",cls:v.cls,status:v.status??void 0,text:v.text,provider:y.provider,legacy:!0,at:de(y),ts:de(y),box:y.boxId,agent:y.provider});break}He();let $=(w.images||[]).filter(Me=>!x(Me,de(y)));if(!String(w.text||"").trim()&&!$.length)break;if(y.provider==="cursor"){let Me=`${y.boxId}|${w.text}`;if(n.has(Me))break;n.add(Me)}(!le||le.box===y.boxId)&&(le=null);let ve=y.provider==="codex"?(w.tools||[]).filter(Me=>!gd.has(Me)&&!la.has(Me)):w.tools||[],b=w.text,P=null;if(jd.has(y.provider)){let Me=Ld(String(w.text||""));Me&&(b=Me.text,P={questions:Me.questions})}t.push({id:y.id,kind:"message",role:"assistant",text:b,ts:de(y),box:y.boxId,agent:y.provider,boxRole:w.boxRole,...P?{ask:P}:{},...ve.length?{tools:ve}:{},...w.model?{model:w.model}:{},...w.sourceId?{sourceId:w.sourceId}:{},...$.length?{images:$}:{}}),P&&(le={id:y.id,groupId:Fd(y.provider,w.sourceId||y.id,P.questions),provider:y.provider,box:y.boxId,questions:P.questions,ts:de(y)});break}case"job.started":{if(!w.id)break;let v=de(y),$=i.get(w.id);if(!$&&(w.kind||"agent")==="agent"&&w.name){let We=l.get(w.name);We&&We.ts>=v&&($=We)}let ve={jobId:w.id,jobKind:w.kind||"agent",name:w.name||null,detail:w.detail||null,status:$?$.status==="killed"?"stopped":$.status&&$.status!=="completed"?"failed":"done":"running",startedTs:v,finishedTs:$?.ts??null,summary:$?.summary??null,resultHead:$?.resultHead??null},b=J.get(y.boxId),P=b?.jobs[b.jobs.length-1]?.startedTs;if(b&&P!=null&&v-P<=q)b.jobs.push(ve);else{let We={id:y.id,kind:"job-dock",dockId:w.id,ts:v,box:y.boxId,agent:y.provider,jobs:[ve]};J.set(y.boxId,We),t.push(We)}break}case"handoff.done":t.push(St(y,"lead-handoff"));break;case"subtask.delegated":t.push({...St(y,"subtask-delegated"),to:y.boxId,toAgent:y.provider});break;case"subtask.merged":t.push(St(y,"subtask-merged"));break;case"pr.state.changed":{if(!w.change)break;if(w.change==="opened"){for(let $ of M.values())$.status="stale";M.clear();for(let $ of C.values())$.status="stale";C.clear()}let v=St(y,`pr-${w.change}`);if(v.box=y.boxId??null,S$.has(w.change)&&w.number!=null){let $=v$(k,E,w);if(k.size>=2){for(let b of E.values())for(let P of b)P.grouped=!0;v.groupPrs=[...k.values()];let ve=[v];for(let b of k.keys())E.set(b,ve)}else{let ve=E.get($)??[];ve.push(v),E.set($,ve)}}else if(w.change==="merged"||w.change==="closed"||w.change==="lost"){let $=Sy(w),ve=I&&!I.complete||k.size>=2;if(k.has($))k.delete($),E.delete($);else if(w.number!=null)for(let b of[...k.keys()])b.endsWith(`#${w.number}`)&&(k.delete(b),E.delete(b));if(ve&&w.number!=null){(!I||I.complete)&&(I={id:y.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:de(y),ts:de(y),box:y.boxId,agent:y.provider},t.push(I)),I.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:de(y)}),I.remaining=[...k.values()],I.complete=k.size===0;break}}t.push(v);break}case"pr.offered":{if(!w.offerId)break;for(let $ of M.values())$.status="stale";M.clear();for(let $ of C.values())$.status="stale";C.clear();let v={id:y.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,available:Array.isArray(w.available)?w.available:null,defaults:Array.isArray(w.defaults)?w.defaults:null,prState:w.prState||null,openPr:w.openPr&&typeof w.openPr=="object"?w.openPr:null,repos:Array.isArray(w.repos)&&w.repos.length?w.repos:null,status:"open",ts:de(y),box:y.boxId,agent:y.provider};M.set(w.offerId,v),N.set(w.offerId,v),t.push(v);break}case"pr.offer.answered":{let v=N.get(w.offerId);v&&(v.status=w.action||"dismiss",v.actions=Array.isArray(w.actions)?w.actions:null,v.notify=w.notify===!0,w.existingPr&&typeof w.existingPr=="object"&&(v.existingPr={number:w.existingPr.number??null}),M.delete(w.offerId));break}case"pr.exists.offered":{if(!w.offerId)break;for(let $ of C.values())$.status="stale";C.clear();let v={id:y.id,kind:"pr-exists-offer",offerId:w.offerId,srcOfferId:w.srcOfferId??null,branch:w.branch||null,base:w.base||null,pr:w.pr&&typeof w.pr=="object"?{number:w.pr.number??null,url:w.pr.url??null,title:w.pr.title??null}:null,actions:Array.isArray(w.actions)&&w.actions.length?w.actions:null,notify:w.notify===!0,status:"open",ts:de(y),box:y.boxId,agent:y.provider};C.set(w.offerId,v),T.set(w.offerId,v),t.push(v);break}case"pr.exists.answered":{let v=T.get(w.offerId);v&&(v.status=w.choice||"dismiss",C.delete(w.offerId));break}case"box.state.changed":{let v=w.change==="model"?"model-change":w.change;if(!v)break;let $=St(y,v);if(v==="model-change"&&($.to=w.model),v==="node-update"&&$.latest){let ve=t.findIndex(b=>b.kind==="event"&&b.type==="node-update"&&b.latest===$.latest);if(ve>=0){let b=t[ve];t[ve]={...$,ts:b.ts,at:b.at,firstAt:b.firstAt??b.ts,updatedAt:$.ts};break}}t.push($);break}case"track.transferred":case"track.recovered":t.push(St(y,"track-moved"));break;case"view.state.changed":{if(w.change==="deleted"){d(`view:${w.viewId}`);break}if(w.change!=="created"||w.restarted)break;f(`view:${w.viewId}`,{id:y.id,kind:"view-card",viewId:w.viewId,name:w.name,slug:w.slug,port:w.port,origin:w.origin,ts:de(y),box:y.boxId,agent:y.provider});break}case"view.spinup.failed":{t.push({id:y.id,kind:"view-spinup-failed",name:w.name||null,reason:w.reason||"no-view",ts:de(y),box:y.boxId,agent:y.provider});break}case"mock.state.changed":{if(w.change==="picked"){t.push({id:y.id,kind:"mock-picked",mockId:w.mockId,name:w.name,group:w.group??null,version:w.version??null,siblings:w.siblings??[],ts:de(y),box:y.boxId,agent:y.provider});break}if(w.change==="deleted"){d(`mock:${w.mockId}`);break}if(w.change!=="created")break;f(`mock:${w.mockId}`,{id:y.id,kind:"mock-card",mockId:w.mockId,name:w.name,group:w.group??null,ts:de(y),box:y.boxId,agent:y.provider});break}case"flow.state.changed":{if(w.change==="deleted"){d(`flow:${w.flowId}`);break}if(w.change!=="created")break;f(`flow:${w.flowId}`,{id:y.id,kind:"flow-card",flowId:w.flowId,name:w.name,stepCount:w.stepCount??null,mockIds:w.mockIds??[],ts:de(y),box:y.boxId,agent:y.provider});break}case"runbook.state.changed":{if(w.change==="deleted"){d(`runbook:${w.runbookId}`);break}if(w.change!=="created")break;f(`runbook:${w.runbookId}`,{id:y.id,kind:"runbook-card",runbookId:w.runbookId,name:w.name,ts:de(y),box:y.boxId,agent:y.provider});break}case"ticket.linked":{t.push({id:y.id,kind:"ticket-card",provider:w.provider||"linear",key:w.key,url:w.url,title:w.title??null,linkSource:w.linkSource??null,ts:de(y),box:y.boxId,agent:y.provider});break}case"box.fullscreen.detected":{if(!w.offerId)break;for(let $ of fe.values())$.box===(w.box||y.boxId)&&($.status="stale",fe.delete($.offerId));let v={id:y.id,kind:"fullscreen-offer",offerId:w.offerId,status:"open",ts:de(y),box:w.box||y.boxId,agent:w.agent||y.provider};fe.set(w.offerId,v),ie.set(w.offerId,v),t.push(v);break}case"box.fullscreen.answered":{let v=ie.get(w.offerId);v&&(v.status=w.action==="restarted"?"restarted":w.action==="cleared"?"cleared":"dismiss",fe.delete(w.offerId));break}case"ticket.move.offered":{if(!w.offerId)break;for(let $ of j.values())$.key===w.key&&($.status="stale",j.delete($.offerId));let v={id:y.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:de(y),box:y.boxId,agent:y.provider};j.set(w.offerId,v),Z.set(w.offerId,v),t.push(v);break}case"ticket.move.answered":{let v=Z.get(w.offerId);v&&(v.status=w.action==="moved"?"moved":"dismiss",j.delete(w.offerId));break}case"ticket.state.changed":{for(let v of j.values())v.key===w.key&&v.to===w.to&&(v.status="stale",j.delete(v.offerId));if(w.mode==="offer")break;t.push(St(y,"ticket-state"));break}case"ticket.commented":{t.push({id:y.id,kind:"ticket-comment",key:w.key,url:w.url??null,excerpt:w.excerpt??null,ts:de(y),box:y.boxId,agent:y.provider});break}case"ticket.flow.changed":t.push(St(y,"ticket-flow"));break;case"ticket.create.offered":{if(!w.proposalId)break;for(let $ of D.values())$.status="stale";D.clear();let v={id:y.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:de(y),box:y.boxId,agent:y.provider};D.set(w.proposalId,v),R.set(w.proposalId,v),t.push(v);break}case"ticket.create.answered":{let v=R.get(w.proposalId);v&&(v.status=w.action==="created"?"created":"dismiss",v.createdCount=w.createdCount??null,v.failedCount=w.failedCount??null,D.delete(w.proposalId));break}case"loop.create.offered":{if(!w.proposalId)break;for(let $ of U.values())$.status="stale";U.clear();let v={id:y.id,kind:"loop-create-offer",proposalId:w.proposalId,name:w.name??"",schedule:w.schedule??"",prompt:w.prompt??"",expectation:w.expectation??null,notify:w.notify??"off",assignment:w.assignment??{provider:"claude"},repo:w.repo??null,nodeId:w.nodeId??null,nodeName:w.nodeName??null,status:"open",ts:de(y),box:y.boxId,agent:y.provider};U.set(w.proposalId,v),pe.set(w.proposalId,v),t.push(v);break}case"loop.create.answered":{let v=pe.get(w.proposalId);v&&(v.status=w.action==="created"?"created":"dismiss",U.delete(w.proposalId));break}case"loop.created":{let v=w.proposalId?pe.get(w.proposalId):null;v&&(v.created={loopId:w.loopId??null,nextFireAt:Number.isFinite(Number(w.nextFireAt))?Number(w.nextFireAt):null});break}case"ticket.created":{let v={items:Array.isArray(w.items)?w.items:[],failed:Array.isArray(w.failed)?w.failed:[],team:w.team??null},$=w.proposalId?R.get(w.proposalId):null;if($){$.created=v;break}t.push({id:y.id,kind:"ticket-created",...v,ts:de(y),box:y.boxId,agent:y.provider});break}case"error":{if(!w.kind)break;t.push(St(y,w.kind));break}case"provider.error":t.push({...St(y,"provider-error"),provider:y.provider,box:y.boxId,agent:y.provider});break;case"usage.cap.hit":t.push(St(y,"usage-cap-hit"));break;case"usage.cap.warning":t.push(St(y,"usage-cap-warning"));break;case"provider.connected":{t.push({id:y.id,kind:"event",type:"provider-connected",provider:w.provider||y.provider,ts:de(y),at:de(y),box:y.boxId,agent:y.provider});break}case"prompt.answered":{if(le=null,w.control||!w.question)break;let v=w.groupId||w.promptId;if(!v)break;let $=Se.get(v);$||($={id:y.id,kind:"prompt",groupId:v,provider:y.provider,box:y.boxId,agent:y.provider,questionTotal:w.questionTotal??1,questions:[],ts:de(y)},Se.set(v,$));let ve=w.questionIndex??0,b={index:ve,header:w.header||"",question:w.question,multiSelect:!!w.multiSelect,chosen:Array.isArray(w.chosen)?w.chosen:[]},P=$.questions.findIndex(We=>We.index===ve&&Oe(We.question)===Oe(b.question));P>=0?$.questions[P]=b:$.questions.push(b),$.questions.sort((We,Ht)=>We.index-Ht.index),$.questionTotal=Math.max($.questionTotal,w.questionTotal??1),$.questions.length>=$.questionTotal&&(t.push($),Se.delete(v));break}default:break}}let Xe=u.size?t.filter(y=>!y.retired):t;return E$(Xe.sort((y,w)=>(y.ts??0)-(w.ts??0)))}function E$(e){let t=new Map,n=[];for(let r of e){if(r.kind!=="message"||!r.box){n.push(r);continue}let o=r.role==="assistant"&&!String(r.text||"").trim()&&r.images?.length,s=t.get(r.box);if(o&&s&&(r.ts??0)-s.lastTs<=T$){s.item.images.push(...r.images),s.lastTs=r.ts??s.lastTs;continue}if(o){let a={...r,images:[...r.images]};t.set(r.box,{item:a,lastTs:r.ts??0}),n.push(a)}else t.delete(r.box),n.push(r)}return n}function Wd(e,t=null){let n=[];for(let o of e||[]){if(o.type!=="tool.called"||o.source!=="transcript")continue;let s=o.payload||{};String(s.command||"").trim()&&n.push({id:s.id??null,tool:s.tool||null,command:s.command,description:s.description||"",summary:A$(s),ts:s.ts??o.ts,n:s.n,box:o.boxId,agent:o.provider,boxRole:s.boxRole})}if(!n.length)return[];let r=(t??Zn(e)).filter(o=>o.kind==="message");return vg(n,r)}function A$(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==No(e.command)?t:Jn(e.command,e.description||"")}function St(e,t){let{ts:n,change:r,...o}=e.payload||{};return{id:e.id,kind:"event",type:t,...o,at:de(e),ts:de(e)}}function vy(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||r.type!=="archived")continue;let o=(r.ts??0)+_$,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&I$.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function Ty(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!$$.has(r.type)||!r.box)continue;let o=(r.ts??0)+R$,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||!(a?.kind==="message"&&a.role==="assistant"&&a.box===r.box||a?.kind==="command-batch"&&a.anchorBox===r.box))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function Ey(e){return(e||[]).filter(t=>O$.has(t?.kind)&&t.status==="open")}var de,S$,Sy,T$,_$,I$,R$,$$,O$,qd=O(()=>{Po();Br();gc();Cd();Bd();Hd();de=e=>e.payload?.ts??e.ts;S$=new Set(["opened","ci-fixed","reconciled","pushed"]),Sy=e=>`${e.repo??""}#${e.number}`;T$=2*6e4;_$=12e4,I$=new Set(["archived","unarchived","opened"]);R$=2500,$$=new Set(["pr-opened","pr-merged","pr-auto-armed"]);O$=new Set(["pr-offer","pr-exists-offer","ticket-offer","ticket-create-offer","loop-create-offer","fullscreen-offer"])});function D$(e){let t=null,n=null,r=null;for(let o of e??[]){let s=o?.ts??null;s&&(r=Math.max(r??0,s)),o?.type==="handoff.artifact"&&(t={ts:o.payload?.ts??s,truncated:!!o.payload?.truncated,...o.payload?.reconstructed?{reconstructed:!0}:{}}),o?.type==="code.pushed"&&(n={ts:o.payload?.ts??s,sha:o.payload?.sha??null,ref:o.payload?.ref??null,wip:!!o.payload?.wip})}return{lastArtifact:t,lastPush:n,lastEventAt:r}}function j$(e){for(let t=(e?.length??0)-1;t>=0;t--){let n=e[t];if(n?.type==="handoff.artifact"&&!n.payload?.reconstructed)return n}return null}function L$(e,t){let n=[],r=[],o=new Set,s=new Set;for(let a=(e?.length??0)-1;a>=0;a--){let i=e[a];if(i?.type!=="tool.called")continue;if(t!=null&&(i.ts??0)<=t)break;let c=i.payload||{};c.path&&!o.has(c.path)&&n.length<N$&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<P$&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function Ry(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=D$(e),s=j$(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=Zn(e??[]).filter(d=>d?.kind==="message"&&typeof d.text=="string"&&d.text.trim()).slice(-C$);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(Iy[r]??Iy.failover),l.push(""),o.lastPush?.sha){let d=_y(n,o.lastPush.ts),p=d?` (~${d} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${p}`+(o.lastPush.wip?" \u2014 a bostrat WIP snapshot (full working tree incl. uncommitted edits at push time).":"."))}else r==="in-place"?l.push("**Code state:** no push recorded \u2014 the working tree in this workspace is the only copy; commit early."):l.push("**Code state:** no push was ever recorded for this Track \u2014 work since the branch's last known state stayed on the offline node.");if(l.push(""),a){let d=_y(n,i);l.push(`## Last real checkpoint (written by the previous agent${d?`, ~${d} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=L$(e??[],i);if((u.files.length||u.heads.length)&&(l.push(`## Work since ${a?"that checkpoint":"the start of the log"} (from the event log)`),l.push(""),u.files.length&&l.push(`Files touched (most recent first): ${u.files.map(d=>`\`${d}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(d=>`\`${d}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let d of c)l.push(`**${d.role==="user"?"operator":"agent"}:** ${B$(d.text,M$)}`),l.push("")}l.push("---"),l.push("Resume: continue the objective above from the verified repository state; ask the operator before redoing anything the log says was already done.");let f=l.join(`
|
|
111
|
-
`);return
|
|
112
|
-
|
|
113
|
-
[\u2026 brief truncated]`}var
|
|
114
|
-
|
|
115
|
-
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function J$(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(My.test(s)){if(r.length>=Wy)continue;let a=z$(s.replace(My,"").replace(/[.;,:]+$/,""),qy,G$);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>Cy&&(o=`${o.slice(0,Cy-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function Ky(e,t,{timeoutMs:n=9e4}={}){let r=er.join(t,q$),o=er.join(r,K$);await Qn.mkdir(r,{recursive:!0});let s=await Oc(o),a=await se(e);if(!a)throw new Error(`session "${e}" not found`);return await Cc(e,a,V$(o)),await Zy(o,s,n),J$(await Qn.readFile(o,"utf8"))}function Gy(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===Ly||n===Hy;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(jy)||n===U$||r.startsWith(Fy)||r.startsWith(Uy)}function Vy(e){let t=[];for(let r of e?.events||[]){if(r.type!=="lead-handoff"||!r.from)continue;let o=r.at??Date.now(),s=r.handoffStartedAt??o-Y$;t.push({box:r.from,from:s,to:o}),r.to&&r.to!==r.from&&t.push({box:r.to,from:s,to:o})}let n=e?.handoff;return n?.fromBox&&n.startedAt&&n.finishedAt&&t.push({box:n.fromBox,from:n.startedAt,to:n.finishedAt}),n?.toBox&&n.startedAt&&n.finishedAt&&t.push({box:n.toBox,from:n.startedAt,to:n.finishedAt}),t}function zy(e,t){let n=[];for(let r of e?.boxes||[]){let o=t?.(r.name);o?.startedAt&&n.push({box:r.name,from:o.startedAt,to:o.finishedAt||Date.now()+1e12})}return n}function Jy(e,{windows:t=[]}={}){if(Gy(e.role,e.text))return!0;if(e.role!=="assistant"||!e.ts)return!1;for(let n of t)if(e.box===n.box&&e.ts>=n.from&&e.ts<=n.to)return!0;return!1}function Mc(e){return Kd.get(e)||null}async function Yy(e){let t=Kd.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await se(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);let r={name:e,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:er.join(n.cwd,Py,Dy)};return Kd.set(e,r),Q$(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function Q$(e,t){e.step="summarize",await Xy(e.name,t),e.step="clear";let n=await se(e.name);await Cc(e.name,n,"/clear"),await zd(X$),e.step="resume",await Cc(e.name,n,W$),e.step="done",e.finishedAt=Date.now()}async function Xy(e,t,{timeoutMs:n=Z$}={}){let r=er.join(t,Py),o=er.join(r,Dy);await Qn.mkdir(r,{recursive:!0});let s=await Oc(o),a=await se(e);await Y({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await Cc(e,a,H$(o)),await Zy(o,s,n),await Y({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await Oc(o)});let i=await oy(t,o,{exclude:u=>Gy(u.role,u.text)});await Y({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(c){await _({id:c,cwd:t},{type:"memory.seed",actor:"orchestrator",payload:{...i,tokensEst:i.chars?Math.round(i.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let u=await Qn.readFile(o,"utf8");await _({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...By(u),ts:Date.now()}})}catch{}}await Vu(t);let l=await eO(r,o);return c&&await Jw(c,l||o,{box:e,agent:a?.agent??null}),o}async function Gd(e,{timeoutMs:t}={}){let n=await se(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return Xy(e,n.cwd,t?{timeoutMs:t}:void 0)}async function Vd(e,t,{trackName:n="this Track"}={}){let r=await L({id:e,cwd:t}),o=Ry(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=er.join(t,Ny);return await Qn.mkdir(er.dirname(s),{recursive:!0}),await Qn.writeFile(s,o,"utf8"),await _({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...By(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),Ny}async function Oc(e){try{return(await Qn.stat(e)).mtimeMs}catch{return 0}}async function Zy(e,t,n){let r=Date.now()+n;for(;;){if(await Oc(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await zd(2e3)}}async function eO(e,t){try{if(!F$.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=er.join(e,`${n}.md`);return await Qn.copyFile(t,r),r}catch{return null}}function zd(e){return new Promise(t=>setTimeout(t,e))}async function Cc(e,t,n){if(t){let o=t.agent==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":t.agent==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":t.agent==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":"";await Xn({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await se(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await $e(e,["Escape"],{kind:"drive-wipe"}),await zd(150),await $o(e,mc,{kind:"drive-wipe"}),await Fe(e,n,{kind:"orchestration-drive",...Gt(r.agent,n)})}var Py,Dy,Oy,jy,Ly,Fy,U$,H$,W$,Uy,Hy,q$,K$,Cy,Wy,qy,G$,V$,My,z$,Y$,X$,Z$,Kd,Ny,Jd=O(()=>{tt();Tc();un();ha();sy();zs();ue();ac();$y();Py=".bostrat/handoff",Dy="latest.md",Oy=32*1024;jy="Checkpoint: write a work-in-progress handoff",Ly="HANDOFF WRITTEN",Fy="Resume from .bostrat/handoff/latest.md",U$="/clear",H$=e=>`${jy} to the EXACT absolute path ${e} (write it there specifically \u2014 NOT into any sub-project or other directory; create the directory if needed, overwrite the file) so a FRESH Claude context with none of this conversation can resume seamlessly. Use these sections: Objective; Done (with file paths); In progress (exact state); Next steps (ordered); Key files & commands; Open decisions/blockers; Resume instruction (one line). Be concrete and self-contained \u2014 assume the reader has none of this chat. When the file is written, reply only with ${Ly} and take no further action.`,W$=`${Fy}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,Uy="Beat: write a",Hy="BEAT WRITTEN",q$=".bostrat/arc",K$="beat.txt",Cy=200,Wy=4,qy=5,G$=36,V$=e=>`${Uy} short summary of what this workstream accomplished, to the EXACT absolute path ${e} (write it there specifically \u2014 NOT into any sub-project or other directory; create the directory if needed, overwrite the file). Format the file as: FIRST LINE = one sentence of at most 18 words, plain past tense, saying the outcome \u2014 no heading, no bullet, no trailing list. THEN, only if this workstream covered several separable pieces of work, up to ${Wy} further lines, each starting with "- " and naming ONE piece in at most ${qy} 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 ${Hy} and take no further action.`,My=/^(?:[-–—*•·]|\d+[.)])\s+/,z$=(e,t,n)=>{let r=e.split(/\s+/).filter(Boolean).slice(0,t).join(" ");return r.length>n?`${r.slice(0,n-1).trimEnd()}\u2026`:r};Y$=6e5;X$=3500,Z$=3e5,Kd=new Map;Ny=".bostrat/handoff/status-latest.md"});function eb(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!tO.test(String(e)))throw new Error(`pricing.js: ${n} window ${t} must be YYYY-MM-DD, got ${JSON.stringify(e)}`);let r=Date.parse(`${e}T${t==="from"?"00:00:00.000":"23:59:59.999"}Z`);if(!Number.isFinite(r))throw new Error(`pricing.js: ${n} window ${t} is not a real date: ${e}`);return r}function nO(e){let t={};for(let[n,r]of Object.entries(e)){if(!Array.isArray(r)||r.length===0)throw new Error(`pricing.js: ${n} must be a non-empty array of rate windows`);t[n]=r.map(o=>({...o,fromMs:eb(o.from,"from",n),toMs:eb(o.to,"to",n)}))}return t}function oO(e){if(e==null)return Date.now();if(typeof e=="number")return Number.isFinite(e)?e:null;if(e instanceof Date){let n=e.getTime();return Number.isFinite(n)?n:null}let t=Date.parse(String(e));return Number.isFinite(t)?t:null}function sO(e){if(!e)return null;let t=String(e).trim();return Yd[t]?t:Qy[t]?Qy[t]:Object.keys(Yd).find(r=>t.startsWith(r))||null}function tb(e,t){let n=sO(e);if(!n)return null;let r=oO(t);return r===null?null:rO[n].find(o=>r>=o.fromMs&&r<=o.toMs)||null}function tr(e,t={},n){let r=tb(e,n);return r?((t.input||0)*r.input+(t.output||0)*r.output+(t.cacheWrite||0)*r.cacheWrite+(t.cacheRead||0)*r.cacheRead)/1e6:0}var Yd,Qy,tO,rO,nb,xa=O(()=>{Yd={"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)."}]},Qy={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"},tO=/^\d{4}-\d{2}-\d{2}$/;rO=nO(Yd);nb=(e,t)=>tb(e,t)!==null});function aO(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function ob(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function pn(e){let t=e.payload?.model||"unknown",n=aO(e.payload?.tokens),r=Number(e.payload?.usd)||0,o=e.source==="vendor",s=r,a=!1;if(n&&!o){let l=tr(t,n,e.ts);l>0&&(s=l,a=Math.abs(l-r)>1e-9)}let i=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,c=!s&&i>0&&!nb(t,e.ts);return{usd:s,model:t,tokens:n,repriced:a,unpriced:c,asRecorded:r}}function nr(e,{boxLabels:t={}}={}){let n=(e||[]).filter(d=>d.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,f=rb();for(let d of n){let{usd:p,model:m,tokens:h,repriced:g,unpriced:x,asRecorded:k}=pn(d);i+=k;let E=d.provider||"unknown",I=d.boxId||"unattributed";if(g&&c++,x&&(l++,u.add(m)),a+=p,ob(f,h),r[m]||={usd:0,tokens:rb(),events:0},r[m].usd+=p,r[m].events+=1,ob(r[m].tokens,h),o[E]||={usd:0,events:0},o[E].usd+=p,o[E].events+=1,s[I]||={usd:0,events:0,provider:E,models:new Set,label:t[I]||I},s[I].usd+=p,s[I].events+=1,m!=="unknown"&&s[I].models.add(m),d.epoch!=null){let M=(s[I].epochs||={})[d.epoch]||={usd:0,events:0,provider:E,models:new Set};M.usd+=p,M.events+=1,M.provider=E,m!=="unknown"&&M.models.add(m)}}return{totalUsd:Bo(a),asRecordedUsd:Bo(i),totalTokens:f,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Nc(r,d=>({...d,usd:Bo(d.usd)})),byProvider:Nc(o,d=>({...d,usd:Bo(d.usd)})),byBox:Nc(s,d=>({usd:Bo(d.usd),events:d.events,provider:d.provider,label:d.label,models:[...d.models],...d.epochs?{epochs:Nc(d.epochs,p=>({usd:Bo(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var rb,Bo,Nc,Fr=O(()=>{xa();rb=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});Bo=e=>Math.round(e*1e4)/1e4,Nc=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function iO(e){if(!Number.isFinite(e)||e<=0)return null;let t=e/864e5;if(t>=1)return`${Math.round(t)}d`;let n=e/36e5;return n>=1?`${Math.round(n)}h`:`${Math.max(1,Math.round(e/6e4))}m`}function sb(e,t=[]){let n=[],r=e?.events||[],o=new Map,s=0;for(let u of t){if(u.type!=="pr.state.changed")continue;let f=u.payload||{};!f.repo||f.number==null||(f.change==="merged"?o.set(`${f.repo}#${f.number}`,f):f.change==="opened"&&s++)}if(o.size){let u=[...o.keys()].map(f=>{let[d,p]=f.split("#");return`${d.split("/").pop()}#${p}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(Xd(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(Xd(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(`${Xd(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=nr(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?iO(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var Xd,ab=O(()=>{Fr();Xd=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as cO}from"node:child_process";import{promisify as lO}from"node:util";import{randomUUID as uO}from"node:crypto";async function Zd(e,t){let n=Date.now()+t;for(;;){if(await dn(e))return!0;if(Date.now()>n)return!1;await Sa(fO)}}async function mO(e,t){let n=cb[t]||cb.default;n.escape&&(await $e(e,["Escape"],{kind:"swap-exit"}),await Sa(150)),n.wipe&&await $o(e,mc,{kind:"swap-exit"}),await hc(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Gt(t).submitDelayMs||0;await Sa(Math.max(r,250)),await $e(e,["Enter"],{kind:"swap-exit"})}async function lb(e,t){try{let{stdout:n}=await Ur(F,H("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function ub(e){await Ur(F,H("respawn-pane","-k","-t",e))}async function Qd(e,t={}){if(!_e(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await se(e);if(!r)throw new Error(`session "${e}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${e}"`);let o=r.agent||"agent",s=t.agent||Gn(n),a=Number(t.exitTimeoutMs)||dO;await Y({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let i=!1;if(!await dn(e)&&(await mO(e,o),!await Zd(e,a)&&(i=!0,await Y({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await ub(e),!await Zd(e,ib))))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);let c=s==="claude"&&Mr(n),l=c?Ui():null,u=await lb(e,"ANTHROPIC_API_KEY"),f=!1;try{l?(await Ur(F,H("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),f=!0):u&&(await Ur(F,H("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),f=!0)}catch(g){throw l?cc(g,l):g}if(f&&!i&&(await ub(e),!await Zd(e,ib)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let d=!!t.resumeSessionId,p=t.resumeSessionId||uO(),m=d?n:rd(n,p,e),h=d?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await Ln(r.cwd),s==="codex"&&await go(r.cwd),s==="cursor"&&await yo(r.cwd),await Nr(e),await Ur(F,H("set-option","-t",e,"@bostrat-sid",p)),await Ur(F,H("set-option","-t",e,"@bostrat-agent",s)),await Sa(pO),c&&na().length&&(await lb(e,ta)||await Ur(F,H("set-environment","-t",e,ta,"1")).catch(()=>{}),await Fe(e,`export ${ta}=1`,{kind:"swap-env"}),await Sa(150)),await Fe(e,m,{kind:"swap-launch"}),await Y({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:p,forced:i,envRespawned:f}),{name:e,cwd:r.cwd,sessionId:p,agent:s,pinned:h,forced:i,envRespawned:f,launchCmd:m}}var Ur,Sa,dO,ib,fO,pO,cb,db=O(()=>{tt();Us();Wi();qi();ha();un();Ur=lO(cO),Sa=e=>new Promise(t=>setTimeout(t,e)),dO=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,ib=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,fO=250,pO=400,cb={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 fb(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await it(e,t)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
|
|
116
|
-
`)){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
|
|
117
|
-
`).trim():"",
|
|
118
|
-
`),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
|
|
119
|
-
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!
|
|
120
|
-
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(
|
|
121
|
-
`),o=-1;for(let u=r.length-1;u>=0;u--)if(
|
|
122
|
-
`),o=
|
|
123
|
-
`),o=
|
|
124
|
-
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=
|
|
125
|
-
`)].reverse().find(a=>
|
|
126
|
-
`),o=
|
|
127
|
-
`),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(
|
|
128
|
-
`),o=
|
|
129
|
-
`).pop()||"")?!0:
|
|
130
|
-
`).pop()||""):e==="codex"&&/Sign in with ChatGPT|one-time code|Paste or type your API key|link to authenticate|Successfully logged in\.[\s\S]*Press Enter to continue\./i.test(n)?!0:e==="cursor"||e==="codex"?/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i.test(n):!1:!1}function
|
|
131
|
-
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];
|
|
132
|
-
`).filter(Boolean);return t&&(r=r.filter(o=>{let s=o.slice(3);return!(s===".bostrat"||s===".bostrat/"||s.startsWith(".bostrat/")||o.startsWith("??")&&(s==="AGENTS.md"||s==="CLAUDE.md"))})),r.length>0}async function
|
|
133
|
-
`).map(s=>s.trim()).filter(Boolean)}async function
|
|
134
|
-
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var
|
|
135
|
-
`),n=t.findIndex(o=>/Select Model and Effort/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<t.length;o++){let s=t[o].match(/^\s*(›\s*)?(\d+)\.\s+(\S+?)(?:\s+\((current|default)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),id:s[3],current:s[4]==="current",active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(t[o]))break}return r.length?r:null}function
|
|
136
|
-
`),n=t.findIndex(o=>/Select Reasoning Level/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<t.length;o++){let s=t[o].match(/^\s*(›\s*)?(\d+)\.\s+([A-Za-z ]+?)(?:\s+\((default|current)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),label:s[3].trim(),isDefault:!!s[4],active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(t[o]))break}return r.length?r:null}function
|
|
137
|
-
`);for(let n=t.length-1;n>=0;n--){let r=t[n].match(/Model changed to\s+(\S+)(?:\s+(\S+))?\s*$/i);if(r)return{model:r[1],effort:r[2]||null}}return null}function
|
|
138
|
-
`),n=t.findIndex(s=>/Available models|Models matching/i.test(s));if(n<0)return null;let r=/Models matching/i.test(t[n]),o=[];for(let s=n+1;s<t.length;s++){let a=t[s];if(/^\s*Filter:/i.test(a)||!a.trim())continue;if(/to filter|Enter to select|^\s*\d+-\d+ of \d+/i.test(a))break;let i=a.match(/^\s*(→\s+)?(.+?)\s*$/);if(!i)continue;let c=i[2].replace(/\s*\(Tab to modify\)\s*$/i,""),l=null,u=c.split(/\s{2,}/);u.length>1&&(c=u[0],l=u.slice(1).join(" ").trim()||null),c&&o.push({label:c.trim(),params:l,active:!!i[1]})}return o.length?{filtered:r,rows:o}:null}function wk(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim();if(e==="codex"){let[r,o]=n.split(/[\s:]+/);if(o&&!pk.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=mk.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!pC.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function Ia(e,t,{timeoutMs:n=dC}={}){let r=Date.now()+n;for(;;){let o=await Ae(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await qc(fC)}}async function wC(e,t){return await Fe(e,`/model ${t.model}`,Gt("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function gC(e,t){await pc(e),await Fe(e,"/model",{enter:!1}),await qc(250),await $e(e,["Enter"],{kind:"model-switch"});let n=await Ia(e,lk);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await $e(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${t.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=hC(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await Fe(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await Ia(e,i=>uk(i)||!lk(i)&&o(i)&&{done:o(i)});if(!s)throw await $e(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?pk.get(t.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await $e(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await Fe(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Ia(e,i=>!uk(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 yC(){let e=ck.join(process.env.CURSOR_CONFIG_DIR||ck.join(uC.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await lC.readFile(e,"utf8"))?.model||null}catch{return null}}async function bC(e,t){await pc(e),await hc(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await Ia(e,dk),r=c=>String(c||"").toLowerCase(),o=c=>r(c.label).startsWith(r(t.filter)),s=n?.rows.find(c=>c.active)||null;if(n&&!(s&&o(s)))for(let c=0;c<12;c++){let l=await Ia(e,dk,{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 $e(e,["Down"],{kind:"model-switch"}),await qc(250)}if(!n||!s||!o(s)){await $e(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await $e(e,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${t.filter}"`:"cursor model picker didn't open")}await $e(e,["Enter"],{kind:"model-switch"}),await qc(800);let a=await yC();return{verified:!!a&&(a.modelId===t.model||a.displayModelId===t.model||r(a.displayName).startsWith(r(t.filter))),model:a?.modelId||null,note:"cursor applies this as the global default for new sessions too"}}async function gk(e,t,n){return e==="codex"?gC(t,n):e==="cursor"?bC(t,n):wC(t,n)}var qc,dC,fC,fk,pC,mC,pk,mk,yk=O(()=>{tt();ha();qc=e=>new Promise(t=>setTimeout(t,e)),dC=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,fC=300,fk=[{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"}],pC=new Set([...fk.map(e=>e.alias),"best","opus[1m]","sonnet[1m]","claude-opus-5","claude-opus-4-8","claude-sonnet-5","claude-sonnet-4-6","claude-haiku-4-5","claude-fable-5"]),mC=[{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"}],pk=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),mk=[{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 Gc(e){let t=e||{},n=kC(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function kC(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{text:e.text,boxRole:e.boxRole,...e.kind?{kind:e.kind}:{},...e.attachments?.length?{attachments:e.attachments}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{saidTs:e.saidTs,text:e.text,boxRole:e.boxRole}};case"orchestration-prompt":return{type:"turn.user",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{text:e.prompt,kind:e.kind,boxRole:e.boxRole}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:ct(e.toAgent),payload:{from:e.from,fromAgent:e.fromAgent,to:e.to,toAgent:e.toAgent,reason:e.reason,handoffStartedAt:e.handoffStartedAt,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,singleBox:e.singleBox,forced:e.forced,recovered:e.recovered,checkpoint:e.checkpoint}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.toAgent),payload:{box:e.box,fromAgent:e.fromAgent,toAgent:e.toAgent,reason:e.reason,error:e.error,rolledBack:e.rolledBack,handoffStartedAt:e.handoffStartedAt}};case"subtask-delegated":return{type:"subtask.delegated",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:ct(e.toAgent),payload:{task:e.task,subtaskId:e.subtaskId,reason:e.reason}};case"subtask-merged":return{type:"subtask.merged",actor:"orchestrator",source:"orchestrator",boxId:e.box||e.from,provider:ct(e.agent),payload:{...sr(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:ct(e.agent||e.toAgent),payload:{kind:"subtask-failed",...sr(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{change:"subtask-resumed",...sr(e)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"model",model:e.to,from:e.from??null,verified:e.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:"codex",payload:{change:"cli-update",from:e.from??null,to:e.to??null,status:e.status,...e.error?{error:e.error}:{}}};case"node-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",payload:{change:"node-update",...sr(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...sr(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...sr(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{change:"rehydrated"}};case"transferred-in":return{type:e.mode==="recover"?"track.recovered":"track.transferred",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{...sr(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...sr(e)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:ct(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};default:return null}}function xk(e,t){let n=Gc(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;Cf(()=>_(r,n))}async function Sk(e){if(!(!e?.id||bk.has(e.id))){bk.add(e.id);try{let t=await L(e),n=new Set(t.filter(r=>r.source==="orchestrator").map(r=>`${r.type}|${r.payload?.ts??"?"}`));for(let r of e.events||[]){let o=Gc(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await _(e,o)}}catch{}}}function sr(e){let{type:t,at:n,...r}=e;return r}function vk(e,t,n){!n?.promptId||Of.get(e.id)===n.promptId||(Of.set(e.id,n.promptId),Cf(()=>_(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:ct(n.provider||t?.agent),payload:{promptId:n.promptId,question:n.question||n.title||null,options:(n.options||[]).map(r=>r?.label??r).slice(0,12)}})))}function Mf(e,t,n,r={}){Of.delete(e.id),Cf(()=>_(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:ct(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var ct,Kc,Cf,kk,bk,Of,Nf=O(()=>{ue();ct=e=>e||null;Kc=Promise.resolve(),Cf=e=>(Kc=Kc.then(e).catch(()=>{}),Kc),kk=()=>Kc;bk=new Set;Of=new Map});function EC(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function Rk(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of EC(n).reverse()){if(!xC.test(r))continue;let o=r.endsWith("?")&&r.length<=TC&&vC.test(r);if(SC.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function Ok(e){let t=String(e||""),n=[];AC.test(t)&&n.push("commit"),$k.test(t)&&n.push("rebase"),_C.test(t)&&n.push("push");let r=RC.test(t);!r&&IC.test(t)&&n.push("pr");let o=null;if(r){let s=t.match(/(?:pr|pull\s+request)\s*\(?\s*#(\d+)\)?/i)||t.match(/#(\d+)/);o={number:s?Number(s[1]):null}}return{offered:n.length?n:null,toPr:o}}function Ck(e){return $k.test(String(e||""))}function Jo(e,{branch:t=null,base:n=null,toPr:r=null,repos:o=null,offeredRebase:s=!1}={}){Array.isArray(e)||(e=e==="push"?["commit","push"]:["commit","push","pr"]);let a=new Set(e),i=Array.isArray(o)&&o.length>0,c=i?o.map(p=>`${p.name}/`).join(", "):null,l=t?` on this branch (${t})`:"",u=[];if(a.has("commit")&&u.push(i?`commit the work in each changed checkout (${c})${l} with a clear commit message per repo`:`commit the work${l} with a clear commit message`),a.has("rebase")){let p=a.has("commit")?"the branch":`this branch${t?` (${t})`:""}`;u.push(i?`rebase ${p} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${p} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`)}if(a.has("push")||a.has("pr")){let p=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",m=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${m}${p}`:`push this branch${t?` (${t})`:""} from each changed checkout (${c}) to its remote${p}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${m}${p}`:`push this branch${l} to the remote${p}`)}a.has("pr")&&u.push(i?"open a pull request in each changed repo with a concise title and a description of what changed and why (run `gh pr create` inside each checkout)":`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let f=[];a.has("commit")||f.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&f.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&f.push("don't push"),a.has("pr")||f.push("don't open a pull request");let d=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return f.length&&(d+=` ${f.join(", and ").replace(/^./,p=>p.toUpperCase())}.`),a.has("pr")&&(d+=i?" If a repo's PR for this branch is already open, just push that repo's update instead.":" If a PR for this branch is already open, just push the update instead."),d}function Mk(e,{branch:t=null,base:n=null,prNumber:r=null,offeredRebase:o=!1}={}){let s=new Set(Array.isArray(e)?e:["commit","push","pr"]),a=t?` on this branch (${t})`:"",i=[];s.has("commit")&&i.push(`commit the work${a} with a clear commit message`),s.has("rebase")&&i.push(`rebase ${s.has("commit")?"the branch":`this branch${t?` (${t})`:""}`} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`),i.push("move the work to a fresh branch (branch off the current HEAD with a clear new name)"),i.push("push the new branch to the remote"),i.push(`open a pull request${n?` against ${n}`:""} from the new branch with a concise title and a description of what changed and why (use \`gh pr create\`)`);let c=[];s.has("commit")||c.push("don't create any new commits \u2014 ship what's already committed"),o&&!s.has("rebase")&&c.push("don't rebase \u2014 leave the branch's history as it is");let l=`Yes \u2014 go ahead, as a NEW pull request: ${i.join(", then ")}.`;return c.length&&(l+=` ${c.join(", and ").replace(/^./,u=>u.toUpperCase())}.`),l+=` Leave the existing open PR${r?` #${r}`:""}${t?` and its remote branch (${t})`:" and its remote branch"} exactly as they are \u2014 don't push ${t||"the old branch"} itself.`,l}function Nk(e,t=Date.now()){let n=[],r=null;for(let o of e||[]){let s=o.payload||{},a=s.ts??o.ts;if(o.type==="pr.offer.answered"&&s.action==="pr"){r={box:o.boxId,from:a,lastActivity:a};continue}if(o.type==="pr.exists.answered"&&s.choice==="new"){r={box:o.boxId,from:a,lastActivity:a};continue}!r||a==null||a<r.from||(o.type==="pr.offer.answered"||o.type==="pr.exists.answered"?r=null:o.type==="pr.state.changed"&&s.change==="opened"?(n.push({box:r.box,from:r.from,to:a,settled:!0}),r=null):o.type==="turn.user"&&o.source!=="hook"&&!s.kind&&String(s.text||"").trim()?r=null:o.boxId===r.box&&/^(?:turn|tool)\./.test(o.type)&&(r.lastActivity=Math.max(r.lastActivity,a)))}return r&&t-r.lastActivity<=$C&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function Pk(e,t){let n=new Set;for(let r of t||[]){if(!r.settled)continue;let o=null;for(let s of e||[])s.kind!=="message"||s.role!=="assistant"||s.box!==r.box||s.ts==null||s.ts<r.from||s.ts>r.to+OC||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function Dk(e){let t=(Array.isArray(e)?e:[]).filter(r=>Tk[r]).map(r=>Tk[r]);return`The ship you were given the go-ahead for${t.length?` (${t.join(", then ")})`:""} should now be complete. This is an automated ship notification the operator armed on the offer: carry straight on with this Track's follow-up work (dependent tasks, cleanup, next steps) \u2014 don't wait for review or merge. If a ship step failed or is unfinished, finish the ship first. If nothing needs doing, say so briefly.`}var Ek,Ak,_k,Ik,xC,SC,vC,TC,zo,AC,$k,_C,IC,RC,$C,OC,Tk,Yo=O(()=>{Ek=/(?<!\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,Ak=/(?<!\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,_k=/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,Ik=/(?<!\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,xC=new RegExp(`\\b(?:${Ek}|${Ak}|${_k}|${Ik})\\b`,"i"),SC=/\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,vC=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,TC=90;zo=["commit","rebase","push","pr"],AC=new RegExp(`\\b(?:${Ek})\\b`,"i"),$k=new RegExp(`\\b(?:${Ik})\\b`,"i"),_C=new RegExp(`\\b(?:${Ak})\\b`,"i"),IC=new RegExp(`\\b(?:${_k})\\b`,"i"),RC=/\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;$C=3e4;OC=2500;Tk={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import CC from"node:crypto";import Vc from"node:fs/promises";import Bk from"node:path";import MC from"node:os";async function Ce(){return ar||(Pf??=Vc.readFile(Bf,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(ar=e,!t&&Ee()){let{records:n,source:r}=await je("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;ar[Q(i.repo,i.number)]=i}Object.keys(ar).length&&await jf().catch(()=>{})}}return ar}).finally(()=>Pf=null),Pf)}async function jf(){let e=JSON.stringify({links:ar},null,2),t=`${Bf}.${process.pid}.tmp`;await Vc.mkdir(jk,{recursive:!0}),await Vc.writeFile(t,e),await Vc.rename(t,Bf)}async function Ue(e){Df=Df.then(jf,jf),await Df.catch(()=>{});let t=ar?.[e];t&&et("prlinks",{id:Lk(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function mt({repo:e,number:t,trackId:n,url:r=null,headRef:o=null,title:s=null,source:a="matched"}){if(!e||!Number.isInteger(Number(t))||!n)return{link:null,created:!1};let i=await Ce(),c=Q(e,t);return i[c]?{link:i[c],created:!1}:(i[c]={repo:e,number:Number(t),trackId:n,url:r,headRef:o,title:s,source:a,announced:NC.has(a),firstSeen:Date.now()},await Ue(Q(e,t)),{link:i[c],created:!0})}async function hn(e,t){if(!e||t==null)return null;let n=(await Ce())[Q(e,t)];return!n||n.announced?null:(n.announced=!0,await Ue(Q(e,t)),n)}async function qe(e,t){return!e||t==null?null:(await Ce())[Q(e,t)]??null}async function ir(e,t,n){if(!e||t==null)return null;let r=(await Ce())[Q(e,t)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,delete r.notifyOnMerge,delete r.notifyPending,await Ue(Q(e,t)),r):null}async function Fk(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ce())[Q(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Ue(Q(e,t)),o):null}async function Uk(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ce())[Q(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Ue(Q(e,t)),o):null}async function zc(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ce())[Q(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Ue(Q(e,t)),r)}async function Jc(e,t){if(!e||t==null)return null;let n=(await Ce())[Q(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Ue(Q(e,t)),n)}async function Yc(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ce())[Q(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Ue(Q(e,t)),r)}async function Xc(e,t){if(!e||t==null)return null;let n=(await Ce())[Q(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Ue(Q(e,t)),n)}async function Hk(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await Ce())[Q(e,t)];return r?(r.baseFoldAt=n,await Ue(Q(e,t)),r):null}async function Wk(e,t){if(!e||t==null)return null;let n=(await Ce())[Q(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Ue(Q(e,t)),n)}async function qk(e,t,n){if(!e||t==null||!n)return null;let r=(await Ce())[Q(e,t)];return r?(r.lastHeadSha=n,await Ue(Q(e,t)),r):null}async function Kk(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await Ce())[Q(e,t)];if(!r)return{prev:null,link:null};let o=r.verdict??null;return o&&o.blocked===(n.blocked??null)&&(o.sha??null)===(n.sha??null)||(r.verdict={blocked:n.blocked??null,sha:n.sha??null,at:n.at??Date.now()},await Ue(Q(e,t))),{prev:o,link:r}}async function Ra(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await Ce())[Q(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Ue(Q(e,t)),r):null}async function Yr(e,t,n=!0){if(!e||t==null)return null;let r=(await Ce())[Q(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Ue(Q(e,t)),r):null}async function $a(e,t,n=!0){if(!e||t==null)return null;let r=(await Ce())[Q(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Ue(Q(e,t)),r):null}async function Gk(e,t,n){if(!e||t==null)return null;let r=(await Ce())[Q(e,t)];if(!r)return null;r.notifyOnMerge=!0;let o=new Map((r.notifyPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.notifyPending=[...o.values()],await Ue(Q(e,t)),r}async function Oa(e,t){if(!e||t==null)return null;let n=(await Ce())[Q(e,t)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,await Ue(Q(e,t)),n)}async function Vk(){return Object.values(await Ce()).filter(e=>!PC.has(e.state))}async function Xr(){return Object.values(await Ce()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function zk(){if(!Ee())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await je("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Ce(),r=0;for(let[o,s]of Object.entries(n))await et("prlinks",{id:Lk(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var jk,Bf,Q,Lk,ar,Pf,Df,NC,PC,wn=O(()=>{Kn();jk=process.env.BOSTRAT_STATE_DIR||Bk.join(process.env.HOME||MC.tmpdir(),".bostrat"),Bf=Bk.join(jk,"pr-links.json"),Q=(e,t)=>`${e}#${Number(t)}`,Lk=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=CC.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},ar=null,Pf=null,Df=Promise.resolve();NC=new Set(["api","observed","reconciled"]);PC=new Set(["merged","closed","lost"])});import DC from"node:fs";import Lf from"node:fs/promises";import Zk from"node:path";import BC from"node:os";function t0(e,t=!1){if(!t&&e-Yk<jC)return;Yk=e;let n=JSON.stringify({windowStart:Xo,byCaller:Object.fromEntries(gn),lastHour:cr});Xk=Xk.then(()=>Lf.mkdir(Qk,{recursive:!0})).then(()=>Lf.writeFile(`${Zc}.tmp`,n)).then(()=>Lf.rename(`${Zc}.tmp`,Zc)).catch(()=>{})}function n0(e){e-Xo<e0||(cr={start:Xo,total:Qc(gn),byCaller:Object.fromEntries(gn)},cr.total&&console.log(`[github] ${cr.total} calls last hour`,cr.byCaller),Xo=e,gn=new Map,t0(e,!0))}function yn(e="other",t=Date.now()){n0(t),gn.set(e,(gn.get(e)||0)+1),t0(t)}function r0(e=Date.now()){return n0(e),{windowStart:Xo,total:Qc(gn),byCaller:Object.fromEntries(gn),lastHour:cr,backoffUntil:Zr>e?Zr:null}}function lr(e){return LC.test(String(e?.message||e||""))}function ur(e,t=Date.now()){return Zr<=t&&console.warn(`[github] rate limited \u2014 backing off ${Jk/6e4} min:`,String(e?.message||e||"").slice(0,200)),Zr=Math.max(Zr,t+Jk),Zr}function el(e=Date.now()){return Math.max(0,Zr-e)}var Qk,Zc,e0,Jk,jC,Xo,gn,cr,Zr,Yk,Qc,Xk,LC,Ca=O(()=>{Qk=process.env.BOSTRAT_STATE_DIR||Zk.join(process.env.HOME||BC.tmpdir(),".bostrat"),Zc=Zk.join(Qk,"github-metrics.json"),e0=60*6e4,Jk=5*6e4,jC=6e4,Xo=Date.now(),gn=new Map,cr=null,Zr=0,Yk=0,Qc=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(DC.readFileSync(Zc,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<e0?(Xo=e.windowStart,gn=new Map(Object.entries(e.byCaller||{})),cr=e.lastHour||null):Qc(new Map(Object.entries(e.byCaller||{})))&&(cr={start:e.windowStart,total:Qc(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}Xk=Promise.resolve();LC=/rate limit|too quickly|abuse detection|HTTP 429/i});import o0 from"node:fs/promises";import s0 from"node:path";import FC from"node:os";function nl({change:e,repo:t=null,number:n=null,githubAt:r=null,observedAt:o=Date.now(),lane:s=null,intervalMs:a=null,source:i=null}){let c=r?o-r:null,l={change:e,repo:t,number:n,githubAt:r,observedAt:o,latencyMs:c,lane:s,intervalMs:a,source:i};tl.push(l),tl.length>HC&&tl.shift();let u=Ff.get(e)||{count:0,measured:0,totalMs:0,maxMs:0};return u.count+=1,c!=null&&(u.measured+=1,u.totalMs+=c,u.maxMs=Math.max(u.maxMs,c)),Ff.set(e,u),WC(l),l}async function WC(e){try{await o0.mkdir(a0,{recursive:!0}),await o0.appendFile(UC,JSON.stringify(e)+`
|
|
139
|
-
`)}catch{}}function i0(){let e={};for(let[t,n]of Ff)e[t]={count:n.count,measured:n.measured,avgMs:n.measured?Math.round(n.totalMs/n.measured):null,maxMs:n.measured?n.maxMs:null};return{byChange:e,recent:tl.slice(-20)}}var a0,UC,HC,tl,Ff,rl=O(()=>{a0=process.env.BOSTRAT_STATE_DIR||s0.join(process.env.HOME||FC.tmpdir(),".bostrat"),UC=s0.join(a0,"github-latency.ndjson"),HC=100,tl=[],Ff=new Map});import{execFile as qC}from"node:child_process";import{promisify as KC}from"node:util";async function GC(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([Qo("git",["-C",e,"rev-parse","HEAD"]),Qo("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await _({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function vt(e,t,n={}){let{caller:r="prs",...o}=n,s=await qt({cwd:e});yn(r);try{let{stdout:a}=await Qo("gh",t,{cwd:e,...VC,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw lr(a)&&ur(a),a}}async function Wf(){if(ol&&Date.now()-ol.at<6e4)return ol.data;let e;try{yn("auth");let t=await Qo("gh",["auth","status"],{timeout:1e4});e={installed:!0,authenticated:!0,login:`${t.stdout||""}${t.stderr||""}`.match(/account\s+(\S+)/)?.[1]??null}}catch(t){e=t?.code==="ENOENT"||t?.code===127?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(e.installed&&!e.authenticated)try{let t=await qs();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return ol={at:Date.now(),data:e},e}async function zC(e){try{let{stdout:t}=await Qo("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function l0(e){let t=e.name||e.context||"check",n=e.detailsUrl||e.targetUrl||null,r=e.completedAt||e.createdAt||null,o;return e.__typename==="StatusContext"?o=e.state==="SUCCESS"?"pass":e.state==="PENDING"||e.state==="EXPECTED"?"pending":"fail":e.status&&e.status!=="COMPLETED"?o="pending":["SUCCESS","NEUTRAL","SKIPPED"].includes(e.conclusion)?o="pass":o="fail",{name:t,state:o,url:n,completedAt:r}}function Uf(e){let t=(e||[]).map(l0),n=t.filter(s=>s.state==="pending").length,r=t.map(s=>Date.parse(s.completedAt||"")).filter(s=>s>0),o=t.length&&!n&&r.length?Math.max(...r):null;return{total:t.length,passed:t.filter(s=>s.state==="pass").length,failed:t.filter(s=>s.state==="fail").length,pending:n,failing:t.filter(s=>s.state==="fail").map(({name:s,url:a})=>({name:s,url:a})),settledAt:o}}function Kf(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function d0(e,t){if(!t)return null;let n=await me(e);if(!n)return null;try{let r=await vt(e,["api",`repos/${n}/actions/runs?head_sha=${encodeURIComponent(t)}&per_page=100`,"--jq",".workflow_runs"]),o=new Map;for(let s of JSON.parse(r||"[]"))o.has(s.name)||o.set(s.name,s);return[...o.values()]}catch{return null}}function f0(e){return(e||[]).map(t=>({name:t.name,detailsUrl:t.html_url||null,status:String(t.status||"").toUpperCase(),conclusion:t.conclusion?String(t.conclusion).toUpperCase():null,completedAt:t.status==="completed"&&t.updated_at||null}))}async function p0(e,t){let n=await d0(e,t);return n===null?{...Uf([]),unavailable:!0}:Uf(f0(n))}function sl(e,t=Uf(e.statusCheckRollup)){return{number:e.number,title:e.title,url:e.url,author:e.author?.login??null,headRefName:e.headRefName,headSha:e.headRefOid??null,baseRefName:e.baseRefName,isDraft:!!e.isDraft,reviewDecision:e.reviewDecision||null,mergeable:e.mergeable||"UNKNOWN",mergeStateStatus:e.mergeStateStatus||"UNKNOWN",additions:e.additions??0,deletions:e.deletions??0,createdAt:e.createdAt,updatedAt:e.updatedAt,checks:t,green:t.failed===0&&t.pending===0&&e.mergeStateStatus==="CLEAN",autoMergeArmed:!!e.autoMergeRequest,mergeabilityPending:!1}}function YC(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){Zo.delete(s);continue}r.add(s),Zo.has(s)||Zo.set(s,n),o.mergeabilityPending=n-Zo.get(s)<JC}for(let o of Zo.keys())o.startsWith(`${e}#`)&&!r.has(o)&&Zo.delete(o);return t}async function es(e,t){let n=await bn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await vt(n.path,["pr","view",String(r),"--json",qf]);return sl(JSON.parse(o))}catch(o){if(!Kf(o))throw o;let s=await vt(n.path,["pr","view",String(r),"--json",u0]),a=JSON.parse(s);return sl(a,await p0(n.path,a.headRefOid))}}async function Vt(e,{caller:t="prs"}={}){try{let n=await vt(e,["pr","list","--json",qf,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>sl(r)).sort(c0)}catch(n){if(!Kf(n))throw n;let r=await vt(e,["pr","list","--json",u0,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>sl(o,await p0(e,o.headRefOid))))).sort(c0)}}function Jt(){Ma.clear()}async function QC(e,t){if(!e)return t;for(let n of t){let r=await qe(e,n.number).catch(()=>null);if(!r)continue;r.trackId&&(n.trackId=r.trackId),r.autoMerge&&(n.autoMergeArmed=!0),r.archiveOnMerge&&(n.archiveOnMerge=!0),r.notifyOnMerge&&(n.notifyOnMerge=!0);let o=s=>n.headSha&&s.headSha&&n.headSha!==s.headSha;r.ciFix&&(o(r.ciFix)?await zc(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await Yc(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function m0(){let e=await Wf();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await cn())await zC(a.path)&&t.push(a);let n=Date.now(),r=el(n)>0,o=await Promise.all(t.map(async a=>{let i=Ma.get(a.path),c=i?.pending?ZC:XC;if(i&&(r||n-i.at<c))return i.entry;try{let l=await me(a.path).catch(()=>null),u=YC(a.path,await Vt(a.path,{caller:"tab"}),n);if(await QC(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&&nl({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 Ma.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:zt(l)}}})),s=new Set(t.map(a=>a.path));for(let a of Ma.keys())s.has(a)||Ma.delete(a);return{auth:e,repos:o}}async function bn(e){let t=(await cn()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function Qr(e){return bo(e)}async function ts(e,t,{method:n="squash"}={}){let r=await bn(e);if(!Na.has(n))throw new Error(`invalid merge method: ${n}`);let o=Number(t);if(!Number.isInteger(o)||o<=0)throw new Error("invalid PR number");let s=["pr","merge",String(o),`--${n}`];if(n!=="rebase"&&(await qt({cwd:r.path})).GH_TOKEN){let a=await Bu().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await vt(r.path,s)}catch(a){throw new Error(zt(a))}finally{Jt()}return{ok:!0,number:o}}async function Pa(e,t){let n=await bn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await vt(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(zt(s))}finally{Jt()}let o=await me(n.path).catch(()=>null);return o&&await Hk(o,r).catch(()=>{}),{ok:!0,number:r}}async function Gf(e,t){let n=await bn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await vt(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(zt(o))}finally{Jt()}return{ok:!0,number:r}}function Vf(e){return e.green&&!e.isDraft}function rM(e){return e.isDraft?"draft":e.mergeable==="CONFLICTING"?`conflicts with ${e.baseRefName}`:e.checks.failed>0?"checks failing":e.checks.pending>0?"checks running":e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN"?"mergeability unresolved":e.mergeStateStatus==="BEHIND"?`behind ${e.baseRefName}`:e.mergeStateStatus==="BLOCKED"?"blocked by branch protection":`not clean (${e.mergeStateStatus})`}function h0(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:Vf(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function w0(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function g0(){let e={};for(let[t,n]of Hf)e[t]={...n};return e}async function y0(e,{method:t="merge",onMerged:n=null}={}){let r=await bn(e);if(!Na.has(t))throw new Error(`invalid merge method: ${t}`);if(Hf.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Vt(r.path)).filter(Vf).sort((a,i)=>Date.parse(a.createdAt)-Date.parse(i.createdAt));if(!o.length)throw new Error("no PRs ready to merge");let s={running:!0,startedAt:Date.now(),finishedAt:null,method:t,total:o.length,current:o[0].number,queue:o.map(a=>({number:a.number,title:a.title})),merged:[],skipped:[],failed:[]};return Hf.set(r.path,s),sM(s,o,{listPrs:()=>Vt(r.path),merge:a=>ts(r.path,a,{method:t}),sleep:nM,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function oM(e,t){let n=Date.now()+(t.settleMaxMs??tM),r=null;for(;;){let o=null;try{if(o=await t.listPrs(),r=null,!o.some(a=>e.includes(a.number)&&(a.mergeable==="UNKNOWN"||a.mergeStateStatus==="UNKNOWN")))return o}catch(s){r=s}if(Date.now()>=n){if(o)return o;if(r)throw r;return[]}await t.sleep(eM)}}async function sM(e,t,n){try{let r=t;for(let o=0;o<t.length;o++){let s=t[o];if(e.current=s.number,o>0)try{r=await oM(t.slice(o).map(i=>i.number),n)}catch(i){for(let c of t.slice(o))e.failed.push({number:c.number,title:c.title,reason:`couldn't re-check PR state: ${zt(i)}`});break}let a=r.find(i=>i.number===s.number);if(!a){e.skipped.push({number:s.number,title:s.title,reason:"no longer open"});continue}if(!Vf(a)){e.skipped.push({number:a.number,title:a.title,reason:rM(a)});continue}try{await n.merge(a.number),e.merged.push({number:a.number,title:a.title});try{await n.onMerged?.(a)}catch{}}catch(i){e.failed.push({number:a.number,title:a.title,reason:zt(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),Jt()}}async function zf(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await Qo("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${zt(l)}`)}await GC(e);let o=n||"";if((await qt({cwd:e})).GH_TOKEN){let l=await Bu().catch(()=>null);l&&(o=`${o?`${o}
|
|
140
|
-
|
|
141
|
-
`:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",t.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await vt(e,s)}catch(l){throw new Error(
|
|
113
|
+
`});function G$(e){return{id:e.id,kind:"prompt",groupId:e.groupId,provider:e.provider,box:e.box,agent:e.provider,questionTotal:e.questions.length,answeredVia:"chat",questions:e.questions.map((t,n)=>({index:n,header:t.header||"",question:t.question,multiSelect:!!t.multiSelect,chosen:[]})),ts:e.ts}}function z$(e,t,n){let r=Py(n),o=e.has(r)?r:null;if(!o&&n.number!=null)for(let[i,c]of e){if(Number(c.number)!==Number(n.number))continue;let l=!!(n.url&&c.url&&n.url===c.url),u=!c.repo&&!!n.repo||!!c.repo&&!n.repo;if(l||u&&(!c.url||!n.url||c.url===n.url)){o=i;break}}let s=n.repo||!o?r:o,a=o?e.get(o):e.get(s);if(o&&o!==s){e.delete(o);let i=t.get(o);if(t.delete(o),i?.length){let c=t.get(s)??[];t.set(s,c.concat(i))}}return e.set(s,{number:n.number,url:n.url??a?.url??null,title:n.title??a?.title??null,repo:n.repo??a?.repo??null,branch:n.branch??a?.branch??null}),s}function Zn(e){let t=[],n=new Set,r=new Set,o=new Map,s=new Map,a=new Set,i=new Map,c=new Set,l=new Map,u=new Map,d=(y,w)=>{u.set(y,w),t.push(w)},f=y=>{let w=u.get(y);w&&(w.retired=!0)},p=/^Agent "([\s\S]+)" finished$/,m=new Map;for(let y of e||[]){if(y.type!=="tool.called"||y.source!=="transcript")continue;let w=String(y.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(w))for(let v of w.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let $=v[0].split("/").pop().toLowerCase();m.has($)||m.set($,[]),m.get($).push(y.payload?.ts??y.ts)}}let h=3*6e4,g=20*6e4,x=(y,w)=>!!(y?.name&&m.get(String(y.name).toLowerCase()))?.some($=>$>=w-h&&$<=w+g);for(let y of e||[])if(y.type==="turn.retracted"){r.add(`${y.boxId}|${y.payload?.saidTs}`);let w=Oe(y.payload?.text);w&&(o.has(y.boxId)||o.set(y.boxId,[]),o.get(y.boxId).push(w))}else if(y.type==="turn.user"&&y.source==="orchestrator"){let w=Oe(y.payload?.text);w&&(s.has(y.boxId)||s.set(y.boxId,new Set),s.get(y.boxId).add(w))}else if(y.type==="job.started"&&y.payload?.id)a.add(y.payload.id),(y.payload.kind||"agent")==="agent"&&y.payload.name&&c.add(y.payload.name);else if(y.type==="turn.user"&&y.payload?.note?.kind==="task"){let w=y.payload.note,v={status:w.status||null,summary:w.summary||null,resultHead:w.resultHead??null,ts:de(y)};w.toolUseId&&i.set(w.toolUseId,v);let $=String(w.summary||"").match(p);$&&l.set($[1],v)}let k=new Map,A=new Map,I=null,M=new Map,N=new Map,O=new Map,T=new Map,j=new Map,Z=new Map,fe=new Map,ie=new Map,D=new Map,R=new Map,U=new Map,pe=new Map,Se=new Map,He=()=>{for(let y of Se.values())y.questions.length&&t.push(y);Se.clear()},le=null,q=6e4,J=new Map,ee=(y,w)=>{let v=o.get(y);if(!v?.length)return!1;let $=Oe(w),ve=s.get(y);return v.some(b=>{if(!$.startsWith(b))return!1;let P=Oe($.slice(b.length));return!!P&&!!ve?.has(P)})};for(let y of e||[]){let w=y.payload||{};switch(y.type){case"turn.user":{if(y.source==="hook"||!String(w.text||"").trim()&&!w.images?.length&&!w.attachments?.length)break;{let v=w.note||Bc(w.text);if(v){if(v.kind==="task"){if(v.toolUseId&&a.has(v.toolUseId))break;let $=String(v.summary||"").match(p);if(!v.toolUseId&&$&&c.has($[1]))break}if(v.kind==="command"&&Iy(v.command))break;if(v.kind==="resume-summary"){t.push({id:y.id,kind:"resume-summary",title:v.title||"Context resumed from summary",bullets:v.bullets||[],sections:v.sections||[],rawChars:v.rawChars??null,ts:de(y),box:y.boxId,agent:y.provider});break}v.kind!=="suppress"&&t.push({id:y.id,kind:"event",type:$y(v),name:v.name??null,command:v.command??null,summary:v.summary??null,status:v.status??null,ts:de(y),box:y.boxId,agent:y.provider});break}}if(r.has(`${y.boxId}|${de(y)}`)||y.source==="transcript"&&ee(y.boxId,w.text))break;if(w.kind==="merge-notify"||w.kind==="ship-notify"){t.push({id:y.id,kind:"event",type:w.kind==="ship-notify"?"ship-notified":"merge-notified",text:w.text,ts:de(y),box:y.boxId,agent:y.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"&&(le=null);break}for(let v of M.values())v.status="stale";M.clear();for(let v of O.values())v.status="stale";O.clear(),le&&le.box===y.boxId&&(t.push(G$(le)),le=null),t.push({id:y.id,kind:"message",role:"user",text:w.text,ts:de(y),box:y.boxId,agent:y.provider,boxRole:w.boxRole,...y.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"peer.message":{let v=w.from||{};t.push({id:y.id,kind:"event",type:"peer-message",text:w.text,from:v.trackName||v.boxId||v.name||null,fromTrackId:v.trackId||null,ts:de(y),box:y.boxId,agent:y.provider});break}case"turn.agent":{if(w.model==="<synthetic>"&&/^No response requested\.$/.test(String(w.text||"").trim()))break;let v=qg(w,{codexFailedText:jr});if(v){t.push({id:y.id,kind:"event",type:"provider-error",cls:v.cls,status:v.status??void 0,text:v.text,provider:y.provider,legacy:!0,at:de(y),ts:de(y),box:y.boxId,agent:y.provider});break}He();let $=(w.images||[]).filter(Me=>!x(Me,de(y)));if(!String(w.text||"").trim()&&!$.length)break;if(y.provider==="cursor"){let Me=`${y.boxId}|${w.text}`;if(n.has(Me))break;n.add(Me)}(!le||le.box===y.boxId)&&(le=null);let ve=y.provider==="codex"?(w.tools||[]).filter(Me=>!Ad.has(Me)&&!ga.has(Me)):w.tools||[],b=w.text,P=null;if(zd.has(y.provider)){let Me=Jd(String(w.text||""));Me&&(b=Me.text,P={questions:Me.questions})}t.push({id:y.id,kind:"message",role:"assistant",text:b,ts:de(y),box:y.boxId,agent:y.provider,boxRole:w.boxRole,...P?{ask:P}:{},...ve.length?{tools:ve}:{},...w.model?{model:w.model}:{},...w.sourceId?{sourceId:w.sourceId}:{},...$.length?{images:$}:{}}),P&&(le={id:y.id,groupId:Yd(y.provider,w.sourceId||y.id,P.questions),provider:y.provider,box:y.boxId,questions:P.questions,ts:de(y)});break}case"job.started":{if(!w.id)break;let v=de(y),$=i.get(w.id);if(!$&&(w.kind||"agent")==="agent"&&w.name){let We=l.get(w.name);We&&We.ts>=v&&($=We)}let ve={jobId:w.id,jobKind:w.kind||"agent",name:w.name||null,detail:w.detail||null,status:$?$.status==="killed"?"stopped":$.status&&$.status!=="completed"?"failed":"done":"running",startedTs:v,finishedTs:$?.ts??null,summary:$?.summary??null,resultHead:$?.resultHead??null},b=J.get(y.boxId),P=b?.jobs[b.jobs.length-1]?.startedTs;if(b&&P!=null&&v-P<=q)b.jobs.push(ve);else{let We={id:y.id,kind:"job-dock",dockId:w.id,ts:v,box:y.boxId,agent:y.provider,jobs:[ve]};J.set(y.boxId,We),t.push(We)}break}case"handoff.done":t.push(St(y,"lead-handoff"));break;case"subtask.delegated":t.push({...St(y,"subtask-delegated"),to:y.boxId,toAgent:y.provider});break;case"subtask.merged":t.push(St(y,"subtask-merged"));break;case"pr.state.changed":{if(!w.change)break;if(w.change==="opened"){for(let $ of M.values())$.status="stale";M.clear();for(let $ of O.values())$.status="stale";O.clear()}let v=St(y,`pr-${w.change}`);if(v.box=y.boxId??null,V$.has(w.change)&&w.number!=null){let $=z$(k,A,w);if(k.size>=2){for(let b of A.values())for(let P of b)P.grouped=!0;v.groupPrs=[...k.values()];let ve=[v];for(let b of k.keys())A.set(b,ve)}else{let ve=A.get($)??[];ve.push(v),A.set($,ve)}}else if(w.change==="merged"||w.change==="closed"||w.change==="lost"){let $=Py(w),ve=I&&!I.complete||k.size>=2;if(k.has($))k.delete($),A.delete($);else if(w.number!=null)for(let b of[...k.keys()])b.endsWith(`#${w.number}`)&&(k.delete(b),A.delete(b));if(ve&&w.number!=null){(!I||I.complete)&&(I={id:y.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:de(y),ts:de(y),box:y.boxId,agent:y.provider},t.push(I)),I.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:de(y)}),I.remaining=[...k.values()],I.complete=k.size===0;break}}t.push(v);break}case"pr.offered":{if(!w.offerId)break;for(let $ of M.values())$.status="stale";M.clear();for(let $ of O.values())$.status="stale";O.clear();let v={id:y.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,available:Array.isArray(w.available)?w.available:null,defaults:Array.isArray(w.defaults)?w.defaults:null,prState:w.prState||null,openPr:w.openPr&&typeof w.openPr=="object"?w.openPr:null,repos:Array.isArray(w.repos)&&w.repos.length?w.repos:null,status:"open",ts:de(y),box:y.boxId,agent:y.provider};M.set(w.offerId,v),N.set(w.offerId,v),t.push(v);break}case"pr.offer.answered":{let v=N.get(w.offerId);v&&(v.status=w.action||"dismiss",v.actions=Array.isArray(w.actions)?w.actions:null,v.notify=w.notify===!0,w.existingPr&&typeof w.existingPr=="object"&&(v.existingPr={number:w.existingPr.number??null}),M.delete(w.offerId));break}case"pr.exists.offered":{if(!w.offerId)break;for(let $ of O.values())$.status="stale";O.clear();let v={id:y.id,kind:"pr-exists-offer",offerId:w.offerId,srcOfferId:w.srcOfferId??null,branch:w.branch||null,base:w.base||null,pr:w.pr&&typeof w.pr=="object"?{number:w.pr.number??null,url:w.pr.url??null,title:w.pr.title??null}:null,actions:Array.isArray(w.actions)&&w.actions.length?w.actions:null,notify:w.notify===!0,status:"open",ts:de(y),box:y.boxId,agent:y.provider};O.set(w.offerId,v),T.set(w.offerId,v),t.push(v);break}case"pr.exists.answered":{let v=T.get(w.offerId);v&&(v.status=w.choice||"dismiss",O.delete(w.offerId));break}case"box.state.changed":{let v=w.change==="model"?"model-change":w.change;if(!v)break;let $=St(y,v);if(v==="model-change"&&($.to=w.model),v==="node-update"&&$.latest){let ve=t.findIndex(b=>b.kind==="event"&&b.type==="node-update"&&b.latest===$.latest);if(ve>=0){let b=t[ve];t[ve]={...$,ts:b.ts,at:b.at,firstAt:b.firstAt??b.ts,updatedAt:$.ts};break}}t.push($);break}case"track.transferred":case"track.recovered":t.push(St(y,"track-moved"));break;case"view.state.changed":{if(w.change==="deleted"){f(`view:${w.viewId}`);break}if(w.change!=="created"||w.restarted)break;d(`view:${w.viewId}`,{id:y.id,kind:"view-card",viewId:w.viewId,name:w.name,slug:w.slug,port:w.port,origin:w.origin,ts:de(y),box:y.boxId,agent:y.provider});break}case"view.spinup.failed":{t.push({id:y.id,kind:"view-spinup-failed",name:w.name||null,reason:w.reason||"no-view",ts:de(y),box:y.boxId,agent:y.provider});break}case"mock.state.changed":{if(w.change==="picked"){t.push({id:y.id,kind:"mock-picked",mockId:w.mockId,name:w.name,group:w.group??null,version:w.version??null,siblings:w.siblings??[],ts:de(y),box:y.boxId,agent:y.provider});break}if(w.change==="deleted"){f(`mock:${w.mockId}`);break}if(w.change!=="created")break;d(`mock:${w.mockId}`,{id:y.id,kind:"mock-card",mockId:w.mockId,name:w.name,group:w.group??null,ts:de(y),box:y.boxId,agent:y.provider});break}case"flow.state.changed":{if(w.change==="deleted"){f(`flow:${w.flowId}`);break}if(w.change!=="created")break;d(`flow:${w.flowId}`,{id:y.id,kind:"flow-card",flowId:w.flowId,name:w.name,stepCount:w.stepCount??null,mockIds:w.mockIds??[],ts:de(y),box:y.boxId,agent:y.provider});break}case"runbook.state.changed":{if(w.change==="deleted"){f(`runbook:${w.runbookId}`);break}if(w.change!=="created")break;d(`runbook:${w.runbookId}`,{id:y.id,kind:"runbook-card",runbookId:w.runbookId,name:w.name,ts:de(y),box:y.boxId,agent:y.provider});break}case"ticket.linked":{t.push({id:y.id,kind:"ticket-card",provider:w.provider||"linear",key:w.key,url:w.url,title:w.title??null,linkSource:w.linkSource??null,ts:de(y),box:y.boxId,agent:y.provider});break}case"box.fullscreen.detected":{if(!w.offerId)break;for(let $ of fe.values())$.box===(w.box||y.boxId)&&($.status="stale",fe.delete($.offerId));let v={id:y.id,kind:"fullscreen-offer",offerId:w.offerId,status:"open",ts:de(y),box:w.box||y.boxId,agent:w.agent||y.provider};fe.set(w.offerId,v),ie.set(w.offerId,v),t.push(v);break}case"box.fullscreen.answered":{let v=ie.get(w.offerId);v&&(v.status=w.action==="restarted"?"restarted":w.action==="cleared"?"cleared":"dismiss",fe.delete(w.offerId));break}case"ticket.move.offered":{if(!w.offerId)break;for(let $ of j.values())$.key===w.key&&($.status="stale",j.delete($.offerId));let v={id:y.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:de(y),box:y.boxId,agent:y.provider};j.set(w.offerId,v),Z.set(w.offerId,v),t.push(v);break}case"ticket.move.answered":{let v=Z.get(w.offerId);v&&(v.status=w.action==="moved"?"moved":"dismiss",j.delete(w.offerId));break}case"ticket.state.changed":{for(let v of j.values())v.key===w.key&&v.to===w.to&&(v.status="stale",j.delete(v.offerId));if(w.mode==="offer")break;t.push(St(y,"ticket-state"));break}case"ticket.commented":{t.push({id:y.id,kind:"ticket-comment",key:w.key,url:w.url??null,excerpt:w.excerpt??null,ts:de(y),box:y.boxId,agent:y.provider});break}case"ticket.flow.changed":t.push(St(y,"ticket-flow"));break;case"ticket.create.offered":{if(!w.proposalId)break;for(let $ of D.values())$.status="stale";D.clear();let v={id:y.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:de(y),box:y.boxId,agent:y.provider};D.set(w.proposalId,v),R.set(w.proposalId,v),t.push(v);break}case"ticket.create.answered":{let v=R.get(w.proposalId);v&&(v.status=w.action==="created"?"created":"dismiss",v.createdCount=w.createdCount??null,v.failedCount=w.failedCount??null,D.delete(w.proposalId));break}case"loop.create.offered":{if(!w.proposalId)break;for(let $ of U.values())$.status="stale";U.clear();let v={id:y.id,kind:"loop-create-offer",proposalId:w.proposalId,name:w.name??"",schedule:w.schedule??"",prompt:w.prompt??"",expectation:w.expectation??null,notify:w.notify??"off",assignment:w.assignment??{provider:"claude"},repo:w.repo??null,nodeId:w.nodeId??null,nodeName:w.nodeName??null,status:"open",ts:de(y),box:y.boxId,agent:y.provider};U.set(w.proposalId,v),pe.set(w.proposalId,v),t.push(v);break}case"loop.create.answered":{let v=pe.get(w.proposalId);v&&(v.status=w.action==="created"?"created":"dismiss",U.delete(w.proposalId));break}case"loop.created":{let v=w.proposalId?pe.get(w.proposalId):null;v&&(v.created={loopId:w.loopId??null,nextFireAt:Number.isFinite(Number(w.nextFireAt))?Number(w.nextFireAt):null});break}case"ticket.created":{let v={items:Array.isArray(w.items)?w.items:[],failed:Array.isArray(w.failed)?w.failed:[],team:w.team??null},$=w.proposalId?R.get(w.proposalId):null;if($){$.created=v;break}t.push({id:y.id,kind:"ticket-created",...v,ts:de(y),box:y.boxId,agent:y.provider});break}case"error":{if(!w.kind)break;t.push(St(y,w.kind));break}case"provider.error":t.push({...St(y,"provider-error"),provider:y.provider,box:y.boxId,agent:y.provider});break;case"usage.cap.hit":t.push(St(y,"usage-cap-hit"));break;case"usage.cap.warning":t.push(St(y,"usage-cap-warning"));break;case"provider.connected":{t.push({id:y.id,kind:"event",type:"provider-connected",provider:w.provider||y.provider,ts:de(y),at:de(y),box:y.boxId,agent:y.provider});break}case"prompt.answered":{if(le=null,w.control||!w.question)break;let v=w.groupId||w.promptId;if(!v)break;let $=Se.get(v);$||($={id:y.id,kind:"prompt",groupId:v,provider:y.provider,box:y.boxId,agent:y.provider,questionTotal:w.questionTotal??1,questions:[],ts:de(y)},Se.set(v,$));let ve=w.questionIndex??0,b={index:ve,header:w.header||"",question:w.question,multiSelect:!!w.multiSelect,chosen:Array.isArray(w.chosen)?w.chosen:[]},P=$.questions.findIndex(We=>We.index===ve&&Oe(We.question)===Oe(b.question));P>=0?$.questions[P]=b:$.questions.push(b),$.questions.sort((We,Ht)=>We.index-Ht.index),$.questionTotal=Math.max($.questionTotal,w.questionTotal??1),$.questions.length>=$.questionTotal&&(t.push($),Se.delete(v));break}default:break}}let Ze=u.size?t.filter(y=>!y.retired):t;return Y$(Ze.sort((y,w)=>(y.ts??0)-(w.ts??0)))}function Y$(e){let t=new Map,n=[];for(let r of e){if(r.kind!=="message"||!r.box){n.push(r);continue}let o=r.role==="assistant"&&!String(r.text||"").trim()&&r.images?.length,s=t.get(r.box);if(o&&s&&(r.ts??0)-s.lastTs<=J$){s.item.images.push(...r.images),s.lastTs=r.ts??s.lastTs;continue}if(o){let a={...r,images:[...r.images]};t.set(r.box,{item:a,lastTs:r.ts??0}),n.push(a)}else t.delete(r.box),n.push(r)}return n}function Qd(e,t=null){let n=[];for(let o of e||[]){if(o.type!=="tool.called"||o.source!=="transcript")continue;let s=o.payload||{};String(s.command||"").trim()&&n.push({id:s.id??null,tool:s.tool||null,command:s.command,description:s.description||"",summary:X$(s),ts:s.ts??o.ts,n:s.n,box:o.boxId,agent:o.provider,boxRole:s.boxRole})}if(!n.length)return[];let r=(t??Zn(e)).filter(o=>o.kind==="message");return Dg(n,r)}function X$(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==Bo(e.command)?t:Yn(e.command,e.description||"")}function St(e,t){let{ts:n,change:r,...o}=e.payload||{};return{id:e.id,kind:"event",type:t,...o,at:de(e),ts:de(e)}}function Dy(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||r.type!=="archived")continue;let o=(r.ts??0)+Z$,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&Q$.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function By(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!tO.has(r.type)||!r.box)continue;let o=(r.ts??0)+eO,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||!(a?.kind==="message"&&a.role==="assistant"&&a.box===r.box||a?.kind==="command-batch"&&a.anchorBox===r.box))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function jy(e){return(e||[]).filter(t=>nO.has(t?.kind)&&t.status==="open")}var de,V$,Py,J$,Z$,Q$,eO,tO,nO,ef=C(()=>{jo();Lr();Tc();Cc();Vd();Zd();de=e=>e.payload?.ts??e.ts;V$=new Set(["opened","ci-fixed","reconciled","pushed"]),Py=e=>`${e.repo??""}#${e.number}`;J$=2*6e4;Z$=12e4,Q$=new Set(["archived","unarchived","opened"]);eO=2500,tO=new Set(["pr-opened","pr-merged","pr-auto-armed"]);nO=new Set(["pr-offer","pr-exists-offer","ticket-offer","ticket-create-offer","loop-create-offer","fullscreen-offer"])});function iO(e){let t=null,n=null,r=null;for(let o of e??[]){let s=o?.ts??null;s&&(r=Math.max(r??0,s)),o?.type==="handoff.artifact"&&(t={ts:o.payload?.ts??s,truncated:!!o.payload?.truncated,...o.payload?.reconstructed?{reconstructed:!0}:{}}),o?.type==="code.pushed"&&(n={ts:o.payload?.ts??s,sha:o.payload?.sha??null,ref:o.payload?.ref??null,wip:!!o.payload?.wip})}return{lastArtifact:t,lastPush:n,lastEventAt:r}}function lO(e){for(let t=(e?.length??0)-1;t>=0;t--){let n=e[t];if(n?.type==="handoff.artifact"&&!n.payload?.reconstructed)return n}return null}function uO(e,t){let n=[],r=[],o=new Set,s=new Set;for(let a=(e?.length??0)-1;a>=0;a--){let i=e[a];if(i?.type!=="tool.called")continue;if(t!=null&&(i.ts??0)<=t)break;let c=i.payload||{};c.path&&!o.has(c.path)&&n.length<sO&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<aO&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function Hy(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=iO(e),s=lO(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=Zn(e??[]).filter(f=>f?.kind==="message"&&typeof f.text=="string"&&f.text.trim()).slice(-rO);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(Uy[r]??Uy.failover),l.push(""),o.lastPush?.sha){let f=Fy(n,o.lastPush.ts),p=f?` (~${f} min before the loss)`:"";l.push(`**Code state:** last pushed ref \`${o.lastPush.ref}\` @ \`${o.lastPush.sha.slice(0,12)}\`${p}`+(o.lastPush.wip?" \u2014 a bostrat WIP snapshot (full working tree incl. uncommitted edits at push time).":"."))}else r==="in-place"?l.push("**Code state:** no push recorded \u2014 the working tree in this workspace is the only copy; commit early."):l.push("**Code state:** no push was ever recorded for this Track \u2014 work since the branch's last known state stayed on the offline node.");if(l.push(""),a){let f=Fy(n,i);l.push(`## Last real checkpoint (written by the previous agent${f?`, ~${f} min before this reconstruction`:""})`),l.push(""),l.push(a.trim()),l.push("")}let u=uO(e??[],i);if((u.files.length||u.heads.length)&&(l.push(`## Work since ${a?"that checkpoint":"the start of the log"} (from the event log)`),l.push(""),u.files.length&&l.push(`Files touched (most recent first): ${u.files.map(f=>`\`${f}\``).join(", ")}`),u.heads.length&&(u.files.length&&l.push(""),l.push(`Commands run (most recent first): ${u.heads.map(f=>`\`${f}\``).join(" \xB7 ")}`)),l.push("")),c.length){l.push(`## Recent conversation (last ${c.length} message${c.length===1?"":"s"})`),l.push("");for(let f of c)l.push(`**${f.role==="user"?"operator":"agent"}:** ${cO(f.text,oO)}`),l.push("")}l.push("---"),l.push("Resume: continue the objective above from the verified repository state; ask the operator before redoing anything the log says was already done.");let d=l.join(`
|
|
114
|
+
`);return d.length<=Ly?d:`${d.slice(0,Ly)}
|
|
115
|
+
|
|
116
|
+
[\u2026 brief truncated]`}var rO,oO,Ly,sO,aO,cO,Fy,Uy,Wy=C(()=>{ef();rO=12,oO=2e3,Ly=48e3,sO=30,aO=10;cO=(e,t)=>{let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1)}\u2026`},Fy=(e,t)=>e&&t?Math.max(1,Math.round((e-t)/6e4)):null;Uy={failover:"**This is a reconstruction, not a checkpoint.** The node this Track lived on went offline without a clean handoff; bostrat assembled this brief from the durable event log. Treat every claim below as a lead, not a fact \u2014 **verify the repository state first** (git status, git log, the diff against the last pushed ref) before continuing work.","in-place":"**This is a reconstruction, not a checkpoint.** The previous lead ended without writing one (out of credits, capped, or unresponsive); bostrat assembled this status from the Track's event log. You are in the SAME live workspace \u2014 uncommitted work is present locally. **Run git status and git log first** and treat every claim below as a lead, not a fact.",transfer:"**This Track was MOVED to a new machine while it was alive.** The previous node checkpointed and pushed its work as part of the move; this workspace was re-created from that push (uncommitted work re-applied when possible). **Run git status and git log to confirm what carried over**, then continue the objective below."}});import dO from"node:fs";import Qn from"node:fs/promises";import er from"node:path";function Xy(e){let t=Nr(String(e??"")),n=Buffer.byteLength(t,"utf8");return n<=qy?{content:t,bytes:n,truncated:!1}:{content:`${Buffer.from(t,"utf8").subarray(0,qy).toString("utf8").replace(/�+$/,"")}
|
|
117
|
+
|
|
118
|
+
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function kO(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(Gy.test(s)){if(r.length>=rb)continue;let a=bO(s.replace(Gy,"").replace(/[.;,:]+$/,""),ob,gO);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>Ky&&(o=`${o.slice(0,Ky-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function sb(e,t,{timeoutMs:n=9e4}={}){let r=er.join(t,hO),o=er.join(r,wO);await Qn.mkdir(r,{recursive:!0});let s=await jc(o),a=await ae(e);if(!a)throw new Error(`session "${e}" not found`);return await Lc(e,a,yO(o)),await fb(o,s,n),kO(await Qn.readFile(o,"utf8"))}function ab(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===Qy||n===nb;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(Zy)||n===fO||r.startsWith(eb)||r.startsWith(tb)}function ib(e){let t=[];for(let r of e?.events||[]){if(r.type!=="lead-handoff"||!r.from)continue;let o=r.at??Date.now(),s=r.handoffStartedAt??o-xO;t.push({box:r.from,from:s,to:o}),r.to&&r.to!==r.from&&t.push({box:r.to,from:s,to:o})}let n=e?.handoff;return n?.fromBox&&n.startedAt&&n.finishedAt&&t.push({box:n.fromBox,from:n.startedAt,to:n.finishedAt}),n?.toBox&&n.startedAt&&n.finishedAt&&t.push({box:n.toBox,from:n.startedAt,to:n.finishedAt}),t}function cb(e,t){let n=[];for(let r of e?.boxes||[]){let o=t?.(r.name);o?.startedAt&&n.push({box:r.name,from:o.startedAt,to:o.finishedAt||Date.now()+1e12})}return n}function lb(e,{windows:t=[]}={}){if(ab(e.role,e.text))return!0;if(e.role!=="assistant"||!e.ts)return!1;for(let n of t)if(e.box===n.box&&e.ts>=n.from&&e.ts<=n.to)return!0;return!1}function Fc(e){return tf.get(e)||null}async function ub(e){let t=tf.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await ae(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);let r={name:e,step:"summarize",startedAt:Date.now(),finishedAt:null,error:null,file:er.join(n.cwd,Jy,Yy)};return tf.set(e,r),TO(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function TO(e,t){e.step="summarize",await db(e.name,t),e.step="clear";let n=await ae(e.name);await Lc(e.name,n,"/clear"),await of(SO),e.step="resume",await Lc(e.name,n,mO),e.step="done",e.finishedAt=Date.now()}async function db(e,t,{timeoutMs:n=vO}={}){let r=er.join(t,Jy),o=er.join(r,Yy);await Qn.mkdir(r,{recursive:!0});let s=await jc(o),a=await ae(e);await Y({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await Lc(e,a,pO(o)),await fb(o,s,n),await Y({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await jc(o)});let i=await xy(t,o,{exclude:u=>ab(u.role,u.text)});await Y({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(c){await _({id:c,cwd:t},{type:"memory.seed",actor:"orchestrator",payload:{...i,tokensEst:i.chars?Math.round(i.chars/4):0,ts:Date.now()}}).catch(()=>{});try{let u=await Qn.readFile(o,"utf8");await _({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...Xy(u),ts:Date.now()}})}catch{}}await td(t);let l=await AO(r,o);return c&&await cg(c,l||o,{box:e,agent:a?.agent??null}),o}async function nf(e,{timeoutMs:t}={}){let n=await ae(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return db(e,n.cwd,t?{timeoutMs:t}:void 0)}async function rf(e,t,{trackName:n="this Track"}={}){let r=await L({id:e,cwd:t}),o=Hy(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=er.join(t,Vy);return await Qn.mkdir(er.dirname(s),{recursive:!0}),await Qn.writeFile(s,o,"utf8"),await _({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...Xy(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),Vy}async function jc(e){try{return(await Qn.stat(e)).mtimeMs}catch{return 0}}async function fb(e,t,n){let r=Date.now()+n;for(;;){if(await jc(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await of(2e3)}}async function AO(e,t){try{if(!dO.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=er.join(e,`${n}.md`);return await Qn.copyFile(t,r),r}catch{return null}}function of(e){return new Promise(t=>setTimeout(t,e))}function zy(e){return e==="claude"?process.env.BOSTRAT_AGENT_CMD||"claude":e==="cursor"?process.env.BOSTRAT_CURSOR_CMD||"cursor-agent":e==="codex"?process.env.BOSTRAT_CODEX_CMD||"codex":""}async function Lc(e,t,n){if(t){let o=zy(t.agent);await Xn({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await ae(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await Ie(e,["Escape"],{kind:"drive-wipe"}),await of(150),await Mo(e,xc,{kind:"drive-wipe"}),await Le(e,n,{kind:"orchestration-drive",...zt(r.agent,n)}),await Aa({sessionName:e,agent:r.agent,agentCmd:zy(r.agent),text:n,flow:"orchestration-drive"})}var Jy,Yy,qy,Zy,Qy,eb,fO,pO,mO,tb,nb,hO,wO,Ky,rb,ob,gO,yO,Gy,bO,xO,SO,vO,tf,Vy,sf=C(()=>{Ve();xa();Gt();Sa();Fd();Sy();na();ue();pc();Wy();Jy=".bostrat/handoff",Yy="latest.md",qy=32*1024;Zy="Checkpoint: write a work-in-progress handoff",Qy="HANDOFF WRITTEN",eb="Resume from .bostrat/handoff/latest.md",fO="/clear",pO=e=>`${Zy} to the EXACT absolute path ${e} (write it there specifically \u2014 NOT into any sub-project or other directory; create the directory if needed, overwrite the file) so a FRESH Claude context with none of this conversation can resume seamlessly. Use these sections: Objective; Done (with file paths); In progress (exact state); Next steps (ordered); Key files & commands; Open decisions/blockers; Resume instruction (one line). Be concrete and self-contained \u2014 assume the reader has none of this chat. When the file is written, reply only with ${Qy} and take no further action.`,mO=`${eb}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,tb="Beat: write a",nb="BEAT WRITTEN",hO=".bostrat/arc",wO="beat.txt",Ky=200,rb=4,ob=5,gO=36,yO=e=>`${tb} short summary of what this workstream accomplished, to the EXACT absolute path ${e} (write it there specifically \u2014 NOT into any sub-project or other directory; create the directory if needed, overwrite the file). Format the file as: FIRST LINE = one sentence of at most 18 words, plain past tense, saying the outcome \u2014 no heading, no bullet, no trailing list. THEN, only if this workstream covered several separable pieces of work, up to ${rb} further lines, each starting with "- " and naming ONE piece in at most ${ob} 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 ${nb} and take no further action.`,Gy=/^(?:[-–—*•·]|\d+[.)])\s+/,bO=(e,t,n)=>{let r=e.split(/\s+/).filter(Boolean).slice(0,t).join(" ");return r.length>n?`${r.slice(0,n-1).trimEnd()}\u2026`:r};xO=6e5;SO=3500,vO=3e5,tf=new Map;Vy=".bostrat/handoff/status-latest.md"});function nr(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!Uc.test(String(e)))throw new Error(`pricing.js: ${n} window ${t} must be YYYY-MM-DD, got ${JSON.stringify(e)}`);let r=Date.parse(`${e}T${t==="from"?"00:00:00.000":"23:59:59.999"}Z`);if(!Number.isFinite(r))throw new Error(`pricing.js: ${n} window ${t} is not a real date: ${e}`);return r}function hb(e){let t={};for(let[n,r]of Object.entries(e)){if(!Array.isArray(r)||r.length===0)throw new Error(`pricing.js: ${n} must be a non-empty array of rate windows`);t[n]=r.map(o=>({...o,fromMs:nr(o.from,"from",n),toMs:nr(o.to,"to",n)}))}return t}function cf(e){if(e==null)return Date.now();if(typeof e=="number")return Number.isFinite(e)?e:null;if(e instanceof Date){let n=e.getTime();return Number.isFinite(n)?n:null}let t=Date.parse(String(e));return Number.isFinite(t)?t:null}function _O(e){if(!e)return null;let t=String(e).trim();return tr[t]?t:pb[t]?pb[t]:Object.keys(tr).find(r=>t.startsWith(r))||null}function IO(e){if(!e||e===tr)return EO;let t=mb.get(e);return t||(t=hb(e),mb.set(e,t)),t}function wb(e,t,n){let r=IO(n),o=n&&n!==tr?r[e]?e:null:_O(e);if(!o)return null;let s=cf(t);return s===null?null:r[o].find(a=>s>=a.fromMs&&s<=a.toMs)||null}function rr(e,t={},n,r){let o=wb(e,n,r);return o?((t.input||0)*o.input+(t.output||0)*o.output+(t.cacheWrite||0)*o.cacheWrite+(t.cacheRead||0)*o.cacheRead)/1e6:0}function RO(e=tr){let t=[];for(let[n,r]of Object.entries(e)){if(!Array.isArray(r)||r.length===0){t.push(`${n}: must be a non-empty array of windows`);continue}let o;try{o=r.map(a=>({fromMs:nr(a.from,"from",n),toMs:nr(a.to,"to",n)}))}catch(a){t.push(String(a.message));continue}let s=r.filter(a=>a.to==null);s.length!==1?t.push(`${n}: expected exactly 1 open-ended window (to: null), found ${s.length}`):r[r.length-1].to!=null&&t.push(`${n}: the open-ended window must be last`),r[0].from!=null&&t.push(`${n}: the first window must be open at the start (from: null)`);for(let a=0;a<o.length;a++){if(o[a].fromMs>o[a].toMs&&t.push(`${n}: window ${a} ends before it begins (${r[a].from} \u2192 ${r[a].to})`),a===0)continue;let i=o[a-1],c=o[a];c.fromMs<=i.toMs?t.push(`${n}: windows ${a-1} and ${a} overlap (${r[a-1].to} / ${r[a].from})`):c.fromMs-i.toMs>2&&t.push(`${n}: gap between windows ${a-1} and ${a} (${r[a-1].to} \u2192 ${r[a].from})`),r[a].from||t.push(`${n}: window ${a} must declare a from bound`)}for(let a of r){a.note||t.push(`${n}: every window needs a note recording what was verified and when`),Uc.test(String(a.verifiedAt??""))||t.push(`${n}: every window needs a verifiedAt (YYYY-MM-DD) \u2014 the date its rates were checked against the vendor's published page`);for(let i of["input","output","cacheWrite","cacheRead"])(typeof a[i]!="number"||!Number.isFinite(a[i])||a[i]<0)&&t.push(`${n}: window ${i} must be a non-negative number, got ${JSON.stringify(a[i])}`)}}return t}function OO({now:e=Date.now(),table:t=tr,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...$O,...n},a=[],i=cf(e);if(i===null)return[`pricingFreshness: unparseable now ${JSON.stringify(e)}`];for(let[c,l]of Object.entries(t)){if(!Array.isArray(l))continue;let u;try{u=l.map(d=>({...d,fromMs:nr(d.from,"from",c),toMs:nr(d.to,"to",c),verifiedMs:Uc.test(String(d.verifiedAt??""))?Date.parse(`${d.verifiedAt}T00:00:00.000Z`):NaN}))}catch(d){a.push(String(d.message));continue}for(let d of u){if(!Number.isFinite(d.verifiedMs)){a.push(`${c}: window ${d.from??"\u2026"} \u2192 ${d.to??"now"} has no verifiedAt`);continue}let f=i>=d.fromMs&&i<=d.toMs,p=d.fromMs>i&&d.fromMs-i<=o*af;f&&i-d.verifiedMs>r*af&&a.push(`${c}: current rate last verified ${d.verifiedAt} (>${r}d) \u2014 re-check the vendor page and bump verifiedAt`),p&&d.fromMs-d.verifiedMs>s*af&&a.push(`${c}: scheduled rate change on ${d.from} was last verified ${d.verifiedAt} \u2014 confirm it is still happening (vendors cancel; sonnet-5 did) and bump verifiedAt`)}}return a}function Wo({now:e=Date.now(),table:t=tr,...n}={}){let r=cf(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=nr(l.from,"from",i),d=nr(l.to,"to",i);r>=u&&r<=d&&Uc.test(String(l.verifiedAt??""))&&(!o||l.verifiedAt<o)&&(o=l.verifiedAt),u>r&&s.push({model:i,from:l.from,verifiedAt:l.verifiedAt??null})}s.sort((i,c)=>i.from<c.from?-1:1);let a=[...RO(t),...OO({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var tr,pb,Uc,EO,mb,gb,$O,af,or=C(()=>{tr={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,verifiedAt:"2026-08-17",note:"GA rates; verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-08-17",note:"$2/$10 is the standard price (intro pricing made permanent; the 2026-09-01 $3/$15 increase was cancelled) \u2014 verified 2026-08-17 against platform.claude.com/docs/en/about-claude/pricing."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-fable-5":[{from:null,to:null,input:10,output:50,cacheWrite:20,cacheRead:1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); 83% of fleet spend."}],"gpt-5.5":[{from:null,to:null,input:5,output:30,cacheWrite:5,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-03, re-verified 2026-08-17 (developers.openai.com pricing); no cache-write premium on 5.5-and-earlier."}],"gpt-5.6-sol":[{from:null,to:null,input:5,output:30,cacheWrite:6.25,cacheRead:.5,verifiedAt:"2026-08-17",note:"verified 2026-07-18, re-verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-terra":[{from:null,to:"2026-08-16",input:2.5,output:15,cacheWrite:3.125,cacheRead:.25,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:2,output:12,cacheWrite:2.5,cacheRead:.2,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}],"gpt-5.6-luna":[{from:null,to:"2026-08-16",input:1,output:6,cacheWrite:1.25,cacheRead:.1,verifiedAt:"2026-07-18",note:"as verified 2026-07-18; superseded by the 2026-08-17 sweep (0 corpus rows in this window)."},{from:"2026-08-17",to:null,input:.2,output:1.2,cacheWrite:.25,cacheRead:.02,verifiedAt:"2026-08-17",note:"verified 2026-08-17 (developers.openai.com pricing); cacheWrite = 1.25\xD7 input (30-min TTL)."}]},pb={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"},Uc=/^\d{4}-\d{2}-\d{2}$/;EO=hb(tr);mb=new WeakMap;gb=(e,t)=>wb(e,t)!==null;$O=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),af=864e5});function CO(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function bb(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function mn(e){let t=e.payload?.model||"unknown",n=CO(e.payload?.tokens),r=Number(e.payload?.usd)||0,o=e.source==="vendor",s=r,a=!1;if(n&&!o){let l=rr(t,n,e.ts);l>0&&(s=l,a=Math.abs(l-r)>1e-9)}let i=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,c=!s&&i>0&&!gb(t,e.ts);return{usd:s,model:t,tokens:n,repriced:a,unpriced:c,asRecorded:r}}function sr(e,{boxLabels:t={}}={}){let n=(e||[]).filter(f=>f.type==="cost.recorded"),r={},o={},s={},a=0,i=0,c=0,l=0,u=new Set,d=yb();for(let f of n){let{usd:p,model:m,tokens:h,repriced:g,unpriced:x,asRecorded:k}=mn(f);i+=k;let A=f.provider||"unknown",I=f.boxId||"unattributed";if(g&&c++,x&&(l++,u.add(m)),a+=p,bb(d,h),r[m]||={usd:0,tokens:yb(),events:0},r[m].usd+=p,r[m].events+=1,bb(r[m].tokens,h),o[A]||={usd:0,events:0},o[A].usd+=p,o[A].events+=1,s[I]||={usd:0,events:0,provider:A,models:new Set,label:t[I]||I},s[I].usd+=p,s[I].events+=1,m!=="unknown"&&s[I].models.add(m),f.epoch!=null){let M=(s[I].epochs||={})[f.epoch]||={usd:0,events:0,provider:A,models:new Set};M.usd+=p,M.events+=1,M.provider=A,m!=="unknown"&&M.models.add(m)}}return{totalUsd:qo(a),asRecordedUsd:qo(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Hc(r,f=>({...f,usd:qo(f.usd)})),byProvider:Hc(o,f=>({...f,usd:qo(f.usd)})),byBox:Hc(s,f=>({usd:qo(f.usd),events:f.events,provider:f.provider,label:f.label,models:[...f.models],...f.epochs?{epochs:Hc(f.epochs,p=>({usd:qo(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var yb,qo,Hc,Hr=C(()=>{or();yb=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});qo=e=>Math.round(e*1e4)/1e4,Hc=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function MO(e){if(!Number.isFinite(e)||e<=0)return null;let t=e/864e5;if(t>=1)return`${Math.round(t)}d`;let n=e/36e5;return n>=1?`${Math.round(n)}h`:`${Math.max(1,Math.round(e/6e4))}m`}function kb(e,t=[]){let n=[],r=e?.events||[],o=new Map,s=0;for(let u of t){if(u.type!=="pr.state.changed")continue;let d=u.payload||{};!d.repo||d.number==null||(d.change==="merged"?o.set(`${d.repo}#${d.number}`,d):d.change==="opened"&&s++)}if(o.size){let u=[...o.keys()].map(d=>{let[f,p]=d.split("#");return`${f.split("/").pop()}#${p}`});n.push(`Merged ${u.slice(0,4).join(", ")}${u.length>4?` +${u.length-4}`:""}`)}else s&&n.push(lf(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(lf(a,"subtask"));let i=r.filter(u=>u.type==="lead-handoff"&&u.fromAgent&&u.toAgent);if(i.length){let u=i.slice(0,3).map(d=>`${d.fromAgent}\u2192${d.toAgent}`).join(", ");n.push(`${lf(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=sr(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?MO(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var lf,xb=C(()=>{Hr();lf=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as NO}from"node:child_process";import{promisify as PO}from"node:util";import{randomUUID as DO}from"node:crypto";async function uf(e,t){let n=Date.now()+t;for(;;){if(await fn(e))return!0;if(Date.now()>n)return!1;await $a(jO)}}async function FO(e,t){let n=vb[t]||vb.default;n.escape&&(await Ie(e,["Escape"],{kind:"swap-exit"}),await $a(150)),n.wipe&&await Mo(e,xc,{kind:"swap-exit"}),await Sc(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=zt(t).submitDelayMs||0;await $a(Math.max(r,250)),await Ie(e,["Enter"],{kind:"swap-exit"})}async function Tb(e,t){try{let{stdout:n}=await Wr(F,H("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function Ab(e){await Wr(F,H("respawn-pane","-k","-t",e))}async function df(e,t={}){if(!_e(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await ae(e);if(!r)throw new Error(`session "${e}" not found`);if(!r.cwd)throw new Error(`can't resolve cwd for "${e}"`);let o=r.agent||"agent",s=t.agent||Vn(n),a=Number(t.exitTimeoutMs)||BO;await Y({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let i=!1;if(!await fn(e)&&(await FO(e,o),!await uf(e,a)&&(i=!0,await Y({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await Ab(e),!await uf(e,Sb))))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);let c=s==="claude"&&Pr(n),l=c?zi():null,u=await Tb(e,"ANTHROPIC_API_KEY"),d=!1;try{l?(await Wr(F,H("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await Wr(F,H("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),d=!0)}catch(g){throw l?hc(g,l):g}if(d&&!i&&(await Ab(e),!await uf(e,Sb)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let f=!!t.resumeSessionId,p=t.resumeSessionId||DO(),m=f?n:dd(n,p,e),h=f?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await Fn(r.cwd),s==="codex"&&await ko(r.cwd),s==="cursor"&&await xo(r.cwd),await Dr(e),await Wr(F,H("set-option","-t",e,"@bostrat-sid",p)),await Wr(F,H("set-option","-t",e,"@bostrat-agent",s)),await $a(LO),c&&ua().length&&(await Tb(e,la)||await Wr(F,H("set-environment","-t",e,la,"1")).catch(()=>{}),await Le(e,`export ${la}=1`,{kind:"swap-env"}),await $a(150)),await Le(e,m,{kind:"swap-launch"}),await Y({flow:"swap",phase:"done",sessionName:e,from:o,to:s,sid:p,forced:i,envRespawned:d}),{name:e,cwd:r.cwd,sessionId:p,agent:s,pinned:h,forced:i,envRespawned:d,launchCmd:m}}var Wr,$a,BO,Sb,jO,LO,vb,Eb=C(()=>{Ve();Js();Yi();Xi();Sa();Gt();Wr=PO(NO),$a=e=>new Promise(t=>setTimeout(t,e)),BO=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,Sb=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,jO=250,LO=400,vb={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 _b(e,{maxBytes:t=2097152,limit:n=100}={}){let r;try{r=await ct(e,t)}catch{return[]}let o=new Map,s=new Set;for(let i of r.split(`
|
|
119
|
+
`)){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 d=l.timestamp?Date.parse(l.timestamp):null;for(let f of u)if(!(!f||typeof f!="object"))if(f.type==="tool_use"&&f.id){if(f.name==="Agent"){let p=f.input||{};o.set(f.id,{id:f.id,kind:"agent",name:String(p.description||"").trim()||null,detail:String(p.subagent_type||"").trim()||null,ts:d,proven:!1})}else if(f.name==="Bash"&&f.input?.run_in_background===!0){let p=String(f.input.command||"").trim();o.set(f.id,{id:f.id,kind:"command",name:String(f.input.description||"").trim()||(p?Bo(p):null),detail:p?Bo(p):null,ts:d,proven:!0})}}else f.type==="tool_result"&&f.tool_use_id&&UO.test(HO(f.content))&&s.add(f.tool_use_id)}let a=[];for(let i of o.values()){if(!i.proven&&!s.has(i.id))continue;let{proven:c,...l}=i;a.push(l)}return a.slice(-n)}var UO,HO,Ib=C(()=>{Pt();jo();UO=/^Async (?:agent|task) launched/i,HO=e=>{if(typeof e=="string")return e.trim();if(!Array.isArray(e))return"";let t=e.find(n=>n&&n.type==="text"&&typeof n.text=="string");return t?t.text.trim():""}});function ar(e,t){if(!e)return null;let n=String(e);if(t){let o=t.endsWith("/")?t:`${t}/`;if(n===t)return".";if(n.startsWith(o))return n.slice(o.length)||"."}let r=n.split("/").filter(Boolean);return n.startsWith("/")&&r.length>2?r.slice(-2).join("/"):n}function WO(e){let t=typeof e=="string"?e:String(e?.patch??e?.input??""),n=[];for(let r of t.matchAll(/^\*{3} (?:Update|Add|Delete) File: (.+)$/gm))n.push(r[1].trim());return n}function Rb(e,t,n){switch(e){case"Shell":{let r=String(t?.command||"").trim();return r?{command:r,description:String(t?.description||"").trim()}:null}case"Read":{let r=t?.path;if(!r)return null;let o=Number(t?.offset)||null,s=o&&Number(t?.limit)?o+Number(t.limit)-1:null;return{command:Dt(`read ${ar(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${Ko(r)}`:`Read ${Ko(r)}`}}case"Grep":{let r=t?.pattern;if(!r)return null;let o=t?.path?ar(t.path,n):null,s=t?.glob?` --glob ${ff(t.glob)}`:"",a=Dt(String(r),40);return{command:Dt(`grep ${ff(r)}${o&&o!=="."?` ${o}`:""}${s}`,140),description:o&&o!=="."?`Search for \u201C${a}\u201D in ${o}`:`Search for \u201C${a}\u201D`}}case"Glob":{let r=t?.glob_pattern;if(!r)return null;let o=t?.target_directory?ar(t.target_directory,n):null;return{command:Dt(`glob ${ff(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${Dt(String(r),40)}\u201D`}}case"StrReplace":return t?.path?{command:Dt(`edit ${ar(t.path,n)}`,140),description:`Edit ${Ko(t.path)}`}:null;case"Write":return t?.path?{command:Dt(`write ${ar(t.path,n)}`,140),description:`Write ${Ko(t.path)}`}:null;case"Delete":return t?.path?{command:Dt(`delete ${ar(t.path,n)}`,140),description:`Delete ${Ko(t.path)}`}:null;case"ReadLints":{let o=(Array.isArray(t?.paths)?t.paths:[]).map(s=>ar(s,n)).filter(Boolean);return{command:Dt(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=WO(t);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:Dt(`apply_patch ${ar(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${Ko(r[0])}`}}case"Await":{let r=t?.shell_id??t?.task_id;return{command:Dt(`await ${r??"background output"}`,140),description:"Wait for a background command"}}default:return null}}var Wc,Dt,Ko,ff,$b=C(()=>{Wc=new Set(["Shell","Read","Grep","Glob","StrReplace","Write","ReadLints","Delete","ApplyPatch","Await"]),Dt=(e,t=88)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,Ko=e=>String(e).replace(/\/+$/,"").split("/").pop()||String(e);ff=e=>`"${String(e).replace(/"/g,'\\"')}"`});import Oa from"node:fs";import qO from"node:fs/promises";import qr from"node:path";import KO from"node:os";import GO from"node:crypto";import VO from"node:zlib";function Mb(e){let t=GO.createHash("md5").update(String(e)).digest("hex");return qr.join(zO,t)}function Nb(e){try{let t=JSON.parse(Oa.readFileSync(qr.join(e,"meta.json"),"utf8"));return{createdAtMs:Number(t.createdAtMs)||null,updatedAtMs:Number(t.updatedAtMs)||Number(t.createdAtMs)||null,hasConversation:t.hasConversation===!0}}catch{return null}}function JO(e){let t=Mb(e),n;try{n=Oa.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=qr.join(t,o.name),a=qr.join(s,"store.db");if(!Oa.existsSync(a))continue;let i=Nb(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 qc(e,t){if(!e)return null;if(t){let n=qr.join(Mb(e),t),r=qr.join(n,"store.db");if(Oa.existsSync(r))return{id:t,dir:n,store:r,...Nb(n)||{createdAtMs:null,updatedAtMs:null}}}return JO(e)}function Ob(e,t){let n=e.prepare("SELECT data FROM blobs WHERE id = ?").get(t);return n?Buffer.from(n.data):null}function YO(e){if(!e)return null;let t=e;if(t.length>1&&t[0]===31&&t[1]===139)try{t=VO.gunzipSync(t)}catch{}try{return JSON.parse(t.toString("utf8"))}catch{return null}}function XO(e){let t=[],n=0,r=e.length,o=()=>{let s=0,a=0;for(;n<r;){let i=e[n++];if(s+=(i&127)*2**a,!(i&128))break;a+=7}return s};for(;n<r;){let s=o(),a=s>>>3,i=s&7;if(i===2){let c=o(),l=e.subarray(n,n+c);n+=c,a===1&&c===32&&t.push(l.toString("hex"))}else if(i===0)o();else if(i===5)n+=4;else if(i===1)n+=8;else break}return t}function ZO(e,t){if(!Array.isArray(e))return[];let n=[];for(let r of e){if(!r||r.type!=="tool-call"||!Wc.has(r.toolName))continue;let o=Rb(r.toolName,r.args,t);!o||!o.command||n.push({id:r.toolCallId||null,tool:r.toolName,command:o.command,description:o.description||""})}return n}async function pf(e,t){if(!Cb)return Go;let{DatabaseSync:n}=Cb,r;try{r=new n(e,{readOnly:!0})}catch{return Go}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 Go;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return Go}let a=s.lastUsedModel||null,i=s.latestRootBlobId?Ob(r,s.latestRootBlobId):null;if(!i)return{...Go,model:a};let c=[],l=[],u=[],d=null,f=[];for(let p of XO(i)){let m=YO(Ob(r,p));if(!m)continue;let h=m.providerOptions?.cursor;if(h?.requestId&&f.push(h.requestId),h?.modelProviderMessageId&&f.push(h.modelProviderMessageId),m.role!=="assistant")continue;if(Array.isArray(m.content))for(let k of m.content){let A=k?.providerOptions?.cursor?.modelName;A&&(d=A)}l.push(...ZO(m.content,t).map((k,A)=>({...k,id:k.id||`${p}:${A}`}))),u.push(...eC(m.content));let g=QO(m.content);if(!g)continue;let x=[...new Set(u)];u=[],c.push({id:p,text:g,tools:x.length?x:void 0,commands:l,model:d}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:f}}catch{return Go}finally{try{r.close()}catch{}}}function Pb(e,t){let n=e.updatedAtMs;if(!n)try{n=Math.round(Oa.statSync(e.store).mtimeMs)}catch{n=Date.now()}let r=e.createdAtMs;(!r||r>n)&&(r=n-t*1e3);let o=Math.max(0,n-r);return{end:n,tsAt:s=>Math.round(r+o*(s+1)/t)}}async function Db(e,t,{limit:n=80}={}){let r=qc(e,t);if(!r)return[];let{turns:o,model:s}=await pf(r.store,e);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=Pb(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 Bb(e,t){let n=qc(e,t);if(!n)return null;let{turns:r,requestIds:o,model:s}=await pf(n.store,e);return{chatId:n.id,requestIds:o,models:[...new Set(r.map(a=>a.model).filter(Boolean))],configuredModel:s,updatedAtMs:n.updatedAtMs||null}}async function jb(e,t,{limit:n=80}={}){let r=qc(e,t);if(!r)return[];let{turns:o,trailingCommands:s}=await pf(r.store,e);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=Pb(r,a.length||1),l=[];a.forEach((u,d)=>{for(let f of u.commands)l.push({...f,summary:Yn(f.command,f.description),ts:c(d)})});for(let u of s)l.push({...u,summary:Yn(u.command,u.description),ts:i+1});return l}async function Lb(e,t){let n=qc(e,t);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await qO.stat(n.store)).mtimeMs)}catch{return null}}var zO,QO,eC,Cb,Go,mf=C(()=>{jo();$b();zO=process.env.BOSTRAT_CURSOR_CHATS||qr.join(KO.homedir(),".cursor","chats");QO=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&t.type==="text"&&typeof t.text=="string").map(t=>t.text).join(`
|
|
120
|
+
`).trim():"",eC=e=>Array.isArray(e)?e.filter(t=>t&&t.type==="tool-call"&&t.toolName).map(t=>t.toolName):[],Cb=process.getBuiltinModule?.("node:sqlite"),Go={turns:[],trailingCommands:[],model:null,requestIds:[]}});import tC from"node:fs/promises";function yf(e){return Fb.set(e.agent,e),e}function Ca(e){return Fb.get(e)||nC}async function kf(e,t={}){return(await Ca(e.agent).readMessages(e,t)).map(bf(e))}async function Ub(e,t={}){return(await Ca(e.agent).readCommands(e,t)).map(bf(e))}async function Hb(e,t={}){return(await Ca(e.agent).readJobs(e,t)).map(bf(e))}var Vo,hf,wf,gf,nC,Fb,bf,Wb=C(()=>{Pt();Ac();jo();Ib();mf();Lr();Vo=class{constructor(t,n=new Set){this.agent=t,this.cliTools=n}async readMessages(){return[]}async readCommands(){return[]}async readJobs(){return[]}async updatedAt(){return null}},hf=class extends Vo{transcript(t){return!t?.cwd||!t?.sessionId?null:Nt(t.cwd,t.sessionId)}async readMessages(t,n){let r=this.transcript(t);return r?_d(r,n):[]}async readCommands(t,n){let r=this.transcript(t);return r?Pg(r,n):[]}async readJobs(t,n){let r=this.transcript(t);return r?_b(r,n):[]}async updatedAt(t){let n=this.transcript(t);if(!n)return null;try{return Math.round((await tC.stat(n)).mtimeMs)}catch{return null}}},wf=class extends Vo{async readMessages(t,n){return Db(t.cwd,t.sessionId,n)}async readCommands(t,n){return jb(t.cwd,t.sessionId,n)}async updatedAt(t){return Lb(t.cwd,t.sessionId)}},gf=class extends Vo{sessionId(t){return t?.pinned?t.sessionId:null}async readMessages(t,n){return Lg(t.cwd,this.sessionId(t),n)}async readCommands(t,n){return Fg(t.cwd,this.sessionId(t),n)}async updatedAt(t){return Ug(t.cwd,this.sessionId(t))}},nC=new Vo("unknown"),Fb=new Map;yf(new hf("claude",Sd));yf(new wf("cursor",Wc));yf(new gf("codex",ga));bf=e=>t=>({...t,box:e.name,agent:e.agent,boxRole:e.role})});function Vr(e){let t=e.search(Tf);return(t>=0?e.slice(0,t):e).trim()}function rC(e){if(!e)return"";let t=e.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!t||/hidden|\d+\s+lines?/i.test(t)?"":t}function oC(e){let t=String(e).trim();return!!(!t||Gr.test(e)||Tf.test(t)||/hidden|\d+\s+lines?/i.test(t)||/notespress|press n to add/i.test(t)||/^\s*notes?\s*$/i.test(t))}function sC(e,t=""){let n=[],r=/([☐☒])\s+([^☐☒←→✔]+)/g,o;for(;(o=r.exec(e))!==null;)n.push({label:o[2].trim(),answered:o[1]==="\u2612",current:!1});if(!n.length)return null;let s=aC(n,t);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function aC(e,t){let n=String(t).toLowerCase(),r=e.findIndex(c=>!c.answered),o=r>=0?r:e.length-1;if(!n)return o;let s=e.map(c=>{let l=c.label.toLowerCase(),u=l.split(/\s+/).filter(f=>f.length>3&&n.includes(f)).length;if(u)return u;let d=l.slice(0,8).trim();return d.length>=4&&n.includes(d)?1:0}),a=Math.max(...s);if(a===0||s[o]===a)return o;let i=s.flatMap((c,l)=>c===a?[l]:[]);return i.length===1?i[0]:o}function Kb(e){return e.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function iC(e,t=""){let n=String(e);if(!Vb.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
121
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(/^\s*Switch model\?\s*$/.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[];for(let u=o+1;u<r.length;u++){let d=r[u].match(Kr);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let f=r[u].trim();f&&!Gr.test(r[u])&&a.push(f)}}if(s.length<2)return null;let i=s.map(u=>({label:Vr(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=["Switch model?",...a].join(" ");return{provider:"claude",box:t,multiSelect:!1,cursorIndex:c,promptId:`claude:model-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Model",multiSelect:!1,options:i}]}}function lC(e,t=""){let n=String(e).replace(/\r/g,"").split(`
|
|
122
|
+
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!zb.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(Kr);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&&!(cC.test(n[c])||Gr.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:Vr(c.label),description:""}));return{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,s.findIndex(c=>c.cursor)),promptId:`claude:confirm-gate:${i.map(c=>c.label).join("|")}`.slice(0,240),questions:[{question:a.join(" ")||"Claude Code is asking you to confirm",header:"Confirm",multiSelect:!1,options:i}]}}function uC(e,t=""){let n=String(e);if(!/ready to submit your answers/i.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
123
|
+
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(Kr);l&&o.push({cursor:!!l[1],label:l[4].trim()})}let s=o.filter(c=>/^(submit answers|cancel)$/i.test(c.label));if(s.length<2)return null;let a=s.map(c=>({label:c.label,description:""})),i=Math.max(0,s.findIndex(c=>c.cursor));return{provider:"claude",box:t,multiSelect:!1,cursorIndex:i,review:!0,promptId:"claude:review:submit-answers",questions:[{question:"Ready to submit your answers?",header:"Review",multiSelect:!1,options:a}]}}function fC(e,t=""){let n=String(e);if(!xf.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
124
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(dC.test(r[u])){o=u;break}if(o<0)return null;let s=[],a=[r[o].trim()];for(let u=o+1;u<r.length;u++){let d=r[u].match(Kr);if(d)s.push({cursor:!!d[1],label:d[4].trim()});else if(s.length){if(r[u].trim()==="")break}else{let f=r[u].trim();f&&!Gr.test(r[u])&&a.push(f)}}if(s.length<2||!s.some(u=>xf.test(u.label)))return null;let i=s.map(u=>({label:Vr(u.label),description:""})),c=Math.max(0,s.findIndex(u=>u.cursor)),l=a.join(" ");return{provider:"claude",box:t,multiSelect:!1,cursorIndex:c,promptId:`claude:resume-confirm:${i.map(u=>u.label).join("|")}`.slice(0,240),questions:[{question:l,header:"Resume",multiSelect:!1,options:i}]}}function Af(e){let t=[];for(let r=0;r<e.length;r++){let o=e[r].match(Kr);o&&t.push({line:r,cursor:!!o[1],n:Number(o[2]),label:o[4].trim()})}let n=t.sort((r,o)=>r.n-o.n);return n.length<2||n.some((r,o)=>r.n!==o+1)?null:n}function pC(e,t=""){let n=String(e);if(!Jb.test(n)||!Yb.test(n)||zr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
125
|
+
`),o=Af(r);return o?{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"theme",promptId:`claude:setup:theme:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks: choose the text style that looks best with your terminal. You can change it later with /theme.",header:"Theme",multiSelect:!1,options:o.map(s=>({label:Vr(s.label),description:""}))}]}:null}function mC(e,t=""){let n=String(e);if(!Xb.test(n)||zr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
126
|
+
`),o=Af(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:Vr(i),description:c.join("\xB7").trim()}});return{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(a=>a.cursor)),setup:"login-method",promptId:`claude:setup:login-method:${s.map(a=>a.label).join("|")}`.slice(0,200),questions:[{question:"How do you want to sign in to Claude? A Claude subscription (Pro/Max/Team) uses your existing plan; a Console account bills API usage. Your login stays on this box \u2014 bostrat never sees it.",header:"Log in",multiSelect:!1,options:s}]}}function wC(e,t){let n=e[t].trim();for(let r=t+1;r<e.length;r++){let o=e[r].trim();if(!o||!hC.test(o)||Ef.test(e[r]))break;n+=o}return n}function gC(e,t=""){let n=String(e);if(!Ef.test(n)||zr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
127
|
+
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=wC(r,s);break}return o?{provider:"claude",box:t,multiSelect:!1,cursorIndex:0,answerMode:"text",setup:"oauth",url:o,inputLabel:"Paste your sign-in code",promptId:"claude:setup:oauth",questions:[{question:"Open the sign-in link, approve access, then paste the code you're shown back here. The code goes straight to the Claude CLI on your box \u2014 bostrat relays the keystrokes and never stores it.",header:"Log in",multiSelect:!1,options:[]}]}:null}function bC(e,t=""){let n=String(e);if(!Sf.test(n)||!yC.test(n)||zr.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
128
|
+
`)].reverse().find(a=>Sf.test(a))||"OAuth error").trim();return{provider:"claude",box:t,multiSelect:!1,cursorIndex:0,setup:"oauth-error",promptId:`claude:setup:oauth-error:${s}`.slice(0,200),questions:[{question:`${s} \u2014 retry to get a fresh sign-in link.`,header:"Log in",multiSelect:!1,options:[{label:"Retry sign-in",description:"Mints a new sign-in link and code"}]}]}}function kC(e,t=""){let n=String(e);if(!Zb.test(n)||zr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
129
|
+
`),o=Af(r);return o?{provider:"claude",box:t,multiSelect:!1,cursorIndex:Math.max(0,o.findIndex(s=>s.cursor)),setup:"trust",promptId:`claude:setup:trust:${o.map(s=>s.label).join("|")}`.slice(0,200),questions:[{question:"Claude Code asks whether it can read, edit, and run files in this workspace. This box's workspace is bostrat-managed scratch space, so trusting it is safe.",header:"Folder trust",multiSelect:!1,options:o.map(s=>({label:Vr(s.label),description:""}))}]}:null}function xC(e,t=""){let n=String(e);if(zr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
130
|
+
`),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(Qb);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(Gr.test(r[l])||Tf.test(u))break;a.unshift(u)}let i=s[1].trim();i&&a.push(i);let c=a.join(" ")||"Claude Code is waiting for you to continue.";return{provider:"claude",box:t,multiSelect:!1,cursorIndex:0,setup:"continue",promptId:`claude:setup:continue:${c}`.slice(0,200),questions:[{question:c,header:"Continue",multiSelect:!1,options:[{label:"Continue",description:""}]}]}}function SC(e){let t=-1;for(let r=e.length-1;r>=0;r--)if(vf.test(e[r])){t=r;break}if(t<0)return null;let n=-1;for(let r=t-1;r>=0;r--)if(/[☐☒]/.test(e[r])){n=r;break}return n<0?null:{headerLine:n,chatLine:t,optStart:n+1,optEnd:t}}function vC(e,t=""){let n=String(e);if(!Gb.test(n)||!vf.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
131
|
+
`),o=SC(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let T=i;T<c;T++){let j=r[T].match(Kr);j&&(u<0&&(u=T),l.push({line:T,cursor:!!j[1],checked:j[3]!=null&&j[3]!==" ",hasCheckbox:j[3]!=null,label:j[4].trim(),desc:""}))}if(!l.length)return null;for(let T of l)for(let j=T.line+1;j<Math.min(r.length,T.line+4)&&!(Kr.test(r[j])||Gr.test(r[j]));j++){let Z=r[j].trim();if(!(!Z||oC(r[j])||qb.test(Z))){T.desc=rC(Z);break}}let d=l.filter(T=>!qb.test(T.label));if(!d.length)return null;let f=d.some(T=>T.hasCheckbox),p=Math.max(0,d.findIndex(T=>T.cursor)),m=s>=0?s+1:0,h=[];for(let T=m;T<u;T++){let j=r[T].trim();j&&!Gr.test(r[T])&&!/[☐☒]/.test(r[T])&&h.push(j)}let g=h.join(" ")||(s>=0?Kb(r[s]):"")||"The agent is asking a question",x=s>=0?sC(r[s],g):null,k=x?.currentTabLabel||(s>=0?Kb(r[s]):""),A=d.map(T=>({label:Vr(T.label),description:T.desc})),I=A.map(T=>T.label).join("|"),N=`claude:${x?`q${x.questionIndex+1}/${x.questionTotal}`:"q1/1"}:${k}:${g}:${I}`.slice(0,240),O=x?`claude:ask:${x.tabs.map(T=>T.label).join("|")}`.slice(0,240):N;return{provider:"claude",box:t,multiSelect:f,cursorIndex:p,promptId:N,groupId:O,...x?{questionIndex:x.questionIndex,questionTotal:x.questionTotal,currentTabLabel:x.currentTabLabel,tabs:x.tabs}:{},questions:[{question:g,header:k,multiSelect:f,options:A}]}}function Jr(e,t){let n=String(t||"");return n?e==="claude"?Gb.test(n)&&vf.test(n)||Vb.test(n)||xf.test(n)||zb.test(n.trimEnd().split(`
|
|
132
|
+
`).pop()||"")?!0:zr.test(n)?!1:Jb.test(n)&&Yb.test(n)||Xb.test(n)||Ef.test(n)||Sf.test(n)||Zb.test(n)||Qb.test(n.trimEnd().split(`
|
|
133
|
+
`).pop()||""):e==="codex"&&/Sign in with ChatGPT|one-time code|Paste or type your API key|link to authenticate|Successfully logged in\.[\s\S]*Press Enter to continue\./i.test(n)?!0:e==="cursor"||e==="codex"?/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i.test(n):!1:!1}function ir(e,t){return!e||!t?null:e.agent==="claude"?uC(t,e.name)||vC(t,e.name)||iC(t,e.name)||fC(t,e.name)||bC(t,e.name)||gC(t,e.name)||mC(t,e.name)||pC(t,e.name)||kC(t,e.name)||xC(t,e.name)||lC(t,e.name):e.agent==="cursor"||e.agent==="codex"?ny(e.agent,t,e.name):null}async function _f(e){if(!e)return null;let t=await pn(e.name).catch(()=>"");return ir(e,t)}function ek(e,t){let n=Array.isArray(t?.[0])?t[0].filter(a=>Number.isInteger(a)&&a>=0):[],r=Number.isInteger(e?.cursorIndex)?e.cursorIndex:0,o=(a,i)=>{let c=i-a;return Array.from({length:Math.abs(c)},()=>c>0?"Down":"Up")},s=[];if(e?.multiSelect){let a=r;for(let i of[...n].sort((c,l)=>c-l))s.push(...o(a,i),"Enter"),a=i;s.push("Tab","Enter")}else{let a=n.length?n[0]:0;s.push(...o(r,a),"Enter")}return s}function tk(e,t){let n=Number.isInteger(e?.questionIndex)?e.questionIndex:0,o=(Number.isInteger(t)?t:n)-n;if(!o)return[];let s=o>0?"Tab":"BTab";return Array.from({length:Math.abs(o)},()=>s)}var Gb,vf,Kr,qb,Gr,Tf,Vb,zb,cC,xf,dC,zr,Jb,Yb,Xb,Ef,hC,Sf,yC,Zb,Qb,If=C(()=>{Ve();_c();Gb=/to navigate\b.*\bEsc to cancel|Enter to select\b/,vf=/Chat about this/i,Kr=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,qb=/^(type something|chat about this|submit)\b/i,Gr=/^[─—-]{6,}\s*$/,Tf=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;Vb=/^\s*Switch model\?\s*$/m;zb=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,cC=/^\s*[─—▔-]{6,}\s*$/;xf=/Resume from summary/i,dC=/^\s*This session is\b/;zr=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;Jb=/Choose the text style that looks best with your terminal/,Yb=/Let's get started\./;Xb=/Select login method:/;Ef=/Paste code here if prompted/,hC=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;Sf=/OAuth error:/,yC=/Press Enter to retry\./;Zb=/Quick safety check: Is this a project you created or one you trust/;Qb=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function Kc(e,t,n){if(!e||!t)return null;for(let r of TC)if(r.provider===e.agent)try{if(r.match(t,n))return r}catch{}return null}function EC(e){let t=Rf.get(e);return t||(t={sentAt:[],last:0,emitted:new Set},Rf.set(e,t)),t}function ok(e,t,n=Date.now()){let r=EC(e);if(r.sentAt=r.sentAt.filter(s=>n-s<rk),r.sentAt.length>=nk)return{action:"give-up"};if(n-r.last<AC)return{action:"wait"};r.last=n,r.sentAt.push(n);let o=!r.emitted.has(t);return o&&r.emitted.add(t),{action:"send",first:o}}function sk(e,t=Date.now()){let n=Rf.get(e);return n?n.sentAt.filter(r=>t-r<rk).length>=nk:!1}var TC,AC,nk,rk,Rf,$f=C(()=>{TC=[{provider:"claude",id:"fullscreen-upsell",match(e,t){let n=e?.questions?.[0],r=(n?.options||[]).map(s=>String(s.label||"").trim());if(r.length!==2||!/^Yes, try it$/.test(r[0])||!/^Not now$/.test(r[1]))return!1;let o=`${n?.question||""}
|
|
134
|
+
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];AC=5e3,nk=5,rk=3600*1e3,Rf=new Map});function zo(e){return typeof e=="string"?e.trim():""}var ak=C(()=>{});import{execFile as _C}from"node:child_process";import{promisify as IC}from"node:util";async function $e(e,t,{input:n}={}){let r=Cf("git",["-C",e,...t],{maxBuffer:ik});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Jo(e){if(!e)return!1;try{let{stdout:t}=await $e(e,["rev-parse","--is-inside-work-tree"]);return t.trim()==="true"}catch{return!1}}async function ck(e){let{stdout:t}=await $e(e,["rev-parse","--show-toplevel"]);return t.trim()}async function RC(e){try{return await $e(e,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function Yo(e,t="bostrat: subtask snapshot"){let{stdout:n}=await $e(e,["rev-parse","--absolute-git-dir"]),r=`${n.trim()}/bostrat-snapshot-index`,o={...process.env,GIT_INDEX_FILE:r},s=(a,i)=>Cf("git",["-C",e,...a],{maxBuffer:ik,env:o,...i});try{let a=await RC(e);a&&await s(["read-tree","HEAD"]),await s(["add","-A"]);let{stdout:i}=await s(["write-tree"]),c=i.trim(),l=a?["commit-tree",c,"-p","HEAD","-m",t]:["commit-tree",c,"-m",t],{stdout:u}=await s(l);return u.trim()}finally{try{await Cf("rm",["-f",r])}catch{}}}async function Mf(e,t){let{stdout:n}=await $e(e,["status","--porcelain"]);return n.trim()?(await $e(e,["add","-A"]),await $e(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","-m",t]),!0):!1}async function Ma(e,{ignorePlumbing:t=!1}={}){let{stdout:n}=await $e(e,["status","--porcelain"]),r=n.split(`
|
|
135
|
+
`).filter(Boolean);return t&&(r=r.filter(o=>{let s=o.slice(3);return!(s===".bostrat"||s===".bostrat/"||s.startsWith(".bostrat/")||o.startsWith("??")&&(s==="AGENTS.md"||s==="CLAUDE.md"))})),r.length>0}async function Na(e,t){if(!t)return 0;try{let{stdout:n}=await $e(e,["rev-list","--count",`${t}..HEAD`]);return Number(n.trim())||0}catch{return 0}}async function Xo(e,t,n,r){return await $e(e,["worktree","add","--no-track","-b",n,t,r]),t}async function lk(e,t){try{return await $e(e,["rev-parse","--verify","--quiet",`refs/heads/${t}`]),!0}catch{return!1}}async function Gc(e,t,n){return await $e(e,["worktree","add",t,n]),t}async function Zo(e){if(process.env.BOSTRAT_TRACK_BASE_REF)return process.env.BOSTRAT_TRACK_BASE_REF;try{let{stdout:t}=await $e(e,["symbolic-ref","--short","refs/remotes/origin/HEAD"]);if(t.trim())return t.trim()}catch{}for(let[t,n]of[["refs/remotes/origin/main","origin/main"],["refs/remotes/origin/master","origin/master"],["refs/heads/main","main"],["refs/heads/master","master"]])try{return await $e(e,["rev-parse","--verify","--quiet",t]),n}catch{}return"HEAD"}async function Nf(e){await $e(e,["init","-q"]),await $e(e,["-c","user.email=agent@bostrat.local","-c","user.name=bostrat","commit","--allow-empty","-qm","bostrat: track workspace"])}async function pt(e,t){try{await $e(e,["worktree","remove","--force",t])}catch{try{await $e(e,["worktree","prune"])}catch{}}}function $C(e){let t=/(\d+) files? changed/.exec(e),n=/(\d+) insertions?\(\+\)/.exec(e),r=/(\d+) deletions?\(-\)/.exec(e);return{files:t?Number(t[1]):0,insertions:n?Number(n[1]):0,deletions:r?Number(r[1]):0}}function Pf(e,t){return!t||!t.length?e:[...e,"--",".",...t.map(n=>`:(exclude)${n}`)]}async function Df(e,t,n,r){let{stdout:o}=await $e(e,Pf(["diff","--shortstat",t,n],r));return $C(o)}async function Bf(e,t,n,r){let{stdout:o}=await $e(e,Pf(["diff","--name-only",t,n],r));return o.split(`
|
|
136
|
+
`).map(s=>s.trim()).filter(Boolean)}async function jf(e,t,n,r){let{stdout:o}=await $e(e,Pf(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await Of(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await Of(e,o,["--3way","--whitespace=nowarn"]),a=await OC(e);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await Of(e,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function Of(e,t,n){try{return await $e(e,["apply",...n,"-"],{input:t}),{ok:!0}}catch(r){return{ok:!1,err:r?.stderr?.trim()||r?.message||String(r)}}}async function OC(e){try{let{stdout:t}=await $e(e,["diff","--name-only","--diff-filter=U"]);return t.split(`
|
|
137
|
+
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var Cf,ik,Vc=C(()=>{Cf=IC(_C),ik=64*1024*1024});import Lf from"node:fs/promises";import dk from"node:path";import CC from"node:os";function hk(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function wk(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function gk(e){let t=new Date(hk(e));return t.setDate(t.getDate()+7),t.getTime()}function yk(e){let t=new Date(wk(e));return t.setMonth(t.getMonth()+1),t.getTime()}function Jc(e=Date.now()){for(let[t,n]of Object.entries(Qo)){let r=n.startOf(e);if(r!==hn[t]){hn[t]=r;for(let o of Yr.values())o[t]=0;for(let o of es)o.includes(`:${t}:`)&&!o.endsWith(`:${r}`)&&es.delete(o)}}}function bk(e,t){let n=Bt?.caps?.[e]?.[Qo[t].capKey];return Number.isFinite(n)&&n>0?n:null}function Xr(e,t=Date.now()){if(!e||!Uf||!Bt?.caps)return null;Jc(t);let n=Yr.get(e);for(let r of Object.keys(Qo)){let o=bk(e,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:e,window:r,limitUsd:o,spentUsd:zc(s),resetsAt:Qo[r].nextStart(t)}}return null}function kk(e,t){let n=`hit:${e}:${t.provider}:${t.window}:${hn[t.window]}`;return es.has(n)?!1:(es.add(n),!0)}function uk(e,t,n,r=Date.now()){if(!e||!t)return;Jc(r);let o=MC(e);for(let[s,a]of Object.entries(Qo))n>=hn[s]&&n<a.nextStart(r)&&(o[s]+=t)}async function NC(e,t,n=Date.now()){if(!Bt?.caps||!Ff)return;let r=Yr.get(e);if(r)for(let[o,s]of Object.entries(Qo)){let a=bk(e,o);if(a==null)continue;let i=r[o]||0;if(i<a*mk||i>=a)continue;let c=`warn:${e}:${o}:${hn[o]}`;if(!es.has(c)){es.add(c);try{let l=await Ff(t);if(!l)continue;await _(l,{type:"usage.cap.warning",actor:"system",provider:e,payload:{provider:e,window:o,limitUsd:a,spentUsd:zc(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function xk(e){let t={};for(let[n,r]of Object.entries(e||{})){if(typeof n!="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(n))throw new Error(`bad provider key "${n}"`);let o={};for(let s of["weeklyUsd","monthlyUsd"]){let a=r?.[s];if(a==null||a==="")continue;let i=Number(a);if(!Number.isFinite(i)||i<=0)throw new Error(`${n}.${s} must be a positive number or null`);o[s]=i}Object.keys(o).length&&(t[n]=o)}return t}async function Yc(e,{source:t="local",updatedAt:n=Date.now()}={}){Bt={caps:xk(e),updatedAt:n,source:t};try{await Lf.mkdir(fk,{recursive:!0}),await Lf.writeFile(pk(),JSON.stringify(Bt,null,2))}catch{}return Pa()}function Pa(e=Date.now()){Jc(e);let t=new Set([...Yr.keys(),...Object.keys(Bt?.caps||{})]),n={};for(let r of t){let o=Yr.get(r);n[r]={weeklyUsd:zc(o?.weekly||0),monthlyUsd:zc(o?.monthly||0)}}return{caps:Bt?.caps||{},configured:!!Bt,seeded:Uf,updatedAt:Bt?.updatedAt??null,spent:n,windows:{weekly:{start:hn.weekly,resetsAt:gk(e)},monthly:{start:hn.monthly,resetsAt:yk(e)}},warnPct:mk}}async function Sk({listTracks:e,getTrack:t}={}){Ff=t||null,Jc();try{let o=JSON.parse(await Lf.readFile(pk(),"utf8"));Bt={caps:xk(o?.caps),updatedAt:o?.updatedAt??null,source:o?.source??"file"}}catch{Bt=null}let n=Date.now();at.on("event",o=>{if(o?.type!=="cost.recorded")return;let{usd:s}=mn(o);uk(o.provider||"unknown",s,o.ts??Date.now()),NC(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(hn.weekly,hn.monthly);for(let o of await e?.()||[]){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||uk(a.provider||"unknown",mn(a).usd,i,n)}}return Uf=!0,Pa()}var fk,pk,mk,Qo,Bt,Uf,hn,Yr,es,Ff,MC,zc,Xc=C(()=>{ue();Hr();fk=process.env.BOSTRAT_STATE_DIR||dk.join(process.env.HOME||CC.tmpdir(),".bostrat"),pk=()=>dk.join(fk,"usage-caps.json"),mk=.8;Qo={weekly:{startOf:hk,nextStart:gk,capKey:"weeklyUsd"},monthly:{startOf:wk,nextStart:yk,capKey:"monthlyUsd"}},Bt=null,Uf=!1,hn={weekly:0,monthly:0},Yr=new Map,es=new Set,Ff=null,MC=e=>{let t=Yr.get(e);return t||(t={weekly:0,monthly:0},Yr.set(e,t)),t};zc=e=>Math.round(e*1e4)/1e4});import PC from"node:fs/promises";import DC from"node:os";import vk from"node:path";function $k(e){return e==="codex"?FC:e==="cursor"?Rk:_k}function Tk(e){let t=String(e||"").split(`
|
|
138
|
+
`),n=t.findIndex(o=>/Select Model and Effort/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<t.length;o++){let s=t[o].match(/^\s*(›\s*)?(\d+)\.\s+(\S+?)(?:\s+\((current|default)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),id:s[3],current:s[4]==="current",active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(t[o]))break}return r.length?r:null}function Ak(e){let t=String(e||"").split(`
|
|
139
|
+
`),n=t.findIndex(o=>/Select Reasoning Level/i.test(o));if(n<0)return null;let r=[];for(let o=n+1;o<t.length;o++){let s=t[o].match(/^\s*(›\s*)?(\d+)\.\s+([A-Za-z ]+?)(?:\s+\((default|current)\))?(?:\s{2,}|\s*$)/);if(s)r.push({n:Number(s[2]),label:s[3].trim(),isDefault:!!s[4],active:!!s[1]});else if(r.length&&/Press enter|esc to go back/i.test(t[o]))break}return r.length?r:null}function UC(e){let t=String(e||"").split(`
|
|
140
|
+
`);for(let n=t.length-1;n>=0;n--){let r=t[n].match(/Model changed to\s+(\S+)(?:\s+(\S+))?\s*$/i);if(r)return{model:r[1],effort:r[2]||null}}return null}function Ek(e){let t=String(e||"").split(`
|
|
141
|
+
`),n=t.findIndex(s=>/Available models|Models matching/i.test(s));if(n<0)return null;let r=/Models matching/i.test(t[n]),o=[];for(let s=n+1;s<t.length;s++){let a=t[s];if(/^\s*Filter:/i.test(a)||!a.trim())continue;if(/to filter|Enter to select|^\s*\d+-\d+ of \d+/i.test(a))break;let i=a.match(/^\s*(→\s+)?(.+?)\s*$/);if(!i)continue;let c=i[2].replace(/\s*\(Tab to modify\)\s*$/i,""),l=null,u=c.split(/\s{2,}/);u.length>1&&(c=u[0],l=u.slice(1).join(" ").trim()||null),c&&o.push({label:c.trim(),params:l,active:!!i[1]})}return o.length?{filtered:r,rows:o}:null}function Ok(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim();if(e==="codex"){let[r,o]=n.split(/[\s:]+/);if(o&&!Ik.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=Rk.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!LC.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function Da(e,t,{timeoutMs:n=BC}={}){let r=Date.now()+n;for(;;){let o=await Te(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await Zc(jC)}}async function HC(e,t){return await Le(e,`/model ${t.model}`,zt("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function WC(e,t){await kc(e),await Le(e,"/model",{enter:!1}),await Zc(250),await Ie(e,["Enter"],{kind:"model-switch"});let n=await Da(e,Tk);if(!n)throw new Error("codex model picker didn't open");let r=n.find(i=>i.id===t.model);if(!r)throw await Ie(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`unknown model "${t.model}" \u2014 codex offers: ${n.map(i=>i.id).join(", ")}`);let o=i=>{let c=UC(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await Le(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await Da(e,i=>Ak(i)||!Tk(i)&&o(i)&&{done:o(i)});if(!s)throw await Ie(e,["Escape"],{kind:"model-switch"}).catch(()=>{}),new Error("codex reasoning-level picker didn't open");if(!s.done){let i=t.effort?Ik.get(t.effort):null,c=i?s.find(l=>l.label.toLowerCase()===i.toLowerCase()):s.find(l=>l.isDefault)||s[0];if(!c)throw await Ie(e,["Escape","Escape"],{kind:"model-switch"}).catch(()=>{}),new Error(`codex doesn't offer effort "${t.effort}" for ${t.model}`);await Le(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Da(e,i=>!Ak(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 qC(){let e=vk.join(process.env.CURSOR_CONFIG_DIR||vk.join(DC.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await PC.readFile(e,"utf8"))?.model||null}catch{return null}}async function KC(e,t){await kc(e),await Sc(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await Da(e,Ek),r=c=>String(c||"").toLowerCase(),o=c=>r(c.label).startsWith(r(t.filter)),s=n?.rows.find(c=>c.active)||null;if(n&&!(s&&o(s)))for(let c=0;c<12;c++){let l=await Da(e,Ek,{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 Ie(e,["Down"],{kind:"model-switch"}),await Zc(250)}if(!n||!s||!o(s)){await Ie(e,["Escape"],{kind:"model-switch"}).catch(()=>{});let c=Array(`/model ${t.filter}`.length).fill("BSpace");throw await Ie(e,c,{settleMs:15,kind:"model-switch"}).catch(()=>{}),new Error(n?`cursor picker has no model matching "${t.filter}"`:"cursor model picker didn't open")}await Ie(e,["Enter"],{kind:"model-switch"}),await Zc(800);let a=await qC();return{verified:!!a&&(a.modelId===t.model||a.displayModelId===t.model||r(a.displayName).startsWith(r(t.filter))),model:a?.modelId||null,note:"cursor applies this as the global default for new sessions too"}}async function Ck(e,t,n){return e==="codex"?WC(t,n):e==="cursor"?KC(t,n):HC(t,n)}var Zc,BC,jC,_k,LC,FC,Ik,Rk,Mk=C(()=>{Ve();Sa();Zc=e=>new Promise(t=>setTimeout(t,e)),BC=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,jC=300,_k=[{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"}],LC=new Set([..._k.map(e=>e.alias),"best","opus[1m]","sonnet[1m]","claude-opus-5","claude-opus-4-8","claude-sonnet-5","claude-sonnet-4-6","claude-haiku-4-5","claude-fable-5"]),FC=[{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"}],Ik=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),Rk=[{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 el(e){let t=e||{},n=GC(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function GC(e){switch(e.type){case"said":return{type:"turn.user",actor:"user",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{text:e.text,boxRole:e.boxRole,...e.kind?{kind:e.kind}:{},...e.attachments?.length?{attachments:e.attachments}:{}}};case"unsaid":return{type:"turn.retracted",actor:"user",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{saidTs:e.saidTs,text:e.text,boxRole:e.boxRole}};case"orchestration-prompt":return{type:"turn.user",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{text:e.prompt,kind:e.kind,boxRole:e.boxRole}};case"lead-handoff":return{type:"handoff.done",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:lt(e.toAgent),payload:{from:e.from,fromAgent:e.fromAgent,to:e.to,toAgent:e.toAgent,reason:e.reason,handoffStartedAt:e.handoffStartedAt,fromSessionId:e.fromSessionId,toSessionId:e.toSessionId,epoch:e.epoch,singleBox:e.singleBox,forced:e.forced,recovered:e.recovered,checkpoint:e.checkpoint}};case"lead-handoff-failed":return{type:"handoff.failed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.toAgent),payload:{box:e.box,fromAgent:e.fromAgent,toAgent:e.toAgent,reason:e.reason,error:e.error,rolledBack:e.rolledBack,handoffStartedAt:e.handoffStartedAt}};case"subtask-delegated":return{type:"subtask.delegated",actor:"orchestrator",source:"orchestrator",boxId:e.to,provider:lt(e.toAgent),payload:{task:e.task,subtaskId:e.subtaskId,reason:e.reason}};case"subtask-merged":return{type:"subtask.merged",actor:"orchestrator",source:"orchestrator",boxId:e.box||e.from,provider:lt(e.agent),payload:{...cr(e)}};case"subtask-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box||e.to,provider:lt(e.agent||e.toAgent),payload:{kind:"subtask-failed",...cr(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{change:"subtask-resumed",...cr(e)}};case"model-change":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"model",model:e.to,from:e.from??null,verified:e.verified??null}};case"codex-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:"codex",payload:{change:"cli-update",from:e.from??null,to:e.to??null,status:e.status,...e.error?{error:e.error}:{}}};case"node-update":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",payload:{change:"node-update",...cr(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...cr(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...cr(e)}};case"rehydrated":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{change:"rehydrated"}};case"transferred-in":return{type:e.mode==="recover"?"track.recovered":"track.transferred",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{...cr(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...cr(e)}};case"rehydrate-failed":return{type:"error",actor:"system",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{kind:"rehydrate-failed",reason:e.reason}};default:return null}}function Dk(e,t){let n=el(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;Wf(()=>_(r,n))}async function Bk(e){if(!(!e?.id||Nk.has(e.id))){Nk.add(e.id);try{let t=await L(e),n=new Set(t.filter(r=>r.source==="orchestrator").map(r=>`${r.type}|${r.payload?.ts??"?"}`));for(let r of e.events||[]){let o=el(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await _(e,o)}}catch{}}}function cr(e){let{type:t,at:n,...r}=e;return r}function jk(e,t,n){!n?.promptId||Hf.get(e.id)===n.promptId||(Hf.set(e.id,n.promptId),Wf(()=>_(e,{type:"prompt.raised",actor:"agent",source:"pane",boxId:t?.name,provider:lt(n.provider||t?.agent),payload:{promptId:n.promptId,question:n.question||n.title||null,options:(n.options||[]).map(r=>r?.label??r).slice(0,12)}})))}function qf(e,t,n,r={}){Hf.delete(e.id),Wf(()=>_(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:lt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var lt,Qc,Wf,Pk,Nk,Hf,Kf=C(()=>{ue();lt=e=>e||null;Qc=Promise.resolve(),Wf=e=>(Qc=Qc.then(e).catch(()=>{}),Qc),Pk=()=>Qc;Nk=new Set;Hf=new Map});function XC(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function qk(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of XC(n).reverse()){if(!VC.test(r))continue;let o=r.endsWith("?")&&r.length<=YC&&JC.test(r);if(zC.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function Gk(e){let t=String(e||""),n=[];ZC.test(t)&&n.push("commit"),Kk.test(t)&&n.push("rebase"),QC.test(t)&&n.push("push");let r=tM.test(t);!r&&eM.test(t)&&n.push("pr");let o=null;if(r){let s=t.match(/(?:pr|pull\s+request)\s*\(?\s*#(\d+)\)?/i)||t.match(/#(\d+)/);o={number:s?Number(s[1]):null}}return{offered:n.length?n:null,toPr:o}}function Vk(e){return Kk.test(String(e||""))}function ns(e,{branch:t=null,base:n=null,toPr:r=null,repos:o=null,offeredRebase:s=!1}={}){Array.isArray(e)||(e=e==="push"?["commit","push"]:["commit","push","pr"]);let a=new Set(e),i=Array.isArray(o)&&o.length>0,c=i?o.map(p=>`${p.name}/`).join(", "):null,l=t?` on this branch (${t})`:"",u=[];if(a.has("commit")&&u.push(i?`commit the work in each changed checkout (${c})${l} with a clear commit message per repo`:`commit the work${l} with a clear commit message`),a.has("rebase")){let p=a.has("commit")?"the branch":`this branch${t?` (${t})`:""}`;u.push(i?`rebase ${p} in each changed checkout (${c}) onto its own base branch (fetch origin first), resolving any conflicts so both sides' intent is preserved`:`rebase ${p} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`)}if(a.has("push")||a.has("pr")){let p=r&&!a.has("pr")?` (the open PR${r.number?` #${r.number}`:""} picks it up)`:"",m=a.has("rebase")?" with `--force-with-lease` (the rebase rewrote history)":"";i?u.push(a.has("commit")||a.has("rebase")?`push each one to its remote${m}${p}`:`push this branch${t?` (${t})`:""} from each changed checkout (${c}) to its remote${p}`):u.push(a.has("commit")||a.has("rebase")?`push it to the remote${m}${p}`:`push this branch${l} to the remote${p}`)}a.has("pr")&&u.push(i?"open a pull request in each changed repo with a concise title and a description of what changed and why (run `gh pr create` inside each checkout)":`open a pull request${n?` against ${n}`:""} with a concise title and a description of what changed and why (use \`gh pr create\`)`);let d=[];a.has("commit")||d.push("don't create any new commits \u2014 ship what's already committed"),s&&!a.has("rebase")&&d.push("don't rebase \u2014 leave the branch's history as it is"),!a.has("push")&&!a.has("pr")&&d.push("don't push"),a.has("pr")||d.push("don't open a pull request");let f=`Yes \u2014 go ahead: ${u.join(", then ")}.`;return d.length&&(f+=` ${d.join(", and ").replace(/^./,p=>p.toUpperCase())}.`),a.has("pr")&&(f+=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."),f}function zk(e,{branch:t=null,base:n=null,prNumber:r=null,offeredRebase:o=!1}={}){let s=new Set(Array.isArray(e)?e:["commit","push","pr"]),a=t?` on this branch (${t})`:"",i=[];s.has("commit")&&i.push(`commit the work${a} with a clear commit message`),s.has("rebase")&&i.push(`rebase ${s.has("commit")?"the branch":`this branch${t?` (${t})`:""}`} onto ${n?`the latest ${n}`:"its base branch"} (fetch origin first), resolving any conflicts so both sides' intent is preserved`),i.push("move the work to a fresh branch (branch off the current HEAD with a clear new name)"),i.push("push the new branch to the remote"),i.push(`open a pull request${n?` against ${n}`:""} from the new branch with a concise title and a description of what changed and why (use \`gh pr create\`)`);let c=[];s.has("commit")||c.push("don't create any new commits \u2014 ship what's already committed"),o&&!s.has("rebase")&&c.push("don't rebase \u2014 leave the branch's history as it is");let l=`Yes \u2014 go ahead, as a NEW pull request: ${i.join(", then ")}.`;return c.length&&(l+=` ${c.join(", and ").replace(/^./,u=>u.toUpperCase())}.`),l+=` Leave the existing open PR${r?` #${r}`:""}${t?` and its remote branch (${t})`:" and its remote branch"} exactly as they are \u2014 don't push ${t||"the old branch"} itself.`,l}function Jk(e,t=Date.now()){let n=[],r=null;for(let o of e||[]){let s=o.payload||{},a=s.ts??o.ts;if(o.type==="pr.offer.answered"&&s.action==="pr"){r={box:o.boxId,from:a,lastActivity:a};continue}if(o.type==="pr.exists.answered"&&s.choice==="new"){r={box:o.boxId,from:a,lastActivity:a};continue}!r||a==null||a<r.from||(o.type==="pr.offer.answered"||o.type==="pr.exists.answered"?r=null:o.type==="pr.state.changed"&&s.change==="opened"?(n.push({box:r.box,from:r.from,to:a,settled:!0}),r=null):o.type==="turn.user"&&o.source!=="hook"&&!s.kind&&String(s.text||"").trim()?r=null:o.boxId===r.box&&/^(?:turn|tool)\./.test(o.type)&&(r.lastActivity=Math.max(r.lastActivity,a)))}return r&&t-r.lastActivity<=nM&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function Yk(e,t){let n=new Set;for(let r of t||[]){if(!r.settled)continue;let o=null;for(let s of e||[])s.kind!=="message"||s.role!=="assistant"||s.box!==r.box||s.ts==null||s.ts<r.from||s.ts>r.to+rM||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function Xk(e){let t=(Array.isArray(e)?e:[]).filter(r=>Lk[r]).map(r=>Lk[r]);return`The ship you were given the go-ahead for${t.length?` (${t.join(", then ")})`:""} should now be complete. This is an automated ship notification the operator armed on the offer: carry straight on with this Track's follow-up work (dependent tasks, cleanup, next steps) \u2014 don't wait for review or merge. If a ship step failed or is unfinished, finish the ship first. If nothing needs doing, say so briefly.`}var Fk,Uk,Hk,Wk,VC,zC,JC,YC,ts,ZC,Kk,QC,eM,tM,nM,rM,Lk,rs=C(()=>{Fk=/(?<!\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,Uk=/(?<!\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,Hk=/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,Wk=/(?<!\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,VC=new RegExp(`\\b(?:${Fk}|${Uk}|${Hk}|${Wk})\\b`,"i"),zC=/\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,JC=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,YC=90;ts=["commit","rebase","push","pr"],ZC=new RegExp(`\\b(?:${Fk})\\b`,"i"),Kk=new RegExp(`\\b(?:${Wk})\\b`,"i"),QC=new RegExp(`\\b(?:${Uk})\\b`,"i"),eM=new RegExp(`\\b(?:${Hk})\\b`,"i"),tM=/\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;nM=3e4;rM=2500;Lk={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import oM from"node:crypto";import tl from"node:fs/promises";import Zk from"node:path";import sM from"node:os";async function Ce(){return lr||(Gf??=tl.readFile(zf,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(lr=e,!t&&Ee()){let{records:n,source:r}=await Fe("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;lr[Q(i.repo,i.number)]=i}Object.keys(lr).length&&await Jf().catch(()=>{})}}return lr}).finally(()=>Gf=null),Gf)}async function Jf(){let e=JSON.stringify({links:lr},null,2),t=`${zf}.${process.pid}.tmp`;await tl.mkdir(Qk,{recursive:!0}),await tl.writeFile(t,e),await tl.rename(t,zf)}async function Ue(e){Vf=Vf.then(Jf,Jf),await Vf.catch(()=>{});let t=lr?.[e];t&&tt("prlinks",{id:e0(e),updatedAt:Date.now(),...t},{trackId:t.trackId??null}).catch(()=>{})}async function mt({repo:e,number:t,trackId:n,url:r=null,headRef:o=null,title:s=null,source:a="matched"}){if(!e||!Number.isInteger(Number(t))||!n)return{link:null,created:!1};let i=await Ce(),c=Q(e,t);return i[c]?{link:i[c],created:!1}:(i[c]={repo:e,number:Number(t),trackId:n,url:r,headRef:o,title:s,source:a,announced:aM.has(a),firstSeen:Date.now()},await Ue(Q(e,t)),{link:i[c],created:!0})}async function wn(e,t){if(!e||t==null)return null;let n=(await Ce())[Q(e,t)];return!n||n.announced?null:(n.announced=!0,await Ue(Q(e,t)),n)}async function qe(e,t){return!e||t==null?null:(await Ce())[Q(e,t)]??null}async function ur(e,t,n){if(!e||t==null)return null;let r=(await Ce())[Q(e,t)];return r?(r.state=n,r.resolvedAt=Date.now(),delete r.armedAt,delete r.autoMerge,delete r.archiveOnMerge,delete r.notifyOnMerge,delete r.notifyPending,await Ue(Q(e,t)),r):null}async function t0(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ce())[Q(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await Ue(Q(e,t)),o):null}async function n0(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Ce())[Q(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await Ue(Q(e,t)),o):null}async function nl(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ce())[Q(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await Ue(Q(e,t)),r)}async function rl(e,t){if(!e||t==null)return null;let n=(await Ce())[Q(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await Ue(Q(e,t)),n)}async function ol(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Ce())[Q(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await Ue(Q(e,t)),r)}async function sl(e,t){if(!e||t==null)return null;let n=(await Ce())[Q(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await Ue(Q(e,t)),n)}async function r0(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await Ce())[Q(e,t)];return r?(r.baseFoldAt=n,await Ue(Q(e,t)),r):null}async function o0(e,t){if(!e||t==null)return null;let n=(await Ce())[Q(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await Ue(Q(e,t)),n)}async function s0(e,t,n){if(!e||t==null||!n)return null;let r=(await Ce())[Q(e,t)];return r?(r.lastHeadSha=n,await Ue(Q(e,t)),r):null}async function a0(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await Ce())[Q(e,t)];if(!r)return{prev:null,link:null};let o=r.verdict??null;return o&&o.blocked===(n.blocked??null)&&(o.sha??null)===(n.sha??null)||(r.verdict={blocked:n.blocked??null,sha:n.sha??null,at:n.at??Date.now()},await Ue(Q(e,t))),{prev:o,link:r}}async function Ba(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await Ce())[Q(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await Ue(Q(e,t)),r):null}async function Zr(e,t,n=!0){if(!e||t==null)return null;let r=(await Ce())[Q(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await Ue(Q(e,t)),r):null}async function ja(e,t,n=!0){if(!e||t==null)return null;let r=(await Ce())[Q(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await Ue(Q(e,t)),r):null}async function i0(e,t,n){if(!e||t==null)return null;let r=(await Ce())[Q(e,t)];if(!r)return null;r.notifyOnMerge=!0;let o=new Map((r.notifyPending||[]).map(s=>[`${s.repo}#${s.number}`,s]));for(let s of n||[])s&&s.number!=null&&o.set(`${s.repo}#${s.number}`,s);return r.notifyPending=[...o.values()],await Ue(Q(e,t)),r}async function La(e,t){if(!e||t==null)return null;let n=(await Ce())[Q(e,t)];return!n||!n.armedAt&&!n.autoMerge?null:(delete n.armedAt,delete n.autoMerge,delete n.archiveOnMerge,delete n.notifyOnMerge,delete n.notifyPending,await Ue(Q(e,t)),n)}async function c0(){return Object.values(await Ce()).filter(e=>!iM.has(e.state))}async function Qr(){return Object.values(await Ce()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function l0(){if(!Ee())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Fe("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Ce(),r=0;for(let[o,s]of Object.entries(n))await tt("prlinks",{id:e0(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var Qk,zf,Q,e0,lr,Gf,Vf,aM,iM,gn=C(()=>{Gn();Qk=process.env.BOSTRAT_STATE_DIR||Zk.join(process.env.HOME||sM.tmpdir(),".bostrat"),zf=Zk.join(Qk,"pr-links.json"),Q=(e,t)=>`${e}#${Number(t)}`,e0=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=oM.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},lr=null,Gf=null,Vf=Promise.resolve();aM=new Set(["api","observed","reconciled"]);iM=new Set(["merged","closed","lost"])});import cM from"node:fs";import Yf from"node:fs/promises";import p0 from"node:path";import lM from"node:os";function w0(e,t=!1){if(!t&&e-d0<uM)return;d0=e;let n=JSON.stringify({windowStart:os,byCaller:Object.fromEntries(yn),lastHour:dr});f0=f0.then(()=>Yf.mkdir(m0,{recursive:!0})).then(()=>Yf.writeFile(`${al}.tmp`,n)).then(()=>Yf.rename(`${al}.tmp`,al)).catch(()=>{})}function g0(e){e-os<h0||(dr={start:os,total:il(yn),byCaller:Object.fromEntries(yn)},dr.total&&console.log(`[github] ${dr.total} calls last hour`,dr.byCaller),os=e,yn=new Map,w0(e,!0))}function bn(e="other",t=Date.now()){g0(t),yn.set(e,(yn.get(e)||0)+1),w0(t)}function y0(e=Date.now()){return g0(e),{windowStart:os,total:il(yn),byCaller:Object.fromEntries(yn),lastHour:dr,backoffUntil:eo>e?eo:null}}function fr(e){return dM.test(String(e?.message||e||""))}function pr(e,t=Date.now()){return eo<=t&&console.warn(`[github] rate limited \u2014 backing off ${u0/6e4} min:`,String(e?.message||e||"").slice(0,200)),eo=Math.max(eo,t+u0),eo}function cl(e=Date.now()){return Math.max(0,eo-e)}var m0,al,h0,u0,uM,os,yn,dr,eo,d0,il,f0,dM,Fa=C(()=>{m0=process.env.BOSTRAT_STATE_DIR||p0.join(process.env.HOME||lM.tmpdir(),".bostrat"),al=p0.join(m0,"github-metrics.json"),h0=60*6e4,u0=5*6e4,uM=6e4,os=Date.now(),yn=new Map,dr=null,eo=0,d0=0,il=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse(cM.readFileSync(al,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<h0?(os=e.windowStart,yn=new Map(Object.entries(e.byCaller||{})),dr=e.lastHour||null):il(new Map(Object.entries(e.byCaller||{})))&&(dr={start:e.windowStart,total:il(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}f0=Promise.resolve();dM=/rate limit|too quickly|abuse detection|HTTP 429/i});import b0 from"node:fs/promises";import k0 from"node:path";import fM from"node:os";function ul({change:e,repo:t=null,number:n=null,githubAt:r=null,observedAt:o=Date.now(),lane:s=null,intervalMs:a=null,source:i=null}){let c=r?o-r:null,l={change:e,repo:t,number:n,githubAt:r,observedAt:o,latencyMs:c,lane:s,intervalMs:a,source:i};ll.push(l),ll.length>mM&&ll.shift();let u=Xf.get(e)||{count:0,measured:0,totalMs:0,maxMs:0};return u.count+=1,c!=null&&(u.measured+=1,u.totalMs+=c,u.maxMs=Math.max(u.maxMs,c)),Xf.set(e,u),hM(l),l}async function hM(e){try{await b0.mkdir(x0,{recursive:!0}),await b0.appendFile(pM,JSON.stringify(e)+`
|
|
142
|
+
`)}catch{}}function S0(){let e={};for(let[t,n]of Xf)e[t]={count:n.count,measured:n.measured,avgMs:n.measured?Math.round(n.totalMs/n.measured):null,maxMs:n.measured?n.maxMs:null};return{byChange:e,recent:ll.slice(-20)}}var x0,pM,mM,ll,Xf,dl=C(()=>{x0=process.env.BOSTRAT_STATE_DIR||k0.join(process.env.HOME||fM.tmpdir(),".bostrat"),pM=k0.join(x0,"github-latency.ndjson"),mM=100,ll=[],Xf=new Map});import{execFile as wM}from"node:child_process";import{promisify as gM}from"node:util";async function yM(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([as("git",["-C",e,"rev-parse","HEAD"]),as("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await _({id:t,cwd:e},{type:"code.pushed",actor:"orchestrator",payload:{ref:r.trim(),sha:n.trim(),wip:!1,ts:Date.now()}})}catch{}}async function vt(e,t,n={}){let{caller:r="prs",...o}=n,s=await qt({cwd:e});bn(r);try{let{stdout:a}=await as("gh",t,{cwd:e,...bM,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw fr(a)&&pr(a),a}}async function ep(){if(fl&&Date.now()-fl.at<6e4)return fl.data;let e;try{bn("auth");let t=await as("gh",["auth","status"],{timeout:1e4});e={installed:!0,authenticated:!0,login:`${t.stdout||""}${t.stderr||""}`.match(/account\s+(\S+)/)?.[1]??null}}catch(t){e=t?.code==="ENOENT"||t?.code===127?{installed:!1,authenticated:!1,login:null}:{installed:!0,authenticated:!1,login:null}}if(e.installed&&!e.authenticated)try{let t=await Zs();t.configured&&(e={installed:!0,authenticated:!0,login:t.username,via:"broker"})}catch{}return fl={at:Date.now(),data:e},e}async function kM(e){try{let{stdout:t}=await as("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function T0(e){let t=e.name||e.context||"check",n=e.detailsUrl||e.targetUrl||null,r=e.completedAt||e.createdAt||null,o;return e.__typename==="StatusContext"?o=e.state==="SUCCESS"?"pass":e.state==="PENDING"||e.state==="EXPECTED"?"pending":"fail":e.status&&e.status!=="COMPLETED"?o="pending":["SUCCESS","NEUTRAL","SKIPPED"].includes(e.conclusion)?o="pass":o="fail",{name:t,state:o,url:n,completedAt:r}}function Zf(e){let t=(e||[]).map(T0),n=t.filter(s=>s.state==="pending").length,r=t.map(s=>Date.parse(s.completedAt||"")).filter(s=>s>0),o=t.length&&!n&&r.length?Math.max(...r):null;return{total:t.length,passed:t.filter(s=>s.state==="pass").length,failed:t.filter(s=>s.state==="fail").length,pending:n,failing:t.filter(s=>s.state==="fail").map(({name:s,url:a})=>({name:s,url:a})),settledAt:o}}function np(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function E0(e,t){if(!t)return null;let n=await me(e);if(!n)return null;try{let r=await vt(e,["api",`repos/${n}/actions/runs?head_sha=${encodeURIComponent(t)}&per_page=100`,"--jq",".workflow_runs"]),o=new Map;for(let s of JSON.parse(r||"[]"))o.has(s.name)||o.set(s.name,s);return[...o.values()]}catch{return null}}function _0(e){return(e||[]).map(t=>({name:t.name,detailsUrl:t.html_url||null,status:String(t.status||"").toUpperCase(),conclusion:t.conclusion?String(t.conclusion).toUpperCase():null,completedAt:t.status==="completed"&&t.updated_at||null}))}async function I0(e,t){let n=await E0(e,t);return n===null?{...Zf([]),unavailable:!0}:Zf(_0(n))}function pl(e,t=Zf(e.statusCheckRollup)){return{number:e.number,title:e.title,url:e.url,author:e.author?.login??null,headRefName:e.headRefName,headSha:e.headRefOid??null,baseRefName:e.baseRefName,isDraft:!!e.isDraft,reviewDecision:e.reviewDecision||null,mergeable:e.mergeable||"UNKNOWN",mergeStateStatus:e.mergeStateStatus||"UNKNOWN",additions:e.additions??0,deletions:e.deletions??0,createdAt:e.createdAt,updatedAt:e.updatedAt,checks:t,green:t.failed===0&&t.pending===0&&e.mergeStateStatus==="CLEAN",autoMergeArmed:!!e.autoMergeRequest,mergeabilityPending:!1}}function SM(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){ss.delete(s);continue}r.add(s),ss.has(s)||ss.set(s,n),o.mergeabilityPending=n-ss.get(s)<xM}for(let o of ss.keys())o.startsWith(`${e}#`)&&!r.has(o)&&ss.delete(o);return t}async function is(e,t){let n=await kn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{let o=await vt(n.path,["pr","view",String(r),"--json",tp]);return pl(JSON.parse(o))}catch(o){if(!np(o))throw o;let s=await vt(n.path,["pr","view",String(r),"--json",A0]),a=JSON.parse(s);return pl(a,await I0(n.path,a.headRefOid))}}async function Jt(e,{caller:t="prs"}={}){try{let n=await vt(e,["pr","list","--json",tp,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>pl(r)).sort(v0)}catch(n){if(!np(n))throw n;let r=await vt(e,["pr","list","--json",A0,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>pl(o,await I0(e,o.headRefOid))))).sort(v0)}}function Xt(){Ua.clear()}async function AM(e,t){if(!e)return t;for(let n of t){let r=await qe(e,n.number).catch(()=>null);if(!r)continue;r.trackId&&(n.trackId=r.trackId),r.autoMerge&&(n.autoMergeArmed=!0),r.archiveOnMerge&&(n.archiveOnMerge=!0),r.notifyOnMerge&&(n.notifyOnMerge=!0);let o=s=>n.headSha&&s.headSha&&n.headSha!==s.headSha;r.ciFix&&(o(r.ciFix)?await nl(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await ol(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function R0(){let e=await ep();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await un())await kM(a.path)&&t.push(a);let n=Date.now(),r=cl(n)>0,o=await Promise.all(t.map(async a=>{let i=Ua.get(a.path),c=i?.pending?TM:vM;if(i&&(r||n-i.at<c))return i.entry;try{let l=await me(a.path).catch(()=>null),u=SM(a.path,await Jt(a.path,{caller:"tab"}),n);if(await AM(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&&ul({change:p.checks.failed>0?"checks-failed":"checks-green",repo:l||a.name,number:p.number,githubAt:p.checks.settledAt,source:"tab"});let d={name:a.name,path:a.path,prs:u},f=u.some(p=>p.mergeabilityPending||p.checks.pending>0||p.autoMergeArmed);return Ua.set(a.path,{at:Date.now(),entry:d,pending:f}),d}catch(l){return i?i.entry:{name:a.name,path:a.path,prs:[],error:Yt(l)}}})),s=new Set(t.map(a=>a.path));for(let a of Ua.keys())s.has(a)||Ua.delete(a);return{auth:e,repos:o}}async function kn(e){let t=(await un()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function to(e){return So(e)}async function cs(e,t,{method:n="squash"}={}){let r=await kn(e);if(!Ha.has(n))throw new Error(`invalid merge method: ${n}`);let o=Number(t);if(!Number.isInteger(o)||o<=0)throw new Error("invalid PR number");let s=["pr","merge",String(o),`--${n}`];if(n!=="rebase"&&(await qt({cwd:r.path})).GH_TOKEN){let a=await Ku().catch(()=>null);a&&s.push("--body",`Merged by bostrat on behalf of @${a}`)}try{await vt(r.path,s)}catch(a){throw new Error(Yt(a))}finally{Xt()}return{ok:!0,number:o}}async function Wa(e,t){let n=await kn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await vt(n.path,["api","-X","PUT",`repos/{owner}/{repo}/pulls/${r}/update-branch`])}catch(s){throw new Error(Yt(s))}finally{Xt()}let o=await me(n.path).catch(()=>null);return o&&await r0(o,r).catch(()=>{}),{ok:!0,number:r}}async function rp(e,t){let n=await kn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");try{await vt(n.path,["pr","merge",String(r),"--disable-auto"])}catch(o){throw new Error(Yt(o))}finally{Xt()}return{ok:!0,number:r}}function op(e){return e.green&&!e.isDraft}function RM(e){return e.isDraft?"draft":e.mergeable==="CONFLICTING"?`conflicts with ${e.baseRefName}`:e.checks.failed>0?"checks failing":e.checks.pending>0?"checks running":e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN"?"mergeability unresolved":e.mergeStateStatus==="BEHIND"?`behind ${e.baseRefName}`:e.mergeStateStatus==="BLOCKED"?"blocked by branch protection":`not clean (${e.mergeStateStatus})`}function $0(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:op(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function O0(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function C0(){let e={};for(let[t,n]of Qf)e[t]={...n};return e}async function M0(e,{method:t="merge",onMerged:n=null}={}){let r=await kn(e);if(!Ha.has(t))throw new Error(`invalid merge method: ${t}`);if(Qf.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Jt(r.path)).filter(op).sort((a,i)=>Date.parse(a.createdAt)-Date.parse(i.createdAt));if(!o.length)throw new Error("no PRs ready to merge");let s={running:!0,startedAt:Date.now(),finishedAt:null,method:t,total:o.length,current:o[0].number,queue:o.map(a=>({number:a.number,title:a.title})),merged:[],skipped:[],failed:[]};return Qf.set(r.path,s),OM(s,o,{listPrs:()=>Jt(r.path),merge:a=>cs(r.path,a,{method:t}),sleep:IM,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function $M(e,t){let n=Date.now()+(t.settleMaxMs??_M),r=null;for(;;){let o=null;try{if(o=await t.listPrs(),r=null,!o.some(a=>e.includes(a.number)&&(a.mergeable==="UNKNOWN"||a.mergeStateStatus==="UNKNOWN")))return o}catch(s){r=s}if(Date.now()>=n){if(o)return o;if(r)throw r;return[]}await t.sleep(EM)}}async function OM(e,t,n){try{let r=t;for(let o=0;o<t.length;o++){let s=t[o];if(e.current=s.number,o>0)try{r=await $M(t.slice(o).map(i=>i.number),n)}catch(i){for(let c of t.slice(o))e.failed.push({number:c.number,title:c.title,reason:`couldn't re-check PR state: ${Yt(i)}`});break}let a=r.find(i=>i.number===s.number);if(!a){e.skipped.push({number:s.number,title:s.title,reason:"no longer open"});continue}if(!op(a)){e.skipped.push({number:a.number,title:a.title,reason:RM(a)});continue}try{await n.merge(a.number),e.merged.push({number:a.number,title:a.title});try{await n.onMerged?.(a)}catch{}}catch(i){e.failed.push({number:a.number,title:a.title,reason:Yt(i)})}}}finally{e.current=null,e.running=!1,e.finishedAt=Date.now(),Xt()}}async function sp(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await as("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Yt(l)}`)}await yM(e);let o=n||"";if((await qt({cwd:e})).GH_TOKEN){let l=await Ku().catch(()=>null);l&&(o=`${o?`${o}
|
|
143
|
+
|
|
144
|
+
`:""}Opened by bostrat on behalf of @${l}`)}let s=["pr","create","--title",t.trim(),"--body",o];r&&s.push("--base",r);let a;try{a=await vt(e,s)}catch(l){throw new Error(Yt(l))}finally{Xt()}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 N0(e,t){let n=await kn(e),r=Number(t);if(!Number.isInteger(r)||r<=0)throw new Error("invalid PR number");let o;try{let i;try{i=await vt(n.path,["pr","checks",String(r),"--json","name,state,link,bucket"])}catch(c){if(i=String(c?.stdout||""),!i.trim())throw new Error(Yt(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!np(i))throw i;let c=await vt(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await E0(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(Yt(i));o=_0(l).map(T0).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=CM(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await vt(n.path,["run","view",c,"--log-failed"]).then(u=>MM(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function CM(e){return String(e||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function MM(e,{maxLines:t=400,maxBytes:n=64*1024}={}){let r=String(e||""),o=r.split(`
|
|
142
145
|
`);return o.length>t&&(r=`\u2026 (${o.length-t} earlier lines truncated)
|
|
143
146
|
`+o.slice(-t).join(`
|
|
144
147
|
`)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
|
|
145
|
-
`+r.slice(-n)),r}function
|
|
146
|
-
`).find(n=>n.trim())||"gh failed"}var
|
|
147
|
-
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+$M(a)+"[ \\t]*$","m").exec(t.slice(i+1));if(!l)continue;let u=i+1+l.index+l[0].length;t=t.slice(0,i)+t.slice(u)}return t}function Xf(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function M0(e){let t=O0(e),n=t.match(_M);if(!n)return null;let r=Xf(t,n.index);return RM.test(r)?null:{repo:r.match(C0)?.[1]??null,head:r.match(OM)?.[1]??null}}function N0(e){let t=O0(e),n=t.match(IM);if(!n)return null;let r=Xf(t,n.index),o=Xf(t,n.index+n[0].length),s=null,a=o.trim().split(/\s+/).filter(Boolean);for(let i=0;i<a.length;i++){let c=a[i];if(c.startsWith("-")){MM.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(C0)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function PM(e,t,{exclude:n=()=>!1}={}){let r=async i=>{yn("observer");let{stdout:c}=await _0("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",NM],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw lr(l)&&ur(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-TM&&!n(c)),s=await qt({slug:e}),a=o(await r(s));return!a.length&&s.GH_TOKEN&&(a=o(await r({}).catch(()=>[]))),a.sort((i,c)=>Date.parse(c.createdAt)-Date.parse(i.createdAt))}async function DM(e,t){if(t)return[t];let n=[];for(let r of P0(e)){let o=await me(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await cn().catch(()=>[])){let o=await me(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function P0(e){let t=e.workspace;return t?.kind==="multi"?(t.repos??[]).map(n=>n?.base).filter(Boolean):[t?.base||e.cwd].filter(Boolean)}async function BM(e,t,n){let r=await os(e).catch(()=>[e.workspace?.branch].filter(Boolean)),o=t?.head??null,s=c=>r.includes(c)||o&&c===o,a=c=>o!=null&&!s(c.headRefName)||CM(c.headRefName,e.id),i=[];for(let c of await DM(e,t?.repo??null)){let l;try{l=await PM(c,n,{exclude:a})}catch{continue}for(let u of l){let f=await qe(c,u.number);if(s(u.headRefName)){if(f&&f.trackId===e.id&&f.announced)return{settled:!0};if(f&&f.trackId!==e.id)continue;return{slug:c,pr:u}}f&&(f.trackId!==e.id||f.announced)||i.push({slug:c,pr:u})}}return i.length?(i.sort((c,l)=>Date.parse(l.pr.createdAt)-Date.parse(c.pr.createdAt)),i[0]):null}function D0(e,t,{delays:n=I0,turnEnd:r}={}){let o=M0(t.command);if(!o)return null;let s=!!N0(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(dr.has(a))return null;dr.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(h=>setTimeout(h,l));let u=await BM(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:f,pr:d}=u,{link:p,created:m}=await mt({repo:f,number:d.number,trackId:e.id,url:d.url,headRef:d.headRefName,title:d.title,source:"observed"});return!m&&!(p?.trackId===e.id&&await hn(f,d.number))?null:(await Yf(t.box,t.agent,r),await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"opened",number:d.number,url:d.url,title:d.title,branch:d.headRefName,repo:f,...s?{selfMerge:!0}:{}}}),d)}return null}finally{dr.delete(a)}})();return c.catch(()=>dr.delete(a)),c}function B0(e,t,{delays:n=I0,turnEnd:r,openedWait:o}={}){let s=N0(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(dr.has(a))return null;dr.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=P0(e);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let h of m){let g=await me(h).catch(()=>null);g&&!f.includes(g)&&f.push(g)}}if(l||=(await os(e).catch(()=>[])).filter(m=>m!==e.workspace?.branch)[0]||e.workspace?.branch,!f.length||!l)return null;c=f[0];let d=async(m,h)=>{let g=await qe(c,m.number);return g&&g.trackId!==e.id?null:(g||await mt({repo:c,number:m.number,trackId:e.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await Ra(c,m.number),h&&((await L(e).catch(()=>[])).some(E=>E.type==="pr.state.changed"&&(E.payload?.change==="auto-armed"||E.payload?.change==="merged")&&E.payload?.number===m.number)||(await Yf(t.box,t.agent,r),await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c}}))),m)},p=null;for(let m of n){await new Promise(N=>setTimeout(N,m));let h=[];for(let N of f)try{yn("observer");let{stdout:C}=await _0("gh",["pr","view",String(l),"--repo",N,"--json",jM],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await qt({slug:N})}}),T=JSON.parse(C||"null");T?.number&&h.push({slug:N,pr:T})}catch(C){lr(C)&&ur(C)}let g=h.find(N=>N.pr.state==="MERGED")??(h.length===1?h[0]:null);if(!g)continue;c=g.slug;let x=g.pr;if(!x?.number)continue;if(x.state==="CLOSED")return null;if(x.state!=="MERGED"){if(p=x,s.auto)return d(x,!0);continue}let k=await qe(c,x.number);if(k&&k.trackId!==e.id)return null;k||await mt({repo:c,number:x.number,trackId:e.id,url:x.url,headRef:x.headRefName,title:x.title,source:"merge-observed"}),await Yf(t.box,t.agent,r);let E=await L(e).catch(()=>[]);if(E.some(N=>N.type==="pr.state.changed"&&N.payload?.change==="merged"&&N.payload?.number===x.number))return null;let I=N=>N.some(C=>C.type==="pr.state.changed"&&C.payload?.change==="opened"&&C.payload?.number===x.number),M=0;if(!I(E)){if(M0(t.command)&&await hn(c,x.number)){let N=Date.now();M=N+1,await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:N,change:"opened",number:x.number,url:x.url,title:x.title,branch:x.headRefName,repo:c,selfMerge:!0}})}else if((await qe(c,x.number))?.announced){let N=o?.capMs??EM,C=o?.pollMs??AM,T=Date.now()+N;for(;Date.now()<T&&(await new Promise(j=>setTimeout(j,C)),!I(await L(e).catch(()=>[]))););}}return await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Math.max(Date.now(),M),change:"merged",number:x.number,url:x.url,title:x.title,branch:x.headRefName,repo:c}}),await xn(c,x.number).catch(()=>{}),await kn(c,x.number).catch(()=>{}),await ir(c,x.number,"merged"),x}return p?d(p,!1):null}finally{dr.delete(a)}})();return i.catch(()=>dr.delete(a)),i}var _0,I0,SM,vM,TM,EM,AM,R0,$0,_M,IM,RM,A0,$M,C0,OM,CM,MM,NM,dr,jM,j0=O(()=>{ue();Un();Qe();Ca();wn();fr();Ba();ss();_0=xM(kM),I0=[4e3,15e3,45e3],SM=2500,vM=5*6e4;TM=15e3,EM=9e4,AM=2e3,R0=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,$0=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,_M=new RegExp(`${R0}${$0}create\\b`),IM=new RegExp(`${R0}${$0}merge\\b`),RM=/\s--web\b/,A0=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,$M=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");C0=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,OM=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,CM=E0;MM=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);NM="number,url,title,headRefName,createdAt";dr=new Set;jM="number,url,title,headRefName,state"});import LM from"node:fs/promises";import Qf from"node:path";import FM from"node:os";function Zf(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function ll(e){let t=UM.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=Zf(n,"from"),s=o?o.replace(/^uds:/,""):null,a=L0(s);return{socket:s,pid:a,name:Zf(n,"from-name"),mode:Zf(n,"from-mode"),text:r.trim()}}function L0(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function HM(){return process.env.CLAUDE_CONFIG_DIR?Qf.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):Qf.join(FM.homedir(),".claude","sessions")}async function WM(e){let t=L0(e);if(!t)return null;try{let n=await LM.readFile(Qf.join(HM(),`${t}.json`),"utf8"),r=JSON.parse(n);return{pid:t,sessionId:r.sessionId??null,cwd:r.cwd??null,name:r.name??null,tmux:r.tmux??null}}catch{return null}}async function F0(e,t){let n=await WM(e?.socket),r=n?.cwd??null,o=null;if((n?.sessionId||r)&&typeof t=="function")try{o=await t(n?.sessionId??null,r)??null}catch{o=null}return{socket:e?.socket??null,pid:n?.pid??e?.pid??null,sessionId:n?.sessionId??null,cwd:r,tmux:n?.tmux??null,name:n?.name??e?.name??null,mode:e?.mode??null,trackId:o?.track?.id??null,trackName:o?.track?.name??null,boxId:o?.box?.name??null}}var UM,ep=O(()=>{UM=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function tp(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function np({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!tp(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),U0);try{let a=await H0(`${ke(r)}/v1/loops/${encodeURIComponent(e)}/report`,{method:"POST",headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN||""}`,"content-type":"application/json"},body:JSON.stringify({outcome:t,...n?{note:n}:{}}),signal:o.signal}),i=await a.json().catch(()=>({}));return a.ok?{ok:!0,source:"plane",status:a.status,run:i?.run||null}:{ok:!1,source:"plane",status:a.status,error:i?.error||`plane ${a.status}`}}catch(a){return{ok:!1,source:"unreachable",error:a?.message||String(a)}}finally{clearTimeout(s)}}async function W0(e,{provider:t=null,cls:n="other",status:r=null,text:o=""}={},{env:s=process.env,now:a=Date.now}={}){if(!e?.id||!String(e.name||"").startsWith("\u21BB"))return{ok:!1,source:"not-a-loop-track"};if(!tp(s))return{ok:!1,source:"unconfigured"};let i=ul.get(e.id)||0;if(a()-i<qM)return{ok:!1,source:"debounced"};ul.set(e.id,a());let c=await dl({env:s}),l=c.loops.find(d=>d.trackId===e.id);if(!l)return ul.delete(e.id),{ok:!1,source:c.source==="plane"?"no-loop":c.source};let u=`${t||"provider"} ${r?r+" ":""}${n}: ${String(o||"").replace(/\s+/g," ").trim()}`.slice(0,500),f=await np({loopId:l.id,outcome:"errored",note:u,env:s});return f.ok||ul.delete(e.id),{...f,loop:{id:l.id,name:l.name}}}async function dl({env:e=process.env}={}){if(!tp(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),U0);try{let r=await H0(`${ke(e)}/v1/loops`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`},signal:t.signal});if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json();return{loops:Array.isArray(o?.loops)?o.loops:[],source:"plane"}}catch(r){return{loops:[],source:"unreachable",error:r?.message||String(r)}}finally{clearTimeout(n)}}var U0,H0,qM,ul,rp=O(()=>{Qe();U0=1e4,H0=(...e)=>fetch(...e);qM=10*6e4,ul=new Map});import{randomUUID as KM}from"node:crypto";function ja(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function q0(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function K0(e){return`job|${e.box}|id:${e.id}`}async function sp(e){return op.has(e.id)?op.get(e.id):(fl.has(e.id)||fl.set(e.id,L(e).then(t=>{let n={keys:new Set,cmdN:0};for(let r of t)if((r.type==="turn.user"||r.type==="turn.agent"||r.type==="provider.error")&&r.payload?.text!=null){let o=r.type==="turn.user"?"user":"assistant";r.payload.sourceId&&n.keys.add(ja({role:o,sourceId:r.payload.sourceId})),n.keys.add(ja({role:o,ts:r.payload.ts,text:r.payload.text}))}else if(r.type==="peer.message"&&r.source==="transcript")r.payload?.sourceId&&n.keys.add(ja({role:"user",sourceId:r.payload.sourceId})),n.keys.add(ja({role:"user",ts:r.payload?.ts,text:r.payload?.keyText??""}));else if(r.type==="tool.called"&&r.source==="transcript"&&r.payload?.command){let o=r.payload;n.keys.add(q0({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(K0({id:r.payload.id,box:r.boxId}));return op.set(e.id,n),n}).finally(()=>fl.delete(e.id))),fl.get(e.id))}async function G0(e,t){let n=new Set;try{let r=(await sp(e)).keys;for(let o of t||[]){if(!o||o.ts==null||!String(o.text||"").trim()&&!o.images?.length)continue;let s=ja(o);if(r.has(s))continue;r.add(s);let a=o.role==="user"?ll(o.text):null;if(a){await _(e,{type:"peer.message",actor:"peer",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{text:a.text,ts:o.ts,keyText:String(o.text||"").slice(0,40),from:await F0(a,La),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=$g(o);if(i){await _(e,{type:"provider.error",actor:"system",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{cls:i.cls,code:i.code||void 0,status:i.status||void 0,requestId:i.requestId||void 0,text:o.text,ts:o.ts,model:o.model||null,boxRole:o.boxRole,sourceId:o.sourceId||void 0}}),W0(e,{provider:o.agent||null,cls:i.cls,status:i.status,text:o.text}).catch(()=>{});continue}if(o.role!=="user"&&o.box&&n.add(o.box),await _(e,{type:o.role==="user"?"turn.user":"turn.agent",actor:o.role==="user"?"user":"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{text:o.text,ts:o.ts,model:o.model||null,tools:o.tools||void 0,boxRole:o.boxRole,sourceId:o.sourceId||void 0,note:o.note||void 0,images:o.images?.length?o.images:void 0}}),o.role==="assistant"&&o.box===e.leadBox&&e.workspace?.branch){let c=Rk(o.text);if(c){let l=Ck(c),u;if(e.workspace?.kind==="multi"){let m=await as(e).catch(()=>[]);u=m.length?m.map(h=>({name:h.name,slug:h.repo??null})):void 0}let f=(e.workspace?.kind==="multi"?null:await rs(e.cwd).catch(()=>null))||e.workspace.branch,d=await k0(e,{branch:f,rebase:l}).catch(()=>null),p=d?null:Ok(c);if(d&&d.available.length===0)continue;await _(e,{type:"pr.offered",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{offerId:KM(),ts:o.ts,excerpt:c,branch:f,base:e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:o.boxRole,available:d?.available,defaults:d?.defaults,prState:d?.prState,openPr:d?.openPr||void 0,repos:d?.repos||u,offered:d?.defaults||p?.offered||void 0,toPr:p?.toPr||void 0}})}}}}catch{}return{agentTurnBoxes:n}}async function V0(e,t){try{let n=await sp(e),r=[...t||[]].filter(o=>o&&String(o.command||"").trim()).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=q0(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await _(e,{type:"tool.called",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id??null,tool:o.tool||null,command:o.command,description:o.description||"",summary:o.summary||o.command,ts:o.ts??null,boxRole:o.boxRole,n:n.cmdN}}),D0(e,o),B0(e,o))}}catch{}}async function z0(e,t){try{let n=await sp(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=K0(o);n.keys.has(s)||(n.keys.add(s),await _(e,{type:"job.started",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,detail:o.detail||null,ts:o.ts??null,boxRole:o.boxRole}}))}}catch{}}var op,fl,J0=O(()=>{ue();Yo();x0();j0();ss();ep();fr();gc();rp();op=new Map,fl=new Map});function GM(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
148
|
-
`)){let o=r.trim();if(!o||o[0]!=="{")continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s.payload||{};if(s.type==="turn_context"&&a.model&&(n=a.model),s.type==="event_msg"&&a.type==="token_count"&&a.info?.last_token_usage){let i=a.info.last_token_usage;t.push({ts:s.timestamp?Date.parse(s.timestamp):null,model:n,last:{input:i.input_tokens||0,cachedInput:i.cached_input_tokens||0,output:i.output_tokens||0,reasoningOutput:i.reasoning_output_tokens||0},occupancy:i.total_tokens??null,contextWindow:a.info.model_context_window??null})}}return t}async function VM(e,t){let n=`${e.id}:${t.name}`;if(!ap.has(n)){let r=await L(e),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===t.name&&s.payload?.codexTs!=null&&o.add(s.payload.codexTs);ap.set(n,o)}return ap.get(n)}async function Y0(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await Pr(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await VM(e,t),o=GM(await it(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||t.model||"gpt-5.5";await _(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:tr(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await _(e,{type:"context.usage",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{occupancy:a.occupancy,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}var ap,X0=O(()=>{ue();Br();Nt();xa();ap=new Map});import pl from"node:fs";import cp from"node:path";import zM from"node:os";async function XM({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(YM,{method:"POST",headers:{"content-type":"application/json",origin:"https://cursor.com",cookie:`WorkosCursorSessionToken=${t}`},body:JSON.stringify({teamId:0,startDate:String(e),endDate:String(Date.now()),page:o,pageSize:Z0})});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<Z0)break}return r}function ZM(e){if(!e)return null;let t=Number(e.timestamp)||null,n=e.tokenUsage||null,r=n?{input:n.inputTokens||0,output:n.outputTokens||0,cacheRead:n.cacheReadTokens||0,cacheWrite:n.cacheWriteTokens||0}:null,o=e.conversationId||null;return{id:[o||"?",t??"?",r?`${r.input}.${r.output}.${r.cacheRead}.${r.cacheWrite}`:"?"].join(":"),conversationId:o,ts:t,model:e.model||e.modelIntent||null,kind:e.kindLabel||e.kind||null,isHeadless:e.isHeadless===!0,tokens:r,vendorCents:n?.totalCents??null,chargedCents:e.chargedCents??null}}function ex(){try{return JSON.parse(pl.readFileSync(ml(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function eN(e){try{pl.mkdirSync(cp.dirname(ml()),{recursive:!0});let t=ml()+".tmp";pl.writeFileSync(t,JSON.stringify(e)),pl.renameSync(t,ml())}catch{}}async function nN({fetchImpl:e,now:t=Date.now()}={}){if(pr.rows&&t-pr.at<QM)return pr.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=ex(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await XM({sinceMs:o,cookie:n,fetchImpl:e});pr={at:t,rows:s.map(ZM).filter(Boolean),error:null}}catch(s){pr={at:t,rows:null,error:String(s?.message||s)}}return pr.rows}async function rN(e,t){let n=`${e.id}:${t.name}`;if(!ip.has(n)){let r=await L(e),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===t.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);ip.set(n,o)}return ip.get(n)}async function tx(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!Q0())return;let r=await nN({fetchImpl:n});if(!r||!r.length)return;let o=await Sb(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await rN(e,t),a=ex(),i=new Set(a.seen||[]),c=a.sinceMs||0;for(let l of r){if(!l.conversationId||l.conversationId!==o.chatId||s.has(l.id)||i.has(`${e.id}:${t.name}:${l.id}`))continue;s.add(l.id),i.add(`${e.id}:${t.name}:${l.id}`),l.ts&&l.ts>c&&(c=l.ts);let u=l.model||o.models[o.models.length-1]||"cursor-unknown",f=l.tokens||null,d=l.vendorCents!=null?l.vendorCents/100:0;!d&&f&&(d=tr(u,f)),!d&&l.chargedCents!=null&&(d=l.chargedCents/100),await _(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.name,provider:"cursor",payload:{usd:d,model:u,...f?{tokens:f}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}eN({sinceMs:c,seen:[...i].slice(-tN)})}catch{}}var JM,Q0,YM,Z0,QM,pr,ml,tN,lp,ip,up=O(()=>{ue();nf();xa();JM=process.env.BOSTRAT_STATE_DIR||cp.join(process.env.HOME||zM.tmpdir(),".bostrat"),Q0=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,YM="https://cursor.com/api/dashboard/get-filtered-usage-events",Z0=100;QM=5*6e4,pr={at:0,rows:null,error:null},ml=()=>cp.join(JM,"cursor-usage.json");tN=5e3;lp=()=>({enabled:Q0(),lastPollAt:pr.at||null,error:pr.error}),ip=new Map});import Ha from"node:fs/promises";import lt from"node:path";import oN from"node:os";import sN from"node:net";import{randomUUID as aN}from"node:crypto";import{execFile as iN}from"node:child_process";import{promisify as cN}from"node:util";function hN(e){for(let t of gp(e))if(!ix.test(t))return t;return null}function cx(e){let t=gp(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function wN(e,t){let n=cx(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return lt.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return lt.resolve(e,n[r+1])}return e}function yN(e){let t=!1,n=!1;for(let r of gp(e)){if(!t){if(ix.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&dp(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,dp(r))return`serves the filesystem root ("${r}")`;continue}if(dp(r))return`serves the filesystem root ("${r}")`;if(gN(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function kN(e,t){if(lt.resolve(e)===lt.resolve(t))return!0;let[n,r]=await Promise.all([hl(e),hl(t)]);return n===r}async function xN(e){let n=(await Ha.readdir(e,{withFileTypes:!0}).catch(()=>[])).filter(o=>o.isDirectory()&&!o.name.startsWith(".")&&o.name!=="node_modules");return(await Promise.all(n.map(async o=>await lx(lt.join(e,o.name,mp))?`./${o.name}`:null))).filter(Boolean)}async function SN(e,t){let[n,r]=await Promise.all([hl(e),hl(t)]),o=lt.relative(n,r);if(o&&(o.startsWith("..")||lt.isAbsolute(o)))throw Fa(`serve directory escapes the Track workspace: ${t}`)}async function vN(e,t){let n=yN(t);if(n)throw Fa(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await bN(e))throw Fa(`serve directory does not exist: ${e}`);let r=hN(t);if(!r)return;if(!r.includes("/")&&!await og(r,{loginShell:!0,failOpen:!0}))throw Fa(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(fN.test(t)||pN.has(r)||!dN.has(r)||!cx(t).includes("run")||mN.test(t))return;let o=wN(e,t);if(await lx(lt.join(o,mp)))return;let s=await xN(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Fa(`no ${mp} in ${o}${a}`)}async function Tn(){return Yt||(fp||=(async()=>{try{Yt=JSON.parse(await Ha.readFile(ax,"utf8"))}catch{Yt={views:[],adoptWatermarks:{}}}finally{fp=null}return Yt.views||=[],Yt.adoptWatermarks||={},Yt})(),fp)}async function Zt(){await at(ax,Yt)}function vn(e,t,n={}){return _({id:e.trackId,cwd:e.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,payload:{ts:Date.now(),change:t,viewId:e.id,name:e.name,slug:e.slug,port:e.port,cmd:e.cmd||null,...n}}).catch(()=>{})}function bp(e){return Ua.get(e)||null}function dx(e,{name:t,box:n}={}){let r=Ua.get(e);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o={trackId:e,name:t||"preview",box:n||null,step:"spinning-up",startedAt:Date.now(),finishedAt:null,viewId:null,error:null};return Ua.set(e,o),o}function hp(e,t){let n=Ua.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function wl(e,{error:t=null}={}){let n=Ua.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function fx(e){let t=[];for(let n of e?.events||[])n.type!=="view-spinup"||!n.box||!n.startedAt||!n.endedAt||t.push({box:n.box,from:n.startedAt,to:n.endedAt});return t}function wp(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function TN(e,t){let n=wp(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!ox.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!ox.has(s))return s}}function Wa(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=sN.connect({port:e,host:"127.0.0.1"}),o=s=>{r.destroy(),n(s)};r.setTimeout(t,()=>o(!1)),r.once("connect",()=>o(!0)),r.once("error",()=>o(!1))})}async function kp(e){if(process.platform!=="darwin"){await Xt("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await Xt("lsof",["-t","-sTCP:LISTEN","-i",`tcp:${e}`]))}catch{return}for(let n of t.split(/\s+/).filter(Boolean)){let r=Number(n);if(!(!r||r===process.pid))try{process.kill(r,"SIGKILL")}catch{}}}function px(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function EN(e,t){let n=new Set(e.views.map(o=>o.port)),r=px();if(t){let o=Number(t);if(!Number.isInteger(o)||o<1024||o>65535)throw new Error("invalid port");if(r.has(o))throw new Error(`port ${o} is the node's own API port`);if(n.has(o))throw new Error(`port ${o} is already owned by a view`);return o}for(let o=nx;o<=rx;o++)if(!(n.has(o)||r.has(o))&&!await Wa(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${nx}-${rx}`)}async function AN(e){try{let{stdout:t}=await Xt(F,H("list-sessions","-F","#{session_name}"));return t.split(`
|
|
149
|
-
`).some(n=>n.trim()===
|
|
148
|
+
`+r.slice(-n)),r}function Yt(e){return String(e?.stderr||e?.message||e||"").trim().split(`
|
|
149
|
+
`).find(n=>n.trim())||"gh failed"}var as,bM,fl,tp,A0,xM,ss,v0,vM,TM,Ua,Ha,EM,_M,IM,Qf,ls=C(()=>{Hn();et();gn();Fa();dl();ue();as=gM(wM),bM={timeout:3e4,maxBuffer:8*1024*1024};fl=null;tp="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",A0=tp.replace("statusCheckRollup,","");xM=9e4,ss=new Map;v0=(e,t)=>e.number-t.number;vM=3e4,TM=1500,Ua=new Map;Ha=new Set(["squash","merge","rebase"]);EM=2500,_M=9e4,IM=e=>new Promise(t=>setTimeout(t,e));Qf=new Map});import{execFile as NM}from"node:child_process";import{promisify as PM}from"node:util";async function BM(e,t){try{let{stdout:n}=await DM("git",["-C",e,"rev-list","--count",t]);return Number(n.trim())||0}catch{return null}}async function jM(e,t,n,{rebase:r=!1,listPrs:o=Jt}={}){let s=await Ma(e.dir,{ignorePlumbing:!0}).catch(()=>!1),a=await Na(e.dir,n).catch(()=>0),i=t?await BM(e.dir,`refs/remotes/origin/${t}..HEAD`):null,c=i==null?a>0:i>0,l=null,u="unknown";try{l=(await o(e.dir,{caller:"ship-state"})||[]).find(h=>h.headRefName===t)||null,u=l?"open":"none"}catch{}let d=s||a>0,f=[];s&&f.push("commit"),r&&f.push("rebase"),(s||c)&&f.push("push"),d&&(u!=="open"||s||c)&&f.push("pr");let p=f.filter(m=>!(m==="pr"&&u==="open"));return{name:e.name??null,slug:e.repo??null,dirty:s,unpushed:c,aheadBase:a,prState:u,openPr:l?{number:l.number,url:l.url??null,title:l.title??null}:null,available:ml.filter(m=>f.includes(m)),defaults:ml.filter(m=>p.includes(m))}}async function P0(e,{branch:t,rebase:n=!1,listPrs:r=Jt}={}){let o=e?.workspace,s=o?.kind==="multi"?(o.repos||[]).filter(f=>f?.dir):o?.base&&(o.dir||e?.cwd)?[{name:o.base?.split("/").pop()??null,dir:o.dir||e.cwd,repo:o.repo??null,baseRef:o.baseRef}]:[];if(!s.length||!await Jo(s[0].dir).catch(()=>!1))return null;let a=[];for(let f of s)a.push(await jM(f,t,f.baseRef||o.baseRef,{rebase:n,listPrs:r}));let i=a.filter(f=>f.dirty||f.aheadBase>0||f.openPr),c=i.length?i:a,l=ml.filter(f=>c.some(p=>p.available.includes(f))),u=ml.filter(f=>c.some(p=>p.defaults.includes(f))),d=c.filter(f=>f.openPr);return{available:l,defaults:u,prState:d.length?"open":c.every(f=>f.prState==="none")?"none":"unknown",openPr:c.length===1?c[0].openPr:null,repos:c.map(f=>({name:f.name,slug:f.slug,dirty:f.dirty,unpushed:f.unpushed,prState:f.prState,openPr:f.openPr}))}}var DM,ml,D0=C(()=>{Vc();ls();DM=PM(NM),ml=["commit","rebase","push","pr"]});function hl(e,t,n){let{state:r,reason:o}=Md(t,n);if(r!=="unknown"){if(e){let a=Date.now();for(let[i,c]of qa)a-c.at>=B0()&&qa.delete(i);qa.set(e,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!e)return!1;let s=qa.get(e);return s?Date.now()-s.at>=B0()?(qa.delete(e),!1):s.busy:!1}async function ap(e,t){let n={working:!1,prompted:!1};if(!e)return n;let r=await Te(e,{lines:je}).catch(()=>"");if(!hl(e,t,r))return cy(t,r)?{working:!0,prompted:!1}:n;if(Jr(t,r)){let o=await pn(e).catch(()=>r),s=ir({name:e,agent:t},o);if(s)return!Kc({name:e,agent:t},s,o)||sk(e)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function no(e,t){return(await ap(e,t)).working}var B0,qa,Ka=C(()=>{Ve();xa();If();$f();B0=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,qa=new Map});import{execFile as LM}from"node:child_process";import{promisify as FM}from"node:util";async function us(e){if(!e)return null;let t=j0.get(e);if(t&&Date.now()-t.at<HM)return t.branch;let n=null;try{let{stdout:r}=await UM("git",["-C",e,"symbolic-ref","--short","-q","HEAD"],{timeout:5e3});n=r.trim()||null}catch{n=null}return j0.set(e,{at:Date.now(),branch:n}),n}function L0(e){return String(e||"").match(WM)?.[1]??null}function qM(e,t){let n=L0(e);return!!n&&!!t&&n===t}function F0(e,t){let n=L0(e);return!!n&&n!==t}function KM(e){let t=e?.workspace;return t?.kind==="multi"?(t.repos??[]).map(n=>n?.dir).filter(Boolean):[t?.dir||e?.cwd].filter(Boolean)}async function ds(e){let t=[],n=r=>{r&&!t.includes(r)&&t.push(r)};n(e?.workspace?.branch??null);for(let r of KM(e))n(await us(r));return t}async function wl(e,t,n=()=>!0){if(!t)return null;let r=(e??[]).filter(a=>a&&n(a)),o=r.find(a=>a.workspace?.branch===t);if(o)return o;let s=r.find(a=>qM(t,a.id));if(s)return s;for(let a of r)if(!(a.archivedAt||a.transferredTo)&&(await ds(a)).includes(t))return a;return null}var UM,HM,j0,WM,fs=C(()=>{UM=FM(LM),HM=5e3,j0=new Map;WM=/^track\/.+-([0-9a-f]{8})$/});import{execFile as GM}from"node:child_process";import{promisify as VM}from"node:util";async function ip(e,t,{midTurn:n=no,pollMs:r=zM,capMs:o=JM}={}){let s=Date.now()+o;for(;await n(e,t).catch(()=>!1);){if(Date.now()>=s)return!1;await new Promise(a=>setTimeout(a,r))}return!0}function G0(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){U0.lastIndex=n;let o=U0.exec(t);if(!o)break;n=o.index+o[0].length;let s=o[1]==="-",a=o[2]??o[3]??o[4],i=t.indexOf(`
|
|
150
|
+
`,o.index);if(i<0)break;let l=new RegExp("^"+(s?"\\t*":"")+nN(a)+"[ \\t]*$","m").exec(t.slice(i+1));if(!l)continue;let u=i+1+l.index+l[0].length;t=t.slice(0,i)+t.slice(u)}return t}function cp(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function z0(e){let t=G0(e),n=t.match(QM);if(!n)return null;let r=cp(t,n.index);return tN.test(r)?null:{repo:r.match(V0)?.[1]??null,head:r.match(rN)?.[1]??null}}function J0(e){let t=G0(e),n=t.match(eN);if(!n)return null;let r=cp(t,n.index),o=cp(t,n.index+n[0].length),s=null,a=o.trim().split(/\s+/).filter(Boolean);for(let i=0;i<a.length;i++){let c=a[i];if(c.startsWith("-")){sN.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(V0)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function iN(e,t,{exclude:n=()=>!1}={}){let r=async i=>{bn("observer");let{stdout:c}=await H0("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",aN],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw fr(l)&&pr(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-YM&&!n(c)),s=await qt({slug:e}),a=o(await r(s));return!a.length&&s.GH_TOKEN&&(a=o(await r({}).catch(()=>[]))),a.sort((i,c)=>Date.parse(c.createdAt)-Date.parse(i.createdAt))}async function cN(e,t){if(t)return[t];let n=[];for(let r of Y0(e)){let o=await me(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await un().catch(()=>[])){let o=await me(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function Y0(e){let t=e.workspace;return t?.kind==="multi"?(t.repos??[]).map(n=>n?.base).filter(Boolean):[t?.base||e.cwd].filter(Boolean)}async function lN(e,t,n){let r=await ds(e).catch(()=>[e.workspace?.branch].filter(Boolean)),o=t?.head??null,s=c=>r.includes(c)||o&&c===o,a=c=>o!=null&&!s(c.headRefName)||oN(c.headRefName,e.id),i=[];for(let c of await cN(e,t?.repo??null)){let l;try{l=await iN(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await qe(c,u.number);if(s(u.headRefName)){if(d&&d.trackId===e.id&&d.announced)return{settled:!0};if(d&&d.trackId!==e.id)continue;return{slug:c,pr:u}}d&&(d.trackId!==e.id||d.announced)||i.push({slug:c,pr:u})}}return i.length?(i.sort((c,l)=>Date.parse(l.pr.createdAt)-Date.parse(c.pr.createdAt)),i[0]):null}function X0(e,t,{delays:n=W0,turnEnd:r}={}){let o=z0(t.command);if(!o)return null;let s=!!J0(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(mr.has(a))return null;mr.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(h=>setTimeout(h,l));let u=await lN(e,o,i).catch(()=>null);if(u?.settled)return null;if(!u)continue;let{slug:d,pr:f}=u,{link:p,created:m}=await mt({repo:d,number:f.number,trackId:e.id,url:f.url,headRef:f.headRefName,title:f.title,source:"observed"});return!m&&!(p?.trackId===e.id&&await wn(d,f.number))?null:(await ip(t.box,t.agent,r),await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"opened",number:f.number,url:f.url,title:f.title,branch:f.headRefName,repo:d,...s?{selfMerge:!0}:{}}}),f)}return null}finally{mr.delete(a)}})();return c.catch(()=>mr.delete(a)),c}function Z0(e,t,{delays:n=W0,turnEnd:r,openedWait:o}={}){let s=J0(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(mr.has(a))return null;mr.add(a);let i=(async()=>{try{let c=s.repo,l=s.selector,u=l?.match(/github\.com\/([\w.-]+\/[\w.-]+)\/pull\/(\d+)/);u&&([,c,l]=u);let d=[];if(c)d=[c];else{let m=Y0(e);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let h of m){let g=await me(h).catch(()=>null);g&&!d.includes(g)&&d.push(g)}}if(l||=(await ds(e).catch(()=>[])).filter(m=>m!==e.workspace?.branch)[0]||e.workspace?.branch,!d.length||!l)return null;c=d[0];let f=async(m,h)=>{let g=await qe(c,m.number);return g&&g.trackId!==e.id?null:(g||await mt({repo:c,number:m.number,trackId:e.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await Ba(c,m.number),h&&((await L(e).catch(()=>[])).some(A=>A.type==="pr.state.changed"&&(A.payload?.change==="auto-armed"||A.payload?.change==="merged")&&A.payload?.number===m.number)||(await ip(t.box,t.agent,r),await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Date.now(),change:"auto-armed",number:m.number,url:m.url,title:m.title,branch:m.headRefName,repo:c}}))),m)},p=null;for(let m of n){await new Promise(N=>setTimeout(N,m));let h=[];for(let N of d)try{bn("observer");let{stdout:O}=await H0("gh",["pr","view",String(l),"--repo",N,"--json",uN],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await qt({slug:N})}}),T=JSON.parse(O||"null");T?.number&&h.push({slug:N,pr:T})}catch(O){fr(O)&&pr(O)}let g=h.find(N=>N.pr.state==="MERGED")??(h.length===1?h[0]:null);if(!g)continue;c=g.slug;let x=g.pr;if(!x?.number)continue;if(x.state==="CLOSED")return null;if(x.state!=="MERGED"){if(p=x,s.auto)return f(x,!0);continue}let k=await qe(c,x.number);if(k&&k.trackId!==e.id)return null;k||await mt({repo:c,number:x.number,trackId:e.id,url:x.url,headRef:x.headRefName,title:x.title,source:"merge-observed"}),await ip(t.box,t.agent,r);let A=await L(e).catch(()=>[]);if(A.some(N=>N.type==="pr.state.changed"&&N.payload?.change==="merged"&&N.payload?.number===x.number))return null;let I=N=>N.some(O=>O.type==="pr.state.changed"&&O.payload?.change==="opened"&&O.payload?.number===x.number),M=0;if(!I(A)){if(z0(t.command)&&await wn(c,x.number)){let N=Date.now();M=N+1,await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:N,change:"opened",number:x.number,url:x.url,title:x.title,branch:x.headRefName,repo:c,selfMerge:!0}})}else if((await qe(c,x.number))?.announced){let N=o?.capMs??XM,O=o?.pollMs??ZM,T=Date.now()+N;for(;Date.now()<T&&(await new Promise(j=>setTimeout(j,O)),!I(await L(e).catch(()=>[]))););}}return await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"agent",source:"transcript",boxId:t.box??null,provider:t.agent??null,payload:{ts:Math.max(Date.now(),M),change:"merged",number:x.number,url:x.url,title:x.title,branch:x.headRefName,repo:c}}),await Sn(c,x.number).catch(()=>{}),await xn(c,x.number).catch(()=>{}),await ur(c,x.number,"merged"),x}return p?f(p,!1):null}finally{mr.delete(a)}})();return i.catch(()=>mr.delete(a)),i}var H0,W0,zM,JM,YM,XM,ZM,q0,K0,QM,eN,tN,U0,nN,V0,rN,oN,sN,aN,mr,uN,Q0=C(()=>{ue();Hn();et();Fa();gn();hr();Ka();fs();H0=VM(GM),W0=[4e3,15e3,45e3],zM=2500,JM=5*6e4;YM=15e3,XM=9e4,ZM=2e3,q0=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,K0=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,QM=new RegExp(`${q0}${K0}create\\b`),eN=new RegExp(`${q0}${K0}merge\\b`),tN=/\s--web\b/,U0=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,nN=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");V0=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,rN=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,oN=F0;sN=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);aN="number,url,title,headRefName,createdAt";mr=new Set;uN="number,url,title,headRefName,state"});import dN from"node:fs/promises";import up from"node:path";import fN from"node:os";function lp(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function gl(e){let t=pN.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=lp(n,"from"),s=o?o.replace(/^uds:/,""):null,a=ex(s);return{socket:s,pid:a,name:lp(n,"from-name"),mode:lp(n,"from-mode"),text:r.trim()}}function ex(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function mN(){return process.env.CLAUDE_CONFIG_DIR?up.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):up.join(fN.homedir(),".claude","sessions")}async function hN(e){let t=ex(e);if(!t)return null;try{let n=await dN.readFile(up.join(mN(),`${t}.json`),"utf8"),r=JSON.parse(n);return{pid:t,sessionId:r.sessionId??null,cwd:r.cwd??null,name:r.name??null,tmux:r.tmux??null}}catch{return null}}async function tx(e,t){let n=await hN(e?.socket),r=n?.cwd??null,o=null;if((n?.sessionId||r)&&typeof t=="function")try{o=await t(n?.sessionId??null,r)??null}catch{o=null}return{socket:e?.socket??null,pid:n?.pid??e?.pid??null,sessionId:n?.sessionId??null,cwd:r,tmux:n?.tmux??null,name:n?.name??e?.name??null,mode:e?.mode??null,trackId:o?.track?.id??null,trackName:o?.track?.name??null,boxId:o?.box?.name??null}}var pN,dp=C(()=>{pN=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function fp(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function pp({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!fp(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),nx);try{let a=await rx(`${ke(r)}/v1/loops/${encodeURIComponent(e)}/report`,{method:"POST",headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN||""}`,"content-type":"application/json"},body:JSON.stringify({outcome:t,...n?{note:n}:{}}),signal:o.signal}),i=await a.json().catch(()=>({}));return a.ok?{ok:!0,source:"plane",status:a.status,run:i?.run||null}:{ok:!1,source:"plane",status:a.status,error:i?.error||`plane ${a.status}`}}catch(a){return{ok:!1,source:"unreachable",error:a?.message||String(a)}}finally{clearTimeout(s)}}async function ox(e,{provider:t=null,cls:n="other",status:r=null,text:o=""}={},{env:s=process.env,now:a=Date.now}={}){if(!e?.id||!String(e.name||"").startsWith("\u21BB"))return{ok:!1,source:"not-a-loop-track"};if(!fp(s))return{ok:!1,source:"unconfigured"};let i=yl.get(e.id)||0;if(a()-i<wN)return{ok:!1,source:"debounced"};yl.set(e.id,a());let c=await bl({env:s}),l=c.loops.find(f=>f.trackId===e.id);if(!l)return yl.delete(e.id),{ok:!1,source:c.source==="plane"?"no-loop":c.source};let u=`${t||"provider"} ${r?r+" ":""}${n}: ${String(o||"").replace(/\s+/g," ").trim()}`.slice(0,500),d=await pp({loopId:l.id,outcome:"errored",note:u,env:s});return d.ok||yl.delete(e.id),{...d,loop:{id:l.id,name:l.name}}}async function bl({env:e=process.env}={}){if(!fp(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),nx);try{let r=await rx(`${ke(e)}/v1/loops`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`},signal:t.signal});if(!r.ok)throw new Error(`plane ${r.status}`);let o=await r.json();return{loops:Array.isArray(o?.loops)?o.loops:[],source:"plane"}}catch(r){return{loops:[],source:"unreachable",error:r?.message||String(r)}}finally{clearTimeout(n)}}var nx,rx,wN,yl,mp=C(()=>{et();nx=1e4,rx=(...e)=>fetch(...e);wN=10*6e4,yl=new Map});import{randomUUID as gN}from"node:crypto";function Ga(e){return e.sourceId?`${e.role}|id:${e.sourceId}`:`${e.role}|${e.ts??"?"}|${String(e.text||"").slice(0,40)}`}function sx(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function ax(e){return`job|${e.box}|id:${e.id}`}async function wp(e){return hp.has(e.id)?hp.get(e.id):(kl.has(e.id)||kl.set(e.id,L(e).then(t=>{let n={keys:new Set,cmdN:0};for(let r of t)if((r.type==="turn.user"||r.type==="turn.agent"||r.type==="provider.error")&&r.payload?.text!=null){let o=r.type==="turn.user"?"user":"assistant";r.payload.sourceId&&n.keys.add(Ga({role:o,sourceId:r.payload.sourceId})),n.keys.add(Ga({role:o,ts:r.payload.ts,text:r.payload.text}))}else if(r.type==="peer.message"&&r.source==="transcript")r.payload?.sourceId&&n.keys.add(Ga({role:"user",sourceId:r.payload.sourceId})),n.keys.add(Ga({role:"user",ts:r.payload?.ts,text:r.payload?.keyText??""}));else if(r.type==="tool.called"&&r.source==="transcript"&&r.payload?.command){let o=r.payload;n.keys.add(sx({id:o.id,box:r.boxId,ts:o.ts,command:o.command})),Number(o.n)>n.cmdN&&(n.cmdN=Number(o.n))}else r.type==="job.started"&&r.payload?.id&&n.keys.add(ax({id:r.payload.id,box:r.boxId}));return hp.set(e.id,n),n}).finally(()=>kl.delete(e.id))),kl.get(e.id))}async function ix(e,t){let n=new Set;try{let r=(await wp(e)).keys;for(let o of t||[]){if(!o||o.ts==null||!String(o.text||"").trim()&&!o.images?.length)continue;let s=Ga(o);if(r.has(s))continue;r.add(s);let a=o.role==="user"?gl(o.text):null;if(a){await _(e,{type:"peer.message",actor:"peer",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{text:a.text,ts:o.ts,keyText:String(o.text||"").slice(0,40),from:await tx(a,Va),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=Wg(o);if(i){await _(e,{type:"provider.error",actor:"system",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{cls:i.cls,code:i.code||void 0,status:i.status||void 0,requestId:i.requestId||void 0,text:o.text,ts:o.ts,model:o.model||null,boxRole:o.boxRole,sourceId:o.sourceId||void 0}}),ox(e,{provider:o.agent||null,cls:i.cls,status:i.status,text:o.text}).catch(()=>{});continue}if(o.role!=="user"&&o.box&&n.add(o.box),await _(e,{type:o.role==="user"?"turn.user":"turn.agent",actor:o.role==="user"?"user":"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{text:o.text,ts:o.ts,model:o.model||null,tools:o.tools||void 0,boxRole:o.boxRole,sourceId:o.sourceId||void 0,note:o.note||void 0,images:o.images?.length?o.images:void 0}}),o.role==="assistant"&&o.box===e.leadBox&&e.workspace?.branch){let c=qk(o.text);if(c){let l=Vk(c),u;if(e.workspace?.kind==="multi"){let m=await ps(e).catch(()=>[]);u=m.length?m.map(h=>({name:h.name,slug:h.repo??null})):void 0}let d=(e.workspace?.kind==="multi"?null:await us(e.cwd).catch(()=>null))||e.workspace.branch,f=await P0(e,{branch:d,rebase:l}).catch(()=>null),p=f?null:Gk(c);if(f&&f.available.length===0)continue;await _(e,{type:"pr.offered",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{offerId:gN(),ts:o.ts,excerpt:c,branch:d,base:e.workspace.baseRef?.replace(/^origin\//,"")||null,boxRole:o.boxRole,available:f?.available,defaults:f?.defaults,prState:f?.prState,openPr:f?.openPr||void 0,repos:f?.repos||u,offered:f?.defaults||p?.offered||void 0,toPr:p?.toPr||void 0}})}}}}catch{}return{agentTurnBoxes:n}}async function cx(e,t){try{let n=await wp(e),r=[...t||[]].filter(o=>o&&String(o.command||"").trim()).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=sx(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await _(e,{type:"tool.called",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id??null,tool:o.tool||null,command:o.command,description:o.description||"",summary:o.summary||o.command,ts:o.ts??null,boxRole:o.boxRole,n:n.cmdN}}),X0(e,o),Z0(e,o))}}catch{}}async function lx(e,t){try{let n=await wp(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=ax(o);n.keys.has(s)||(n.keys.add(s),await _(e,{type:"job.started",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{id:o.id,kind:o.kind||"agent",name:o.name||null,detail:o.detail||null,ts:o.ts??null,boxRole:o.boxRole}}))}}catch{}}var hp,kl,ux=C(()=>{ue();rs();D0();Q0();fs();dp();hr();Tc();mp();hp=new Map,kl=new Map});function yN(e){let t=[],n=null;for(let r of String(e||"").split(`
|
|
151
|
+
`)){let o=r.trim();if(!o||o[0]!=="{")continue;let s;try{s=JSON.parse(o)}catch{continue}let a=s.payload||{};if(s.type==="turn_context"&&a.model&&(n=a.model),s.type==="event_msg"&&a.type==="token_count"&&a.info?.last_token_usage){let i=a.info.last_token_usage;t.push({ts:s.timestamp?Date.parse(s.timestamp):null,model:n,last:{input:i.input_tokens||0,cachedInput:i.cached_input_tokens||0,output:i.output_tokens||0,reasoningOutput:i.reasoning_output_tokens||0},occupancy:i.total_tokens??null,contextWindow:a.info.model_context_window??null})}}return t}async function bN(e,t){let n=`${e.id}:${t.name}`;if(!gp.has(n)){let r=await L(e),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===t.name&&s.payload?.codexTs!=null&&o.add(s.payload.codexTs);gp.set(n,o)}return gp.get(n)}async function dx(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await Br(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await bN(e,t),o=yN(await ct(n.file,262144)),s=o[o.length-1];for(let a of o){if(a.ts==null||r.has(a.ts))continue;r.add(a.ts);let i={input:Math.max(0,a.last.input-a.last.cachedInput),output:a.last.output,cacheRead:a.last.cachedInput,cacheWrite:0},c=a.model||t.model||"gpt-5.5";await _(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:rr(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await _(e,{type:"context.usage",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{occupancy:a.occupancy,window:a.contextWindow,pct:a.occupancy!=null?a.occupancy/a.contextWindow:null,model:a.model||null,ts:a.ts}})}}catch{}}var gp,fx=C(()=>{ue();Lr();Pt();or();gp=new Map});import xl from"node:fs";import bp from"node:path";import kN from"node:os";async function vN({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(SN,{method:"POST",headers:{"content-type":"application/json",origin:"https://cursor.com",cookie:`WorkosCursorSessionToken=${t}`},body:JSON.stringify({teamId:0,startDate:String(e),endDate:String(Date.now()),page:o,pageSize:px})});if(!s.ok)throw new Error(`cursor usage feed: HTTP ${s.status}`);let a=await s.json(),i=a?.usageEventsDisplay||a?.usageEvents||[];if(r.push(...i),i.length<px)break}return r}function TN(e){if(!e)return null;let t=Number(e.timestamp)||null,n=e.tokenUsage||null,r=n?{input:n.inputTokens||0,output:n.outputTokens||0,cacheRead:n.cacheReadTokens||0,cacheWrite:n.cacheWriteTokens||0}:null,o=e.conversationId||null;return{id:[o||"?",t??"?",r?`${r.input}.${r.output}.${r.cacheRead}.${r.cacheWrite}`:"?"].join(":"),conversationId:o,ts:t,model:e.model||e.modelIntent||null,kind:e.kindLabel||e.kind||null,isHeadless:e.isHeadless===!0,tokens:r,vendorCents:n?.totalCents??null,chargedCents:e.chargedCents??null}}function hx(){try{return JSON.parse(xl.readFileSync(Sl(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function EN(e){try{xl.mkdirSync(bp.dirname(Sl()),{recursive:!0});let t=Sl()+".tmp";xl.writeFileSync(t,JSON.stringify(e)),xl.renameSync(t,Sl())}catch{}}async function IN({fetchImpl:e,now:t=Date.now()}={}){if(wr.rows&&t-wr.at<AN)return wr.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=hx(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await vN({sinceMs:o,cookie:n,fetchImpl:e});wr={at:t,rows:s.map(TN).filter(Boolean),error:null}}catch(s){wr={at:t,rows:null,error:String(s?.message||s)}}return wr.rows}async function RN(e,t){let n=`${e.id}:${t.name}`;if(!yp.has(n)){let r=await L(e),o=new Set;for(let s of r)s.type==="cost.recorded"&&s.boxId===t.name&&s.payload?.cursorEventId&&o.add(s.payload.cursorEventId);yp.set(n,o)}return yp.get(n)}async function wx(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!mx())return;let r=await IN({fetchImpl:n});if(!r||!r.length)return;let o=await Bb(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await RN(e,t),a=hx(),i=new Set(a.seen||[]),c=a.sinceMs||0;for(let l of r){if(!l.conversationId||l.conversationId!==o.chatId||s.has(l.id)||i.has(`${e.id}:${t.name}:${l.id}`))continue;s.add(l.id),i.add(`${e.id}:${t.name}:${l.id}`),l.ts&&l.ts>c&&(c=l.ts);let u=l.model||o.models[o.models.length-1]||"cursor-unknown",d=l.tokens||null,f=l.vendorCents!=null?l.vendorCents/100:0;!f&&d&&(f=rr(u,d)),!f&&l.chargedCents!=null&&(f=l.chargedCents/100),await _(e,{type:"cost.recorded",actor:"agent",source:"vendor",boxId:t.name,provider:"cursor",payload:{usd:f,model:u,...d?{tokens:d}:{},...l.vendorCents!=null?{vendorCents:l.vendorCents}:{},...l.chargedCents!=null?{chargedCents:l.chargedCents}:{},...l.kind?{kind:l.kind}:{},conversationId:l.conversationId,cursorEventId:l.id,ts:l.ts??void 0}})}EN({sinceMs:c,seen:[...i].slice(-_N)})}catch{}}var xN,mx,SN,px,AN,wr,Sl,_N,kp,yp,xp=C(()=>{ue();mf();or();xN=process.env.BOSTRAT_STATE_DIR||bp.join(process.env.HOME||kN.tmpdir(),".bostrat"),mx=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,SN="https://cursor.com/api/dashboard/get-filtered-usage-events",px=100;AN=5*6e4,wr={at:0,rows:null,error:null},Sl=()=>bp.join(xN,"cursor-usage.json");_N=5e3;kp=()=>({enabled:mx(),lastPollAt:wr.at||null,error:wr.error}),yp=new Map});import Ya from"node:fs/promises";import ut from"node:path";import $N from"node:os";import ON from"node:net";import{randomUUID as CN}from"node:crypto";import{execFile as MN}from"node:child_process";import{promisify as NN}from"node:util";function UN(e){for(let t of Ip(e))if(!Sx.test(t))return t;return null}function vx(e){let t=Ip(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function HN(e,t){let n=vx(t);for(let r=0;r<n.length;r++){let o=/^--prefix=(.+)$/.exec(n[r]);if(o)return ut.resolve(e,o[1]);if(n[r]==="--prefix"&&n[r+1])return ut.resolve(e,n[r+1])}return e}function qN(e){let t=!1,n=!1;for(let r of Ip(e)){if(!t){if(Sx.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&Sp(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,Sp(r))return`serves the filesystem root ("${r}")`;continue}if(Sp(r))return`serves the filesystem root ("${r}")`;if(WN(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function GN(e,t){if(ut.resolve(e)===ut.resolve(t))return!0;let[n,r]=await Promise.all([vl(e),vl(t)]);return n===r}async function VN(e){let n=(await Ya.readdir(e,{withFileTypes:!0}).catch(()=>[])).filter(o=>o.isDirectory()&&!o.name.startsWith(".")&&o.name!=="node_modules");return(await Promise.all(n.map(async o=>await Tx(ut.join(e,o.name,Ap))?`./${o.name}`:null))).filter(Boolean)}async function zN(e,t){let[n,r]=await Promise.all([vl(e),vl(t)]),o=ut.relative(n,r);if(o&&(o.startsWith("..")||ut.isAbsolute(o)))throw za(`serve directory escapes the Track workspace: ${t}`)}async function JN(e,t){let n=qN(t);if(n)throw za(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await KN(e))throw za(`serve directory does not exist: ${e}`);let r=UN(t);if(!r)return;if(!r.includes("/")&&!await gg(r,{loginShell:!0,failOpen:!0}))throw za(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(jN.test(t)||LN.has(r)||!BN.has(r)||!vx(t).includes("run")||FN.test(t))return;let o=HN(e,t);if(await Tx(ut.join(o,Ap)))return;let s=await VN(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw za(`no ${Ap} in ${o}${a}`)}async function An(){return Zt||(vp||=(async()=>{try{Zt=JSON.parse(await Ya.readFile(xx,"utf8"))}catch{Zt={views:[],adoptWatermarks:{}}}finally{vp=null}return Zt.views||=[],Zt.adoptWatermarks||={},Zt})(),vp)}async function en(){await it(xx,Zt)}function Tn(e,t,n={}){return _({id:e.trackId,cwd:e.cwd},{type:"view.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,payload:{ts:Date.now(),change:t,viewId:e.id,name:e.name,slug:e.slug,port:e.port,cmd:e.cmd||null,...n}}).catch(()=>{})}function $p(e){return Ja.get(e)||null}function Ex(e,{name:t,box:n}={}){let r=Ja.get(e);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o={trackId:e,name:t||"preview",box:n||null,step:"spinning-up",startedAt:Date.now(),finishedAt:null,viewId:null,error:null};return Ja.set(e,o),o}function Ep(e,t){let n=Ja.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function Tl(e,{error:t=null}={}){let n=Ja.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function _x(e){let t=[];for(let n of e?.events||[])n.type!=="view-spinup"||!n.box||!n.startedAt||!n.endedAt||t.push({box:n.box,from:n.startedAt,to:n.endedAt});return t}function _p(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function YN(e,t){let n=_p(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!bx.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!bx.has(s))return s}}function Xa(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=ON.connect({port:e,host:"127.0.0.1"}),o=s=>{r.destroy(),n(s)};r.setTimeout(t,()=>o(!1)),r.once("connect",()=>o(!0)),r.once("error",()=>o(!1))})}async function Op(e){if(process.platform!=="darwin"){await Qt("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await Qt("lsof",["-t","-sTCP:LISTEN","-i",`tcp:${e}`]))}catch{return}for(let n of t.split(/\s+/).filter(Boolean)){let r=Number(n);if(!(!r||r===process.pid))try{process.kill(r,"SIGKILL")}catch{}}}function Ix(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function XN(e,t){let n=new Set(e.views.map(o=>o.port)),r=Ix();if(t){let o=Number(t);if(!Number.isInteger(o)||o<1024||o>65535)throw new Error("invalid port");if(r.has(o))throw new Error(`port ${o} is the node's own API port`);if(n.has(o))throw new Error(`port ${o} is already owned by a view`);return o}for(let o=gx;o<=yx;o++)if(!(n.has(o)||r.has(o))&&!await Xa(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${gx}-${yx}`)}async function ZN(e){try{let{stdout:t}=await Qt(F,H("list-sessions","-F","#{session_name}"));return t.split(`
|
|
152
|
+
`).some(n=>n.trim()===ms(e))}catch{return!1}}async function Za(e){try{await Qt(F,H("kill-session","-t",ms(e)))}catch{}}async function Cp(e){if(!await da())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=ms(e.slug),n=e.serveCwd??e.cwd,r=ut.dirname(process.execPath);await Qt(F,H("new-session","-d","-s",t,"-c",n)),await Qt(F,H("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await Qt(F,H("send-keys","-t",t,"Enter")),await Qt(F,H("send-keys","-t",t,"-l",e.cmd)),await Qt(F,H("send-keys","-t",t,"Enter"))}async function kx(e,t=12){try{let{stdout:n}=await Qt(F,H("capture-pane","-t",ms(e),"-p"));return String(n||"").trimEnd().split(`
|
|
150
153
|
`).filter(Boolean).slice(-t).join(`
|
|
151
|
-
`)}catch{return""}}async function IN(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await AN(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await sx(e.slug)||"server exited before listening"),Sn.delete(e.id),await vn(e,e.state,e.error?{error:e.error}:{}),!0}if(await Wa(e.port))return Sn.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await vn(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>_N?(e.state="error",e.error=await sx(e.slug)||"never started listening",await vn(e,"error",{error:e.error}),!0):!1;let o=(Sn.get(e.id)||0)+1;return Sn.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function gl(){if(!pp){pp=!0;try{let e=await Tn(),t=!1;for(let n of e.views)await IN(n)&&(t=!0);t&&await Zt()}finally{pp=!1}}}async function yl({trackId:e}={}){await gl();let t=await Tn();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function mx(e){let n=(await Tn()).views.find(r=>r.id===e);return n?{...n}:null}function Sp(e){return Yt&&Yt.views.find(t=>t.slug===e)||null}async function vp({trackId:e,name:t,cmd:n,cwd:r,serveCwd:o,port:s,box:a,origin:i="agent",branch:c=null}){if(!e)throw new Error("trackId required");if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(i!=="detected"&&(typeof n!="string"||!n.trim()))throw new Error("cmd (string) required");if(!r)throw new Error("cwd required");let l=o!=null,u=lt.resolve(r,o??"."),f=i==="detected"?null:async()=>{await SN(r,u),await vN(u,n)},d=await Tn();if(i!=="detected"){let h=wp(t),g=String(n).trim(),x=d.views.find(k=>k.trackId===e&&(k.slug===h||wp(k.name)===h||k.cmd===g&<.resolve(k.serveCwd??k.cwd)===u));if(x)return RN(d,x,{cmd:g,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>=mr)throw new Error(`view cap reached (${mr} live) \u2014 stop one first`);let m={id:aN().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:TN(d,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await EN(d,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await xp(m),d.views.push(m),await Zt(),await vn(m,"created",{origin:i}),hp(e,m.id),i==="detected"&&await Wa(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await Zt(),await vn(m,"live")),{...m}}async function RN(e,t,{cmd:n,box:r,branch:o,serveCwd:s,validate:a}){r&&(t.box=r),o&&(t.branch=o);let i=t.cmd!==n,c=s!==void 0&&!await kN(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Wa(t.port))return await Zt(),hp(t.trackId,t.id),{...t,reused:!0};if(a&&await a(),e.views.filter(f=>f.id!==t.id&&(f.state==="live"||f.state==="starting")).length>=mr)throw new Error(`view cap reached (${mr} live) \u2014 stop one first`);return t.origin==="detected"?(await kp(t.port),t.origin="agent"):await qa(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),Sn.delete(t.id),await xp(t),await Zt(),await vn(t,"created",{restarted:!0,reused:!0}),hp(t.trackId,t.id),{...t,reused:!0}}async function hx(e){let n=(await Tn()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await kp(n.port):await qa(n.slug),n.state!=="stopped"&&(n.state="stopped",Sn.delete(n.id),await Zt(),await vn(n,"stopped")),{...n}}async function wx(e){let t=await Tn(),n=t.views.find(o=>o.id===e);if(!n)throw new Error("view not found");if(!n.cmd)throw new Error("a detected view has no known command \u2014 recreate it as a managed view");if(t.views.filter(o=>o.id!==e&&(o.state==="live"||o.state==="starting")).length>=mr)throw new Error(`view cap reached (${mr} live) \u2014 stop one first`);return await qa(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),Sn.delete(n.id),await xp(n),await Zt(),await vn(n,"created",{restarted:!0}),{...n}}async function gx(e){let t=await Tn(),n=t.views.findIndex(o=>o.id===e);if(n===-1)throw new Error("view not found");let r=t.views[n];return r.origin==="detected"?await kp(r.port):await qa(r.slug),t.views.splice(n,1),Sn.delete(r.id),await Zt(),await vn(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function yx(e){let t=await Tn(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await qa(r.slug),Sn.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await Zt()),n.length}async function bx(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await Tn(),r=n.adoptWatermarks[e.id]??null,o=t.filter(c=>c.role==="assistant"&&c.ts!=null&&(r==null?!1:c.ts>r)),s=t.reduce((c,l)=>l.ts>c?l.ts:c,r??0);if(n.adoptWatermarks[e.id]!==s&&(n.adoptWatermarks[e.id]=s,await Zt()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll($N)){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(!px().has(c)&&!n.views.some(l=>l.port===c)&&await Wa(c))try{i.push(await vp({trackId:e.id,name:`${e.name} preview`,cmd:null,cwd:e.cwd,port:c,origin:"detected",branch:e.workspace?.branch??null}))}catch{}return i}var Xt,lN,ax,nx,rx,mr,uN,is,ox,Fa,dN,mp,fN,pN,mN,gp,ix,dp,gN,bN,lx,hl,Yt,fp,Ua,yp,ux,Sn,_N,pp,$N,bl=O(()=>{tt();ue();Wn();Xt=cN(iN),lN=process.env.BOSTRAT_STATE_DIR||lt.join(process.env.HOME||oN.tmpdir(),".bostrat"),ax=lt.join(lN,"views.json"),[nx,rx]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),mr=Number(process.env.BOSTRAT_VIEW_MAX)||3,uN="bostrat-view-",is=e=>`${uN}${e}`,ox=new Set(["bostrat","www","api","views","app"]),Fa=e=>Object.assign(new Error(e),{status:400}),dN=new Set(["npm","pnpm","yarn","bun"]),mp="package.json",fN=/[|;&`]|\$\(/,pN=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),mN=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,gp=e=>String(e).trim().split(/\s+/).filter(Boolean),ix=/^[A-Za-z_][A-Za-z0-9_]*=/;dp=e=>e==="/",gN=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");bN=e=>Ha.stat(e).then(t=>t.isDirectory(),()=>!1),lx=e=>Ha.stat(e).then(t=>t.isFile(),()=>!1),hl=e=>Ha.realpath(e).catch(()=>lt.resolve(e));Yt=null,fp=null;Ua=new Map,yp=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,ux=12e3;Sn=new Map,_N=12e4;pp=!1;$N=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function kl(e,t=Date.now()){let n=null;for(let r of e||[]){let o=r.payload?.ts??r.ts;r.type==="pr.offer.answered"?n=r.payload?.action==="pr"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null,offerType:"offer"}:null:r.type==="pr.exists.answered"?n=r.payload?.choice==="push"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null,offerType:"exists"}:null:r.type==="pr.state.changed"&&(r.payload?.change==="opened"||r.payload?.change==="pushed")&&n!=null&&o>=n.ts&&(n=null)}return n!=null&&t-n.ts>CN?null:n}function MN(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function kx(e){let t=e?.workspace?.branch;if(!t)return;let n;try{n=await L(e)}catch{return}let r=kl(n);if(r==null)return;let o=(e.boxes||[]).find(u=>u.name===e.leadBox);if(await eo(e.leadBox,o?.agent).catch(()=>!1))return;let s=Date.now();if(s-(Tp.get(e.id)||0)<ON)return;Tp.set(e.id,s);let a;try{a=await Vt(e.cwd,{caller:"reconcile"})}catch{return}let i=await os(e).catch(()=>[t]),c=(a||[]).find(u=>i.includes(u.headRefName));if(!c)return;let l=await me(e.cwd).catch(()=>null);if(l){let{link:u,created:f}=await mt({repo:l,number:c.number,trackId:e.id,url:c.url,headRef:c.headRefName||t,title:c.title,source:"reconciled"});if(!f&&!(u?.trackId===e.id&&await hn(l,c.number))){if(!MN(u,r))return;let d=await L(e).catch(()=>null);if(d&&kl(d)==null)return;await _(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"pushed",number:c.number,url:c.url,title:c.title,branch:t,repo:l}});return}}await _(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:c.number,url:c.url,title:c.title,branch:t,...l?{repo:l}:{}}}).catch(()=>{}),Tp.delete(e.id)}var ON,Tp,CN,Ep=O(()=>{ue();ns();Un();wn();Ba();ss();ON=6e3,Tp=new Map,CN=30*6e4});import Ap from"node:os";import{execFile as NN}from"node:child_process";import{promisify as PN}from"node:util";async function xl(){if(process.platform!=="darwin")return Ap.freemem();try{let{stdout:e}=await DN("vm_stat",[],{timeout:3e3}),t=Number(/page size of (\d+) bytes/.exec(e)?.[1])||4096,n=o=>Number(new RegExp(`Pages ${o}:\\s+(\\d+)\\.`).exec(e)?.[1])||0,r=(n("free")+n("inactive")+n("speculative"))*t;return r>0?r:Ap.freemem()}catch{return Ap.freemem()}}var DN,_p=O(()=>{DN=PN(NN)});import Ne from"node:fs/promises";import Pe from"node:path";import Tx from"node:os";import{randomUUID as BN}from"node:crypto";import{execFile as jN}from"node:child_process";import{promisify as LN}from"node:util";import{chromium as FN}from"playwright-core";async function tn(){return to||(Ip||=(async()=>{try{to=JSON.parse(await Ne.readFile(Ax,"utf8"))}catch{to={mocks:[]}}finally{Ip=null}return to.mocks||=[],to})(),Ip)}async function Tt(){await at(Ax,to)}function $x(e,t){en(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&en(e,t.v,"png")}function nt(e,t,n={}){return _({id:e.trackId,cwd:e.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,mockId:e.id,name:e.name,...e.group?{group:e.group}:{},...e.kind==="video"?{kind:"video"}:{},...n}}).catch(()=>{})}function Et(e,t){let n=Ga(e.cwd);return{html:Pe.join(n,`${e.id}.v${t}.html`),png:Pe.join(n,`${e.id}.v${t}.png`)}}function Ka(e,t,n){if(!t)return null;let r=n==="html"?t.htmlRel:n==="png"?t.pngRel??t.posterRel??null:n==="video"?t.videoRel??null:null;return r?Pe.join(e.cwd,r):null}function Bt(e,t=null){let n=e.versions||[];return t!=null?n.find(r=>r.v===Number(t))||null:n.length?n[n.length-1]:null}function Op(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function Sl(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(VN)}function zN(e){return!Buffer.isBuffer(e)||e.length<32?null:e.subarray(4,8).toString("latin1")==="ftyp"?{ext:"mp4",mime:"video/mp4"}:e.readUInt32BE(0)===440786851?{ext:"webm",mime:"video/webm"}:null}async function JN(e){let t=Buffer.alloc(24),n=await Ne.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function YN({imgW:e,imgH:t,bbox:n,pad:r,align:o=1}){if(!n||!(e>0)||!(t>0))return null;let s=n.right-n.left+1,a=n.bottom-n.top+1;if(s<=0||a<=0||s>=e*.9&&a>=t*.9)return null;let i=s+2*r,c=a+2*r;return i+=(o-i%o)%o,c+=(o-c%o)%o,{sx:n.left,sy:n.top,sw:s,sh:a,outW:i,outH:c,dx:Math.floor((i-s)/2),dy:Math.floor((c-a)/2)}}async function XN(e,t){return e.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,g=0;g<o;g++,h+=4)Math.abs(c[h]-l[0])+Math.abs(c[h+1]-l[1])+Math.abs(c[h+2]-l[2])>24&&(g<u&&(u=g),g>d&&(d=g),m<f&&(f=m),p=m);return d<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:f,right:d,bottom:p}}},t.toString("base64"))}async function ZN(e,t,{bg:n,crop:r}){let o=await e.evaluate(async({b64:s,bg:a,crop:i})=>{let c=new Image;await new Promise((f,d)=>{c.onload=f,c.onerror=()=>d(new Error("decode failed")),c.src=`data:image/png;base64,${s}`});let l=document.createElement("canvas");l.width=i.outW,l.height=i.outH;let u=l.getContext("2d");return u.fillStyle=`rgb(${a[0]},${a[1]},${a[2]})`,u.fillRect(0,0,i.outW,i.outH),u.drawImage(c,i.sx,i.sy,i.sw,i.sh,i.dx,i.dy,i.sw,i.sh),l.toDataURL("image/png").split(",")[1]},{b64:t.toString("base64"),bg:n,crop:r});return Buffer.from(o,"base64")}async function QN({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await FN.launch({executablePath:_x,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new ht("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:cs})).newPage();a.setDefaultTimeout(ls),await a.goto(`file://${e}`,{waitUntil:"load",timeout:ls}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:ls});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await XN(a,i),u=l&&YN({...l,pad:WN*cs,align:cs});u&&(i=await ZN(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Ix)throw new ht(`capture produced a ${i.length}-byte image`);let c=Op(i);if(!c)throw new ht("output is not a PNG");return await Ne.writeFile(t,i),{width:Math.round(c.width/cs),height:Math.round(c.height/cs),dpr:cs,renderMs:Date.now()-r}}catch(s){throw s instanceof ht?s:new ht(/timeout/i.test(s?.message||"")?`render timed out after ${ls}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function vx({htmlPath:e,outPath:t,width:n}){let r=await Ne.mkdtemp(Pe.join(Tx.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await $p(xx,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:ls,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new ht(`firefox not found (${xx})`,""):new ht(c?.killed?`render timed out after ${ls}ms`:"firefox exited non-zero",s)}let a=await Ne.stat(t).catch(()=>null);if(!a||a.size<Ix)throw new ht(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Op(await JN(t));if(!i)throw new ht("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await Ne.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function eP(e){if(UN==="firefox")return vx(e);try{return await QN(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await vx(e)}catch(n){throw/not found/.test(n?.message||"")?new ht(`no browser installed \u2014 chromium missing at ${_x} and no firefox on PATH; install chromium (Debian: apt install chromium fonts-dejavu-core fonts-liberation) or point BOSTRAT_CHROMIUM_BIN at a Chrome/Chromium binary`,t?.message||""):n}}}function Ox(e){return Rp=Rp.then(e,e),Rp}async function Cx(e,t){let n=await tn(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await Tt(),await nt(r,"rendering",{version:t});try{let o=await xl();if(o<qN)throw new ht(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=Et(r,t),a=await tP({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await Ne.rm(s.png,{force:!0}).catch(()=>{});return}Cp(r,{v:t,source:"html",htmlRel:Pe.relative(r.cwd,s.html),pngRel:Pe.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await Tt(),await nt(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),Qt(r),en(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await Tt(),await nt(r,"error",{version:t,error:r.error}),Qt(r)}}}function Cp(e,t){e.versions=(e.versions||[]).filter(r=>r.v!==t.v).concat(t),e.versions.sort((r,o)=>r.v-o.v);let n=e.versions[e.versions.length-1];e.readyVersion=n.v,e.height=n.height,e.dpr=n.dpr,e.renderMs=n.renderMs??null,n.v===e.version&&(e.state="ready",e.error=null)}function nP({trackId:e,name:t,cwd:n,width:r}){if(!e)throw new Error("trackId required");if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(!n)throw new Error("cwd required");let o=r==null?HN:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function Va({trackId:e,group:t}={}){let n=await tn(),r=e?n.mocks.filter(o=>o.trackId===e):n.mocks;return t&&(r=r.filter(o=>o.group===t)),r.map(o=>({...o}))}async function En(e){let n=(await tn()).mocks.find(r=>r.id===e);return n?{...n}:null}function Mx({html:e,image:t,video:n}){if([typeof e=="string"&&e.trim(),!!t,!!n].filter(Boolean).length!==1)throw new Error("exactly one of html (string), image (png) or video (mp4/webm) required");if(t&&!Sl(t))throw new Error("image must be a PNG");if(n){let o=zN(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>Sx)throw new Error(`video must be ${Sx/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function Nx({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let f=nP({trackId:e,name:t,cwd:s,width:a}),d=typeof n=="string"&&n.trim(),p=Mx({html:n,image:r,video:o}),m=await tn(),h={id:BN().slice(0,8),trackId:e,box:c||null,agent:l||null,name:t.trim(),prompt:u?String(u).slice(0,500):null,group:i?String(i).trim():null,cwd:s,width:f,height:null,dpr:null,state:"queued",source:d?"html":o?"video":"image",kind:o?"video":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await Ne.mkdir(Ga(s),{recursive:!0}),m.mocks.push(h),d?(await Ne.writeFile(Et(h,1).html,n),await Tt(),await nt(h,"created"),Qt(h),en(h,1,"html"),Ox(()=>Cx(h.id,1))):o?(await Dx(h,1,o,p),await Tt(),await nt(h,"created"),await nt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr,durationS:h.versions[0]?.durationS??null}),Qt(h),$x(h,h.versions[0])):(await Bx(h,1,r),await Tt(),await nt(h,"created"),await nt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr}),Qt(h),en(h,1,"png")),{...h}}async function Px(e,{html:t,image:n,video:r,width:o}={}){let a=(await tn()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=Mx({html:t,image:n,video:r});if(o!=null){let u=Number(o);if(!Number.isInteger(u)||u<320||u>4e3)throw new Error("invalid width (320-4000)");a.width=u}let l=a.version+1;return a.version=l,a.updatedAt=Date.now(),a.source=i?"html":r?"video":"image",a.kind=r?"video":"image",await Ne.mkdir(Ga(a.cwd),{recursive:!0}),i?(a.state="queued",await Ne.writeFile(Et(a,l).html,t),await Tt(),await nt(a,"updated",{version:l}),Qt(a),en(a,l,"html"),Ox(()=>Cx(a.id,l))):r?(await Dx(a,l,r,c),await Tt(),await nt(a,"updated",{version:l}),await nt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:Bt(a)?.durationS??null}),Qt(a),$x(a,a.versions.find(u=>u.v===l))):(await Bx(a,l,n),await Tt(),await nt(a,"updated",{version:l}),await nt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),Qt(a),en(a,l,"png")),{...a}}async function rP(e){try{let{stdout:t}=await $p(KN,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:Rx,killSignal:"SIGKILL",maxBuffer:4194304}),n=JSON.parse(t),r=(n.streams||[]).find(s=>s.codec_type==="video"),o=Number(n.format?.duration)||Number(r?.duration)||0;return{durationS:o>0?Math.round(o*10)/10:null,width:Number(r?.width)||null,height:Number(r?.height)||null}}catch{return null}}async function oP(e,t){try{await $p(GN,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:Rx,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await Ne.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function Dx(e,t,n,r){let o=Ga(e.cwd),s=Pe.join(o,`${e.id}.v${t}.${r.ext}`),a=Pe.join(o,`${e.id}.v${t}.poster.png`);await Ne.writeFile(s,n);let i=await rP(s),c=await oP(s,a);Cp(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:Pe.relative(e.cwd,s),posterRel:c?Pe.relative(e.cwd,a):null,mimeType:r.mime,bytes:n.length,durationS:i?.durationS??null,width:i?.width??null,height:i?.height??null,dpr:1,renderMs:null,createdAt:Date.now()}),i?.width&&(e.width=i.width)}async function Bx(e,t,n){let r=Op(n);if(!r)throw new Error("image must be a PNG");let o=Et(e,t);await Ne.writeFile(o.png,n),Cp(e,{v:t,source:"image",htmlRel:null,pngRel:Pe.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function jx(e){let t=await tn(),n=t.mocks.find(o=>o.id===e);if(!n)throw new Error("mock not found");if(n.readyVersion==null)throw new Error(`mock is ${n.state} \u2014 nothing to pick yet`);let r=n.group?t.mocks.filter(o=>o.trackId===n.trackId&&o.group===n.group&&o.id!==e&&o.readyVersion!=null).map(o=>({mockId:o.id,name:o.name})):[];return await nt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function Lx(e){let t=await tn(),n=t.mocks.findIndex(o=>o.id===e);if(n===-1)throw new Error("mock not found");let r=t.mocks[n];t.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=Et(r,o);await Ne.rm(s.html,{force:!0}).catch(()=>{}),await Ne.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await Ne.rm(Pe.join(r.cwd,s),{force:!0}).catch(()=>{});return await Tt(),await nt(r,"deleted"),$t("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function Fx(e){let n=(await tn()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=Pe.join(Ex,"mock-archive",e.id),o=Ga(e.cwd),s=Pe.join(r,".bostrat","mocks");await Ne.mkdir(s,{recursive:!0});let a=await Ne.readdir(o).catch(()=>[]);for(let i of a)await Ne.copyFile(Pe.join(o,i),Pe.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await Ne.stat(Pe.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=e.name||e.id,i.updatedAt=Date.now(),Qt(i)}return await Tt(),n.length}async function Ux(e){let t=await tn(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await Tt();for(let r of n)$t("mocks",r.id).catch(()=>{})}return n.length}async function Hx(){if(!Ee())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await je("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await tn();for(let r of n.mocks){await Qt(r);for(let o of r.versions||[])o.htmlRel&&await en(r,o.v,"html"),(o.pngRel||o.posterRel)&&await en(r,o.v,"png"),o.videoRel&&await en(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var $p,Ex,Ax,UN,_x,xx,HN,cs,WN,Ix,ls,qN,Sx,KN,GN,Rx,ht,to,Ip,Qt,en,Ga,VN,tP,Rp,us=O(()=>{ue();Wn();_p();Kn();$p=LN(jN),Ex=process.env.BOSTRAT_STATE_DIR||Pe.join(process.env.HOME||Tx.tmpdir(),".bostrat"),Ax=Pe.join(Ex,"mocks.json"),UN=process.env.BOSTRAT_MOCK_ENGINE||"chromium",_x=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",xx=process.env.BOSTRAT_FIREFOX_BIN||"firefox",HN=1300,cs=2,WN=32,Ix=1500,ls=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,qN=700*1024*1024,Sx=100*1024*1024,KN=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",GN=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",Rx=2e4,ht=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
154
|
+
`)}catch{return""}}async function eP(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await ZN(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await kx(e.slug)||"server exited before listening"),vn.delete(e.id),await Tn(e,e.state,e.error?{error:e.error}:{}),!0}if(await Xa(e.port))return vn.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await Tn(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>QN?(e.state="error",e.error=await kx(e.slug)||"never started listening",await Tn(e,"error",{error:e.error}),!0):!1;let o=(vn.get(e.id)||0)+1;return vn.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function Al(){if(!Tp){Tp=!0;try{let e=await An(),t=!1;for(let n of e.views)await eP(n)&&(t=!0);t&&await en()}finally{Tp=!1}}}async function El({trackId:e}={}){await Al();let t=await An();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function Rx(e){let n=(await An()).views.find(r=>r.id===e);return n?{...n}:null}function Mp(e){return Zt&&Zt.views.find(t=>t.slug===e)||null}async function Np({trackId:e,name:t,cmd:n,cwd:r,serveCwd:o,port:s,box:a,origin:i="agent",branch:c=null}){if(!e)throw new Error("trackId required");if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(i!=="detected"&&(typeof n!="string"||!n.trim()))throw new Error("cmd (string) required");if(!r)throw new Error("cwd required");let l=o!=null,u=ut.resolve(r,o??"."),d=i==="detected"?null:async()=>{await zN(r,u),await JN(u,n)},f=await An();if(i!=="detected"){let h=_p(t),g=String(n).trim(),x=f.views.find(k=>k.trackId===e&&(k.slug===h||_p(k.name)===h||k.cmd===g&&ut.resolve(k.serveCwd??k.cwd)===u));if(x)return tP(f,x,{cmd:g,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),f.views.filter(h=>h.state==="live"||h.state==="starting").length>=gr)throw new Error(`view cap reached (${gr} live) \u2014 stop one first`);let m={id:CN().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:YN(f,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await XN(f,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Cp(m),f.views.push(m),await en(),await Tn(m,"created",{origin:i}),Ep(e,m.id),i==="detected"&&await Xa(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await en(),await Tn(m,"live")),{...m}}async function tP(e,t,{cmd:n,box:r,branch:o,serveCwd:s,validate:a}){r&&(t.box=r),o&&(t.branch=o);let i=t.cmd!==n,c=s!==void 0&&!await GN(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await Xa(t.port))return await en(),Ep(t.trackId,t.id),{...t,reused:!0};if(a&&await a(),e.views.filter(d=>d.id!==t.id&&(d.state==="live"||d.state==="starting")).length>=gr)throw new Error(`view cap reached (${gr} live) \u2014 stop one first`);return t.origin==="detected"?(await Op(t.port),t.origin="agent"):await Za(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),vn.delete(t.id),await Cp(t),await en(),await Tn(t,"created",{restarted:!0,reused:!0}),Ep(t.trackId,t.id),{...t,reused:!0}}async function $x(e){let n=(await An()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await Op(n.port):await Za(n.slug),n.state!=="stopped"&&(n.state="stopped",vn.delete(n.id),await en(),await Tn(n,"stopped")),{...n}}async function Ox(e){let t=await An(),n=t.views.find(o=>o.id===e);if(!n)throw new Error("view not found");if(!n.cmd)throw new Error("a detected view has no known command \u2014 recreate it as a managed view");if(t.views.filter(o=>o.id!==e&&(o.state==="live"||o.state==="starting")).length>=gr)throw new Error(`view cap reached (${gr} live) \u2014 stop one first`);return await Za(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),vn.delete(n.id),await Cp(n),await en(),await Tn(n,"created",{restarted:!0}),{...n}}async function Cx(e){let t=await An(),n=t.views.findIndex(o=>o.id===e);if(n===-1)throw new Error("view not found");let r=t.views[n];return r.origin==="detected"?await Op(r.port):await Za(r.slug),t.views.splice(n,1),vn.delete(r.id),await en(),await Tn(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function Mx(e){let t=await An(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await Za(r.slug),vn.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await en()),n.length}async function Nx(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await An(),r=n.adoptWatermarks[e.id]??null,o=t.filter(c=>c.role==="assistant"&&c.ts!=null&&(r==null?!1:c.ts>r)),s=t.reduce((c,l)=>l.ts>c?l.ts:c,r??0);if(n.adoptWatermarks[e.id]!==s&&(n.adoptWatermarks[e.id]=s,await en()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(nP)){let u=Number(l[1]||80);u>=1024&&u<=65535&&a.add(u)}if(!a.size)return[];let i=[];for(let c of a)if(!Ix().has(c)&&!n.views.some(l=>l.port===c)&&await Xa(c))try{i.push(await Np({trackId:e.id,name:`${e.name} preview`,cmd:null,cwd:e.cwd,port:c,origin:"detected",branch:e.workspace?.branch??null}))}catch{}return i}var Qt,PN,xx,gx,yx,gr,DN,ms,bx,za,BN,Ap,jN,LN,FN,Ip,Sx,Sp,WN,KN,Tx,vl,Zt,vp,Ja,Rp,Ax,vn,QN,Tp,nP,_l=C(()=>{Ve();ue();qn();Qt=NN(MN),PN=process.env.BOSTRAT_STATE_DIR||ut.join(process.env.HOME||$N.tmpdir(),".bostrat"),xx=ut.join(PN,"views.json"),[gx,yx]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),gr=Number(process.env.BOSTRAT_VIEW_MAX)||3,DN="bostrat-view-",ms=e=>`${DN}${e}`,bx=new Set(["bostrat","www","api","views","app"]),za=e=>Object.assign(new Error(e),{status:400}),BN=new Set(["npm","pnpm","yarn","bun"]),Ap="package.json",jN=/[|;&`]|\$\(/,LN=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),FN=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Ip=e=>String(e).trim().split(/\s+/).filter(Boolean),Sx=/^[A-Za-z_][A-Za-z0-9_]*=/;Sp=e=>e==="/",WN=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");KN=e=>Ya.stat(e).then(t=>t.isDirectory(),()=>!1),Tx=e=>Ya.stat(e).then(t=>t.isFile(),()=>!1),vl=e=>Ya.realpath(e).catch(()=>ut.resolve(e));Zt=null,vp=null;Ja=new Map,Rp=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,Ax=12e3;vn=new Map,QN=12e4;Tp=!1;nP=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function Il(e,t=Date.now()){let n=null;for(let r of e||[]){let o=r.payload?.ts??r.ts;r.type==="pr.offer.answered"?n=r.payload?.action==="pr"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null,offerType:"offer"}:null:r.type==="pr.exists.answered"?n=r.payload?.choice==="push"?{ts:o,offerId:r.payload?.offerId??null,box:r.boxId??null,offerType:"exists"}:null:r.type==="pr.state.changed"&&(r.payload?.change==="opened"||r.payload?.change==="pushed")&&n!=null&&o>=n.ts&&(n=null)}return n!=null&&t-n.ts>oP?null:n}function sP(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function Px(e){let t=e?.workspace?.branch;if(!t)return;let n;try{n=await L(e)}catch{return}let r=Il(n);if(r==null)return;let o=(e.boxes||[]).find(u=>u.name===e.leadBox);if(await no(e.leadBox,o?.agent).catch(()=>!1))return;let s=Date.now();if(s-(Pp.get(e.id)||0)<rP)return;Pp.set(e.id,s);let a;try{a=await Jt(e.cwd,{caller:"reconcile"})}catch{return}let i=await ds(e).catch(()=>[t]),c=(a||[]).find(u=>i.includes(u.headRefName));if(!c)return;let l=await me(e.cwd).catch(()=>null);if(l){let{link:u,created:d}=await mt({repo:l,number:c.number,trackId:e.id,url:c.url,headRef:c.headRefName||t,title:c.title,source:"reconciled"});if(!d&&!(u?.trackId===e.id&&await wn(l,c.number))){if(!sP(u,r))return;let f=await L(e).catch(()=>null);if(f&&Il(f)==null)return;await _(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"pushed",number:c.number,url:c.url,title:c.title,branch:t,repo:l}});return}}await _(e,{type:"pr.state.changed",actor:"agent",source:"orchestrator",boxId:e.leadBox,payload:{ts:Date.now(),change:"opened",number:c.number,url:c.url,title:c.title,branch:t,...l?{repo:l}:{}}}).catch(()=>{}),Pp.delete(e.id)}var rP,Pp,oP,Dp=C(()=>{ue();ls();Hn();gn();Ka();fs();rP=6e3,Pp=new Map,oP=30*6e4});import Bp from"node:os";import{execFile as aP}from"node:child_process";import{promisify as iP}from"node:util";async function Rl(){if(process.platform!=="darwin")return Bp.freemem();try{let{stdout:e}=await cP("vm_stat",[],{timeout:3e3}),t=Number(/page size of (\d+) bytes/.exec(e)?.[1])||4096,n=o=>Number(new RegExp(`Pages ${o}:\\s+(\\d+)\\.`).exec(e)?.[1])||0,r=(n("free")+n("inactive")+n("speculative"))*t;return r>0?r:Bp.freemem()}catch{return Bp.freemem()}}var cP,jp=C(()=>{cP=iP(aP)});import Pe from"node:fs/promises";import De from"node:path";import Lx from"node:os";import{randomUUID as lP}from"node:crypto";import{execFile as uP}from"node:child_process";import{promisify as dP}from"node:util";import{chromium as fP}from"playwright-core";async function rn(){return ro||(Lp||=(async()=>{try{ro=JSON.parse(await Pe.readFile(Ux,"utf8"))}catch{ro={mocks:[]}}finally{Lp=null}return ro.mocks||=[],ro})(),Lp)}async function Tt(){await it(Ux,ro)}function Kx(e,t){nn(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&nn(e,t.v,"png")}function nt(e,t,n={}){return _({id:e.trackId,cwd:e.cwd},{type:"mock.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,mockId:e.id,name:e.name,...e.group?{group:e.group}:{},...e.kind==="video"?{kind:"video"}:{},...n}}).catch(()=>{})}function At(e,t){let n=ei(e.cwd);return{html:De.join(n,`${e.id}.v${t}.html`),png:De.join(n,`${e.id}.v${t}.png`)}}function Qa(e,t,n){if(!t)return null;let r=n==="html"?t.htmlRel:n==="png"?t.pngRel??t.posterRel??null:n==="video"?t.videoRel??null:null;return r?De.join(e.cwd,r):null}function jt(e,t=null){let n=e.versions||[];return t!=null?n.find(r=>r.v===Number(t))||null:n.length?n[n.length-1]:null}function Hp(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function $l(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(bP)}function kP(e){return!Buffer.isBuffer(e)||e.length<32?null:e.subarray(4,8).toString("latin1")==="ftyp"?{ext:"mp4",mime:"video/mp4"}:e.readUInt32BE(0)===440786851?{ext:"webm",mime:"video/webm"}:null}async function xP(e){let t=Buffer.alloc(24),n=await Pe.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function SP({imgW:e,imgH:t,bbox:n,pad:r,align:o=1}){if(!n||!(e>0)||!(t>0))return null;let s=n.right-n.left+1,a=n.bottom-n.top+1;if(s<=0||a<=0||s>=e*.9&&a>=t*.9)return null;let i=s+2*r,c=a+2*r;return i+=(o-i%o)%o,c+=(o-c%o)%o,{sx:n.left,sy:n.top,sw:s,sh:a,outW:i,outH:c,dx:Math.floor((i-s)/2),dy:Math.floor((c-a)/2)}}async function vP(e,t){return e.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,d=s,f=-1,p=-1;for(let m=0;m<s;m++)for(let h=m*o*4,g=0;g<o;g++,h+=4)Math.abs(c[h]-l[0])+Math.abs(c[h+1]-l[1])+Math.abs(c[h+2]-l[2])>24&&(g<u&&(u=g),g>f&&(f=g),m<d&&(d=m),p=m);return f<0?null:{imgW:o,imgH:s,bg:l,bbox:{left:u,top:d,right:f,bottom:p}}},t.toString("base64"))}async function TP(e,t,{bg:n,crop:r}){let o=await e.evaluate(async({b64:s,bg:a,crop:i})=>{let c=new Image;await new Promise((d,f)=>{c.onload=d,c.onerror=()=>f(new Error("decode failed")),c.src=`data:image/png;base64,${s}`});let l=document.createElement("canvas");l.width=i.outW,l.height=i.outH;let u=l.getContext("2d");return u.fillStyle=`rgb(${a[0]},${a[1]},${a[2]})`,u.fillRect(0,0,i.outW,i.outH),u.drawImage(c,i.sx,i.sy,i.sw,i.sh,i.dx,i.dy,i.sw,i.sh),l.toDataURL("image/png").split(",")[1]},{b64:t.toString("base64"),bg:n,crop:r});return Buffer.from(o,"base64")}async function AP({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await fP.launch({executablePath:Hx,args:["--disable-gpu"],timeout:3e4})}catch(s){throw new ht("chromium failed to launch",s?.message||"")}try{let a=await(await o.newContext({viewport:{width:n,height:800},deviceScaleFactor:hs})).newPage();a.setDefaultTimeout(ws),await a.goto(`file://${e}`,{waitUntil:"load",timeout:ws}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:ws});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await vP(a,i),u=l&&SP({...l,pad:hP*hs,align:hs});u&&(i=await TP(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Wx)throw new ht(`capture produced a ${i.length}-byte image`);let c=Hp(i);if(!c)throw new ht("output is not a PNG");return await Pe.writeFile(t,i),{width:Math.round(c.width/hs),height:Math.round(c.height/hs),dpr:hs,renderMs:Date.now()-r}}catch(s){throw s instanceof ht?s:new ht(/timeout/i.test(s?.message||"")?`render timed out after ${ws}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function jx({htmlPath:e,outPath:t,width:n}){let r=await Pe.mkdtemp(De.join(Lx.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Up(Dx,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:ws,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new ht(`firefox not found (${Dx})`,""):new ht(c?.killed?`render timed out after ${ws}ms`:"firefox exited non-zero",s)}let a=await Pe.stat(t).catch(()=>null);if(!a||a.size<Wx)throw new ht(`render produced ${a?`a ${a.size}-byte file`:"no file"} (silent no-op \u2014 another firefox holding the profile?)`,s);let i=Hp(await xP(t));if(!i)throw new ht("output is not a PNG",s);return{width:i.width,height:i.height,dpr:1,renderMs:Date.now()-o}}finally{await Pe.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function EP(e){if(pP==="firefox")return jx(e);try{return await AP(e)}catch(t){if(!/failed to launch/i.test(t?.message||""))throw t;try{return await jx(e)}catch(n){throw/not found/.test(n?.message||"")?new ht(`no browser installed \u2014 chromium missing at ${Hx} and no firefox on PATH; install chromium (Debian: apt install chromium fonts-dejavu-core fonts-liberation) or point BOSTRAT_CHROMIUM_BIN at a Chrome/Chromium binary`,t?.message||""):n}}}function Gx(e){return Fp=Fp.then(e,e),Fp}async function Vx(e,t){let n=await rn(),r=n.mocks.find(o=>o.id===e);if(r){r.state="rendering",await Tt(),await nt(r,"rendering",{version:t});try{let o=await Rl();if(o<wP)throw new ht(`box is low on memory (${Math.round(o/1e6)} MB available) \u2014 not spawning a browser`);let s=At(r,t),a=await _P({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await Pe.rm(s.png,{force:!0}).catch(()=>{});return}Wp(r,{v:t,source:"html",htmlRel:De.relative(r.cwd,s.html),pngRel:De.relative(r.cwd,s.png),width:a.width,height:a.height,dpr:a.dpr,renderMs:a.renderMs,createdAt:Date.now()}),await Tt(),await nt(r,"ready",{version:t,width:a.width,height:a.height,dpr:a.dpr}),tn(r),nn(r,t,"png")}catch(o){if(!n.mocks.some(s=>s.id===e))return;r.state="error",r.error=o?.message||String(o),await Tt(),await nt(r,"error",{version:t,error:r.error}),tn(r)}}}function Wp(e,t){e.versions=(e.versions||[]).filter(r=>r.v!==t.v).concat(t),e.versions.sort((r,o)=>r.v-o.v);let n=e.versions[e.versions.length-1];e.readyVersion=n.v,e.height=n.height,e.dpr=n.dpr,e.renderMs=n.renderMs??null,n.v===e.version&&(e.state="ready",e.error=null)}function IP({trackId:e,name:t,cwd:n,width:r}){if(!e)throw new Error("trackId required");if(typeof t!="string"||!t.trim())throw new Error("name (string) required");if(!n)throw new Error("cwd required");let o=r==null?mP:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function ti({trackId:e,group:t}={}){let n=await rn(),r=e?n.mocks.filter(o=>o.trackId===e):n.mocks;return t&&(r=r.filter(o=>o.group===t)),r.map(o=>({...o}))}async function En(e){let n=(await rn()).mocks.find(r=>r.id===e);return n?{...n}:null}function zx({html:e,image:t,video:n}){if([typeof e=="string"&&e.trim(),!!t,!!n].filter(Boolean).length!==1)throw new Error("exactly one of html (string), image (png) or video (mp4/webm) required");if(t&&!$l(t))throw new Error("image must be a PNG");if(n){let o=kP(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>Bx)throw new Error(`video must be ${Bx/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function Jx({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=IP({trackId:e,name:t,cwd:s,width:a}),f=typeof n=="string"&&n.trim(),p=zx({html:n,image:r,video:o}),m=await rn(),h={id:lP().slice(0,8),trackId:e,box:c||null,agent:l||null,name:t.trim(),prompt:u?String(u).slice(0,500):null,group:i?String(i).trim():null,cwd:s,width:d,height:null,dpr:null,state:"queued",source:f?"html":o?"video":"image",kind:o?"video":"image",version:1,readyVersion:null,versions:[],error:null,renderMs:null,createdAt:Date.now(),updatedAt:Date.now()};return await Pe.mkdir(ei(s),{recursive:!0}),m.mocks.push(h),f?(await Pe.writeFile(At(h,1).html,n),await Tt(),await nt(h,"created"),tn(h),nn(h,1,"html"),Gx(()=>Vx(h.id,1))):o?(await Xx(h,1,o,p),await Tt(),await nt(h,"created"),await nt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr,durationS:h.versions[0]?.durationS??null}),tn(h),Kx(h,h.versions[0])):(await Zx(h,1,r),await Tt(),await nt(h,"created"),await nt(h,"ready",{version:1,width:h.width,height:h.height,dpr:h.dpr}),tn(h),nn(h,1,"png")),{...h}}async function Yx(e,{html:t,image:n,video:r,width:o}={}){let a=(await rn()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=zx({html:t,image:n,video:r});if(o!=null){let u=Number(o);if(!Number.isInteger(u)||u<320||u>4e3)throw new Error("invalid width (320-4000)");a.width=u}let l=a.version+1;return a.version=l,a.updatedAt=Date.now(),a.source=i?"html":r?"video":"image",a.kind=r?"video":"image",await Pe.mkdir(ei(a.cwd),{recursive:!0}),i?(a.state="queued",await Pe.writeFile(At(a,l).html,t),await Tt(),await nt(a,"updated",{version:l}),tn(a),nn(a,l,"html"),Gx(()=>Vx(a.id,l))):r?(await Xx(a,l,r,c),await Tt(),await nt(a,"updated",{version:l}),await nt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr,durationS:jt(a)?.durationS??null}),tn(a),Kx(a,a.versions.find(u=>u.v===l))):(await Zx(a,l,n),await Tt(),await nt(a,"updated",{version:l}),await nt(a,"ready",{version:l,width:a.width,height:a.height,dpr:a.dpr}),tn(a),nn(a,l,"png")),{...a}}async function RP(e){try{let{stdout:t}=await Up(gP,["-v","error","-print_format","json","-show_format","-show_streams",e],{timeout:qx,killSignal:"SIGKILL",maxBuffer:4194304}),n=JSON.parse(t),r=(n.streams||[]).find(s=>s.codec_type==="video"),o=Number(n.format?.duration)||Number(r?.duration)||0;return{durationS:o>0?Math.round(o*10)/10:null,width:Number(r?.width)||null,height:Number(r?.height)||null}}catch{return null}}async function $P(e,t){try{await Up(yP,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:qx,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await Pe.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function Xx(e,t,n,r){let o=ei(e.cwd),s=De.join(o,`${e.id}.v${t}.${r.ext}`),a=De.join(o,`${e.id}.v${t}.poster.png`);await Pe.writeFile(s,n);let i=await RP(s),c=await $P(s,a);Wp(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:De.relative(e.cwd,s),posterRel:c?De.relative(e.cwd,a):null,mimeType:r.mime,bytes:n.length,durationS:i?.durationS??null,width:i?.width??null,height:i?.height??null,dpr:1,renderMs:null,createdAt:Date.now()}),i?.width&&(e.width=i.width)}async function Zx(e,t,n){let r=Hp(n);if(!r)throw new Error("image must be a PNG");let o=At(e,t);await Pe.writeFile(o.png,n),Wp(e,{v:t,source:"image",htmlRel:null,pngRel:De.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function Qx(e){let t=await rn(),n=t.mocks.find(o=>o.id===e);if(!n)throw new Error("mock not found");if(n.readyVersion==null)throw new Error(`mock is ${n.state} \u2014 nothing to pick yet`);let r=n.group?t.mocks.filter(o=>o.trackId===n.trackId&&o.group===n.group&&o.id!==e&&o.readyVersion!=null).map(o=>({mockId:o.id,name:o.name})):[];return await nt(n,"picked",{version:n.readyVersion,siblings:r}),{mock:{...n},siblings:r}}async function eS(e){let t=await rn(),n=t.mocks.findIndex(o=>o.id===e);if(n===-1)throw new Error("mock not found");let r=t.mocks[n];t.mocks.splice(n,1);for(let o=1;o<=r.version;o++){let s=At(r,o);await Pe.rm(s.html,{force:!0}).catch(()=>{}),await Pe.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await Pe.rm(De.join(r.cwd,s),{force:!0}).catch(()=>{});return await Tt(),await nt(r,"deleted"),Ot("mocks",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function tS(e){let n=(await rn()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=De.join(Fx,"mock-archive",e.id),o=ei(e.cwd),s=De.join(r,".bostrat","mocks");await Pe.mkdir(s,{recursive:!0});let a=await Pe.readdir(o).catch(()=>[]);for(let i of a)await Pe.copyFile(De.join(o,i),De.join(s,i));for(let i of n){for(let c of i.versions||[])for(let l of[c.htmlRel,c.pngRel,c.videoRel,c.posterRel]){if(!l)continue;if(!(await Pe.stat(De.join(r,l)).catch(()=>null))?.isFile())throw new Error(`mock evacuation incomplete: ${l} missing from the archive`)}i.cwd=r,i.trackDeletedAt=Date.now(),i.trackName=e.name||e.id,i.updatedAt=Date.now(),tn(i)}return await Tt(),n.length}async function nS(e){let t=await rn(),n=t.mocks.filter(r=>r.trackId===e);if(n.length){t.mocks=t.mocks.filter(r=>r.trackId!==e),await Tt();for(let r of n)Ot("mocks",r.id).catch(()=>{})}return n.length}async function rS(){if(!Ee())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Fe("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await rn();for(let r of n.mocks){await tn(r);for(let o of r.versions||[])o.htmlRel&&await nn(r,o.v,"html"),(o.pngRel||o.posterRel)&&await nn(r,o.v,"png"),o.videoRel&&await nn(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var Up,Fx,Ux,pP,Hx,Dx,mP,hs,hP,Wx,ws,wP,Bx,gP,yP,qx,ht,ro,Lp,tn,nn,ei,bP,_P,Fp,gs=C(()=>{ue();qn();jp();Gn();Up=dP(uP),Fx=process.env.BOSTRAT_STATE_DIR||De.join(process.env.HOME||Lx.tmpdir(),".bostrat"),Ux=De.join(Fx,"mocks.json"),pP=process.env.BOSTRAT_MOCK_ENGINE||"chromium",Hx=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",Dx=process.env.BOSTRAT_FIREFOX_BIN||"firefox",mP=1300,hs=2,hP=32,Wx=1500,ws=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,wP=700*1024*1024,Bx=100*1024*1024,gP=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",yP=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",qx=2e4,ht=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
152
155
|
${n.trim().split(`
|
|
153
156
|
`).slice(-6).join(`
|
|
154
|
-
`)}`:t),this.name="MockRenderError"}},to=null,Ip=null;Qt=e=>et("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),en=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?Ka(e,r,"video"):r&&Ka(e,r,n)||(n==="html"?Et(e,t).html:null);return o?qn("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};Ga=e=>Pe.join(e,".bostrat","mocks");VN=Buffer.from([137,80,78,71,13,10,26,10]);tP=eP,Rp=Promise.resolve()});import sP from"node:fs/promises";import Vx from"node:path";import aP from"node:os";import{randomUUID as zx}from"node:crypto";async function _n(){return An||(Mp||=(async()=>{let e=!0;try{An=JSON.parse(await sP.readFile(Jx,"utf8"))}catch{An={flows:[]},e=!1}finally{Mp=null}if(An.flows||=[],!e&&Ee()){let{records:t,source:n}=await je("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&An.flows.push(r.data);An.flows.length&&await ds().catch(()=>{})}}return An})(),Mp)}async function ds(){await at(Jx,An)}function Np(e,t,n={}){return _({id:e.trackId,cwd:e.cwd},{type:"flow.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,flowId:e.id,name:e.name,stepCount:e.steps.length,mockIds:[...new Set(e.steps.map(r=>r.mockId))],...n}}).catch(()=>{})}async function Yx({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>Wx)throw new Error(`steps must be an array of 1-${Wx}`);let r=await Va({trackId:e}),o=[...n],s=[];for(let i=0;i<t.length;i++){let c=t[i]||{},l=`step ${i+1}`,u=typeof c.caption=="string"?c.caption.trim():"";if(!u)throw new Error(`${l}: caption (string) required`);if(u.length>Kx)throw new Error(`${l}: caption over ${Kx} chars`);let f=c.title==null?null:String(c.title).trim()||null;if(f&&f.length>Gx)throw new Error(`${l}: title over ${Gx} chars`);let d=String(c.mock??"").trim().replace(/^mock:/,"");if(!d)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(k=>k.id===d);if(p.length||(p=r.filter(k=>k.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(k=>`mock:${k.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(k=>k.v===h)))throw new Error(`${l}: "${m.name}" has no captured v${c.v} to pin`);let g=o.findIndex(k=>k.mockId===m.id),x=g>=0?o.splice(g,1)[0].id:zx().slice(0,8);s.push({id:x,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 cP(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>qx)throw new Error(`name over ${qx} chars`);return e.trim()}async function Xx({trackId:e}={}){let t=await _n();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function lP(e){let n=(await _n()).flows.find(r=>r.id===e);return n?{...n}:null}async function Zx(e){let t=await lP(e);if(!t)return null;let n=new Map((await Va({trackId:t.trackId})).map(r=>[r.id,r]));return t.steps=t.steps.map(r=>{let o=n.get(r.mockId);if(!o)return{...r,missing:!0,mockName:null,mockState:null,servedV:null,width:null,height:null};let s=Bt(o,r.v);return{...r,missing:!1,mockName:o.name,mockState:o.state,servedV:s?.v??null,width:s?.width??null,height:s?.height??null}}),t}async function Qx({trackId:e,name:t,steps:n,cwd:r,box:o,agent:s}){if(!e)throw new Error("trackId required");if(!r)throw new Error("cwd required");let a=cP(t),i=await _n(),c=i.flows.find(p=>p.trackId===e&&p.name.toLowerCase()===a.toLowerCase());if(c)throw new Error(`flow "${c.name}" already exists in this Track (flow:${c.id}) \u2014 revise it with \`bostrat-flow update\``);let{steps:l,resolved:u}=await Yx({trackId:e,steps:n}),f=Date.now(),d={id:zx().slice(0,8),trackId:e,box:o||null,agent:s||null,name:a,cwd:r,version:1,versions:[{v:1,steps:l,resolved:u,note:null,createdAt:f}],steps:l,createdAt:f,updatedAt:f};return i.flows.push(d),await ds(),await Np(d,"created"),vl(d),{...d}}async function eS(e,{steps:t,note:n}={}){let o=(await _n()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await Yx({trackId:o.trackId,steps:t,prevSteps:o.steps});return o.version+=1,o.versions.push({v:o.version,steps:s,resolved:a,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),o.steps=s,o.updatedAt=Date.now(),await ds(),await Np(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),vl(o),{...o}}async function tS(e){let t=await _n(),n=t.flows.findIndex(o=>o.id===e);if(n===-1)throw new Error("flow not found");let r=t.flows[n];return t.flows.splice(n,1),await ds(),await Np(r,"deleted"),$t("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function nS(e){return(await _n()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function rS(e){let n=(await _n()).flows.filter(r=>r.trackId===e.id);for(let r of n)r.trackDeletedAt=Date.now(),r.trackName=e.name||e.id,r.updatedAt=Date.now(),vl(r);return n.length&&await ds(),n.length}async function oS(e){let t=await _n(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await ds();for(let r of n)$t("flows",r.id).catch(()=>{})}return n.length}async function sS(){if(!Ee())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await je("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await _n();for(let r of n.flows)await vl(r);return{pushed:n.flows.length,source:"plane"}}var iP,Jx,Wx,qx,Kx,Gx,An,Mp,vl,Pp=O(()=>{ue();Wn();us();Kn();iP=process.env.BOSTRAT_STATE_DIR||Vx.join(process.env.HOME||aP.tmpdir(),".bostrat"),Jx=Vx.join(iP,"flows.json"),Wx=20,qx=80,Kx=500,Gx=80,An=null,Mp=null;vl=e=>et("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import $n from"node:fs/promises";import On from"node:path";import uP from"node:os";import{randomUUID as dP}from"node:crypto";function za(e){return fP.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function nn(){return In||(Dp||=(async()=>{let e=!0;try{In=JSON.parse(await $n.readFile(iS,"utf8"))}catch{In={runbooks:[]},e=!1}finally{Dp=null}return In.runbooks||=[],!e&&Ee()&&await pP().catch(()=>{}),In})(),Dp)}async function ro(){await at(iS,In)}function El(e){return Ja(et("runbooks",{...e},{trackId:e.trackId}))}async function pP(){let{records:e,source:t}=await je("runbooks");if(!(t!=="plane"||!e.length)){await $n.mkdir(Tl,{recursive:!0});for(let n of e){if(n.deletedAt||!n.data?.id)continue;let r=n.data;for(let o of r.versions||[]){let s=await Zs("runbooks",r.id,o.v,"md");typeof s=="string"&&await $n.writeFile(Rn(r,o.v),s).catch(()=>{})}In.runbooks.push(r)}In.runbooks.length&&await ro()}}function fs(e,t,n={}){return _({id:e.trackId,cwd:e.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,runbookId:e.id,name:e.name,status:e.status,...n}}).catch(()=>{})}function Rn(e,t){return On.join(Tl,`${e.id}.v${t}.md`)}function cS(e,t=null){let n=e.versions||[];return t!=null?n.find(r=>r.v===Number(t))||null:n.length?n[n.length-1]:null}function lS(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>aS)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${aS/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=za(e);if(n.length)throw new Error(`runbook appears to contain a secret VALUE (${n.join(", ")}) \u2014 runbooks ship off-box; name the secret and where it goes, never its value. Replace it with a placeholder like <your key> and retry.`);return t}function mP(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function hP({trackId:e,status:t}={}){let n=await nn(),r=e?n.runbooks.filter(o=>o.trackId===e):n.runbooks;return t&&(r=r.filter(o=>o.status===t)),r.map(o=>({...o}))}async function uS({trackId:e,status:t}={}){let n=await nn(),r=await hP({trackId:e,status:t});if(!Ee())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await je("runbooks");if(s!=="plane")return{runbooks:r,fleetDegraded:!0};let a=new Set(n.runbooks.map(c=>c.id)),i=o.filter(c=>!c.deletedAt&&c.data?.id&&!a.has(c.data.id)).map(c=>({...c.data,originNode:c.originNode||null,remote:!0})).filter(c=>(!e||c.trackId===e)&&(!t||c.status===t));return{runbooks:[...r,...i],fleetDegraded:!1}}async function Bp(e){let t=await wP(e);if(t)return t;if(!Ee())return null;let{records:n,source:r}=await je("runbooks");if(r!=="plane")return null;let o=n.find(s=>!s.deletedAt&&s.data?.id===e);return o?{...o.data,originNode:o.originNode||null,remote:!0}:null}async function dS(e,t){let n=await Zs("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function wP(e){let n=(await nn()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function fS({trackId:e,trackName:t,name:n,md:r,cwd:o,box:s,agent:a}){if(!e)throw new Error("trackId required");if(typeof n!="string"||!n.trim())throw new Error("name (string) required");let i=lS(r),c=await nn(),l={id:dP().slice(0,8),trackId:e,trackName:t||e,box:s||null,agent:a||null,name:n.trim(),status:"open",version:1,versions:[],trackDeletedAt:null,cwd:o||null,createdAt:Date.now(),updatedAt:Date.now()};return await $n.mkdir(Tl,{recursive:!0}),await $n.writeFile(Rn(l,1),r),l.versions.push({v:1,mdRel:On.relative(no,Rn(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await ro(),await fs(l,"created",{version:1}),El(l),Ja(qn("runbooks",l.id,1,"md",Rn(l,1))),{...l}}async function pS(e,{md:t,note:n}={}){let o=(await nn()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");mP(o,"update");let s=lS(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await $n.mkdir(Tl,{recursive:!0}),await $n.writeFile(Rn(o,a),t),o.versions.push({v:a,mdRel:On.relative(no,Rn(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await ro(),await fs(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),El(o),Ja(qn("runbooks",o.id,a,"md",Rn(o,a))),{...o}}async function mS(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await nn()).runbooks.find(o=>o.id===e);if(!r)throw new Error("runbook not found");return r.status===t?{...r}:(r.status=t,r.updatedAt=Date.now(),await ro(),await fs(r,t==="done"?"done":"reopened"),El(r),{...r})}async function hS(e){let t=await nn(),n=t.runbooks.findIndex(o=>o.id===e);if(n===-1)throw new Error("runbook not found");let r=t.runbooks[n];t.runbooks.splice(n,1);for(let o of r.versions||[])await $n.rm(On.join(no,o.mdRel),{force:!0}).catch(()=>{});return await ro(),r.trackDeletedAt||await fs(r,"deleted"),Ja($t("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function wS(e){let n=(await nn()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await fs(r,"orphaned"),r.cwd=null,El(r);return n.length&&await ro(),n.length}async function gS(e){let t=await nn(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await fs(r,"deleted");for(let o of r.versions||[])await $n.rm(On.join(no,o.mdRel),{force:!0}).catch(()=>{});Ja($t("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await ro()),n.length}async function yS(){if(!Ee())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await je("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await nn();for(let r of n.runbooks){await et("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await qn("runbooks",r.id,o.v,"md",On.join(no,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var no,iS,Tl,aS,fP,In,Dp,Ja,Al=O(()=>{Wn();ue();Kn();no=process.env.BOSTRAT_STATE_DIR||On.join(process.env.HOME||uP.tmpdir(),".bostrat"),iS=On.join(no,"runbooks.json"),Tl=On.join(no,"runbooks"),aS=256*1024,fP=[{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/}];In=null,Dp=null;Ja=e=>Promise.resolve(e).catch(()=>{})});import jp from"node:fs/promises";import Ya from"node:path";import{randomUUID as gP}from"node:crypto";function Fp(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return yP.has(t)?"image":bP.has(t)?"doc":null}function kP(e){let r=Ya.basename(String(e||"")).replace(/[\x00-\x1f]/g,"").replace(/[^A-Za-z0-9._-]+/g,"_").replace(/^_+|_+$/g,"").slice(-80)||"file";return r.startsWith(".")?`f${r}`:r}async function xS(e,{filename:t,buffer:n}){if(!e)throw new Error("cwd required");if(!Buffer.isBuffer(n)||n.length===0)throw new Error("empty upload");if(n.length>bS)throw new Error(`file too large \u2014 max ${Math.floor(bS/(1024*1024))} MB`);let r=kP(t),o=Ya.extname(r).slice(1),s=Fp(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=gP().slice(0,8),i=Lp(e);await jp.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await jp.writeFile(Ya.join(i,c),n,{mode:384}),await jp.chmod(Ya.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${kS}/${c}`,kind:s,size:n.length}}function SS(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function Up(e){return e.role==="user"&&xP.test((e.text||"").trim())}var kS,Lp,bS,yP,bP,xP,Hp=O(()=>{kS=".bostrat/uploads",Lp=e=>Ya.join(e,kS),bS=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,yP=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),bP=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"]);xP=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function oo(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}var vS=O(()=>{});import{execFile as SP}from"node:child_process";import{promisify as vP}from"node:util";function Wp(e){return`refs/bostrat/wip/${e}`}function AP(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function AS(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function _P(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:EP}async function qp(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await hr(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await hr(r,["remote","get-url","origin"])}catch{return TS.has(r)||(TS.add(r),t.log?.(`[wip-sync] ${n}: no origin remote \u2014 durable code fidelity is chat/memory-only for this Track`)),{pushed:!1,reason:"no-origin"}}try{let o=await Wo(r,"bostrat: wip snapshot"),{stdout:s}=await hr(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(_l.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=Wp(n);return await hr(r,["push","--force","origin",`${o}:${i}`],ES),_l.set(r,a),await _({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,wip:!0,ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${n}: pushed ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return t.log?.(`[wip-sync] ${n}: push failed (will retry): ${String(o?.message||o).split(`
|
|
155
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function
|
|
156
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function
|
|
157
|
+
`)}`:t),this.name="MockRenderError"}},ro=null,Lp=null;tn=e=>tt("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),nn=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?Qa(e,r,"video"):r&&Qa(e,r,n)||(n==="html"?At(e,t).html:null);return o?Kn("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};ei=e=>De.join(e,".bostrat","mocks");bP=Buffer.from([137,80,78,71,13,10,26,10]);_P=EP,Fp=Promise.resolve()});import OP from"node:fs/promises";import cS from"node:path";import CP from"node:os";import{randomUUID as lS}from"node:crypto";async function In(){return _n||(qp||=(async()=>{let e=!0;try{_n=JSON.parse(await OP.readFile(uS,"utf8"))}catch{_n={flows:[]},e=!1}finally{qp=null}if(_n.flows||=[],!e&&Ee()){let{records:t,source:n}=await Fe("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&_n.flows.push(r.data);_n.flows.length&&await ys().catch(()=>{})}}return _n})(),qp)}async function ys(){await it(uS,_n)}function Kp(e,t,n={}){return _({id:e.trackId,cwd:e.cwd},{type:"flow.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,flowId:e.id,name:e.name,stepCount:e.steps.length,mockIds:[...new Set(e.steps.map(r=>r.mockId))],...n}}).catch(()=>{})}async function dS({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>oS)throw new Error(`steps must be an array of 1-${oS}`);let r=await ti({trackId:e}),o=[...n],s=[];for(let i=0;i<t.length;i++){let c=t[i]||{},l=`step ${i+1}`,u=typeof c.caption=="string"?c.caption.trim():"";if(!u)throw new Error(`${l}: caption (string) required`);if(u.length>aS)throw new Error(`${l}: caption over ${aS} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>iS)throw new Error(`${l}: title over ${iS} chars`);let f=String(c.mock??"").trim().replace(/^mock:/,"");if(!f)throw new Error(`${l}: mock (name or id) required`);let p=r.filter(k=>k.id===f);if(p.length||(p=r.filter(k=>k.name.toLowerCase()===f.toLowerCase())),!p.length)throw new Error(`${l}: no mock matching "${f}" in this Track \u2014 see \`bostrat-mock ls\``);if(p.length>1)throw new Error(`${l}: "${f}" is ambiguous (${p.map(k=>`mock:${k.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(k=>k.v===h)))throw new Error(`${l}: "${m.name}" has no captured v${c.v} to pin`);let g=o.findIndex(k=>k.mockId===m.id),x=g>=0?o.splice(g,1)[0].id:lS().slice(0,8);s.push({id:x,mockId:m.id,caption:u,title:d,v:h})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function NP(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>sS)throw new Error(`name over ${sS} chars`);return e.trim()}async function fS({trackId:e}={}){let t=await In();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function PP(e){let n=(await In()).flows.find(r=>r.id===e);return n?{...n}:null}async function pS(e){let t=await PP(e);if(!t)return null;let n=new Map((await ti({trackId:t.trackId})).map(r=>[r.id,r]));return t.steps=t.steps.map(r=>{let o=n.get(r.mockId);if(!o)return{...r,missing:!0,mockName:null,mockState:null,servedV:null,width:null,height:null};let s=jt(o,r.v);return{...r,missing:!1,mockName:o.name,mockState:o.state,servedV:s?.v??null,width:s?.width??null,height:s?.height??null}}),t}async function mS({trackId:e,name:t,steps:n,cwd:r,box:o,agent:s}){if(!e)throw new Error("trackId required");if(!r)throw new Error("cwd required");let a=NP(t),i=await In(),c=i.flows.find(p=>p.trackId===e&&p.name.toLowerCase()===a.toLowerCase());if(c)throw new Error(`flow "${c.name}" already exists in this Track (flow:${c.id}) \u2014 revise it with \`bostrat-flow update\``);let{steps:l,resolved:u}=await dS({trackId:e,steps:n}),d=Date.now(),f={id:lS().slice(0,8),trackId:e,box:o||null,agent:s||null,name:a,cwd:r,version:1,versions:[{v:1,steps:l,resolved:u,note:null,createdAt:d}],steps:l,createdAt:d,updatedAt:d};return i.flows.push(f),await ys(),await Kp(f,"created"),Ol(f),{...f}}async function hS(e,{steps:t,note:n}={}){let o=(await In()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await dS({trackId:o.trackId,steps:t,prevSteps:o.steps});return o.version+=1,o.versions.push({v:o.version,steps:s,resolved:a,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),o.steps=s,o.updatedAt=Date.now(),await ys(),await Kp(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Ol(o),{...o}}async function wS(e){let t=await In(),n=t.flows.findIndex(o=>o.id===e);if(n===-1)throw new Error("flow not found");let r=t.flows[n];return t.flows.splice(n,1),await ys(),await Kp(r,"deleted"),Ot("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function gS(e){return(await In()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function yS(e){let n=(await In()).flows.filter(r=>r.trackId===e.id);for(let r of n)r.trackDeletedAt=Date.now(),r.trackName=e.name||e.id,r.updatedAt=Date.now(),Ol(r);return n.length&&await ys(),n.length}async function bS(e){let t=await In(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await ys();for(let r of n)Ot("flows",r.id).catch(()=>{})}return n.length}async function kS(){if(!Ee())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Fe("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await In();for(let r of n.flows)await Ol(r);return{pushed:n.flows.length,source:"plane"}}var MP,uS,oS,sS,aS,iS,_n,qp,Ol,Gp=C(()=>{ue();qn();gs();Gn();MP=process.env.BOSTRAT_STATE_DIR||cS.join(process.env.HOME||CP.tmpdir(),".bostrat"),uS=cS.join(MP,"flows.json"),oS=20,sS=80,aS=500,iS=80,_n=null,qp=null;Ol=e=>tt("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import On from"node:fs/promises";import Cn from"node:path";import DP from"node:os";import{randomUUID as BP}from"node:crypto";function ni(e){return jP.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function on(){return Rn||(Vp||=(async()=>{let e=!0;try{Rn=JSON.parse(await On.readFile(SS,"utf8"))}catch{Rn={runbooks:[]},e=!1}finally{Vp=null}return Rn.runbooks||=[],!e&&Ee()&&await LP().catch(()=>{}),Rn})(),Vp)}async function so(){await it(SS,Rn)}function Ml(e){return ri(tt("runbooks",{...e},{trackId:e.trackId}))}async function LP(){let{records:e,source:t}=await Fe("runbooks");if(!(t!=="plane"||!e.length)){await On.mkdir(Cl,{recursive:!0});for(let n of e){if(n.deletedAt||!n.data?.id)continue;let r=n.data;for(let o of r.versions||[]){let s=await aa("runbooks",r.id,o.v,"md");typeof s=="string"&&await On.writeFile($n(r,o.v),s).catch(()=>{})}Rn.runbooks.push(r)}Rn.runbooks.length&&await so()}}function bs(e,t,n={}){return _({id:e.trackId,cwd:e.cwd},{type:"runbook.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box||null,provider:e.agent||null,payload:{ts:Date.now(),change:t,runbookId:e.id,name:e.name,status:e.status,...n}}).catch(()=>{})}function $n(e,t){return Cn.join(Cl,`${e.id}.v${t}.md`)}function vS(e,t=null){let n=e.versions||[];return t!=null?n.find(r=>r.v===Number(t))||null:n.length?n[n.length-1]:null}function TS(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>xS)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${xS/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=ni(e);if(n.length)throw new Error(`runbook appears to contain a secret VALUE (${n.join(", ")}) \u2014 runbooks ship off-box; name the secret and where it goes, never its value. Replace it with a placeholder like <your key> and retry.`);return t}function FP(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function UP({trackId:e,status:t}={}){let n=await on(),r=e?n.runbooks.filter(o=>o.trackId===e):n.runbooks;return t&&(r=r.filter(o=>o.status===t)),r.map(o=>({...o}))}async function AS({trackId:e,status:t}={}){let n=await on(),r=await UP({trackId:e,status:t});if(!Ee())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await Fe("runbooks");if(s!=="plane")return{runbooks:r,fleetDegraded:!0};let a=new Set(n.runbooks.map(c=>c.id)),i=o.filter(c=>!c.deletedAt&&c.data?.id&&!a.has(c.data.id)).map(c=>({...c.data,originNode:c.originNode||null,remote:!0})).filter(c=>(!e||c.trackId===e)&&(!t||c.status===t));return{runbooks:[...r,...i],fleetDegraded:!1}}async function zp(e){let t=await HP(e);if(t)return t;if(!Ee())return null;let{records:n,source:r}=await Fe("runbooks");if(r!=="plane")return null;let o=n.find(s=>!s.deletedAt&&s.data?.id===e);return o?{...o.data,originNode:o.originNode||null,remote:!0}:null}async function ES(e,t){let n=await aa("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function HP(e){let n=(await on()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function _S({trackId:e,trackName:t,name:n,md:r,cwd:o,box:s,agent:a}){if(!e)throw new Error("trackId required");if(typeof n!="string"||!n.trim())throw new Error("name (string) required");let i=TS(r),c=await on(),l={id:BP().slice(0,8),trackId:e,trackName:t||e,box:s||null,agent:a||null,name:n.trim(),status:"open",version:1,versions:[],trackDeletedAt:null,cwd:o||null,createdAt:Date.now(),updatedAt:Date.now()};return await On.mkdir(Cl,{recursive:!0}),await On.writeFile($n(l,1),r),l.versions.push({v:1,mdRel:Cn.relative(oo,$n(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await so(),await bs(l,"created",{version:1}),Ml(l),ri(Kn("runbooks",l.id,1,"md",$n(l,1))),{...l}}async function IS(e,{md:t,note:n}={}){let o=(await on()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");FP(o,"update");let s=TS(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await On.mkdir(Cl,{recursive:!0}),await On.writeFile($n(o,a),t),o.versions.push({v:a,mdRel:Cn.relative(oo,$n(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await so(),await bs(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),Ml(o),ri(Kn("runbooks",o.id,a,"md",$n(o,a))),{...o}}async function RS(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await on()).runbooks.find(o=>o.id===e);if(!r)throw new Error("runbook not found");return r.status===t?{...r}:(r.status=t,r.updatedAt=Date.now(),await so(),await bs(r,t==="done"?"done":"reopened"),Ml(r),{...r})}async function $S(e){let t=await on(),n=t.runbooks.findIndex(o=>o.id===e);if(n===-1)throw new Error("runbook not found");let r=t.runbooks[n];t.runbooks.splice(n,1);for(let o of r.versions||[])await On.rm(Cn.join(oo,o.mdRel),{force:!0}).catch(()=>{});return await so(),r.trackDeletedAt||await bs(r,"deleted"),ri(Ot("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function OS(e){let n=(await on()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await bs(r,"orphaned"),r.cwd=null,Ml(r);return n.length&&await so(),n.length}async function CS(e){let t=await on(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await bs(r,"deleted");for(let o of r.versions||[])await On.rm(Cn.join(oo,o.mdRel),{force:!0}).catch(()=>{});ri(Ot("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await so()),n.length}async function MS(){if(!Ee())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Fe("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await on();for(let r of n.runbooks){await tt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await Kn("runbooks",r.id,o.v,"md",Cn.join(oo,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var oo,SS,Cl,xS,jP,Rn,Vp,ri,Nl=C(()=>{qn();ue();Gn();oo=process.env.BOSTRAT_STATE_DIR||Cn.join(process.env.HOME||DP.tmpdir(),".bostrat"),SS=Cn.join(oo,"runbooks.json"),Cl=Cn.join(oo,"runbooks"),xS=256*1024,jP=[{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/}];Rn=null,Vp=null;ri=e=>Promise.resolve(e).catch(()=>{})});import Jp from"node:fs/promises";import oi from"node:path";import{randomUUID as WP}from"node:crypto";function Xp(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return qP.has(t)?"image":KP.has(t)?"doc":null}function GP(e){let r=oi.basename(String(e||"")).replace(/[\x00-\x1f]/g,"").replace(/[^A-Za-z0-9._-]+/g,"_").replace(/^_+|_+$/g,"").slice(-80)||"file";return r.startsWith(".")?`f${r}`:r}async function DS(e,{filename:t,buffer:n}){if(!e)throw new Error("cwd required");if(!Buffer.isBuffer(n)||n.length===0)throw new Error("empty upload");if(n.length>NS)throw new Error(`file too large \u2014 max ${Math.floor(NS/(1024*1024))} MB`);let r=GP(t),o=oi.extname(r).slice(1),s=Xp(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=WP().slice(0,8),i=Yp(e);await Jp.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await Jp.writeFile(oi.join(i,c),n,{mode:384}),await Jp.chmod(oi.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${PS}/${c}`,kind:s,size:n.length}}function BS(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function Zp(e){return e.role==="user"&&VP.test((e.text||"").trim())}var PS,Yp,NS,qP,KP,VP,Qp=C(()=>{PS=".bostrat/uploads",Yp=e=>oi.join(e,PS),NS=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,qP=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),KP=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"]);VP=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function ao(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}var jS=C(()=>{});import{execFile as zP}from"node:child_process";import{promisify as JP}from"node:util";function em(e){return`refs/bostrat/wip/${e}`}function ZP(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function US(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function QP(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:XP}async function tm(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await yr(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await yr(r,["remote","get-url","origin"])}catch{return LS.has(r)||(LS.add(r),t.log?.(`[wip-sync] ${n}: no origin remote \u2014 durable code fidelity is chat/memory-only for this Track`)),{pushed:!1,reason:"no-origin"}}try{let o=await Yo(r,"bostrat: wip snapshot"),{stdout:s}=await yr(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(Pl.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=em(n);return await yr(r,["push","--force","origin",`${o}:${i}`],FS),Pl.set(r,a),await _({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,wip:!0,ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${n}: pushed ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return t.log?.(`[wip-sync] ${n}: push failed (will retry): ${String(o?.message||o).split(`
|
|
158
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}async function nm(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await yr(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await yr(r,["remote","get-url","origin"])}catch{return{pushed:!1,reason:"no-origin"}}try{let o=await Yo(r,"bostrat: transfer rescue snapshot"),{stdout:s}=await yr(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(Pl.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=ZP(n);return await yr(r,["push","origin",`${o}:${i}`],FS),Pl.set(r,a),await _({id:n,cwd:r},{type:"code.pushed",actor:"orchestrator",payload:{ref:i,sha:o,rescue:!0,ts:Date.now()}}).catch(()=>{}),t.log?.(`[wip-sync] ${n}: rescued stranded work ${o.slice(0,7)} \u2192 ${i}`),{pushed:!0,sha:o,ref:i}}catch(o){return t.log?.(`[wip-sync] ${n}: rescue push failed: ${String(o?.message||o).split(`
|
|
159
|
+
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function HS({listTracks:e,sweepMs:t=QP(),log:n=console}){let r=!1,o=async()=>{if(!r){r=!0;try{let a=await e()??[];for(let i of a)i?.archivedAt||i?.kind==="setup"||i?.transferredTo||await tm(i,{log:n})}catch(a){n.log?.(`[wip-sync] sweep failed: ${a?.message||a}`)}finally{r=!1}}},s=setInterval(o,t);return s.unref?.(),o(),{stop:()=>clearInterval(s),cycle:o}}var YP,XP,FS,yr,Pl,LS,rm=C(()=>{Vc();ue();YP=JP(zP),XP=12e4,FS=6e4;yr=(e,t,n=15e3)=>YP("git",["-C",e,...t],{timeout:n,maxBuffer:4*1024*1024}),Pl=new Map,LS=new Set});import eD from"node:fs";import om from"node:fs/promises";import tD from"node:path";function sD(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function WS(e,{env:t=process.env,fetchImpl:n=(...o)=>fetch(...o),log:r=console}={}){let o=e?.id;if(!o)return{restored:!1,reason:"no track id"};if(!sD(t))return{restored:!1,reason:"plane not configured"};let s=Ao(e),a=tD.join(s,`${o}.ndjson`);try{if(eD.statSync(a).size>0)return{restored:!1,reason:"log not empty"}}catch{}let i=ke(t),c={authorization:`Bearer ${t.BOSTRAT_PLANE_TOKEN||""}`},l=[],u=0;for(;;){let f=new AbortController,p=setTimeout(()=>f.abort(),oD),m;try{m=await n(`${i}/v1/spine/${encodeURIComponent(o)}?after=${u}&limit=${nD}`,{headers:c,signal:f.signal})}finally{clearTimeout(p)}if(m.status===404)return{restored:!1,reason:"unknown to plane"};if(!m.ok)return{restored:!1,reason:`plane ${m.status}`};let h=await m.json();for(let g of h.events||[])l.push(JSON.stringify(g));if(l.length>rD)return{restored:!1,reason:"event count over safety cap"};if(h.next==null)break;u=h.next}if(!l.length)return{restored:!1,reason:"unknown to plane"};await om.mkdir(s,{recursive:!0});let d=`${a}.pull-${process.pid}-${Date.now()}`;return await om.writeFile(d,l.join(`
|
|
157
160
|
`)+`
|
|
158
|
-
`),await
|
|
161
|
+
`),await om.rename(d,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var nD,rD,oD,qS=C(()=>{et();ue();nD=1e3,rD=5e5,oD=3e4});var vv={};ju(vv,{HANDOFF_LATEST_REL:()=>io,KNOWN_AGENTS:()=>hi,TRACK_NAME_MAX:()=>gm,addTrackRepo:()=>Mm,adoptTrack:()=>Ym,agentAvailability:()=>Ll,announceNodeUpdate:()=>an,answerFullscreenOffer:()=>Wm,answerPrompt:()=>Ul,archiveTrack:()=>uo,archiveTrackForMergedPr:()=>xn,augmentFirstPromptAfterHandoff:()=>ev,buildAgentCmd:()=>ZS,canResumeSubtask:()=>kv,cancelQueued:()=>gi,cancelQueuedToDraft:()=>Em,cancelStopJob:()=>yi,cancelSubtask:()=>Xl,changedRepos:()=>ps,commitTransferOut:()=>Jl,createSetupTrack:()=>Dm,createTrack:()=>ql,deleteTrack:()=>jm,demoteTransferredTracks:()=>Jm,ensureLeadEpochs:()=>Vl,forceMergeSubtask:()=>eh,getHandoffJob:()=>Rs,getSubtaskJob:()=>iB,getTrack:()=>ne,getTransferOutJob:()=>Gm,healDeadLead:()=>pi,interruptLead:()=>Wl,isArchiving:()=>XD,isHandoffNoteEcho:()=>pm,isInterruptEcho:()=>mm,isModelSwitchEcho:()=>hm,leadCliDead:()=>Lm,listSubtaskJobs:()=>fo,listTracks:()=>oe,matchTranscriptsToBoxes:()=>rv,mergeNotifyMessage:()=>wv,navigatePrompt:()=>Am,notifyTrackForMergedPr:()=>Sn,notifyTrackForResolvedPr:()=>Kl,pendingOffers:()=>vm,queueShipCompleteNotify:()=>Im,queuedMessages:()=>co,reapArchivedOrphans:()=>Hm,reconcileHandoffOutcome:()=>gv,reconcileInterruptedHandoffs:()=>Km,reconcileInterruptedSubtasks:()=>Xm,reconcileNodeUpdateNotices:()=>xm,reconcileSubtaskOutcome:()=>bv,refreshStandingInstructions:()=>Pm,refuseIfTransferred:()=>zm,rehydrateLead:()=>ki,removeTrackRepo:()=>Nm,renameTrack:()=>Um,reposOf:()=>gt,resolveTrackBox:()=>Va,resolveTrackBoxByHookToken:()=>Sm,restartLead:()=>Gl,resumeAgentCmd:()=>km,sayToLead:()=>It,sendOptionsForAgent:()=>zt,setTrackDraft:()=>Om,setTrackModel:()=>Cm,startLeadHandoff:()=>zl,startSpinUpView:()=>Rm,startSubtask:()=>Qm,startTransferOut:()=>Vm,stopJobActive:()=>av,subtaskMessages:()=>Tm,trackMessages:()=>Fl,unarchiveTrack:()=>Fm});import W from"node:fs/promises";import G from"node:path";import{randomUUID as _s}from"node:crypto";import{execFile as aD}from"node:child_process";import{promisify as iD}from"node:util";function ZS(e,{skipPermissions:t=!0}={}){let n=jl[e];if(!n)throw new Error(`unknown agent "${e}"`);let r=process.env[n.envVar];return r||(t&&n.skipFlag?`${n.base} ${n.skipFlag}`:n.base)}function km(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=jl[e];if(!s)throw new Error(`unknown agent "${e}"`);let a=process.env[s.envVar];if(a)return a;let i=n&&s.skipFlag?` ${s.skipFlag}`:"";switch(e){case"claude":{if(!t)return`${s.base}${i}`;if(r&&!Nt(r,t)){let c=o&&_e(o)?` --name ${o}`:"";return`${s.base} --session-id ${t}${c}${i}`}return`${s.base} --resume ${t}${i}`}case"cursor":return`${s.base} --continue${i}`;case"codex":return`${s.base} resume --last${i}`;default:return`${s.base}${i}`}}function Is(e){let t=jl[e];if(t)return process.env[t.envVar]||t.base}async function Ll({maxAgeMs:e=6e4}={}){if(br.value&&Date.now()-br.at<e)return br.value;let t=br.value,n=await Promise.all(hi.map(async r=>{let o=await yc(Is(r),{retries:[]});return o===Oo?[r,t?.[r]??!0]:[r,o===gc]}));return br.value=Object.fromEntries(n),br.at=Date.now(),br.value}async function wi(e,t=!0){let n=ZS(e,{skipPermissions:t});if(await yc(n)===$o){br.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.":fd[r]?`Install it on this node: ${fd[r]}`:"Its CLI must be installed and on PATH first.";throw new Error(`agent "${e}" isn't available on this box (command not found: ${r}). ${o}`)}return n}function ev(e,t=io){return`${e.trim()}
|
|
159
162
|
|
|
160
|
-
(Read ${t} for handoff context from the prior lead \u2014 you are now the lead; none of the prior chat is in your context. Then respond to the user above.)`}function
|
|
161
|
-
`;return Jp=Jp.then(()=>W.mkdir(rn,{recursive:!0})).then(()=>W.appendFile(DP,n)).catch(()=>{}),Jp}async function on({current:e,latest:t,min:n=null,installKind:r=null,required:o=!1,status:s="available",policy:a=null,error:i=null,reason:c=null}={}){if(!t)return{announced:0};if(s==="available"){let f=null;try{f=JSON.parse(await W.readFile(Yp,"utf8"))}catch{}if(f?.latest===t&&f?.current===e)return{announced:0,deduped:!0}}let l=await z(),u=0;for(let f of l.tracks)f.archivedAt||f.transferredTo||!f.leadBox||(he(f,"node-update",{status:s,current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},...a?{policy:a}:{},...i?{error:String(i).slice(0,400)}:{},...c?{reason:c}:{},required:o===!0}),u++);if(await X().catch(()=>{}),s==="available")try{await W.mkdir(G.dirname(Yp),{recursive:!0}),await W.writeFile(Yp,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}function FS(e){if(!e)return null;let t=typeof e=="string"?[e]:Array.isArray(e?.codes)&&e.codes.length?e.codes:e?.code?[e.code]:[],n=[...new Set(t.filter(s=>HP.has(s)))];if(!n.length)return null;let r=typeof e=="object"?e?.note:null,o=r&&String(r).slice(0,500).trim()||null;return{code:n[0],...n.length>1?{codes:n}:{},note:o}}async function US(e,t,n,{kind:r}={}){let o=await se(t);if(!o)throw new Error(`box "${t}" is not running`);let s=ci(o.agent);await Xn({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s}),await Fe(t,n,{...Gt(o.agent,n),kind:r||"orchestration"}),await Y({flow:"orchestration",phase:"sent",trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let a=await z(),i=re(a,e);if(!i)return;let c=i.boxes.find(l=>l.name===t);he(i,"orchestration-prompt",{box:t,agent:c?.agent||o.agent,boxRole:c?.role,prompt:n,kind:r||"orchestration"}),await X()}async function WP(e){let t=G.join(So(e),`${e.id}.ndjson`),n;try{n=await W.stat(t)}catch{return null}let r=$S.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await L(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return $S.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function qP(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:Jf(n.name,n.agent)}async function _t(e,t=null){let n=t??new Map((await Ot()).map(i=>[i.name,i])),r=e.boxes.map(i=>{let c=n.get(i.name),l=Mn.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([WP(e),qP(e,r)]);return{...e,boxes:r,lastResponseAt:o,working:s,prompted:a,archiving:oi.has(e.id)}}function HS(e,t,n){let r=[...e].sort((l,u)=>l.createdAt-u.createdAt),o=[...t].sort((l,u)=>l.birthMs-u.birthMs),s=new Set,a=[];for(let l of r)l.sessionId&&n(l.sessionId)&&!s.has(l.sessionId)?s.add(l.sessionId):a.push(l);let i=new Map(r.map((l,u)=>[l,u])),c=[];for(let l of a){if(l.sessionId&&n(l.sessionId)){c.push({box:l.name,sessionId:null});continue}if(!l.sessionId||l.pinned)continue;let u=i.get(l),f=l.createdAt-KP,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 WS(e){let t=new Map;for(let r of e.tracks)for(let o of r.boxes)o.agent==="claude"&&o.cwd&&(t.has(o.cwd)||t.set(o.cwd,[]),t.get(o.cwd).push({track:r,box:o}));let n=!1;for(let[r,o]of t){let s=o.map(d=>d.box.sessionId),a=wg(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=HS(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 ig(d.box,d.sessionId),n=!0}}return n}async function ae(){let e=await z();await WS(e)&&await X();let t=new Map((await Ot()).map(n=>[n.name,n]));return Promise.all(e.tracks.map(n=>_t(n,t)))}async function La(e,t){let n=await z();if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===e);if(o)return{track:r,box:o}}if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===t);if(o)return{track:r,box:o}}return null}async function dm(e){let t=js(e);if(!t)return null;let n=await z();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&tw(o.hookTokenHash,t))return{track:r,box:o};return null}async function ne(e){let t=await z(),n=re(t,e);return n?(await WS(t)&&await X(),_t(n)):null}async function fm(e){let t=await L(e).catch(()=>[]),n;try{n=Zn(t)}catch{return[]}return Ey(n).map(r=>({kind:r.kind,id:r.offerId??r.proposalId??r.id,ts:r.ts??null}))}function om(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(GP[e.agent]||[]).some(n=>n.test(t))}function sm(e){let t=(e.text||"").trim();return t?VP.some(n=>n.test(t)):!1}async function Cl(e,t={}){let n=await ne(e);if(!n)return null;let r=new Set([...n.events.filter(b=>b.type==="orchestration-prompt"&&b.prompt).map(b=>String(b.prompt).trim()),...n.events.filter(b=>b.type==="said"&&b.text).map(b=>String(b.text).trim())]),o=xs(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=bp(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...fx(n),...l],d={windows:[...Vy(n),...zy(n,Mc),...a,...u]},m=(await Promise.all(n.boxes.map(b=>lf(b,t)))).flat(),h=m.map(b=>{let P={kind:"message",...b};if(P.role==="assistant"&&(P.text||"").trim()&&P.tools?.length){let Me=Ta(P.agent).cliTools,We=P.tools.filter(Ht=>!Me.has(Ht));return{...P,tools:We.length?We:void 0}}return P}).map(b=>{if(b.role!=="user")return b;let P=$c(b.text);return P?P.kind==="suppress"?null:{...b,note:P,text:wy(P)}:b}).filter(Boolean).filter(b=>!(b.role==="user"&&r.has((b.text||"").trim()))).filter(b=>!om(b)).filter(b=>!sm(b)).filter(b=>!rm(b)).filter(b=>!Up(b)).filter(b=>b.role!=="assistant"||!!(b.text||"").trim()||!!b.images?.length).filter(b=>!Jy(b,d)),g=h.filter(b=>b.failed&&b.text===Dr);if(g.length){let b=new Map;for(let P of g){if(!b.has(P.box)){let Ht=[...(await Ae(P.box,{lines:Le}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(te=>te[1]);b.set(P.box,Ht.length?Ht[Ht.length-1]:null)}let Me=b.get(P.box);Me&&(P.text=`\u25A0 ${Me}`)}}let x=await Promise.all(n.boxes.map(b=>_b(b,t)));await z0(n,x.flat());let{agentTurnBoxes:k}=await G0(n,h);for(let b of n.boxes)k.has(b.name)&&b.cwd&&tc(b.cwd);await Sk(n),await bx(n,h).catch(()=>{});for(let b of n.boxes)b.agent==="codex"&&b.state==="live"&&await Y0(n,b),b.agent==="cursor"&&b.state==="live"&&await tx(n,b);let E=await Promise.all(n.boxes.map(b=>Ab(b,t)));await V0(n,E.flat().filter(b=>!Xp(b.box,b.ts,u)));let I=await L(n),M=c&&i.viewId?i.viewId:null,N=Nk(I),C=Zn(I).filter(b=>!(b.kind==="message"&&(om(b)||sm(b)||rm(b)||Up(b)))&&!(b.kind==="view-card"&&M&&b.viewId===M)),T=Pk(C,N),j=C.filter(b=>!(b.kind==="message"&&b.role==="assistant"&&!T.has(b)&&Xp(b.box,b.ts,N))),Z=Wd(I,j).map(b=>({kind:"command-batch",id:b.id,ts:b.pending?b.ts:(b.anchorTs??b.ts)-1,anchorTs:b.anchorTs,anchorBox:b.anchorBox,agent:b.agent,boxRole:b.boxRole,commands:b.commands,pending:b.pending})).filter(b=>!Xp(b.anchorBox,b.anchorTs??b.ts,u)),fe=vy(Ty([...j,...Z].filter(b=>b.ts!=null).sort((b,P)=>b.ts-P.ts))),ie=JP(n),D=ie.size?fe.filter(b=>b.kind==="event"||!ie.has(b.box??b.anchorBox)):fe,R=n.boxes.find(b=>b.name===n.leadBox)||null,U=R?await aa(R.cwd,R.sessionId,{fallbackNewest:!1}):null;if(!U&&R){let b=[...I].reverse().find(P=>P.type==="context.usage"&&P.boxId===R.name);b?.payload?.window&&(U={pct:b.payload.pct??(b.payload.occupancy!=null?b.payload.occupancy/b.payload.window:null),occupancy:b.payload.occupancy??null,window:b.payload.window,windowKnown:!0,model:b.payload.model||null})}let pe=[...m].reverse().find(b=>b.role==="assistant"&&b.model),Se=pe?.model||null,He=[...n.events||[]].reverse().find(b=>b.type==="model-change"),le=U?.model||Se;He&&(!pe?.ts||He.at>=pe.ts)&&(le=He.to);let q=R?await Ae(R.name,{lines:Le}).catch(()=>""):"",J=R&&Vr(R.agent,q)?await fn(R.name).catch(()=>q):q,ee=R?or(R,J):null;if(ee&&R){let b=Bc(R,ee,J);if(b){let P=Wb(R.name,ee.promptId);P.action!=="give-up"&&(P.action==="send"&&($e(R.name,b.keys,{kind:"auto-reject"}).catch(()=>{}),P.first&&_(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:R.name,provider:R.agent,payload:{ts:Date.now(),promptId:ee.promptId,rule:b.id}}).catch(()=>{})),ee=null)}}!ee&&R&&!pa(R.agent,q)&&!ao(n.id).length&&(ee=Ud(D,{leadBoxId:R.name,provider:R.agent,isAnswered:b=>I.some(P=>P.type==="prompt.answered"&&P.payload?.promptId===b)})),ee&&vk(n,R,ee);let Xe=D.filter(b=>!(b.kind==="message"&&b.role==="assistant"&&b.ask&&!String(b.text||"").trim()&&!b.images?.length)),w=!!R&&il(R.name,R.agent,q)&&!ee;R&&!R.readyAt&&(w||ee||q&&vc(R.agent,q))&&(R.readyAt=Date.now(),await X());let v=!!R&&!R.readyAt&&Date.now()-(n.createdAt||0)<zP;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&R&&vc(R.agent,q)&&!ee){let b=await z(),P=re(b,n.id);P?.setup&&!P.setup.connectedAt&&(P.setup.connectedAt=Date.now(),n.setup.connectedAt=P.setup.connectedAt,await _(P,{type:"provider.connected",actor:"system",source:"pane",boxId:R.name,provider:R.agent,payload:{provider:R.agent,ts:P.setup.connectedAt}}).catch(()=>{}),await X())}let $=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,ve=null;if(c){let b=Date.now()-i.startedAt;if(i.viewId&&!w){let P=wl(n.id);P&&(he(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:P.finishedAt}),await X())}else if(ee||b>ux&&!w&&!i.viewId||b>yp){let P=ee?"needs-input":b>yp?"timeout":"no-view";wl(n.id,{error:P})&&await _(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:R?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:P}}).catch(()=>{})}else ve={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:Xe,context:U?{pct:U.pct,occupancy:U.occupancy,window:U.window,windowKnown:U.windowKnown,model:U.model}:null,models:{current:le,options:hk(R?.agent)},pendingPrompt:ee,working:w,stopping:KS(n.id),leadStarting:v,handoff:$,view:ve,queued:ao(n.id).filter(b=>b.kind!=="ship-notify"&&b.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function JP(e){let t=new Set;for(let n of e.boxes||[])n.role==="subtask"&&n.name&&t.add(n.name);for(let n of Object.values(e.subtasks||{}))n?.box&&t.add(n.box);return t}async function pm(e,t){let n=await ne(e);if(!n)return null;let r=co(e).find(f=>f.id===t)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await L(n),a=Zn(s).filter(f=>f.box===o&&!(f.kind==="message"&&f.role==="assistant"&&f.ask&&!String(f.text||"").trim()&&!f.images?.length)),i=Wd(s.filter(f=>f.boxId===o),a.filter(f=>f.kind==="message")).map(f=>({kind:"command-batch",id:f.id,ts:f.pending?f.ts:(f.anchorTs??f.ts)-1,anchorTs:f.anchorTs,anchorBox:f.anchorBox,agent:f.agent,boxRole:f.boxRole,commands:f.commands,pending:f.pending})),c=[...a,...i].filter(f=>f.ts!=null).sort((f,d)=>f.ts-d.ts),l=n.boxes.find(f=>f.name===o)||null,u=!1;if(l&&l.state==="live"){let f=await Ae(o,{lines:Le}).catch(()=>"");u=il(o,l.agent||r.agent,f)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Ml(e,{promptId:t,answers:n,text:r}={}){let o=re(await z(),e);if(!o)throw new Error(`track "${e}" not found`);di(e,"prompt-answer");let s=o.boxes.find(m=>m.name===o.leadBox);if(!s)throw new Error(`track "${e}" has no lead box`);if(!await se(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await wf(s);if(i||(i=await YP(o,s)),!i)throw new Error("no pending prompt to answer");if(t&&t!==i.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let c;if(i.answerMode==="text"){let m=typeof r=="string"?r.trim():"";if(!m)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(m)||m.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await Fe(o.leadBox,m,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await $e(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let m=ky(i,n||[],typeof r=="string"?r:"");if(!m)throw new Error("no answer selected \u2014 pick an option or reply in chat");await It(o.id,m,{kind:"prompt-answer"}),c={mode:"say",value:m,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let m=Lb(i,n||[]);await $e(o.leadBox,m,{kind:"answer-prompt"}),c={mode:"keys",value:m.join(" "),count:m.length}}else{let m=qg(i,n||[]);await Fe(o.leadBox,m,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:m,count:1}}if(i.answerMode==="say"){let m=i.questions||[];for(let h=0;h<m.length;h++){let g=m[h],x=new Set(Array.isArray(n?.[h])?n[h]:[]),k=[...x].map(I=>g.options?.[I]).filter(Boolean).map(I=>({label:I.label,description:I.description||""})),E=(g.options||[]).map((I,M)=>x.has(M)?null:I?.label).filter(Boolean);Mf(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:h,questionTotal:i.questionTotal??m.length,header:g.header||"",question:g.question||"",multiSelect:!!g.multiSelect,chosen:k,rejected:E})}return await Y({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=new Set(Array.isArray(n?.[0])?n[0]:[]),f=[...u].map(m=>l.options?.[m]).filter(Boolean).map(m=>({label:m.label,description:m.description||""})),d=(l.options||[]).map((m,h)=>u.has(h)?null:m?.label).filter(Boolean),p=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:f,rejected:d,...p?{control:p}:{}},await Y({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),Mf(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function YP(e,t){if(!t)return null;let n=await L(e).catch(()=>[]);return Ud(Zn(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function mm(e,{promptId:t,questionIndex:n}={}){let r=re(await z(),e);if(!r)throw new Error(`track "${e}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(!await se(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await wf(o);if(!a)throw new Error("no pending prompt to navigate");if(a.provider!=="claude")throw new Error("prompt navigation not supported for this provider");if(a.review)throw new Error("cannot navigate on the review screen \u2014 answer or cancel");let i=a.questionTotal??1;if(i<2)throw new Error("not a multi-question prompt");if(!Number.isInteger(n)||n<0||n>=i)throw new Error("questionIndex out of range");if(t&&t!==a.promptId)throw new Error("prompt changed since it was shown \u2014 reload and try again");let c=Fb(a,n);return c.length?(await $e(r.leadBox,c,{kind:"navigate-prompt"}),await Y({flow:"prompt",phase:"navigated",sessionName:r.leadBox,agent:o.agent,provider:a.provider,promptId:a.promptId,fromIndex:a.questionIndex??0,toIndex:n,mode:"keys",value:c.join(" "),count:c.length}).catch(()=>{}),{ok:!0,promptId:a.promptId,sent:c.length}):{ok:!0,promptId:a.promptId,sent:0}}function Nl(e){let t=ei.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},ei.set(e,t)),t}function ao(e){return(ei.get(e)?.items??[]).map(t=>({...t}))}function ui(e){let t=ei.get(e);if(!t)return[];let n=t.items.splice(0);return t.idleSince=null,t.timer&&(clearInterval(t.timer),t.timer=null),n}async function hm(e){let t=ui(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=re(await z(),e);if(o)return r=gs(o,n),await X(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function wm(e){let t=Nl(e);t.timer||(t.timer=setInterval(()=>ZP(e).catch(()=>{}),XP))}async function ZP(e){let t=ei.get(e);if(!(!t||t.flushing)){if(!t.items.length){t.timer&&(clearInterval(t.timer),t.timer=null);return}t.flushing=!0;try{let n=re(await z(),e);if(!n||n.archivedAt){ui(e);return}let r=xs(e);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){oo(t,!1);return}let o=await se(n.leadBox),s=n.boxes.find(c=>c.name===n.leadBox);if(o&&await Im(o)&&(o=await si(n,s,"queue")?await se(n.leadBox):null,await X()),!o){oo(t,!1);return}if(Jr(s?.agent)){oo(t,!1);return}let a=await Ae(n.leadBox,{lines:Le}).catch(()=>"");if(Sc(s?.agent,a)){oo(t,!1);return}if(Vr(s?.agent,a)){let c=await fn(n.leadBox).catch(()=>a);if(or(s,c)){oo(t,!1);return}}if(!oo(t,!0))return;let i=t.items.shift();t.idleSince=null;try{await am(n,s,o,i.text,i.kind??null,i.attachments??null)}catch(c){if(c?.code!=="cli-not-running")throw c;t.items.unshift(i),await si(n,s,"queue-gate")}await X()}finally{t.flushing=!1}}}async function qS(e,t,n,r){let o=Jr(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(gs(e,[n]),await X()),sk(e.id,o))try{await _(e,{type:"usage.cap.hit",actor:"system",provider:t?.agent??null,boxId:e.leadBox,payload:{...o,blockedKind:r??null}})}catch{}let s=new Error(`${t?.agent??"the lead provider"} is paused \u2014 its ${o.window} spend limit is reached ($${o.spentUsd.toFixed(2)} of $${o.limitUsd.toFixed(2)})`);throw s.usageCap=o,s}async function QP(e,t){let n=await Ae(e.leadBox,{lines:Le}).catch(()=>"");if(!Vr(t?.agent,n))return!1;let r=await fn(e.leadBox).catch(()=>n);return!!or(t,r)}async function am(e,t,n,r,o=null,s=null){await qS(e,t,r,o);let a=ti.get(e.id)||0,i=r,c=!1,l=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(i=LS(r,e.pendingHandoffHydrate.artifact||so),delete e.pendingHandoffHydrate,c=!0);let u=Array.isArray(s)?s.filter(d=>d&&d.rel):[];if(u.length){let d=SS(u.map(p=>p.rel));i=i.trim()?`${i.trim()}
|
|
163
|
+
(Read ${t} for handoff context from the prior lead \u2014 you are now the lead; none of the prior chat is in your context. Then respond to the user above.)`}function pm(e){return e.role==="user"&&uD.test((e.text||"").trim())}async function z(){return Et||(sm||=(async()=>{try{Et=JSON.parse(await W.readFile(XS,"utf8"))}catch{Et={tracks:[]}}finally{sm=null}return Et})(),sm)}async function X(){await it(XS,Et)}function pD(e,t){let n=JSON.stringify({trackId:e,...t})+`
|
|
164
|
+
`;return am=am.then(()=>W.mkdir(sn,{recursive:!0})).then(()=>W.appendFile(cD,n)).catch(()=>{}),am}async function an({current:e,latest:t,min:n=null,installKind:r=null,required:o=!1,status:s="available",policy:a=null,error:i=null,reason:c=null}={}){if(!t)return{announced:0};if(s==="available"){let d=null;try{d=JSON.parse(await W.readFile(im,"utf8"))}catch{}if(d?.latest===t&&d?.current===e)return{announced:0,deduped:!0}}let l=await z(),u=0;for(let d of l.tracks)d.archivedAt||d.transferredTo||!d.leadBox||(he(d,"node-update",{status:s,current:e??null,latest:t,...n?{min:n}:{},...r?{installKind:r}:{},...a?{policy:a}:{},...i?{error:String(i).slice(0,400)}:{},...c?{reason:c}:{},required:o===!0}),u++);if(await X().catch(()=>{}),s==="available")try{await W.mkdir(G.dirname(im),{recursive:!0}),await W.writeFile(im,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function xm({current:e,installKind:t=null,policy:n=null}={}){if(!e)return{reconciled:!1,announced:0,targets:[]};let r=await z(),o=0,s=new Set;for(let a of r.tracks){if(a.archivedAt||a.transferredTo||!a.leadBox)continue;let i=new Map;for(let c of a.events||[])c.type==="node-update"&&c.latest&&i.set(c.latest,c);for(let[c,l]of i)["done","failed","rolled-back"].includes(l.status)||mD(e,c)||(he(a,"node-update",{status:"done",current:l.current??null,latest:c,...t?{installKind:t}:{},...n?{policy:n}:{},reason:"version-reconciled",installed:e,required:!1}),o++,s.add(c))}return o&&await X().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function mD(e,t){let n=String(e).split(".").map(Number),r=String(t).split(".").map(Number);if(n.some(Number.isNaN)||r.some(Number.isNaN))return!0;for(let o=0;o<Math.max(n.length,r.length);o++){let s=(n[o]||0)-(r[o]||0);if(s)return s<0}return!1}function tv(e){if(!e)return null;let t=typeof e=="string"?[e]:Array.isArray(e?.codes)&&e.codes.length?e.codes:e?.code?[e.code]:[],n=[...new Set(t.filter(s=>hD.has(s)))];if(!n.length)return null;let r=typeof e=="object"?e?.note:null,o=r&&String(r).slice(0,500).trim()||null;return{code:n[0],...n.length>1?{codes:n}:{},note:o}}async function nv(e,t,n,{kind:r}={}){let o=await ae(t);if(!o)throw new Error(`box "${t}" is not running`);let s=Is(o.agent);await Xn({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s}),await Le(t,n,{...zt(o.agent,n),kind:r||"orchestration"});let a=await Aa({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration"});await Y({flow:"orchestration",phase:"sent",submitted:a,trackId:e,sessionName:t,agent:o.agent,kind:r||"orchestration",promptHead:n.slice(0,160)});let i=await z(),c=re(i,e);if(!c)return;let l=c.boxes.find(u=>u.name===t);he(c,"orchestration-prompt",{box:t,agent:l?.agent||o.agent,boxRole:l?.role,prompt:n,kind:r||"orchestration"}),await X()}async function wD(e){let t=G.join(Ao(e),`${e.id}.ndjson`),n;try{n=await W.stat(t)}catch{return null}let r=KS.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await L(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return KS.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function gD(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:ap(n.name,n.agent)}async function _t(e,t=null){let n=t??new Map((await Ct()).map(i=>[i.name,i])),r=e.boxes.map(i=>{let c=n.get(i.name),l=Nn.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([wD(e),gD(e,r)]);return{...e,boxes:r,lastResponseAt:o,working:s,prompted:a,archiving:fi.has(e.id)}}function rv(e,t,n){let r=[...e].sort((l,u)=>l.createdAt-u.createdAt),o=[...t].sort((l,u)=>l.birthMs-u.birthMs),s=new Set,a=[];for(let l of r)l.sessionId&&n(l.sessionId)&&!s.has(l.sessionId)?s.add(l.sessionId):a.push(l);let i=new Map(r.map((l,u)=>[l,u])),c=[];for(let l of a){if(l.sessionId&&n(l.sessionId)){c.push({box:l.name,sessionId:null});continue}if(!l.sessionId||l.pinned)continue;let u=i.get(l),d=l.createdAt-yD,f=r[u+1]?.createdAt??1/0,p=o.find(m=>!s.has(m.id)&&m.birthMs>=d&&m.birthMs<f);p&&(s.add(p.id),c.push({box:l.name,sessionId:p.id}))}return c}async function ov(e){let t=new Map;for(let r of e.tracks)for(let o of r.boxes)o.agent==="claude"&&o.cwd&&(t.has(o.cwd)||t.set(o.cwd,[]),t.get(o.cwd).push({track:r,box:o}));let n=!1;for(let[r,o]of t){let s=o.map(f=>f.box.sessionId),a=Rg(r),i=new Set(a.map(f=>f.id)),c=s.every(f=>f&&i.has(f)),l=new Set(s).size===s.length;if(c&&l)continue;let u=o.map(f=>f.box),d=rv(u,a,f=>i.has(f));for(let f of d){let p=u.find(m=>m.name===f.box);p&&(p.sessionId=f.sessionId),await kg(f.box,f.sessionId),n=!0}}return n}async function oe(){let e=await z();await ov(e)&&await X();let t=new Map((await Ct()).map(n=>[n.name,n]));return Promise.all(e.tracks.map(n=>_t(n,t)))}async function Va(e,t){let n=await z();if(e)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.sessionId&&s.sessionId===e);if(o)return{track:r,box:o}}if(t)for(let r of n.tracks||[]){let o=r.boxes.find(s=>s.cwd===t);if(o)return{track:r,box:o}}return null}async function Sm(e){let t=Gs(e);if(!t)return null;let n=await z();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&mw(o.hookTokenHash,t))return{track:r,box:o};return null}async function ne(e){let t=await z(),n=re(t,e);return n?(await ov(t)&&await X(),_t(n)):null}async function vm(e){let t=await L(e).catch(()=>[]),n;try{n=Zn(t)}catch{return[]}return jy(n).map(r=>({kind:r.kind,id:r.offerId??r.proposalId??r.id,ts:r.ts??null}))}function mm(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(bD[e.agent]||[]).some(n=>n.test(t))}function hm(e){let t=(e.text||"").trim();return t?kD.some(n=>n.test(t)):!1}async function Fl(e,t={}){let n=await ne(e);if(!n)return null;let r=new Set([...n.events.filter(b=>b.type==="orchestration-prompt"&&b.prompt).map(b=>String(b.prompt).trim()),...n.events.filter(b=>b.type==="said"&&b.text).map(b=>String(b.text).trim())]),o=Rs(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=$p(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[..._x(n),...l],f={windows:[...ib(n),...cb(n,Fc),...a,...u]},m=(await Promise.all(n.boxes.map(b=>kf(b,t)))).flat(),h=m.map(b=>{let P={kind:"message",...b};if(P.role==="assistant"&&(P.text||"").trim()&&P.tools?.length){let Me=Ca(P.agent).cliTools,We=P.tools.filter(Ht=>!Me.has(Ht));return{...P,tools:We.length?We:void 0}}return P}).map(b=>{if(b.role!=="user")return b;let P=Bc(b.text);return P?P.kind==="suppress"?null:{...b,note:P,text:Ry(P)}:b}).filter(Boolean).filter(b=>!(b.role==="user"&&r.has((b.text||"").trim()))).filter(b=>!mm(b)).filter(b=>!hm(b)).filter(b=>!pm(b)).filter(b=>!Zp(b)).filter(b=>b.role!=="assistant"||!!(b.text||"").trim()||!!b.images?.length).filter(b=>!lb(b,f)),g=h.filter(b=>b.failed&&b.text===jr);if(g.length){let b=new Map;for(let P of g){if(!b.has(P.box)){let Ht=[...(await Te(P.box,{lines:je}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(te=>te[1]);b.set(P.box,Ht.length?Ht[Ht.length-1]:null)}let Me=b.get(P.box);Me&&(P.text=`\u25A0 ${Me}`)}}let x=await Promise.all(n.boxes.map(b=>Hb(b,t)));await lx(n,x.flat());let{agentTurnBoxes:k}=await ix(n,h);for(let b of n.boxes)k.has(b.name)&&b.cwd&&cc(b.cwd);await Bk(n),await Nx(n,h).catch(()=>{});for(let b of n.boxes)b.agent==="codex"&&b.state==="live"&&await dx(n,b),b.agent==="cursor"&&b.state==="live"&&await wx(n,b);let A=await Promise.all(n.boxes.map(b=>Ub(b,t)));await cx(n,A.flat().filter(b=>!cm(b.box,b.ts,u)));let I=await L(n),M=c&&i.viewId?i.viewId:null,N=Jk(I),O=Zn(I).filter(b=>!(b.kind==="message"&&(mm(b)||hm(b)||pm(b)||Zp(b)))&&!(b.kind==="view-card"&&M&&b.viewId===M)),T=Yk(O,N),j=O.filter(b=>!(b.kind==="message"&&b.role==="assistant"&&!T.has(b)&&cm(b.box,b.ts,N))),Z=Qd(I,j).map(b=>({kind:"command-batch",id:b.id,ts:b.pending?b.ts:(b.anchorTs??b.ts)-1,anchorTs:b.anchorTs,anchorBox:b.anchorBox,agent:b.agent,boxRole:b.boxRole,commands:b.commands,pending:b.pending})).filter(b=>!cm(b.anchorBox,b.anchorTs??b.ts,u)),fe=Dy(By([...j,...Z].filter(b=>b.ts!=null).sort((b,P)=>b.ts-P.ts))),ie=SD(n),D=ie.size?fe.filter(b=>b.kind==="event"||!ie.has(b.box??b.anchorBox)):fe,R=n.boxes.find(b=>b.name===n.leadBox)||null,U=R?await ma(R.cwd,R.sessionId,{fallbackNewest:!1}):null;if(!U&&R){let b=[...I].reverse().find(P=>P.type==="context.usage"&&P.boxId===R.name);b?.payload?.window&&(U={pct:b.payload.pct??(b.payload.occupancy!=null?b.payload.occupancy/b.payload.window:null),occupancy:b.payload.occupancy??null,window:b.payload.window,windowKnown:!0,model:b.payload.model||null})}let pe=[...m].reverse().find(b=>b.role==="assistant"&&b.model),Se=pe?.model||null,He=[...n.events||[]].reverse().find(b=>b.type==="model-change"),le=U?.model||Se;He&&(!pe?.ts||He.at>=pe.ts)&&(le=He.to);let q=R?await Te(R.name,{lines:je}).catch(()=>""):"",J=R&&Jr(R.agent,q)?await pn(R.name).catch(()=>q):q,ee=R?ir(R,J):null;if(ee&&R){let b=Kc(R,ee,J);if(b){let P=ok(R.name,ee.promptId);P.action!=="give-up"&&(P.action==="send"&&(Ie(R.name,b.keys,{kind:"auto-reject"}).catch(()=>{}),P.first&&_(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:R.name,provider:R.agent,payload:{ts:Date.now(),promptId:ee.promptId,rule:b.id}}).catch(()=>{})),ee=null)}}!ee&&R&&!Fo(R.agent,q)&&!co(n.id).length&&(ee=Xd(D,{leadBoxId:R.name,provider:R.agent,isAnswered:b=>I.some(P=>P.type==="prompt.answered"&&P.payload?.promptId===b)})),ee&&jk(n,R,ee);let Ze=D.filter(b=>!(b.kind==="message"&&b.role==="assistant"&&b.ask&&!String(b.text||"").trim()&&!b.images?.length)),w=!!R&&hl(R.name,R.agent,q)&&!ee;R&&!R.readyAt&&(w||ee||q&&Uo(R.agent,q))&&(R.readyAt=Date.now(),await X());let v=!!R&&!R.readyAt&&Date.now()-(n.createdAt||0)<xD;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&R&&Uo(R.agent,q)&&!ee){let b=await z(),P=re(b,n.id);P?.setup&&!P.setup.connectedAt&&(P.setup.connectedAt=Date.now(),n.setup.connectedAt=P.setup.connectedAt,await _(P,{type:"provider.connected",actor:"system",source:"pane",boxId:R.name,provider:R.agent,payload:{provider:R.agent,ts:P.setup.connectedAt}}).catch(()=>{}),await X())}let $=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,ve=null;if(c){let b=Date.now()-i.startedAt;if(i.viewId&&!w){let P=Tl(n.id);P&&(he(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:P.finishedAt}),await X())}else if(ee||b>Ax&&!w&&!i.viewId||b>Rp){let P=ee?"needs-input":b>Rp?"timeout":"no-view";Tl(n.id,{error:P})&&await _(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:R?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:P}}).catch(()=>{})}else ve={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:Ze,context:U?{pct:U.pct,occupancy:U.occupancy,window:U.window,windowKnown:U.windowKnown,model:U.model}:null,models:{current:le,options:$k(R?.agent)},pendingPrompt:ee,working:w,stopping:av(n.id),leadStarting:v,handoff:$,view:ve,queued:co(n.id).filter(b=>b.kind!=="ship-notify"&&b.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function SD(e){let t=new Set;for(let n of e.boxes||[])n.role==="subtask"&&n.name&&t.add(n.name);for(let n of Object.values(e.subtasks||{}))n?.box&&t.add(n.box);return t}async function Tm(e,t){let n=await ne(e);if(!n)return null;let r=fo(e).find(d=>d.id===t)||null;if(!r)return null;let o=r.box||null;if(!o)return{items:[],working:!1,box:null,agent:r.agent,task:r.task};let s=await L(n),a=Zn(s).filter(d=>d.box===o&&!(d.kind==="message"&&d.role==="assistant"&&d.ask&&!String(d.text||"").trim()&&!d.images?.length)),i=Qd(s.filter(d=>d.boxId===o),a.filter(d=>d.kind==="message")).map(d=>({kind:"command-batch",id:d.id,ts:d.pending?d.ts:(d.anchorTs??d.ts)-1,anchorTs:d.anchorTs,anchorBox:d.anchorBox,agent:d.agent,boxRole:d.boxRole,commands:d.commands,pending:d.pending})),c=[...a,...i].filter(d=>d.ts!=null).sort((d,f)=>d.ts-f.ts),l=n.boxes.find(d=>d.name===o)||null,u=!1;if(l&&l.state==="live"){let d=await Te(o,{lines:je}).catch(()=>"");u=hl(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Ul(e,{promptId:t,answers:n,text:r}={}){let o=re(await z(),e);if(!o)throw new Error(`track "${e}" not found`);yi(e,"prompt-answer");let s=o.boxes.find(m=>m.name===o.leadBox);if(!s)throw new Error(`track "${e}" has no lead box`);if(!await ae(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await _f(s);if(i||(i=await vD(o,s)),!i)throw new Error("no pending prompt to answer");if(t&&t!==i.promptId)throw new Error("prompt changed since it was shown \u2014 reload and answer the current prompt");let c;if(i.answerMode==="text"){let m=typeof r=="string"?r.trim():"";if(!m)throw new Error("this prompt needs a code \u2014 paste it into the card");if(/\s/.test(m)||m.length>2048)throw new Error("that doesn't look like a sign-in code \u2014 paste the code exactly as shown");await Le(o.leadBox,m,{enter:!0,kind:"answer-prompt"}),c={mode:"text-input",value:"\u2039code\u203A",count:1,redacted:!0}}else if(i.answerMode==="esc")await Ie(o.leadBox,["Escape"],{kind:"answer-prompt"}),c={mode:"keys",value:"Escape",count:1};else if(i.answerMode==="say"){let m=My(i,n||[],typeof r=="string"?r:"");if(!m)throw new Error("no answer selected \u2014 pick an option or reply in chat");await It(o.id,m,{kind:"prompt-answer"}),c={mode:"say",value:m,count:1}}else if(i.provider==="claude"||i.answerMode==="keys"){let m=ek(i,n||[]);await Ie(o.leadBox,m,{kind:"answer-prompt"}),c={mode:"keys",value:m.join(" "),count:m.length}}else{let m=ry(i,n||[]);await Le(o.leadBox,m,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:m,count:1}}if(i.answerMode==="say"){let m=i.questions||[];for(let h=0;h<m.length;h++){let g=m[h],x=new Set(Array.isArray(n?.[h])?n[h]:[]),k=[...x].map(I=>g.options?.[I]).filter(Boolean).map(I=>({label:I.label,description:I.description||""})),A=(g.options||[]).map((I,M)=>x.has(M)?null:I?.label).filter(Boolean);qf(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:h,questionTotal:i.questionTotal??m.length,header:g.header||"",question:g.question||"",multiSelect:!!g.multiSelect,chosen:k,rejected:A})}return await Y({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=new Set(Array.isArray(n?.[0])?n[0]:[]),d=[...u].map(m=>l.options?.[m]).filter(Boolean).map(m=>({label:m.label,description:m.description||""})),f=(l.options||[]).map((m,h)=>u.has(h)?null:m?.label).filter(Boolean),p=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:d,rejected:f,...p?{control:p}:{}},await Y({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),qf(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function vD(e,t){if(!t)return null;let n=await L(e).catch(()=>[]);return Xd(Zn(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function Am(e,{promptId:t,questionIndex:n}={}){let r=re(await z(),e);if(!r)throw new Error(`track "${e}" not found`);let o=r.boxes.find(l=>l.name===r.leadBox);if(!o)throw new Error(`track "${e}" has no lead box`);if(!await ae(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await _f(o);if(!a)throw new Error("no pending prompt to navigate");if(a.provider!=="claude")throw new Error("prompt navigation not supported for this provider");if(a.review)throw new Error("cannot navigate on the review screen \u2014 answer or cancel");let i=a.questionTotal??1;if(i<2)throw new Error("not a multi-question prompt");if(!Number.isInteger(n)||n<0||n>=i)throw new Error("questionIndex out of range");if(t&&t!==a.promptId)throw new Error("prompt changed since it was shown \u2014 reload and try again");let c=tk(a,n);return c.length?(await Ie(r.leadBox,c,{kind:"navigate-prompt"}),await Y({flow:"prompt",phase:"navigated",sessionName:r.leadBox,agent:o.agent,provider:a.provider,promptId:a.promptId,fromIndex:a.questionIndex??0,toIndex:n,mode:"keys",value:c.join(" "),count:c.length}).catch(()=>{}),{ok:!0,promptId:a.promptId,sent:c.length}):{ok:!0,promptId:a.promptId,sent:0}}function Hl(e){let t=ci.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},ci.set(e,t)),t}function co(e){return(ci.get(e)?.items??[]).map(t=>({...t}))}function gi(e){let t=ci.get(e);if(!t)return[];let n=t.items.splice(0);return t.idleSince=null,t.timer&&(clearInterval(t.timer),t.timer=null),n}async function Em(e){let t=gi(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=re(await z(),e);if(o)return r=Ts(o,n),await X(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function _m(e){let t=Hl(e);t.timer||(t.timer=setInterval(()=>AD(e).catch(()=>{}),TD))}async function AD(e){let t=ci.get(e);if(!(!t||t.flushing)){if(!t.items.length){t.timer&&(clearInterval(t.timer),t.timer=null);return}t.flushing=!0;try{let n=re(await z(),e);if(!n||n.archivedAt){gi(e);return}let r=Rs(e);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){ao(t,!1);return}let o=await ae(n.leadBox),s=n.boxes.find(c=>c.name===n.leadBox);if(o&&await Lm(o)&&(o=await pi(n,s,"queue")?await ae(n.leadBox):null,await X()),!o){ao(t,!1);return}if(Xr(s?.agent)){ao(t,!1);return}let a=await Te(n.leadBox,{lines:je}).catch(()=>"");if($c(s?.agent,a)){ao(t,!1);return}if(Jr(s?.agent,a)){let c=await pn(n.leadBox).catch(()=>a);if(ir(s,c)){ao(t,!1);return}}if(!ao(t,!0))return;let i=t.items.shift();t.idleSince=null;try{await wm(n,s,o,i.text,i.kind??null,i.attachments??null)}catch(c){if(c?.code!=="cli-not-running")throw c;t.items.unshift(i),await pi(n,s,"queue-gate")}await X()}finally{t.flushing=!1}}}async function sv(e,t,n,r){let o=Xr(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(Ts(e,[n]),await X()),kk(e.id,o))try{await _(e,{type:"usage.cap.hit",actor:"system",provider:t?.agent??null,boxId:e.leadBox,payload:{...o,blockedKind:r??null}})}catch{}let s=new Error(`${t?.agent??"the lead provider"} is paused \u2014 its ${o.window} spend limit is reached ($${o.spentUsd.toFixed(2)} of $${o.limitUsd.toFixed(2)})`);throw s.usageCap=o,s}async function ED(e,t){let n=await Te(e.leadBox,{lines:je}).catch(()=>"");if(!Jr(t?.agent,n))return!1;let r=await pn(e.leadBox).catch(()=>n);return!!ir(t,r)}async function wm(e,t,n,r,o=null,s=null){await sv(e,t,r,o);let a=li.get(e.id)||0,i=r,c=!1,l=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(i=ev(r,e.pendingHandoffHydrate.artifact||io),delete e.pendingHandoffHydrate,c=!0);let u=Array.isArray(s)?s.filter(f=>f&&f.rel):[];if(u.length){let f=BS(u.map(p=>p.rel));i=i.trim()?`${i.trim()}
|
|
162
165
|
|
|
163
|
-
${
|
|
166
|
+
${f}`:f}let d=he(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,...o?{kind:o}:{},...u.length?{attachments:u}:{}});o||vs(e,null);try{if(await Xn({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:Is(n.agent),bailOnDialog:!0}),(li.get(e.id)||0)!==a){!o&&r.trim()&&(Ts(e,[r]),await X());let f=new Error("delivery stopped \u2014 the message was handed back as the draft");throw f.interrupted=!0,f}if(await ED(e,t)){let f=Hl(e.id),p={id:_s(),text:r,ts:Date.now(),...o?{kind:o}:{},...u.length?{attachments:u}:{}};f.items.unshift(p),_m(e.id);let m=new Error("lead is blocked on a prompt \u2014 message queued");throw m.requeued={id:p.id,position:f.items.length},m}await Le(e.leadBox,i,{...zt(t?.agent,i),kind:c?"lead-handoff-first-prompt":"say"})}catch(f){if(l&&!e.pendingHandoffHydrate&&(e.pendingHandoffHydrate=l),he(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:d.at,text:r,...f?.interrupted?{reason:"interrupted"}:{},...f?.requeued?{reason:"requeued"}:{}}),f?.requeued)return{queued:!0,...f.requeued};throw f}await FD(e,t,i)||(await iv(e.leadBox,t?.agent,i.length+8,i).catch(()=>{}),he(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:d.at,text:r,reason:"unsubmitted"}),o||Ts(e,[r])),c&&await Y({flow:"lead-handoff",phase:"first-prompt",trackId:e.id,sessionName:e.leadBox,agent:t?.agent,userTextLen:r.length})}function Im(e,{actions:t}={}){Hl(e).items.push({id:_s(),text:Xk(t),ts:Date.now(),kind:"ship-notify"}),_m(e)}async function It(e,t,{kind:n=null,attachments:r=null}={}){let o=Array.isArray(r)?r.filter(p=>p&&typeof p.rel=="string"):[];if((typeof t!="string"||!zo(t))&&!o.length)throw new Error("text (string) or an attachment required");let s=re(await z(),e);if(!s)throw new Error(`track "${e}" not found`);if(s.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before driving it`);zm(s);let a=s.boxes.find(p=>p.name===s.leadBox),i=zo(t);await sv(s,a,i,n),yi(e,"say");let c=await ae(s.leadBox);if(c&&await Lm(c)?await pi(s,a,"say")?(await X(),c=await ae(s.leadBox)):c=null:!c&&await ki(s)&&(await X(),c=await ae(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=Hl(e),u=await Te(s.leadBox,{lines:je}).catch(()=>""),d=l.items.length>0||$c(a?.agent,u);if(!d&&Jr(a?.agent,u)){let p=await pn(s.leadBox).catch(()=>u);d=!!ir(a,p)}if(d){let p={id:_s(),text:i,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(p),_m(e),n||vs(s,null),await X(),{ok:!0,leadBox:s.leadBox,queued:!0,id:p.id,position:l.items.length}}let f;try{f=await wm(s,a,c,i,n,o)}catch(p){if(p?.code!=="cli-not-running"||!await pi(s,a,"say-gate")||(await X(),c=await ae(s.leadBox),!c))throw p;f=await wm(s,a,c,i,n,o)}return await X(),f?.queued?{ok:!0,leadBox:s.leadBox,queued:!0,id:f.id,position:f.position}:{ok:!0,leadBox:s.leadBox}}async function Rm(e,{hint:t}={}){let n=re(await z(),e);if(!n)throw new Error(`track "${e}" not found`);if(n.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it first`);let r=$p(n.id);if(r&&!r.finishedAt)throw new Error("a view is already being spun up for this Track");let o=n.boxes.find(i=>i.name===n.leadBox),s=String(t||"").trim()?String(t).trim().slice(0,40):"preview";Ex(n.id,{name:s,box:n.leadBox});let a=String(t||"").trim()?`${GS}
|
|
164
167
|
|
|
165
|
-
What to preview: ${String(t).trim()}`:
|
|
168
|
+
What to preview: ${String(t).trim()}`:GS;try{await It(e,a,{kind:"spin-up-view"})}catch(i){throw Tl(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function Wl(e){let t=re(await z(),e);if(!t)throw new Error(`track "${e}" not found`);if(t.archivedAt)throw new Error(`track "${e}" is archived`);li.set(e,(li.get(e)||0)+1);let n=gi(e),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=Ss.get(e);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=Ts(t,r),await X()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:t.draftRev||0}}let s=CD(e,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,lo(_D)]);let a=re(await z(),e)||t;return{ok:!0,interrupted:s.result!=="no-session",stopped:s.result==="stopped"||s.result==="stopped-late",stopping:!s.result,dropped:n.length,restored:s.restored,draftRev:a.draftRev||0}}function av(e){let t=Ss.get(e);return!!t&&!t.canceled}function yi(e,t){let n=Ss.get(e);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=t||null)}function $m(e){yi(e,"track-ended"),li.delete(e),gi(e)}function CD(e,{droppedCount:t,droppedTexts:n}){let r={startedAt:Date.now(),clicks:1,escSends:0,bgSends:0,queuedTexts:[...n],canceled:!1,cancelReason:null,result:null,restored:null},o;return r.settled=new Promise(s=>{o=s}),Ss.set(e,r),(async()=>{let s=re(await z(),e),a=s?.boxes.find(g=>g.name===s.leadBox),i=s?await ae(s.leadBox):null,c=!1,l=!1;if(i){e:for(let g=0;g<ID&&!r.canceled;g++){await Ie(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let x of RD){await lo(x);let k=await Te(s.leadBox,{lines:je}).catch(()=>"");if(!Fo(a?.agent,k)){c=!0;break e}}}if(!c){l=!0;for(let g=0;g<OD&&!r.canceled&&(await lo($D),!r.canceled);g++){let x=await Te(s.leadBox,{lines:je}).catch(()=>"");if(!Fo(a?.agent,x)){c=!0;break}await Ie(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=re(await z(),e)||s,d=u?.boxes.find(g=>g.name===u.leadBox)||a,f=null,p=null;u&&d&&i&&c&&!r.canceled&&(f=await DD(u,d).catch(()=>null),f||(p=await LD(u,d).catch(()=>null)));let m=u&&i&&!r.canceled?await ND(u).catch(()=>null):null,h=[f||p,...r.queuedTexts].filter(Boolean);u&&h.length&&(r.restored=Ts(u,h),await X()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await MD(u||s,d,{ts:r.startedAt,result:r.result,dropped:t,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!f,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!m,durationMs:Date.now()-r.startedAt,...r.cancelReason?{cancelReason:r.cancelReason}:{}}),await Y({flow:"interrupt",phase:r.result==="gave-up"?"failed":"done",trackId:e,sessionName:u?.leadBox,agent:d?.agent,result:r.result,escSends:r.escSends,bgSends:r.bgSends,clicks:r.clicks,unsent:!!f,lostRestored:!!p,queuedRestored:r.queuedTexts.length,shipDismissed:!!m}).catch(()=>{})})().catch(()=>{}).finally(()=>{r.result||(r.result=r.canceled?"canceled":"gave-up"),Ss.get(e)===r&&Ss.delete(e),o()}),r}async function MD(e,t,n){await _(e,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:e.leadBox,provider:t?.agent||null,payload:n}).catch(()=>{})}async function ND(e){let t=Il(await L(e));if(!t?.offerId)return null;let n=t.offerType==="exists";return await _(e,{type:n?"pr.exists.answered":"pr.offer.answered",actor:"user",source:"orchestrator",boxId:t.box,payload:{ts:Date.now(),offerId:t.offerId,...n?{choice:"dismiss"}:{action:"dismiss",actions:null},reason:"interrupt"}}),t.offerId}async function DD(e,t){if(t?.agent!=="claude")return null;let n=[...e.events||[]].reverse().find(r=>r.type==="said"&&r.text);if(!n)return null;for(let r of PD){await lo(r);let o=await Te(e.leadBox,{lines:je}).catch(()=>""),s=va(t.agent,o);if(s&&Oc(n.text,s))return await iv(e.leadBox,t.agent,n.text.length+8),he(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:n.at,text:n.text}),n.text}return null}async function LD(e,t){if(t?.agent!=="claude")return null;let n=e.events||[],r=[...n].reverse().find(s=>s.type==="said"&&s.text);if(!r||r.kind||Date.now()-r.at>BD||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!Oe(r.text))return null;for(let s of jD){await lo(s);let a;try{a=await kf(t,{})}catch{return null}if(fy(a,r))return null;let i=await Te(e.leadBox,{lines:je}).catch(()=>"");if(va(t.agent,i))return null}return he(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}async function FD(e,t,n){return Aa({sessionName:e.leadBox,agent:t?.agent,agentCmd:Is(t?.agent),text:n,trackId:e.id,flow:"say"})}async function iv(e,t,n,r=null){for(let o=0;o<3;o++){await Mo(e,n,{kind:"unsend-wipe"}).catch(()=>{}),await lo(200);let s=await Te(e,{lines:je}).catch(()=>""),a=va(t,s);if(!a||r&&!Oc(r,a)&&!(t==="codex"&&Ld(r,a)))return!0;n=a.length+8}return!1}function vs(e,t,{source:n}={}){e.draftRev=(e.draftRev||0)+1;let r=typeof t=="string"?t:"";return r.trim()?e.draft={text:r,updatedAt:Date.now(),...n?{source:n}:{}}:delete e.draft,e.draft||null}function Ts(e,t){let n=[],r=o=>n.some(s=>Oe(s).includes(Oe(o)));for(let o of[e.draft?.text,...t])o&&o.trim()&&!r(o)&&n.push(o);return n.length?vs(e,n.join(`
|
|
166
169
|
|
|
167
|
-
`),{source:"restore"})?.text??null:null}function
|
|
170
|
+
`),{source:"restore"})?.text??null:null}function HD(e,t){let n=Oe(t);if(!n)return!1;if(co(e.id).some(o=>Oe(o.text)===n))return!0;let r=e.events||[];for(let o=r.length-1;o>=0;o--){let s=r[o];if(typeof s.at=="number"&&Date.now()-s.at>UD)break;if(!(s.type!=="said"||!s.text||Oe(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function Om(e,t,{rev:n=null}={}){if(typeof t!="string")throw new Error("text (string) required");let r=re(await z(),e);if(!r)throw new Error(`track "${e}" not found`);let o=t,s=zo(t);if(typeof n=="number"&&n<(r.draftRev||0)&&s&&HD(r,s))return{ok:!0,draft:r.draft||null,rev:null,ignored:"already-sent"};if(typeof n=="number"&&n<(r.draftRev||0)&&!o.trim()&&r.draft?.text)return{ok:!0,draft:r.draft,rev:r.draftRev||0,ignored:"stale-clear"};if(typeof n=="number"&&n<(r.draftRev||0)&&r.draft?.source==="restore"){let i=r.draft.text;Oe(o).includes(Oe(i))?vs(r,o):vs(r,`${i}
|
|
168
171
|
|
|
169
|
-
${o}`,{source:"restore"})}else
|
|
170
|
-
`);return`${
|
|
172
|
+
${o}`,{source:"restore"})}else vs(r,o);return await X(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Cm(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=re(await z(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await ae(r.leadBox);if(!o)throw new Error(`lead box "${r.leadBox}" is not running`);let a=r.boxes.find(d=>d.name===r.leadBox)?.agent||o.agent||"claude",i=Ok(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await ma(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await Xn({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:Is(o.agent)});let u=await Ck(a,r.leadBox,i);return he(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 cv(e){for(let t of e){if(!t||!await Jo(t))continue;let n=await ck(t).catch(()=>null);if(!n)continue;if((await W.stat(G.join(n,".git")).catch(()=>null))?.isDirectory())return n}return null}function gt(e){let t=e;return t?t.kind==="multi"?Array.isArray(t.repos)?t.repos.filter(Boolean):[]:t.base?[{name:G.basename(t.base),dir:t.dir??null,base:t.base,baseRef:t.baseRef??null,repo:t.repo??null}]:[]:[]}async function ps(e){let t=[];for(let n of gt(e?.workspace)){if(!n?.dir)continue;let r=await Ma(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await Na(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function lv(e){let t=e;typeof t=="string"&&!G.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await So(t)??await ec(t));let n=await cv([t]);if(!n)throw new Error(`tagged repo "${e}" is not a usable base (need a primary checkout or a synced owner/name)`);return n}async function WD(e,t,n,r){let o=`track/${Zm(t)}-${e}`,s=[];try{await W.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await lv(i);if(s.some(p=>p.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await me(c).catch(()=>null),u=G.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let p=2;a.has(u);p++)u=`${G.basename(c)}-${p}`;a.add(u);let d=G.join(n,u),f=await Zo(c);await Xo(c,d,o,f),s.push({name:u,dir:d,base:c,baseRef:f,repo:l})}return await bi(n,s,o),{dir:n,kind:"multi",branch:o,repos:s}}catch(a){for(let i of s)await pt(i.base,i.dir).catch(()=>{});throw await W.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function Mm(e,t){let n=await z(),r=re(n,e);if(!r)throw new Error(`track "${e}" not found`);if(r.archivedAt)throw new Error("track is archived \u2014 unarchive it first");let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track \u2014 a single-repo Track's workspace IS its repo checkout; create the Track with multiple repos tagged instead");let s=await lv(typeof t=="string"?t.trim():t);if((o.repos??[]).some(f=>f?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await me(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(f=>f?.name).filter(Boolean)]),c=G.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let f=2;i.has(c);f++)c=`${G.basename(s)}-${f}`;let l=G.join(o.dir,c),u=await Zo(s);await lk(s,o.branch)?await Gc(s,l,o.branch):await Xo(s,l,o.branch,u);let d={name:c,dir:l,base:s,baseRef:u,repo:a};return o.repos=[...(o.repos??[]).filter(Boolean),d],await bi(o.dir,o.repos,o.branch),he(r,"retagged",{action:"add",repo:a??s,name:c,workspace:uv(o)}),await X(),d}async function Nm(e,t){let n=await z(),r=re(n,e);if(!r)throw new Error(`track "${e}" not found`);let o=r.workspace;if(o?.kind!=="multi")throw new Error("live re-tagging needs a multi-repo Track");let s=(o.repos??[]).find(c=>c&&(c.name===t||c.repo===t||c.base===t));if(!s)throw new Error(`repo "${t}" is not tagged on this Track`);if((o.repos??[]).filter(Boolean).length<=1)throw new Error("can't untag the last repo \u2014 archive or delete the Track instead");for(let c of wt.values())if(c.trackId===e&&!c.finishedAt)throw new Error("a subtask is still running on this Track \u2014 wait for it to merge back before untagging");let a=await Ma(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await Na(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 pt(s.base,s.dir).catch(()=>{}),await W.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await bi(o.dir,o.repos,o.branch),he(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:uv(o)}),await X(),{removed:s.name}}async function qD(e,t,{repo:n,repos:r,cwd:o}={}){await W.mkdir(ai,{recursive:!0});let s=G.join(ai,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return WD(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!G.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await So(n)??await ec(n));let i=await cv([n,o,lD]);if(i){let c=`track/${Zm(t)}-${e}`;try{let l=await Zo(i);await Xo(i,s,c,l),await di(s);let u=await me(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await W.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await W.mkdir(s,{recursive:!0}),await Nf(s),await di(s),{dir:s,base:null,branch:null,kind:"repo"}}async function di(e){try{await W.writeFile(G.join(e,"AGENTS.md"),si,{flag:"wx"})}catch{return}await zD(e,["/AGENTS.md"])}function VD(e,t){let n=e.map(r=>`- \`${r.name}/\`${r.repo?` \u2014 github.com/${r.repo}`:""}${r.baseRef?` (forked from ${r.baseRef})`:""}`).join(`
|
|
173
|
+
`);return`${GD}
|
|
171
174
|
|
|
172
175
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
173
176
|
git worktree per tagged repo, ALL on the same branch \`${t}\`:
|
|
@@ -179,20 +182,20 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
179
182
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
180
183
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
181
184
|
or preview an app, run it from that app's own repo directory.
|
|
182
|
-
`}async function
|
|
185
|
+
`}async function bi(e,t,n){let r=VD(t,n);for(let o of["CLAUDE.md","AGENTS.md"])await W.writeFile(G.join(e,o),r).catch(()=>{})}async function fv(e){let{stdout:t}=await YS("git",["-C",e,"rev-parse","--git-path","info/exclude"]);return G.resolve(e,t.trim())}async function zD(e,t){try{let n=await fv(e);await W.mkdir(G.dirname(n),{recursive:!0});let r=await W.readFile(n,"utf8").catch(()=>""),o=t.filter(s=>!r.split(`
|
|
183
186
|
`).includes(s));o.length&&await W.appendFile(n,(r.endsWith(`
|
|
184
187
|
`)||!r?"":`
|
|
185
188
|
`)+o.join(`
|
|
186
189
|
`)+`
|
|
187
|
-
`)}catch{}}async function
|
|
190
|
+
`)}catch{}}async function JD(e,t){try{let n=await fv(e),r=await W.readFile(n,"utf8"),o=r.split(`
|
|
188
191
|
`).filter(s=>s!==t);o.length!==r.split(`
|
|
189
192
|
`).length&&await W.writeFile(n,o.join(`
|
|
190
|
-
`))}catch{}}async function
|
|
193
|
+
`))}catch{}}async function Pm(){let e={refreshed:0,removed:0,codexGlobal:null};try{await W.mkdir(sn,{recursive:!0}),await W.writeFile(G.join(sn,"CLAUDE.md"),KD).catch(()=>{}),await W.writeFile(G.join(sn,"AGENTS.md"),si).catch(()=>{});let t=process.env.CODEX_HOME||G.join(process.env.HOME||"/tmp",".codex");if((await W.stat(t).catch(()=>null))?.isDirectory()){let r=G.join(t,"AGENTS.md"),o=await W.readFile(r,"utf8").catch(()=>null);o===null?(await W.writeFile(r,um,{flag:"wx"}).catch(()=>{}),e.codexGlobal="created"):lm(o)&&o!==um&&(await W.writeFile(r,um).catch(()=>{}),e.codexGlobal="refreshed")}for(let r of[ai,Es]){let o=await W.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=G.join(r,s.name),i=G.join(a,"AGENTS.md"),c=await W.readFile(i,"utf8").catch(()=>null);c!==null&&lm(c)&&c!==si&&(await W.writeFile(i,si).catch(()=>{}),e.refreshed++);let l=G.join(a,"CLAUDE.md"),u=await W.readFile(l,"utf8").catch(()=>null);u!==null&&lm(u)&&(await W.rm(l,{force:!0}).catch(()=>{}),await JD(a,"/CLAUDE.md"),e.removed++)}}}catch{}return e}async function pv(e){let t=e.workspace;if(!(!t||!t.dir))try{if(t.kind==="multi"){for(let n of gt(t))n.base&&n.dir&&await pt(n.base,n.dir).catch(()=>{});await W.rm(t.dir,{recursive:!0,force:!0})}else t.kind==="worktree"&&t.base?await pt(t.base,t.dir):await W.rm(t.dir,{recursive:!0,force:!0})}catch{}}async function ql({name:e,cwd:t,repo:n,repos:r,agent:o="claude",skipPermissions:s=!0}){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");let a=await wi(o,s),i=await z(),c=_s().slice(0,8),l=await qD(c,e,{repo:n,repos:r,cwd:t}),u=new Set(i.tracks.flatMap(m=>(m.boxes||[]).map(h=>h.name))),d=`${e.trim()} lead`;u.has(bc(d))&&(d=`${e.trim().slice(0,50).trim()} ${c} lead`);let f=await Jn(d,l.dir,{agentCmd:a,agent:o,freshWorkspace:!0});ka({cwd:f.cwd,sessionName:f.name,agent:o,agentCmd:a});let p={id:c,name:e.trim(),cwd:f.cwd,workspace:l,leadBox:f.name,boxes:[qm(f,"lead","live")],events:[],createdAt:Date.now()};return he(p,"opened",{agent:o,box:f.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:h,baseRef:g,repo:x})=>({name:m,base:h,baseRef:g,repo:x}))}:{}}}),i.tracks.push(p),await X(),_t(p)}async function Dm({provider:e="claude",fresh:t=!1}={}){if(!YD.has(e))throw new Error(`unknown setup provider "${e}" (claude | codex | cursor)`);let r=(await z()).tracks.find(u=>u.kind==="setup"&&u.setup?.provider===e&&!u.archivedAt);if(r){if(!t){let u=await _t(r);if(u.boxes.find(f=>f.name===u.leadBox)?.state==="live")return u}await uo(r.id).catch(()=>{})}let o=(await wi(e,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=_s().slice(0,8),a=G.join(sn,"setup",s);await W.mkdir(a,{recursive:!0});let i=await Jn(`Connect ${e} ${s}`,a,{agentCmd:o,agent:e,firstRun:!0});e==="codex"&&ka({cwd:i.cwd,sessionName:i.name,agent:e,agentCmd:o});let c={id:s,name:`Connect ${e}`,kind:"setup",setup:{provider:e,startedAt:Date.now(),connectedAt:null},cwd:i.cwd,workspace:{dir:a,base:null,branch:null,kind:"scratch"},leadBox:i.name,boxes:[qm(i,"lead","live")],events:[],createdAt:Date.now()};return he(c,"opened",{agent:e,box:i.name,skipPermissions:!1,trackKind:"setup",name:c.name,cwd:c.cwd,workspace:{base:null,branch:null,kind:"scratch"}}),(await z()).tracks.push(c),await X(),_t(c)}async function Bm(e,t,n){await ed(t.cwd).catch(()=>{}),await Mx(e).catch(()=>{});let r=As.get(e);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),As.delete(e),t.handoff&&!t.handoff.finishedAt&&(t.handoff.step="error",t.handoff.error=n,t.handoff.finishedAt=Date.now());for(let a of fo(e))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await th(a).catch(()=>{}),wt.delete(a.id);for(let a of Object.values(t.subtasks||{}))a.finishedAt||(a.step="error",a.error=n,a.canceled=!0,a.finishedAt=Date.now());let o=await z(),s=new Set((o.tracks||[]).filter(a=>a.id!==e&&!a.archivedAt).flatMap(a=>(a.boxes||[]).map(i=>i.name)));for(let a of t.boxes)s.has(a.name)||await Mt(a.name).catch(()=>{})}async function jm(e,{cascadeMocks:t=!0,cascadeRunbooks:n=!1}={}){let r=await z(),o=r.tracks.findIndex(a=>a.id===e);if(o===-1)throw new Error(`track "${e}" not found`);let s=r.tracks[o];return t||await tS(s),$m(e),await Bm(e,s,"track deleted"),n?await CS(e).catch(()=>{}):await OS(e).catch(()=>{}),await Pk(),await pv(s),t&&await nS(e).catch(()=>{}),t?await bS(e).catch(()=>{}):await yS(s).catch(()=>{}),he(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await X(),{ok:!0,id:e,name:s.name,boxes:s.boxes.length}}async function uo(e){let t=await z(),n=re(t,e);if(!n)throw new Error(`track "${e}" not found`);if(n.archivedAt)throw new Error(`track "${e}" is already archived`);return n.archivedAt=Date.now(),$m(e),he(n,"archived",{boxes:n.boxes.length}),await X(),n.kind==="setup"?(await mv(e),_t(n)):(fi.add(e),QD(e).catch(()=>{}).finally(()=>fi.delete(e)),{...await _t(n),archiving:!0})}async function QD(e){try{await eB(e)}catch{}await mv(e).catch(()=>{})}async function mv(e){let t=await z(),n=re(t,e);if(!(!n||!n.archivedAt)){await Bm(e,n,"track archived");for(let r of n.boxes)Nn.has(r.state)||(r.state="stopped");await X()}}async function eB(e){let t=await z(),n=re(t,e);if(!n||!n.archivedAt)return;let r=null,o=[],s="derived",a=(n.events||[]).some(l=>l.type==="said"),i=n.leadBox?await ae(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await sb(n.leadBox,i.cwd,{timeoutMs:ZD})),s="lead-cli"}catch{}if(r||(r=kb(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 _(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 xn(e,t){let n=await qe(e,t).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await hv(n);if(r.length){for(let o of r)await Zr(o.repo,o.number,!0).catch(()=>{});return!1}try{return await uo(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function hv(e){return(await Qr().catch(()=>[])).filter(n=>n.trackId===e.trackId&&n.state!=="merged"&&n.state!=="closed"&&n.state!=="lost"&&!(n.repo===e.repo&&n.number===e.number))}async function Sn(e,t){return Kl(e,t,"merged")}async function Kl(e,t,n="merged"){let r=await qe(e,t).catch(()=>null);if(!r?.notifyOnMerge||r.archiveOnMerge||!r.trackId)return!1;let o={repo:r.repo??null,number:r.number,title:r.title??null,url:r.url??null,headRef:r.headRef??null,outcome:n},s=await hv(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await i0(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await It(r.trackId,wv(a),{kind:"merge-notify"}),!0}catch{return!1}}function wv(e){let t=i=>`${i.repo?`${i.repo}`:""}#${i.number}${i.title?` "${i.title}"`:""}${i.url?` (${i.url})`:""}`;if(e.length===1){let i=e[0],c=`#${i.number}${i.title?` "${i.title}"`:""}`;return i.outcome!=="merged"?`PR ${c}${i.url?` (${i.url})`:""}${i.headRef?` \u2014 branch ${i.headRef} \u2014`:""} was ${VS[i.outcome]??i.outcome}. This is an automated notification: the work did NOT land \u2014 check whether it still needs to ship another way. If not, say so briefly.`:`PR ${c}${i.url?` (${i.url})`:""}${i.headRef?` \u2014 branch ${i.headRef} \u2014`:""} has merged. This is an automated merge notification: pick up whatever follow-up work this merge unblocks (dependent tasks, cleanup, next steps that were waiting on it). If nothing needs doing, say so briefly.`}let n=e.filter(i=>i.outcome==="merged"),r=e.filter(i=>i.outcome!=="merged"),o=r.length?`${n.length} of the ${e.length} PRs on this Track merged; ${r.length} did not land:`:`All ${e.length} PRs on this Track have merged:`,s=e.map(i=>`- ${t(i)} \u2014 ${VS[i.outcome]??i.outcome}`).join(`
|
|
191
194
|
`),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}
|
|
192
195
|
${s}
|
|
193
|
-
${a}`}async function Im(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<oa()?!1:dn(e.name)}async function si(e,t,n){return!t||e.archivedAt||Mn.has(t.state)?!1:(await Y({flow:"rehydrate",phase:"resume-dead-cli",trackId:e.id,sessionName:t.name,agent:t.agent,via:n}).catch(()=>{}),console.warn(`[track] ${t.name}: CLI is not running (bare shell) \u2014 relaunching (${n})`),await Ct(t.name).catch(()=>{}),t.state="stopped",pi(e))}async function pi(e){let t=e.boxes.find(o=>o.name===e.leadBox);if(!t)return!1;let n=tm.get(t.name);if(n)return n;let r=$D(e,t).finally(()=>tm.delete(t.name));return tm.set(t.name,r),r}async function $D(e,t){if(e.archivedAt||e.transferredTo||Mn.has(t.state)||await se(t.name))return!1;let n=um(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),r=await dc(n);if(r===_o)return he(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;r===Io&&console.warn(`[track] ${t.name}: could not confirm "${t.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);try{let o=await zn(t.name,t.cwd,{agentCmd:n,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=js(o.hookToken),ma({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:n}),t.state="live",he(e,"rehydrated",{box:t.name,agent:t.agent}),!0}catch(o){return he(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(o?.message||o)}),!1}}async function Rm(e){let t=await z(),n=re(t,e);if(!n)throw new Error(`track "${e}" not found`);if(!n.archivedAt)throw new Error(`track "${e}" is not archived`);return delete n.archivedAt,oi.delete(e),he(n,"unarchived",{}),await pi(n),await X(),_t(n)}async function $m(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>im)throw new Error(`name too long (max ${im} chars)`);let r=await z(),o=re(r,e);if(!o)throw new Error(`track "${e}" not found`);if(String(o.name||"").startsWith("\u21BB"))throw new Error("loop run Tracks are named by their loop \u2014 rename the loop on the Loops tab");if(n===o.name)return _t(o);let s=o.name;return o.name=n,he(o,"renamed",{from:s,to:n}),await X(),_t(o)}async function Om(){let e=await z(),t=new Set((await Ot()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!s.archivedAt).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(s.archivedAt)for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Ct(a.name).catch(()=>{}),he(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!Mn.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await X(),r}async function jl(e){let t=await z(),n=re(t,e);if(!n)throw new Error(`track "${e}" not found`);if(n.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before restarting the lead`);Bm(n);let r=n.boxes.find(a=>a.name===n.leadBox);if(!r)throw new Error(`track "${e}" has no lead box`);if(Mn.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await se(n.leadBox)){let a=await Ae(n.leadBox,{lines:Le}).catch(()=>"");if(Sc(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Ct(n.leadBox),r.state="stopped"}let s=await pi(n);if(await X(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return _t(n)}async function Cm(e,t,n){let r=await ne(e);if(!r)throw new Error(`track "${e}" not found`);if(!t)throw new Error("unknown offer");let o=await L(r).catch(()=>[]);if(!o.some(i=>i.type==="box.fullscreen.detected"&&i.payload?.offerId===t))throw new Error("unknown offer");if(o.some(i=>i.type==="box.fullscreen.answered"&&i.payload?.offerId===t))throw new Error("offer already answered");return n&&await jl(e),await _(r,{type:"box.fullscreen.answered",actor:"user",source:"orchestrator",boxId:r.leadBox,payload:{ts:Date.now(),offerId:t,action:n?"restarted":"dismiss"}}).catch(()=>{}),{ok:!0,action:n?"restarted":"dismiss"}}function Ll(e){return(!Array.isArray(e.epochs)||e.epochs.length===0)&&(e.epochs=[{agent:e.agent??null,sessionId:e.sessionId??null,pinned:e.pinned===!0,startedAt:e.createdAt??null,endedAt:null}]),e.epochs}async function Rl(e,t){let n=await z(),r=re(n,e);r&&(r.handoff={...r.handoff||{},...t},await X())}function nv(e,t=Date.now()){let n=e?.handoff;if(!n||n.finishedAt)return null;let r=n.step,o=!!n.toBox&&n.toBox===n.fromBox,s=e.boxes?.find?.(i=>i.name===e.leadBox)||null;return(o?e.leadBox===n.fromBox&&!!n.startedAt&&Array.isArray(s?.epochs)&&s.epochs.some(i=>i.startedAt!=null&&i.startedAt>=n.startedAt):!!n.toBox&&e.leadBox===n.toBox)?{...n,step:"done",finishedAt:t,recovered:`committed before restart (was "${r}")`}:{...n,step:"error",finishedAt:t,error:`interrupted by an agent restart during "${r}"; lead "${e.leadBox}" retained \u2014 retry the handoff`,recovered:"rolled-back"}}async function Nm(){let e=await z(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=nv(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await se(r.fromBox).catch(()=>null),l=n.boxes.find(u=>u.name===r.fromBox);if(c&&l&&c.agent===r.toAgent&&r.toAgent!==l.agent){let u=Date.now(),f=Ll(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:so,at:u},he(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,toAgent:r.toAgent,fromSessionId:m,toSessionId:c.sessionId||null,epoch:f.length-1,singleBox:!0,skipPermissions:r.skipPermissions,reason:r.reason??null,handoffStartedAt:r.startedAt,recovered:!0}),n.handoff={...r,step:"done",finishedAt:u,recovered:"swap adopted from live tmux tags after restart"},await Y({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 Y({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Ct(i).catch(()=>{}),a&&await Ct(a).catch(()=>{})}return t&&await X(),t}async function Fl(e,t,n=!0,r=null){let o=ys.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=re(await z(),e);if(!s)throw new Error(`track "${e}" not found`);di(e,"handoff");let a=await li(t,n),i=Jr(t);if(i){let u=new Error(`can't hand off to ${t} \u2014 its ${i.window} spend limit is reached ($${i.spentUsd.toFixed(2)} of $${i.limitUsd.toFixed(2)})`);throw u.usageCap=i,u}let c=await se(s.leadBox);if(!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l={trackId:e,step:"checkpoint",fromBox:s.leadBox,toAgent:t,skipPermissions:n,reason:FS(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return ys.set(e,l),await Rl(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),OD(l,s,c,a,t).catch(async u=>{l.error=u?.message||String(u),l.finishedAt=Date.now(),console.error(`[lead-handoff ${e}] FAILED at step "${l.step}": ${l.error}`),await Rl(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),Y({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),Y({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function OD(e,t,n,r,o){let s=async(M,N={})=>{e.step=M,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${M}`),await Rl(e.trackId,{step:M,toBox:e.toBox}),await Y({flow:"lead-handoff",phase:M,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:N.sessionName,...N})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await z(),i=re(a,e.trackId)?.boxes.find(M=>M.name===e.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:e.fromBox}),e.checkpoint="llm",Jr(c))e.checkpoint="skipped-capped";else try{await Gd(e.fromBox,{timeoutMs:jS})}catch(M){e.checkpoint="reconstructed",await Y({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:M?.message||String(M)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await Vd(e.trackId,n.cwd,{trackName:t.name||t.id}).catch(()=>null),await s("checkpoint-degraded",{sessionName:e.fromBox,checkpoint:e.checkpoint,statusArtifact:e.statusArtifact})),await s("swap",{cwd:n.cwd,agentCmd:r});let d=await Qd(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let p=Jg(d.launchCmd);await Xn({cwd:n.cwd,sessionId:d.sessionId,sessionName:e.fromBox,agent:o,agentCmd:ci(o),launchEcho:p}).catch(M=>{if(M?.code!=="cli-not-running")throw M});let m=Id(await Ae(e.fromBox,{lines:Le}).catch(()=>""),p);if(!vc(o,m)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let M=um(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await Qd(e.fromBox,{agentCmd:M,agent:c,resumeSessionId:l,pinned:u});let N=await z(),C=re(N,e.trackId);if(C){let T=C.boxes.find(j=>j.name===e.fromBox);T&&(T.state="live"),he(C,"lead-handoff-failed",{box:e.fromBox,fromAgent:c,toAgent:o,reason:e.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:e.startedAt}),await X()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let h=await z(),g=re(h,e.trackId);if(!g)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let x=g.boxes.find(M=>M.name===e.fromBox);if(!x)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let k=Date.now(),E=Ll(x),I=E[E.length-1];I.endedAt=k,e.reason&&(I.endReason=e.reason),E.push({agent:o,sessionId:d.sessionId,pinned:d.pinned,startedAt:k,endedAt:null}),x.agent=o,x.sessionId=d.sessionId,x.pinned=d.pinned,x.state="live",x.role="lead",g.pendingHandoffHydrate={artifact:e.statusArtifact||so,at:k},he(g,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:d.sessionId,epoch:E.length-1,singleBox:!0,forced:d.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await X(),await s("done",{sessionName:e.fromBox}),await Rl(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function Dm(e){let t=lm.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await z(),r=re(n,e);if(!r)throw new Error(`track "${e}" not found`);if(r.archivedAt)throw new Error("track is archived \u2014 restore it before moving it");if(r.kind==="setup")throw new Error("setup boxes don't move");if(r.transferredTo)throw new Error(`track already transferred to ${r.transferredTo}`);let o=ys.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of wt.values())if(a.trackId===e&&!a.finishedAt)throw new Error("a subtask is still running on this Track \u2014 wait for it to merge back before moving");let s={trackId:e,step:"checkpoint",startedAt:Date.now(),finishedAt:null,error:null};return lm.set(e,s),CD(s,r).catch(a=>{s.error=a?.message||String(a),s.finishedAt=Date.now(),console.error(`[transfer-out ${e}] FAILED at step "${s.step}": ${s.error}`)}),s}async function CD(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await se(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&Jr(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await Gd(t.leadBox,{timeoutMs:jS})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await Vd(t.id,t.cwd,{trackName:t.name||t.id}).catch(()=>null),e.step="push";let o=t.workspace?.branch??null,s=[];if(o)for(let u of gt(t.workspace)){if(!u?.dir)continue;let f={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await Cn(u.dir,["push","origin",o],cm);let{stdout:d}=await Cn(u.dir,["rev-parse","HEAD"]);f.baseSha=d.trim(),f.pushed=!0}catch(d){f.error=String(d?.message||d).split(`
|
|
194
|
-
`)[0]}s.push(
|
|
195
|
-
`)[0]}else l?.sha&&(T="wip snapshots cover single-repo worktrees only");await
|
|
196
|
+
${a}`}async function Lm(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<fa()?!1:fn(e.name)}async function pi(e,t,n){return!t||e.archivedAt||Nn.has(t.state)?!1:(await Y({flow:"rehydrate",phase:"resume-dead-cli",trackId:e.id,sessionName:t.name,agent:t.agent,via:n}).catch(()=>{}),console.warn(`[track] ${t.name}: CLI is not running (bare shell) \u2014 relaunching (${n})`),await Mt(t.name).catch(()=>{}),t.state="stopped",ki(e))}async function ki(e){let t=e.boxes.find(o=>o.name===e.leadBox);if(!t)return!1;let n=dm.get(t.name);if(n)return n;let r=tB(e,t).finally(()=>dm.delete(t.name));return dm.set(t.name,r),r}async function tB(e,t){if(e.archivedAt||e.transferredTo||Nn.has(t.state)||await ae(t.name))return!1;let n=km(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),r=await yc(n);if(r===$o)return he(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;r===Oo&&console.warn(`[track] ${t.name}: could not confirm "${t.agent}" on PATH \u2014 relaunching anyway (launch verifier will catch a real miss)`);try{let o=await Jn(t.name,t.cwd,{agentCmd:n,agent:t.agent,resumeSessionId:t.sessionId,pinned:t.pinned===!0});return t.hookTokenHash=Gs(o.hookToken),ka({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:n}),t.state="live",he(e,"rehydrated",{box:t.name,agent:t.agent}),!0}catch(o){return he(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(o?.message||o)}),!1}}async function Fm(e){let t=await z(),n=re(t,e);if(!n)throw new Error(`track "${e}" not found`);if(!n.archivedAt)throw new Error(`track "${e}" is not archived`);return delete n.archivedAt,fi.delete(e),he(n,"unarchived",{}),await ki(n),await X(),_t(n)}async function Um(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>gm)throw new Error(`name too long (max ${gm} chars)`);let r=await z(),o=re(r,e);if(!o)throw new Error(`track "${e}" not found`);if(String(o.name||"").startsWith("\u21BB"))throw new Error("loop run Tracks are named by their loop \u2014 rename the loop on the Loops tab");if(n===o.name)return _t(o);let s=o.name;return o.name=n,he(o,"renamed",{from:s,to:n}),await X(),_t(o)}async function Hm(){let e=await z(),t=new Set((await Ct()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!s.archivedAt).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(s.archivedAt)for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Mt(a.name).catch(()=>{}),he(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!Nn.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await X(),r}async function Gl(e){let t=await z(),n=re(t,e);if(!n)throw new Error(`track "${e}" not found`);if(n.archivedAt)throw new Error(`track "${e}" is archived \u2014 restore it before restarting the lead`);zm(n);let r=n.boxes.find(a=>a.name===n.leadBox);if(!r)throw new Error(`track "${e}" has no lead box`);if(Nn.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await ae(n.leadBox)){let a=await Te(n.leadBox,{lines:je}).catch(()=>"");if($c(r.agent,a))throw new Error("lead is mid-turn \u2014 retry when it goes idle");await Mt(n.leadBox),r.state="stopped"}let s=await ki(n);if(await X(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return _t(n)}async function Wm(e,t,n){let r=await ne(e);if(!r)throw new Error(`track "${e}" not found`);if(!t)throw new Error("unknown offer");let o=await L(r).catch(()=>[]);if(!o.some(i=>i.type==="box.fullscreen.detected"&&i.payload?.offerId===t))throw new Error("unknown offer");if(o.some(i=>i.type==="box.fullscreen.answered"&&i.payload?.offerId===t))throw new Error("offer already answered");return n&&await Gl(e),await _(r,{type:"box.fullscreen.answered",actor:"user",source:"orchestrator",boxId:r.leadBox,payload:{ts:Date.now(),offerId:t,action:n?"restarted":"dismiss"}}).catch(()=>{}),{ok:!0,action:n?"restarted":"dismiss"}}function Vl(e){return(!Array.isArray(e.epochs)||e.epochs.length===0)&&(e.epochs=[{agent:e.agent??null,sessionId:e.sessionId??null,pinned:e.pinned===!0,startedAt:e.createdAt??null,endedAt:null}]),e.epochs}async function Bl(e,t){let n=await z(),r=re(n,e);r&&(r.handoff={...r.handoff||{},...t},await X())}function gv(e,t=Date.now()){let n=e?.handoff;if(!n||n.finishedAt)return null;let r=n.step,o=!!n.toBox&&n.toBox===n.fromBox,s=e.boxes?.find?.(i=>i.name===e.leadBox)||null;return(o?e.leadBox===n.fromBox&&!!n.startedAt&&Array.isArray(s?.epochs)&&s.epochs.some(i=>i.startedAt!=null&&i.startedAt>=n.startedAt):!!n.toBox&&e.leadBox===n.toBox)?{...n,step:"done",finishedAt:t,recovered:`committed before restart (was "${r}")`}:{...n,step:"error",finishedAt:t,error:`interrupted by an agent restart during "${r}"; lead "${e.leadBox}" retained \u2014 retry the handoff`,recovered:"rolled-back"}}async function Km(){let e=await z(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=gv(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await ae(r.fromBox).catch(()=>null),l=n.boxes.find(u=>u.name===r.fromBox);if(c&&l&&c.agent===r.toAgent&&r.toAgent!==l.agent){let u=Date.now(),d=Vl(l),f=d[d.length-1],p=f.agent,m=f.sessionId??null;f.endedAt=u,r.reason&&(f.endReason=r.reason);let h=c.agent==="claude"&&!!c.sessionId;d.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:io,at:u},he(n,"lead-handoff",{from:r.fromBox,to:r.fromBox,fromAgent:p,toAgent:r.toAgent,fromSessionId:m,toSessionId:c.sessionId||null,epoch:d.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 Y({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 Y({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Mt(i).catch(()=>{}),a&&await Mt(a).catch(()=>{})}return t&&await X(),t}async function zl(e,t,n=!0,r=null){let o=As.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=re(await z(),e);if(!s)throw new Error(`track "${e}" not found`);yi(e,"handoff");let a=await wi(t,n),i=Xr(t);if(i){let u=new Error(`can't hand off to ${t} \u2014 its ${i.window} spend limit is reached ($${i.spentUsd.toFixed(2)} of $${i.limitUsd.toFixed(2)})`);throw u.usageCap=i,u}let c=await ae(s.leadBox);if(!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l={trackId:e,step:"checkpoint",fromBox:s.leadBox,toAgent:t,skipPermissions:n,reason:tv(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return As.set(e,l),await Bl(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),nB(l,s,c,a,t).catch(async u=>{l.error=u?.message||String(u),l.finishedAt=Date.now(),console.error(`[lead-handoff ${e}] FAILED at step "${l.step}": ${l.error}`),await Bl(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),Y({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),Y({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function nB(e,t,n,r,o){let s=async(M,N={})=>{e.step=M,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${M}`),await Bl(e.trackId,{step:M,toBox:e.toBox}),await Y({flow:"lead-handoff",phase:M,trackId:e.trackId,fromBox:e.fromBox,toBox:e.toBox,toAgent:o,sessionName:N.sessionName,...N})};console.log(`[lead-handoff ${e.trackId}] ${e.fromBox}: ${n.agent} \u2192 ${o} IN PLACE`);let a=await z(),i=re(a,e.trackId)?.boxes.find(M=>M.name===e.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:e.fromBox}),e.checkpoint="llm",Xr(c))e.checkpoint="skipped-capped";else try{await nf(e.fromBox,{timeoutMs:QS})}catch(M){e.checkpoint="reconstructed",await Y({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:M?.message||String(M)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await rf(e.trackId,n.cwd,{trackName:t.name||t.id}).catch(()=>null),await s("checkpoint-degraded",{sessionName:e.fromBox,checkpoint:e.checkpoint,statusArtifact:e.statusArtifact})),await s("swap",{cwd:n.cwd,agentCmd:r});let f=await df(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let p=ly(f.launchCmd);await Xn({cwd:n.cwd,sessionId:f.sessionId,sessionName:e.fromBox,agent:o,agentCmd:Is(o),launchEcho:p}).catch(M=>{if(M?.code!=="cli-not-running")throw M});let m=Dd(await Te(e.fromBox,{lines:je}).catch(()=>""),p);if(!Uo(o,m)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let M=km(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await df(e.fromBox,{agentCmd:M,agent:c,resumeSessionId:l,pinned:u});let N=await z(),O=re(N,e.trackId);if(O){let T=O.boxes.find(j=>j.name===e.fromBox);T&&(T.state="live"),he(O,"lead-handoff-failed",{box:e.fromBox,fromAgent:c,toAgent:o,reason:e.reason,error:`incoming ${o} CLI never drew its composer`,rolledBack:!0,handoffStartedAt:e.startedAt}),await X()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let h=await z(),g=re(h,e.trackId);if(!g)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let x=g.boxes.find(M=>M.name===e.fromBox);if(!x)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let k=Date.now(),A=Vl(x),I=A[A.length-1];I.endedAt=k,e.reason&&(I.endReason=e.reason),A.push({agent:o,sessionId:f.sessionId,pinned:f.pinned,startedAt:k,endedAt:null}),x.agent=o,x.sessionId=f.sessionId,x.pinned=f.pinned,x.state="live",x.role="lead",g.pendingHandoffHydrate={artifact:e.statusArtifact||io,at:k},he(g,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:A.length-1,singleBox:!0,forced:f.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await X(),await s("done",{sessionName:e.fromBox}),await Bl(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function Vm(e){let t=bm.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await z(),r=re(n,e);if(!r)throw new Error(`track "${e}" not found`);if(r.archivedAt)throw new Error("track is archived \u2014 restore it before moving it");if(r.kind==="setup")throw new Error("setup boxes don't move");if(r.transferredTo)throw new Error(`track already transferred to ${r.transferredTo}`);let o=As.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is running \u2014 retry when it settles");for(let a of wt.values())if(a.trackId===e&&!a.finishedAt)throw new Error("a subtask is still running on this Track \u2014 wait for it to merge back before moving");let s={trackId:e,step:"checkpoint",startedAt:Date.now(),finishedAt:null,error:null};return bm.set(e,s),rB(s,r).catch(a=>{s.error=a?.message||String(a),s.finishedAt=Date.now(),console.error(`[transfer-out ${e}] FAILED at step "${s.step}": ${s.error}`)}),s}async function rB(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await ae(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&Xr(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await nf(t.leadBox,{timeoutMs:QS})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await rf(t.id,t.cwd,{trackName:t.name||t.id}).catch(()=>null),e.step="push";let o=t.workspace?.branch??null,s=[];if(o)for(let u of gt(t.workspace)){if(!u?.dir)continue;let d={name:u.name??null,repo:u.repo??null,baseSha:null,pushed:!1};try{await Mn(u.dir,["push","origin",o],ym);let{stdout:f}=await Mn(u.dir,["rev-parse","HEAD"]);d.baseSha=f.trim(),d.pushed=!0}catch(f){d.error=String(f?.message||f).split(`
|
|
197
|
+
`)[0]}s.push(d)}e.step="wip";let a=await tm({id:t.id,cwd:t.cwd}),i=await z(),c=re(i,t.id),l=c?.boxes.find(u=>u.name===c.leadBox)||null;l&&Vl(l),await X(),e.result={name:c?.name??t.name,agent:l?.agent??null,leadBox:c?.leadBox??t.leadBox,createdAt:c?.createdAt??null,branch:o,repos:s.filter(u=>u.pushed).map(({name:u,repo:d,baseSha:f})=>({name:u,repo:d,baseSha:f})),unpushed:s.filter(u=>!u.pushed).map(({name:u,repo:d,error:f})=>({name:u,repo:d,error:f})),wip:a.pushed?{ref:a.ref,sha:a.sha}:null,checkpoint:e.checkpoint,epochs:l?.epochs??[],codeless:!o||!s.some(u=>u.pushed)},e.step="ready",e.finishedAt=Date.now()}function zm(e){if(!e?.transferredTo)return;let t=new Error(`track "${e.id}" moved to node ${e.transferredTo} \u2014 drive it there`);throw t.transferredTo=e.transferredTo,t}async function Jl(e,{toNode:t,mode:n="transfer"}={}){if(!t||typeof t!="string")throw new Error("toNode required");let r=await z(),o=re(r,e);if(!o)throw new Error(`track "${e}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};$m(e),await Bm(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();for(let i of o.boxes){Nn.has(i.state)||(i.state="stopped");let c=Array.isArray(i.epochs)?i.epochs[i.epochs.length-1]:null;c&&typeof c=="object"&&c.endedAt==null&&(c.endedAt=s,c.endReason==null&&(c.endReason=n==="recover"?"node-offline":"node-transfer"))}let a=await nm({id:o.id,cwd:o.cwd});return o.transferredTo=t,o.transferredAt=s,he(o,"transferred-out",{toNode:t,mode:n,box:o.leadBox,...a.pushed?{rescueRef:a.ref,rescueSha:a.sha}:{}}),await X(),{ok:!0,toNode:t,rescue:a.pushed?{ref:a.ref,sha:a.sha}:null}}async function Jm(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await Jl(n.id,{toNode:n.toNode,mode:n.mode==="recover"?"recover":"transfer"});t.push({id:n.id,...r})}catch(r){/not found/i.test(String(r?.message))||console.error(`[transfer] demote of ${n.id} failed: ${r?.message||r}`)}return t}async function zS(e,t,n){try{await Gc(e,t,n)}catch{await Mn(e,["worktree","prune"]).catch(()=>{}),await Gc(e,t,n)}return t}async function Ym(e={}){let{trackId:t,name:n,agent:r="claude",skipPermissions:o=!0,mode:s="transfer"}=e;if(!t||typeof t!="string"||!/^[0-9a-f]{8}$/.test(t))throw new Error("trackId (8-hex string) required");if(typeof n!="string"||!n.trim())throw new Error("name (string) required");if(s!=="transfer"&&s!=="recover")throw new Error(`unknown adopt mode "${s}"`);if(fm.has(t))throw new Error("an adopt is already running for this Track");fm.set(t,{startedAt:Date.now()});try{return await oB({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{fm.delete(t)}}async function oB({trackId:e,name:t,agent:n,skipPermissions:r,mode:o,fromNode:s=null,toNode:a=null,branch:i=null,repos:c=[],wip:l=null,brief:u=null,epochs:d=[],createdAt:f=null,leadBox:p=null,reason:m=null}){let h=await wi(n,r),g=await z(),x=re(g,e);if(x&&!x.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);x&&(await nm({id:x.id,cwd:x.cwd}).catch(()=>{}),await pv(x),g.tracks.splice(g.tracks.indexOf(x),1),await X()),await W.mkdir(ai,{recursive:!0});let k=G.join(ai,e);await W.rm(k,{recursive:!0,force:!0}).catch(()=>{});let A=null,I=!1,M=[];try{let N=[];if(i)for(let q of Array.isArray(c)?c:[]){let J=typeof q=="string"?q:q?.repo;if(!J||!/^[^/\s]+\/[^/\s]+$/.test(J))continue;let ee=await So(J)??await ec(J);await Mn(ee,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],ym),N.push({slug:J,base:ee,name:typeof q=="object"?q?.name??null:null})}if(i&&N.length===1){let{base:q,slug:J}=N[0];await zS(q,k,i),M.push({base:q,dir:k}),await di(k);let ee=await Zo(q);A={dir:k,base:q,branch:i,kind:"worktree",baseRef:ee,repo:J}}else if(i&&N.length>=2){await W.mkdir(k,{recursive:!0});let q=new Set([".bostrat"]),J=[];for(let ee of N){let Ze=ee.name||G.basename(ee.base);q.has(Ze)&&(Ze=ee.slug.replace("/","-"));for(let v=2;q.has(Ze);v++)Ze=`${G.basename(ee.base)}-${v}`;q.add(Ze);let y=G.join(k,Ze);await zS(ee.base,y,i),M.push({base:ee.base,dir:y});let w=await Zo(ee.base);J.push({name:Ze,dir:y,base:ee.base,baseRef:w,repo:ee.slug})}await bi(k,J,i),A={dir:k,kind:"multi",branch:i,repos:J}}else I=!0,await W.mkdir(k,{recursive:!0}),await Nf(k),await di(k),A={dir:k,base:null,branch:null,kind:"repo"};let O=!1,T=null;if(l?.sha&&A.kind==="worktree")try{await Mn(k,["fetch","origin",l.ref||em(e)],ym);let{stdout:q}=await Mn(k,["rev-parse","FETCH_HEAD^"]),{stdout:J}=await Mn(k,["rev-parse","HEAD"]);q.trim()===J.trim()?(await Mn(k,["read-tree","--reset","-u","FETCH_HEAD"]),await Mn(k,["reset","-q"]),O=!0):T="snapshot does not parent the fetched branch head"}catch(q){T=String(q?.message||q).split(`
|
|
198
|
+
`)[0]}else l?.sha&&(T="wip snapshots cover single-repo worktrees only");await WS({id:e,cwd:k}).catch(()=>({restored:!1}));let j=G.join(k,io),Z=null;(await fc(k)).restored?Z="checkpoint":typeof u=="string"&&u.trim()&&(await W.mkdir(G.dirname(j),{recursive:!0}),await W.writeFile(j,u),Z="brief");let ie=new Set(g.tracks.flatMap(q=>(q.boxes||[]).map(J=>J.name))),D=null;p&&!ie.has(p)&&!await ae(p).catch(()=>null)?D=p:(D=`${t.trim()} lead`,ie.has(bc(D))&&(D=`${t.trim().slice(0,50).trim()} ${e} lead`));let R=await Jn(D,k,{agentCmd:h,agent:n});ka({cwd:R.cwd,sessionName:R.name,agent:n,agentCmd:h});let U=Date.now(),pe=qm(R,"lead","live"),Se=(Array.isArray(d)?d:[]).filter(q=>q&&typeof q=="object").map(q=>({...q})),He=Se[Se.length-1];He&&He.endedAt==null&&(He.endedAt=U,He.endReason==null&&(He.endReason=o==="recover"?"node-offline":"node-transfer")),Se.push({agent:n,sessionId:R.sessionId??null,pinned:R.pinned===!0,startedAt:U,endedAt:null}),pe.epochs=Se;let le={id:e,name:t.trim(),cwd:R.cwd,workspace:A,leadBox:R.name,boxes:[pe],events:[],createdAt:Number.isFinite(f)?f:U,...Z?{pendingHandoffHydrate:{artifact:io,at:U}}:{}};return he(le,"transferred-in",{box:R.name,agent:n,fromNode:s,toNode:a,mode:o,reason:m,branch:A.branch??null,wip:O,epoch:Se.length-1,name:le.name,cwd:le.cwd,workspace:{base:A.base??null,branch:A.branch??null,kind:A.kind,...A.baseRef?{baseRef:A.baseRef}:{},...A.repo?{repo:A.repo}:{},...A.repos?{repos:A.repos.map(({name:q,base:J,baseRef:ee,repo:Ze})=>({name:q,base:J,baseRef:ee,repo:Ze}))}:{}}}),g.tracks.push(le),await X(),{...await _t(le),adopt:{wipApplied:O,wipSkipped:T,codeless:I,artifactSource:Z}}}catch(N){for(let O of M)await pt(O.base,O.dir).catch(()=>{});throw await W.rm(k,{recursive:!0,force:!0}).catch(()=>{}),N}}async function yv(e,t,n){let r=await z(),o=re(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await X())}async function xs(e,t){e.step=t,await xi(e)}function bv(e,t=Date.now()){if(!e||e.finishedAt)return null;let n=e.step;return["snapshot","spawn","working"].includes(n)?{...e,step:"error",finishedAt:t,error:`interrupted by an agent restart during "${n}"; no changes were applied to the lead \u2014 retry the delegation`,recovered:"rolled-back"}:{...e,step:"error",finishedAt:t,error:`interrupted by an agent restart during "${n}"; the lead's working tree may hold a PARTIAL merge \u2014 review git status. The subtask's work is preserved on branch ${e.branch}`,recovered:"partial-merge"}}function kv(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function cB(e,t,n){if(!kv(n)||wt.has(t)||!await ae(n.box).catch(()=>null))return!1;try{await W.access(n.dir)}catch{return!1}let o=await ae(e.leadBox).catch(()=>null);if(!o?.cwd)return!1;let s={...n,id:t,leadBox:e.leadBox,step:"working",canceled:!1,forceMerge:!1};return wt.set(t,s),Sv(s,o).catch(a=>xv(s,a)),!0}async function Xm(){let e=await z(),t=!1;for(let n of e.tracks||[]){for(let[r,o]of Object.entries(n.subtasks||{})){if(wt.has(r))continue;if(await cB(n,r,o)){t=!0,he(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await Y({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=bv(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!Nn.has(i.state)&&(i.state="canceled"),he(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await Y({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await JS(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||Nn.has(r.state)||wt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?G.join(Es,`${n.id}-${r.subtaskId}`):null;await Y({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await JS(n,r.name,o)}}return t=await lB(e)||t,t&&await X(),t}async function JS(e,t,n){if(t&&await Mt(t).catch(()=>{}),!n)return;if(e.workspace?.kind==="multi"){for(let o of gt(e.workspace)){let s=G.join(n,o.name);await pt(o.base,s).then(()=>!0,()=>!1)||await pt(o.dir,s).catch(()=>{})}await W.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await ae(e.leadBox).catch(()=>null);r?.cwd&&await pt(r.cwd,n).catch(()=>{})}async function lB(e){let t;try{t=await W.readdir(Es)}catch{return!1}let n=new Set;for(let o of e.tracks||[]){for(let s of Object.keys(o.subtasks||{}))n.add(`${o.id}-${s}`);for(let s of o.boxes||[])s.subtaskId&&n.add(`${o.id}-${s.subtaskId}`)}for(let o of wt.values())o.dir&&n.add(G.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=G.join(Es,o),a=(e.tracks||[]).find(i=>o.startsWith(`${i.id}-`));if(a?.workspace?.kind==="multi"){for(let i of gt(a.workspace))await pt(i.base,G.join(s,i.name)).catch(()=>{});await W.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await ae(a.leadBox).catch(()=>null):null;i?.cwd?await pt(i.cwd,s).catch(()=>{}):await W.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await Y({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function Qm(e,{agent:t,task:n,skipPermissions:r=!0,reason:o=null}={}){if(typeof n!="string"||!zo(n))throw new Error("task (string) required");let s=await wi(t,r),a=Xr(t);if(a){let p=new Error(`can't delegate to ${t} \u2014 its ${a.window} spend limit is reached ($${a.spentUsd.toFixed(2)} of $${a.limitUsd.toFixed(2)})`);throw p.usageCap=a,p}let i=re(await z(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await ae(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=gt(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 Jo(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 Jo(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=_s().slice(0,8),f={id:d,trackId:e,trackName:i.name,agent:t,task:zo(n),skipPermissions:r,reason:tv(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${Zm(i.name)}-${d}`,base:null,repos:u,dir:G.join(Es,`${e}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return wt.set(d,f),await xi(f),uB(f,c,s).catch(p=>xv(f,p)),f}async function xv(e,t){e.failedStep=e.step,e.error=t instanceof mi?"canceled":t?.message||String(t),e.step=t instanceof mi?"canceled":"error",e.finishedAt=Date.now(),await th(e).catch(()=>{}),await xi(e).catch(()=>{}),await mB(e).catch(()=>{})}function Xl(e){let t=wt.get(e);if(!t)throw new Error(`subtask "${e}" not found`);if(t.finishedAt)throw new Error("subtask already finished");return t.canceled=!0,t}function eh(e){let t=wt.get(e);if(!t)throw new Error(`subtask "${e}" not found`);if(t.finishedAt)throw new Error("subtask already finished");if(t.step!=="working")throw new Error(`subtask is not waiting to merge (step: ${t.step})`);return t.forceMerge=!0,t}async function uB(e,t,n){if(await xs(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await Yo(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await xi(e)}else e.base=await Yo(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await xs(e,"spawn"),await W.mkdir(Es,{recursive:!0}),e.repos){await W.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await Xo(a.leadDir,G.join(e.dir,a.name),e.branch,a.base);let s=gt((re(await z(),e.trackId)||{}).workspace);await bi(e.dir,e.repos.map(a=>{let i=s.find(c=>c?.name===a.name);return{name:a.name,repo:i?.repo??null,baseRef:null}}),e.branch)}else await Xo(t.cwd,e.dir,e.branch,e.base),await di(e.dir);await dB(e,t);let r=e.trackName.slice(0,38).trim(),o=await Jn(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await fB(e,o),await nv(e.trackId,o.name,dD(e.task,!!e.repos),{kind:"subtask"}),await Sv(e,t)}async function Sv(e,t){await xs(e,"working");let n=G.join(e.dir,Dl);if(await wB(n,e),e.canceled)throw new mi;if(await xs(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=G.join(e.dir,l.name);await Mf(u,`bostrat: subtask ${e.id} result (auto-committed)`),l.stat=await Df(l.leadDir,l.base,e.branch,ks),l.files=await Bf(l.leadDir,l.base,e.branch,ks);let d=await jf(l.leadDir,l.base,e.branch,ks);l.applied=d.applied,l.conflicts=d.conflicts??[],s.files+=l.stat.files,s.insertions+=l.stat.insertions,s.deletions+=l.stat.deletions,a.push(...l.files.map(f=>G.join(l.name,f))),i.push(...l.conflicts.map(f=>f.startsWith("patch did not apply cleanly")?`${l.name}: ${f}`:G.join(l.name,f))),d.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await Mf(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`),e.stat=await Df(t.cwd,e.base,e.branch,ks),e.files=await Bf(t.cwd,e.base,e.branch,ks);let s=await jf(t.cwd,e.base,e.branch,ks);e.applied=s.applied,e.conflicts=s.conflicts}await xi(e);let r=await hB(e,t,n);await xs(e,"resume"),await nv(e.trackId,e.leadBox,fD(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await xs(e,"reap"),await th(e),e.step="done";let o=Date.now();await yv(e.trackId,e.id,{...Yl(e),finishedAt:o}),await pB(e,o),e.finishedAt=o}async function dB(e,t){let n=G.join(e.dir,G.dirname(ii));await W.mkdir(n,{recursive:!0});let r="";try{r=await Gg(t.cwd,t.sessionId)}catch{}if(!r)try{r=await W.readFile(G.join(t.cwd,".bostrat/handoff/latest.md"),"utf8")}catch{r="_(no prior conversation digest available \u2014 rely on the working tree.)_"}let o=`# Lead context for subtask ${e.id}
|
|
196
199
|
|
|
197
200
|
**Delegated task:** ${e.task}
|
|
198
201
|
|
|
@@ -203,15 +206,15 @@ ${a}`}async function Im(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<
|
|
|
203
206
|
## Conversation so far (lead \u2192 you)
|
|
204
207
|
|
|
205
208
|
${r}
|
|
206
|
-
`;await W.writeFile(G.join(e.dir,
|
|
209
|
+
`;await W.writeFile(G.join(e.dir,ii),o)}async function fB(e,t){let n=await z(),r=re(n,e.trackId);r&&(r.boxes.push({name:t.name,agent:t.agent,role:"subtask",state:"live",cwd:t.cwd,sessionId:t.sessionId,hookTokenHash:Gs(t.hookToken),createdAt:Date.now(),subtaskId:e.id,branch:e.branch,task:e.task}),he(r,"subtask-delegated",{to:t.name,toAgent:e.agent,task:e.task,subtaskId:e.id,skipPermissions:e.skipPermissions,reason:e.reason}),r.subtasks=r.subtasks||{},r.subtasks[e.id]={...r.subtasks[e.id]||{},...Yl(e)},await X())}async function pB(e,t=e.finishedAt){let n=await z(),r=re(n,e.trackId);if(!r)return;let o=r.boxes.find(s=>s.subtaskId===e.id);o&&(o.state=e.conflicts.length?"conflict":"merged",o.stat=e.stat),he(r,"subtask-merged",{from:e.box,fromAgent:e.agent,task:e.task,subtaskId:e.id,stat:e.stat,conflicts:e.conflicts.length,files:e.files,durationMs:t?t-e.startedAt:null}),await X()}async function mB(e){let t=await z(),n=re(t,e.trackId);if(!n)return;let r=e.canceled||e.error==="canceled",o=n.boxes.find(s=>s.subtaskId===e.id);o&&(o.state=r?"canceled":"failed"),he(n,r?"subtask-canceled":"subtask-failed",{from:e.box,fromAgent:e.agent,task:e.task,subtaskId:e.id,failedStep:e.failedStep||e.step,error:e.error,durationMs:e.finishedAt?e.finishedAt-e.startedAt:null}),n.subtasks=n.subtasks||{},n.subtasks[e.id]={...n.subtasks[e.id]||{},...Yl(e)},await X()}async function hB(e,t,n){let r=`.bostrat/handoff/subtask-${e.id}.md`;try{let o=G.join(t.cwd,r);await W.mkdir(G.dirname(o),{recursive:!0}),await W.copyFile(n,o)}catch{}return r}async function th(e){if(e.dir&&await ed(e.dir).catch(()=>{}),e.box&&await Mt(e.box).catch(()=>{}),e.repos&&e.dir){for(let t of e.repos)await pt(t.leadDir,G.join(e.dir,t.name)).catch(()=>{});await W.rm(e.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(e.dir){let n=(await ae(e.leadBox).catch(()=>null))?.cwd;n&&await pt(n,e.dir).catch(()=>{})}}async function wB(e,t){let n=(t.startedAt||Date.now())+sB;for(;;){if(t.canceled||t.forceMerge)return;try{await W.access(e);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await lo(aB)}}function lo(e){return new Promise(t=>setTimeout(t,e))}var YS,sn,XS,cD,ai,lD,jl,hi,br,io,QS,uD,Dl,ii,dD,fD,Et,sm,re,am,he,im,cm,hD,Nn,KS,yD,bD,kD,xD,TD,ci,GS,_D,ID,RD,$D,OD,Ss,li,PD,BD,jD,UD,uv,ui,dv,KD,si,lm,GD,um,YD,fi,XD,ZD,VS,dm,gm,qm,As,Rs,ym,Mn,bm,Gm,fm,sB,aB,Es,ks,wt,iB,fo,Yl,xi,Zm,mi,hr=C(()=>{na();Ve();sf();xb();xa();qn();Ec();Eb();et();Gt();zs();Ac();Wb();Lr();If();$f();ak();Cc();Fd();_c();Zd();Pt();Vc();Hn();Sa();Xc();Mk();Kf();ux();ef();Vd();fx();xp();ue();_l();Ka();rs();Dp();gs();Gp();Nl();Qp();gn();jS();rm();qS();pc();YS=iD(aD),sn=process.env.BOSTRAT_STATE_DIR||G.join(process.env.HOME||"/tmp",".bostrat"),XS=G.join(sn,"tracks.json"),cD=G.join(sn,"events.jsonl"),ai=G.join(sn,"workspaces"),lD=process.env.BOSTRAT_DEFAULT_REPO||null,jl={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"}},hi=Object.keys(jl);br={at:0,value:null};io=".bostrat/handoff/latest.md",QS=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;uD=/\(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\.\)$/;Dl=".bostrat/handoff/subtask-result.md",ii=".bostrat/handoff/lead-context.md",dD=(e,t=!1)=>t?`You are a SUBTASK agent working in an isolated multi-repo workspace, delegated by a lead agent on a shared workstream. This directory is NOT itself a git repo: each subdirectory is an isolated git worktree of one of the workstream's repos, already seeded with the lead's full working tree for that repo. The conversation so far is summarized in ${ii} \u2014 read that first for context. Your scoped task:
|
|
207
210
|
|
|
208
211
|
${e}
|
|
209
212
|
|
|
210
|
-
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 ${
|
|
213
|
+
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 ${Dl} (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 ${ii} \u2014 read that first for context. Your scoped task:
|
|
211
214
|
|
|
212
215
|
${e}
|
|
213
216
|
|
|
214
|
-
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 ${
|
|
217
|
+
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 ${Dl} 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.`,fD=(e,t,n,r)=>`A subtask box you delegated has finished: "${e}". Its changes are now merged into your working tree (${t.files} file${t.files===1?"":"s"}, +${t.insertions} \u2212${t.deletions}). Read ${r} for exactly what it did. `+(n.length?`IMPORTANT: ${n.length} merge conflict${n.length===1?"":"s"} need resolving \u2014 fix the conflict markers in: ${n.join(", ")}, then continue.`:"Review the merged changes and continue the work."),Et=null,sm=null;re=(e,t)=>e.tracks.find(n=>n.id===t)||null,am=Promise.resolve();he=(e,t,n={})=>{let r={at:Date.now(),type:t,...n};return e.events.push(r),pD(e.id,r),Dk(e,r),r};Yg(async({sessionName:e,from:t,to:n,version:r,status:o,error:s})=>{let i=(await z()).tracks.find(c=>c.leadBox===e||(c.boxes||[]).some(l=>l.name===e));i&&(he(i,"codex-update",{box:e,from:t,to:r||n,status:o,...s?{error:s}:{}}),await X().catch(()=>{}))});im=G.join(sn,"plane-link","update-notice.json");cm=(e,t,n)=>t!=null&&n.some(r=>e===r.box&&t>=r.from&&t<=r.to),hD=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);Nn=new Set(["merged","conflict","canceled","failed","handed-off"]),KS=new Map;yD=5e3;bD={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};kD=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];xD=10*6e4;TD=1500,ci=new Map;GS="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.";_D=4e3,ID=3,RD=[300,350,400],$D=1e3,OD=12,Ss=new Map,li=new Map;PD=[250,400,500,600];BD=3e4,jD=[350,650,900];UD=12e4;uv=e=>({branch:e.branch,kind:e.kind,repos:(e.repos??[]).filter(Boolean).map(({name:t,base:n,baseRef:r,repo:o})=>({name:t,base:n,baseRef:r,repo:o}))});ui="# bostrat workspace",dv=`${ui}
|
|
215
218
|
|
|
216
219
|
TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
|
|
217
220
|
before substantive work \u2014 it is the shared cross-provider memory for this repo
|
|
@@ -369,10 +372,13 @@ of publishing it.
|
|
|
369
372
|
When a real DECISION gets made in this Track \u2014 an approach chosen over
|
|
370
373
|
alternatives, a tradeoff settled, a plan changed \u2014 record it:
|
|
371
374
|
|
|
372
|
-
bostrat-decision record --title "<the choice>"
|
|
373
|
-
[--alt "<option rejected>"]\u2026 "<what was decided>"
|
|
375
|
+
bostrat-decision record --title "<the choice, \u226472 chars>" --summary "<one sentence, \u2264160 chars>"
|
|
376
|
+
[--context "<why it came up>"] [--alt "<option rejected>"]\u2026 "<what was decided>"
|
|
374
377
|
|
|
375
|
-
|
|
378
|
+
The title is the choice AS A LABEL; the summary is one sentence a reader can
|
|
379
|
+
act on without opening the record \u2014 both render on the Track-details rail, so
|
|
380
|
+
\`--summary\` is required and the caps are enforced (the full story goes in the
|
|
381
|
+
decision text). Record the WHY, not a summary of the work: name each option you rejected
|
|
376
382
|
(\`--alt\`), because that is the half nobody can reconstruct weeks later. One
|
|
377
383
|
decision per record. Records are IMMUTABLE \u2014 if the call later changes, record
|
|
378
384
|
a NEW one with \`--supersedes <id>\` (\`bostrat-decision ls\` prints ids) and the
|
|
@@ -479,15 +485,15 @@ which steps to run (and which to skip). An offer buried mid-message, phrased as
|
|
|
479
485
|
statement, or omitted entirely renders no controls \u2014 so close EVERY completed
|
|
480
486
|
implementation turn this way, every time. When the operator has already asked you
|
|
481
487
|
to commit / push / open the PR, just do it; do not re-offer.
|
|
482
|
-
`,
|
|
483
|
-
`)),
|
|
484
|
-
`,`${
|
|
488
|
+
`,KD=dv,si=dv+Ny;lm=e=>typeof e=="string"&&(e===ui||e.startsWith(`${ui}
|
|
489
|
+
`)),GD="# bostrat multi-repo workspace";um=si.replace(`${ui}
|
|
490
|
+
`,`${ui}
|
|
485
491
|
|
|
486
492
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
487
493
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
488
494
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
489
495
|
When working anywhere else, ignore everything below.
|
|
490
|
-
`);ED=new Set(["claude","codex","cursor"]);oi=new Set,AD=e=>oi.has(e);_D=9e4;CS={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};tm=new Map;im=120;Mm=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:js(e.hookToken),createdAt:Date.now()});ys=new Map,xs=e=>ys.get(e)||(At?re(At,e)?.handoff:null)||null;cm=6e4,Cn=(e,t,n=15e3)=>PS("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),lm=new Map,Pm=e=>lm.get(e)||null;nm=new Map;ND=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,PD=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,bs=G.join(rn,"worktrees"),ps=[Qa,Il],wt=new Map,DD=e=>wt.get(e)||(At?At.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,co=e=>{let t=new Map([...wt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=At?re(At,e):null;if(n?.subtasks)for(let[r,o]of Object.entries(n.subtasks))t.has(r)||t.set(r,o);return[...t.values()].sort((r,o)=>r.startedAt-o.startedAt)},Hl=e=>({id:e.id,trackId:e.trackId,trackName:e.trackName,agent:e.agent,task:e.task,skipPermissions:e.skipPermissions,reason:e.reason||null,step:e.step,failedStep:e.failedStep||null,box:e.box,branch:e.branch,base:e.base,repos:e.repos||null,dir:e.dir,stat:e.stat,files:e.files||null,conflicts:e.conflicts||[],applied:e.applied,error:e.error,canceled:e.canceled,startedAt:e.startedAt,finishedAt:e.finishedAt});mi=e=>rv(e.trackId,e.id,Hl(e));Um=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",ai=class extends Error{}});tt();import ge from"node:path";import Ye from"node:fs";import YF from"node:readline";import{fileURLToPath as XF}from"node:url";import Rt from"express";import ZF from"node:http";import QF from"node:https";import Xh from"node:os";import{WebSocketServer as eU}from"ws";tt();import wI from"node-pty";async function dg(e,t,n={}){if(!_e(t)){wc(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await Vn()){wc(e,`host tmux server not running on ${ft}`),e.close(1011,"tmux server unavailable");return}if(!await se(t)){wc(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=ug(n.cols,120,fg),o=ug(n.rows,32,pg),s;try{s=wI.spawn(F,["-S",ft,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){wc(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&sd(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(sd(e,{type:"exit",code:a??null,signal:i??null}),e.close(1e3,"tmux exited"))}),e.on("message",a=>{let i;try{i=JSON.parse(a.toString())}catch{return}if(!(!i||typeof i!="object")){if(i.type==="data"&&typeof i.data=="string")s.write(i.data);else if(i.type==="resize"&&kI(i.cols,i.rows))try{s.resize(Math.min(500,i.cols|0),Math.min(500,i.rows|0))}catch{}}}),e.on("close",()=>{try{s.kill()}catch{}bI(t)})}var gI=6,yI=250;async function bI(e,t=gI,n=yI){for(let r=0;r<t;r++){if(await Nr(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var fg=20,pg=6;function kI(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=fg&&Number(t)>=pg}function ug(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function sd(e,t){try{e.send(JSON.stringify(t))}catch{}}function wc(e,t){sd(e,{type:"error",message:t})}function mg(e){let t=typeof e=="string"&&e?e:"/",n=t.indexOf("?"),r=n===-1?t:t.slice(0,n),o=n===-1?"":t.slice(n+1),s=r.match(/^\/api\/attach\/([^/]+)$/);if(!s)return{ok:!1,status:404};let a;try{a=decodeURIComponent(s[1])}catch{return{ok:!1,status:400}}let i=new URLSearchParams(o);return{ok:!0,sessionName:a,cols:Number(i.get("cols"))||void 0,rows:Number(i.get("rows"))||void 0}}Nt();yc();Jd();fr();import{createRequire as tU}from"node:module";var GD=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function Km({tracks:e=[],views:t=[],queuedOf:n=()=>[]}={}){let r=[];for(let o of e)!o||o.archivedAt&&!o.archiving||o.transferredTo||(o.working&&r.push(`working:${o.id}`),o.prompted&&r.push(`prompted:${o.id}`),(n(o.id)??[]).length&&r.push(`queued:${o.id}`),(o.boxes||[]).some(s=>s?.role==="subtask"&&s?.state==="live")&&r.push(`subtask:${o.id}`),Object.values(o.subtasks||{}).some(GD)&&r.push(`merge-back:${o.id}`));return t.some(o=>o?.state==="live"||o?.state==="starting")&&r.push("views"),{busy:r.length>0,reasons:r}}import hv from"node:fs";import yr from"node:fs/promises";import iB from"node:os";import Ge from"node:path";import{spawn as cB}from"node:child_process";import{createRequire as lB}from"node:module";ue();Wc();import pv from"node:fs";import Vm from"node:fs/promises";import JD from"node:http";import Nn from"node:path";import Gl from"node:os";import{createRequire as YD}from"node:module";import zm from"ws";import VD from"node:path";import zD from"node:os";var hi=process.env.BOSTRAT_STATE_DIR||VD.join(process.env.HOME||zD.tmpdir(),".bostrat");function Gm(e=process.env){let t=(e.BOSTRAT_HOST||"").trim();if(t!=="all")return t||(e.BOSTRAT_PLANE_URL?"127.0.0.1":void 0)}function lv(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=Gm(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function uv(e){let t=String(e||"").trim();if(!t)return"";let n=/^[a-z][a-z0-9+.-]*:\/\//i.test(t)?t:`wss://${t}`,r;try{r=new URL(n)}catch{return t}let o={"https:":"wss:","http:":"ws:","wss:":"wss:","ws:":"ws:"}[r.protocol];return o?(r.protocol=o,(r.pathname===""||r.pathname==="/")&&(r.pathname="/v1/node"),r.toString()):t}var jt=(()=>{try{return YD(import.meta.url)("../package.json").version||null}catch{return null}})(),XD=process.env.BOSTRAT_STATE_DIR||Nn.join(process.env.HOME||Gl.tmpdir(),".bostrat");function lo({env:e=process.env,pkgDir:t=Nn.resolve(Nn.dirname(new URL(import.meta.url).pathname),".."),exists:n=pv.existsSync}={}){let r=String(e.BOSTRAT_INSTALL_KIND||"").trim();return["container","npm-global","git-checkout","dev"].includes(r)?r:/\/lib\/node_modules\/bostrat-node(?:\/|$)/.test(t.replace(/\\/g,"/"))?"npm-global":n(Nn.join(t,"..",".git"))?"git-checkout":"dev"}function wi(e,t){let n=String(e).split(".").map(Number),r=String(t).split(".").map(Number);if(n.some(Number.isNaN)||r.some(Number.isNaN))return!1;for(let o=0;o<Math.max(n.length,r.length);o++){let s=(n[o]||0)-(r[o]||0);if(s)return s<0}return!1}function ql(e){switch(e){case"npm-global":return"npm install -g bostrat-node && restart the bostrat-node service";case"git-checkout":return"scripts/deploy-pi.sh (or The Bakery \u2192 Deploy to Pi \u2192 bostrat)";case"container":return"Links \u2192 Fly \u2192 Update (or the plane's auto-update while the node sleeps)";default:return"update this node's bostrat-node checkout/install"}}function ZD({home:e=process.env.HOME||Gl.homedir(),env:t=process.env}={}){let n=r=>{try{return pv.statSync(r),!0}catch{return!1}};return{claude:n(Nn.join(e,".claude",".credentials.json")),codex:n(Nn.join(t.CODEX_HOME||Nn.join(e,".codex"),"auth.json"))}}var Ke=1,dv=1e3,fv=3e4,QD=6e4,eB=1500,tB=500,Kl=100,nB=2e3,rB=25e3,oB=6e4,sB=6e4,aB=6e4;function mv({url:e,token:t,nodeId:n=Gl.hostname(),name:r=Gl.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,metricsMs:c=sB,onUpdateAvailable:l=null,onUpdatePolicy:u=null,onHelloRefused:f=null,updatePolicy:d=null,installKind:p=lo(),selfUrl:m=`http://127.0.0.1:${process.env.PORT||3362}`,log:h=console}){if(!e||!t)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");e=uv(e);let g=m.replace(/^http/,"ws"),x=new Set,k=new Map,E=new Map,I=Nn.join(XD,"plane-link","cursor.json"),M={},N={},C=new Map,T=null,j=!1,Z=!1,fe=dv,ie=null,D=null,R=null,U=null,pe=null,Se=!1,He=0;async function le(){try{M=JSON.parse(await Vm.readFile(I,"utf8"))}catch{M={}}}function q(){Se=!0,!R&&(R=setTimeout(async()=>{if(R=null,!!Se){Se=!1;try{await Vm.mkdir(Nn.dirname(I),{recursive:!0}),await Vm.writeFile(I,JSON.stringify(M))}catch(te){h.error("[plane-link] cursor flush failed:",te?.message||te)}}},nB),R.unref?.())}function J(te){return!T||T.readyState!==zm.OPEN?!1:(T.send(JSON.stringify(te)),!0)}function ee(te){let V=Date.now();for(let{trackId:A,event:B}of te){let ye=C.get(B.id);C.set(B.id,{trackId:A,seq:B.seq,event:B,sentAt:V,tries:(ye?.tries??0)+1})}return J({v:Ke,ch:"events",type:"append",events:te})}function Xe(te){let V=null;for(let A of C.values())A.trackId===te&&(V==null||A.seq<V)&&(V=A.seq);return V}function y(te){let V=N[te];if(V==null)return;let A=Xe(te),B=A==null?V:Math.min(V,A-1);B>(M[te]??0)&&(M[te]=B,q())}function w(){if(!j)return;let te=Date.now(),V=[...C.values()].filter(A=>A.event&&te-A.sentAt>=oB);if(V.length){V.sort((A,B)=>A.trackId===B.trackId?A.seq-B.seq:A.trackId<B.trackId?-1:1),h.log(`[plane-link] re-sending ${V.length} unacked event(s) the plane never confirmed`);for(let A=0;A<V.length;A+=Kl){let B=V.slice(A,A+Kl).map(ye=>({trackId:ye.trackId,event:ye.event}));if(!ee(B))return}}}async function v(){if(!i)return;let te;try{te=await i()}catch(V){h.error("[plane-link] metrics sample failed:",V?.message||V);return}te&&J({v:Ke,ch:"ctl",type:"metrics",metrics:te})}function $(){i&&(pe&&clearInterval(pe),v(),pe=setInterval(v,c),pe.unref?.())}async function ve(){let te;try{te=await o()}catch(V){h.error("[plane-link] backfill listTracks failed:",V?.message||V);return}J({v:Ke,ch:"ctl",type:"tracks",ids:te.filter(V=>!V.transferredTo).map(V=>V.id).filter(V=>typeof V=="string")});for(let V of te){if(!j)return;let A;try{A=await L(V)}catch{continue}let B=M[V.id]??0,ye=A.filter(be=>be.seq>B&&typeof be.id=="string");for(let be=0;be<ye.length;be+=Kl){let Ze=ye.slice(be,be+Kl).map(kt=>({trackId:V.id,event:kt}));if(!ee(Ze))return}}}function b(te){!j||!te?.trackId||typeof te.id!="string"||ee([{trackId:te.trackId,event:te}])}let P=new Map;async function Me(te){let{reqId:V,method:A,path:B,headers:ye={},body:be}=te,Ze=Object.fromEntries(Object.entries(ye).filter(([,Wt])=>Wt!=null)),kt=new AbortController;P.set(V,kt);try{if(te.host){await We(te,Ze,kt);return}let Wt=await fetch(m+B,{method:A,headers:Ze,body:be?Buffer.from(be,"base64"):void 0,signal:kt.signal}),jn=Object.fromEntries(Wt.headers);for(let xt of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete jn[xt];if(jn["content-type"]||(jn["content-type"]="application/octet-stream"),J({v:Ke,ch:"control",type:"http_head",reqId:V,status:Wt.status,headers:jn}),Wt.body){for await(let xt of Wt.body)if(!J({v:Ke,ch:"control",type:"http_data",reqId:V,data:Buffer.from(xt).toString("base64")}))break}J({v:Ke,ch:"control",type:"http_end",reqId:V,error:null})}finally{P.delete(V)}}function We(te,V,A){let{reqId:B,method:ye,path:be,body:Ze}=te,kt=new URL(m);return new Promise(Wt=>{let jn=JD.request({host:kt.hostname,port:kt.port,method:ye,path:be,headers:{...V,host:te.host},signal:A.signal},xt=>{let{connection:yU,"keep-alive":bU,"transfer-encoding":kU,upgrade:xU,...lA}=xt.headers;J({v:Ke,ch:"control",type:"http_head",reqId:B,status:xt.statusCode||502,headers:lA}),xt.on("data",uA=>{J({v:Ke,ch:"control",type:"http_data",reqId:B,data:Buffer.from(uA).toString("base64")})||xt.destroy()}),xt.on("end",()=>{J({v:Ke,ch:"control",type:"http_end",reqId:B,error:null}),Wt()}),xt.on("error",()=>{J({v:Ke,ch:"control",type:"http_end",reqId:B,error:"view upstream error"}),Wt()})});jn.on("error",xt=>{J({v:Ke,ch:"control",type:"http_end",reqId:B,error:xt?.message||"view request failed"}),Wt()}),Ze&&jn.write(Buffer.from(Ze,"base64")),jn.end()})}function Ht(){if(Z)return;T=new zm(e,{headers:{authorization:`Bearer ${t}`}}),T.on("open",async()=>{let V=null;try{V=await s?.()||null}catch{V=null}J({v:Ke,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:jt,installKind:p,...typeof d=="function"?{updatePolicy:d()}:d?{updatePolicy:d}:{},providers:ZD(),...V?{agents:V}:{}})}),T.on("message",V=>{let A;try{A=JSON.parse(V)}catch{return}if(A.ch==="ctl"&&A.type==="hello_ok"){j=!0,fe=dv,ie=null,h.log(`[plane-link] connected to plane as ${n}`),A.minNodeVersion&&jt&&wi(jt,A.minNodeVersion)&&h.error(`[plane-link] this node is bostrat-node ${jt}; the plane asks for \u2265${A.minNodeVersion} \u2014 update with: ${ql(p)}`);try{u?.(typeof A.updatePolicy=="string"?A.updatePolicy:null)}catch{}let B=typeof A.latestNodeVersion=="string"?A.latestNodeVersion:null;if(B&&jt&&wi(jt,B)&&!x.has(B)){x.add(B),h.log(`[plane-link] bostrat-node ${B} is available (this node runs ${jt}) \u2014 ${ql(p)}`);try{l?.({current:jt,latest:B,min:A.minNodeVersion??null,installKind:p,required:!!(A.minNodeVersion&&wi(jt,A.minNodeVersion))})}catch(ye){h.error(`[plane-link] update notice failed: ${ye?.message||ye}`)}}U&&clearInterval(U),U=setInterval(()=>{J({v:Ke,ch:"ctl",type:"ping"}),w()},rB),U.unref?.(),$(),ve().catch(ye=>h.error("[plane-link] backfill failed:",ye?.message||ye));return}if(A.ch==="ctl"&&A.type==="hello_refused"){let B=typeof A.minNodeVersion=="string"?A.minNodeVersion:null,ye=typeof A.latestNodeVersion=="string"?A.latestNodeVersion:null;h.error(`[plane-link] plane REFUSED this node: bostrat-node ${jt??"?"} is below its minimum ${B??"?"} \u2014 ${A.fix||ql(p)}`),fe=fv,ie=0;try{f?.({current:jt,min:B,latest:ye,fix:A.fix||ql(p),installKind:p})}catch(be){h.error(`[plane-link] refusal handler failed: ${be?.message||be}`)}return}if(A.ch==="ctl"&&A.type==="update_policy"){try{u?.(typeof A.mode=="string"?A.mode:null)}catch{}return}if(A.ch==="ctl"&&A.type==="usage_caps"){Hc(A.caps??{},{source:"plane",updatedAt:A.updatedAt??Date.now()}).then(B=>h.log(`[plane-link] spend caps ${Object.keys(B.caps).length?`updated (${Object.keys(B.caps).join(", ")})`:"cleared"} from plane`)).catch(B=>h.error("[plane-link] spend-caps push rejected:",B?.message||B));return}if(A.ch==="ctl"&&A.type==="transferred"&&Array.isArray(A.tracks)){Promise.resolve().then(()=>a?.(A.tracks)).catch(B=>h.error("[plane-link] transfer notice failed:",B?.message||B));return}if(A.ch==="events"&&A.type==="ack"&&Array.isArray(A.ids)){let B=new Set;for(let ye of A.ids){let be=C.get(ye);be&&(C.delete(ye),(N[be.trackId]??0)<be.seq&&(N[be.trackId]=be.seq),B.add(be.trackId))}for(let ye of B)y(ye);return}if(A.ch==="control"&&A.type==="http_open"){Me(A).catch(B=>J({v:Ke,ch:"control",type:"http_end",reqId:A.reqId,error:B?.message||"tunnel error"}));return}if(A.ch==="control"&&A.type==="http_cancel"){P.get(A.reqId)?.abort();return}if(A.ch==="attach"&&A.type==="open"){let B=new zm(g+A.path,Array.isArray(A.protocols)?A.protocols:[],A.host?{headers:{host:A.host}}:void 0);k.set(A.streamId,B),E.set(A.streamId,[]),B.on("open",()=>{let be=E.get(A.streamId)||[];E.delete(A.streamId);for(let{buf:Ze,bin:kt}of be)B.send(Ze,{binary:kt})}),B.on("message",(be,Ze)=>{let kt=Ze?be:Buffer.from(String(be));J({v:Ke,ch:"attach",type:"data",streamId:A.streamId,data:kt.toString("base64"),...Ze?{bin:!0}:{}})});let ye=()=>{E.delete(A.streamId),k.delete(A.streamId)&&J({v:Ke,ch:"attach",type:"close",streamId:A.streamId})};B.on("close",ye),B.on("error",ye);return}if(A.ch==="attach"&&A.type==="data"){let B=k.get(A.streamId);if(!B)return;let ye=Buffer.from(A.data,"base64"),be=A.bin===!0,Ze=E.get(A.streamId);Ze?Ze.push({buf:ye,bin:be}):B.send(ye,{binary:be});return}if(A.ch==="attach"&&A.type==="close"){let B=k.get(A.streamId);k.delete(A.streamId),E.delete(A.streamId),B?.close();return}});let te=()=>{j&&h.log("[plane-link] disconnected from plane"),j=!1,U&&(clearInterval(U),U=null),pe&&(clearInterval(pe),pe=null),C.clear(),N={};for(let B of k.values())B.close();k.clear(),E.clear();for(let B of P.values())B.abort();if(P.clear(),Z||D)return;ie==null&&(ie=Date.now());let V=Date.now()-ie<QD,A=V?eB+Math.random()*tB:fe;D=setTimeout(()=>{D=null,Ht()},A),D.unref?.(),V||(fe=Math.min(fe*2,fv))};T.on("close",te),T.on("error",V=>{let A=/Unexpected server response: (\d+)/.exec(V?.message||"")?.[1];(A==="401"||A==="403")&&Date.now()-He>=aB&&(He=Date.now(),h.error(`[plane-link] plane refused this node's credentials (HTTP ${A}) \u2014 the token was revoked or is unknown to the plane; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN (still retrying)`)),te()})}return st.on("event",b),le().then(Ht),{stop:()=>{Z=!0,st.off("event",b),D&&clearTimeout(D),R&&clearTimeout(R),U&&clearInterval(U),pe&&clearInterval(pe),J({v:Ke,ch:"ctl",type:"bye",reason:"shutdown"}),T?.close()},status:()=>({connected:j,nodeId:n,cursor:{...M},inflight:C.size})}}bc();var Vl=(()=>{try{return lB(import.meta.url)("../package.json").version||null}catch{return null}})(),uB=process.env.BOSTRAT_STATE_DIR||Ge.join(process.env.HOME||iB.tmpdir(),".bostrat"),wv=Ge.join(uB,"self-update"),zl=Ge.join(wv,"status.json"),dB=1440*6e4,fB=6e4,Jm=new Set(["npm-global","git-checkout"]);function gv({kind:e=lo(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:Jm.has(e)?e==="git-checkout"||pB(t)?"auto":"prompt":"off"}function pB(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var yt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function mB({kind:e,to:t,from:n=Vl,statusFile:r=zl,logFile:o=Ge.join(wv,"last.log"),port:s=Number(process.env.PORT)||3362,npmPrefix:a=null,serviceName:i=process.platform==="darwin"?"com.bostrat.node":"bostrat-node.service",uid:c=typeof process.getuid=="function"?process.getuid():0,liveDir:l=process.env.BOSTRAT_LIVE_CHECKOUT||Ge.resolve(Ge.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!Jm.has(e))throw new Error(`self-update: install kind "${e}" is not actionable here`);if(!/^\d+\.\d+\.\d+$/.test(String(t)))throw new Error(`self-update: bad target version "${t}"`);if(n&&!/^\d+\.\d+\.\d+$/.test(String(n)))throw new Error(`self-update: bad current version "${n}"`);let f=(g,x="")=>`write_status ${yt(g)} ${x}`,d=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${yt(i)}`,p=`
|
|
496
|
+
`);YD=new Set(["claude","codex","cursor"]);fi=new Set,XD=e=>fi.has(e);ZD=9e4;VS={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};dm=new Map;gm=120;qm=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:Gs(e.hookToken),createdAt:Date.now()});As=new Map,Rs=e=>As.get(e)||(Et?re(Et,e)?.handoff:null)||null;ym=6e4,Mn=(e,t,n=15e3)=>YS("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),bm=new Map,Gm=e=>bm.get(e)||null;fm=new Map;sB=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,aB=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,Es=G.join(sn,"worktrees"),ks=[ii,Dl],wt=new Map,iB=e=>wt.get(e)||(Et?Et.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,fo=e=>{let t=new Map([...wt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Et?re(Et,e):null;if(n?.subtasks)for(let[r,o]of Object.entries(n.subtasks))t.has(r)||t.set(r,o);return[...t.values()].sort((r,o)=>r.startedAt-o.startedAt)},Yl=e=>({id:e.id,trackId:e.trackId,trackName:e.trackName,agent:e.agent,task:e.task,skipPermissions:e.skipPermissions,reason:e.reason||null,step:e.step,failedStep:e.failedStep||null,box:e.box,branch:e.branch,base:e.base,repos:e.repos||null,dir:e.dir,stat:e.stat,files:e.files||null,conflicts:e.conflicts||[],applied:e.applied,error:e.error,canceled:e.canceled,startedAt:e.startedAt,finishedAt:e.finishedAt});xi=e=>yv(e.trackId,e.id,Yl(e));Zm=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",mi=class extends Error{}});Ve();import ge from"node:path";import Xe from"node:fs";import xU from"node:readline";import{fileURLToPath as SU}from"node:url";import $t from"express";import vU from"node:http";import TU from"node:https";import uw from"node:os";import{WebSocketServer as AU}from"ws";Ve();import CI from"node-pty";async function Tg(e,t,n={}){if(!_e(t)){vc(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await zn()){vc(e,`host tmux server not running on ${ft}`),e.close(1011,"tmux server unavailable");return}if(!await ae(t)){vc(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=vg(n.cols,120,Ag),o=vg(n.rows,32,Eg),s;try{s=CI.spawn(F,["-S",ft,"attach-session","-d","-t",t],{name:"xterm-256color",cols:r,rows:o,cwd:process.env.HOME||"/",env:{...process.env,TERM:"xterm-256color"}})}catch(a){vc(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&pd(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(pd(e,{type:"exit",code:a??null,signal:i??null}),e.close(1e3,"tmux exited"))}),e.on("message",a=>{let i;try{i=JSON.parse(a.toString())}catch{return}if(!(!i||typeof i!="object")){if(i.type==="data"&&typeof i.data=="string")s.write(i.data);else if(i.type==="resize"&&DI(i.cols,i.rows))try{s.resize(Math.min(500,i.cols|0),Math.min(500,i.rows|0))}catch{}}}),e.on("close",()=>{try{s.kill()}catch{}PI(t)})}var MI=6,NI=250;async function PI(e,t=MI,n=NI){for(let r=0;r<t;r++){if(await Dr(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var Ag=20,Eg=6;function DI(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=Ag&&Number(t)>=Eg}function vg(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function pd(e,t){try{e.send(JSON.stringify(t))}catch{}}function vc(e,t){pd(e,{type:"error",message:t})}function _g(e){let t=typeof e=="string"&&e?e:"/",n=t.indexOf("?"),r=n===-1?t:t.slice(0,n),o=n===-1?"":t.slice(n+1),s=r.match(/^\/api\/attach\/([^/]+)$/);if(!s)return{ok:!1,status:404};let a;try{a=decodeURIComponent(s[1])}catch{return{ok:!1,status:400}}let i=new URLSearchParams(o);return{ok:!0,sessionName:a,cols:Number(i.get("cols"))||void 0,rows:Number(i.get("rows"))||void 0}}Pt();Ac();sf();hr();import{createRequire as EU}from"node:module";var gB=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function nh({tracks:e=[],views:t=[],queuedOf:n=()=>[]}={}){let r=[];for(let o of e)!o||o.archivedAt&&!o.archiving||o.transferredTo||(o.working&&r.push(`working:${o.id}`),o.prompted&&r.push(`prompted:${o.id}`),(n(o.id)??[]).length&&r.push(`queued:${o.id}`),(o.boxes||[]).some(s=>s?.role==="subtask"&&s?.state==="live")&&r.push(`subtask:${o.id}`),Object.values(o.subtasks||{}).some(gB)&&r.push(`merge-back:${o.id}`));return t.some(o=>o?.state==="live"||o?.state==="starting")&&r.push("views"),{busy:r.length>0,reasons:r}}import $v from"node:fs";import kr from"node:fs/promises";import CB from"node:os";import Ge from"node:path";import{spawn as MB}from"node:child_process";import{createRequire as NB}from"node:module";ue();Xc();import Iv from"node:fs";import oh from"node:fs/promises";import kB from"node:http";import Pn from"node:path";import eu from"node:os";import{createRequire as xB}from"node:module";import sh from"ws";import yB from"node:path";import bB from"node:os";var Si=process.env.BOSTRAT_STATE_DIR||yB.join(process.env.HOME||bB.tmpdir(),".bostrat");function rh(e=process.env){let t=(e.BOSTRAT_HOST||"").trim();if(t!=="all")return t||(e.BOSTRAT_PLANE_URL?"127.0.0.1":void 0)}function Tv(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=rh(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function Av(e){let t=String(e||"").trim();if(!t)return"";let n=/^[a-z][a-z0-9+.-]*:\/\//i.test(t)?t:`wss://${t}`,r;try{r=new URL(n)}catch{return t}let o={"https:":"wss:","http:":"ws:","wss:":"wss:","ws:":"ws:"}[r.protocol];return o?(r.protocol=o,(r.pathname===""||r.pathname==="/")&&(r.pathname="/v1/node"),r.toString()):t}var Lt=(()=>{try{return xB(import.meta.url)("../package.json").version||null}catch{return null}})(),SB=process.env.BOSTRAT_STATE_DIR||Pn.join(process.env.HOME||eu.tmpdir(),".bostrat");function po({env:e=process.env,pkgDir:t=Pn.resolve(Pn.dirname(new URL(import.meta.url).pathname),".."),exists:n=Iv.existsSync}={}){let r=String(e.BOSTRAT_INSTALL_KIND||"").trim();return["container","npm-global","git-checkout","dev"].includes(r)?r:/\/lib\/node_modules\/bostrat-node(?:\/|$)/.test(t.replace(/\\/g,"/"))?"npm-global":n(Pn.join(t,"..",".git"))?"git-checkout":"dev"}function vi(e,t){let n=String(e).split(".").map(Number),r=String(t).split(".").map(Number);if(n.some(Number.isNaN)||r.some(Number.isNaN))return!1;for(let o=0;o<Math.max(n.length,r.length);o++){let s=(n[o]||0)-(r[o]||0);if(s)return s<0}return!1}function Zl(e){switch(e){case"npm-global":return"npm install -g bostrat-node && restart the bostrat-node service";case"git-checkout":return"scripts/deploy-pi.sh (or The Bakery \u2192 Deploy to Pi \u2192 bostrat)";case"container":return"Links \u2192 Fly \u2192 Update (or the plane's auto-update while the node sleeps)";default:return"update this node's bostrat-node checkout/install"}}function vB({home:e=process.env.HOME||eu.homedir(),env:t=process.env}={}){let n=r=>{try{return Iv.statSync(r),!0}catch{return!1}};return{claude:n(Pn.join(e,".claude",".credentials.json")),codex:n(Pn.join(t.CODEX_HOME||Pn.join(e,".codex"),"auth.json"))}}var Ke=1,Ev=1e3,_v=3e4,TB=6e4,AB=1500,EB=500,Ql=100,_B=2e3,IB=25e3,RB=6e4,$B=6e4,OB=6e4;function Rv({url:e,token:t,nodeId:n=eu.hostname(),name:r=eu.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,metricsMs:c=$B,onUpdateAvailable:l=null,onUpdatePolicy:u=null,onHelloRefused:d=null,updatePolicy:f=null,installKind:p=po(),selfUrl:m=`http://127.0.0.1:${process.env.PORT||3362}`,log:h=console}){if(!e||!t)throw new Error("plane-link: url and token are required");if(typeof o!="function")throw new Error("plane-link: listTracks is required");e=Av(e);let g=m.replace(/^http/,"ws"),x=new Set,k=new Map,A=new Map,I=Pn.join(SB,"plane-link","cursor.json"),M={},N={},O=new Map,T=null,j=!1,Z=!1,fe=Ev,ie=null,D=null,R=null,U=null,pe=null,Se=!1,He=0;async function le(){try{M=JSON.parse(await oh.readFile(I,"utf8"))}catch{M={}}}function q(){Se=!0,!R&&(R=setTimeout(async()=>{if(R=null,!!Se){Se=!1;try{await oh.mkdir(Pn.dirname(I),{recursive:!0}),await oh.writeFile(I,JSON.stringify(M))}catch(te){h.error("[plane-link] cursor flush failed:",te?.message||te)}}},_B),R.unref?.())}function J(te){return!T||T.readyState!==sh.OPEN?!1:(T.send(JSON.stringify(te)),!0)}function ee(te){let V=Date.now();for(let{trackId:E,event:B}of te){let ye=O.get(B.id);O.set(B.id,{trackId:E,seq:B.seq,event:B,sentAt:V,tries:(ye?.tries??0)+1})}return J({v:Ke,ch:"events",type:"append",events:te})}function Ze(te){let V=null;for(let E of O.values())E.trackId===te&&(V==null||E.seq<V)&&(V=E.seq);return V}function y(te){let V=N[te];if(V==null)return;let E=Ze(te),B=E==null?V:Math.min(V,E-1);B>(M[te]??0)&&(M[te]=B,q())}function w(){if(!j)return;let te=Date.now(),V=[...O.values()].filter(E=>E.event&&te-E.sentAt>=RB);if(V.length){V.sort((E,B)=>E.trackId===B.trackId?E.seq-B.seq:E.trackId<B.trackId?-1:1),h.log(`[plane-link] re-sending ${V.length} unacked event(s) the plane never confirmed`);for(let E=0;E<V.length;E+=Ql){let B=V.slice(E,E+Ql).map(ye=>({trackId:ye.trackId,event:ye.event}));if(!ee(B))return}}}async function v(){if(!i)return;let te;try{te=await i()}catch(V){h.error("[plane-link] metrics sample failed:",V?.message||V);return}te&&J({v:Ke,ch:"ctl",type:"metrics",metrics:te})}function $(){i&&(pe&&clearInterval(pe),v(),pe=setInterval(v,c),pe.unref?.())}async function ve(){let te;try{te=await o()}catch(V){h.error("[plane-link] backfill listTracks failed:",V?.message||V);return}J({v:Ke,ch:"ctl",type:"tracks",ids:te.filter(V=>!V.transferredTo).map(V=>V.id).filter(V=>typeof V=="string")});for(let V of te){if(!j)return;let E;try{E=await L(V)}catch{continue}let B=M[V.id]??0,ye=E.filter(be=>be.seq>B&&typeof be.id=="string");for(let be=0;be<ye.length;be+=Ql){let Qe=ye.slice(be,be+Ql).map(kt=>({trackId:V.id,event:kt}));if(!ee(Qe))return}}}function b(te){!j||!te?.trackId||typeof te.id!="string"||ee([{trackId:te.trackId,event:te}])}let P=new Map;async function Me(te){let{reqId:V,method:E,path:B,headers:ye={},body:be}=te,Qe=Object.fromEntries(Object.entries(ye).filter(([,Wt])=>Wt!=null)),kt=new AbortController;P.set(V,kt);try{if(te.host){await We(te,Qe,kt);return}let Wt=await fetch(m+B,{method:E,headers:Qe,body:be?Buffer.from(be,"base64"):void 0,signal:kt.signal}),Ln=Object.fromEntries(Wt.headers);for(let xt of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Ln[xt];if(Ln["content-type"]||(Ln["content-type"]="application/octet-stream"),J({v:Ke,ch:"control",type:"http_head",reqId:V,status:Wt.status,headers:Ln}),Wt.body){for await(let xt of Wt.body)if(!J({v:Ke,ch:"control",type:"http_data",reqId:V,data:Buffer.from(xt).toString("base64")}))break}J({v:Ke,ch:"control",type:"http_end",reqId:V,error:null})}finally{P.delete(V)}}function We(te,V,E){let{reqId:B,method:ye,path:be,body:Qe}=te,kt=new URL(m);return new Promise(Wt=>{let Ln=kB.request({host:kt.hostname,port:kt.port,method:ye,path:be,headers:{...V,host:te.host},signal:E.signal},xt=>{let{connection:WU,"keep-alive":qU,"transfer-encoding":KU,upgrade:GU,...AE}=xt.headers;J({v:Ke,ch:"control",type:"http_head",reqId:B,status:xt.statusCode||502,headers:AE}),xt.on("data",EE=>{J({v:Ke,ch:"control",type:"http_data",reqId:B,data:Buffer.from(EE).toString("base64")})||xt.destroy()}),xt.on("end",()=>{J({v:Ke,ch:"control",type:"http_end",reqId:B,error:null}),Wt()}),xt.on("error",()=>{J({v:Ke,ch:"control",type:"http_end",reqId:B,error:"view upstream error"}),Wt()})});Ln.on("error",xt=>{J({v:Ke,ch:"control",type:"http_end",reqId:B,error:xt?.message||"view request failed"}),Wt()}),Qe&&Ln.write(Buffer.from(Qe,"base64")),Ln.end()})}function Ht(){if(Z)return;T=new sh(e,{headers:{authorization:`Bearer ${t}`}}),T.on("open",async()=>{let V=null;try{V=await s?.()||null}catch{V=null}J({v:Ke,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:Lt,installKind:p,...typeof f=="function"?{updatePolicy:f()}:f?{updatePolicy:f}:{},providers:vB(),...V?{agents:V}:{}})}),T.on("message",V=>{let E;try{E=JSON.parse(V)}catch{return}if(E.ch==="ctl"&&E.type==="hello_ok"){j=!0,fe=Ev,ie=null,h.log(`[plane-link] connected to plane as ${n}`),E.minNodeVersion&&Lt&&vi(Lt,E.minNodeVersion)&&h.error(`[plane-link] this node is bostrat-node ${Lt}; the plane asks for \u2265${E.minNodeVersion} \u2014 update with: ${Zl(p)}`);try{u?.(typeof E.updatePolicy=="string"?E.updatePolicy:null)}catch{}let B=typeof E.latestNodeVersion=="string"?E.latestNodeVersion:null;if(B&&Lt&&vi(Lt,B)&&!x.has(B)){x.add(B),h.log(`[plane-link] bostrat-node ${B} is available (this node runs ${Lt}) \u2014 ${Zl(p)}`);try{l?.({current:Lt,latest:B,min:E.minNodeVersion??null,installKind:p,required:!!(E.minNodeVersion&&vi(Lt,E.minNodeVersion))})}catch(ye){h.error(`[plane-link] update notice failed: ${ye?.message||ye}`)}}U&&clearInterval(U),U=setInterval(()=>{J({v:Ke,ch:"ctl",type:"ping"}),w()},IB),U.unref?.(),$(),ve().catch(ye=>h.error("[plane-link] backfill failed:",ye?.message||ye));return}if(E.ch==="ctl"&&E.type==="hello_refused"){let B=typeof E.minNodeVersion=="string"?E.minNodeVersion:null,ye=typeof E.latestNodeVersion=="string"?E.latestNodeVersion:null;h.error(`[plane-link] plane REFUSED this node: bostrat-node ${Lt??"?"} is below its minimum ${B??"?"} \u2014 ${E.fix||Zl(p)}`),fe=_v,ie=0;try{d?.({current:Lt,min:B,latest:ye,fix:E.fix||Zl(p),installKind:p})}catch(be){h.error(`[plane-link] refusal handler failed: ${be?.message||be}`)}return}if(E.ch==="ctl"&&E.type==="update_policy"){try{u?.(typeof E.mode=="string"?E.mode:null)}catch{}return}if(E.ch==="ctl"&&E.type==="usage_caps"){Yc(E.caps??{},{source:"plane",updatedAt:E.updatedAt??Date.now()}).then(B=>h.log(`[plane-link] spend caps ${Object.keys(B.caps).length?`updated (${Object.keys(B.caps).join(", ")})`:"cleared"} from plane`)).catch(B=>h.error("[plane-link] spend-caps push rejected:",B?.message||B));return}if(E.ch==="ctl"&&E.type==="transferred"&&Array.isArray(E.tracks)){Promise.resolve().then(()=>a?.(E.tracks)).catch(B=>h.error("[plane-link] transfer notice failed:",B?.message||B));return}if(E.ch==="events"&&E.type==="ack"&&Array.isArray(E.ids)){let B=new Set;for(let ye of E.ids){let be=O.get(ye);be&&(O.delete(ye),(N[be.trackId]??0)<be.seq&&(N[be.trackId]=be.seq),B.add(be.trackId))}for(let ye of B)y(ye);return}if(E.ch==="control"&&E.type==="http_open"){Me(E).catch(B=>J({v:Ke,ch:"control",type:"http_end",reqId:E.reqId,error:B?.message||"tunnel error"}));return}if(E.ch==="control"&&E.type==="http_cancel"){P.get(E.reqId)?.abort();return}if(E.ch==="attach"&&E.type==="open"){let B=new sh(g+E.path,Array.isArray(E.protocols)?E.protocols:[],E.host?{headers:{host:E.host}}:void 0);k.set(E.streamId,B),A.set(E.streamId,[]),B.on("open",()=>{let be=A.get(E.streamId)||[];A.delete(E.streamId);for(let{buf:Qe,bin:kt}of be)B.send(Qe,{binary:kt})}),B.on("message",(be,Qe)=>{let kt=Qe?be:Buffer.from(String(be));J({v:Ke,ch:"attach",type:"data",streamId:E.streamId,data:kt.toString("base64"),...Qe?{bin:!0}:{}})});let ye=()=>{A.delete(E.streamId),k.delete(E.streamId)&&J({v:Ke,ch:"attach",type:"close",streamId:E.streamId})};B.on("close",ye),B.on("error",ye);return}if(E.ch==="attach"&&E.type==="data"){let B=k.get(E.streamId);if(!B)return;let ye=Buffer.from(E.data,"base64"),be=E.bin===!0,Qe=A.get(E.streamId);Qe?Qe.push({buf:ye,bin:be}):B.send(ye,{binary:be});return}if(E.ch==="attach"&&E.type==="close"){let B=k.get(E.streamId);k.delete(E.streamId),A.delete(E.streamId),B?.close();return}});let te=()=>{j&&h.log("[plane-link] disconnected from plane"),j=!1,U&&(clearInterval(U),U=null),pe&&(clearInterval(pe),pe=null),O.clear(),N={};for(let B of k.values())B.close();k.clear(),A.clear();for(let B of P.values())B.abort();if(P.clear(),Z||D)return;ie==null&&(ie=Date.now());let V=Date.now()-ie<TB,E=V?AB+Math.random()*EB:fe;D=setTimeout(()=>{D=null,Ht()},E),D.unref?.(),V||(fe=Math.min(fe*2,_v))};T.on("close",te),T.on("error",V=>{let E=/Unexpected server response: (\d+)/.exec(V?.message||"")?.[1];(E==="401"||E==="403")&&Date.now()-He>=OB&&(He=Date.now(),h.error(`[plane-link] plane refused this node's credentials (HTTP ${E}) \u2014 the token was revoked or is unknown to the plane; mint a new node token and update this node's BOSTRAT_PLANE_TOKEN (still retrying)`)),te()})}return at.on("event",b),le().then(Ht),{stop:()=>{Z=!0,at.off("event",b),D&&clearTimeout(D),R&&clearTimeout(R),U&&clearInterval(U),pe&&clearInterval(pe),J({v:Ke,ch:"ctl",type:"bye",reason:"shutdown"}),T?.close()},status:()=>({connected:j,nodeId:n,cursor:{...M},inflight:O.size})}}Ec();var tu=(()=>{try{return NB(import.meta.url)("../package.json").version||null}catch{return null}})(),PB=process.env.BOSTRAT_STATE_DIR||Ge.join(process.env.HOME||CB.tmpdir(),".bostrat"),Ov=Ge.join(PB,"self-update"),nu=Ge.join(Ov,"status.json"),DB=1440*6e4,BB=6e4,ah=new Set(["npm-global","git-checkout"]);function Cv({kind:e=po(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:ah.has(e)?e==="git-checkout"||jB(t)?"auto":"prompt":"off"}function jB(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var yt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function LB({kind:e,to:t,from:n=tu,statusFile:r=nu,logFile:o=Ge.join(Ov,"last.log"),port:s=Number(process.env.PORT)||3362,npmPrefix:a=null,serviceName:i=process.platform==="darwin"?"com.bostrat.node":"bostrat-node.service",uid:c=typeof process.getuid=="function"?process.getuid():0,liveDir:l=process.env.BOSTRAT_LIVE_CHECKOUT||Ge.resolve(Ge.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!ah.has(e))throw new Error(`self-update: install kind "${e}" is not actionable here`);if(!/^\d+\.\d+\.\d+$/.test(String(t)))throw new Error(`self-update: bad target version "${t}"`);if(n&&!/^\d+\.\d+\.\d+$/.test(String(n)))throw new Error(`self-update: bad current version "${n}"`);let d=(g,x="")=>`write_status ${yt(g)} ${x}`,f=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${yt(i)}`,p=`
|
|
491
497
|
health() {
|
|
492
498
|
for i in $(seq 1 45); do
|
|
493
499
|
sleep 1
|
|
@@ -511,15 +517,15 @@ write_status() { # <status> [error]
|
|
|
511
517
|
}
|
|
512
518
|
${p}
|
|
513
519
|
`;if(e==="npm-global"){let g=a?Ge.join(a,"bin","npm"):"npm",x=a?`export PATH=${yt(Ge.join(a,"bin"))}:"$PATH"
|
|
514
|
-
`:"",k=a?` --prefix ${yt(a)}`:"",
|
|
515
|
-
${
|
|
516
|
-
if ! ${
|
|
517
|
-
${
|
|
520
|
+
`:"",k=a?` --prefix ${yt(a)}`:"",A=I=>`${yt(g)} install -g "bostrat-node@${I}" --no-audit --no-fund${k}`;return m+x+`
|
|
521
|
+
${d("updating",'"" ')}
|
|
522
|
+
if ! ${A("$TO")}; then
|
|
523
|
+
${d("failed",'"npm install failed"')}
|
|
518
524
|
exit 1
|
|
519
525
|
fi
|
|
520
|
-
${
|
|
521
|
-
if ! ${
|
|
522
|
-
${
|
|
526
|
+
${d("restarting")}
|
|
527
|
+
if ! ${f}; then
|
|
528
|
+
${d("failed",'"service restart failed"')}
|
|
523
529
|
exit 1
|
|
524
530
|
fi
|
|
525
531
|
if health; then
|
|
@@ -527,12 +533,12 @@ if health; then
|
|
|
527
533
|
exit 0
|
|
528
534
|
fi
|
|
529
535
|
# \u2500\u2500 rollback: the same primitive with the previous version \u2500\u2500
|
|
530
|
-
${
|
|
531
|
-
if [ -n "$FROM" ] && ${
|
|
532
|
-
${
|
|
536
|
+
${d("rolling-back",'"agent did not answer on :'+s+' after the update"')}
|
|
537
|
+
if [ -n "$FROM" ] && ${A("$FROM")} && ${f} && health; then
|
|
538
|
+
${d("rolled-back",'"agent did not answer on :'+s+' after the update; rolled back"')}
|
|
533
539
|
exit 1
|
|
534
540
|
fi
|
|
535
|
-
${
|
|
541
|
+
${d("failed",'"agent did not answer after the update AND the rollback"')}
|
|
536
542
|
exit 1
|
|
537
543
|
`}let h=g=>`( cd ${yt(l)} && git fetch -q origin main --tags && git show origin/main:scripts/deploy-pi.sh > "$TMP_SCRIPT" && bash "$TMP_SCRIPT" "v${g}" )`;return m+`
|
|
538
544
|
LIVE=${yt(l)}
|
|
@@ -541,71 +547,73 @@ trap 'rm -f "$TMP_SCRIPT"' EXIT
|
|
|
541
547
|
# "restarting" is written BEFORE deploy-pi.sh runs (it does fetch \u2192 build \u2192
|
|
542
548
|
# restart \u2192 health in one call): the agent it restarts settles "done" on boot,
|
|
543
549
|
# and nothing here may write after that boot or the settle would repeat.
|
|
544
|
-
${
|
|
550
|
+
${d("restarting")}
|
|
545
551
|
if ${h("$TO")}; then
|
|
546
552
|
exit 0
|
|
547
553
|
fi
|
|
548
|
-
${
|
|
554
|
+
${d("rolling-back",'"deploy-pi.sh v$TO failed"')}
|
|
549
555
|
if [ -n "$FROM" ] && ${h("$FROM")}; then
|
|
550
|
-
${
|
|
556
|
+
${d("rolled-back",'"deploy-pi.sh v$TO failed; rolled back to v$FROM"')}
|
|
551
557
|
exit 1
|
|
552
558
|
fi
|
|
553
|
-
${
|
|
559
|
+
${d("failed",'"deploy-pi.sh v$TO failed AND the rollback failed"')}
|
|
554
560
|
exit 1
|
|
555
|
-
`}async function Ss(e=zl){try{return JSON.parse(await yr.readFile(e,"utf8"))}catch{return null}}var hB=new Set(["updating","restarting","rolling-back"]),gi=e=>!!(e&&hB.has(e.status));async function Jl({to:e,from:t=Vl,kind:n=lo(),reason:r="manual",statusFile:o=zl,spawnImpl:s=cB,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!Jm.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!wi(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await Ss(o);if(gi(l))return{ok:!1,reason:`an update to ${l.to} is already in flight`,status:l};if(l&&l.to===e&&(l.status==="failed"||l.status==="rolled-back"))return{ok:!1,reason:`update to ${e} already ${l.status} (${l.error||"no detail"}) \u2014 retried when a newer release exists`,status:l};await yr.mkdir(Ge.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?Sd(await wB())||Ge.resolve(Ge.dirname(process.execPath),".."):null,f=mB({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),d=Ge.join(Ge.dirname(o),`update-${e}.sh`),p=Ge.join(Ge.dirname(o),"last.log");await yr.writeFile(d,f,{mode:448}),await yr.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let m=hv.existsSync("/run/systemd/system")&&await gB("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",d]]:["setsid",["-f","bash","-c",`exec bash ${yt(d)} >> ${yt(p)} 2>&1`]];try{s(m[0],m[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(h){return await yr.writeFile(o,JSON.stringify({status:"failed",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a(),error:`could not spawn updater: ${h?.message||h}`})),{ok:!1,reason:`could not spawn updater: ${h?.message||h}`}}return c.log?.(`[self-update] ${t??"?"} \u2192 ${e} (${n}, ${r}) started \u2014 status ${o}`),{ok:!0,to:e,from:t,kind:n,scriptFile:d,statusFile:o}}async function wB(){try{return await yr.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function gB(e){for(let t of String(process.env.PATH||"").split(Ge.delimiter))try{return await yr.access(Ge.join(t,e),hv.constants.X_OK),!0}catch{}return!1}var yB=45*6e4;async function yv({statusFile:e=zl,current:t=Vl,now:n=Date.now,staleMs:r=yB}={}){let o=await Ss(e);if(!o)return null;let s=a=>yr.writeFile(e,JSON.stringify(a)).catch(()=>{});if(gi(o)){if(t&&o.to===t){let a={...o,status:"done",updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}if(n()-Number(o.updatedAt||o.startedAt||0)>r){let a={...o,status:"failed",error:`updater went silent while ${o.status} (no status change for ${Math.round(r/6e4)} min)`,updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}return null}if(["failed","rolled-back","done"].includes(o.status)&&!o.announced){let a={...o,announced:!0};return await s(a),a}return null}function bv({isBusy:e,start:t=Jl,onStuck:n=null,onStarted:r=null,tickMs:o=fB,stuckMs:s=dB,now:a=Date.now,setIntervalImpl:i=setInterval,clearIntervalImpl:c=clearInterval,log:l=console}={}){let u=null,f=()=>{u?.timer&&c(u.timer),u=null},d=async()=>{if(!u)return;let p=!0;try{p=await e()!==!1}catch{p=!0}if(p){if(!u.stuckFired&&a()-u.since>=s){u.stuckFired=!0;try{await n?.({to:u.to,from:u.from,since:u.since})}catch{}}return}let m=u;f();let h=await t({to:m.to,from:m.from,kind:m.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${h.ok?"started":`not started: ${h.reason}`}`);try{await r?.({...m,result:h})}catch{}};return{arm({to:p,from:m=Vl,kind:h=lo()}){return u?.to===p?!1:(f(),u={to:p,from:m,kind:h,since:a(),stuckFired:!1,timer:i(d,o)},u.timer.unref?.(),!0)},tick:d,cancel:f,get armed(){return u?{to:u.to,from:u.from,kind:u.kind,since:u.since}:null}}}ue();Qe();var bB=(...e)=>fetch(...e);function kB(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Ym(e=process.env){if(!kB(e))throw new Error("no plane configured \u2014 circuits live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let t=new AbortController,n=setTimeout(()=>t.abort(),1e4);try{let r=await bB(`${ke(e)}/v1/circuits`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN}`},signal:t.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return Array.isArray(o.circuits)?o.circuits:[]}finally{clearTimeout(n)}}async function vs(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await Ym(t),o=r.find(c=>c.id===n);if(o)return o;let s=n.toLowerCase(),a=r.filter(c=>c.name.toLowerCase()===s);if(a.length===1)return a[0];if(a.length>1)throw new Error(`"${n}" names ${a.length} circuits \u2014 use an id (bostrat-circuit ls)`);let i=r.filter(c=>c.name.toLowerCase().includes(s)).slice(0,5);throw new Error(i.length?`no circuit named "${n}" \u2014 near matches: ${i.map(c=>`${c.name} (${c.id})`).join(", ")}`:`no circuit named "${n}" \u2014 create it on the Circuits page first (bostrat-circuit ls lists yours)`)}async function Yl(e,t,{source:n="chat"}={}){return _(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}ue();Qe();var xB=(...e)=>fetch(...e);async function Xl(e,t,n,r=process.env){if(!(ke(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 blueprints live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await xB(`${ke(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}function SB(e,t=process.env){return Xl("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function kv({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await SB(t,o):await vs(e,o),a=await Xl("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function Xm(e,t=process.env){let n=await vs(e,t),r=await Xl("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function Zm(e,t=process.env){return(await Xl("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function xv(e,t,n,r=process.env){let{blueprint:o,items:s}=await Xm(t,r),a=new Map(s.map(c=>[c.itemId,c])),i=n.filter(c=>!a.get(c)?.bindable);if(i.length){let c=s.filter(l=>l.bindable).map(l=>l.itemId);throw new Error(`unknown/unbindable item(s): ${i.join(", ")} \u2014 bindable ids: ${c.join(", ")||"(none)"}`)}return await _(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}ue();Qe();var vB=(...e)=>fetch(...e);async function TB(e,t,n,r=process.env){if(!(ke(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 the decision ledger lives on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await vB(`${ke(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}async function Sv(e,{title:t,context:n="",decision:r,alternatives:o=[],supersedesId:s=null}){if(!String(t??"").trim())throw new Error("a title is required");if(!String(r??"").trim())throw new Error("the decision itself is required (what was decided)");return await _(e,{type:"decision.recorded",actor:"orchestrator",source:"orchestrator",payload:{title:String(t).trim(),context:String(n??"").trim(),decision:String(r).trim(),alternatives:(Array.isArray(o)?o:[]).map(a=>typeof a=="string"?{label:a}:a).filter(a=>a?.label),...s?{supersedesId:s}:{}}}),{title:String(t).trim()}}function vv(e,t=process.env){return TB("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}Hp();ue();import vi from"node:fs/promises";import au from"node:path";import m1 from"node:os";Wn();import EB from"node:fs/promises";import Tv from"node:path";import AB from"node:os";var Ev=()=>Tv.join(process.env.BOSTRAT_STATE_DIR||Tv.join(process.env.HOME||AB.tmpdir(),".bostrat"),"integrations.json");async function Pn(){try{return JSON.parse(await EB.readFile(Ev(),"utf8"))||{}}catch{return{}}}var Zl=["linked","prOpened","prMerged","create"],Qm=["prompt","auto","off"],_B={create:["prompt","off"]},Av=e=>_B[e]??Qm;function Ql(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of Zl){let s=Av(o),a=s.includes(n)?n:"prompt",i=e.ticketFlow?.[o]??t.ticketFlow?.[o];r[o]=s.includes(i)?i:a}return r}async function uo(){let e=await Pn(),t=process.env.BOSTRAT_LINEAR_API_KEY||e.linear?.apiKey,n=process.env.BOSTRAT_LINEAR_DEFAULT_TEAM||e.linear?.defaultTeam||null;return t?{apiKey:t,ticketFlow:Ql(e),defaultTeam:n}:null}async function eu(){let e=await Pn(),t=e.jira||{},n=process.env.BOSTRAT_JIRA_BASE_URL||t.baseUrl,r=process.env.BOSTRAT_JIRA_EMAIL||t.email,o=process.env.BOSTRAT_JIRA_API_TOKEN||t.apiToken;return!n||!r||!o?null:{baseUrl:String(n).replace(/\/$/,""),email:r,apiToken:o,tokenExpiresAt:t.tokenExpiresAt??null,ticketFlow:Ql(e)}}async function eh(){return Ql(await Pn())}async function _v(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${Zl.join(", ")}`);for(let[r,o]of t){if(!Zl.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${Zl.join(", ")}`);let s=Av(r);if(!s.includes(o))throw new Error(s.length<Qm.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${Qm.join(", ")}`)}let n=await Pn();return n.ticketFlow={...Ql(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await at(Ev(),n,{mode:384}),n.ticketFlow}async function yi(){let e=(await Pn()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function IB(){let e=(await Pn()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var RB={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function tu(){let t=(await Pn()).voiceChat||{},n=t.provider||"openai",r=RB[n];if(!r)return null;let o=n==="openai"?await IB():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function Iv(){let e=(await Pn()).cedar||{},t=process.env.BOSTRAT_CEDAR_INGEST_KEY||e.ingestKey,n=process.env.BOSTRAT_CEDAR_URL||e.url||"https://cedar-api.fly.dev";return t?{url:n,ingestKey:t}:null}async function fo(){let e=(await Pn()).slack||{},t=process.env.BOSTRAT_SLACK_BOT_TOKEN||e.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||e.appToken;return!t||!n?null:{botToken:t,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||e.channel||null,maxConcurrent:Number.isInteger(e.maxConcurrent)&&e.maxConcurrent>0?e.maxConcurrent:2}}ue();Kn();Fr();us();import ki from"node:fs/promises";import Bv from"node:path";import e1 from"node:os";import{randomUUID as jv}from"node:crypto";var nh={};$u(nh,{createComment:()=>jB,findIssueUrl:()=>MB,issueStates:()=>UB,mdToAdf:()=>Ov,moveIssue:()=>HB,parseIssueRef:()=>CB,resolveIssue:()=>NB,uploadImage:()=>LB,upsertCard:()=>PB,whoami:()=>th});var Ts=e=>String(e.baseUrl||"").replace(/\/$/,""),$v=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function po(e,t,n,r){let o=await fetch(`${Ts(e)}${n}`,{method:t,headers:{authorization:$v(e),accept:"application/json",...r!==void 0?{"content-type":"application/json"}:{}},body:r!==void 0?JSON.stringify(r):void 0}),s=await o.text(),a=null;try{a=s?JSON.parse(s):null}catch{a=null}if(!o.ok){let i=a?.errorMessages?.[0]??Object.values(a?.errors??{})[0]??`HTTP ${o.status}`,c=new Error(`jira: ${i}`);throw c.status=o.status,c}return a}var $B=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,OB=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function CB(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match($B);return r?r[1].toUpperCase():null}let n=t.match(OB);return n?n[1]:null}function MB(e,t=null){let n=null;try{n=t?.baseUrl?new URL(t.baseUrl).host:null}catch{n=null}let r=/https?:\/\/[^\s)]+\/browse\/[A-Za-z][A-Za-z0-9]{0,9}-\d+[^\s)]*/g;for(let o of String(e||"").matchAll(r))try{let s=new URL(o[0]).host;if(s===n||/\.atlassian\.net$/i.test(s))return o[0]}catch{}return null}async function NB(e,t){let n;try{n=await po(e,"GET",`/rest/api/3/issue/${encodeURIComponent(t)}?fields=summary,status`)}catch(r){throw r.status===404?new Error(`jira: issue "${t}" not found`):r}return{issueId:n.id,key:n.key,title:n.fields?.summary??null,url:`${Ts(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function PB(e,t,{url:n,title:r,subtitle:o}){await po(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/remotelink`,{globalId:n,application:{type:"com.bostrat.tracks",name:"Bostrat"},object:{url:n,title:r,summary:o,icon:{title:"Bostrat"}}})}var DB=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function BB(e){let t=[],n=0;for(let r of e.matchAll(DB)){r.index>n&&t.push({type:"text",text:e.slice(n,r.index)});let o=r[0];if(o.startsWith("![")){let[,s,a]=o.match(/^!\[([^\]]*)\]\(([^)\s]+)\)$/);t.push({type:"text",text:s||a,marks:[{type:"link",attrs:{href:a}}]})}else o.startsWith("**")?t.push({type:"text",text:o.slice(2,-2),marks:[{type:"strong"}]}):o.startsWith("`")?t.push({type:"text",text:o.slice(1,-1),marks:[{type:"code"}]}):o.startsWith("_")?t.push({type:"text",text:o.slice(1,-1),marks:[{type:"em"}]}):t.push({type:"text",text:o,marks:[{type:"link",attrs:{href:o}}]});n=r.index+o.length}return n<e.length&&t.push({type:"text",text:e.slice(n)}),t}function Ov(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
556
|
-
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...
|
|
561
|
+
`}async function $s(e=nu){try{return JSON.parse(await kr.readFile(e,"utf8"))}catch{return null}}var FB=new Set(["updating","restarting","rolling-back"]),Ti=e=>!!(e&&FB.has(e.status));async function ru({to:e,from:t=tu,kind:n=po(),reason:r="manual",statusFile:o=nu,spawnImpl:s=MB,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!ah.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!vi(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await $s(o);if(Ti(l))return{ok:!1,reason:`an update to ${l.to} is already in flight`,status:l};if(l&&l.to===e&&(l.status==="failed"||l.status==="rolled-back"))return{ok:!1,reason:`update to ${e} already ${l.status} (${l.error||"no detail"}) \u2014 retried when a newer release exists`,status:l};await kr.mkdir(Ge.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?$d(await UB())||Ge.resolve(Ge.dirname(process.execPath),".."):null,d=LB({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),f=Ge.join(Ge.dirname(o),`update-${e}.sh`),p=Ge.join(Ge.dirname(o),"last.log");await kr.writeFile(f,d,{mode:448}),await kr.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let m=$v.existsSync("/run/systemd/system")&&await HB("systemd-run")?["systemd-run",["--user","--collect","--quiet",`--unit=bostrat-self-update-${e}`,"--property=KillMode=process","bash",f]]:["setsid",["-f","bash","-c",`exec bash ${yt(f)} >> ${yt(p)} 2>&1`]];try{s(m[0],m[1],{detached:!0,stdio:"ignore"}).unref?.()}catch(h){return await kr.writeFile(o,JSON.stringify({status:"failed",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a(),error:`could not spawn updater: ${h?.message||h}`})),{ok:!1,reason:`could not spawn updater: ${h?.message||h}`}}return c.log?.(`[self-update] ${t??"?"} \u2192 ${e} (${n}, ${r}) started \u2014 status ${o}`),{ok:!0,to:e,from:t,kind:n,scriptFile:f,statusFile:o}}async function UB(){try{return await kr.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function HB(e){for(let t of String(process.env.PATH||"").split(Ge.delimiter))try{return await kr.access(Ge.join(t,e),$v.constants.X_OK),!0}catch{}return!1}var WB=45*6e4;async function Mv({statusFile:e=nu,current:t=tu,now:n=Date.now,staleMs:r=WB}={}){let o=await $s(e);if(!o)return null;let s=a=>kr.writeFile(e,JSON.stringify(a)).catch(()=>{});if(Ti(o)){if(t&&o.to===t){let a={...o,status:"done",updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}if(n()-Number(o.updatedAt||o.startedAt||0)>r){let a={...o,status:"failed",error:`updater went silent while ${o.status} (no status change for ${Math.round(r/6e4)} min)`,updatedAt:n(),finishedAt:n(),announced:!0};return await s(a),a}return null}if(["failed","rolled-back","done"].includes(o.status)&&!o.announced){let a={...o,announced:!0};return await s(a),a}return null}function Nv({isBusy:e,start:t=ru,onStuck:n=null,onStarted:r=null,tickMs:o=BB,stuckMs:s=DB,now:a=Date.now,setIntervalImpl:i=setInterval,clearIntervalImpl:c=clearInterval,log:l=console}={}){let u=null,d=()=>{u?.timer&&c(u.timer),u=null},f=async()=>{if(!u)return;let p=!0;try{p=await e()!==!1}catch{p=!0}if(p){if(!u.stuckFired&&a()-u.since>=s){u.stuckFired=!0;try{await n?.({to:u.to,from:u.from,since:u.since})}catch{}}return}let m=u;d();let h=await t({to:m.to,from:m.from,kind:m.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${h.ok?"started":`not started: ${h.reason}`}`);try{await r?.({...m,result:h})}catch{}};return{arm({to:p,from:m=tu,kind:h=po()}){return u?.to===p?!1:(d(),u={to:p,from:m,kind:h,since:a(),stuckFired:!1,timer:i(f,o)},u.timer.unref?.(),!0)},tick:f,cancel:d,get armed(){return u?{to:u.to,from:u.from,kind:u.kind,since:u.since}:null}}}ue();et();var qB=(...e)=>fetch(...e);function KB(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function ih(e=process.env){if(!KB(e))throw new Error("no plane configured \u2014 circuits live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let t=new AbortController,n=setTimeout(()=>t.abort(),1e4);try{let r=await qB(`${ke(e)}/v1/circuits`,{headers:{authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN}`},signal:t.signal}),o=await r.json().catch(()=>({}));if(!r.ok)throw new Error(o?.error||`plane answered ${r.status}`);return Array.isArray(o.circuits)?o.circuits:[]}finally{clearTimeout(n)}}async function Os(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await ih(t),o=r.find(c=>c.id===n);if(o)return o;let s=n.toLowerCase(),a=r.filter(c=>c.name.toLowerCase()===s);if(a.length===1)return a[0];if(a.length>1)throw new Error(`"${n}" names ${a.length} circuits \u2014 use an id (bostrat-circuit ls)`);let i=r.filter(c=>c.name.toLowerCase().includes(s)).slice(0,5);throw new Error(i.length?`no circuit named "${n}" \u2014 near matches: ${i.map(c=>`${c.name} (${c.id})`).join(", ")}`:`no circuit named "${n}" \u2014 create it on the Circuits page first (bostrat-circuit ls lists yours)`)}async function ou(e,t,{source:n="chat"}={}){return _(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}ue();et();var GB=(...e)=>fetch(...e);async function su(e,t,n,r=process.env){if(!(ke(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 blueprints live on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await GB(`${ke(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}function VB(e,t=process.env){return su("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function Pv({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await VB(t,o):await Os(e,o),a=await su("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function ch(e,t=process.env){let n=await Os(e,t),r=await su("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function lh(e,t=process.env){return(await su("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function Dv(e,t,n,r=process.env){let{blueprint:o,items:s}=await ch(t,r),a=new Map(s.map(c=>[c.itemId,c])),i=n.filter(c=>!a.get(c)?.bindable);if(i.length){let c=s.filter(l=>l.bindable).map(l=>l.itemId);throw new Error(`unknown/unbindable item(s): ${i.join(", ")} \u2014 bindable ids: ${c.join(", ")||"(none)"}`)}return await _(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}ue();et();var zB=(...e)=>fetch(...e);async function JB(e,t,n,r=process.env){if(!(ke(r)&&r.BOSTRAT_PLANE_TOKEN))throw new Error("no plane configured \u2014 the decision ledger lives on the plane (set BOSTRAT_PLANE_URL/BOSTRAT_PLANE_TOKEN via bostrat-node init)");let o=new AbortController,s=setTimeout(()=>o.abort(),15e3);try{let a=await zB(`${ke(r)}${t}`,{method:e,headers:{authorization:`Bearer ${r.BOSTRAT_PLANE_TOKEN}`,...n?{"content-type":"application/json"}:{}},body:n?JSON.stringify(n):void 0,signal:o.signal}),i=await a.json().catch(()=>({}));if(!a.ok)throw new Error(i?.error||`plane answered ${a.status}`);return i}finally{clearTimeout(s)}}var Bv=72,uh=160;async function jv(e,{title:t,context:n="",decision:r,summary:o,alternatives:s=[],supersedesId:a=null}){let i=String(t??"").trim(),c=String(o??"").trim();if(!i)throw new Error("a title is required");if(i.length>Bv)throw new Error(`the title is the choice as a LABEL \u2014 ${Bv} chars max (yours is ${i.length}); the full story goes in the decision text`);if(!c)throw new Error(`a summary is required \u2014 one sentence (max ${uh} chars) a reader can act on without opening the record`);if(c.length>uh)throw new Error(`the summary is one sentence \u2014 ${uh} chars max (yours is ${c.length}); the full story goes in the decision text`);if(!String(r??"").trim())throw new Error("the decision itself is required (what was decided)");return await _(e,{type:"decision.recorded",actor:"orchestrator",source:"orchestrator",payload:{title:i,context:String(n??"").trim(),decision:String(r).trim(),summary:c,alternatives:(Array.isArray(s)?s:[]).map(l=>typeof l=="string"?{label:l}:l).filter(l=>l?.label),...a?{supersedesId:a}:{}}}),{title:i}}function Lv(e,t=process.env){return JB("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}Qp();ue();import $i from"node:fs/promises";import mu from"node:path";import L1 from"node:os";qn();import YB from"node:fs/promises";import Fv from"node:path";import XB from"node:os";var Uv=()=>Fv.join(process.env.BOSTRAT_STATE_DIR||Fv.join(process.env.HOME||XB.tmpdir(),".bostrat"),"integrations.json");async function Dn(){try{return JSON.parse(await YB.readFile(Uv(),"utf8"))||{}}catch{return{}}}var au=["linked","prOpened","prMerged","create"],dh=["prompt","auto","off"],ZB={create:["prompt","off"]},Hv=e=>ZB[e]??dh;function iu(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of au){let s=Hv(o),a=s.includes(n)?n:"prompt",i=e.ticketFlow?.[o]??t.ticketFlow?.[o];r[o]=s.includes(i)?i:a}return r}async function mo(){let e=await Dn(),t=process.env.BOSTRAT_LINEAR_API_KEY||e.linear?.apiKey,n=process.env.BOSTRAT_LINEAR_DEFAULT_TEAM||e.linear?.defaultTeam||null;return t?{apiKey:t,ticketFlow:iu(e),defaultTeam:n}:null}async function cu(){let e=await Dn(),t=e.jira||{},n=process.env.BOSTRAT_JIRA_BASE_URL||t.baseUrl,r=process.env.BOSTRAT_JIRA_EMAIL||t.email,o=process.env.BOSTRAT_JIRA_API_TOKEN||t.apiToken;return!n||!r||!o?null:{baseUrl:String(n).replace(/\/$/,""),email:r,apiToken:o,tokenExpiresAt:t.tokenExpiresAt??null,ticketFlow:iu(e)}}async function fh(){return iu(await Dn())}async function Wv(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${au.join(", ")}`);for(let[r,o]of t){if(!au.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${au.join(", ")}`);let s=Hv(r);if(!s.includes(o))throw new Error(s.length<dh.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${dh.join(", ")}`)}let n=await Dn();return n.ticketFlow={...iu(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await it(Uv(),n,{mode:384}),n.ticketFlow}async function Ai(){let e=(await Dn()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function QB(){let e=(await Dn()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var e1={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function lu(){let t=(await Dn()).voiceChat||{},n=t.provider||"openai",r=e1[n];if(!r)return null;let o=n==="openai"?await QB():null;return o?{provider:n,model:t.model||r.model,voice:t.voice||r.voice,apiKey:o.apiKey}:null}async function qv(){let e=(await Dn()).cedar||{},t=process.env.BOSTRAT_CEDAR_INGEST_KEY||e.ingestKey,n=process.env.BOSTRAT_CEDAR_URL||e.url||"https://cedar-api.fly.dev";return t?{url:n,ingestKey:t}:null}async function ho(){let e=(await Dn()).slack||{},t=process.env.BOSTRAT_SLACK_BOT_TOKEN||e.botToken,n=process.env.BOSTRAT_SLACK_APP_TOKEN||e.appToken;return!t||!n?null:{botToken:t,appToken:n,channel:process.env.BOSTRAT_SLACK_CHANNEL||e.channel||null,maxConcurrent:Number.isInteger(e.maxConcurrent)&&e.maxConcurrent>0?e.maxConcurrent:2}}ue();Gn();Hr();gs();import _i from"node:fs/promises";import Qv from"node:path";import A1 from"node:os";import{randomUUID as eT}from"node:crypto";var mh={};ju(mh,{createComment:()=>l1,findIssueUrl:()=>o1,issueStates:()=>f1,mdToAdf:()=>Vv,moveIssue:()=>p1,parseIssueRef:()=>r1,resolveIssue:()=>s1,uploadImage:()=>u1,upsertCard:()=>a1,whoami:()=>ph});var Cs=e=>String(e.baseUrl||"").replace(/\/$/,""),Gv=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function wo(e,t,n,r){let o=await fetch(`${Cs(e)}${n}`,{method:t,headers:{authorization:Gv(e),accept:"application/json",...r!==void 0?{"content-type":"application/json"}:{}},body:r!==void 0?JSON.stringify(r):void 0}),s=await o.text(),a=null;try{a=s?JSON.parse(s):null}catch{a=null}if(!o.ok){let i=a?.errorMessages?.[0]??Object.values(a?.errors??{})[0]??`HTTP ${o.status}`,c=new Error(`jira: ${i}`);throw c.status=o.status,c}return a}var t1=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,n1=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function r1(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(t1);return r?r[1].toUpperCase():null}let n=t.match(n1);return n?n[1]:null}function o1(e,t=null){let n=null;try{n=t?.baseUrl?new URL(t.baseUrl).host:null}catch{n=null}let r=/https?:\/\/[^\s)]+\/browse\/[A-Za-z][A-Za-z0-9]{0,9}-\d+[^\s)]*/g;for(let o of String(e||"").matchAll(r))try{let s=new URL(o[0]).host;if(s===n||/\.atlassian\.net$/i.test(s))return o[0]}catch{}return null}async function s1(e,t){let n;try{n=await wo(e,"GET",`/rest/api/3/issue/${encodeURIComponent(t)}?fields=summary,status`)}catch(r){throw r.status===404?new Error(`jira: issue "${t}" not found`):r}return{issueId:n.id,key:n.key,title:n.fields?.summary??null,url:`${Cs(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function a1(e,t,{url:n,title:r,subtitle:o}){await wo(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/remotelink`,{globalId:n,application:{type:"com.bostrat.tracks",name:"Bostrat"},object:{url:n,title:r,summary:o,icon:{title:"Bostrat"}}})}var i1=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function c1(e){let t=[],n=0;for(let r of e.matchAll(i1)){r.index>n&&t.push({type:"text",text:e.slice(n,r.index)});let o=r[0];if(o.startsWith("![")){let[,s,a]=o.match(/^!\[([^\]]*)\]\(([^)\s]+)\)$/);t.push({type:"text",text:s||a,marks:[{type:"link",attrs:{href:a}}]})}else o.startsWith("**")?t.push({type:"text",text:o.slice(2,-2),marks:[{type:"strong"}]}):o.startsWith("`")?t.push({type:"text",text:o.slice(1,-1),marks:[{type:"code"}]}):o.startsWith("_")?t.push({type:"text",text:o.slice(1,-1),marks:[{type:"em"}]}):t.push({type:"text",text:o,marks:[{type:"link",attrs:{href:o}}]});n=r.index+o.length}return n<e.length&&t.push({type:"text",text:e.slice(n)}),t}function Vv(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
562
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...c1(o))}),{type:"paragraph",content:r.length?r:[{type:"text",text:" "}]}});return{type:"doc",version:1,content:t.length?t:[{type:"paragraph",content:[{type:"text",text:" "}]}]}}async function l1(e,t,n){let r=await wo(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/comment`,{body:Vv(n)});return r?.id?`${Cs(e)}/browse/${t.key}?focusedCommentId=${r.id}`:null}async function ph(e){let t=await wo(e,"GET","/rest/api/3/myself"),n=null;try{n=new URL(Cs(e)).host}catch{n=e.baseUrl??null}return{user:t?.displayName??t?.emailAddress??null,org:n}}async function u1(e,{issue:t,filename:n,contentType:r,buffer:o}){let s=new FormData;s.append("file",new Blob([o],{type:r}),n);let a=await fetch(`${Cs(e)}/rest/api/3/issue/${encodeURIComponent(t)}/attachments`,{method:"POST",headers:{authorization:Gv(e),"x-atlassian-token":"no-check",accept:"application/json"},body:s}),i=await a.json().catch(()=>null),c=Array.isArray(i)?i[0]:null;if(!a.ok||!c)throw new Error(`jira: attachment upload failed (HTTP ${a.status})`);return c.content??`${Cs(e)}/rest/api/3/attachment/content/${c.id}`}var d1={new:"unstarted",indeterminate:"started",done:"completed"},Kv=e=>d1[e?.key]??"started";async function f1(e,t){let n=encodeURIComponent(t.key),[r,o]=await Promise.all([wo(e,"GET",`/rest/api/3/issue/${n}?fields=status`),wo(e,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:Kv(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:Kv(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function p1(e,t,n){await wo(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/transitions`,{transition:{id:String(n)}})}var yh={};ju(yh,{createComment:()=>x1,createIssue:()=>gh,createProject:()=>wh,findIssueUrl:()=>y1,issueStates:()=>v1,moveIssue:()=>T1,parseIssueRef:()=>g1,resolveIssue:()=>b1,teams:()=>Ms,uploadImage:()=>S1,upsertCard:()=>k1,whoami:()=>hh});var m1=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function cn(e,t,n={}){let r=await fetch(m1(),{method:"POST",headers:{"content-type":"application/json",authorization:e.apiKey},body:JSON.stringify({query:t,variables:n})}),o=await r.json().catch(()=>null);if(!r.ok)throw new Error(`linear: HTTP ${r.status}${o?.errors?.[0]?.message?` \u2014 ${o.errors[0].message}`:""}`);if(o?.errors?.length){let s=o.errors[0],a=(s.extensions?.validationErrors??[]).flatMap(i=>Object.values(i?.constraints??{})).join("; ");throw new Error(`linear: ${s.message}${a?` \u2014 ${a}`:""}`)}return o?.data??{}}var h1=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,w1=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function g1(e){let t=String(e||"").trim(),n=t.match(h1);if(n)return n[1].toUpperCase();let r=t.match(w1);return r?r[1]:null}function y1(e){let t=String(e||"").match(/https?:\/\/linear\.app\/[^/\s]+\/issue\/[A-Za-z][A-Za-z0-9]{0,9}-\d+[^\s)]*/);return t?t[0]:null}async function b1(e,t){let r=(await cn(e,"query($id: String!) { issue(id: $id) { id identifier title url state { name } team { key } } }",{id:t})).issue;if(!r)throw new Error(`linear: issue "${t}" not found`);return{issueId:r.id,key:r.identifier,title:r.title,url:r.url,state:r.state?.name??null,team:r.team?.key??null}}async function k1(e,t,{url:n,title:r,subtitle:o}){let s=await cn(e,"mutation($input: AttachmentCreateInput!) { attachmentCreate(input: $input) { success attachment { id } } }",{input:{issueId:t.issueId,url:n,title:r,subtitle:o}});if(!s.attachmentCreate?.success)throw new Error("linear: attachmentCreate did not succeed");return s.attachmentCreate.attachment?.id??null}async function x1(e,t,n){let r=await cn(e,"mutation($input: CommentCreateInput!) { commentCreate(input: $input) { success comment { url } } }",{input:{issueId:t.issueId,body:n}});if(!r.commentCreate?.success)throw new Error("linear: commentCreate did not succeed");return r.commentCreate.comment?.url??null}async function hh(e){let t=await cn(e,"query { viewer { name email } organization { name urlKey } }");return{user:t.viewer?.name??t.viewer?.email??null,org:t.organization?.name??t.organization?.urlKey??null}}async function S1(e,{filename:t,contentType:n,buffer:r}){let o=await cn(e,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
557
563
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
558
564
|
success
|
|
559
565
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
560
566
|
}
|
|
561
|
-
}`,{contentType:n,filename:t,size:r.length}),s=o.fileUpload?.uploadFile;if(!o.fileUpload?.success||!s?.uploadUrl)throw new Error("linear: fileUpload did not succeed");let a={"content-type":n};for(let c of s.headers||[])a[c.key]=c.value;let i=await fetch(s.uploadUrl,{method:"PUT",headers:a,body:r});if(!i.ok)throw new Error(`linear: asset PUT failed (HTTP ${i.status})`);return s.assetUrl}async function ZB(e,t){let n=await sn(e,"query($id: String!) { issue(id: $id) { state { id name type } team { states { nodes { id name type position } } } } }",{id:t.key});if(!n.issue)throw new Error(`linear: issue "${t.key}" not found`);return{current:n.issue.state??null,states:n.issue.team?.states?.nodes??[]}}async function QB(e,t,n){if(!(await sn(e,"mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success } }",{id:t.issueId,input:{stateId:n}})).issueUpdate?.success)throw new Error("linear: issueUpdate did not succeed")}async function Es(e){return((await sn(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var Cv=255;async function oh(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>Cv,a=s?`${o.slice(0,Cv-1).trimEnd()}\u2026`:o,i=await sn(e,"mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id url name } } }",{input:{teamIds:[t],name:n,...a?{description:a}:{},...s?{content:o}:{}}}),c=i.projectCreate?.project;if(!i.projectCreate?.success||!c)throw new Error("linear: projectCreate did not succeed");return{id:c.id,url:c.url,name:c.name}}async function sh(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await sn(e,"mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier url title } } }",{input:{teamId:t,title:n,...r?{description:r}:{},...o?{projectId:o}:{},...Number.isInteger(s)?{priority:s}:{}}}),i=a.issueCreate?.issue;if(!a.issueCreate?.success||!i)throw new Error("linear: issueCreate did not succeed");return{id:i.id,key:i.identifier,url:i.url,title:i.title}}var Lv={linear:{api:ah,config:uo},jira:{api:nh,config:eu}};async function ph(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(Lv)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function mo(e){let t=Lv[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function mh(e,t){if(!e||e.name)return e;let n=t.find(r=>r.id===e.id);return n?{...e,name:n.name}:e}var Fv=()=>process.env.BOSTRAT_STATE_DIR||Bv.join(process.env.HOME||e1.tmpdir(),".bostrat"),uh=()=>Bv.join(Fv(),"tickets.json"),Uv=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),nu=e=>`${Uv()}/track/${e}`,br=null,ih=null,ch=Promise.resolve();async function Si(){return br||(ih??=ki.readFile(uh(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(br=e,!t&&Ee()){let{records:n,source:r}=await je("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;br[s]=i}Object.keys(br).length&&await dh().catch(()=>{})}}return br}).finally(()=>ih=null),ih)}async function dh(){let e=JSON.stringify({tickets:br},null,2),t=`${uh()}.${process.pid}.tmp`;await ki.mkdir(Fv(),{recursive:!0}),await ki.writeFile(t,e),await ki.rename(t,uh())}async function kr(e=null){if(ch=ch.then(dh,dh),await ch.catch(()=>{}),!e)return;let t=br?.[e];t?et("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):$t("tickets",e).catch(()=>{})}async function an(e){return(await Si())[e]??null}async function Hv(){return{...await Si()}}async function Wv(){return(await ph()).length>0}async function As(e,t,{source:n="api"}={}){let r=await ph();if(!r.length)throw new Error("no ticketing integration configured \u2014 add a linear or jira entry to ~/.bostrat/integrations.json");let o=null,s=null,a=null;for(let u of r){let f=u.api.parseIssueRef(t);if(f)try{s=await u.api.resolveIssue(u.cfg,f),o=u;break}catch(d){a=d}}if(!o)throw a||new Error(`"${t}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(e.boxes||[]).find(u=>u.name===e.leadBox),c={provider:o.name,key:s.key,issueId:s.issueId,url:s.url,title:s.title,state:s.state,source:n,linkedAt:Date.now(),track:{name:e.name,branch:e.workspace?.branch??null,leadAgent:i?.agent??null,cwd:e.cwd??null,...e.workspace?.kind==="multi"?{repos:(e.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await Si())[e.id]=c,await kr(e.id),await _({id:e.id,cwd:e.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await xi(e.id,c);let l=[];return c.track.branch&&l.push(`\`${c.track.branch}\``),c.track.repos?.length&&l.push(`repos ${c.track.repos.join(" + ")}`),c.track.leadAgent&&l.push(`lead ${c.track.leadAgent}`),await bi(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await Gv(e.id,c,"linked"),c}async function hh(e){let t=await Si();return t[e]?(delete t[e],await kr(e),!0):!1}var fh=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function qv(e,t){if(!e.track?.cwd)return null;let n=nr(await L({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function t1(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${fh(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${fh(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function n1(e,t){let n=[];return e.track.leadAgent&&n.push(`lead ${e.track.leadAgent}`),e.track.branch&&n.push(e.track.branch),e.track.repos?.length&&n.push(e.track.repos.join(" + ")),e.pr?.number&&n.push(`PR #${e.pr.number} ${e.pr.change==="merged"?"merged":"open"}`),t&&n.push(fh(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function xi(e,t){let n=await mo(t);if(!n)return;let r=await qv(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:nu(e),title:`Bostrat Track: ${t.track.name}`,subtitle:n1(t,r)})}function ru(e){let t=new URL(Uv());return`${t.protocol}//${e}.${t.host}/`}var r1={linked:"linked",opened:"prOpened",merged:"prMerged"};function o1(e,t,n){let r=o=>t.filter(s=>s.type===o).sort((s,a)=>s.position-a.position);if(n==="linked"){if(["triage","backlog","unstarted"].includes(e?.type))return r("started")[0]??null}else if(n==="opened"){if(e?.type!=="completed")return r("started").find(o=>/review/i.test(o.name))??null}else if(n==="merged"&&e?.type!=="completed")return r("completed")[0]??null;return null}async function Kv(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await kr(e),await _({id:e,cwd:t.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:t.key,url:t.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function Gv(e,t,n){let r=await mo(t);if(!r)return;let o=r.cfg.ticketFlow?.[r1[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=mh(s,a),c=o1(i,a,n);if(!c||c.id===i?.id)return;let l=i?.name??t.state??null;if(o==="auto"){await r.api.moveIssue(r.cfg,t,c.id),await Kv(e,t,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await _({id:e,cwd:t.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:jv(),provider:t.provider,key:t.key,url:t.url,phase:n,from:l,to:{id:c.id,name:c.name,type:c.type},...t.pr?.number?{prNumber:t.pr.number}:{}}}).catch(()=>{})}async function Vv(e){let t=await an(e);if(!t)throw new Error("no ticket linked to this Track");let n=await mo(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=mh(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await kr(e)),{key:t.key,url:t.url,current:s?{id:s.id,name:s.name??null,type:s.type??null}:null,states:[...o].sort((a,i)=>a.position-i.position).map(({id:a,name:i,type:c,position:l})=>({id:a,name:i,type:c,position:l}))}}async function wh(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await an(e);if(!r)throw new Error("no ticket linked to this Track");let o=await mo(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=mh(s,a),c=a.find(u=>u.id===t);if(!c)throw new Error("unknown state for this ticket's team");let l=i?.name??r.state??null;return i?.id===c.id?(r.state!==c.name&&(r.state=c.name,await kr(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await Kv(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function zv(e,t){let n=await an(e);if(!n)throw new Error("no ticket linked to this Track");let r=await mo(n);if(!r)throw new Error("no ticketing integration configured");let o=String(t||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await _({id:e,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function Jv(e,t,n){if(!t)throw new Error("offerId required");let r=await L({id:e.id,cwd:e.cwd}),o=r.find(a=>a.type==="ticket.move.offered"&&a.payload?.offerId===t);if(!o)throw new Error("unknown offer");if(r.some(a=>a.type==="ticket.move.answered"&&a.payload?.offerId===t))throw new Error("offer already answered");let s=null;return n&&(s=await wh(e.id,o.payload.to?.id,{mode:"offer"})),await _({id:e.id,cwd:e.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:t,action:n?"moved":"dismiss",...s??{}}}var Mv=50;async function Yv(){let e=await uo();if(!e)throw new Error("no ticketing integration configured");return Es(e)}function s1(e){let t=e&&typeof e=="object"?e:{},n=(Array.isArray(t.issues)?t.issues:[]).map(o=>o&&typeof o=="object"?o:{title:String(o||"")}).map(o=>({title:String(o.title||"").trim(),description:o.description?String(o.description):null,priority:Number.isInteger(o.priority)&&o.priority>=0&&o.priority<=4?o.priority:null})).filter(o=>o.title),r=t.epic&&String(t.epic.name||"").trim()?{name:String(t.epic.name).trim(),description:t.epic.description?String(t.epic.description):null}:null;if(!n.length&&!r)throw new Error("a plan needs an epic or at least one issue");if(n.length>Mv)throw new Error(`too many issues (${n.length}); cap is ${Mv} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function a1(e,t,n,r){let o=r??await Es(e),s=i=>o.find(c=>c.id===i||c.key.toLowerCase()===String(i).toLowerCase()||c.name.toLowerCase()===String(i).toLowerCase())??null;if(t.team){let i=s(t.team);if(i)return i}let a=await an(n.id);if(a?.key){let i=a.key.split("-")[0],c=o.find(l=>l.key===i);if(c)return c}if(e.defaultTeam){let i=s(e.defaultTeam);if(i)return i}return o.length===1?o[0]:null}async function Xv(e,t){let n=await uo();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=s1(t),o=await Es(n),s=await a1(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=jv();return await _({id:e.id,cwd:e.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function Zv(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await L({id:e.id,cwd:e.cwd}),s=o.find(m=>m.type==="ticket.create.offered"&&m.payload?.proposalId===t);if(!s)throw new Error("unknown proposal");if(o.some(m=>m.type==="ticket.create.answered"&&m.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await _({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};let a=await uo();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await Es(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 oh(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 sh(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 _({id:e.id,cwd:e.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,team:{key:c.key,name:c.name},items:l,failed:u}}),await _({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",createdCount:l.length,failedCount:u.length}});let d=l.filter(m=>m.kind==="issue"),p=null;return i.linkCurrentTrack&&d.length===1&&!await an(e.id)&&(p=await As(e,d[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:p?p.key:null}}async function bi(e,t){let n=await mo(e);n&&await n.api.createComment(n.cfg,e,t)}async function i1(e,t,n){let r=await mo(e);if(!r)return null;let o=await En(t);if(!o)return null;let s=Bt(o,n);if(!s)return null;let a=await ki.readFile(Et(o,s.v).png).catch(()=>null);return a?r.api.uploadImage(r.cfg,{issue:e.key,filename:`${o.name.replace(/[^\w.-]+/g,"-")}.v${s.v}.png`,contentType:"image/png",buffer:a}):null}var Nv=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),lh=new Map;function c1(e,t){if(Nv()<=0)return xi(e,t);if(lh.has(e))return;let n=setTimeout(()=>{lh.delete(e),an(e).then(r=>r?xi(e,r):null).catch(()=>{})},Nv());n.unref?.(),lh.set(e,n)}var Pv=new Map;async function Dv(e,t,n){let r=Pv.get(e)??0;Date.now()-r<600*1e3||(Pv.set(e,Date.now()),await _({id:e,cwd:t},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function l1(e,{resolveTrack:t=null}={}){let n=await an(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await ph())if(o=a.api.findIssueUrl(r.text,a.cfg),o)break;if(!o)return;let s=t?await t(e.trackId):null;if(!s)return;try{await As(s,o,{source:"chat"})}catch(a){await Dv(e.trackId,s.cwd,a)}return}if(!n.track.cwd&&t){let o=await t(e.trackId).catch(()=>null);o?.cwd&&(n.track.cwd=o.cwd,await kr(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await kr(e.trackId),await xi(e.trackId,n);break;case"pr.state.changed":{if(!r.change)break;if(r.number&&(n.pr={number:r.number,url:r.url??n.pr?.url??null,change:r.change}),await kr(e.trackId),await xi(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await qv(n,e.trackId).catch(()=>null):null;await bi(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
567
|
+
}`,{contentType:n,filename:t,size:r.length}),s=o.fileUpload?.uploadFile;if(!o.fileUpload?.success||!s?.uploadUrl)throw new Error("linear: fileUpload did not succeed");let a={"content-type":n};for(let c of s.headers||[])a[c.key]=c.value;let i=await fetch(s.uploadUrl,{method:"PUT",headers:a,body:r});if(!i.ok)throw new Error(`linear: asset PUT failed (HTTP ${i.status})`);return s.assetUrl}async function v1(e,t){let n=await cn(e,"query($id: String!) { issue(id: $id) { state { id name type } team { states { nodes { id name type position } } } } }",{id:t.key});if(!n.issue)throw new Error(`linear: issue "${t.key}" not found`);return{current:n.issue.state??null,states:n.issue.team?.states?.nodes??[]}}async function T1(e,t,n){if(!(await cn(e,"mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success } }",{id:t.issueId,input:{stateId:n}})).issueUpdate?.success)throw new Error("linear: issueUpdate did not succeed")}async function Ms(e){return((await cn(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var zv=255;async function wh(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>zv,a=s?`${o.slice(0,zv-1).trimEnd()}\u2026`:o,i=await cn(e,"mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id url name } } }",{input:{teamIds:[t],name:n,...a?{description:a}:{},...s?{content:o}:{}}}),c=i.projectCreate?.project;if(!i.projectCreate?.success||!c)throw new Error("linear: projectCreate did not succeed");return{id:c.id,url:c.url,name:c.name}}async function gh(e,{teamId:t,title:n,description:r,projectId:o,priority:s}){let a=await cn(e,"mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier url title } } }",{input:{teamId:t,title:n,...r?{description:r}:{},...o?{projectId:o}:{},...Number.isInteger(s)?{priority:s}:{}}}),i=a.issueCreate?.issue;if(!a.issueCreate?.success||!i)throw new Error("linear: issueCreate did not succeed");return{id:i.id,key:i.identifier,url:i.url,title:i.title}}var tT={linear:{api:yh,config:mo},jira:{api:mh,config:cu}};async function Ah(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(tT)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function go(e){let t=tT[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function Eh(e,t){if(!e||e.name)return e;let n=t.find(r=>r.id===e.id);return n?{...e,name:n.name}:e}var nT=()=>process.env.BOSTRAT_STATE_DIR||Qv.join(process.env.HOME||A1.tmpdir(),".bostrat"),Sh=()=>Qv.join(nT(),"tickets.json"),rT=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),uu=e=>`${rT()}/track/${e}`,xr=null,bh=null,kh=Promise.resolve();async function Ri(){return xr||(bh??=_i.readFile(Sh(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(xr=e,!t&&Ee()){let{records:n,source:r}=await Fe("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;xr[s]=i}Object.keys(xr).length&&await vh().catch(()=>{})}}return xr}).finally(()=>bh=null),bh)}async function vh(){let e=JSON.stringify({tickets:xr},null,2),t=`${Sh()}.${process.pid}.tmp`;await _i.mkdir(nT(),{recursive:!0}),await _i.writeFile(t,e),await _i.rename(t,Sh())}async function Sr(e=null){if(kh=kh.then(vh,vh),await kh.catch(()=>{}),!e)return;let t=xr?.[e];t?tt("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):Ot("tickets",e).catch(()=>{})}async function ln(e){return(await Ri())[e]??null}async function oT(){return{...await Ri()}}async function sT(){return(await Ah()).length>0}async function Ns(e,t,{source:n="api"}={}){let r=await Ah();if(!r.length)throw new Error("no ticketing integration configured \u2014 add a linear or jira entry to ~/.bostrat/integrations.json");let o=null,s=null,a=null;for(let u of r){let d=u.api.parseIssueRef(t);if(d)try{s=await u.api.resolveIssue(u.cfg,d),o=u;break}catch(f){a=f}}if(!o)throw a||new Error(`"${t}" is not a Linear or Jira issue URL or key (ABC-123)`);let i=(e.boxes||[]).find(u=>u.name===e.leadBox),c={provider:o.name,key:s.key,issueId:s.issueId,url:s.url,title:s.title,state:s.state,source:n,linkedAt:Date.now(),track:{name:e.name,branch:e.workspace?.branch??null,leadAgent:i?.agent??null,cwd:e.cwd??null,...e.workspace?.kind==="multi"?{repos:(e.workspace.repos??[]).map(u=>u?.name).filter(Boolean)}:{}},pr:null};(await Ri())[e.id]=c,await Sr(e.id),await _({id:e.id,cwd:e.cwd},{type:"ticket.linked",actor:"user",source:"orchestrator",payload:{ts:Date.now(),provider:c.provider,key:c.key,url:c.url,title:c.title,linkSource:n}}).catch(()=>{}),await Ii(e.id,c);let l=[];return c.track.branch&&l.push(`\`${c.track.branch}\``),c.track.repos?.length&&l.push(`repos ${c.track.repos.join(" + ")}`),c.track.leadAgent&&l.push(`lead ${c.track.leadAgent}`),await Ei(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await cT(e.id,c,"linked"),c}async function _h(e){let t=await Ri();return t[e]?(delete t[e],await Sr(e),!0):!1}var Th=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function aT(e,t){if(!e.track?.cwd)return null;let n=sr(await L({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function E1(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Th(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${Th(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function _1(e,t){let n=[];return e.track.leadAgent&&n.push(`lead ${e.track.leadAgent}`),e.track.branch&&n.push(e.track.branch),e.track.repos?.length&&n.push(e.track.repos.join(" + ")),e.pr?.number&&n.push(`PR #${e.pr.number} ${e.pr.change==="merged"?"merged":"open"}`),t&&n.push(Th(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function Ii(e,t){let n=await go(t);if(!n)return;let r=await aT(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:uu(e),title:`Bostrat Track: ${t.track.name}`,subtitle:_1(t,r)})}function du(e){let t=new URL(rT());return`${t.protocol}//${e}.${t.host}/`}var I1={linked:"linked",opened:"prOpened",merged:"prMerged"};function R1(e,t,n){let r=o=>t.filter(s=>s.type===o).sort((s,a)=>s.position-a.position);if(n==="linked"){if(["triage","backlog","unstarted"].includes(e?.type))return r("started")[0]??null}else if(n==="opened"){if(e?.type!=="completed")return r("started").find(o=>/review/i.test(o.name))??null}else if(n==="merged"&&e?.type!=="completed")return r("completed")[0]??null;return null}async function iT(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await Sr(e),await _({id:e,cwd:t.track?.cwd},{type:"ticket.state.changed",actor:a,source:"orchestrator",payload:{ts:Date.now(),key:t.key,url:t.url,from:n??null,to:r,mode:o,...s?{phase:s}:{}}}).catch(()=>{})}async function cT(e,t,n){let r=await go(t);if(!r)return;let o=r.cfg.ticketFlow?.[I1[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=Eh(s,a),c=R1(i,a,n);if(!c||c.id===i?.id)return;let l=i?.name??t.state??null;if(o==="auto"){await r.api.moveIssue(r.cfg,t,c.id),await iT(e,t,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await _({id:e,cwd:t.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:eT(),provider:t.provider,key:t.key,url:t.url,phase:n,from:l,to:{id:c.id,name:c.name,type:c.type},...t.pr?.number?{prNumber:t.pr.number}:{}}}).catch(()=>{})}async function lT(e){let t=await ln(e);if(!t)throw new Error("no ticket linked to this Track");let n=await go(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=Eh(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await Sr(e)),{key:t.key,url:t.url,current:s?{id:s.id,name:s.name??null,type:s.type??null}:null,states:[...o].sort((a,i)=>a.position-i.position).map(({id:a,name:i,type:c,position:l})=>({id:a,name:i,type:c,position:l}))}}async function Ih(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await ln(e);if(!r)throw new Error("no ticket linked to this Track");let o=await go(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Eh(s,a),c=a.find(u=>u.id===t);if(!c)throw new Error("unknown state for this ticket's team");let l=i?.name??r.state??null;return i?.id===c.id?(r.state!==c.name&&(r.state=c.name,await Sr(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await iT(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function uT(e,t){let n=await ln(e);if(!n)throw new Error("no ticket linked to this Track");let r=await go(n);if(!r)throw new Error("no ticketing integration configured");let o=String(t||"").trim();if(!o)throw new Error("comment body required");let s=await r.api.createComment(r.cfg,n,o);return await _({id:e,cwd:n.track?.cwd},{type:"ticket.commented",actor:"user",source:"orchestrator",payload:{ts:Date.now(),key:n.key,url:s??n.url,excerpt:o.length>280?`${o.slice(0,279)}\u2026`:o}}).catch(()=>{}),{key:n.key,url:s??n.url}}async function dT(e,t,n){if(!t)throw new Error("offerId required");let r=await L({id:e.id,cwd:e.cwd}),o=r.find(a=>a.type==="ticket.move.offered"&&a.payload?.offerId===t);if(!o)throw new Error("unknown offer");if(r.some(a=>a.type==="ticket.move.answered"&&a.payload?.offerId===t))throw new Error("offer already answered");let s=null;return n&&(s=await Ih(e.id,o.payload.to?.id,{mode:"offer"})),await _({id:e.id,cwd:e.cwd},{type:"ticket.move.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),offerId:t,action:n?"moved":"dismiss",...s?{from:s.from,to:s.to}:{}}}),{offerId:t,action:n?"moved":"dismiss",...s??{}}}var Jv=50;async function fT(){let e=await mo();if(!e)throw new Error("no ticketing integration configured");return Ms(e)}function $1(e){let t=e&&typeof e=="object"?e:{},n=(Array.isArray(t.issues)?t.issues:[]).map(o=>o&&typeof o=="object"?o:{title:String(o||"")}).map(o=>({title:String(o.title||"").trim(),description:o.description?String(o.description):null,priority:Number.isInteger(o.priority)&&o.priority>=0&&o.priority<=4?o.priority:null})).filter(o=>o.title),r=t.epic&&String(t.epic.name||"").trim()?{name:String(t.epic.name).trim(),description:t.epic.description?String(t.epic.description):null}:null;if(!n.length&&!r)throw new Error("a plan needs an epic or at least one issue");if(n.length>Jv)throw new Error(`too many issues (${n.length}); cap is ${Jv} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function O1(e,t,n,r){let o=r??await Ms(e),s=i=>o.find(c=>c.id===i||c.key.toLowerCase()===String(i).toLowerCase()||c.name.toLowerCase()===String(i).toLowerCase())??null;if(t.team){let i=s(t.team);if(i)return i}let a=await ln(n.id);if(a?.key){let i=a.key.split("-")[0],c=o.find(l=>l.key===i);if(c)return c}if(e.defaultTeam){let i=s(e.defaultTeam);if(i)return i}return o.length===1?o[0]:null}async function pT(e,t){let n=await mo();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=$1(t),o=await Ms(n),s=await O1(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=eT();return await _({id:e.id,cwd:e.cwd},{type:"ticket.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:i,provider:"linear",epic:r.epic,issues:r.issues,team:a,needsTeam:!s,linkCurrentTrack:r.linkCurrentTrack}}),{proposalId:i,epic:r.epic,issues:r.issues,team:a,needsTeam:!s}}async function mT(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await L({id:e.id,cwd:e.cwd}),s=o.find(m=>m.type==="ticket.create.offered"&&m.payload?.proposalId===t);if(!s)throw new Error("unknown proposal");if(o.some(m=>m.type==="ticket.create.answered"&&m.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await _({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};let a=await mo();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await Ms(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=[],d=null;if(i.epic){let m=await wh(a,{teamId:c.id,name:i.epic.name,description:i.epic.description});d=m.id,l.push({kind:"epic",title:m.name,url:m.url})}for(let m of i.issues||[])try{let h=await gh(a,{teamId:c.id,title:m.title,description:m.description,projectId:d,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 _({id:e.id,cwd:e.cwd},{type:"ticket.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,team:{key:c.key,name:c.name},items:l,failed:u}}),await _({id:e.id,cwd:e.cwd},{type:"ticket.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",createdCount:l.length,failedCount:u.length}});let f=l.filter(m=>m.kind==="issue"),p=null;return i.linkCurrentTrack&&f.length===1&&!await ln(e.id)&&(p=await Ns(e,f[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:p?p.key:null}}async function Ei(e,t){let n=await go(e);n&&await n.api.createComment(n.cfg,e,t)}async function C1(e,t,n){let r=await go(e);if(!r)return null;let o=await En(t);if(!o)return null;let s=jt(o,n);if(!s)return null;let a=await _i.readFile(At(o,s.v).png).catch(()=>null);return a?r.api.uploadImage(r.cfg,{issue:e.key,filename:`${o.name.replace(/[^\w.-]+/g,"-")}.v${s.v}.png`,contentType:"image/png",buffer:a}):null}var Yv=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),xh=new Map;function M1(e,t){if(Yv()<=0)return Ii(e,t);if(xh.has(e))return;let n=setTimeout(()=>{xh.delete(e),ln(e).then(r=>r?Ii(e,r):null).catch(()=>{})},Yv());n.unref?.(),xh.set(e,n)}var Xv=new Map;async function Zv(e,t,n){let r=Xv.get(e)??0;Date.now()-r<600*1e3||(Xv.set(e,Date.now()),await _({id:e,cwd:t},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function N1(e,{resolveTrack:t=null}={}){let n=await ln(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await Ah())if(o=a.api.findIssueUrl(r.text,a.cfg),o)break;if(!o)return;let s=t?await t(e.trackId):null;if(!s)return;try{await Ns(s,o,{source:"chat"})}catch(a){await Zv(e.trackId,s.cwd,a)}return}if(!n.track.cwd&&t){let o=await t(e.trackId).catch(()=>null);o?.cwd&&(n.track.cwd=o.cwd,await Sr(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Sr(e.trackId),await Ii(e.trackId,n);break;case"pr.state.changed":{if(!r.change)break;if(r.number&&(n.pr={number:r.number,url:r.url??n.pr?.url??null,change:r.change}),await Sr(e.trackId),await Ii(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await aT(n,e.trackId).catch(()=>null):null;await Ei(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
562
568
|
|
|
563
|
-
Agent cost for this Track: ${
|
|
569
|
+
Agent cost for this Track: ${E1(o)}.`:"")),await cT(e.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await C1(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await Ei(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
564
570
|
|
|
565
|
-
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await
|
|
566
|
-
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await
|
|
567
|
-
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${
|
|
571
|
+
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await Ei(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 Ei(n,`**Bostrat** \u2014 live preview **${r.name}**: ${du(r.slug)}
|
|
572
|
+
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await M1(e.trackId,n);break;default:break}}catch(o){let s=t?await t(e.trackId).catch(()=>null):null;await Zv(e.trackId,s?.cwd??null,o)}}function hT({resolveTrack:e}){at.on("event",t=>{N1(t,{resolveTrack:e}).catch(()=>{})})}async function wT(){if(!Ee())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await Fe("tickets");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Ri(),r=0;for(let[o,s]of Object.entries(n))await tt("tickets",{id:o,updatedAt:Date.now(),...s},{trackId:o}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}rs();gs();import P1 from"ws";var gT=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function fu(e,t,n={}){let r=await fetch(`${gT()}/${e}`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json; charset=utf-8"},body:JSON.stringify(n)}),o=await r.json().catch(()=>null);if(!o?.ok)throw new Error(`slack ${e}: ${o?.error||`http ${r.status}`}`);return o}var Rh=(e,t)=>fu("chat.postMessage",e,t),yo=(e,t)=>fu("chat.update",e,t);async function rt(e,t){await fetch(e,{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify(t)}).catch(()=>{})}async function D1(e,t,n={}){let r=await fetch(`${gT()}/${e}`,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/x-www-form-urlencoded; charset=utf-8"},body:new URLSearchParams(n).toString()}),o=await r.json().catch(()=>null);if(!o?.ok)throw new Error(`slack ${e}: ${o?.error||`http ${r.status}`}`);return o}async function yT(e,{filename:t,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await D1("files.getUploadURLExternal",e,{filename:t,length:String(n.length)}),l=await fetch(i,{method:"POST",body:n});if(!l.ok)throw new Error(`slack upload: http ${l.status}`);return fu("files.completeUploadExternal",e,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var B1=2200;function bT({getToken:e,onEnvelope:t,log:n=()=>{}}){let r=!1,o=null,s=null,a=1e3;async function i(){if(r)return;let l;try{let u=await e();if(!u)throw new Error("app token missing");({url:l}=await fu("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new P1(l),o.on("message",u=>{let d;try{d=JSON.parse(u)}catch{return}if(d.type==="hello"){a=1e3,n("socket connected");return}if(d.type==="disconnect"){try{o.close()}catch{}return}if(!d.envelope_id)return;let f=h=>{try{o.send(JSON.stringify(h?{envelope_id:d.envelope_id,payload:h}:{envelope_id:d.envelope_id}))}catch{}},p=!1,m=setTimeout(()=>{p=!0,f()},B1);Promise.resolve().then(()=>t(d)).then(h=>{clearTimeout(m),!p&&(p=!0,f(d.accepts_response_payload?h:void 0))},h=>{clearTimeout(m),p||(p=!0,f()),n(`envelope handler failed: ${h?.message||h}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var ST=()=>process.env.BOSTRAT_STATE_DIR||mu.join(process.env.HOME||L1.tmpdir(),".bostrat"),Nh=()=>mu.join(ST(),"slack.json"),pu=null,$h=null,Oh=Promise.resolve();async function Dh(){return pu||($h??=$i.readFile(Nh(),"utf8").then(e=>JSON.parse(e)?.threads??{}).catch(()=>({})).then(e=>pu=e).finally(()=>$h=null),$h)}async function kT(){let e=JSON.stringify({threads:pu},null,2),t=`${Nh()}.${process.pid}.tmp`;await $i.mkdir(ST(),{recursive:!0}),await $i.writeFile(t,e),await $i.rename(t,Nh())}async function Ft(){Oh=Oh.then(kT,kT),await Oh.catch(()=>{})}async function Bh(e){return(await Dh())[e]??null}async function F1(e,t){let n=await Dh();for(let[r,o]of Object.entries(n))if(o.channel===e&&o.ts===t)return{trackId:r,rec:o};return null}var ce=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");function Oi(e,t){let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function U1(e,t=500){let n=String(e??"").trim().split(/\n\s*\n/)[0]??"";return Oi(n.replace(/\n+/g," "),t)}function vT(e){let t=`*${ce(e.name)}*${e.archivedAt?" \xB7 _archived_":""}`,n=`${ce(e.repoLabel)} \xB7 \`${ce(e.branch||"?")}\` \xB7 lead *${ce(e.lead)}*`,r=e.kickoff?`> ${ce(Oi(e.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${t}
|
|
573
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${uu(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Ph(e,t){await yo(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:vT(t)})}async function Tr(e,t,n,r=void 0){return Rh(e.botToken,{channel:t.channel,thread_ts:t.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var xT=new Set;function vr(e){xT.add(e),e.catch(()=>{}).finally(()=>xT.delete(e))}var Ae=e=>({response_type:"ephemeral",text:e}),H1="*/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._",W1=["claude","cursor","codex"];function Ch(e,t){let n=(t||"").toLowerCase(),r=e.filter(s=>!s.archivedAt),o=r.filter(s=>s.name.toLowerCase().startsWith(n));return o.sort((s,a)=>(a.lastResponseAt??a.createdAt??0)-(s.lastResponseAt??s.createdAt??0)),{match:o[0]??null,live:r}}var Mh=(e,t)=>Ae(`No live Track matches \u201C${ce(t)}\u201D. Live: ${e.map(n=>`\`${ce(n.name)}\``).join(" \xB7 ")||"none"}`);async function TT(e,t,{name:n,repo:r,responseUrl:o}){let s=e.deps;try{let a=(await s.listTracks()).filter(d=>!d.archivedAt);if(a.length>=t.maxConcurrent){await rt(o,{...Ae(`At capacity \u2014 ${a.length} live Tracks (limit ${t.maxConcurrent}). Archive or stop something first.`),replace_original:!0});return}if(!t.channel){await rt(o,{...Ae("No designated channel configured \u2014 set `slack.channel` in integrations.json."),replace_original:!0});return}let i=await s.createTrack({name:n,...r?{repo:r}:{}}),c=i.workspace||{},l={trackId:i.id,channel:t.channel,name:i.name,branch:c.branch||null,repoLabel:c.repo||(c.base?mu.basename(c.base):"fresh workspace"),lead:"claude"},u=await Rh(t.botToken,{channel:t.channel,text:`Track started: ${l.name}`,blocks:vT(l),unfurl_links:!1});l.ts=u.ts,(await Dh())[i.id]=l,await Ft(),await rt(o,{...Ae(`\u2713 *${ce(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await rt(o,{...Ae(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function q1(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.archiveTrack(n.id);let s=await Bh(n.id);s&&(s.archivedAt=Date.now(),await Ft(),await Ph(t,s).catch(()=>{})),await rt(r,{...Ae(`\u2713 *${ce(n.name)}* archived.`),replace_original:!0})}catch(s){await rt(r,{...Ae(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function K1(e,t,{track:n,agent:r,responseUrl:o}){try{await e.deps.startLeadHandoff(n.id,r),await rt(o,{...Ae(`\u2713 Handing *${ce(n.name)}* to ${ce(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await rt(o,{...Ae(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function G1(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.interruptLead(n.id);let s=(o.listSubtaskJobs?.(n.id)??[]).filter(a=>!a.finishedAt);for(let a of s)try{o.cancelSubtask(a.id)}catch{}await rt(r,{...Ae(`\u2713 Stopped *${ce(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 rt(r,{...Ae(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function V1(e,t){let n=[...t.slice(0,99).map(r=>({text:{type:"plain_text",text:Oi(`${r.name} (${r.branch})`,75)},value:r.path})),{text:{type:"plain_text",text:"fresh workspace"},value:"__fresh__"}];return[{type:"section",block_id:JSON.stringify({name:e}),text:{type:"mrkdwn",text:`Repo for *${ce(e)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function z1(e){let t=await e.listTracks(),n=t.filter(s=>!s.archivedAt);if(n.sort((s,a)=>(a.lastResponseAt??a.createdAt??0)-(s.lastResponseAt??s.createdAt??0)),!n.length)return"No live Tracks.";let r=n.map(s=>{let a=s.workspace||{},i=a.repo||(a.base?mu.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 *${ce(s.name)}* \u2014 ${ce(i)}${a.branch?` \`${ce(a.branch)}\``:""} \xB7 lead ${ce(c)} \xB7 ${l}`}),o=t.length-n.length;return r.join(`
|
|
568
574
|
`)+(o?`
|
|
569
|
-
_${o} archived_`:"")}async function
|
|
570
|
-
\u2713 *${ce(a)}*`}}]}).catch(()=>{})}}catch(c){await rt(n.response_url,
|
|
571
|
-
\u2713 _answered_`}}]})}async function
|
|
572
|
-
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return
|
|
573
|
-
`)){let r=n.trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a={input:s.input_tokens||0,output:s.output_tokens||0,cacheWrite:s.cache_creation_input_tokens||0,cacheRead:s.cache_read_input_tokens||0};if(a.input+a.output+a.cacheWrite+a.cacheRead===0)continue;let i=o.message?.id||o.uuid||null;i&&t.set(i,{messageId:i,tokens:a,model:o.message?.model||o.model||null})}return[...t.values()]}function
|
|
574
|
-
`)}up();Wc();ue();Nf();import ij from"node:fs/promises";import cj from"node:path";Rd();Fr();var lu=e=>e.payload?.ts??e.ts,rj=12e4,Tr=e=>Math.round(e*1e4)/1e4,oj=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function sj(e,{since:t}){let{track:n,events:r,brief:o}=e,s=k=>lu(k)>=t,a=Lr([wa(n?.name),wa(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let k of r)if(k.type==="subtask.delegated"&&k.boxId&&k.payload?.task){let E=Lr(k.payload.task);E&&i.set(k.boxId,E)}let c=new Map,l=k=>{let E=k||"unattributed";return c.has(E)||c.set(E,{segments:[u(null)],lastTs:null,provider:null}),c.get(E)},u=k=>({textCat:k,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),f=[...r].sort((k,E)=>lu(k)-lu(E)),d=0,p=0,m=0,h={turns:0};for(let k of f){let E=l(k.boxId),I=E.segments[E.segments.length-1],M=lu(k);switch(E.lastTs!=null&&M>E.lastTs&&s(k)&&(I.activeMs+=Math.min(M-E.lastTs,rj)),E.lastTs=M,k.provider&&(E.provider=k.provider),k.type){case"turn.user":{let N=k.payload?.text;if(k.source==="hook"||!String(N||"").trim())break;let C=u(Lr(N));E.segments.push(C),s(k)&&(C.turns+=1);break}case"peer.message":{let N=k.payload?.text;if(!String(N||"").trim())break;let C=u(Lr(N));E.segments.push(C),s(k)&&(C.turns+=1);break}case"tool.called":{if(k.source==="transcript")break;let N=k.payload?.tool;N&&(I.tools[N]=(I.tools[N]||0)+1),s(k)&&(I.toolCalls+=1);break}case"cost.recorded":{if(!s(k))break;let{usd:N,tokens:C,model:T,unpriced:j}=pn(k);j&&(h.turns+=1),I.usd+=N,C&&(I.tokens.input+=C.input,I.tokens.output+=C.output,I.tokens.cacheRead+=C.cacheRead,I.tokens.cacheWrite+=C.cacheWrite),I.models[T]=(I.models[T]||0)+N,k.provider&&(I.provider=k.provider);break}case"handoff.done":s(k)&&(d+=1);break;case"subtask.merged":s(k)&&(p+=1);break;case"error":s(k)&&k.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let g=new Map,x=!1;for(let[k,E]of c)for(let I of E.segments){if(!(I.usd>0||I.activeMs>0||I.turns>0||I.toolCalls>0))continue;let N=I.textCat?.category??i.get(k)?.category??Xg(I.tools)?.category??a?.category??"uncategorized";g.has(N)||g.set(N,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let C=g.get(N);C.usd+=I.usd,C.activeMs+=I.activeMs,C.turns+=I.turns,C.toolCalls+=I.toolCalls;for(let[j,Z]of Object.entries(I.models))C.models[j]=(C.models[j]||0)+Z;let T=I.provider||E.provider;T&&(C.providers[T]=(C.providers[T]||0)+I.usd),T==="cursor"&&I.usd===0&&(x=!0)}return{perCategory:g,handoffs:d,merged:p,failed:m,sawCursorActivity:x,unpricedTurns:h.turns}}function yT(e,{windowMs:t=null,now:n=Date.now()}={}){let r=t==null?0:n-t,o=new Map,s=[],a=0,i=!1,c=0;for(let h of e||[]){let{track:g,events:x}=h;if(!x?.length){a+=1;continue}let{perCategory:k,handoffs:E,merged:I,failed:M,sawCursorActivity:N,unpricedTurns:C}=sj(h,{since:r});N&&(i=!0),c+=C;let T={trackId:g?.id,name:g?.name||g?.id,archived:!!g?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:E,subtasksMerged:I,subtasksFailed:M};for(let[j,Z]of k){T.usd+=Z.usd,T.activeMs+=Z.activeMs,T.turns+=Z.turns,T.categories[j]={usd:Tr(Z.usd),activeMs:Z.activeMs};for(let[D,R]of Object.entries(Z.models))T.models[D]=Tr((T.models[D]||0)+R);for(let[D,R]of Object.entries(Z.providers))T.providers[D]=Tr((T.providers[D]||0)+R);o.has(j)||o.set(j,oj());let fe=o.get(j);fe.usd+=Z.usd,fe.activeMs+=Z.activeMs,fe.turns+=Z.turns,fe.toolCalls+=Z.toolCalls;for(let[D,R]of Object.entries(Z.models))fe.models[D]=(fe.models[D]||0)+R;for(let[D,R]of Object.entries(Z.providers))fe.providers[D]=(fe.providers[D]||0)+R;let ie=fe.byTrack.get(g?.id)||{usd:0,activeMs:0};ie.usd+=Z.usd,ie.activeMs+=Z.activeMs,fe.byTrack.set(g?.id,ie)}(T.usd>0||T.activeMs>0||T.turns>0)&&(T.usd=Tr(T.usd),T.dominantCategory=aj(T.categories),s.push(T))}let l=[...o.values()].reduce((h,g)=>h+g.usd,0),u=[...o.values()].reduce((h,g)=>h+g.activeMs,0),f=l>0?"usd":"time",d=f==="usd"?l:u,p=new Map((e||[]).map(h=>[h.track?.id,h.track?.name||h.track?.id])),m=[...o.entries()].map(([h,g])=>({category:h,sharePct:d>0?Math.round((f==="usd"?g.usd:g.activeMs)/d*100):0,shareBasis:f,usd:Tr(g.usd),activeMs:g.activeMs,turns:g.turns,toolCalls:g.toolCalls,models:gT(g.models),providers:gT(g.providers),topWorkstreams:[...g.byTrack.entries()].sort((x,k)=>k[1].usd-x[1].usd||k[1].activeMs-x[1].activeMs).slice(0,2).map(([x,k])=>({trackId:x,name:p.get(x),usd:Tr(k.usd),activeMs:k.activeMs}))})).sort((h,g)=>g.usd-h.usd||g.activeMs-h.activeMs);return s.sort((h,g)=>g.usd-h.usd||g.activeMs-h.activeMs),{since:r,capturedAt:n,totalUsd:Tr(l),activeMs:u,turns:[...o.values()].reduce((h,g)=>h+g.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var aj=e=>{let t=null;for(let[n,r]of Object.entries(e))(!t||r.usd>t.v.usd||r.usd===t.v.usd&&r.activeMs>t.v.activeMs)&&(t={cat:n,v:r});return t?.cat??null},gT=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Tr(n)]));var Is={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function lj(e){let t=String(e||"7d");return t in Is?{key:t,ms:Is[t]}:{key:"7d",ms:Is["7d"]}}function uj(e,t){let n=new Set((t||[]).filter(o=>o.source==="orchestrator").map(o=>`${o.type}|${o.payload?.ts??"?"}`)),r=[...t||[]];for(let o of e?.events||[]){let s=Gc(o);!s||n.has(`${s.type}|${s.payload?.ts??"?"}`)||r.push({trackId:e.id,boxId:s.boxId??null,ts:o.at,type:s.type,actor:s.actor,provider:s.provider??null,source:s.source,payload:s.payload})}return r}async function dj(e){if(!e?.cwd)return null;try{let n=(await ij.readFile(cj.join(e.cwd,".bostrat","handoff","latest.md"),"utf8")).match(/^##\s*Objective\s*\n([\s\S]*?)(?:\n##\s|\n*$)/m);return n?n[1].trim().slice(0,1e3):null}catch{return null}}async function bT(e,{window:t="7d",from:n,to:r,now:o=Date.now()}={}){let s=Number(n),a=Number(r),i=Number.isFinite(s)&&s>0,c=i&&Number.isFinite(a)&&a>s?Math.min(a,o):o,{key:l,ms:u}=i?{key:"custom",ms:c-s}:lj(t),f=[];for(let d of e||[]){let p=await L(d),m=uj(d,p);i&&c<o&&(m=m.filter(h=>(h.ts??0)<=c)),f.push({track:d,events:m,brief:await dj(d)})}return{window:l,...yT(f,{windowMs:u,now:c})}}ue();Fr();var Er=36e5,vT=24*Er,fj=400,pj=12,Rs=e=>Math.round(e*1e4)/1e4;function mj({window:e,from:t,to:n,now:r=Date.now()}={}){let o=Number(t),s=Number(n);if(Number.isFinite(o)&&o>0){let c=Number.isFinite(s)&&s>o?Math.min(s,r):r;return{key:"custom",since:o,until:c}}let a=String(e||"7d")in Is?String(e||"7d"):"7d",i=Is[a];return{key:a,since:i==null?null:r-i,until:r}}var hj=(e,t)=>e!=null&&t-e<=48*Er?Er:vT;function kT(e,t){if(t===Er)return Math.floor(e/Er)*Er;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function wj(e,t){if(t===Er)return e+Er;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var xT=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),ST=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},gj=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Rs(n)]));async function TT(e,t={}){let n=t.now??Date.now(),r=mj({...t,now:n}),o=r.since==null?vT:hj(r.since,r.until),s=T=>(r.since==null||T>=r.since)&&T<r.until,a=r.since!=null?r.until-r.since:null,i=T=>a!=null&&T>=r.since-a&&T<r.since,c={},l={},u=xT(),f=0,d=0,p=0,m=0,h=0,g=new Set,x=new Map,k=[],E=[],I=[],M=null;for(let T of e||[]){let Z=(await L(T)).filter(D=>D.type==="cost.recorded");if(!Z.length){T.archivedAt||I.push(T.name||T.id);continue}let fe=[];for(let D of Z){let R=D.ts??0;s(R)?fe.push(D):i(R)&&(h+=pn(D).usd)}if(!fe.length)continue;let ie=nr(fe,{boxLabels:Ah(T)});f+=ie.totalUsd,d+=ie.turns,p+=ie.repricedTurns||0,m+=ie.unpricedTurns||0;for(let D of ie.unpricedModels||[])g.add(D);ST(u,ie.totalTokens);for(let[D,R]of Object.entries(ie.byModel))c[D]||={usd:0,events:0,tokens:xT()},c[D].usd=Rs(c[D].usd+R.usd),c[D].events+=R.events,ST(c[D].tokens,R.tokens);for(let[D,R]of Object.entries(ie.byProvider))l[D]||={usd:0,events:0},l[D].usd=Rs(l[D].usd+R.usd),l[D].events+=R.events;for(let D of fe){let{usd:R}=pn(D);if(!R)continue;let U=kT(D.ts??0,o),pe=x.get(U)??{t:U,total:0,byProvider:{}};pe.total+=R;let Se=D.provider||"unknown";pe.byProvider[Se]=(pe.byProvider[Se]||0)+R,x.set(U,pe),(M==null||U<M)&&(M=U)}k.push({id:T.id,name:T.name||T.id,archived:!!T.archivedAt,usd:ie.totalUsd,turns:ie.turns,providers:Object.keys(ie.byProvider)});for(let[D,R]of Object.entries(ie.byBox))E.push({trackId:T.id,trackName:T.name||T.id,boxId:D,...R})}let N=r.since!=null?kT(r.since,o):M,C=[];for(let T=N,j=0;T!=null&&T<r.until&&j<fj;T=wj(T,o),j++){let Z=x.get(T);C.push(Z?{t:T,total:Rs(Z.total),byProvider:gj(Z.byProvider)}:{t:T,total:0,byProvider:{}})}return k.sort((T,j)=>j.usd-T.usd),E.sort((T,j)=>j.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:C,fleet:{totalUsd:Rs(f),turns:d,repricedTurns:p,unpricedTurns:m,unpricedModels:[...g],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:k.length,tracksSilentLive:I},prior:a!=null?{totalUsd:Rs(h),since:r.since-a,until:r.since}:null,tracks:k.slice(0,50),boxes:E.slice(0,pj)}}Qe();import $s from"node:fs/promises";import yj from"node:fs";import ze from"node:path";import bj from"node:os";import{execFile as kj}from"node:child_process";import{promisify as xj}from"node:util";var Sj=xj(kj),ET=process.env.BOSTRAT_STATE_DIR||ze.join(process.env.HOME||bj.tmpdir(),".bostrat"),uu=!1,_h=null;async function Ih(e,t){let n=await $s.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await $s.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await $s.mkdir(ze.dirname(t),{recursive:!0}),await $s.copyFile(e,t),!0)}var vj=new Set([".claude",".codex",".cursor",".credentials.json","auth.json",".claude.json","api-token","node.env","certs","integrations.json","gcloud","application_default_credentials.json",".gemini"]);async function du(e,t,{skip:n=null}={}){let r=0,o=await $s.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(vj.has(s.name)||n?.has(s.name))continue;let a=ze.join(e,s.name);s.isDirectory()?r+=await du(a,ze.join(t,s.name)):s.isFile()&&(r+=await Ih(a,ze.join(t,s.name))?1:0)}return r}async function AT({stateDir:e=ET,dest:t=process.env.BOSTRAT_SYNC_DIR||`${ET}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await $s.mkdir(t,{recursive:!0});let o=0;o+=await Ih(ze.join(e,"tracks.json"),ze.join(t,"tracks.json"))?1:0,o+=await Ih(ze.join(e,"events.jsonl"),ze.join(t,"events.jsonl"))?1:0,o+=await du(ze.join(e,"events"),ze.join(t,"events")),o+=await du(ze.join(e,"memory"),ze.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await du(ze.join(u.cwd,".bostrat"),ze.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>Sj("git",["-C",t,...u]);yj.existsSync(ze.join(t,".git"))||(await a("init","-q"),await a("config","user.email","agent@bostrat.local"),await a("config","user.name","bostrat state sync")),n&&/^https:\/\/github\.com\//i.test(n)&&(await a("config","--get","credential.useHttpPath").then(()=>!0).catch(()=>!1)||await ju(t).catch(()=>{}));let i=!1,{stdout:c}=await a("status","--porcelain");c.trim()&&(await a("add","-A"),await a("commit","-q","-m",`state sync ${new Date().toISOString()}`),i=!0);let l=!1;if(n&&(i||uu))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,uu&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),uu=!1,_h=null}catch(u){uu=!0;let f=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
575
|
-
`).slice(-3).join(" | ").slice(0,300);
|
|
576
|
-
`).map(s=>s.replace(/\s+$/,"").trim()),n=s=>{let a=null,i=null;for(let c=s+1;c<Math.min(s+5,t.length)&&!/^Current\s/i.test(t[c]);c++){let l=t[c].match(/(\d+)%\s*used/i);l&&a===null&&(a=Number(l[1]));let u=t[c].match(/^Resets\s+(.+)$/i);u&&i===null&&(i=u[1].trim())}return a===null?null:{pct:a,resets:i||null}},r=s=>{let a=t.findIndex(i=>s.test(i));return a<0?null:n(a)},o=[];return t.forEach((s,a)=>{let i=s.match(/^Current week \(([^)]+)\)$/i);if(!i||/all models/i.test(i[1]))return;let c=n(a);c&&o.push({model:i[1].trim(),...c})}),{session:r(/^Current session\b/i),week:r(/^Current week \(all models\)/i)||r(/^Current week$/i),weekModels:o.length?o:null}}function
|
|
577
|
-
`).map(s=>s.replace(/\s+$/,"")),n=t.findIndex(s=>/What's contributing/i.test(s));if(n<0)return null;let r=/(Usage credits|Esc to cancel|d to day · w to week|^\s*d to day)/i,o=[];for(let s=n+1;s<t.length&&!r.test(t[s]);s++)t[s].trim()&&o.push(t[s].trim());return o.length?o:null}async function
|
|
578
|
-
`),r=!1;for(let o=n.length-1;o>=0;o--){let s=n[o].trim();if(!s||s[0]!=="{"||!s.includes("token_count"))continue;let a;try{a=JSON.parse(s)}catch{continue}let i=a?.payload;if(i?.type!=="token_count")continue;r=!0;let c=i.rate_limits;if(!c)return
|
|
579
|
-
`,{mode:384}),
|
|
575
|
+
_${o} archived_`:"")}async function J1(e,t){let n=await ho();if(!n)return Ae("Slack integration not configured.");let r=e.deps,o=(t.text||"").trim().split(/\s+/).filter(Boolean),s=(o[0]||"help").toLowerCase(),a=o.slice(1);switch(s){case"start":{if(!a.length)return Ae("Usage: `/bostrat start <name> [repo]`");let i=await r.listRepos(),c=a[a.length-1]?.toLowerCase(),l=a.length>1?i.find(d=>d.name.toLowerCase()===c):null,u=(l?a.slice(0,-1):a).join(" ");return l?(vr(TT(e,n,{name:u,repo:l.path,responseUrl:t.response_url})),Ae(`Starting *${ce(u)}* on ${ce(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:V1(u,i)}}case"archive":{if(!a.length)return Ae("Usage: `/bostrat archive <track>`");let{match:i,live:c}=Ch(await r.listTracks(),a.join(" "));return i?(vr(q1(e,n,{track:i,responseUrl:t.response_url})),Ae(`Archiving *${ce(i.name)}*\u2026`)):Mh(c,a.join(" "))}case"handoff":{let i=r.knownAgents??W1,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return Ae(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:d}=Ch(await r.listTracks(),l);return u?(vr(K1(e,n,{track:u,agent:c,responseUrl:t.response_url})),Ae(`Handing *${ce(u.name)}* to ${ce(c)}\u2026`)):Mh(d,l)}case"stop":{if(!a.length)return Ae("Usage: `/bostrat stop <track>`");let{match:i,live:c}=Ch(await r.listTracks(),a.join(" "));return i?(vr(G1(e,n,{track:i,responseUrl:t.response_url})),Ae(`Stopping *${ce(i.name)}*\u2026`)):Mh(c,a.join(" "))}case"list":return Ae(await z1(r));default:return Ae(H1)}}async function Y1(e,t,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Bh(o);try{if(await e.deps.answerPrompt(o,{promptId:s,answers:[a]}),i?.prompt?.promptId===s){let{ts:c,question:l}=i.prompt;delete i.prompt,await Ft(),await yo(t.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ce(l||"question")}~
|
|
576
|
+
\u2713 *${ce(a)}*`}}]}).catch(()=>{})}}catch(c){await rt(n.response_url,Ae(`Couldn't answer: ${c?.message||c}`))}}async function X1(e,t,n,r){let{trackId:o,offerId:s,action:a}=r,i=e.deps;try{let c=await i.resolveTrack(o);if(!c)throw new Error("track not found");let l=await i.readEvents(c),u=l.find(f=>f.type==="pr.offered"&&f.payload?.offerId===s);if(!u)throw new Error("unknown offer");if(l.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===s))throw new Error("offer already answered");let d=Array.isArray(r.actions)?ts.filter(f=>r.actions.includes(f)):a==="pr"?["commit","push","pr"]:a==="push"?["commit","push"]:null;if(d?.includes("pr")&&i.divertShip&&await i.divertShip(c,u,{actions:d}))return;d&&await i.sayToLead(c.id,ns(d,{branch:u.payload.branch,base:u.payload.base}),{kind:d.includes("pr")?"pr-go":"push-go"}),await i.appendEvent({id:c.id,cwd:c.cwd},{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:u.boxId,payload:{ts:Date.now(),offerId:s,action:d?a:"dismiss",actions:d}})}catch(c){await rt(n.response_url,Ae(`Couldn't act on the offer: ${c?.message||c}`))}}async function Z1(e,t,n,r){let{trackId:o,offerId:s,choice:a}=r;try{let i=await e.deps.resolveTrack(o);if(!i)throw new Error("track not found");await e.deps.answerPrExists(i,{offerId:s,choice:a})}catch(i){await rt(n.response_url,Ae(`Couldn't act on the offer: ${i?.message||i}`))}}async function Q1(e,t){let n=t.actions?.[0];if(!n?.action_id)return;let r=await ho();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 rt(t.response_url,{...Ae(`Starting *${ce(s)}*\u2026`),replace_original:!0}),vr(TT(e,r,{name:s,repo:a==="__fresh__"?null:a,responseUrl:t.response_url}));return}let o;try{o=JSON.parse(n.value)}catch{return}n.action_id==="bostrat-prompt"?vr(Y1(e,r,t,o)):n.action_id==="bostrat-ship"?vr(X1(e,r,t,o)):n.action_id==="bostrat-pr-exists"&&vr(Z1(e,r,t,o))}async function ej(e,t){if(!t||t.type!=="message"||t.bot_id||t.subtype||!t.thread_ts||t.thread_ts===t.ts||!t.text?.trim())return;let n=await F1(t.channel,t.thread_ts);if(!n)return;let r=await ho();if(r)try{await e.deps.sayToLead(n.trackId,t.text.trim())}catch(o){await Tr(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function tj(e){return async t=>{if(t.type==="slash_commands")return J1(e,t.payload);if(t.type==="interactive"){await Q1(e,t.payload);return}if(t.type==="events_api"){await ej(e,t.payload?.event);return}}}var nj=new Set(["turn.user","turn.agent","prompt.raised","prompt.answered","pr.offered","pr.offer.answered","pr.exists.offered","pr.exists.answered","pr.state.changed","handoff.done","view.state.changed","mock.state.changed"]);function rj(e,t){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${ce(t.question||"The lead needs an answer")}`}}],r=(t.options||[]).slice(0,12);return r.length?n.push({type:"actions",elements:r.map(o=>({type:"button",action_id:"bostrat-prompt",text:{type:"plain_text",text:Oi(String(o),75)},value:JSON.stringify({trackId:e,promptId:t.promptId,label:String(o)})}))}):n.push({type:"context",elements:[{type:"mrkdwn",text:"_reply in this thread to answer_"}]}),n}async function oj(e,t,n){let r=n.payload||{};if(!r.promptId||t.prompt?.promptId===r.promptId)return;t.prompt&&await yo(e.botToken,{channel:t.channel,ts:t.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ce(t.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await Tr(e,t,r.question||"The lead needs an answer",rj(n.trackId,r));t.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await Ft()}async function sj(e,t,n){let r=n.payload||{};if(!t.prompt||r.promptId&&t.prompt.promptId!==r.promptId)return;let{ts:o,question:s}=t.prompt;delete t.prompt,await Ft(),await yo(e.botToken,{channel:t.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ce(s||"question")}~
|
|
577
|
+
\u2713 _answered_`}}]})}async function aj(e,t,n){let r=n.payload||{};if(!r.offerId||t.offers?.[r.offerId])return;let o=(i,c,l,u)=>({type:"button",action_id:"bostrat-ship",text:{type:"plain_text",text:c},value:JSON.stringify({trackId:n.trackId,offerId:r.offerId,action:i,...u?{actions:u}:{}}),...l?{style:l}:{}}),s;if(!Array.isArray(r.available))s=r.toPr?[o("push",`Commit & push${r.toPr.number?` to #${r.toPr.number}`:""}`,"primary"),o("dismiss","Not now")]:[o("pr","Open PR","primary"),o("push","Just push"),o("dismiss","Not now")];else{let i=r.available,c=Array.isArray(r.defaults)&&r.defaults.length?r.defaults:i,l={commit:"Commit",rebase:"rebase",push:"push",pr:r.prState==="open"?"open new PR":"open PR"},u=c.map(d=>l[d]).filter(Boolean).join(" & ");s=[o(c.includes("pr")?"pr":"push",u||"Ship","primary",c)],i.includes("pr")&&!c.includes("pr")&&s.push(o("pr","Open new PR",void 0,["pr"])),s.push(o("dismiss","Not now"))}let a=await Tr(e,t,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${ce(Oi(r.excerpt||"The lead offered to ship.",300))}`}},{type:"actions",elements:s}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await Ft()}async function ij(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await Ft();let s=r.action==="dismiss"?"dismissed":r.action==="pr-exists"?`PR${r.existingPr?.number?` #${r.existingPr.number}`:""} already open \u2014 pick how to ship`:r.action==="pr"?"\u2713 opening a PR":"\u2713 pushing";await yo(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function cj(e,t,n){let r=n.payload||{};if(!r.offerId||t.offers?.[r.offerId])return;let o=r.pr?.number?`#${r.pr.number}`:"a PR",s=(i,c,l)=>({type:"button",action_id:"bostrat-pr-exists",text:{type:"plain_text",text:c},value:JSON.stringify({trackId:n.trackId,offerId:r.offerId,choice:i}),...l?{style:l}:{}}),a=await Tr(e,t,`PR ${o} already open \u2014 push or new PR?`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${o}${r.branch?` (${ce(r.branch)})`:""} is already open. Push the update to it, or open a new PR?`}},{type:"actions",elements:[s("push",`Push to ${o}`,"primary"),s("new","Open a new PR"),s("dismiss","Not now")]}]);t.offers=t.offers||{},t.offers[r.offerId]=a.ts,await Ft()}async function lj(e,t,n){let r=n.payload||{},o=t.offers?.[r.offerId];if(!o)return;delete t.offers[r.offerId],await Ft();let s=r.choice==="dismiss"?"dismissed":r.choice==="new"?"\u2713 opening a new PR":"\u2713 pushing to the open PR";await yo(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function uj(e,t,n,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??dj)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await yT(e.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:t.channel,threadTs:t.ts,initialComment:`\u{1F3A8} mock *${ce(o.name)}*${a}${o.group?` (group \`${ce(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${ce(a.name)}*`).join(", ");await Tr(e,t,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${ce(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function dj(e,t){let n=await En(e);if(!n)return null;let r=jt(n,t);if(!r)return null;let o=await $i.readFile(At(n,r.v).png).catch(()=>null);return o?{filename:`${n.name.replace(/[^\w.-]+/g,"-")}.v${r.v}.png`,buffer:o}:null}async function fj(e,t){if(!nj.has(t.type))return;let n=await Bh(t.trackId);if(!n)return;let r=await ho();if(!r)return;let o=t.payload||{};switch(t.type){case"turn.user":if(n.kickoff||t.actor!=="user"||!o.text)return;n.kickoff=o.text,await Ft(),await Ph(r,n);return;case"turn.agent":{if(!o.text)return;let s=U1(o.text);if(!s)return;await Tr(r,n,s,[{type:"section",text:{type:"mrkdwn",text:ce(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${uu(t.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return oj(r,n,t);case"prompt.answered":return sj(r,n,t);case"pr.offered":return aj(r,n,t);case"pr.offer.answered":return ij(r,n,t);case"pr.exists.offered":return cj(r,n,t);case"pr.exists.answered":return lj(r,n,t);case"pr.state.changed":{if(o.change!=="opened"&&o.change!=="merged")return;let s=o.change==="opened"?"PR opened":"PR merged";await Tr(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 Ft(),await Ph(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await Tr(r,n,`Live preview ${o.name}: ${du(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${ce(o.name)}*: ${du(o.slug)}
|
|
578
|
+
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return uj(r,n,t,e.deps)}}function AT(e){let t={deps:e};at.on("event",s=>{fj(t,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await ho()||(r&&clearInterval(r),r=null,n=bT({getToken:async()=>(await ho())?.appToken,onEnvelope:tj(t),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}Pt();ue();Gt();dp();or();import pj from"node:fs/promises";function mj(e){let t=new Map;for(let n of String(e||"").split(`
|
|
579
|
+
`)){let r=n.trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a={input:s.input_tokens||0,output:s.output_tokens||0,cacheWrite:s.cache_creation_input_tokens||0,cacheRead:s.cache_read_input_tokens||0};if(a.input+a.output+a.cacheWrite+a.cacheRead===0)continue;let i=o.message?.id||o.uuid||null;if(!i)continue;let c=s.cache_creation,l=c&&typeof c=="object"?{m5:c.ephemeral_5m_input_tokens||0,h1:c.ephemeral_1h_input_tokens||0}:null;t.set(i,{messageId:i,tokens:a,cacheWriteTtl:l,model:o.message?.model||o.model||null})}return[...t.values()]}function hj(e){let t=new Set;for(let n of e)if(n.type==="cost.recorded"){n.payload?.messageId&&t.add(n.payload.messageId);for(let r of n.payload?.messageIds||[])t.add(r)}return t}var wj=new Set(["Write","Edit","NotebookEdit"]),gj=220,yj=160;function bj(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(wj.has(n)&&typeof o.file_path=="string"&&o.file_path){let s=o.file_path,a=t?.cwd;a&&s.startsWith(`${a}/`)&&(s=s.slice(a.length+1)),r.path=s.slice(0,gj)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=Nr(o.command.trim().replace(/\s+/g," ")).slice(0,yj));return r}function kj(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?Nt(t.cwd,t.sessionId):t?.cwd?wd(t.cwd):null}async function ET(e,t){let n=await t(e?.session_id,e?.cwd);if(!n?.track)return[];let{track:r,box:o}=n,s={boxId:o?.name||null,provider:"claude",source:"hook"},a=[],i=async c=>{a.push(await _(r,{...s,...c}))};switch(e?.hook_event_name){case"SessionStart":await i({type:"session.ready",actor:"system",payload:{sessionId:e.session_id}});break;case"UserPromptSubmit":{let c=String(e.prompt??e.user_prompt??""),l=gl(c);await i({type:"turn.user",actor:l?"peer":"user",payload:{promptHead:(l?l.text:c).slice(0,160)||null,...l?{peer:{socket:l.socket,name:l.name,mode:l.mode}}:{}}});break}case"PostToolUse":await i({type:"tool.called",actor:"agent",payload:bj(e,o)});break;case"Stop":await xj(r,o,e,i);break;default:break}return a}async function xj(e,t,n,r){let o=[],s=kj(n,t);if(s)try{o=mj(await pj.readFile(s,"utf8"))}catch{}let a=o.length?o[o.length-1]:null;if(await r({type:"turn.agent",actor:"agent",payload:{model:a?.model??t?.model??null,messageId:a?.messageId??null}}),!o.length)return;let i=hj(await L(e)),c=o.filter(u=>!i.has(u.messageId));if(c.length){let u=new Map;for(let d of c){let f=d.model||"unknown",p=u.get(f)||{tokens:{input:0,output:0,cacheWrite:0,cacheRead:0},cacheWriteTtl:null,messageIds:[]};for(let m of Object.keys(p.tokens))p.tokens[m]+=d.tokens[m];d.cacheWriteTtl&&(p.cacheWriteTtl??={m5:0,h1:0},p.cacheWriteTtl.m5+=d.cacheWriteTtl.m5,p.cacheWriteTtl.h1+=d.cacheWriteTtl.h1),p.messageIds.push(d.messageId),u.set(f,p)}for(let[d,f]of u)await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:d,usd:rr(d,f.tokens),tokens:f.tokens,cacheWriteTtl:f.cacheWriteTtl,messageId:f.messageIds[f.messageIds.length-1],messageIds:f.messageIds,apiCalls:f.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}})}zs();import Sj from"node:fs";import _T from"node:fs/promises";import Ci from"node:os";import vj from"node:path";jp();Ve();var Tj=1024*1024,hu=e=>Math.round(e/Tj);async function Aj(e){try{let t=await _T.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:hu(r),diskTotalMb:hu(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function Ej(){try{return(await Ct()).filter(t=>t.isBostrat).length}catch{return null}}async function _j(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await _T.readdir(vj.join(Si,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function IT({stateDir:e=Si,now:t=Date.now}={}){let n=Ci.totalmem(),[r,o,s,a]=await Promise.all([Rl().catch(()=>null),Aj(Sj.existsSync(e)?e:Ci.tmpdir()),Ej(),_j()]),i=Ci.cpus()?.length||null,c=Ci.loadavg()?.[0]??null;return{ts:t(),loadavg1:c!=null?Math.round(c*100)/100:null,cpus:i,cpuPct:c!=null&&i?Math.min(100,Math.round(c/i*100)):null,memUsedMb:r!=null?hu(n-r):null,memTotalMb:hu(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(Ci.uptime())}}ue();Hd();na();ue();Hr();or();function jh(e){let t={};for(let n of e.boxes||[]){let r=n.role&&n.role!=="lead"?` \xB7 ${n.role}`:"";t[n.name]=`${n.name} (${n.agent||"?"}${r})`}return t}var RT=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),$T=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},wu=e=>Math.round(e*1e4)/1e4;async function Lh(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=RT(),d=0,f=[];for(let p of e||[]){let m=await L(p),h=sr(m,{boxLabels:jh(p)});if(h.turns===0){p.archivedAt?d++:f.push(p.name||p.id);continue}o+=h.totalUsd,s+=h.turns,a+=h.repricedTurns||0,i+=h.asRecordedUsd||0,c+=h.unpricedTurns||0;for(let g of h.unpricedModels||[])l.add(g);$T(u,h.totalTokens);for(let[g,x]of Object.entries(h.byModel))n[g]||={usd:0,events:0,tokens:RT()},n[g].usd=wu(n[g].usd+x.usd),n[g].events+=x.events,$T(n[g].tokens,x.tokens);for(let[g,x]of Object.entries(h.byProvider))r[g]||={usd:0,events:0},r[g].usd=wu(r[g].usd+x.usd),r[g].events+=x.events;t.push({id:p.id,name:p.name,usd:h.totalUsd,rollup:h})}return t.sort((p,m)=>m.usd-p.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:Wo(),fleet:{totalUsd:wu(o),asRecordedUsd:wu(i),turns:s,repricedTurns:a,unpricedTurns:c,unpricedModels:[...l],totalTokens:u,byModel:n,byProvider:r,tracksWithCost:t.length,tracksSilent:d+f.length,tracksSilentArchived:d,tracksSilentLive:f},tracks:t}}var Ar=e=>`$${e.toFixed(4)}`,Ps=(e,t)=>String(e).padEnd(t);function OT(e){let{fleet:t,tracks:n}=e,r=[];r.push(`BOSTRAT COST READ-OUT \xB7 ${e.generatedAt}`),e.pricingBasis&&r.push(`basis: ${e.pricingBasis}`);let o=t.tracksSilentLive||[];if(r.push(`fleet total ${Ar(t.totalUsd)} over ${t.turns} turn(s) \xB7 ${t.tracksWithCost} track(s) with cost, ${t.tracksSilentArchived??t.tracksSilent} silent (archived)`+(o.length?`, ${o.length} LIVE with no cost data: ${o.join(", ")}`:"")),t.repricedTurns&&r.push(`${t.repricedTurns} turn(s) priced at current table rates (differs from as-recorded)`),t.asRecordedUsd!=null&&Math.abs(t.asRecordedUsd-t.totalUsd)>=.005&&r.push(`as-recorded total ${Ar(t.asRecordedUsd)} \xB7 current-rates total ${Ar(t.totalUsd)} (${t.totalUsd>=t.asRecordedUsd?"+":""}${Ar(t.totalUsd-t.asRecordedUsd)})`),t.unpricedTurns&&r.push(`\u26A0 ${t.unpricedTurns} turn(s) UNPRICED \u2014 total under-reports. Add PER_MTOK rate(s) for: ${(t.unpricedModels||[]).join(", ")}`),e.pricing){let a=e.pricing;r.push(`rates verified against vendor pages: ${a.tableVerifiedAt??"never"}`+(a.upcomingChanges?.length?` \xB7 scheduled: ${a.upcomingChanges.map(i=>`${i.model} ${i.from}`).join(", ")}`:""));for(let i of a.problems||[])r.push(`\u26A0 RATE TABLE: ${i}`)}r.push(""),r.push("BY MODEL");let s=Object.entries(t.byModel).sort((a,i)=>i[1].usd-a[1].usd);s.length||r.push(" (no cost events yet \u2014 hooks emit on each turn of a newly started box)");for(let[a,i]of s){let c=i.tokens;r.push(` ${Ps(a,26)} ${Ps(Ar(i.usd),10)} ${i.events} turn(s) \xB7 in ${c.input} out ${c.output} cacheR ${c.cacheRead} cacheW ${c.cacheCreation}`)}r.push(""),r.push("BY TRACK / TASK");for(let a of n){r.push(` ${Ps(a.name||a.id,30)} ${Ar(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${Ps(c.label,40)} ${Ps(Ar(c.usd),10)} ${c.models.join(",")||"?"}`),c.epochs)for(let[l,u]of Object.entries(c.epochs).sort((d,f)=>Number(d[0])-Number(f[0])))r.push(` \u21B3 tenure ${l} (${u.provider||"?"})${" ".repeat(Math.max(1,24-String(u.provider||"?").length))}${Ps(Ar(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
580
|
+
`)}or();xp();Xc();ue();Kf();import Cj from"node:fs/promises";import Mj from"node:path";Ud();Hr();var gu=e=>e.payload?.ts??e.ts,Ij=12e4,Er=e=>Math.round(e*1e4)/1e4,Rj=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function $j(e,{since:t}){let{track:n,events:r,brief:o}=e,s=k=>gu(k)>=t,a=Ur([Ea(n?.name),Ea(n?.workspace?.branch),o].filter(Boolean).join(" ")),i=new Map;for(let k of r)if(k.type==="subtask.delegated"&&k.boxId&&k.payload?.task){let A=Ur(k.payload.task);A&&i.set(k.boxId,A)}let c=new Map,l=k=>{let A=k||"unattributed";return c.has(A)||c.set(A,{segments:[u(null)],lastTs:null,provider:null}),c.get(A)},u=k=>({textCat:k,tools:{},usd:0,tokens:{input:0,output:0,cacheRead:0,cacheWrite:0},models:{},provider:null,activeMs:0,turns:0,toolCalls:0}),d=[...r].sort((k,A)=>gu(k)-gu(A)),f=0,p=0,m=0,h={turns:0};for(let k of d){let A=l(k.boxId),I=A.segments[A.segments.length-1],M=gu(k);switch(A.lastTs!=null&&M>A.lastTs&&s(k)&&(I.activeMs+=Math.min(M-A.lastTs,Ij)),A.lastTs=M,k.provider&&(A.provider=k.provider),k.type){case"turn.user":{let N=k.payload?.text;if(k.source==="hook"||!String(N||"").trim())break;let O=u(Ur(N));A.segments.push(O),s(k)&&(O.turns+=1);break}case"peer.message":{let N=k.payload?.text;if(!String(N||"").trim())break;let O=u(Ur(N));A.segments.push(O),s(k)&&(O.turns+=1);break}case"tool.called":{if(k.source==="transcript")break;let N=k.payload?.tool;N&&(I.tools[N]=(I.tools[N]||0)+1),s(k)&&(I.toolCalls+=1);break}case"cost.recorded":{if(!s(k))break;let{usd:N,tokens:O,model:T,unpriced:j}=mn(k);j&&(h.turns+=1),I.usd+=N,O&&(I.tokens.input+=O.input,I.tokens.output+=O.output,I.tokens.cacheRead+=O.cacheRead,I.tokens.cacheWrite+=O.cacheWrite),I.models[T]=(I.models[T]||0)+N,k.provider&&(I.provider=k.provider);break}case"handoff.done":s(k)&&(f+=1);break;case"subtask.merged":s(k)&&(p+=1);break;case"error":s(k)&&k.payload?.kind==="subtask-failed"&&(m+=1);break;default:break}}let g=new Map,x=!1;for(let[k,A]of c)for(let I of A.segments){if(!(I.usd>0||I.activeMs>0||I.turns>0||I.toolCalls>0))continue;let N=I.textCat?.category??i.get(k)?.category??my(I.tools)?.category??a?.category??"uncategorized";g.has(N)||g.set(N,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let O=g.get(N);O.usd+=I.usd,O.activeMs+=I.activeMs,O.turns+=I.turns,O.toolCalls+=I.toolCalls;for(let[j,Z]of Object.entries(I.models))O.models[j]=(O.models[j]||0)+Z;let T=I.provider||A.provider;T&&(O.providers[T]=(O.providers[T]||0)+I.usd),T==="cursor"&&I.usd===0&&(x=!0)}return{perCategory:g,handoffs:f,merged:p,failed:m,sawCursorActivity:x,unpricedTurns:h.turns}}function MT(e,{windowMs:t=null,now:n=Date.now()}={}){let r=t==null?0:n-t,o=new Map,s=[],a=0,i=!1,c=0;for(let h of e||[]){let{track:g,events:x}=h;if(!x?.length){a+=1;continue}let{perCategory:k,handoffs:A,merged:I,failed:M,sawCursorActivity:N,unpricedTurns:O}=$j(h,{since:r});N&&(i=!0),c+=O;let T={trackId:g?.id,name:g?.name||g?.id,archived:!!g?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:A,subtasksMerged:I,subtasksFailed:M};for(let[j,Z]of k){T.usd+=Z.usd,T.activeMs+=Z.activeMs,T.turns+=Z.turns,T.categories[j]={usd:Er(Z.usd),activeMs:Z.activeMs};for(let[D,R]of Object.entries(Z.models))T.models[D]=Er((T.models[D]||0)+R);for(let[D,R]of Object.entries(Z.providers))T.providers[D]=Er((T.providers[D]||0)+R);o.has(j)||o.set(j,Rj());let fe=o.get(j);fe.usd+=Z.usd,fe.activeMs+=Z.activeMs,fe.turns+=Z.turns,fe.toolCalls+=Z.toolCalls;for(let[D,R]of Object.entries(Z.models))fe.models[D]=(fe.models[D]||0)+R;for(let[D,R]of Object.entries(Z.providers))fe.providers[D]=(fe.providers[D]||0)+R;let ie=fe.byTrack.get(g?.id)||{usd:0,activeMs:0};ie.usd+=Z.usd,ie.activeMs+=Z.activeMs,fe.byTrack.set(g?.id,ie)}(T.usd>0||T.activeMs>0||T.turns>0)&&(T.usd=Er(T.usd),T.dominantCategory=Oj(T.categories),s.push(T))}let l=[...o.values()].reduce((h,g)=>h+g.usd,0),u=[...o.values()].reduce((h,g)=>h+g.activeMs,0),d=l>0?"usd":"time",f=d==="usd"?l:u,p=new Map((e||[]).map(h=>[h.track?.id,h.track?.name||h.track?.id])),m=[...o.entries()].map(([h,g])=>({category:h,sharePct:f>0?Math.round((d==="usd"?g.usd:g.activeMs)/f*100):0,shareBasis:d,usd:Er(g.usd),activeMs:g.activeMs,turns:g.turns,toolCalls:g.toolCalls,models:CT(g.models),providers:CT(g.providers),topWorkstreams:[...g.byTrack.entries()].sort((x,k)=>k[1].usd-x[1].usd||k[1].activeMs-x[1].activeMs).slice(0,2).map(([x,k])=>({trackId:x,name:p.get(x),usd:Er(k.usd),activeMs:k.activeMs}))})).sort((h,g)=>g.usd-h.usd||g.activeMs-h.activeMs);return s.sort((h,g)=>g.usd-h.usd||g.activeMs-h.activeMs),{since:r,capturedAt:n,totalUsd:Er(l),activeMs:u,turns:[...o.values()].reduce((h,g)=>h+g.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var Oj=e=>{let t=null;for(let[n,r]of Object.entries(e))(!t||r.usd>t.v.usd||r.usd===t.v.usd&&r.activeMs>t.v.activeMs)&&(t={cat:n,v:r});return t?.cat??null},CT=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Er(n)]));var Ds={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function Nj(e){let t=String(e||"7d");return t in Ds?{key:t,ms:Ds[t]}:{key:"7d",ms:Ds["7d"]}}function Pj(e,t){let n=new Set((t||[]).filter(o=>o.source==="orchestrator").map(o=>`${o.type}|${o.payload?.ts??"?"}`)),r=[...t||[]];for(let o of e?.events||[]){let s=el(o);!s||n.has(`${s.type}|${s.payload?.ts??"?"}`)||r.push({trackId:e.id,boxId:s.boxId??null,ts:o.at,type:s.type,actor:s.actor,provider:s.provider??null,source:s.source,payload:s.payload})}return r}async function Dj(e){if(!e?.cwd)return null;try{let n=(await Cj.readFile(Mj.join(e.cwd,".bostrat","handoff","latest.md"),"utf8")).match(/^##\s*Objective\s*\n([\s\S]*?)(?:\n##\s|\n*$)/m);return n?n[1].trim().slice(0,1e3):null}catch{return null}}async function NT(e,{window:t="7d",from:n,to:r,now:o=Date.now()}={}){let s=Number(n),a=Number(r),i=Number.isFinite(s)&&s>0,c=i&&Number.isFinite(a)&&a>s?Math.min(a,o):o,{key:l,ms:u}=i?{key:"custom",ms:c-s}:Nj(t),d=[];for(let f of e||[]){let p=await L(f),m=Pj(f,p);i&&c<o&&(m=m.filter(h=>(h.ts??0)<=c)),d.push({track:f,events:m,brief:await Dj(f)})}return{window:l,...MT(d,{windowMs:u,now:c})}}ue();Hr();or();var _r=36e5,jT=24*_r,Bj=400,jj=12,Bs=e=>Math.round(e*1e4)/1e4;function Lj({window:e,from:t,to:n,now:r=Date.now()}={}){let o=Number(t),s=Number(n);if(Number.isFinite(o)&&o>0){let c=Number.isFinite(s)&&s>o?Math.min(s,r):r;return{key:"custom",since:o,until:c}}let a=String(e||"7d")in Ds?String(e||"7d"):"7d",i=Ds[a];return{key:a,since:i==null?null:r-i,until:r}}var Fj=(e,t)=>e!=null&&t-e<=48*_r?_r:jT;function PT(e,t){if(t===_r)return Math.floor(e/_r)*_r;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function Uj(e,t){if(t===_r)return e+_r;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var DT=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),BT=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},Hj=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Bs(n)]));async function LT(e,t={}){let n=t.now??Date.now(),r=Lj({...t,now:n}),o=r.since==null?jT:Fj(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=DT(),d=0,f=0,p=0,m=0,h=0,g=new Set,x=new Map,k=[],A=[],I=[],M=null;for(let T of e||[]){let Z=(await L(T)).filter(D=>D.type==="cost.recorded");if(!Z.length){T.archivedAt||I.push(T.name||T.id);continue}let fe=[];for(let D of Z){let R=D.ts??0;s(R)?fe.push(D):i(R)&&(h+=mn(D).usd)}if(!fe.length)continue;let ie=sr(fe,{boxLabels:jh(T)});d+=ie.totalUsd,f+=ie.turns,p+=ie.repricedTurns||0,m+=ie.unpricedTurns||0;for(let D of ie.unpricedModels||[])g.add(D);BT(u,ie.totalTokens);for(let[D,R]of Object.entries(ie.byModel))c[D]||={usd:0,events:0,tokens:DT()},c[D].usd=Bs(c[D].usd+R.usd),c[D].events+=R.events,BT(c[D].tokens,R.tokens);for(let[D,R]of Object.entries(ie.byProvider))l[D]||={usd:0,events:0},l[D].usd=Bs(l[D].usd+R.usd),l[D].events+=R.events;for(let D of fe){let{usd:R}=mn(D);if(!R)continue;let U=PT(D.ts??0,o),pe=x.get(U)??{t:U,total:0,byProvider:{}};pe.total+=R;let Se=D.provider||"unknown";pe.byProvider[Se]=(pe.byProvider[Se]||0)+R,x.set(U,pe),(M==null||U<M)&&(M=U)}k.push({id:T.id,name:T.name||T.id,archived:!!T.archivedAt,usd:ie.totalUsd,turns:ie.turns,providers:Object.keys(ie.byProvider)});for(let[D,R]of Object.entries(ie.byBox))A.push({trackId:T.id,trackName:T.name||T.id,boxId:D,...R})}let N=r.since!=null?PT(r.since,o):M,O=[];for(let T=N,j=0;T!=null&&T<r.until&&j<Bj;T=Uj(T,o),j++){let Z=x.get(T);O.push(Z?{t:T,total:Bs(Z.total),byProvider:Hj(Z.byProvider)}:{t:T,total:0,byProvider:{}})}return k.sort((T,j)=>j.usd-T.usd),A.sort((T,j)=>j.usd-T.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:Wo(),window:r.key,since:r.since,until:r.until,bucketMs:o,series:O,fleet:{totalUsd:Bs(d),turns:f,repricedTurns:p,unpricedTurns:m,unpricedModels:[...g],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:k.length,tracksSilentLive:I},prior:a!=null?{totalUsd:Bs(h),since:r.since-a,until:r.since}:null,tracks:k.slice(0,50),boxes:A.slice(0,jj)}}et();import js from"node:fs/promises";import Wj from"node:fs";import Je from"node:path";import qj from"node:os";import{execFile as Kj}from"node:child_process";import{promisify as Gj}from"node:util";var Vj=Gj(Kj),FT=process.env.BOSTRAT_STATE_DIR||Je.join(process.env.HOME||qj.tmpdir(),".bostrat"),yu=!1,Fh=null;async function Uh(e,t){let n=await js.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await js.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await js.mkdir(Je.dirname(t),{recursive:!0}),await js.copyFile(e,t),!0)}var zj=new Set([".claude",".codex",".cursor",".credentials.json","auth.json",".claude.json","api-token","node.env","certs","integrations.json","gcloud","application_default_credentials.json",".gemini"]);async function bu(e,t,{skip:n=null}={}){let r=0,o=await js.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(zj.has(s.name)||n?.has(s.name))continue;let a=Je.join(e,s.name);s.isDirectory()?r+=await bu(a,Je.join(t,s.name)):s.isFile()&&(r+=await Uh(a,Je.join(t,s.name))?1:0)}return r}async function UT({stateDir:e=FT,dest:t=process.env.BOSTRAT_SYNC_DIR||`${FT}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await js.mkdir(t,{recursive:!0});let o=0;o+=await Uh(Je.join(e,"tracks.json"),Je.join(t,"tracks.json"))?1:0,o+=await Uh(Je.join(e,"events.jsonl"),Je.join(t,"events.jsonl"))?1:0,o+=await bu(Je.join(e,"events"),Je.join(t,"events")),o+=await bu(Je.join(e,"memory"),Je.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await bu(Je.join(u.cwd,".bostrat"),Je.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>Vj("git",["-C",t,...u]);Wj.existsSync(Je.join(t,".git"))||(await a("init","-q"),await a("config","user.email","agent@bostrat.local"),await a("config","user.name","bostrat state sync")),n&&/^https:\/\/github\.com\//i.test(n)&&(await a("config","--get","credential.useHttpPath").then(()=>!0).catch(()=>!1)||await Gu(t).catch(()=>{}));let i=!1,{stdout:c}=await a("status","--porcelain");c.trim()&&(await a("add","-A"),await a("commit","-q","-m",`state sync ${new Date().toISOString()}`),i=!0);let l=!1;if(n&&(i||yu))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,yu&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),yu=!1,Fh=null}catch(u){yu=!0;let d=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
581
|
+
`).slice(-3).join(" | ").slice(0,300);d!==Fh&&(Fh=d,console.warn(`[state-sync] PUSH FAILING (retrying each cycle; off-box copy is STALE): ${d}`))}return{staged:o,committed:i,pushed:l}}catch(o){return{staged:0,committed:!1,pushed:!1,error:o?.message||String(o)}}}rm();Pt();Lr();Ve();Js();import{execFile as Jj}from"node:child_process";import{promisify as Yj}from"node:util";import{randomUUID as Xj}from"node:crypto";import Zj from"node:fs";import Qj from"node:os";import eL from"node:path";var Rr=Yj(Jj),ku=e=>new Promise(t=>setTimeout(t,e)),$r=ud,Hh=process.env.BOSTRAT_USAGE_PROBE_CWD||eL.join(Qj.homedir(),".bostrat","usage-probe"),tL=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,nL=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,rL=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,HT=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,Ir=null,Mi=null,Ni=null;async function oL(){try{return await Rr(F,H("has-session","-t",$r)),!0}catch{return!1}}function sL(){Ir&&clearTimeout(Ir),Ir=setTimeout(()=>aL().catch(()=>{}),tL),Ir.unref&&Ir.unref()}async function aL(){Ir&&(clearTimeout(Ir),Ir=null);try{await Rr(F,H("kill-session","-t",$r))}catch{}}async function iL(){let e=Date.now()+rL;for(;;){let t=await Wh().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(t)){await ku(500);return}if(Date.now()>e)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await ku(500)}}async function cL(){if(await oL())return;if(!await zn())throw new Error("host tmux server not running \u2014 start globo-tmux.service");Zj.mkdirSync(Hh,{recursive:!0}),await Fn(Hh);let e=Xj();await Rr(F,H("new-session","-d","-s",$r,"-c",Hh,"-x","200","-y","60",...ua()));let t=`${ld} --session-id ${e}`;await Rr(F,H("send-keys","-t",$r,t,"Enter")),await iL()}async function Wh(){let{stdout:e}=await Rr(F,H("capture-pane","-t",$r,"-p"));return e}async function lL(){await cL(),await Rr(F,H("send-keys","-t",$r,"-l","/usage")),await Rr(F,H("send-keys","-t",$r,"Enter"));let e=Date.now()+nL,t="";for(;await ku(400),t=await Wh(),!(/Current\s+(session|week)/i.test(t)||/\d+%\s*used/i.test(t)||Date.now()>e););return await ku(900),t=await Wh(),await Rr(F,H("send-keys","-t",$r,"Escape")).catch(()=>{}),sL(),t}function uL(e){let t=String(e||"").split(`
|
|
582
|
+
`).map(s=>s.replace(/\s+$/,"").trim()),n=s=>{let a=null,i=null;for(let c=s+1;c<Math.min(s+5,t.length)&&!/^Current\s/i.test(t[c]);c++){let l=t[c].match(/(\d+)%\s*used/i);l&&a===null&&(a=Number(l[1]));let u=t[c].match(/^Resets\s+(.+)$/i);u&&i===null&&(i=u[1].trim())}return a===null?null:{pct:a,resets:i||null}},r=s=>{let a=t.findIndex(i=>s.test(i));return a<0?null:n(a)},o=[];return t.forEach((s,a)=>{let i=s.match(/^Current week \(([^)]+)\)$/i);if(!i||/all models/i.test(i[1]))return;let c=n(a);c&&o.push({model:i[1].trim(),...c})}),{session:r(/^Current session\b/i),week:r(/^Current week \(all models\)/i)||r(/^Current week$/i),weekModels:o.length?o:null}}function dL(e){let t=String(e||"").split(`
|
|
583
|
+
`).map(s=>s.replace(/\s+$/,"")),n=t.findIndex(s=>/What's contributing/i.test(s));if(n<0)return null;let r=/(Usage credits|Esc to cancel|d to day · w to week|^\s*d to day)/i,o=[];for(let s=n+1;s<t.length&&!r.test(t[s]);s++)t[s].trim()&&o.push(t[s].trim());return o.length?o:null}async function WT({fresh:e=!1}={}){return!e&&Mi&&Date.now()-Mi.at<HT?Mi.data:Ni||(Ni=(async()=>{try{let t=await lL(),n=uL(t),r={...n,contributors:dL(t),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Mi={at:Date.now(),data:r},r}catch(t){let n={session:null,week:null,contributors:null,subscription:!1,error:t?.message||String(t),capturedAt:Date.now()};return Mi={at:Date.now()-(HT-3e3),data:n},n}finally{Ni=null}})(),Ni)}var fL=262144;function pL(e){let t=Number(e);if(!Number.isFinite(t)||t<0)return null;let n=Math.floor(t/3600),r=Math.round(t%3600/60);return n>=48?`in ${Math.round(n/24)}d`:n>0?`in ${n}h ${r}m`:`in ${Math.max(1,r)}m`}var mL=new Set([300]),hL=new Set([10080]);function wL(e){let t=Number(e);return!Number.isFinite(t)||t<=0?null:mL.has(t)?"session":hL.has(t)?"week":"other"}function gL(e){let t=Number(e);return!Number.isFinite(t)||t<=0?"Window":t%1440===0?`Window \xB7 ${t/1440}d`:t%60===0?`Window \xB7 ${t/60}h`:`Window \xB7 ${t}m`}function yL(e,t,n){if(!e||typeof e!="object")return null;let r=Number(e.used_percent);if(!Number.isFinite(r))return null;let o=null,s=Number(e.resets_at),a=Number(e.resets_in_seconds);return Number.isFinite(s)&&s>0?o=s*1e3:Number.isFinite(a)&&a>=0&&(o=(Number.isFinite(t)?t:n)+a*1e3),{pct:Math.round(r),resets:o!=null?pL((o-n)/1e3):null,resetsAt:o}}var Pi=e=>({session:null,week:null,other:null,planType:null,credits:null,limitReached:null,sawTokenCount:e});function bL(e,t=Date.now()){let n=String(e||"").split(`
|
|
584
|
+
`),r=!1;for(let o=n.length-1;o>=0;o--){let s=n[o].trim();if(!s||s[0]!=="{"||!s.includes("token_count"))continue;let a;try{a=JSON.parse(s)}catch{continue}let i=a?.payload;if(i?.type!=="token_count")continue;r=!0;let c=i.rate_limits;if(!c)return Pi(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=Pi(r);[c.primary,c.secondary].forEach((p,m)=>{let h=yL(p,l,t);if(!h)return;let g=wL(p.window_minutes);g===null&&(g=m===0?"session":"week"),g==="session"&&!u.session?u.session=h:g==="week"&&!u.week?u.week=h:(u.other??=[]).push({label:gL(p.window_minutes),...h})}),u.planType=typeof c.plan_type=="string"?c.plan_type:null;let f=c.credits;return f&&(f.has_credits||f.unlimited)&&(u.credits={balance:String(f.balance??""),unlimited:!!f.unlimited}),u.limitReached=typeof c.rate_limit_reached_type=="string"&&c.rate_limit_reached_type||(c.spend_control_reached?"spend_control":null),u}if(c.primary_used_percent!=null||c.secondary_used_percent!=null){let l=u=>Number.isFinite(Number(u))?{pct:Math.round(Number(u)),resets:null,resetsAt:null}:null;return{...Pi(r),session:l(c.primary_used_percent),week:l(c.secondary_used_percent)}}return Pi(r)}return Pi(r)}async function qh(e){let t={provider:"codex",supported:!0,contributors:null};try{let n=await Br(e?.cwd,e?.sessionId);if(!n)return{...t,session:null,week:null,subscription:!1,error:"no codex rollout found for this box yet",capturedAt:Date.now()};let r=await ct(n.file,fL),o=bL(r);return{...t,session:o.session,week:o.week,other:o.other,planType:o.planType,credits:o.credits,limitReached:o.limitReached,subscription:!!(o.session||o.week||o.other?.length),capturedAt:n.mtimeMs||Date.now()}}catch(n){return{...t,session:null,week:null,subscription:!1,error:n?.message||String(n),capturedAt:Date.now()}}}async function qT(e,{fresh:t=!1}={}){let n=new Set;for(let o of e||[])for(let s of o.boxes||[])s.agent&&n.add(s.agent);let r=[];if((n.has("claude")||n.size===0)&&r.push(await xu({agent:"claude"},{fresh:t})),n.has("codex")){let o=[],s=[...e||[]].sort((i,c)=>(i.archivedAt?1:0)-(c.archivedAt?1:0)||(c.createdAt||0)-(i.createdAt||0));for(let i of s)for(let c of i.boxes||[])c.agent==="codex"&&o.push({...c,cwd:c.cwd||i.cwd});let a=null;for(let i of o.slice(0,8)){let c=await qh(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await qh(null))}return n.has("cursor")&&r.push(await xu({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function xu(e,{fresh:t=!1}={}){let n=e?.agent||"claude";return n==="codex"?qh(e):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await WT({fresh:t})}}Hn();et();et();function kL(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}var xL=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Su=(e,t)=>Object.assign(new Error(e),{status:t});async function KT(e,t){if(!kL(t))throw Su("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${ke(t)}${e}`,{headers:xL(t)})}catch(o){throw Su(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw Su(r?.error||`plane replied ${n.status}`,n.status);return r}async function GT(e,{max:t=10,env:n=process.env}={}){let r=new URLSearchParams({q:e||""});return t&&r.set("max",String(t)),(await KT(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function VT(e,{env:t=process.env}={}){if(!e)throw Su("message id required",400);return(await KT(`/v1/gmail/messages/${encodeURIComponent(e)}`,t)).message}Fa();dl();function Kh(e){let t=e.split(".");if(t.length!==4)return null;let n=0;for(let r of t){let o=Number(r);if(!Number.isInteger(o)||o<0||o>255)return null;n=n*256+o}return n>>>0}function SL(e){let[t,n]=e.split("/"),r=Kh(t);if(r==null)return null;let o=n==null?32:Number(n);if(!Number.isInteger(o)||o<0||o>32)return null;let s=o===0?0:4294967295<<32-o>>>0;return a=>(a&s)===(r&s)}function zT(e){return(e||"").split(",").map(t=>t.trim()).filter(Boolean).map(SL).filter(Boolean)}function vL(e){let t=e.headers["fly-client-ip"];return typeof t=="string"&&t.trim()?t.trim():(e.socket&&e.socket.remoteAddress||"").replace(/^::ffff:/,"")}function JT(e){let t=zT(e),n=String(e||"").trim().toLowerCase()==="open";return{open:n,clientIp:vL,allowed(r){if(n)return!0;let o=Kh((r||"").replace(/^::ffff:/,""));return o==null?!1:t.some(s=>s(o))}}}function YT(e,t){let n=zT(e);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=Kh(o);if(s==null||!n.some(i=>i(s)))return!1;let a=t(r);return!!(a&&a.view)}}}import XT from"node:crypto";import vu from"node:fs";import TL from"node:path";function ZT(e=process.env,{stateDir:t=Si,log:n=console}={}){let r=String(e.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=TL.join(t,"api-token");try{let a=vu.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=XT.randomBytes(32).toString("base64url");try{vu.mkdirSync(t,{recursive:!0}),vu.writeFileSync(o,s+`
|
|
585
|
+
`,{mode:384}),vu.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function Di(e){let t=e?.socket?.remoteAddress||"";if(!t)return!1;let n=t.replace(/^::ffff:/,"");return n==="::1"||n==="127.0.0.1"||/^127\./.test(n)}function QT(e,t){if(!e||!t)return!1;let n=Buffer.from(String(e)),r=Buffer.from(String(t));return n.length!==r.length?!1:XT.timingSafeEqual(n,r)}function eA(e,t=null){let n=e?.headers?.["x-bostrat-token"];if(typeof n=="string"&&n.trim())return n.trim();let r=e?.headers?.authorization||"",o=/^Bearer\s+(.+)$/i.exec(r)?.[1];if(o)return o.trim();try{return(t??new URL(e.url,"http://node")).searchParams.get("token")||""}catch{return""}}var AL=[/^\/api\/github\/token\b/,/^\/api\/gmail\b/],EL=e=>AL.some(t=>t.test(e));function tA({token:e,enforce:t=!0,log:n=console}={}){return function(o,s,a){let i=o.path||o.url||"";if(!i.startsWith("/api"))return a();let c=eA(o);return QT(c,e)?a():EL(i)?(n.log?.(`[perimeter] refused ${o.method} ${i} \u2014 sensitive route requires the node API token`),s.status(401).json({error:"this route requires the node API token"})):!t||Di(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 nA(e,t,{enforce:n=!0}={}){return!n||QT(eA(e),t)?!0:Di(e)}Gt();_l();_l();import _L from"node:http";import IL from"node:net";function Tu(e){if(!e)return null;let t=String(e).replace(/:\d+$/,"").toLowerCase();if(/^[\d.]+$/.test(t)||t.includes(":"))return null;let n=t.split(".");if(n.length<3)return null;let r=n[0];return/^[a-z0-9-]+$/.test(r)?r:null}function Au(e){let t=Tu(e.headers.host);return t?{slug:t,view:Mp(t)}:null}var RL=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),$L=e=>{let t=RL(e.name);return`<!doctype html><meta charset="utf-8">
|
|
580
586
|
<meta http-equiv="refresh" content="3">
|
|
581
587
|
<title>${t} \u2014 starting</title>
|
|
582
588
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
583
589
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
584
|
-
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function
|
|
590
|
+
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function rA(e,t,n){let r=Au(e);if(!r)return n();let{view:o,slug:s}=r;if(!o)return t.statusCode=404,t.setHeader("content-type","text/plain"),t.end(`bostrat: no view named "${s}"`);if(o.state==="starting")return t.statusCode=503,t.setHeader("content-type","text/html"),t.end($L(o));if(o.state!=="live"&&o.state!=="unhealthy")return t.statusCode=404,t.setHeader("content-type","text/plain"),t.end(`bostrat: view "${s}" is ${o.state} \u2014 restart it from the Views tab`);let a={...e.headers,host:`127.0.0.1:${o.port}`,"x-forwarded-host":e.headers.host,"x-forwarded-proto":e.headers["x-forwarded-proto"]||"http"};delete a.authorization,delete a.cookie;let i=_L.request({host:"127.0.0.1",port:o.port,method:e.method,path:e.url,headers:a},c=>{t.writeHead(c.statusCode||502,c.headers),c.pipe(t)});i.on("error",()=>{t.headersSent?t.destroy():(t.statusCode=502,t.setHeader("content-type","text/plain"),t.end(`bostrat: view "${s}" did not answer on :${o.port}`))}),e.pipe(i),e.on("aborted",()=>i.destroy())}function oA(e,t,n){let r=Au(e);if(!r)return!1;let{view:o,slug:s}=r;if(!o||o.state!=="live"&&o.state!=="unhealthy")return t.write(`HTTP/1.1 404 Not Found\r
|
|
585
591
|
\r
|
|
586
592
|
bostrat: no live view "${s}"\r
|
|
587
|
-
`),t.destroy(),!0;let a=
|
|
593
|
+
`),t.destroy(),!0;let a=IL.connect(o.port,"127.0.0.1",()=>{let c=[`${e.method} ${e.url} HTTP/1.1`];for(let[l,u]of Object.entries(e.headers))l!=="host"&&(l==="authorization"||l==="cookie"||c.push(`${l}: ${Array.isArray(u)?u.join(", "):u}`));c.push(`host: 127.0.0.1:${o.port}`),a.write(c.join(`\r
|
|
588
594
|
`)+`\r
|
|
589
595
|
\r
|
|
590
|
-
`),n?.length&&a.write(n),t.pipe(a),a.pipe(t)}),i=()=>{a.destroy(),t.destroy()};return a.on("error",i),t.on("error",i),!0}function
|
|
591
|
-
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,pL)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${mL}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=bL.exec(i);if(!u||!u[1].trim()||!u[2].trim())throw new Error(`bullet ${r.length+1} must be \`**Short summary** \u2014 the item, one sentence.\` (bold summary title, em dash, then the change) \u2014 got "${i.slice(0,80)}". Rewrite it and re-publish.`);i=`**${u[1].trim().replace(/[:.]\s*$/,"")}** \u2014 ${u[2].trim()}`;let f=eE(i);if(f)throw new Error(`bullet ${r.length+1} mentions "${f}" \u2014 the product changelog never carries business, security/tenancy or internal-ops items, PR numbers, branch names or codenames; drop the bullet or rewrite it as the user-visible change and re-publish.`);if(i.length>QT)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${QT}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=eE(n);if(o)throw new Error(`headline mentions "${o}" \u2014 the product changelog never carries business, security/tenancy or internal-ops items; rewrite it and re-publish.`)}if(!r.length)throw new Error("no `- ` bullets found \u2014 an entry is a bullet list, optionally under a `# headline`");if(r.length>ZT)throw new Error(`${r.length} bullets \u2014 over the ${ZT}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function xL(e,{repos:t=Mh()}={}){let n=new Set(t);return(await Xr()).filter(r=>r.state==="merged"&&(r.resolvedAt??0)>=e&&n.has(r.repo)).sort((r,o)=>(r.resolvedAt??0)-(o.resolvedAt??0)).map(r=>({repo:r.repo,number:r.number,title:r.title||null,url:r.url||null,headRef:r.headRef||null,mergedAt:r.resolvedAt??null,excluded:yL({title:r.title,headRef:r.headRef})}))}async function rE(e,t){let n=await xL(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function oE(e,{md:t,day:n=null}){let r=kL(t),o=n==null||n===""?hL():String(n);if(!tE.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await _({id:e.id,cwd:e.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:e.leadBox||null,payload:{ts:Date.now(),day:o,title:r.title,bullets:r.bullets}});return{day:o,title:r.title,bullets:r.bullets,eventId:s.id}}async function sE(e,{day:t}){let n=String(t||"");if(!tE.test(n)||Number.isNaN(Date.parse(n)))throw new Error(`invalid day "${t}" \u2014 expected YYYY-MM-DD`);let r=await _({id:e.id,cwd:e.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:e.leadBox||null,payload:{ts:Date.now(),day:n,title:null,bullets:[],retract:!0}});return{day:n,retracted:!0,eventId:r.id}}ns();wn();ss();ue();ns();Qe();fr();wn();Ca();rl();Ba();import{execFile as SL}from"node:child_process";import{promisify as vL}from"node:util";var Ph=vL(SL),aE=3e4,TL=30*6e4,EL=aE,AL=10*6e4,_L=10080*6e4,IL=60*6e4,RL=8,$L=1440*6e4,OL=3,iE="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Nh=60*6e4,CL=15*6e4,ML=9e4,NL=1440*6e4,yu=new Map,bu=new Map,PL=/could not resolve|not found|no pull requests?/i,DL=e=>PL.test(String(e?.message||e||""));function BL(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<Nh)}var cE={fast:EL,slow:AL,decay:IL};function lE(e,t){return e.armedAt&&t-e.armedAt<TL||BL(e,t)||uE(e,t,{grace:0})?"fast":t-(e.firstSeen||0)>_L?"decay":"slow"}function jL(e,t){return cE[lE(e,t)]}function Oi(e,t,n,r){let o=e.lanes?.get(Q(t.repo,t.number))||{};return nl({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function LL(e,t){yn("watch");try{let{stdout:n}=await Ph("gh",["pr","view",String(t),"--repo",e,"--json",iE],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await qt({slug:e})}});return JSON.parse(n||"null")}catch(n){throw lr(n)&&ur(n),n}}async function FL(e){yn("watch");try{let{stdout:t}=await Ph("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",iE],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await qt({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw lr(t)&&ur(t),t}}async function UL(){let e=new Map;for(let t of await ae().catch(()=>[]))e.set(t.id,t);return e}async function HL(e,t){await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...t}})}async function WL(e,t){let n=await Qr(e);return n?es(n,t):null}async function qL(e,t,n){let r=await Qr(e);if(!r)throw new Error(`no local checkout for ${e}`);return ts(r,t,{method:n})}async function KL(e,t){let n=await Qr(e);if(!n)throw new Error(`no local checkout for ${e}`);return Pa(n,t)}async function GL(e,t){await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...t}})}async function Os(e,t,n,r,o){if(!await r.disarm(e.repo,e.number).catch(()=>null))return;let a=n.get(e.trackId);a&&await r.emitDisarmed(a,{number:Number(e.number),url:t?.url||e.url,title:t?.title||e.title,branch:t?.headRefName||e.headRef,repo:e.repo,reason:o}).catch(()=>{})}var VL=10*6e4;async function zL(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>$L){await Os(e,null,t,n,"auto-merge expired \u2014 checks never settled within 24h");return}let o=await n.detail(e.repo,e.number).catch(()=>null);if(!o)return;if(o.checks.failed>0){let a=o.checks.failing.map(i=>i.name).join(", ");await Os(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await Os(e,o,t,n,`conflicts with ${o.baseRefName}`);return}if(!o.checks.total){let a=Math.max(e.autoMerge?.armedAt||0,Date.parse(o.updatedAt||"")||0);if((n.now??Date.now())-a<VL)return;await Os(e,o,t,n,o.checks.unavailable?"this token can't read the checks":"no CI checks appeared");return}if(o.checks.pending>0||o.mergeable==="UNKNOWN"||o.mergeStateStatus==="UNKNOWN"||o.isDraft)return;let s=await n.lookup(e.repo,e.number).catch(()=>null);if(s?.autoMerge){if(o.mergeStateStatus==="BEHIND"){try{await n.updateBranch(e.repo,e.number)}catch(a){let i=String(a?.message||a).trim();/no new commits|already up.to.date/i.test(i)||await Os(e,o,t,n,i||`couldn't update with ${o.baseRefName}`)}return}try{await n.merge(e.repo,e.number,s.autoMerge.method||"merge")}catch(a){if(/already merged|not open|is closed/i.test(String(a?.message||a)))return;await Os(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await dE(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function Cs(e,t,n){await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:t,...n}})}async function JL(e,t){try{return await Ph("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function $i(e,t,n,r=()=>!0){return(await L(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var Ms=(e,t)=>({number:Number(e.number),url:t?.url||e.url,title:t?.title||e.title,branch:t?.headRefName||e.headRef,repo:e.repo});function uE(e,t,{grace:n=ML}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=NL}async function YL(e,t,n,r){let o=r.now??Date.now();if(!uE(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||s.archivedAt||s.transferredTo)return!1;let a=(s.boxes||[]).find(f=>f.name===s.leadBox);return await(r.midTurn??eo)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await L(s).catch(()=>[])).some(f=>f.type==="pr.state.changed"&&f.payload?.number===Number(e.number))?(await(r.claim??hn)(e.repo,e.number).catch(()=>null),!1):await(r.claim??hn)(e.repo,e.number).catch(()=>null)?(await Cs(s,"opened",{...Ms(e,t),announcedBy:"watch"}).catch(()=>{}),console.log(`[pr-watch] announced passive link ${e.repo}#${e.number} \u2192 track ${s.id} (no active door claimed it)`),!0):!1}async function XL(e,t,n,r){let o=r.now??Date.now(),s=t.headRefOid||null,a=n.get(e.trackId)||null;if(s&&(e.ciFix?.headSha&&s!==e.ciFix.headSha&&await zc(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await Yc(e.repo,e.number,{headSha:s}).catch(()=>{})),s&&s!==e.lastHeadSha){let i=!e.lastHeadSha,c=!!(e.ciFix||e.reconcile||e.ciFixAwait||e.reconcileAwait),l=!!e.baseFoldAt&&o-e.baseFoldAt<CL;await qk(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await Wk(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await $i(a,"pushed",e.number,f=>f.payload?.sha===s)&&await Cs(a,"pushed",{...Ms(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>Nh)await Jc(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i?.checks){if(i.checks.failed>0)await Jc(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Oi(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await $i(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await Cs(a,"ci-fixed",{...Ms(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await Jc(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>Nh)await Xc(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await Xc(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Oi(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await $i(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await Cs(a,"reconciled",{...Ms(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await Xc(e.repo,e.number).catch(()=>{})}}}}async function ZL(e,t,n,r){let o=Oi(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await $i(s,"closed",e.number)&&await Cs(s,"closed",{...Ms(e,t),githubAt:o.githubAt,latencyMs:o.latencyMs,lane:o.lane}).catch(()=>{}),await r.notifyIfResolved(e.repo,e.number,"closed").catch(()=>{}),await r.mark(e.repo,e.number,"closed").catch(()=>{})}async function QL(e,t,n){Oi(n,e,"lost",null);let r=t.get(e.trackId);r&&!await $i(r,"lost",e.number)&&await Cs(r,"lost",Ms(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function dE(e,t,n,r,o={}){let s=Oi(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await L(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(e.number))||await r.emitMerged(a,{number:Number(e.number),url:t.url||e.url,title:t.title||e.title,branch:t.headRefName||e.headRef,repo:e.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(e.repo,e.number).catch(()=>{}),await r.archiveIfArmed(e.repo,e.number).catch(()=>{}),await r.mark(e.repo,e.number,"merged").catch(()=>{})}async function eF(e={}){let{now:t=Date.now(),auth:n=Wf,view:r=LL,list:o=FL,links:s=Vk,trackMap:a=UL,emitMerged:i=HL,mark:c=ir,archiveIfArmed:l=kn,notifyIfArmed:u=xn,notifyIfResolved:f=Bl,batchThreshold:d=OL,detail:p=WL,merge:m=qL,updateBranch:h=KL,lookup:g=qe,disarm:x=Oa,emitDisarmed:k=GL,localHasCommit:E=JL,claim:I=hn,midTurn:M=eo}=e;if(!(await n().catch(()=>null))?.authenticated||el()>0)return 0;let T=(await s().catch(()=>[])).filter(U=>t-(yu.get(Q(U.repo,U.number))||0)>=jL(U,t));if(!T.length)return 0;let j=Date.now(),Z=new Map,fe=new Map;for(let U of T){let pe=lE(U,t);Z.set(Q(U.repo,U.number),{lane:pe,intervalMs:cE[pe]}),yu.set(Q(U.repo,U.number),t),fe.has(U.repo)||fe.set(U.repo,[]),fe.get(U.repo).push(U)}let ie=await a().catch(()=>new Map),D={now:t,lanes:Z,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:f,detail:p,merge:m,updateBranch:h,lookup:g,disarm:x,emitDisarmed:k,localHasCommit:E,claim:I,midTurn:M};for(let[U,pe]of fe){let Se,He=new Set;if(pe.length>d)Se=await o(U).catch(()=>null);else{Se=new Map;for(let le of pe)try{let q=await r(U,le.number);q?.number!=null&&Se.set(Number(q.number),q)}catch(q){DL(q)&&He.add(Q(le.repo,le.number))}}if(Se)for(let le of pe){let q=Q(le.repo,le.number),J=Se.get(Number(le.number));if(!J){if(He.has(q)){let ee=(bu.get(q)||0)+1;bu.set(q,ee),ee>=RL&&(await QL(le,ie,D),bu.delete(q),yu.delete(q))}continue}bu.delete(q),J.state==="MERGED"||J.state==="CLOSED"?(J.state==="MERGED"?await dE(le,J,ie,D):await ZL(le,J,ie,D),yu.delete(q)):(await YL(le,J,ie,D).catch(()=>{}),await XL(le,J,ie,D).catch(()=>{}),le.autoMerge&&await zL(le,ie,D).catch(()=>{}))}}let R={};for(let{lane:U}of Z.values())R[U]=(R[U]||0)+1;return console.log(`[pr-watch] probed ${T.length} link(s) (${Object.entries(R).map(([U,pe])=>`${U} ${pe}`).join(", ")}) in ${Date.now()-j}ms`),T.length}function fE(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{await eF()}catch{}finally{e=!1}}},aE)}ue();wn();Un();fr();function pE(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function tF(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var nF=new Set(["merged","closed","lost"]);function rF(e,t){if(!e?.blocked)return null;let n=pE(t);if(e.blocked==="red")return t.checks.failed===0&&t.checks.total>0?{change:"ci-fixed",reason:"red"}:null;if(e.blocked==="conflict")return t.mergeable==="MERGEABLE"&&n!=="red"?{change:"reconciled",reason:"conflict"}:null;if(e.blocked==="behind"){let r=t.headSha??null;return n===null&&r&&e.sha&&r!==e.sha?{change:"reconciled",reason:"behind"}:null}return null}function oF(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function sF(e,t,n,r,o){return(await L(e).catch(()=>[])).some(a=>{if(a.type!=="pr.state.changed"||a.payload?.number!==Number(n))return!1;let i=a.payload?.change;return i===t?a.payload?.sha&&r?a.payload.sha===r:(a.payload?.ts||0)>=o:i==="pushed"&&!!r&&a.payload?.sha===r})}async function aF(e,t,n){await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:t,...n}})}async function mE(e,t={}){let{now:n=Date.now(),slugOf:r=me,lookup:o=qe,note:s=Kk,tracks:a=async()=>(await ae()).filter(f=>!f.archivedAt&&!f.transferredTo),asked:i=sF,emitChange:c=aF}=t,l=[],u=null;for(let f of e??[]){if(!f?.prs?.length)continue;let d=await r(f.path).catch(()=>null);if(d)for(let p of f.prs)try{if(!tF(p))continue;let m=await o(d,p.number).catch(()=>null);if(!m?.trackId||nF.has(m.state))continue;let{prev:h}=await s(d,p.number,{blocked:pE(p),sha:p.headSha??null,at:n});if(!h)continue;let g=rF(h,p);if(!g||oF(m,g.change))continue;u??=new Map((await a()).map(I=>[I.id,I]));let x=u.get(m.trackId);if(!x)continue;let k=p.headSha??null;if(await i(x,g.change,p.number,k,h.at||0))continue;let E={number:Number(p.number),url:p.url||m.url,title:p.title||m.title,branch:p.headRefName||m.headRef,repo:d,sha:k,delegated:!1,reason:g.reason};g.change==="ci-fixed"?E.passed=p.checks.passed:E.base=p.baseRefName||null,await c(x,g.change,E),l.push({change:g.change,number:p.number,reason:g.reason})}catch{}}return l}tt();ue();fr();import{randomUUID as iF}from"node:crypto";var cF=2e4,hE=3,Ns=new Map,Dh=new Set;async function wE(e,t,n){let r=await n(e).catch(()=>[]),o=null;for(let s of r)s.type==="box.fullscreen.detected"&&s.boxId===t?o=s.payload?.offerId||null:s.type==="box.fullscreen.answered"&&o&&s.payload?.offerId===o&&(o=null);return o}async function lF(e={}){let{sessions:t=null,tracks:n=null,append:r=_,read:o=L,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await Ot(),i=n??await ae()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(u.archivedAt||u.transferredTo||!u.leadBox)continue;let f=(u.boxes||[]).find(p=>p.name===u.leadBox);if(!f||f.agent!=="claude"||f.state!=="live")continue;let d=c.get(f.name);if(d)if(l.add(f.name),d.alternateOn){let p=(Ns.get(f.name)||0)+1;Ns.set(f.name,p),p===hE&&(Dh.add(f.name),await wE(u,f.name,o)||await r(u,{type:"box.fullscreen.detected",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:iF(),box:f.name,agent:f.agent}}).catch(()=>{}))}else{let p=(Ns.get(f.name)||0)>=hE;if(Ns.delete(f.name),p||!Dh.has(f.name)){Dh.add(f.name);let m=await wE(u,f.name,o);m&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:f.name,provider:f.agent,payload:{ts:s,offerId:m,action:"cleared"}}).catch(()=>{})}}}for(let u of[...Ns.keys()])l.has(u)||Ns.delete(u)}var Ci=null,Bh=!1;function gE(){return Ci?()=>{}:(Ci=setInterval(()=>{Bh||(Bh=!0,lF().catch(()=>{}).finally(()=>{Bh=!1}))},cF),Ci.unref?.(),()=>{clearInterval(Ci),Ci=null})}Yo();Ep();ue();ns();ss();Yo();import{randomUUID as uF}from"node:crypto";async function dF(){let e=await Promise.resolve().then(()=>(fr(),cv));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var jh={listRepoPrs:Vt,readEvents:L,appendEvent:_,sayDeps:dF,currentBranch:rs};async function fF(e,t=jh,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??rs)(e.workspace?.kind==="multi"?null:e.cwd).catch(()=>null)||e?.workspace?.branch;if(!r)return null;let o;try{o=await t.listRepoPrs(e.cwd,{caller:"pr-exists"})}catch{return null}let s=(o||[]).find(a=>a.headRefName===r);return s?{number:s.number,url:s.url??null,title:s.title??null}:null}async function Lh(e,t,{actions:n,notify:r=!1}={},o=jh){let s=t?.payload||{};if(!Array.isArray(n)||!n.includes("pr")||s.toPr||Array.isArray(s.repos)&&s.repos.length||!s.branch)return null;let a=await fF(e,o,{branch:s.branch});if(!a)return null;let i={id:e.id,cwd:e.cwd};await o.appendEvent(i,{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:t.boxId,payload:{ts:Date.now(),offerId:s.offerId,action:"pr-exists",actions:n,...r?{notify:!0}:{},existingPr:{number:a.number}}});let c=uF();return await o.appendEvent(i,{type:"pr.exists.offered",actor:"system",source:"orchestrator",boxId:t.boxId,payload:{ts:Date.now(),offerId:c,srcOfferId:s.offerId??null,branch:s.branch,base:s.base??null,pr:a,actions:n,...r?{notify:!0}:{},offeredRebase:Array.isArray(s.offered)&&s.offered.includes("rebase")}}),{offerId:c,pr:a}}async function Fh(e,{offerId:t,choice:n}={},r=jh){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');let o=await r.readEvents(e),s=o.find(u=>u.type==="pr.exists.offered"&&u.payload?.offerId===t);if(!s)throw new Error("unknown offer");if(o.some(u=>u.type==="pr.exists.answered"&&u.payload?.offerId===t))throw new Error("offer already answered");let a=s.payload||{},i=Array.isArray(a.actions)&&a.actions.length?a.actions:["commit","push","pr"],c=null,l=null;if(n==="push"){let{sayToLead:u}=await r.sayDeps();l=zo.filter(f=>f!=="pr"&&(i.includes(f)||f==="push")),c=await u(e.id,Jo(l,{branch:a.branch??null,base:a.base??null,toPr:{number:a.pr?.number??null},offeredRebase:a.offeredRebase===!0}),{kind:"push-go"})}else if(n==="new"){let{sayToLead:u}=await r.sayDeps();l=i,c=await u(e.id,Mk(i,{branch:a.branch??null,base:a.base??null,prNumber:a.pr?.number??null,offeredRebase:a.offeredRebase===!0}),{kind:"pr-go"})}if(await r.appendEvent({id:e.id,cwd:e.cwd},{type:"pr.exists.answered",actor:"user",source:"orchestrator",boxId:s.boxId,payload:{ts:Date.now(),offerId:t,choice:n}}),n!=="dismiss"&&a.notify===!0){let{queueShipCompleteNotify:u}=await r.sayDeps();u(e.id,{actions:l})}return{say:c,choice:n}}import Bn from"node:fs/promises";import Ps from"node:path";import TE from"node:os";import yE from"node:fs/promises";import ku from"node:path";import{execFile as pF}from"node:child_process";import{promisify as mF}from"node:util";import{fileURLToPath as hF}from"node:url";var wF=mF(pF),kE=ku.dirname(hF(import.meta.url)),gF=ku.resolve(kE,"..","..","client","dist"),yF="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",bF=new Map([...yF].map((e,t)=>[e,t]));function kF(e){let t=[],n=0,r=0;for(let o of e){let s=bF.get(o);if(s===void 0)return t;n+=(s&31)<<r,s&32?r+=5:(t.push(n&1?-(n>>>1):n>>>1),n=0,r=0)}return t}function xF(e){let t=[],n=0,r=0,o=0;for(let s of e.split(";")){let a=[],i=0;if(s)for(let c of s.split(",")){let l=kF(c);l.length!==0&&(i+=l[0],l.length>=4&&(n+=l[1],r+=l[2],o+=l[3],a.push([i,n,r,o])))}t.push(a)}return t}function SF(e,t){if(!e||e.length===0||e[0][0]>t)return null;let n=0,r=e.length-1;for(;n<r;){let o=n+r+1>>1;e[o][0]<=t?n=o:r=o-1}return e[n]}var bE=new Map;async function vF(e){let t;try{t=await yE.stat(e)}catch{return null}let n=bE.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await yE.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:xF(r.mappings||"")};return bE.set(e,o),o}catch{return null}}var Uh=null;function TF(){return Uh||(Uh=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):wF("git",["rev-parse","--short","HEAD"],{cwd:ku.resolve(kE,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),Uh}var EF=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function AF(e,{distDir:t=gF}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(EF)){let[a,i,c,l]=s,u=await vF(ku.join(t,"assets",`${i}.map`)),f=u?SF(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(e.slice(r,s.index)),f&&u.sources[f[1]]){let d=u.sources[f[1]].replace(/^(\.\.\/)+/,"");n.push(`${d}:${f[2]+1}:${f[3]+1}`),o=!0}else n.push(a);r=s.index+a.length}return n.push(e.slice(r)),{stack:n.join(""),symbolicated:o}}async function xE(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await TF();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await AF(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var _F=()=>process.env.BOSTRAT_STATE_DIR||Ps.join(process.env.HOME||TE.tmpdir(),".bostrat"),Rr=()=>Ps.join(_F(),"telemetry"),EE=()=>Ps.join(Rr(),".relay-offsets.json"),IF=50,RF=14,$F=3e4,SE=500,bt=(e,t)=>typeof e=="string"?e.slice(0,t):"";function OF(e,t){if(!e||typeof e!="object")return null;let n=bt(e.name,64);if(!n)return null;let r=Number.isFinite(e.ts)?e.ts:t,o={name:n,ts:Math.abs(r-t)<168*3600*1e3?r:t,receivedAt:t,anonId:bt(e.anonId,64),sessionId:bt(e.sessionId,64),userId:bt(e.userId,64),release:bt(e.release,64),appVersion:bt(e.appVersion,32),route:bt(e.route,128),deviceClass:e.deviceClass==="phone"||e.deviceClass==="desktop"?e.deviceClass:""};return e.props&&typeof e.props=="object"&&!Array.isArray(e.props)&&(JSON.stringify(e.props).length<=8192?o.props=e.props:o.props={clamped:!0}),e.error&&typeof e.error=="object"&&(o.error={kind:bt(e.error.kind,256),message:bt(e.error.message,8192),stack:bt(e.error.stack,32768),fingerprint:bt(e.error.fingerprint,64)}),Array.isArray(e.breadcrumbs)&&(o.breadcrumbs=e.breadcrumbs.slice(-50).map(s=>({t:Number.isFinite(s?.t)?s.t:0,n:bt(s?.n,64),r:bt(s?.r,128)}))),o}var AE=e=>new Date(e).toISOString().slice(0,10),vE=0;async function _E(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,IF):[]).map(s=>OF(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await xE(s);await Bn.mkdir(Rr(),{recursive:!0});let o=Ps.join(Rr(),`${AE(t)}.ndjson`);return await Bn.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
592
|
-
`).join(""),"utf8"),t-
|
|
593
|
-
`).filter(Boolean),f=[];for(let d of u)try{f.push(JSON.parse(d))}catch{}for(let d=0;d<f.length;d+=SE){let p=f.slice(d,d+SE).map(h=>MF(h)),m=await e(`${t.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.ingestKey}`},body:JSON.stringify({events:p})}).catch(()=>null);if(!m||!m.ok)return{sent:o,disabled:!1};o+=p.length}r[s]=i+l+1,await $E(r)}return{sent:o,disabled:!1}}var Hh=null,Wh=!1;function OE(){Hh||(Hh=setInterval(async()=>{if(!Wh){Wh=!0;try{await NF()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{Wh=!1}}},$F),Hh.unref?.())}import vu from"node:fs/promises";import DE from"node:path";import PF from"node:os";var BE=()=>process.env.BOSTRAT_STATE_DIR||DE.join(process.env.HOME||PF.tmpdir(),".bostrat"),jE=()=>DE.join(BE(),"dictation-glossary.json"),DF=100,BF=500,jF=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Tu=null,qh=null,Kh=Promise.resolve();function CE(){let e={},t=Date.now();for(let n of jF)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function LE(){return Tu||(qh??=vu.readFile(jE(),"utf8").then(e=>JSON.parse(e)?.terms??CE()).catch(()=>CE()).then(e=>Tu=e).finally(()=>qh=null),qh)}async function ME(){let e=JSON.stringify({terms:Tu},null,2),t=jE(),n=`${t}.${process.pid}.tmp`;await vu.mkdir(BE(),{recursive:!0}),await vu.writeFile(n,e),await vu.rename(n,t)}function LF(){return Kh=Kh.then(ME,ME),Kh.catch(()=>{})}async function Gh(){return Object.values(await LE()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var FF=e=>Math.max(1,Math.ceil(e.length/4));async function UF(){let e=[],t=0;for(let{term:n}of await Gh()){let r=FF(n);if(!(t+r>BF)&&(e.push(n),t+=r,e.length>=DF))break}return e}async function HF(e,t){let n=await LE(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var NE=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Su=e=>e.toLowerCase();function PE(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function FE(e,t){let n=NE(e),r=NE(t);if(!n.length||!r.length)return[];let o=n.length,s=r.length,a=Array.from({length:o+1},()=>new Array(s+1).fill(0));for(let u=o-1;u>=0;u--)for(let f=s-1;f>=0;f--)a[u][f]=Su(n[u])===Su(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;)Su(n[c])===Su(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(PE(r[l])&&i.add(r[l]),l++);for(;l<s;l++)PE(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await HF(u,"learned");return await LF(),[...i]}var Mi=null;function WF(e,t){let n=null;try{n=JSON.parse(t)}catch{}let r=n?.err_code||n?.error_code||n?.category||"";return e===402||/PAYMENT_REQUIRED/i.test(r)?{status:402,code:"credits_exhausted",persistent:!0,message:"Deepgram credits are exhausted \u2014 dictation is down until you top up at console.deepgram.com or drop a new key into ~/.bostrat/integrations.json."}:e===401||e===403?{status:502,code:"key_invalid",persistent:!0,message:"Deepgram rejected the API key (revoked, or missing ASR scope) \u2014 replace it in ~/.bostrat/integrations.json."}:{status:502,code:"transcription_failed",persistent:!1,message:`Deepgram error ${e}: ${t.slice(0,300)}`}}async function UE(){if(!Mi)return null;let e=await yi();if(!e||e.apiKey.slice(-6)!==Mi.keyTail)return null;let{keyTail:t,...n}=Mi;return n}async function HE(e,t){let n=await yi();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 UF())r.searchParams.append("keyterm",i);let o=await fetch(r,{method:"POST",headers:{Authorization:`Token ${n.apiKey}`,"Content-Type":t||"application/octet-stream"},body:e});if(!o.ok){let i=await o.text().catch(()=>""),c=WF(o.status,i);c.persistent&&(Mi={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 Mi=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}ue();var Vh={"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 qF(e){if(!e)return null;let t=String(e).trim();if(Vh[t])return t;let n=null;for(let r of Object.keys(Vh))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function KF(e,t={}){let n=Vh[qF(e)];if(!n)return 0;let r=0;for(let o of Object.keys(n))r+=(Number(t[o])||0)*n[o];return r/1e6}function GF(e,t){let n=null;try{n=JSON.parse(t)}catch{}let r=n?.error?.code||n?.error?.type||"";return e===429&&/insufficient_quota/i.test(r)?{status:402,code:"credits_exhausted",persistent:!0,message:"The OpenAI account is out of credit \u2014 voice chat is down until you top up at platform.openai.com or drop a new key into ~/.bostrat/integrations.json."}:e===401||e===403?{status:502,code:"key_invalid",persistent:!0,message:"OpenAI rejected the API key (revoked, or missing Realtime access) \u2014 replace it in ~/.bostrat/integrations.json."}:{status:502,code:"mint_failed",persistent:!1,message:`OpenAI realtime session mint failed (${e}): ${t.slice(0,300)}`}}var Ni=null;async function VF(){if(!Ni)return null;let e=await tu();if(!e||e.apiKey.slice(-6)!==Ni.keyTail)return null;let{keyTail:t,...n}=Ni;return n}async function zF(e){let t=await fetch("https://api.openai.com/v1/realtime/client_secrets",{method:"POST",headers:{Authorization:`Bearer ${e.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({session:{type:"realtime",model:e.model,audio:{output:{voice:e.voice}}}})});if(!t.ok){let o=await t.text().catch(()=>""),s=GF(t.status,o);s.persistent&&(Ni={code:s.code,message:s.message,at:Date.now(),keyTail:e.apiKey.slice(-6)});let a=new Error(s.message);throw a.status=s.status,a.code=s.code,a}Ni=null;let n=await t.json(),r=n?.value??n?.client_secret?.value;if(!r){let o=new Error("OpenAI returned no client secret");throw o.status=502,o.code="mint_failed",o}return{clientSecret:{value:r,expiresAt:n?.expires_at??n?.client_secret?.expires_at??null}}}var JF={openai:zF};async function WE(){let e=await tu();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await VF()}:{configured:!1}}async function qE(){let e=await tu();if(!e){let n=new Error("Voice chat is not configured \u2014 add openai.apiKey to ~/.bostrat/integrations.json");throw n.status=503,n.code="not_configured",n}let t=await JF[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function KE(e,{model:t,usage:n={},seconds:r}={}){let o=c=>Math.max(0,Math.round(Number(c)||0)),s={textIn:o(n.textIn),textInCached:o(n.textInCached),textOut:o(n.textOut),audioIn:o(n.audioIn),audioInCached:o(n.audioInCached),audioOut:o(n.audioOut)},a=KF(t,s),i=await _(e,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:t||"unknown",usd:a,tokens:{input:s.textIn+s.audioIn,output:s.textOut+s.audioOut,cacheRead:s.textInCached+s.audioInCached,cacheWrite:0},voice:s,seconds:Math.max(0,Math.round(Number(r)||0)),source:"voice-chat"}});return{usd:a,seq:i.seq}}var XE=ge.dirname(XF(import.meta.url)),GE=Number(process.env.PORT)||3362,Au=Gm(process.env),ZE=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,Ds=NT(process.env.BOSTRAT_ALLOW_IPS),Zh=PT(process.env.BOSTRAT_VIEW_FRONT_CIDRS,gu),QE=BT(),eA=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",S=Rt();S.disable("x-powered-by");S.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),wu(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});S.use((e,t,n)=>{if(Ri(e)||Ds.allowed(Ds.clientIp(e))||Zh.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});S.use(HT);S.use(FT({token:QE,enforce:eA}));S.use("/api/mocks",Rt.json({limit:"1mb"}));S.use("/api/mocks",Rt.raw({type:"image/png",limit:"24mb"}));S.use("/api/mocks",Rt.raw({type:["video/mp4","video/webm"],limit:"105mb"}));S.use("/api/runbooks",Rt.json({limit:"1mb"}));S.use("/api/tracks/adopt",Rt.json({limit:"1mb"}));S.use("/api/telemetry",Rt.json({limit:"256kb"}));S.use("/api/hook",Rt.json({limit:"2mb"}));S.use("/api/dictation",Rt.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));S.use("/api/tracks/:id/upload",Rt.raw({type:()=>!0,limit:"25mb"}));S.use(Rt.json({limit:"64kb"}));S.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:ft,hostTmuxAlive:await Vn(),autoTriggerPct:ZE})});S.get("/api/sessions",async(e,t)=>{t.json({sessions:await Ot()})});S.post("/api/sessions",async(e,t)=>{let{displayName:n,cwd:r}=e.body||{};if(typeof n!="string")return t.status(400).json({error:"displayName (string) required"});try{let o=await zn(n,r||void 0);t.json({ok:!0,...o})}catch(o){let s=o?.message||String(o),a=/already exists/i.test(s)?409:/must be|invalid/i.test(s)?400:500;t.status(a).json({error:s})}});S.delete("/api/sessions/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!_e(n))return t.status(400).json({error:"invalid session name"});if(!await se(n))return t.status(404).json({error:"session not found"});try{await Ct(n),t.json({ok:!0})}catch(r){t.status(500).json({error:`failed to kill: ${r?.message||r}`})}});S.get("/api/context/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!_e(n))return t.status(400).json({error:"invalid session name"});let r=await se(n);if(!r)return t.status(404).json({error:"session not found"});let o=await aa(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});S.post("/api/handoff/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!_e(n))return t.status(400).json({error:"invalid session name"});try{let r=await Yy(n);t.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){t.status(409).json({error:r?.message||String(r)})}});S.get("/api/handoff/:name",(e,t)=>{let n=decodeURIComponent(e.params.name),r=Mc(n);if(!r)return t.json({running:!1});t.json({running:!r.finishedAt,step:r.step,error:r.error,file:r.file,startedAt:r.startedAt,finishedAt:r.finishedAt})});var VE=10;S.get("/api/idle-guard",async(e,t)=>{try{t.json(Km({tracks:await ae(),views:await yl(),queuedOf:n=>ao(n)}))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var Ft=lo(),nU=gv({kind:Ft}),Pi=null,ut=()=>Pi&&["auto","prompt","off"].includes(Pi)?Pi:nU,Eu=null;async function rU(e){let t=ut();Pi=e??null;let n=ut();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){Bs.cancel();return}Eu&&!gi(await Ss())&&Bs.arm({to:Eu.latest,from:Eu.current,kind:Ft})}}var oU=(()=>{try{return tU(import.meta.url)("../package.json").version||null}catch{return null}})(),Bs=bv({isBusy:async()=>Km({tracks:await ae(),views:await yl(),queuedOf:e=>ao(e)}).busy,onStuck:({to:e,from:t})=>on({current:t,latest:e,installKind:Ft,policy:ut(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?on({current:t,latest:e,installKind:Ft,policy:ut(),status:"updating",reason:"auto"}):on({current:t,latest:e,installKind:Ft,policy:ut(),status:"failed",error:n?.reason})).catch(()=>{})});S.get("/api/node/self-update",async(e,t)=>{t.json({installKind:Ft,policy:ut(),policySource:Pi?"plane":"node",current:oU,status:await Ss(),armed:Bs.armed})});S.post("/api/node/self-update",async(e,t)=>{let n=typeof e.body?.to=="string"?e.body.to.trim():"";if(!/^\d+\.\d+\.\d+$/.test(n))return t.status(400).json({error:"body.to must be a release version (X.Y.Z)"});let r=await Jl({to:n,kind:Ft,reason:"manual"});if(r.ok)return Bs.cancel(),on({current:r.from,latest:n,installKind:Ft,policy:ut(),status:"updating",reason:"manual"}).catch(()=>{}),t.json({ok:!0,...r});t.status(409).json({ok:!1,error:r.reason,status:r.status??null})});async function tA(){try{let e=await yv();e&&await on({current:e.from,latest:e.to,installKind:e.kind||Ft,policy:ut(),status:e.status,error:e.error,reason:e.reason})}catch{}}tA();setInterval(tA,3e4).unref();S.get("/api/tracks",async(e,t)=>{let n=(await ae()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>VE){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,VE).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:ii,agentAvailability:await Ol(),tickets:await Hv()})});S.get("/api/cost",async(e,t)=>{try{let n=await ae();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await hT(n);if(r.cursorUsage=lp(),e.query.format==="text")return t.type("text/plain").send(wT(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/roundup",async(e,t)=>{try{t.json(await bT(await ae(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/spend",async(e,t)=>{try{let n=await TT(await ae(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=lp(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=_a(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/caps",(e,t)=>{t.json(_a())});S.post("/api/usage/caps",async(e,t)=>{try{t.json(await Hc(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});S.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await RT(await ae(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await nw(e.get(Fi),{byToken:dm,legacy:()=>La(e.body?.session_id,e.body?.cwd)});if(n==="refused")return Ls("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return Ls("legacy-denied",{reason:"tokenless-denied",hook:e.body?.hook_event_name}),t.status(401).json({ok:!1,error:"tokenless hook ingest is disabled (BOSTRAT_HOOK_LEGACY_DENY)"});n==="token"&&e.body?.session_id&&r.box?.sessionId&&e.body.session_id!==r.box.sessionId&&Ls("mismatch",{track:r.track.id,box:r.box.name}),Ls(n,{track:r?.track?.id,hook:e.body?.hook_event_name,...n==="legacy"?{reason:"tokenless-body-resolved",deny_with:"BOSTRAT_HOOK_LEGACY_DENY=1"}:{}});let o=await uT(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&tc(r.box.cwd),t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});S.post("/api/telemetry",async(e,t)=>{try{if(!IE(Ds.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await _E(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});S.get("/api/repos",async(e,t)=>{try{let n=await cn(),r=new Set((await Promise.all(n.map(a=>me(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await qs()).repos||[]).filter(a=>!r.has(String(a.slug).toLowerCase())).map(a=>({slug:a.slug,name:a.slug.split("/")[1],branch:a.defaultBranch||null,private:!!a.private,remote:!0}));t.json({repos:[...n,...s]})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/github/token",async(e,t)=>{try{t.json(await Gi(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});S.get("/api/github/status",(e,t)=>{t.json({configured:Ki(),calls:r0(),latency:i0()})});S.post("/api/github/refresh",(e,t)=>{fw(),t.json({ok:!0})});S.get("/api/gmail/search",async(e,t)=>{try{let n=await OT(String(e.query.q||""),{max:Number(e.query.max)||10});t.json({messages:n})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});S.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await CT(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});function Qh(e,t,n){let r=e.workspace;return r?.kind==="multi"?(r.repos??[]).some(o=>t&&o?.base===t||n&&o?.repo===n):!r?.base||!t||r.base===t||n&&r.repo===n}async function _u({repo:e=null,number:t=null,branch:n=null}){let r=e?await me(e):null;if(r&&t!=null){let s=await qe(r,t),a=s&&await ne(s.trackId);if(a)return a}if(!n)return null;let o=await ae();return cl(o,n,s=>Qh(s,e,r))}async function nA({repo:e=null,number:t=null,branch:n=null}){let r=await _u({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!r.archivedAt&&!r.transferredTo)return r;if(!n)return null;let o=e?await me(e).catch(()=>null):null,s=await ae();return cl(s,n,a=>!a.archivedAt&&!a.transferredTo&&Qh(a,e,o))}async function Ut(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await _u({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await me(s).catch(()=>null):null);await _({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:e,number:t,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function sU(e){let t=(await ae()).filter(n=>!n.archivedAt&&!n.transferredTo);for(let n of e??[]){if(!n.prs?.length)continue;let r=await me(n.path);if(r)for(let o of n.prs){if(await qe(r,o.number))continue;let s=await cl(t,o.headRefName,a=>Qh(a,n.path,r));s&&await mt({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}S.get("/api/git/prs",async(e,t)=>{try{let n=await m0();await sU(n.repos).catch(()=>{}),await mE(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/git/prs/merge",async(e,t)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:f}=e.body||{};try{if(s){let h=o||"merge";if(!Na.has(h))throw new Error(`invalid merge method: ${h}`);let g=Number(r);if(!Number.isInteger(g)||g<=0)throw new Error("invalid PR number");let x=await me((await bn(n)).path);if(!x)throw new Error("repo has no GitHub remote");let k=await _u({repo:n,number:g,branch:l}).catch(()=>null);if(!k)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await Pa(n,g),await mt({repo:x,number:g,trackId:k.id,url:f,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Ra(x,g,{merge:{method:h}}))throw new Error("couldn't record the auto-merge arm");return await Yr(x,g,i===!0),await $a(x,g,c===!0&&i!==!0),Jt(),await Ut("auto-armed",{number:g,url:f,title:u,branch:l,repo:n,track:k,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:g,auto:!0})}let d=await ts(n,r,{method:o||"squash"});await Ut("merged",{number:d.number,url:f,title:u,branch:l,repo:n});let p=await me(n).catch(()=>null),m=!1;if(p&&(i===!0&&await Yr(p,d.number,!0).catch(()=>{}),c===!0&&i!==!0&&await $a(p,d.number,!0).catch(()=>{}),await xn(p,d.number).catch(()=>{}),m=await kn(p,d.number).catch(()=>!1),await ir(p,d.number,"merged").catch(()=>{})),i===!0&&!m){let h=await _u({repo:n,number:d.number,branch:l}).catch(()=>null);h&&!h.archivedAt&&await io(h.id).catch(()=>{})}t.json(d)}catch(d){let p=d?.message||String(d),m=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(p)?400:502;t.status(m).json({error:p})}});S.post("/api/git/prs/disarm-auto",async(e,t)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=e.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await me((await bn(n)).path).catch(()=>null),l=c?await Oa(c,i).catch(()=>null):null;try{await Gf(n,i)}catch(u){if(!l)throw u}Jt(),await Ut("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),t.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;t.status(l).json({error:c})}});S.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await me(n).catch(()=>null),s=await y0(n,{method:r||"merge",onMerged:a=>{Ut("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&xn(o,a.number).catch(()=>{}).then(()=>kn(o,a.number)).catch(()=>{}).then(()=>ir(o,a.number,"merged")).catch(()=>{})}});t.json(s)}catch(o){let s=o?.message||String(o),a=/already running/i.test(s)?409:/invalid|unknown repo|no PRs ready/i.test(s)?400:502;t.status(a).json({error:s})}});S.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:g0()})});S.post("/api/git/prs/merge-track",async(e,t)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=e.body||{};try{let i=a||"merge";if(!Na.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return t.status(400).json({error:"trackId required"});let c=await ne(n);if(!c)return t.status(404).json({error:`Track ${n} isn't on this node`});let l=(await Xr()).filter(d=>d.trackId===c.id&&d.state!=="merged"&&d.state!=="closed");if(!l.length)return t.status(409).json({error:"no open PRs on this Track"});for(let d of l)await Yr(d.repo,d.number,o===!0).catch(()=>{}),await $a(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 Qr(d.repo);if(!m)throw new Error(`no local checkout for ${d.repo}`);let h=await es(m,d.number);f.push({r:p,l:d,repoPath:m,pr:h,plan:h0(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:g}of w0(f))try{if(g.action==="skip")d.action="skipped",d.reason=g.reason;else if(g.action==="merge")await ts(m,p.number,{method:i}),await Ut("merged",{number:p.number,url:h.url,title:h.title,branch:h.headRefName,repo:m,track:c}),await xn(p.repo,p.number).catch(()=>{}),await kn(p.repo,p.number).catch(()=>{}),await ir(p.repo,p.number,"merged").catch(()=>{}),d.action="merged";else{if(g.action==="update-arm"&&(await Pa(m,p.number).catch(()=>{}),d.action="updated-armed"),!await Ra(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await Ut("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(x){d.ok=!1,d.action="failed",d.reason=x?.message||String(x)}if(Jt(),!u.some(d=>d.ok)){if(o===!0)for(let d of l)await Yr(d.repo,d.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let d of l)await $a(d.repo,d.number,!1).catch(()=>{});return t.status(502).json({error:u.map(d=>`${d.repo}#${d.number}: ${d.reason}`).join("; "),results:u})}t.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;t.status(l).json({error:c})}});S.post("/api/git/prs/disarm-track",async(e,t)=>{let{trackId:n}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let r=await ne(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await Xr()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await Oa(a.repo,a.number).catch(()=>null),c=await Qr(a.repo).catch(()=>null);c&&await Gf(c,a.number).catch(()=>{}),i&&(await Ut("auto-canceled",{number:a.number,url:a.url,title:a.title,branch:a.headRef,repo:c,track:r}),s.push({repo:a.repo,number:a.number}))}Jt(),t.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;t.status(s).json({error:o})}});S.post("/api/git/prs/open",async(e,t)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let i=await ne(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=gt(i.workspace),f,d=Array.isArray(a)?a.filter(Boolean):[];if(d.length)f=d.map(h=>{let g=u.find(x=>x.name===h||x.repo===h||x.base===h||x.dir===h);if(!g)throw new Error(`invalid repo "${h}" \u2014 not tagged on this Track`);return g});else if(f=await as(i),!f.length)return t.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let h of f)try{let g=await zf(h.dir,{title:r,body:o,base:s}),x=h.repo||await me(h.dir).catch(()=>null);x&&g.number&&await mt({repo:x,number:g.number,trackId:i.id,url:g.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Ut("opened",{number:g.number,url:g.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:x??h.name}}),p.push({repo:h.name,slug:x??null,ok:!0,url:g.url,number:g.number})}catch(g){p.push({repo:h.name,slug:h.repo??null,ok:!1,error:g?.message||String(g)})}let m=p.filter(h=>h.ok);return m.length?t.json({ok:!0,multi:!0,results:p,url:m[0].url,number:m[0].number}):t.status(502).json({error:p.map(h=>`${h.repo}: ${h.error}`).join("; "),results:p})}let c=await zf(i.cwd,{title:r,body:o,base:s}),l=await me(i.cwd).catch(()=>null);l&&c.number&&await mt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Ut("opened",{number:c.number,url:c.url,title:r,track:i,branch:i.workspace?.branch,extra:{...l?{repo:l}:{}}}),t.json(c)}catch(i){let c=i?.message||String(i),l=/required|invalid/i.test(c)?400:502;t.status(l).json({error:c})}});S.get("/api/tracks/:id/repos",async(e,t)=>{try{let n=await ne(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await as(n),o=new Map(r.map(a=>[a.base,a])),s=gt(n.workspace).map(a=>{let i=o.get(a.base);return{name:a.name,base:a.base,baseRef:a.baseRef??null,repo:a.repo??null,dirty:i?.dirty??!1,ahead:i?.ahead??0,changed:!!i}});t.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/tracks/:id/repos",async(e,t)=>{let{repo:n}=e.body||{};try{if(!n||typeof n!="string")return t.status(400).json({error:"repo (path or owner/name) required"});let r=await Sm(e.params.id,n);t.json({ok:!0,added:{name:r.name,base:r.base,baseRef:r.baseRef??null,repo:r.repo??null}})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/already tagged|needs a multi|not a usable|archived|required/i.test(o)?400:502;t.status(s).json({error:o})}});S.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await vm(e.params.id,e.params.name);t.json({ok:!0,...n})}catch(n){let r=n?.message||String(n),o=/track .* not found/i.test(r)?404:/has work|still running|last repo/i.test(r)?409:/needs a multi|not tagged/i.test(r)?400:502;t.status(o).json({error:r})}});S.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await es(n,r),s=await me(n).catch(()=>null),a=await nA({repo:n,number:r,branch:o.headRefName});if(!a)return t.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=gt(a.workspace).find(x=>x.base===n||s&&x.repo===s)??null,c=a.workspace?.kind==="multi",l=await b0(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?ge.join("ci",i.name):"ci",f=ge.join(a.cwd,".bostrat",u),d=ge.join(".bostrat",u,`pr-${o.number}-fail.log`);await Ye.promises.mkdir(f,{recursive:!0}),await Ye.promises.writeFile(ge.join(a.cwd,d),l.map(x=>`\u2550\u2550\u2550\u2550 ${x.name}${x.link?` \xB7 ${x.link}`:""} \u2550\u2550\u2550\u2550
|
|
596
|
+
`),n?.length&&a.write(n),t.pipe(a),a.pipe(t)}),i=()=>{a.destroy(),t.destroy()};return a.on("error",i),t.on("error",i),!0}function sA(e,t){let n=typeof t=="string"?{host:t}:t||{},r=n["x-bostrat-view-base"];return typeof r=="string"&&r?`${n["x-forwarded-proto"]==="https"?"https":"http"}://${e.slug}.${r.replace(/:\d+$/,"")}/`:`http://${e.slug}.${OL(n.host)}/`}function OL(e){if(process.env.BOSTRAT_PUBLIC_HOST)return String(process.env.BOSTRAT_PUBLIC_HOST).replace(/:\d+$/,"");let t=String(e||"").replace(/:\d+$/,"").toLowerCase();if(!t||!t.includes(".")||/^[\d.]+$/.test(t))return"bostrat.globopi";let n=t.split(".");return Tu(t)&&Mp(n[0])?n.slice(1).join("."):t}gs();Gp();Nl();mp();ue();import aA from"node:os";import{randomUUID as CL}from"node:crypto";var ML=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||aA.hostname(),nodeName:e.BOSTRAT_NODE_NAME||aA.hostname()});var iA=32768,cA=120,lA=1e3,uA=new Set(["claude","codex","cursor"]),NL=new Set(["off","transition","nag"]),PL=[[0,59],[0,23],[1,31],[1,12],[0,6]];function DL(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=PL[r];return String(n).split(",").every(a=>{if(!a)return!1;let[i,c]=a.split("/");if(c!==void 0&&!(Number.isInteger(Number(c))&&Number(c)>=1))return!1;if(i==="*")return!0;let[l,u]=i.includes("-")?i.split("-").map(Number):[Number(i),Number(i)];return Number.isInteger(l)&&Number.isInteger(u)&&l>=o&&u<=s&&l<=u})})}var Bn=e=>String(e??"").trim();function BL(e,t){let n=e&&typeof e=="object"?e:{},r=Bn(n.name);if(!r)throw new Error("a loop plan needs a name");if(r.length>cA)throw new Error(`name is too long (${r.length}); cap is ${cA}`);let o=Bn(n.schedule);if(!o)throw new Error('a loop plan needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!DL(o))throw new Error(`invalid cron schedule: "${o}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);let s=Bn(n.prompt);if(!s)throw new Error("a loop plan needs a prompt \u2014 it is what the run does");if(s.length>iA)throw new Error(`prompt is too long (${s.length} chars); cap is ${iA}`);let a=Bn(n.expectation)||null;if(a&&a.length>lA)throw new Error(`expectation is too long (${a.length}); cap is ${lA}`);let i=Bn(n.notify)||"off";if(!NL.has(i))throw new Error(`notify must be one of off | transition | nag, got "${n.notify}"`);let c=n.assignment??null,l=Bn(typeof c=="string"?c:c?.provider)||"claude";if(!uA.has(l))throw new Error(`unknown provider "${l}" \u2014 one of ${[...uA].join(", ")}`);let u=typeof c=="object"&&c&&Bn(c.model)||null,d=Bn(t?.workspace?.repo)||Bn(t?.workspace?.base)||null;if(!d)throw new Error("this Track isn't based on a repo, so there's nothing for a loop to run in \u2014 propose from a repo-backed Track");let{nodeId:f,nodeName:p}=ML();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:u?{provider:l,model:u}:{provider:l},repo:d,nodeId:f||null,nodeName:p||null}}async function dA(e,t){let n=BL(t,e),r=CL();return await _({id:e.id,cwd:e.cwd},{type:"loop.create.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),proposalId:r,...n}}),{proposalId:r,...n}}async function fA(e,t,n,{loopId:r=null,nextFireAt:o=null}={}){if(!t)throw new Error("proposalId required");let s=await L({id:e.id,cwd:e.cwd}),a=s.find(c=>c.type==="loop.create.offered"&&c.payload?.proposalId===t);if(!a)throw new Error("unknown proposal");if(s.some(c=>c.type==="loop.create.answered"&&c.payload?.proposalId===t))throw new Error("proposal already answered");if(!n)return await _({id:e.id,cwd:e.cwd},{type:"loop.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"dismiss"}}),{proposalId:t,action:"dismiss"};if(!r)throw new Error("loopId required \u2014 the loop is created on the plane before the card is answered");let i=a.payload;return await _({id:e.id,cwd:e.cwd},{type:"loop.created",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:String(r),name:i.name,schedule:i.schedule,provider:i.assignment?.provider??"claude",repo:i.repo??null,nodeId:i.nodeId??null,nextFireAt:Number.isFinite(Number(o))?Number(o):null}}),await _({id:e.id,cwd:e.cwd},{type:"loop.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",loopId:String(r)}}),{proposalId:t,action:"created",loopId:String(r)}}Gn();ue();gn();Nl();var pA=10,mA=200,jL=120,LL=24,wA=/^\d{4}-\d{2}-\d{2}$/;function Gh(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var FL=(e=Date.now())=>new Date(e).toISOString().slice(0,10),Ye=(e,t="i")=>new RegExp(e,t),UL=[{re:Ye("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:Ye("\\bSPC\\b",""),reason:"business (SPC)"},{re:Ye("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:Ye("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:Ye("\\bReplicas\\b",""),reason:"competitor research"},{re:Ye("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:Ye("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:Ye("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],HL=[Ye("\\bSPC\\b",""),Ye("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),Ye("competitor|cloudcli|\\bkiro\\b"),Ye("\\bReplicas\\b",""),Ye("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),Ye("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),Ye("(^|[^\\w&])#\\d+\\b"),Ye("\\btrack/[\\w.-]+")];function WL({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of UL)if(o.test(n)||o.test(r))return s;return null}function hA(e){let t=String(e||"");for(let n of HL){let r=n.exec(t);if(r)return r[0].trim()}return null}function gA(e,t=Date.now()){if(!e)return t-24*36e5;let n=/^(\d+)\s*([hd])$/.exec(String(e).trim());if(n)return t-Number(n[1])*(n[2]==="h"?36e5:24*36e5);let r=Date.parse(e);if(!Number.isNaN(r))return r;throw new Error(`unparseable since "${e}" \u2014 use 24h, 7d, or an ISO date`)}var qL=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function KL(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=ni(e);if(t.length)throw new Error(`changelog entry appears to contain a secret VALUE (${t.join(", ")}) \u2014 entries ship off-box; never put a credential in one. Replace it and retry.`);let n=null,r=[];for(let o of e.split(`
|
|
597
|
+
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,jL)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${LL}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=qL.exec(i);if(!u||!u[1].trim()||!u[2].trim())throw new Error(`bullet ${r.length+1} must be \`**Short summary** \u2014 the item, one sentence.\` (bold summary title, em dash, then the change) \u2014 got "${i.slice(0,80)}". Rewrite it and re-publish.`);i=`**${u[1].trim().replace(/[:.]\s*$/,"")}** \u2014 ${u[2].trim()}`;let d=hA(i);if(d)throw new Error(`bullet ${r.length+1} mentions "${d}" \u2014 the product changelog never carries business, security/tenancy or internal-ops items, PR numbers, branch names or codenames; drop the bullet or rewrite it as the user-visible change and re-publish.`);if(i.length>mA)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${mA}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=hA(n);if(o)throw new Error(`headline mentions "${o}" \u2014 the product changelog never carries business, security/tenancy or internal-ops items; rewrite it and re-publish.`)}if(!r.length)throw new Error("no `- ` bullets found \u2014 an entry is a bullet list, optionally under a `# headline`");if(r.length>pA)throw new Error(`${r.length} bullets \u2014 over the ${pA}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function GL(e,{repos:t=Gh()}={}){let n=new Set(t);return(await Qr()).filter(r=>r.state==="merged"&&(r.resolvedAt??0)>=e&&n.has(r.repo)).sort((r,o)=>(r.resolvedAt??0)-(o.resolvedAt??0)).map(r=>({repo:r.repo,number:r.number,title:r.title||null,url:r.url||null,headRef:r.headRef||null,mergedAt:r.resolvedAt??null,excluded:WL({title:r.title,headRef:r.headRef})}))}async function yA(e,t){let n=await GL(e,t);return{prs:n.filter(r=>!r.excluded),excluded:n.filter(r=>r.excluded)}}async function bA(e,{md:t,day:n=null}){let r=KL(t),o=n==null||n===""?FL():String(n);if(!wA.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await _({id:e.id,cwd:e.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:e.leadBox||null,payload:{ts:Date.now(),day:o,title:r.title,bullets:r.bullets}});return{day:o,title:r.title,bullets:r.bullets,eventId:s.id}}async function kA(e,{day:t}){let n=String(t||"");if(!wA.test(n)||Number.isNaN(Date.parse(n)))throw new Error(`invalid day "${t}" \u2014 expected YYYY-MM-DD`);let r=await _({id:e.id,cwd:e.cwd},{type:"changelog.published",actor:"agent",source:"orchestrator",boxId:e.leadBox||null,payload:{ts:Date.now(),day:n,title:null,bullets:[],retract:!0}});return{day:n,retracted:!0,eventId:r.id}}ls();gn();fs();ue();ls();et();hr();gn();Fa();dl();Ka();import{execFile as VL}from"node:child_process";import{promisify as zL}from"node:util";var zh=zL(VL),xA=3e4,JL=30*6e4,YL=xA,XL=10*6e4,ZL=10080*6e4,QL=60*6e4,eF=8,tF=1440*6e4,nF=3,SA="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",Vh=60*6e4,rF=15*6e4,oF=9e4,sF=1440*6e4,Eu=new Map,_u=new Map,aF=/could not resolve|not found|no pull requests?/i,iF=e=>aF.test(String(e?.message||e||""));function cF(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<Vh)}var vA={fast:YL,slow:XL,decay:QL};function TA(e,t){return e.armedAt&&t-e.armedAt<JL||cF(e,t)||AA(e,t,{grace:0})?"fast":t-(e.firstSeen||0)>ZL?"decay":"slow"}function lF(e,t){return vA[TA(e,t)]}function ji(e,t,n,r){let o=e.lanes?.get(Q(t.repo,t.number))||{};return ul({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function uF(e,t){bn("watch");try{let{stdout:n}=await zh("gh",["pr","view",String(t),"--repo",e,"--json",SA],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await qt({slug:e})}});return JSON.parse(n||"null")}catch(n){throw fr(n)&&pr(n),n}}async function dF(e){bn("watch");try{let{stdout:t}=await zh("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",SA],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await qt({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw fr(t)&&pr(t),t}}async function fF(){let e=new Map;for(let t of await oe().catch(()=>[]))e.set(t.id,t);return e}async function pF(e,t){await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"merged",...t}})}async function mF(e,t){let n=await to(e);return n?is(n,t):null}async function hF(e,t,n){let r=await to(e);if(!r)throw new Error(`no local checkout for ${e}`);return cs(r,t,{method:n})}async function wF(e,t){let n=await to(e);if(!n)throw new Error(`no local checkout for ${e}`);return Wa(n,t)}async function gF(e,t){await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:"auto-canceled",...t}})}async function Ls(e,t,n,r,o){if(!await r.disarm(e.repo,e.number).catch(()=>null))return;let a=n.get(e.trackId);a&&await r.emitDisarmed(a,{number:Number(e.number),url:t?.url||e.url,title:t?.title||e.title,branch:t?.headRefName||e.headRef,repo:e.repo,reason:o}).catch(()=>{})}var yF=10*6e4;async function bF(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>tF){await Ls(e,null,t,n,"auto-merge expired \u2014 checks never settled within 24h");return}let o=await n.detail(e.repo,e.number).catch(()=>null);if(!o)return;if(o.checks.failed>0){let a=o.checks.failing.map(i=>i.name).join(", ");await Ls(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await Ls(e,o,t,n,`conflicts with ${o.baseRefName}`);return}if(!o.checks.total){let a=Math.max(e.autoMerge?.armedAt||0,Date.parse(o.updatedAt||"")||0);if((n.now??Date.now())-a<yF)return;await Ls(e,o,t,n,o.checks.unavailable?"this token can't read the checks":"no CI checks appeared");return}if(o.checks.pending>0||o.mergeable==="UNKNOWN"||o.mergeStateStatus==="UNKNOWN"||o.isDraft)return;let s=await n.lookup(e.repo,e.number).catch(()=>null);if(s?.autoMerge){if(o.mergeStateStatus==="BEHIND"){try{await n.updateBranch(e.repo,e.number)}catch(a){let i=String(a?.message||a).trim();/no new commits|already up.to.date/i.test(i)||await Ls(e,o,t,n,i||`couldn't update with ${o.baseRefName}`)}return}try{await n.merge(e.repo,e.number,s.autoMerge.method||"merge")}catch(a){if(/already merged|not open|is closed/i.test(String(a?.message||a)))return;await Ls(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await EA(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function Fs(e,t,n){await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:t,...n}})}async function kF(e,t){try{return await zh("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Bi(e,t,n,r=()=>!0){return(await L(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var Us=(e,t)=>({number:Number(e.number),url:t?.url||e.url,title:t?.title||e.title,branch:t?.headRefName||e.headRef,repo:e.repo});function AA(e,t,{grace:n=oF}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=sF}async function xF(e,t,n,r){let o=r.now??Date.now();if(!AA(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||s.archivedAt||s.transferredTo)return!1;let a=(s.boxes||[]).find(d=>d.name===s.leadBox);return await(r.midTurn??no)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await L(s).catch(()=>[])).some(d=>d.type==="pr.state.changed"&&d.payload?.number===Number(e.number))?(await(r.claim??wn)(e.repo,e.number).catch(()=>null),!1):await(r.claim??wn)(e.repo,e.number).catch(()=>null)?(await Fs(s,"opened",{...Us(e,t),announcedBy:"watch"}).catch(()=>{}),console.log(`[pr-watch] announced passive link ${e.repo}#${e.number} \u2192 track ${s.id} (no active door claimed it)`),!0):!1}async function SF(e,t,n,r){let o=r.now??Date.now(),s=t.headRefOid||null,a=n.get(e.trackId)||null;if(s&&(e.ciFix?.headSha&&s!==e.ciFix.headSha&&await nl(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await ol(e.repo,e.number,{headSha:s}).catch(()=>{})),s&&s!==e.lastHeadSha){let i=!e.lastHeadSha,c=!!(e.ciFix||e.reconcile||e.ciFixAwait||e.reconcileAwait),l=!!e.baseFoldAt&&o-e.baseFoldAt<rF;await s0(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await o0(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Bi(a,"pushed",e.number,d=>d.payload?.sha===s)&&await Fs(a,"pushed",{...Us(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>Vh)await rl(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i?.checks){if(i.checks.failed>0)await rl(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=ji(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await Bi(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await Fs(a,"ci-fixed",{...Us(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await rl(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>Vh)await sl(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await sl(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){ji(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await Bi(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await Fs(a,"reconciled",{...Us(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await sl(e.repo,e.number).catch(()=>{})}}}}async function vF(e,t,n,r){let o=ji(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await Bi(s,"closed",e.number)&&await Fs(s,"closed",{...Us(e,t),githubAt:o.githubAt,latencyMs:o.latencyMs,lane:o.lane}).catch(()=>{}),await r.notifyIfResolved(e.repo,e.number,"closed").catch(()=>{}),await r.mark(e.repo,e.number,"closed").catch(()=>{})}async function TF(e,t,n){ji(n,e,"lost",null);let r=t.get(e.trackId);r&&!await Bi(r,"lost",e.number)&&await Fs(r,"lost",Us(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function EA(e,t,n,r,o={}){let s=ji(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await L(a).catch(()=>[])).some(l=>l.type==="pr.state.changed"&&l.payload?.change==="merged"&&l.payload?.number===Number(e.number))||await r.emitMerged(a,{number:Number(e.number),url:t.url||e.url,title:t.title||e.title,branch:t.headRefName||e.headRef,repo:e.repo,githubAt:s.githubAt,latencyMs:s.latencyMs,lane:s.lane}).catch(()=>{})),await r.notifyIfArmed(e.repo,e.number).catch(()=>{}),await r.archiveIfArmed(e.repo,e.number).catch(()=>{}),await r.mark(e.repo,e.number,"merged").catch(()=>{})}async function AF(e={}){let{now:t=Date.now(),auth:n=ep,view:r=uF,list:o=dF,links:s=c0,trackMap:a=fF,emitMerged:i=pF,mark:c=ur,archiveIfArmed:l=xn,notifyIfArmed:u=Sn,notifyIfResolved:d=Kl,batchThreshold:f=nF,detail:p=mF,merge:m=hF,updateBranch:h=wF,lookup:g=qe,disarm:x=La,emitDisarmed:k=gF,localHasCommit:A=kF,claim:I=wn,midTurn:M=no}=e;if(!(await n().catch(()=>null))?.authenticated||cl()>0)return 0;let T=(await s().catch(()=>[])).filter(U=>t-(Eu.get(Q(U.repo,U.number))||0)>=lF(U,t));if(!T.length)return 0;let j=Date.now(),Z=new Map,fe=new Map;for(let U of T){let pe=TA(U,t);Z.set(Q(U.repo,U.number),{lane:pe,intervalMs:vA[pe]}),Eu.set(Q(U.repo,U.number),t),fe.has(U.repo)||fe.set(U.repo,[]),fe.get(U.repo).push(U)}let ie=await a().catch(()=>new Map),D={now:t,lanes:Z,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,detail:p,merge:m,updateBranch:h,lookup:g,disarm:x,emitDisarmed:k,localHasCommit:A,claim:I,midTurn:M};for(let[U,pe]of fe){let Se,He=new Set;if(pe.length>f)Se=await o(U).catch(()=>null);else{Se=new Map;for(let le of pe)try{let q=await r(U,le.number);q?.number!=null&&Se.set(Number(q.number),q)}catch(q){iF(q)&&He.add(Q(le.repo,le.number))}}if(Se)for(let le of pe){let q=Q(le.repo,le.number),J=Se.get(Number(le.number));if(!J){if(He.has(q)){let ee=(_u.get(q)||0)+1;_u.set(q,ee),ee>=eF&&(await TF(le,ie,D),_u.delete(q),Eu.delete(q))}continue}_u.delete(q),J.state==="MERGED"||J.state==="CLOSED"?(J.state==="MERGED"?await EA(le,J,ie,D):await vF(le,J,ie,D),Eu.delete(q)):(await xF(le,J,ie,D).catch(()=>{}),await SF(le,J,ie,D).catch(()=>{}),le.autoMerge&&await bF(le,ie,D).catch(()=>{}))}}let R={};for(let{lane:U}of Z.values())R[U]=(R[U]||0)+1;return console.log(`[pr-watch] probed ${T.length} link(s) (${Object.entries(R).map(([U,pe])=>`${U} ${pe}`).join(", ")}) in ${Date.now()-j}ms`),T.length}function _A(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{await AF()}catch{}finally{e=!1}}},xA)}ue();gn();Hn();hr();function IA(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function EF(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var _F=new Set(["merged","closed","lost"]);function IF(e,t){if(!e?.blocked)return null;let n=IA(t);if(e.blocked==="red")return t.checks.failed===0&&t.checks.total>0?{change:"ci-fixed",reason:"red"}:null;if(e.blocked==="conflict")return t.mergeable==="MERGEABLE"&&n!=="red"?{change:"reconciled",reason:"conflict"}:null;if(e.blocked==="behind"){let r=t.headSha??null;return n===null&&r&&e.sha&&r!==e.sha?{change:"reconciled",reason:"behind"}:null}return null}function RF(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function $F(e,t,n,r,o){return(await L(e).catch(()=>[])).some(a=>{if(a.type!=="pr.state.changed"||a.payload?.number!==Number(n))return!1;let i=a.payload?.change;return i===t?a.payload?.sha&&r?a.payload.sha===r:(a.payload?.ts||0)>=o:i==="pushed"&&!!r&&a.payload?.sha===r})}async function OF(e,t,n){await _({id:e.id,cwd:e.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",boxId:null,payload:{ts:Date.now(),change:t,...n}})}async function RA(e,t={}){let{now:n=Date.now(),slugOf:r=me,lookup:o=qe,note:s=a0,tracks:a=async()=>(await oe()).filter(d=>!d.archivedAt&&!d.transferredTo),asked:i=$F,emitChange:c=OF}=t,l=[],u=null;for(let d of e??[]){if(!d?.prs?.length)continue;let f=await r(d.path).catch(()=>null);if(f)for(let p of d.prs)try{if(!EF(p))continue;let m=await o(f,p.number).catch(()=>null);if(!m?.trackId||_F.has(m.state))continue;let{prev:h}=await s(f,p.number,{blocked:IA(p),sha:p.headSha??null,at:n});if(!h)continue;let g=IF(h,p);if(!g||RF(m,g.change))continue;u??=new Map((await a()).map(I=>[I.id,I]));let x=u.get(m.trackId);if(!x)continue;let k=p.headSha??null;if(await i(x,g.change,p.number,k,h.at||0))continue;let A={number:Number(p.number),url:p.url||m.url,title:p.title||m.title,branch:p.headRefName||m.headRef,repo:f,sha:k,delegated:!1,reason:g.reason};g.change==="ci-fixed"?A.passed=p.checks.passed:A.base=p.baseRefName||null,await c(x,g.change,A),l.push({change:g.change,number:p.number,reason:g.reason})}catch{}}return l}Ve();ue();hr();import{randomUUID as CF}from"node:crypto";var MF=2e4,$A=3,Hs=new Map,Jh=new Set;async function OA(e,t,n){let r=await n(e).catch(()=>[]),o=null;for(let s of r)s.type==="box.fullscreen.detected"&&s.boxId===t?o=s.payload?.offerId||null:s.type==="box.fullscreen.answered"&&o&&s.payload?.offerId===o&&(o=null);return o}async function NF(e={}){let{sessions:t=null,tracks:n=null,append:r=_,read:o=L,now:s=Date.now()}=e;if(process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1")return;let a,i;try{a=t??await Ct(),i=n??await oe()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(u.archivedAt||u.transferredTo||!u.leadBox)continue;let d=(u.boxes||[]).find(p=>p.name===u.leadBox);if(!d||d.agent!=="claude"||d.state!=="live")continue;let f=c.get(d.name);if(f)if(l.add(d.name),f.alternateOn){let p=(Hs.get(d.name)||0)+1;Hs.set(d.name,p),p===$A&&(Jh.add(d.name),await OA(u,d.name,o)||await r(u,{type:"box.fullscreen.detected",actor:"system",source:"orchestrator",boxId:d.name,provider:d.agent,payload:{ts:s,offerId:CF(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let p=(Hs.get(d.name)||0)>=$A;if(Hs.delete(d.name),p||!Jh.has(d.name)){Jh.add(d.name);let m=await OA(u,d.name,o);m&&await r(u,{type:"box.fullscreen.answered",actor:"system",source:"orchestrator",boxId:d.name,provider:d.agent,payload:{ts:s,offerId:m,action:"cleared"}}).catch(()=>{})}}}for(let u of[...Hs.keys()])l.has(u)||Hs.delete(u)}var Li=null,Yh=!1;function CA(){return Li?()=>{}:(Li=setInterval(()=>{Yh||(Yh=!0,NF().catch(()=>{}).finally(()=>{Yh=!1}))},MF),Li.unref?.(),()=>{clearInterval(Li),Li=null})}rs();Dp();ue();ls();fs();rs();import{randomUUID as PF}from"node:crypto";async function DF(){let e=await Promise.resolve().then(()=>(hr(),vv));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var Xh={listRepoPrs:Jt,readEvents:L,appendEvent:_,sayDeps:DF,currentBranch:us};async function BF(e,t=Xh,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??us)(e.workspace?.kind==="multi"?null:e.cwd).catch(()=>null)||e?.workspace?.branch;if(!r)return null;let o;try{o=await t.listRepoPrs(e.cwd,{caller:"pr-exists"})}catch{return null}let s=(o||[]).find(a=>a.headRefName===r);return s?{number:s.number,url:s.url??null,title:s.title??null}:null}async function Zh(e,t,{actions:n,notify:r=!1}={},o=Xh){let s=t?.payload||{};if(!Array.isArray(n)||!n.includes("pr")||s.toPr||Array.isArray(s.repos)&&s.repos.length||!s.branch)return null;let a=await BF(e,o,{branch:s.branch});if(!a)return null;let i={id:e.id,cwd:e.cwd};await o.appendEvent(i,{type:"pr.offer.answered",actor:"user",source:"orchestrator",boxId:t.boxId,payload:{ts:Date.now(),offerId:s.offerId,action:"pr-exists",actions:n,...r?{notify:!0}:{},existingPr:{number:a.number}}});let c=PF();return await o.appendEvent(i,{type:"pr.exists.offered",actor:"system",source:"orchestrator",boxId:t.boxId,payload:{ts:Date.now(),offerId:c,srcOfferId:s.offerId??null,branch:s.branch,base:s.base??null,pr:a,actions:n,...r?{notify:!0}:{},offeredRebase:Array.isArray(s.offered)&&s.offered.includes("rebase")}}),{offerId:c,pr:a}}async function Qh(e,{offerId:t,choice:n}={},r=Xh){if(!t)throw new Error("offerId required");if(!["push","new","dismiss"].includes(n))throw new Error('choice "push" | "new" | "dismiss" required');let o=await r.readEvents(e),s=o.find(u=>u.type==="pr.exists.offered"&&u.payload?.offerId===t);if(!s)throw new Error("unknown offer");if(o.some(u=>u.type==="pr.exists.answered"&&u.payload?.offerId===t))throw new Error("offer already answered");let a=s.payload||{},i=Array.isArray(a.actions)&&a.actions.length?a.actions:["commit","push","pr"],c=null,l=null;if(n==="push"){let{sayToLead:u}=await r.sayDeps();l=ts.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(e.id,ns(l,{branch:a.branch??null,base:a.base??null,toPr:{number:a.pr?.number??null},offeredRebase:a.offeredRebase===!0}),{kind:"push-go"})}else if(n==="new"){let{sayToLead:u}=await r.sayDeps();l=i,c=await u(e.id,zk(i,{branch:a.branch??null,base:a.base??null,prNumber:a.pr?.number??null,offeredRebase:a.offeredRebase===!0}),{kind:"pr-go"})}if(await r.appendEvent({id:e.id,cwd:e.cwd},{type:"pr.exists.answered",actor:"user",source:"orchestrator",boxId:s.boxId,payload:{ts:Date.now(),offerId:t,choice:n}}),n!=="dismiss"&&a.notify===!0){let{queueShipCompleteNotify:u}=await r.sayDeps();u(e.id,{actions:l})}return{say:c,choice:n}}import jn from"node:fs/promises";import Ws from"node:path";import LA from"node:os";import MA from"node:fs/promises";import Iu from"node:path";import{execFile as jF}from"node:child_process";import{promisify as LF}from"node:util";import{fileURLToPath as FF}from"node:url";var UF=LF(jF),PA=Iu.dirname(FF(import.meta.url)),HF=Iu.resolve(PA,"..","..","client","dist"),WF="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",qF=new Map([...WF].map((e,t)=>[e,t]));function KF(e){let t=[],n=0,r=0;for(let o of e){let s=qF.get(o);if(s===void 0)return t;n+=(s&31)<<r,s&32?r+=5:(t.push(n&1?-(n>>>1):n>>>1),n=0,r=0)}return t}function GF(e){let t=[],n=0,r=0,o=0;for(let s of e.split(";")){let a=[],i=0;if(s)for(let c of s.split(",")){let l=KF(c);l.length!==0&&(i+=l[0],l.length>=4&&(n+=l[1],r+=l[2],o+=l[3],a.push([i,n,r,o])))}t.push(a)}return t}function VF(e,t){if(!e||e.length===0||e[0][0]>t)return null;let n=0,r=e.length-1;for(;n<r;){let o=n+r+1>>1;e[o][0]<=t?n=o:r=o-1}return e[n]}var NA=new Map;async function zF(e){let t;try{t=await MA.stat(e)}catch{return null}let n=NA.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await MA.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:GF(r.mappings||"")};return NA.set(e,o),o}catch{return null}}var ew=null;function JF(){return ew||(ew=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):UF("git",["rev-parse","--short","HEAD"],{cwd:Iu.resolve(PA,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),ew}var YF=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function XF(e,{distDir:t=HF}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(YF)){let[a,i,c,l]=s,u=await zF(Iu.join(t,"assets",`${i}.map`)),d=u?VF(u.lines[Number(c)-1],Number(l)-1):null;if(n.push(e.slice(r,s.index)),d&&u.sources[d[1]]){let f=u.sources[d[1]].replace(/^(\.\.\/)+/,"");n.push(`${f}:${d[2]+1}:${d[3]+1}`),o=!0}else n.push(a);r=s.index+a.length}return n.push(e.slice(r)),{stack:n.join(""),symbolicated:o}}async function DA(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await JF();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await XF(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var ZF=()=>process.env.BOSTRAT_STATE_DIR||Ws.join(process.env.HOME||LA.tmpdir(),".bostrat"),Or=()=>Ws.join(ZF(),"telemetry"),FA=()=>Ws.join(Or(),".relay-offsets.json"),QF=50,eU=14,tU=3e4,BA=500,bt=(e,t)=>typeof e=="string"?e.slice(0,t):"";function nU(e,t){if(!e||typeof e!="object")return null;let n=bt(e.name,64);if(!n)return null;let r=Number.isFinite(e.ts)?e.ts:t,o={name:n,ts:Math.abs(r-t)<168*3600*1e3?r:t,receivedAt:t,anonId:bt(e.anonId,64),sessionId:bt(e.sessionId,64),userId:bt(e.userId,64),release:bt(e.release,64),appVersion:bt(e.appVersion,32),route:bt(e.route,128),deviceClass:e.deviceClass==="phone"||e.deviceClass==="desktop"?e.deviceClass:""};return e.props&&typeof e.props=="object"&&!Array.isArray(e.props)&&(JSON.stringify(e.props).length<=8192?o.props=e.props:o.props={clamped:!0}),e.error&&typeof e.error=="object"&&(o.error={kind:bt(e.error.kind,256),message:bt(e.error.message,8192),stack:bt(e.error.stack,32768),fingerprint:bt(e.error.fingerprint,64)}),Array.isArray(e.breadcrumbs)&&(o.breadcrumbs=e.breadcrumbs.slice(-50).map(s=>({t:Number.isFinite(s?.t)?s.t:0,n:bt(s?.n,64),r:bt(s?.r,128)}))),o}var UA=e=>new Date(e).toISOString().slice(0,10),jA=0;async function HA(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,QF):[]).map(s=>nU(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await DA(s);await jn.mkdir(Or(),{recursive:!0});let o=Ws.join(Or(),`${UA(t)}.ndjson`);return await jn.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
598
|
+
`).join(""),"utf8"),t-jA>6*3600*1e3&&(jA=t,rU(t).catch(()=>{})),r.length}async function rU(e=Date.now()){let t=UA(e-eU*24*3600*1e3),n=[];try{n=await jn.readdir(Or())}catch{return}let r=n.filter(s=>s.endsWith(".ndjson")&&s.slice(0,10)<t);if(!r.length)return;for(let s of r)await jn.rm(Ws.join(Or(),s),{force:!0});let o=await qA();for(let s of r)delete o[s];await KA(o)}var Ru=new Map;function WA(e,t=Date.now()){let n=e||"unknown",r=Ru.get(n);return r||(Ru.size>512&&Ru.clear(),r={tokens:40,at:t},Ru.set(n,r)),r.tokens=Math.min(40,r.tokens+(t-r.at)/6e4*20),r.at=t,r.tokens<1?!1:(r.tokens-=1,!0)}async function qA(){try{return JSON.parse(await jn.readFile(FA(),"utf8"))||{}}catch{return{}}}async function KA(e){await jn.mkdir(Or(),{recursive:!0}),await jn.writeFile(FA(),JSON.stringify(e),"utf8")}function oU(e,{env:t,host:n}={}){let r=e.name==="app.error"&&e.error,o={};for(let[a,i]of[["event",e.name],["route",e.route],["session",e.sessionId],["anon",e.anonId],["user",e.userId],["release",e.release],["appVersion",e.appVersion],["device",e.deviceClass]])i&&(o[a]=i);let s={timestamp:new Date(e.ts).toISOString(),service:"bostrat-client",source:"treetop-js",env:t||process.env.BOSTRAT_ENV||"pi",host:n||LA.hostname(),level:r?"error":"info",message:r?`${e.error.kind||"Error"}: ${e.error.message||"(no message)"}`.slice(0,8192):e.name,tags:o,attributes:{...e.props?{props:e.props}:{},receivedAt:e.receivedAt}};return r&&(s.error={kind:e.error.kind,message:e.error.message,stack:e.error.stack},e.error.fingerprint&&(s.fingerprint=e.error.fingerprint),e.error.symbolicated&&(s.attributes.symbolicated=!0),e.breadcrumbs&&(s.attributes.breadcrumbs=e.breadcrumbs)),s}async function sU({fetchImpl:e=fetch}={}){let t=await qv();if(!t)return{sent:0,disabled:!0};let n;try{n=(await jn.readdir(Or())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await qA(),o=0;for(let s of n){let a=Ws.join(Or(),s),i=r[s]||0,c;try{let f=await jn.open(a,"r");try{let p=(await f.stat()).size;if(p<=i)continue;c=Buffer.alloc(p-i),await f.read(c,0,c.length,i)}finally{await f.close()}}catch{continue}let l=c.lastIndexOf(10);if(l<0)continue;let u=c.toString("utf8",0,l+1).split(`
|
|
599
|
+
`).filter(Boolean),d=[];for(let f of u)try{d.push(JSON.parse(f))}catch{}for(let f=0;f<d.length;f+=BA){let p=d.slice(f,f+BA).map(h=>oU(h)),m=await e(`${t.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.ingestKey}`},body:JSON.stringify({events:p})}).catch(()=>null);if(!m||!m.ok)return{sent:o,disabled:!1};o+=p.length}r[s]=i+l+1,await KA(r)}return{sent:o,disabled:!1}}var tw=null,nw=!1;function GA(){tw||(tw=setInterval(async()=>{if(!nw){nw=!0;try{await sU()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{nw=!1}}},tU),tw.unref?.())}import Ou from"node:fs/promises";import XA from"node:path";import aU from"node:os";var ZA=()=>process.env.BOSTRAT_STATE_DIR||XA.join(process.env.HOME||aU.tmpdir(),".bostrat"),QA=()=>XA.join(ZA(),"dictation-glossary.json"),iU=100,cU=500,lU=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],Cu=null,rw=null,ow=Promise.resolve();function VA(){let e={},t=Date.now();for(let n of lU)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function eE(){return Cu||(rw??=Ou.readFile(QA(),"utf8").then(e=>JSON.parse(e)?.terms??VA()).catch(()=>VA()).then(e=>Cu=e).finally(()=>rw=null),rw)}async function zA(){let e=JSON.stringify({terms:Cu},null,2),t=QA(),n=`${t}.${process.pid}.tmp`;await Ou.mkdir(ZA(),{recursive:!0}),await Ou.writeFile(n,e),await Ou.rename(n,t)}function uU(){return ow=ow.then(zA,zA),ow.catch(()=>{})}async function sw(){return Object.values(await eE()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var dU=e=>Math.max(1,Math.ceil(e.length/4));async function fU(){let e=[],t=0;for(let{term:n}of await sw()){let r=dU(n);if(!(t+r>cU)&&(e.push(n),t+=r,e.length>=iU))break}return e}async function pU(e,t){let n=await eE(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var JA=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),$u=e=>e.toLowerCase();function YA(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function tE(e,t){let n=JA(e),r=JA(t);if(!n.length||!r.length)return[];let o=n.length,s=r.length,a=Array.from({length:o+1},()=>new Array(s+1).fill(0));for(let u=o-1;u>=0;u--)for(let d=s-1;d>=0;d--)a[u][d]=$u(n[u])===$u(r[d])?a[u+1][d+1]+1:Math.max(a[u+1][d],a[u][d+1]);let i=new Set,c=0,l=0;for(;c<o&&l<s;)$u(n[c])===$u(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(YA(r[l])&&i.add(r[l]),l++);for(;l<s;l++)YA(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await pU(u,"learned");return await uU(),[...i]}var Fi=null;function mU(e,t){let n=null;try{n=JSON.parse(t)}catch{}let r=n?.err_code||n?.error_code||n?.category||"";return e===402||/PAYMENT_REQUIRED/i.test(r)?{status:402,code:"credits_exhausted",persistent:!0,message:"Deepgram credits are exhausted \u2014 dictation is down until you top up at console.deepgram.com or drop a new key into ~/.bostrat/integrations.json."}:e===401||e===403?{status:502,code:"key_invalid",persistent:!0,message:"Deepgram rejected the API key (revoked, or missing ASR scope) \u2014 replace it in ~/.bostrat/integrations.json."}:{status:502,code:"transcription_failed",persistent:!1,message:`Deepgram error ${e}: ${t.slice(0,300)}`}}async function nE(){if(!Fi)return null;let e=await Ai();if(!e||e.apiKey.slice(-6)!==Fi.keyTail)return null;let{keyTail:t,...n}=Fi;return n}async function rE(e,t){let n=await Ai();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 fU())r.searchParams.append("keyterm",i);let o=await fetch(r,{method:"POST",headers:{Authorization:`Token ${n.apiKey}`,"Content-Type":t||"application/octet-stream"},body:e});if(!o.ok){let i=await o.text().catch(()=>""),c=mU(o.status,i);c.persistent&&(Fi={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 Fi=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}ue();var aw={"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 hU(e){if(!e)return null;let t=String(e).trim();if(aw[t])return t;let n=null;for(let r of Object.keys(aw))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function wU(e,t={}){let n=aw[hU(e)];if(!n)return 0;let r=0;for(let o of Object.keys(n))r+=(Number(t[o])||0)*n[o];return r/1e6}function gU(e,t){let n=null;try{n=JSON.parse(t)}catch{}let r=n?.error?.code||n?.error?.type||"";return e===429&&/insufficient_quota/i.test(r)?{status:402,code:"credits_exhausted",persistent:!0,message:"The OpenAI account is out of credit \u2014 voice chat is down until you top up at platform.openai.com or drop a new key into ~/.bostrat/integrations.json."}:e===401||e===403?{status:502,code:"key_invalid",persistent:!0,message:"OpenAI rejected the API key (revoked, or missing Realtime access) \u2014 replace it in ~/.bostrat/integrations.json."}:{status:502,code:"mint_failed",persistent:!1,message:`OpenAI realtime session mint failed (${e}): ${t.slice(0,300)}`}}var Ui=null;async function yU(){if(!Ui)return null;let e=await lu();if(!e||e.apiKey.slice(-6)!==Ui.keyTail)return null;let{keyTail:t,...n}=Ui;return n}async function bU(e){let t=await fetch("https://api.openai.com/v1/realtime/client_secrets",{method:"POST",headers:{Authorization:`Bearer ${e.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({session:{type:"realtime",model:e.model,audio:{output:{voice:e.voice}}}})});if(!t.ok){let o=await t.text().catch(()=>""),s=gU(t.status,o);s.persistent&&(Ui={code:s.code,message:s.message,at:Date.now(),keyTail:e.apiKey.slice(-6)});let a=new Error(s.message);throw a.status=s.status,a.code=s.code,a}Ui=null;let n=await t.json(),r=n?.value??n?.client_secret?.value;if(!r){let o=new Error("OpenAI returned no client secret");throw o.status=502,o.code="mint_failed",o}return{clientSecret:{value:r,expiresAt:n?.expires_at??n?.client_secret?.expires_at??null}}}var kU={openai:bU};async function oE(){let e=await lu();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await yU()}:{configured:!1}}async function sE(){let e=await lu();if(!e){let n=new Error("Voice chat is not configured \u2014 add openai.apiKey to ~/.bostrat/integrations.json");throw n.status=503,n.code="not_configured",n}let t=await kU[e.provider](e);return{provider:e.provider,model:e.model,voice:e.voice,...t}}async function aE(e,{model:t,usage:n={},seconds:r}={}){let o=c=>Math.max(0,Math.round(Number(c)||0)),s={textIn:o(n.textIn),textInCached:o(n.textInCached),textOut:o(n.textOut),audioIn:o(n.audioIn),audioInCached:o(n.audioInCached),audioOut:o(n.audioOut)},a=wU(t,s),i=await _(e,{type:"cost.recorded",actor:"user",provider:"openai-realtime",payload:{model:t||"unknown",usd:a,tokens:{input:s.textIn+s.audioIn,output:s.textOut+s.audioOut,cacheRead:s.textInCached+s.audioInCached,cacheWrite:0},voice:s,seconds:Math.max(0,Math.round(Number(r)||0)),source:"voice-chat"}});return{usd:a,seq:i.seq}}var fE=ge.dirname(SU(import.meta.url)),iE=Number(process.env.PORT)||3362,Nu=rh(process.env),pE=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,qs=JT(process.env.BOSTRAT_ALLOW_IPS),dw=YT(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Au),mE=ZT(),hE=process.env.BOSTRAT_ALLOW_LAN_TOKENLESS!=="1",S=$t();S.disable("x-powered-by");S.use((e,t,n)=>{t.setHeader("x-content-type-options","nosniff"),Tu(e.headers.host)||(t.setHeader("x-frame-options","DENY"),t.setHeader("referrer-policy","strict-origin-when-cross-origin")),n()});S.use((e,t,n)=>{if(Di(e)||qs.allowed(qs.clientIp(e))||dw.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});S.use(rA);S.use(tA({token:mE,enforce:hE}));S.use("/api/mocks",$t.json({limit:"1mb"}));S.use("/api/mocks",$t.raw({type:"image/png",limit:"24mb"}));S.use("/api/mocks",$t.raw({type:["video/mp4","video/webm"],limit:"105mb"}));S.use("/api/runbooks",$t.json({limit:"1mb"}));S.use("/api/tracks/adopt",$t.json({limit:"1mb"}));S.use("/api/telemetry",$t.json({limit:"256kb"}));S.use("/api/hook",$t.json({limit:"2mb"}));S.use("/api/dictation",$t.raw({type:["audio/*","video/mp4","application/octet-stream"],limit:"32mb"}));S.use("/api/tracks/:id/upload",$t.raw({type:()=>!0,limit:"25mb"}));S.use($t.json({limit:"64kb"}));S.get("/api/health",async(e,t)=>{t.json({ok:!0,tmuxSocket:ft,hostTmuxAlive:await zn(),autoTriggerPct:pE})});S.get("/api/sessions",async(e,t)=>{t.json({sessions:await Ct()})});S.post("/api/sessions",async(e,t)=>{let{displayName:n,cwd:r}=e.body||{};if(typeof n!="string")return t.status(400).json({error:"displayName (string) required"});try{let o=await Jn(n,r||void 0);t.json({ok:!0,...o})}catch(o){let s=o?.message||String(o),a=/already exists/i.test(s)?409:/must be|invalid/i.test(s)?400:500;t.status(a).json({error:s})}});S.delete("/api/sessions/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!_e(n))return t.status(400).json({error:"invalid session name"});if(!await ae(n))return t.status(404).json({error:"session not found"});try{await Mt(n),t.json({ok:!0})}catch(r){t.status(500).json({error:`failed to kill: ${r?.message||r}`})}});S.get("/api/context/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!_e(n))return t.status(400).json({error:"invalid session name"});let r=await ae(n);if(!r)return t.status(404).json({error:"session not found"});let o=await ma(r.cwd,r.sessionId);if(!o)return t.json({available:!1,cwd:r.cwd,sessionId:r.sessionId||null});t.json({available:!0,...o})});S.post("/api/handoff/:name",async(e,t)=>{let n=decodeURIComponent(e.params.name);if(!_e(n))return t.status(400).json({error:"invalid session name"});try{let r=await ub(n);t.json({ok:!0,step:r.step,file:r.file,startedAt:r.startedAt})}catch(r){t.status(409).json({error:r?.message||String(r)})}});S.get("/api/handoff/:name",(e,t)=>{let n=decodeURIComponent(e.params.name),r=Fc(n);if(!r)return t.json({running:!1});t.json({running:!r.finishedAt,step:r.step,error:r.error,file:r.file,startedAt:r.startedAt,finishedAt:r.finishedAt})});var cE=10;S.get("/api/idle-guard",async(e,t)=>{try{t.json(nh({tracks:await oe(),views:await El(),queuedOf:n=>co(n)}))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var Rt=po(),_U=Cv({kind:Rt}),Hi=null,ot=()=>Hi&&["auto","prompt","off"].includes(Hi)?Hi:_U,Mu=null;async function IU(e){let t=ot();Hi=e??null;let n=ot();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){Ks.cancel();return}Mu&&!Ti(await $s())&&Ks.arm({to:Mu.latest,from:Mu.current,kind:Rt})}}var wE=(()=>{try{return EU(import.meta.url)("../package.json").version||null}catch{return null}})(),Ks=Nv({isBusy:async()=>nh({tracks:await oe(),views:await El(),queuedOf:e=>co(e)}).busy,onStuck:({to:e,from:t})=>an({current:t,latest:e,installKind:Rt,policy:ot(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?an({current:t,latest:e,installKind:Rt,policy:ot(),status:"updating",reason:"auto"}):an({current:t,latest:e,installKind:Rt,policy:ot(),status:"failed",error:n?.reason})).catch(()=>{})});S.get("/api/node/self-update",async(e,t)=>{t.json({installKind:Rt,policy:ot(),policySource:Hi?"plane":"node",current:wE,status:await $s(),armed:Ks.armed})});S.post("/api/node/self-update",async(e,t)=>{let n=typeof e.body?.to=="string"?e.body.to.trim():"";if(!/^\d+\.\d+\.\d+$/.test(n))return t.status(400).json({error:"body.to must be a release version (X.Y.Z)"});let r=await ru({to:n,kind:Rt,reason:"manual"});if(r.ok)return Ks.cancel(),an({current:r.from,latest:n,installKind:Rt,policy:ot(),status:"updating",reason:"manual"}).catch(()=>{}),t.json({ok:!0,...r});t.status(409).json({ok:!1,error:r.reason,status:r.status??null})});async function gE(){try{let e=await Mv();e&&await an({current:e.from,latest:e.to,installKind:e.kind||Rt,policy:ot(),status:e.status,error:e.error,reason:e.reason}),await xm({current:wE,installKind:Rt,policy:ot()})}catch{}}gE();setInterval(gE,3e4).unref();S.get("/api/tracks",async(e,t)=>{let n=(await oe()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>cE){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,cE).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:hi,agentAvailability:await Ll(),tickets:await oT()})});S.get("/api/cost",async(e,t)=>{try{let n=await oe();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await Lh(n);if(r.cursorUsage=kp(),e.query.format==="text")return t.type("text/plain").send(OT(r));t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/roundup",async(e,t)=>{try{t.json(await NT(await oe(),{window:e.query.window,from:e.query.from,to:e.query.to}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/spend",async(e,t)=>{try{let n=await LT(await oe(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=kp(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=Pa(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/caps",(e,t)=>{t.json(Pa())});S.post("/api/usage/caps",async(e,t)=>{try{t.json(await Yc(e.body?.caps??{},{source:"local"}))}catch(n){t.status(400).json({error:n?.message||String(n)})}});S.get("/api/usage/limits",async(e,t)=>{try{let n=e.query.fresh==="1"||e.query.fresh==="true";t.json(await qT(await oe(),{fresh:n}))}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/hook",async(e,t)=>{try{let{mode:n,target:r}=await hw(e.get(Vi),{byToken:Sm,legacy:()=>Va(e.body?.session_id,e.body?.cwd)});if(n==="refused")return Vs("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return Vs("legacy-denied",{reason:"tokenless-denied",hook:e.body?.hook_event_name}),t.status(401).json({ok:!1,error:"tokenless hook ingest is disabled (BOSTRAT_HOOK_LEGACY_DENY)"});n==="token"&&e.body?.session_id&&r.box?.sessionId&&e.body.session_id!==r.box.sessionId&&Vs("mismatch",{track:r.track.id,box:r.box.name}),Vs(n,{track:r?.track?.id,hook:e.body?.hook_event_name,...n==="legacy"?{reason:"tokenless-body-resolved",deny_with:"BOSTRAT_HOOK_LEGACY_DENY=1"}:{}});let o=await ET(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&cc(r.box.cwd),t.json({ok:!0,events:o.length})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});S.post("/api/telemetry",async(e,t)=>{try{if(!WA(qs.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await HA(e.body||{});t.json({ok:!0,events:n})}catch(n){t.json({ok:!1,error:n?.message||String(n)})}});S.get("/api/repos",async(e,t)=>{try{let n=await un(),r=new Set((await Promise.all(n.map(a=>me(a.path)))).filter(Boolean).map(a=>a.toLowerCase())),s=((await Zs()).repos||[]).filter(a=>!r.has(String(a.slug).toLowerCase())).map(a=>({slug:a.slug,name:a.slug.split("/")[1],branch:a.defaultBranch||null,private:!!a.private,remote:!0}));t.json({repos:[...n,...s]})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/github/token",async(e,t)=>{try{t.json(await Qi(String(e.query.repo||"")))}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});S.get("/api/github/status",(e,t)=>{t.json({configured:Zi(),calls:y0(),latency:S0()})});S.post("/api/github/refresh",(e,t)=>{Aw(),t.json({ok:!0})});S.get("/api/gmail/search",async(e,t)=>{try{let n=await GT(String(e.query.q||""),{max:Number(e.query.max)||10});t.json({messages:n})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});S.get("/api/gmail/messages/:id",async(e,t)=>{try{t.json({message:await VT(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});function fw(e,t,n){let r=e.workspace;return r?.kind==="multi"?(r.repos??[]).some(o=>t&&o?.base===t||n&&o?.repo===n):!r?.base||!t||r.base===t||n&&r.repo===n}async function Pu({repo:e=null,number:t=null,branch:n=null}){let r=e?await me(e):null;if(r&&t!=null){let s=await qe(r,t),a=s&&await ne(s.trackId);if(a)return a}if(!n)return null;let o=await oe();return wl(o,n,s=>fw(s,e,r))}async function yE({repo:e=null,number:t=null,branch:n=null}){let r=await Pu({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!r.archivedAt&&!r.transferredTo)return r;if(!n)return null;let o=e?await me(e).catch(()=>null):null,s=await oe();return wl(s,n,a=>!a.archivedAt&&!a.transferredTo&&fw(a,e,o))}async function Ut(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Pu({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await me(s).catch(()=>null):null);await _({id:c.id,cwd:c.cwd},{type:"pr.state.changed",actor:"user",source:"orchestrator",payload:{ts:Date.now(),change:e,number:t,url:n,title:r,branch:o??c.workspace?.branch??null,...l?{repo:l}:{},...i}}).catch(()=>{})}async function RU(e){let t=(await oe()).filter(n=>!n.archivedAt&&!n.transferredTo);for(let n of e??[]){if(!n.prs?.length)continue;let r=await me(n.path);if(r)for(let o of n.prs){if(await qe(r,o.number))continue;let s=await wl(t,o.headRefName,a=>fw(a,n.path,r));s&&await mt({repo:r,number:o.number,trackId:s.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"})}}}S.get("/api/git/prs",async(e,t)=>{try{let n=await R0();await RU(n.repos).catch(()=>{}),await RA(n.repos).catch(()=>{}),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/git/prs/merge",async(e,t)=>{let{repo:n,number:r,method:o,auto:s,update:a,archive:i,notify:c,headRefName:l,title:u,url:d}=e.body||{};try{if(s){let h=o||"merge";if(!Ha.has(h))throw new Error(`invalid merge method: ${h}`);let g=Number(r);if(!Number.isInteger(g)||g<=0)throw new Error("invalid PR number");let x=await me((await kn(n)).path);if(!x)throw new Error("repo has no GitHub remote");let k=await Pu({repo:n,number:g,branch:l}).catch(()=>null);if(!k)throw new Error("auto-merge needs a Track to watch this PR \u2014 none owns it");if(a&&await Wa(n,g),await mt({repo:x,number:g,trackId:k.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await Ba(x,g,{merge:{method:h}}))throw new Error("couldn't record the auto-merge arm");return await Zr(x,g,i===!0),await ja(x,g,c===!0&&i!==!0),Xt(),await Ut("auto-armed",{number:g,url:d,title:u,branch:l,repo:n,track:k,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:g,auto:!0})}let f=await cs(n,r,{method:o||"squash"});await Ut("merged",{number:f.number,url:d,title:u,branch:l,repo:n});let p=await me(n).catch(()=>null),m=!1;if(p&&(i===!0&&await Zr(p,f.number,!0).catch(()=>{}),c===!0&&i!==!0&&await ja(p,f.number,!0).catch(()=>{}),await Sn(p,f.number).catch(()=>{}),m=await xn(p,f.number).catch(()=>!1),await ur(p,f.number,"merged").catch(()=>{})),i===!0&&!m){let h=await Pu({repo:n,number:f.number,branch:l}).catch(()=>null);h&&!h.archivedAt&&await uo(h.id).catch(()=>{})}t.json(f)}catch(f){let p=f?.message||String(f),m=/invalid|unknown repo|required|needs a Track|no GitHub remote/i.test(p)?400:502;t.status(m).json({error:p})}});S.post("/api/git/prs/disarm-auto",async(e,t)=>{let{repo:n,number:r,headRefName:o,title:s,url:a}=e.body||{};try{let i=Number(r);if(!Number.isInteger(i)||i<=0)throw new Error("invalid PR number");let c=await me((await kn(n)).path).catch(()=>null),l=c?await La(c,i).catch(()=>null):null;try{await rp(n,i)}catch(u){if(!l)throw u}Xt(),await Ut("auto-canceled",{number:i,url:a,title:s,branch:o,repo:n}),t.json({ok:!0,number:i})}catch(i){let c=i?.message||String(i),l=/invalid|unknown repo|required/i.test(c)?400:502;t.status(l).json({error:c})}});S.post("/api/git/prs/merge-all",async(e,t)=>{let{repo:n,method:r}=e.body||{};try{let o=await me(n).catch(()=>null),s=await M0(n,{method:r||"merge",onMerged:a=>{Ut("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&Sn(o,a.number).catch(()=>{}).then(()=>xn(o,a.number)).catch(()=>{}).then(()=>ur(o,a.number,"merged")).catch(()=>{})}});t.json(s)}catch(o){let s=o?.message||String(o),a=/already running/i.test(s)?409:/invalid|unknown repo|no PRs ready/i.test(s)?400:502;t.status(a).json({error:s})}});S.get("/api/git/prs/merge-all",(e,t)=>{t.json({jobs:C0()})});S.post("/api/git/prs/merge-track",async(e,t)=>{let{trackId:n,update:r,archive:o,notify:s,method:a}=e.body||{};try{let i=a||"merge";if(!Ha.has(i))throw new Error(`invalid merge method: ${i}`);if(!n)return t.status(400).json({error:"trackId required"});let c=await ne(n);if(!c)return t.status(404).json({error:`Track ${n} isn't on this node`});let l=(await Qr()).filter(f=>f.trackId===c.id&&f.state!=="merged"&&f.state!=="closed");if(!l.length)return t.status(409).json({error:"no open PRs on this Track"});for(let f of l)await Zr(f.repo,f.number,o===!0).catch(()=>{}),await ja(f.repo,f.number,s===!0&&o!==!0).catch(()=>{});let u=[],d=[];for(let f of l){let p={repo:f.repo,number:f.number,ok:!0,action:"armed"};u.push(p);try{let m=await to(f.repo);if(!m)throw new Error(`no local checkout for ${f.repo}`);let h=await is(m,f.number);d.push({r:p,l:f,repoPath:m,pr:h,plan:$0(h,{update:r!==!1})})}catch(m){p.ok=!1,p.action="failed",p.reason=m?.message||String(m)}}for(let{r:f,l:p,repoPath:m,pr:h,plan:g}of O0(d))try{if(g.action==="skip")f.action="skipped",f.reason=g.reason;else if(g.action==="merge")await cs(m,p.number,{method:i}),await Ut("merged",{number:p.number,url:h.url,title:h.title,branch:h.headRefName,repo:m,track:c}),await Sn(p.repo,p.number).catch(()=>{}),await xn(p.repo,p.number).catch(()=>{}),await ur(p.repo,p.number,"merged").catch(()=>{}),f.action="merged";else{if(g.action==="update-arm"&&(await Wa(m,p.number).catch(()=>{}),f.action="updated-armed"),!await Ba(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await Ut("auto-armed",{number:p.number,url:h.url,title:h.title,branch:h.headRefName,repo:m,track:c,...f.action==="updated-armed"?{extra:{updated:!0}}:{}})}}catch(x){f.ok=!1,f.action="failed",f.reason=x?.message||String(x)}if(Xt(),!u.some(f=>f.ok)){if(o===!0)for(let f of l)await Zr(f.repo,f.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let f of l)await ja(f.repo,f.number,!1).catch(()=>{});return t.status(502).json({error:u.map(f=>`${f.repo}#${f.number}: ${f.reason}`).join("; "),results:u})}t.json({ok:!0,trackId:c.id,results:u})}catch(i){let c=i?.message||String(i),l=/invalid|required/i.test(c)?400:502;t.status(l).json({error:c})}});S.post("/api/git/prs/disarm-track",async(e,t)=>{let{trackId:n}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let r=await ne(n);if(!r)return t.status(404).json({error:`Track ${n} isn't on this node`});let o=(await Qr()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await La(a.repo,a.number).catch(()=>null),c=await to(a.repo).catch(()=>null);c&&await rp(c,a.number).catch(()=>{}),i&&(await Ut("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}))}Xt(),t.json({ok:!0,trackId:r.id,canceled:s})}catch(r){let o=r?.message||String(r),s=/invalid|required/i.test(o)?400:502;t.status(s).json({error:o})}});S.post("/api/git/prs/open",async(e,t)=>{let{trackId:n,title:r,body:o,base:s,repos:a}=e.body||{};try{if(!n)return t.status(400).json({error:"trackId required"});let i=await ne(n);if(!i)return t.status(404).json({error:`Track ${n} isn't on this node`});if(i.workspace?.kind==="multi"){let u=gt(i.workspace),d,f=Array.isArray(a)?a.filter(Boolean):[];if(f.length)d=f.map(h=>{let g=u.find(x=>x.name===h||x.repo===h||x.base===h||x.dir===h);if(!g)throw new Error(`invalid repo "${h}" \u2014 not tagged on this Track`);return g});else if(d=await ps(i),!d.length)return t.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let h of d)try{let g=await sp(h.dir,{title:r,body:o,base:s}),x=h.repo||await me(h.dir).catch(()=>null);x&&g.number&&await mt({repo:x,number:g.number,trackId:i.id,url:g.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Ut("opened",{number:g.number,url:g.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:x??h.name}}),p.push({repo:h.name,slug:x??null,ok:!0,url:g.url,number:g.number})}catch(g){p.push({repo:h.name,slug:h.repo??null,ok:!1,error:g?.message||String(g)})}let m=p.filter(h=>h.ok);return m.length?t.json({ok:!0,multi:!0,results:p,url:m[0].url,number:m[0].number}):t.status(502).json({error:p.map(h=>`${h.repo}: ${h.error}`).join("; "),results:p})}let c=await sp(i.cwd,{title:r,body:o,base:s}),l=await me(i.cwd).catch(()=>null);l&&c.number&&await mt({repo:l,number:c.number,trackId:i.id,url:c.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Ut("opened",{number:c.number,url:c.url,title:r,track:i,branch:i.workspace?.branch,extra:{...l?{repo:l}:{}}}),t.json(c)}catch(i){let c=i?.message||String(i),l=/required|invalid/i.test(c)?400:502;t.status(l).json({error:c})}});S.get("/api/tracks/:id/repos",async(e,t)=>{try{let n=await ne(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await ps(n),o=new Map(r.map(a=>[a.base,a])),s=gt(n.workspace).map(a=>{let i=o.get(a.base);return{name:a.name,base:a.base,baseRef:a.baseRef??null,repo:a.repo??null,dirty:i?.dirty??!1,ahead:i?.ahead??0,changed:!!i}});t.json({repos:s,branch:n.workspace?.branch??null,kind:n.workspace?.kind??null})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/tracks/:id/repos",async(e,t)=>{let{repo:n}=e.body||{};try{if(!n||typeof n!="string")return t.status(400).json({error:"repo (path or owner/name) required"});let r=await Mm(e.params.id,n);t.json({ok:!0,added:{name:r.name,base:r.base,baseRef:r.baseRef??null,repo:r.repo??null}})}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/already tagged|needs a multi|not a usable|archived|required/i.test(o)?400:502;t.status(s).json({error:o})}});S.delete("/api/tracks/:id/repos/:name",async(e,t)=>{try{let n=await Nm(e.params.id,e.params.name);t.json({ok:!0,...n})}catch(n){let r=n?.message||String(n),o=/track .* not found/i.test(r)?404:/has work|still running|last repo/i.test(r)?409:/needs a multi|not tagged/i.test(r)?400:502;t.status(o).json({error:r})}});S.post("/api/git/prs/fix",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await is(n,r),s=await me(n).catch(()=>null),a=await yE({repo:n,number:r,branch:o.headRefName});if(!a)return t.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=gt(a.workspace).find(x=>x.base===n||s&&x.repo===s)??null,c=a.workspace?.kind==="multi",l=await N0(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?ge.join("ci",i.name):"ci",d=ge.join(a.cwd,".bostrat",u),f=ge.join(".bostrat",u,`pr-${o.number}-fail.log`);await Xe.promises.mkdir(d,{recursive:!0}),await Xe.promises.writeFile(ge.join(a.cwd,f),l.map(x=>`\u2550\u2550\u2550\u2550 ${x.name}${x.link?` \xB7 ${x.link}`:""} \u2550\u2550\u2550\u2550
|
|
594
600
|
${x.log??"(no fetchable log \u2014 see link)"}
|
|
595
601
|
`).join(`
|
|
596
|
-
`));let p=l.map(x=>x.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",h=await It(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"}),g=s;g&&(await mt({repo:g,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Fk(g,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Ut("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(x=>x.name)}}),t.json({ok:!0,leadBox:h.leadBox,queued:!!h.queued,checks:l.map(x=>x.name),log:d})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});S.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await es(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await me(n).catch(()=>null),a=await nA({repo:n,number:r,branch:o.headRefName});if(!a)return t.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=gt(a.workspace).find(f=>f.base===n||s&&f.repo===s)??null,c=a.workspace?.kind==="multi"&&i?` In the ${i.name}/ checkout:`:"",l=await It(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 mt({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await Uk(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Ut("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),t.json({ok:!0,leadBox:l.leadBox,queued:!!l.queued,base:o.baseRefName})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});S.post("/api/tracks/:id/pr-offer",async(e,t)=>{let{offerId:n,action:r}=e.body||{};try{let o=null;if(Array.isArray(e.body?.actions)){if(o=zo.filter(f=>e.body.actions.includes(f)),!o.length)return t.status(400).json({error:"actions must include commit, rebase, push and/or pr"})}else if(r==="pr")o=["commit","push","pr"];else if(r==="push")o=["commit","push"];else if(r!=="dismiss")return t.status(400).json({error:'offerId and actions ["commit"|"rebase"|"push"|"pr"] (or action "dismiss") required'});if(!n)return t.status(400).json({error:"offerId required"});let s=o!=null&&e.body?.notify===!0,a=await ne(e.params.id);if(!a)return t.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 t.status(404).json({error:"unknown offer"});if(i.some(f=>f.type==="pr.offer.answered"&&f.payload?.offerId===n))return t.status(409).json({error:"offer already answered"});if(o?.includes("pr")){let f=await Lh(a,c,{actions:o,notify:s});if(f)return t.json({ok:!0,offerId:n,action:"pr-exists",actions:o,notify:s,existingPr:f.pr})}let l=null;o&&(l=await It(a.id,Jo(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 _({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&&gm(a.id,{actions:o}),t.json({ok:!0,offerId:n,action:u,actions:o,notify:s,queued:!!l?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});S.post("/api/tracks/:id/pr-exists",async(e,t)=>{let{offerId:n,choice:r}=e.body||{};try{let o=await ne(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await Fh(o,{offerId:n,choice:r});t.json({ok:!0,offerId:n,choice:r,queued:!!s?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/unknown offer/i.test(s)?404:/already answered|not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});var Di=e=>t=>({...t,url:qT(t,e.headers)});S.get("/api/views",async(e,t)=>{try{let n=await yl({trackId:e.query.track||void 0});t.json({views:n.map(Di(e)),maxLive:mr})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/views/:id",async(e,t)=>{let n=await mx(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:Di(e)(n),session:is(n.slug)})});S.post("/api/views",async(e,t)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=e.body||{};try{let i=n?await ne(n):null;if(!i&&s&&(i=(await ae()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ge.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await vp({trackId:i.id,name:r,cmd:o,cwd:i.cwd,serveCwd:s,port:a,box:i.leadBox,branch:i.workspace?.branch??null});t.json({ok:!0,view:Di(e)(c)})}catch(i){let c=i?.message||String(i),l=i?.status??(/cap reached/i.test(c)?409:/required|invalid|already owned|no free/i.test(c)?400:500);t.status(l).json({error:c})}});S.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:Di(e)(await hx(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:Di(e)(await wx(e.params.id))})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/cap reached|no known command/i.test(r)?409:500;t.status(o).json({error:r})}});S.delete("/api/views/:id",async(e,t)=>{try{t.json(await gx(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var aU=[["linear",uo,rh],["jira",eu,th]],zh=new Map;S.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await eh(),r={};for(let[o,s,a]of aU){let i=await s();if(!i){zh.delete(o),r[o]={configured:!1};continue}let c=zh.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},zh.set(o,c)}r[o]={...c.body,flow:n},o==="jira"&&i.tokenExpiresAt&&(r[o].tokenExpiresAt=i.tokenExpiresAt)}t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await eh()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await _v(o),i=n?await ne(n):null;!i&&r&&(i=(await ae()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ge.sep)))||null),i&&await _({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),t.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);t.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});var wo=async(e,t)=>{let n=e?await ne(e):null;return!n&&t&&(n=(await ae()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+ge.sep)))||null),n};S.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await Ym()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});S.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await wo(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (circuit name or id) required"});let a=await vs(o);await Yl(s,a,{source:"chat"}),t.json({ok:!0,trackId:s.id,circuit:a})}catch(s){let a=s?.message||String(s);t.status(/required|no circuit|names \d|no plane/i.test(a)?400:502).json({error:a})}});S.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await wo(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await Yl(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var rA=async(e,t,n)=>{if(e)return{ref:e,track:await wo(t,n)};let r=await wo(t,n),o=r?await Zm(r.id):null;if(!o)throw new Error(r?"this Track isn't in a circuit \u2014 pass --circuit <name-or-id> (or link one first: bostrat-circuit link \u2026)":"run from inside a Track workspace, or pass --circuit <name-or-id>");return{ref:o,track:r}};S.post("/api/blueprints/publish",async(e,t)=>{let{trackId:n,cwd:r,circuit:o,newCircuit:s,name:a,markdown:i}=e.body||{};try{if(!i||!String(i).trim())return t.status(400).json({error:"markdown required"});let{ref:c}=s?{ref:null}:await rA(o,n,r),l=await kv({circuitRef:c,newCircuitName:s,name:a,markdown:i});t.json({ok:!0,...l})}catch(c){let l=c?.message||String(c);t.status(/required|no circuit|names \d|no plane|isn't in a circuit|run from inside/i.test(l)?400:502).json({error:l})}});S.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await rA(e.query.circuit,e.query.track,e.query.cwd);t.json(await Xm(n))}catch(n){let r=n?.message||String(n);t.status(/no blueprint|no circuit|isn't in a circuit|run from inside|names \d/i.test(r)?404:502).json({error:r})}});S.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,alternatives:i,supersedesId:c}=e.body||{};try{let l=await wo(n,r);if(!l)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=await Sv(l,{title:o,context:s,decision:a,alternatives:i,supersedesId:c});t.json({ok:!0,trackId:l.id,...u})}catch(l){let u=l?.message||String(l);t.status(/required/i.test(u)?400:502).json({error:u})}});S.get("/api/decisions",async(e,t)=>{try{let n=await wo(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await vv(n.id))}catch(n){let r=n?.message||String(n);t.status(/no plane|required|run from inside/i.test(r)?400:502).json({error:r})}});S.post("/api/blueprints/tag",async(e,t)=>{let{trackId:n,cwd:r,itemIds:o}=e.body||{};try{let s=Array.isArray(o)?o.map(String):[];if(!s.length)return t.status(400).json({error:"itemIds required (BP-\u2026 ids)"});let a=await wo(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await Zm(a.id);if(!i)return t.status(400).json({error:"this Track isn't in a circuit \u2014 link one first (bostrat-circuit link \u2026)"});let c=await xv(a,i,s);t.json({ok:!0,trackId:a.id,...c})}catch(s){let a=s?.message||String(s);t.status(/unknown\/unbindable|required|no blueprint|isn't in a circuit/i.test(a)?400:502).json({error:a})}});S.post("/api/tickets/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=n?await ne(n):null;if(!s&&r&&(s=(await ae()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+ge.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (issue URL or key) required"});let a=await As(s,o,{source:n?"api":"cli"});t.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);t.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});S.get("/api/tickets/status",async(e,t)=>{let{cwd:n,track:r}=e.query;try{let o=r?await ne(r):null;if(!o&&n&&(o=(await ae()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+ge.sep)))||null),!o)return t.status(400).json({error:"track (or a cwd inside a Track workspace) required"});t.json({trackId:o.id,configured:await Wv(),ticket:await an(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});S.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await an(e.params.id)})});S.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await ne(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await As(n,(e.body||{}).ref,{source:"api"});t.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);t.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});S.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await hh(e.params.id)})});var Ru=e=>/no ticket linked|unknown offer|unknown state/i.test(e)?404:/already answered/i.test(e)?409:/required|no ticketing/i.test(e)?400:502;S.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await Vv(e.params.id))}catch(n){let r=n?.message||String(n);t.status(Ru(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await wh(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(Ru(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await zv(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(Ru(r)).json({error:r})}});S.post("/api/tracks/:id/ticket-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{let o=await ne(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await Jv(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(Ru(s)).json({error:s})}});S.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await Cm(e.params.id,n,r===!0))}catch(o){let s=o?.message||String(o),a=/not found|unknown offer/.test(s)?404:/already answered/.test(s)||/mid-turn/.test(s)?409:500;t.status(a).json({error:s})}});S.post("/api/tickets/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await ne(n):null;if(!s&&r&&(s=(await ae()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ge.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await Xv(s,o)})}catch(s){let a=s?.message||String(s);t.status(/no ticketing|are off|needs an epic|too many|required/i.test(a)?400:502).json({error:a})}});S.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await Yv()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});S.post("/api/tracks/:id/ticket-create-offer",async(e,t)=>{let{proposalId:n,accept:r,teamId:o}=e.body||{};try{let s=await ne(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await Zv(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal|unknown team/i.test(a)?404:/already answered/i.test(a)?409:/required|no ticketing|pick one/i.test(a)?400:502).json({error:a})}});S.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await Va({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/mocks/:id",async(e,t)=>{let n=await En(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var Bi={dotfiles:"allow"};S.get("/api/mocks/:id/png",async(e,t)=>{let n=await En(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=Bt(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Ka(n,r,"png");if(!o)return t.status(404).json({error:"no image for this version (poster extraction failed)"});t.set("cache-control","private, max-age=31536000, immutable"),t.sendFile(o,Bi,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});S.get("/api/mocks/:id/media",async(e,t)=>{let n=await En(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=Bt(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Ka(n,r,"video");if(!o)return t.status(404).json({error:"this version has no video (image or html sourced)"});t.set("cache-control","private, max-age=31536000, immutable"),t.sendFile(o,Bi,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});S.get("/api/mocks/:id/html",async(e,t)=>{let n=await En(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=Bt(n,e.query.v??null);if(!r?.htmlRel)return t.status(404).json({error:"this version has no HTML source (image-sourced)"});t.set("content-security-policy","sandbox"),t.sendFile(Et(n,r.v).html,Bi,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});S.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&Sl(n)?n:null,o=n&&!r?n:null,s=n?e.query:e.body||{},{trackId:a,name:i,cwd:c,width:l,group:u}=s;try{let f=a?await ne(a):null;if(!f&&c&&(f=(await ae()).find(h=>h.cwd&&(c===h.cwd||c.startsWith(h.cwd+ge.sep)))||null),!f)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=(f.boxes||[]).find(m=>m.name===f.leadBox),p=await Nx({trackId:f.id,name:i,html:n?null:s.html,image:r,video:o,cwd:f.cwd,width:l,group:u,box:f.leadBox,agent:d?.agent||null});t.json({ok:!0,mock:p})}catch(f){let d=f?.message||String(f);t.status(/required|invalid|must be/i.test(d)?400:500).json({error:d})}});S.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&Sl(n)?n:null;try{let o=await Px(e.params.id,{html:n?null:(e.body||{}).html,image:r,video:n&&!r?n:null,width:n?e.query.width:(e.body||{}).width});t.json({ok:!0,mock:o})}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required|invalid|must be/i.test(s)?400:500;t.status(a).json({error:s})}});S.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await jx(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await It(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),t.json({ok:!0,mock:n,siblings:r})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/nothing to pick/i.test(r)?409:500;t.status(o).json({error:r})}});S.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await nS(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await Lx(e.params.id),...n.length?{danglingFlows:n}:{}})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.get("/api/flows",async(e,t)=>{try{t.json({flows:await Xx({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/flows/:id",async(e,t)=>{let n=await Zx(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});S.post("/api/flows",async(e,t)=>{let{trackId:n,name:r,steps:o,cwd:s}=e.body||{};try{let a=n?await ne(n):null;if(!a&&s&&(a=(await ae()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ge.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await Qx({trackId:a.id,name:r,steps:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,flow:c})}catch(a){let i=a?.message||String(a),c=/already exists/i.test(i)?409:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(i)?400:500;t.status(c).json({error:i})}});S.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await eS(e.params.id,{steps:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(r)?400:500;t.status(o).json({error:r})}});S.delete("/api/flows/:id",async(e,t)=>{try{t.json(await tS(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await ae(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+ge.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await uS({trackId:n,status:e.query.status||void 0});t.json({runbooks:r,...o?{fleetDegraded:!0}:{}})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/runbooks/:id",async(e,t)=>{let n=await Bp(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});S.get("/api/runbooks/:id/md",async(e,t)=>{let n=await Bp(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=cS(n,e.query.v??null);if(!r)return t.status(404).json({error:"no such version"});if(t.set("content-security-policy","sandbox"),t.set("cache-control","private, max-age=31536000, immutable"),t.type("text/markdown; charset=utf-8"),n.remote){let o=await dS(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(Rn(n,r.v),Bi,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});S.get("/api/loops",async(e,t)=>{t.json(await dl())});S.post("/api/loops/report",async(e,t)=>{let{outcome:n,note:r,cwd:o}=e.body||{};if(!["ok","acted","attention"].includes(n))return t.status(400).json({error:"outcome must be one of ok | acted | attention"});if(!o)return t.status(400).json({error:"cwd required"});let a=(await ae()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+ge.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await dl();if(i.source!=="plane")return t.status(503).json({error:i.source==="unconfigured"?"this node isn't linked to a plane \u2014 loops live in the plane":`plane unreachable (${i.error||"no detail"})`});let c=i.loops.find(u=>u.trackId===a.id);if(!c)return t.status(404).json({error:`Track "${a.name}" is not a loop's run Track \u2014 nothing to report on`});let l=await np({loopId:c.id,outcome:n,note:r});if(!l.ok)return t.status(l.status||503).json({error:l.error||"report failed"});t.json({ok:!0,loop:{id:c.id,name:c.name},run:l.run})});S.post("/api/loops/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await ne(n):null;if(!s&&r&&(s=(await ae()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ge.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await YT(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});S.post("/api/tracks/:id/loop-create-offer",async(e,t)=>{let{proposalId:n,accept:r,loopId:o,nextFireAt:s}=e.body||{};try{let a=await ne(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await XT(a,n,r===!0,{loopId:o||null,nextFireAt:s??null})})}catch(a){let i=a?.message||String(a);t.status(/unknown proposal/i.test(i)?404:/already answered/i.test(i)?409:400).json({error:i})}});S.post("/api/runbooks",async(e,t)=>{let{trackId:n,name:r,md:o,cwd:s}=e.body||{};try{let a=n?await ne(n):null;if(!a&&s&&(a=(await ae()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ge.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await fS({trackId:a.id,trackName:a.name,name:r,md:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,runbook:c})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});S.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await pS(e.params.id,{md:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/required|invalid|over the|secret VALUE/i.test(r)?400:500;t.status(o).json({error:r})}});S.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await mS(e.params.id,(e.body||{}).status)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/must be/i.test(r)?400:500;t.status(o).json({error:r})}});S.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await hS(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.get("/api/changelog/draft",async(e,t)=>{try{let n=nE(e.query.since||null);t.json({since:n,repos:Mh(),...await rE(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var oA=async({trackId:e,cwd:t})=>{let n=e?await ne(e):null;return!n&&t&&(n=(await ae()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+ge.sep)))||null),n};S.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=await oA({trackId:o,cwd:s});if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,entry:await oE(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|no `- `|over the|secret VALUE|mentions "/i.test(i)?400:500).json({error:i})}});S.post("/api/changelog/retract",async(e,t)=>{let{day:n,trackId:r,cwd:o}=e.body||{};try{let s=await oA({trackId:r,cwd:o});if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,entry:await sE(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var iU=120,Jh=400,Iu=null;S.post("/api/notify",async(e,t)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a}=e.body||{};try{let i=typeof r=="string"?r.trim():"";if(!i)return t.status(400).json({error:"body (the message) required"});if(i.length>Jh)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${Jh} cap; a notification is a nudge, put the detail in chat`});let c=za(`${n||""}
|
|
597
|
-
${i}`);if(c.length)return t.status(400).json({error:`notification appears to contain a secret VALUE (${c.join(", ")}) \u2014 notifications ship off-box; never put a credential in one`});let l=a?await ne(a):null;if(!l&&s&&(l=(await
|
|
602
|
+
`));let p=l.map(x=>x.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",h=await It(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 ${f} in this ${c?"workspace":"worktree"} \u2014 read it, fix the root cause${c&&i?` in ${i.name}/`:""}, run the relevant tests locally, then commit and push to update the PR.`,{kind:"ci-fix"}),g=s;g&&(await mt({repo:g,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await t0(g,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Ut("fix-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{checks:l.map(x=>x.name)}}),t.json({ok:!0,leadBox:h.leadBox,queued:!!h.queued,checks:l.map(x=>x.name),log:f})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});S.post("/api/git/prs/reconcile",async(e,t)=>{let{repo:n,number:r}=e.body||{};try{let o=await is(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await me(n).catch(()=>null),a=await yE({repo:n,number:r,branch:o.headRefName});if(!a)return t.status(404).json({error:`no active Track owns PR #${o.number} (branch "${o.headRefName}")`});let i=gt(a.workspace).find(d=>d.base===n||s&&d.repo===s)??null,c=a.workspace?.kind==="multi"&&i?` In the ${i.name}/ checkout:`:"",l=await It(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 mt({repo:u,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await n0(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Ut("reconcile-delegated",{number:o.number,url:o.url,title:o.title,branch:o.headRefName,repo:n,track:a,extra:{base:o.baseRefName}}),t.json({ok:!0,leadBox:l.leadBox,queued:!!l.queued,base:o.baseRefName})}catch(o){let s=o?.message||String(o),a=/unknown repo|invalid|required/i.test(s)?400:/not found|no active Track/i.test(s)?404:502;t.status(a).json({error:s})}});S.post("/api/tracks/:id/pr-offer",async(e,t)=>{let{offerId:n,action:r}=e.body||{};try{let o=null;if(Array.isArray(e.body?.actions)){if(o=ts.filter(d=>e.body.actions.includes(d)),!o.length)return t.status(400).json({error:"actions must include commit, rebase, push and/or pr"})}else if(r==="pr")o=["commit","push","pr"];else if(r==="push")o=["commit","push"];else if(r!=="dismiss")return t.status(400).json({error:'offerId and actions ["commit"|"rebase"|"push"|"pr"] (or action "dismiss") required'});if(!n)return t.status(400).json({error:"offerId required"});let s=o!=null&&e.body?.notify===!0,a=await ne(e.params.id);if(!a)return t.status(404).json({error:"track not found"});let i=await L(a),c=i.find(d=>d.type==="pr.offered"&&d.payload?.offerId===n);if(!c)return t.status(404).json({error:"unknown offer"});if(i.some(d=>d.type==="pr.offer.answered"&&d.payload?.offerId===n))return t.status(409).json({error:"offer already answered"});if(o?.includes("pr")){let d=await Zh(a,c,{actions:o,notify:s});if(d)return t.json({ok:!0,offerId:n,action:"pr-exists",actions:o,notify:s,existingPr:d.pr})}let l=null;o&&(l=await It(a.id,ns(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 _({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&&Im(a.id,{actions:o}),t.json({ok:!0,offerId:n,action:u,actions:o,notify:s,queued:!!l?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});S.post("/api/tracks/:id/pr-exists",async(e,t)=>{let{offerId:n,choice:r}=e.body||{};try{let o=await ne(e.params.id);if(!o)return t.status(404).json({error:"track not found"});let{say:s}=await Qh(o,{offerId:n,choice:r});t.json({ok:!0,offerId:n,choice:r,queued:!!s?.queued})}catch(o){let s=o?.message||String(o),a=/required|invalid/i.test(s)?400:/unknown offer/i.test(s)?404:/already answered|not found|not running|archived/i.test(s)?409:502;t.status(a).json({error:s})}});var Wi=e=>t=>({...t,url:sA(t,e.headers)});S.get("/api/views",async(e,t)=>{try{let n=await El({trackId:e.query.track||void 0});t.json({views:n.map(Wi(e)),maxLive:gr})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/views/:id",async(e,t)=>{let n=await Rx(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:Wi(e)(n),session:ms(n.slug)})});S.post("/api/views",async(e,t)=>{let{trackId:n,name:r,cmd:o,cwd:s,port:a}=e.body||{};try{let i=n?await ne(n):null;if(!i&&s&&(i=(await oe()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ge.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await Np({trackId:i.id,name:r,cmd:o,cwd:i.cwd,serveCwd:s,port:a,box:i.leadBox,branch:i.workspace?.branch??null});t.json({ok:!0,view:Wi(e)(c)})}catch(i){let c=i?.message||String(i),l=i?.status??(/cap reached/i.test(c)?409:/required|invalid|already owned|no free/i.test(c)?400:500);t.status(l).json({error:c})}});S.post("/api/views/:id/stop",async(e,t)=>{try{t.json({ok:!0,view:Wi(e)(await $x(e.params.id))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.post("/api/views/:id/restart",async(e,t)=>{try{t.json({ok:!0,view:Wi(e)(await Ox(e.params.id))})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/cap reached|no known command/i.test(r)?409:500;t.status(o).json({error:r})}});S.delete("/api/views/:id",async(e,t)=>{try{t.json(await Cx(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var $U=[["linear",mo,hh],["jira",cu,ph]],iw=new Map;S.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await fh(),r={};for(let[o,s,a]of $U){let i=await s();if(!i){iw.delete(o),r[o]={configured:!1};continue}let c=iw.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},iw.set(o,c)}r[o]={...c.body,flow:n},o==="jira"&&i.tokenExpiresAt&&(r[o].tokenExpiresAt=i.tokenExpiresAt)}t.json(r)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/tickets/flow",async(e,t)=>{try{t.json({flow:await fh()})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/tickets/flow",async(e,t)=>{let{trackId:n,cwd:r,set:o,actor:s}=e.body||{};try{let a=await Wv(o),i=n?await ne(n):null;!i&&r&&(i=(await oe()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+ge.sep)))||null),i&&await _({id:i.id,cwd:i.cwd},{type:"ticket.flow.changed",actor:s==="user"?"user":"agent",source:"orchestrator",payload:{ts:Date.now(),set:{...o},flow:a}}).catch(()=>{}),t.json({ok:!0,flow:a})}catch(a){let i=a?.message||String(a);t.status(/unknown|nothing to set/i.test(i)?400:500).json({error:i})}});var bo=async(e,t)=>{let n=e?await ne(e):null;return!n&&t&&(n=(await oe()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+ge.sep)))||null),n};S.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await ih()})}catch(n){t.status(502).json({error:n?.message||String(n)})}});S.post("/api/circuits/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=await bo(n,r);if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (circuit name or id) required"});let a=await Os(o);await ou(s,a,{source:"chat"}),t.json({ok:!0,trackId:s.id,circuit:a})}catch(s){let a=s?.message||String(s);t.status(/required|no circuit|names \d|no plane/i.test(a)?400:502).json({error:a})}});S.post("/api/circuits/unlink",async(e,t)=>{let{trackId:n,cwd:r}=e.body||{};try{let o=await bo(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await ou(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var bE=async(e,t,n)=>{if(e)return{ref:e,track:await bo(t,n)};let r=await bo(t,n),o=r?await lh(r.id):null;if(!o)throw new Error(r?"this Track isn't in a circuit \u2014 pass --circuit <name-or-id> (or link one first: bostrat-circuit link \u2026)":"run from inside a Track workspace, or pass --circuit <name-or-id>");return{ref:o,track:r}};S.post("/api/blueprints/publish",async(e,t)=>{let{trackId:n,cwd:r,circuit:o,newCircuit:s,name:a,markdown:i}=e.body||{};try{if(!i||!String(i).trim())return t.status(400).json({error:"markdown required"});let{ref:c}=s?{ref:null}:await bE(o,n,r),l=await Pv({circuitRef:c,newCircuitName:s,name:a,markdown:i});t.json({ok:!0,...l})}catch(c){let l=c?.message||String(c);t.status(/required|no circuit|names \d|no plane|isn't in a circuit|run from inside/i.test(l)?400:502).json({error:l})}});S.get("/api/blueprints/show",async(e,t)=>{try{let{ref:n}=await bE(e.query.circuit,e.query.track,e.query.cwd);t.json(await ch(n))}catch(n){let r=n?.message||String(n);t.status(/no blueprint|no circuit|isn't in a circuit|run from inside|names \d/i.test(r)?404:502).json({error:r})}});S.post("/api/decisions/record",async(e,t)=>{let{trackId:n,cwd:r,title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l}=e.body||{};try{let u=await bo(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=await jv(u,{title:o,context:s,decision:a,summary:i,alternatives:c,supersedesId:l});t.json({ok:!0,trackId:u.id,...d})}catch(u){let d=u?.message||String(u);t.status(/required/i.test(d)?400:502).json({error:d})}});S.get("/api/decisions",async(e,t)=>{try{let n=await bo(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await Lv(n.id))}catch(n){let r=n?.message||String(n);t.status(/no plane|required|run from inside/i.test(r)?400:502).json({error:r})}});S.post("/api/blueprints/tag",async(e,t)=>{let{trackId:n,cwd:r,itemIds:o}=e.body||{};try{let s=Array.isArray(o)?o.map(String):[];if(!s.length)return t.status(400).json({error:"itemIds required (BP-\u2026 ids)"});let a=await bo(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await lh(a.id);if(!i)return t.status(400).json({error:"this Track isn't in a circuit \u2014 link one first (bostrat-circuit link \u2026)"});let c=await Dv(a,i,s);t.json({ok:!0,trackId:a.id,...c})}catch(s){let a=s?.message||String(s);t.status(/unknown\/unbindable|required|no blueprint|isn't in a circuit/i.test(a)?400:502).json({error:a})}});S.post("/api/tickets/link",async(e,t)=>{let{trackId:n,cwd:r,ref:o}=e.body||{};try{let s=n?await ne(n):null;if(!s&&r&&(s=(await oe()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+ge.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (issue URL or key) required"});let a=await Ns(s,o,{source:n?"api":"cli"});t.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);t.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});S.get("/api/tickets/status",async(e,t)=>{let{cwd:n,track:r}=e.query;try{let o=r?await ne(r):null;if(!o&&n&&(o=(await oe()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+ge.sep)))||null),!o)return t.status(400).json({error:"track (or a cwd inside a Track workspace) required"});t.json({trackId:o.id,configured:await sT(),ticket:await ln(o.id)})}catch(o){t.status(500).json({error:o?.message||String(o)})}});S.get("/api/tracks/:id/ticket",async(e,t)=>{t.json({ticket:await ln(e.params.id)})});S.post("/api/tracks/:id/ticket",async(e,t)=>{try{let n=await ne(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=await Ns(n,(e.body||{}).ref,{source:"api"});t.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);t.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});S.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await _h(e.params.id)})});var Bu=e=>/no ticket linked|unknown offer|unknown state/i.test(e)?404:/already answered/i.test(e)?409:/required|no ticketing/i.test(e)?400:502;S.get("/api/tracks/:id/ticket/states",async(e,t)=>{try{t.json(await lT(e.params.id))}catch(n){let r=n?.message||String(n);t.status(Bu(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await Ih(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(Bu(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await uT(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(Bu(r)).json({error:r})}});S.post("/api/tracks/:id/ticket-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{let o=await ne(e.params.id);if(!o)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await dT(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(Bu(s)).json({error:s})}});S.post("/api/tracks/:id/fullscreen-offer",async(e,t)=>{let{offerId:n,accept:r}=e.body||{};try{t.json(await Wm(e.params.id,n,r===!0))}catch(o){let s=o?.message||String(o),a=/not found|unknown offer/.test(s)?404:/already answered/.test(s)||/mid-turn/.test(s)?409:500;t.status(a).json({error:s})}});S.post("/api/tickets/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await ne(n):null;if(!s&&r&&(s=(await oe()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ge.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await pT(s,o)})}catch(s){let a=s?.message||String(s);t.status(/no ticketing|are off|needs an epic|too many|required/i.test(a)?400:502).json({error:a})}});S.get("/api/tickets/teams",async(e,t)=>{try{t.json({teams:await fT()})}catch(n){let r=n?.message||String(n);t.status(/no ticketing/i.test(r)?400:502).json({error:r})}});S.post("/api/tracks/:id/ticket-create-offer",async(e,t)=>{let{proposalId:n,accept:r,teamId:o}=e.body||{};try{let s=await ne(e.params.id);if(!s)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await mT(s,n,r===!0,{teamId:o||null})})}catch(s){let a=s?.message||String(s);t.status(/unknown proposal|unknown team/i.test(a)?404:/already answered/i.test(a)?409:/required|no ticketing|pick one/i.test(a)?400:502).json({error:a})}});S.get("/api/mocks",async(e,t)=>{try{t.json({mocks:await ti({trackId:e.query.track||void 0,group:e.query.group||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/mocks/:id",async(e,t)=>{let n=await En(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var qi={dotfiles:"allow"};S.get("/api/mocks/:id/png",async(e,t)=>{let n=await En(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=jt(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Qa(n,r,"png");if(!o)return t.status(404).json({error:"no image for this version (poster extraction failed)"});t.set("cache-control","private, max-age=31536000, immutable"),t.sendFile(o,qi,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});S.get("/api/mocks/:id/media",async(e,t)=>{let n=await En(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=jt(n,e.query.v??null);if(!r)return t.status(409).json({error:`mock is ${n.state} \u2014 no captured version yet`});let o=Qa(n,r,"video");if(!o)return t.status(404).json({error:"this version has no video (image or html sourced)"});t.set("cache-control","private, max-age=31536000, immutable"),t.sendFile(o,qi,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});S.get("/api/mocks/:id/html",async(e,t)=>{let n=await En(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});let r=jt(n,e.query.v??null);if(!r?.htmlRel)return t.status(404).json({error:"this version has no HTML source (image-sourced)"});t.set("content-security-policy","sandbox"),t.sendFile(At(n,r.v).html,qi,o=>{o&&!t.headersSent&&t.status(404).json({error:"source artifact missing"})})});S.post("/api/mocks",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&$l(n)?n:null,o=n&&!r?n:null,s=n?e.query:e.body||{},{trackId:a,name:i,cwd:c,width:l,group:u}=s;try{let d=a?await ne(a):null;if(!d&&c&&(d=(await oe()).find(h=>h.cwd&&(c===h.cwd||c.startsWith(h.cwd+ge.sep)))||null),!d)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let f=(d.boxes||[]).find(m=>m.name===d.leadBox),p=await Jx({trackId:d.id,name:i,html:n?null:s.html,image:r,video:o,cwd:d.cwd,width:l,group:u,box:d.leadBox,agent:f?.agent||null});t.json({ok:!0,mock:p})}catch(d){let f=d?.message||String(d);t.status(/required|invalid|must be/i.test(f)?400:500).json({error:f})}});S.post("/api/mocks/:id/update",async(e,t)=>{let n=Buffer.isBuffer(e.body)?e.body:null,r=n&&$l(n)?n:null;try{let o=await Yx(e.params.id,{html:n?null:(e.body||{}).html,image:r,video:n&&!r?n:null,width:n?e.query.width:(e.body||{}).width});t.json({ok:!0,mock:o})}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required|invalid|must be/i.test(s)?400:500;t.status(a).json({error:s})}});S.post("/api/mocks/:id/pick",async(e,t)=>{try{let{mock:n,siblings:r}=await Qx(e.params.id),o=r.length?` over ${r.map(s=>`"${s.name}"`).join(", ")}`:"";await It(n.trackId,`Design decision: go with "${n.name}" (mock:${n.id})${o} \u2014 proceed with this design.`,{kind:"mock-picked"}).catch(()=>{}),t.json({ok:!0,mock:n,siblings:r})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/nothing to pick/i.test(r)?409:500;t.status(o).json({error:r})}});S.delete("/api/mocks/:id",async(e,t)=>{try{let n=(await gS(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await eS(e.params.id),...n.length?{danglingFlows:n}:{}})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.get("/api/flows",async(e,t)=>{try{t.json({flows:await fS({trackId:e.query.track||void 0})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/flows/:id",async(e,t)=>{let n=await pS(e.params.id);if(!n)return t.status(404).json({error:"flow not found"});t.json({flow:n})});S.post("/api/flows",async(e,t)=>{let{trackId:n,name:r,steps:o,cwd:s}=e.body||{};try{let a=n?await ne(n):null;if(!a&&s&&(a=(await oe()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ge.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await mS({trackId:a.id,name:r,steps:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,flow:c})}catch(a){let i=a?.message||String(a),c=/already exists/i.test(i)?409:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(i)?400:500;t.status(c).json({error:i})}});S.post("/api/flows/:id/update",async(e,t)=>{try{let{steps:n,note:r}=e.body||{};t.json({ok:!0,flow:await hS(e.params.id,{steps:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/required|invalid|no mock|ambiguous|over \d+ chars|must be|no captured/i.test(r)?400:500;t.status(o).json({error:r})}});S.delete("/api/flows/:id",async(e,t)=>{try{t.json(await wS(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.get("/api/runbooks",async(e,t)=>{try{let n=e.query.track||void 0;if(!n&&e.query.cwd){let s=await oe(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+ge.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await AS({trackId:n,status:e.query.status||void 0});t.json({runbooks:r,...o?{fleetDegraded:!0}:{}})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/runbooks/:id",async(e,t)=>{let n=await zp(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});t.json({runbook:n})});S.get("/api/runbooks/:id/md",async(e,t)=>{let n=await zp(e.params.id);if(!n)return t.status(404).json({error:"runbook not found"});let r=vS(n,e.query.v??null);if(!r)return t.status(404).json({error:"no such version"});if(t.set("content-security-policy","sandbox"),t.set("cache-control","private, max-age=31536000, immutable"),t.type("text/markdown; charset=utf-8"),n.remote){let o=await ES(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile($n(n,r.v),qi,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});S.get("/api/loops",async(e,t)=>{t.json(await bl())});S.post("/api/loops/report",async(e,t)=>{let{outcome:n,note:r,cwd:o}=e.body||{};if(!["ok","acted","attention"].includes(n))return t.status(400).json({error:"outcome must be one of ok | acted | attention"});if(!o)return t.status(400).json({error:"cwd required"});let a=(await oe()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+ge.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await bl();if(i.source!=="plane")return t.status(503).json({error:i.source==="unconfigured"?"this node isn't linked to a plane \u2014 loops live in the plane":`plane unreachable (${i.error||"no detail"})`});let c=i.loops.find(u=>u.trackId===a.id);if(!c)return t.status(404).json({error:`Track "${a.name}" is not a loop's run Track \u2014 nothing to report on`});let l=await pp({loopId:c.id,outcome:n,note:r});if(!l.ok)return t.status(l.status||503).json({error:l.error||"report failed"});t.json({ok:!0,loop:{id:c.id,name:c.name},run:l.run})});S.post("/api/loops/propose",async(e,t)=>{let{trackId:n,cwd:r,plan:o}=e.body||{};try{let s=n?await ne(n):null;if(!s&&r&&(s=(await oe()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+ge.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await dA(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});S.post("/api/tracks/:id/loop-create-offer",async(e,t)=>{let{proposalId:n,accept:r,loopId:o,nextFireAt:s}=e.body||{};try{let a=await ne(e.params.id);if(!a)return t.status(404).json({error:"track not found"});t.json({ok:!0,...await fA(a,n,r===!0,{loopId:o||null,nextFireAt:s??null})})}catch(a){let i=a?.message||String(a);t.status(/unknown proposal/i.test(i)?404:/already answered/i.test(i)?409:400).json({error:i})}});S.post("/api/runbooks",async(e,t)=>{let{trackId:n,name:r,md:o,cwd:s}=e.body||{};try{let a=n?await ne(n):null;if(!a&&s&&(a=(await oe()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+ge.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await _S({trackId:a.id,trackName:a.name,name:r,md:o,cwd:a.cwd,box:a.leadBox,agent:i?.agent||null});t.json({ok:!0,runbook:c})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|over the|secret VALUE/i.test(i)?400:500).json({error:i})}});S.post("/api/runbooks/:id/update",async(e,t)=>{try{let{md:n,note:r}=e.body||{};t.json({ok:!0,runbook:await IS(e.params.id,{md:n,note:r})})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/required|invalid|over the|secret VALUE/i.test(r)?400:500;t.status(o).json({error:r})}});S.post("/api/runbooks/:id/status",async(e,t)=>{try{t.json({ok:!0,runbook:await RS(e.params.id,(e.body||{}).status)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/orphaned/i.test(r)?410:/must be/i.test(r)?400:500;t.status(o).json({error:r})}});S.delete("/api/runbooks/:id",async(e,t)=>{try{t.json(await $S(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.get("/api/changelog/draft",async(e,t)=>{try{let n=gA(e.query.since||null);t.json({since:n,repos:Gh(),...await yA(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var kE=async({trackId:e,cwd:t})=>{let n=e?await ne(e):null;return!n&&t&&(n=(await oe()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+ge.sep)))||null),n};S.post("/api/changelog/publish",async(e,t)=>{let{md:n,day:r,trackId:o,cwd:s}=e.body||{};try{let a=await kE({trackId:o,cwd:s});if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,entry:await bA(a,{md:n,day:r})})}catch(a){let i=a?.message||String(a);t.status(/required|invalid|no `- `|over the|secret VALUE|mentions "/i.test(i)?400:500).json({error:i})}});S.post("/api/changelog/retract",async(e,t)=>{let{day:n,trackId:r,cwd:o}=e.body||{};try{let s=await kE({trackId:r,cwd:o});if(!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,entry:await kA(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var OU=120,cw=400,Du=null;S.post("/api/notify",async(e,t)=>{let{title:n,body:r,urgency:o,cwd:s,trackId:a}=e.body||{};try{let i=typeof r=="string"?r.trim():"";if(!i)return t.status(400).json({error:"body (the message) required"});if(i.length>cw)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${cw} cap; a notification is a nudge, put the detail in chat`});let c=ni(`${n||""}
|
|
603
|
+
${i}`);if(c.length)return t.status(400).json({error:`notification appears to contain a secret VALUE (${c.join(", ")}) \u2014 notifications ship off-box; never put a credential in one`});let l=a?await ne(a):null;if(!l&&s&&(l=(await oe()).find(m=>m.cwd&&(s===m.cwd||s.startsWith(m.cwd+ge.sep)))||null),!l)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let u=(l.boxes||[]).find(p=>p.name===l.leadBox),d=(p,m)=>String(p).replace(/\s+/g," ").trim().slice(0,m),f={title:n?d(n,OU):l.name,body:d(i,cw),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await _(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:f}),t.json({ok:!0,notification:f,delivered:Du?Du.status().connected:null})}catch(i){t.status(500).json({error:i?.message||String(i)})}});S.post("/api/dictation",async(e,t)=>{if(!Buffer.isBuffer(e.body)||e.body.length===0)return t.status(400).json({error:"expected a raw audio body"});try{t.json(await rE(e.body,e.headers["content-type"]))}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});S.get("/api/dictation/glossary",async(e,t)=>{t.json({configured:!!await Ai(),terms:await sw(),problem:await nE()})});S.post("/api/dictation/learn",async(e,t)=>{let{transcript:n,finalText:r}=e.body||{};if(typeof n!="string"||typeof r!="string")return t.status(400).json({error:"transcript and finalText are required"});t.json({learned:await tE(n,r)})});S.get("/api/tracks/:id/voice-chat",async(e,t)=>{if(!await ne(e.params.id))return t.status(404).json({error:"track not found"});t.json(await oE())});S.post("/api/tracks/:id/voice-chat/session",async(e,t)=>{try{let n=await ne(e.params.id);if(!n)return t.status(404).json({error:"track not found"});if(n.archivedAt)return t.status(409).json({error:"track is archived"});t.json(await sE())}catch(n){t.status(n?.status||500).json({error:n?.message||String(n),code:n?.code})}});S.post("/api/tracks/:id/voice-chat/usage",async(e,t)=>{try{let n=await ne(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let{model:r,usage:o,seconds:s}=e.body||{};t.json({ok:!0,...await aE(n,{model:r,usage:o,seconds:s})})}catch(n){t.status(n?.status||500).json({error:n?.message||String(n)})}});S.post("/api/tracks",async(e,t)=>{let{name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i,ticket:c,circuit:l}=e.body||{};try{let u=await ql({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),d=null;if(c)try{await Ns(u,c,{source:"create"})}catch(p){d=p?.message||String(p)}let f=null;if(l)try{await ou(u,await Os(l),{source:"picker"})}catch(p){f=p?.message||String(p)}t.json({ok:!0,track:u,...d?{ticketWarning:d}:{},...f?{circuitWarning:f}:{}})}catch(u){let d=u?.message||String(u);t.status(/required|unknown agent|invalid|must be/i.test(d)?400:500).json({error:d})}});S.get("/api/tracks/:id",async(e,t)=>{let n=await ne(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json({track:n,handoff:xE(Rs(e.params.id)),subtasks:fo(e.params.id).map(Ki),pendingOffers:await vm(n)})});S.delete("/api/tracks/:id",async(e,t)=>{try{let n=await jm(e.params.id,{cascadeMocks:e.query.mocks!=="0",cascadeRunbooks:e.query.runbooks==="1"});await _h(e.params.id).catch(()=>{}),t.json(n)}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});S.post("/api/tracks/:id/archive",async(e,t)=>{try{t.json({ok:!0,track:await uo(e.params.id)})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/already archived/i.test(r)?409:500).json({error:r})}});S.post("/api/tracks/:id/rename",async(e,t)=>{try{t.json({ok:!0,track:await Um(e.params.id,e.body?.name)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:/loop run Tracks/i.test(r)?409:/required|too long/i.test(r)?400:500;t.status(o).json({error:r})}});S.post("/api/tracks/:id/unarchive",async(e,t)=>{try{t.json({ok:!0,track:await Fm(e.params.id)})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/not archived/i.test(r)?409:500).json({error:r})}});S.get("/api/tracks/:id/messages",async(e,t)=>{let n=await Fl(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.json(n)});S.get("/api/tracks/:id/events",async(e,t)=>{let n=await ne(e.params.id);if(!n)return t.status(404).json({error:"track not found"});t.set({"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive","x-accel-buffering":"no"}),t.flushHeaders();let r=i=>t.write(`id: ${i.seq}
|
|
598
604
|
data: ${JSON.stringify(i)}
|
|
599
605
|
|
|
600
|
-
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await L(n))i.seq>o&&r(i);let s=
|
|
606
|
+
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await L(n))i.seq>o&&r(i);let s=Mw(n.id,r),a=setInterval(()=>t.write(`: hb
|
|
601
607
|
|
|
602
|
-
`),25e3);e.on("close",()=>{clearInterval(a),s()})});S.get("/api/tracks/:id/search",async(e,t)=>{let n=await ne(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=String(e.query.q||"").trim();if(!r)return t.status(400).json({error:"q (query) is required"});let o=Math.min(Math.max(Number(e.query.k)||8,1),50),s=
|
|
603
|
-
`);S.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await It(e.params.id,
|
|
608
|
+
`),25e3);e.on("close",()=>{clearInterval(a),s()})});S.get("/api/tracks/:id/search",async(e,t)=>{let n=await ne(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=String(e.query.q||"").trim();if(!r)return t.status(400).json({error:"q (query) is required"});let o=Math.min(Math.max(Number(e.query.k)||8,1),50),s=_a(await L(n)),a=Mc(s,r,{k:o,role:e.query.role||null,tag:e.query.tag||null});t.json({q:r,indexed:s.N,hits:a.map(i=>({score:Number(i.score.toFixed(3)),seq:i.chunk.seq,ts:i.chunk.ts,role:i.chunk.role,box:i.chunk.boxId,provider:i.chunk.provider,tags:i.chunk.tags,text:i.chunk.text}))})});S.get("/api/memory",async(e,t)=>{t.json({stores:await Yw(),harvest:Gw()})});S.delete("/api/memory/:key/:file",async(e,t)=>{let n=String(e.params.key||"");if(!/^[a-z0-9][a-z0-9._-]*$/.test(n))return t.status(400).json({error:"bad store key"});try{t.json({ok:!0,...await Xw(n,String(e.params.file||""))})}catch(r){let o=r?.message||String(r);t.status(/not found/i.test(o)?404:/bad fact/i.test(o)?400:500).json({error:o})}});var CU=["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(`
|
|
609
|
+
`);S.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await It(e.params.id,CU)})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/archived/i.test(r)?409:/not running/i.test(r)?400:500).json({error:r})}});S.post("/api/tracks/:id/lead/restart",async(e,t)=>{try{t.json({ok:!0,track:await Gl(e.params.id)})}catch(n){let r=n?.message||String(n),o=/not found/i.test(r)?404:n?.transferredTo||/mid-turn|terminal|archived/i.test(r)?409:500;t.status(o).json({error:r,...n?.transferredTo?{transferredTo:n.transferredTo}:{}})}});S.post("/api/tracks/:id/say",async(e,t)=>{let{text:n,attachments:r}=e.body||{};try{t.json(await It(e.params.id,n,{attachments:r}))}catch(o){let s=o?.message||String(o);if(o?.usageCap)return t.status(429).json({error:"usage_cap",message:s,cap:o.usageCap});if(o?.transferredTo)return t.status(409).json({error:s,transferredTo:o.transferredTo});t.status(/not found/i.test(s)?404:/archived/i.test(s)?409:/required|not running/i.test(s)?400:500).json({error:s})}});S.post("/api/tracks/:id/upload",async(e,t)=>{try{let n=await ne(e.params.id);if(!n)return t.status(404).json({error:`track "${e.params.id}" not found`});if(n.archivedAt)return t.status(409).json({error:"track is archived"});if(!n.cwd)return t.status(409).json({error:"track has no workspace yet"});let r=Buffer.isBuffer(e.body)?e.body:null,o=await DS(n.cwd,{filename:e.query.filename,buffer:r});t.json({ok:!0,attachment:o})}catch(n){let r=n?.message||String(n);t.status(/too large|unsupported|empty|required/i.test(r)?400:500).json({error:r})}});S.get("/api/tracks/:id/uploads/:file",async(e,t)=>{try{let n=await ne(e.params.id);if(!n)return t.status(404).json({error:`track "${e.params.id}" not found`});if(!n.cwd)return t.status(404).json({error:"track has no workspace"});let r=ge.basename(String(e.params.file||""));if(!r||!Xp(ge.extname(r).slice(1)))return t.status(400).json({error:"not an upload path"});t.set("cache-control","private, max-age=31536000, immutable"),t.set("content-security-policy","sandbox");let o=Yp(n.cwd),s,a;try{s=await Xe.promises.realpath(o),a=await Xe.promises.realpath(ge.join(o,r))}catch{return t.status(404).json({error:"upload not found"})}if(!a.startsWith(s+ge.sep))return t.status(404).json({error:"upload not found"});t.sendFile(r,{root:o},i=>{i&&!t.headersSent&&t.status(404).json({error:"upload not found"})})}catch(n){t.status(500).json({error:n?.message||String(n)})}});var MU=/^[0-9a-fA-F-]{8,64}$/;S.get("/api/tracks/:id/chat-images/:box/:uuid/:idx",async(e,t)=>{try{let n=await ne(e.params.id);if(!n)return t.status(404).json({error:`track "${e.params.id}" not found`});let{box:r,uuid:o}=e.params,s=Number(e.params.idx);if(!MU.test(o)||!Number.isInteger(s)||s<0)return t.status(400).json({error:"bad image ref"});let a=(n.boxes||[]).find(f=>f.name===r);if(!a||a.agent!=="claude"||!a.cwd||!a.sessionId)return t.status(404).json({error:"image source not available"});let i=Nt(a.cwd,a.sessionId);if(!i)return t.status(404).json({error:"transcript not found"});let c=`"${o}"`,l=null,u=xU.createInterface({input:Xe.createReadStream(i,{encoding:"utf8"}),crlfDelay:1/0});for await(let f of u){if(!f.includes(c))continue;let p;try{p=JSON.parse(f)}catch{continue}if(p.uuid===o){l=p,u.close();break}}let d=l?Ed(l)[s]:null;if(!d||!/^image\//.test(d.mediaType))return t.status(404).json({error:"image not found"});t.set("content-type",d.mediaType),t.set("cache-control","private, max-age=31536000, immutable"),t.set("content-security-policy","sandbox"),t.send(Buffer.from(d.data,"base64"))}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/tracks/:id/interrupt",async(e,t)=>{try{t.json(await Wl(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:/archived/i.test(r)?409:500).json({error:r})}});S.delete("/api/tracks/:id/queue",async(e,t)=>{try{let{removed:n,restored:r,draftRev:o}=await Em(e.params.id);t.json({ok:!0,removed:n.filter(s=>s.kind!=="ship-notify"),restored:r,draftRev:o})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.post("/api/tracks/:id/answer-prompt",async(e,t)=>{let{promptId:n,answers:r,text:o}=e.body||{};try{t.json(await Ul(e.params.id,{promptId:n,answers:r,text:o}))}catch(s){let a=s?.message||String(s),i=/not found/i.test(a)?404:/no pending prompt|changed since|not running|not supported|needs a code|look like a sign-in code|no answer selected/i.test(a)?409:500;t.status(i).json({error:a})}});S.post("/api/setup/:provider/start",async(e,t)=>{try{let n=await Dm({provider:e.params.provider,fresh:e.body?.fresh===!0});t.json({ok:!0,track:n})}catch(n){let r=n?.message||String(n);t.status(/unknown setup provider|isn't available/i.test(r)?400:500).json({error:r})}});S.post("/api/tracks/:id/navigate-prompt",async(e,t)=>{let{promptId:n,questionIndex:r}=e.body||{};try{t.json(await Am(e.params.id,{promptId:n,questionIndex:r}))}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/no pending prompt|changed since|not running|not supported|out of range|review screen/i.test(s)?409:500;t.status(a).json({error:s})}});S.put("/api/tracks/:id/draft",async(e,t)=>{let{text:n,rev:r}=e.body||{};try{t.json(await Om(e.params.id,typeof n=="string"?n:"",{rev:typeof r=="number"?r:null}))}catch(o){let s=o?.message||String(o),a=/not found/i.test(s)?404:/required/i.test(s)?400:500;t.status(a).json({error:s})}});S.get("/api/tracks/:id/usage",async(e,t)=>{let n=await ne(e.params.id);if(!n)return t.status(404).json({error:"track not found"});let r=n.boxes?.find(s=>s.name===n.leadBox)||null,o=e.query.fresh==="1"||e.query.fresh==="true";t.json(await xu(r,{fresh:o}))});S.post("/api/tracks/:id/model",async(e,t)=>{let{model:n}=e.body||{};try{t.json(await Cm(e.params.id,n))}catch(r){let o=r?.message||String(r),s=/not found/i.test(o)?404:/required|unknown model|not running/i.test(o)?400:500;t.status(s).json({error:o})}});S.post("/api/tracks/:id/subtask",async(e,t)=>{let{agent:n,task:r,skipPermissions:o,reason:s}=e.body||{};try{let a=await Qm(e.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});t.json({ok:!0,subtask:Ki(a)})}catch(a){let i=a?.message||String(a);if(a?.usageCap)return t.status(429).json({error:"usage_cap",message:i,cap:a.usageCap});let c=/not found/i.test(i)?404:/required|unknown agent|git repo|not running|can't resolve/i.test(i)?400:500;t.status(c).json({error:i})}});S.get("/api/tracks/:id/subtasks",(e,t)=>{t.json({subtasks:fo(e.params.id).map(Ki)})});S.get("/api/tracks/:id/subtask/:sid/messages",async(e,t)=>{let n=await Tm(e.params.id,e.params.sid);if(!n)return t.status(404).json({error:"subtask not found"});t.json(n)});S.post("/api/tracks/:id/subtask/:sid/cancel",(e,t)=>{try{t.json({ok:!0,subtask:Ki(Xl(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});S.post("/api/tracks/:id/subtask/:sid/merge-now",(e,t)=>{try{t.json({ok:!0,subtask:Ki(eh(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});function Ki(e){return e?{id:e.id,agent:e.agent,task:e.task,skipPermissions:e.skipPermissions,step:e.step,running:!e.finishedAt,box:e.box,branch:e.branch,stat:e.stat,files:e.files||null,conflicts:e.conflicts||[],applied:e.applied,error:e.error,canceled:e.canceled,startedAt:e.startedAt,finishedAt:e.finishedAt}:null}S.post("/api/tracks/:id/handoff-lead",async(e,t)=>{let{toAgent:n,skipPermissions:r,reason:o}=e.body||{};try{let s=await zl(e.params.id,n,r!==!1,o);t.json({ok:!0,step:s.step,toAgent:s.toAgent,fromBox:s.fromBox,skipPermissions:s.skipPermissions})}catch(s){let a=s?.message||String(s);if(s?.usageCap)return t.status(429).json({error:"usage_cap",message:a,cap:s.usageCap});t.status(/already running/i.test(a)?409:/not found/i.test(a)?404:400).json({error:a})}});S.post("/api/tracks/:id/spin-up-view",async(e,t)=>{try{t.json(await Rm(e.params.id,e.body||{}))}catch(n){let r=n?.message||String(n);t.status(/already being spun up/i.test(r)?409:/not found/i.test(r)?404:400).json({error:r})}});S.get("/api/tracks/:id/handoff-lead",(e,t)=>{t.json(xE(Rs(e.params.id))??{running:!1})});S.post("/api/tracks/:id/transfer-out",async(e,t)=>{try{let n=await Vm(e.params.id);t.json({ok:!0,step:n.step,startedAt:n.startedAt})}catch(n){let r=n?.message||String(n);t.status(/already running/i.test(r)?409:/not found/i.test(r)?404:400).json({error:r})}});S.get("/api/tracks/:id/transfer-out",(e,t)=>{let n=Gm(e.params.id);if(!n)return t.json({running:!1});t.json({running:!n.finishedAt,step:n.step,error:n.error,startedAt:n.startedAt,finishedAt:n.finishedAt,...n.result??{}})});S.post("/api/tracks/:id/transfer-out/commit",async(e,t)=>{try{t.json(await Jl(e.params.id,e.body||{}))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:400).json({error:r})}});S.post("/api/tracks/adopt",async(e,t)=>{let{trackId:n}=e.body||{};try{t.json(await Ym({...e.body||{},trackId:n}))}catch(r){let o=r?.message||String(r);t.status(/already running|already hosted/i.test(o)?409:/not found/i.test(o)?404:400).json({error:o})}});function xE(e){return e?{running:!e.finishedAt,step:e.step,error:e.error,fromBox:e.fromBox,toBox:e.toBox,toAgent:e.toAgent,skipPermissions:e.skipPermissions,startedAt:e.startedAt,finishedAt:e.finishedAt}:null}S.get("/api/debug/handoff",async(e,t)=>{let n=Math.min(Number(e.query.limit)||80,500);t.json({verbose:mc(),paths:fg,trace:await dg({limit:n})})});var lw=ge.resolve(fE,"..","..","client","dist");Xe.existsSync(lw)&&(S.use($t.static(lw)),S.get(/^(?!\/api\/).*/,(e,t)=>t.sendFile(ge.join(lw,"index.html"),qi)));var SE=vU.createServer(S),NU=new AU({noServer:!0}),vE=(e,t,n)=>{if(!(Di(e)||qs.allowed(qs.clientIp(e))||dw.allows(e))){t.write(`HTTP/1.1 403 Forbidden\r
|
|
604
610
|
\r
|
|
605
|
-
`),t.destroy();return}if(
|
|
611
|
+
`),t.destroy();return}if(oA(e,t,n))return;if(!nA(e,mE,{enforce:hE})){t.write(`HTTP/1.1 401 Unauthorized\r
|
|
606
612
|
\r
|
|
607
|
-
`),t.destroy();return}let r=
|
|
613
|
+
`),t.destroy();return}let r=_g(e.url);if(!r.ok){t.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
608
614
|
\r
|
|
609
615
|
`),t.destroy();return}let o=r.sessionName;if(!_e(o)){t.write(`HTTP/1.1 400 Bad Request\r
|
|
610
616
|
\r
|
|
611
|
-
`),t.destroy();return}let{cols:s,rows:a}=r;
|
|
617
|
+
`),t.destroy();return}let{cols:s,rows:a}=r;NU.handleUpgrade(e,t,n,i=>Tg(i,o,{cols:s,rows:a}))};SE.on("upgrade",vE);var TE=process.env.BOSTRAT_CERT_DIR||ge.join(process.env.HOME||uw.tmpdir(),".bostrat","certs"),lE=Number(process.env.BOSTRAT_TLS_PORT)||3443,uE=ge.join(TE,"bostrat.globopi.key"),dE=ge.join(TE,"bostrat.globopi.crt");if(Xe.existsSync(uE)&&Xe.existsSync(dE)){let e=TU.createServer({key:Xe.readFileSync(uE),cert:Xe.readFileSync(dE)},S);e.on("upgrade",vE),e.on("error",t=>{console.error(`[bostrat-agent] https listener failed (${t?.code||t?.message}) \u2014 continuing HTTP-only`)}),e.listen(lE,Nu,()=>{console.log(`[bostrat-agent] https listening on ${Nu||"0.0.0.0"}:${lE}`)})}SE.listen(iE,Nu,()=>{console.log(`[bostrat-agent] listening on ${Nu||"0.0.0.0"}:${iE}`);let e=Tv(process.env);e&&console.warn(`[bostrat-agent] \u26A0 SECURITY: plane-linked node bound to ${e.host?e.host:"ALL interfaces (BOSTRAT_HOST=all)"} \u2014 the API + Views are reachable off-loopback (LAN/VPN), where the IP allowlist is spoofable and Views need no token. The plane tunnel does not require this; set BOSTRAT_HOST=127.0.0.1 once any local reverse-proxy/WireGuard path is retired (F-2).`),console.log(`[bostrat-agent] tmux socket: ${ft}`),console.log(`[bostrat-agent] auto-trigger default: ${pE}%`),console.log(`[bostrat-agent] ip allowlist: ${qs.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(dw.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),Km().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),Xm().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Hm().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)),Pm().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)),HU(),BU(),jU(),UU(),Sk({listTracks:oe,getTrack:ne}).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)),US()&&HS({listTracks:oe}),_A(),CA(),hT({resolveTrack:ne}),AT({listTracks:oe,createTrack:ql,archiveTrack:uo,listRepos:un,resolveTrack:ne,sayToLead:It,answerPrompt:Ul,startLeadHandoff:zl,interruptLead:Wl,listSubtaskJobs:fo,cancelSubtask:Xl,readEvents:L,appendEvent:_,divertShip:Zh,answerPrExists:Qh,knownAgents:hi}),PU(),da().then(t=>{t||console.warn(`[boot] no host tmux server on ${ft} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),GA(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(Du=Rv({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||uw.hostname(),name:process.env.BOSTRAT_NODE_NAME||uw.hostname(),listTracks:oe,detectAgents:Ll,sampleMetrics:IT,onTracksTransferred:Jm,onUpdateAvailable:async t=>{try{Mu={latest:t.latest,current:t.current},await an({...t,policy:ot()}),ot()==="auto"&&!Ti(await $s())&&Ks.arm({to:t.latest,from:t.current,kind:Rt})}catch(n){console.error(`[plane-link] update notice failed: ${n?.message||n}`)}},onUpdatePolicy:t=>IU(t).catch(()=>{}),onHelloRefused:async({current:t,min:n,latest:r,installKind:o,fix:s})=>{try{let a=r||n;if(await an({current:t,latest:a,min:n,installKind:o,required:!0,policy:ot(),status:"available",reason:"refused"}),ot()!=="off"&&a){let i=await ru({to:a,kind:Rt,reason:"hard-floor"});i.ok?(Ks.cancel(),await an({current:t,latest:a,min:n,installKind:o,required:!0,policy:ot(),status:"updating",reason:"hard-floor"})):console.error(`[self-update] hard-floor update not started: ${i.reason} \u2014 ${s}`)}}catch(a){console.error(`[plane-link] hard-floor handling failed: ${a?.message||a}`)}},updatePolicy:ot}),console.log(`[bostrat-agent] plane link \u2192 ${process.env.BOSTRAT_PLANE_URL}`),Io().catch(()=>{}),MS().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),wT().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),rS().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),kS().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),l0().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{Du?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function PU(){DU(),Al().catch(()=>{}),setInterval(()=>Al().catch(()=>{}),1e4)}function DU(){for(let e of["bostrat-view","bostrat-mock","bostrat-flow","bostrat-ticket","bostrat-circuit","bostrat-blueprint","bostrat-decision","bostrat-runbook","bostrat-loop","bostrat-notify","bostrat-gmail","bostrat-changelog","gh"])try{let t=ge.resolve(fE,"..","bin",e),n=ge.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!Xe.existsSync(t))continue;Xe.mkdirSync(n,{recursive:!0});let r=ge.join(n,e);(Xe.existsSync(r)?Xe.readlinkSync(r):null)!==t&&(Xe.rmSync(r,{force:!0}),Xe.symlinkSync(t,r)),console.log(`[bostrat-agent] ${e} CLI linked at ${r}`)}catch(t){console.error(`[bostrat-agent] ${e} CLI link failed:`,t?.message||t)}}function BU(){let e=!1,t=async()=>{if(!e){e=!0;try{let n=await oe(),r=await UT({tracks:n});r.committed&&console.log(`[state-sync] committed (${r.staged} file(s) staged${r.pushed?", pushed":""})`)}catch{}finally{e=!1}}};setInterval(t,6e4),t()}function jU(){let e=null,t=async()=>{try{let n=Wo(),r=0,o=[];try{let c=await Lh(await oe());r=c.fleet.unpricedTurns||0,o=c.fleet.unpricedModels||[]}catch{}let s=[...n.problems],a=o.filter(c=>c!=="unknown");a.length&&s.push(`${r} unpriced turn(s) \u2014 add rates for: ${a.join(", ")}`);let i=s.join(`
|
|
618
|
+
`);if(!s.length){e!==""&&console.log(`[pricing] table ok \u2014 rates verified ${n.tableVerifiedAt??"never"}`),e="";return}for(let c of s)console.warn(`[pricing] \u26A0 ${c}`);i!==e&&process.env.BOSTRAT_NTFY_URL&&fetch(process.env.BOSTRAT_NTFY_URL,{method:"POST",headers:{Title:"bostrat: cost instrument needs attention",Tags:"warning"},body:s.join(`
|
|
619
|
+
`).slice(0,900)}).catch(()=>{}),e=i}catch(n){console.warn(`[pricing] watch failed: ${n?.message||n}`)}};setInterval(t,1440*60*1e3),setTimeout(t,3e4)}var LU=Number(process.env.BOSTRAT_MEMORY_RECONCILE_MS)||5*6e4,FU=Number(process.env.BOSTRAT_MEMORY_RECONCILE_FIRST_MS)||5*6e4;function UU(){let e=async()=>{try{let n=await Jw();n.ran&&console.log(`[memory] one-time full backfill sweep: ${n.harvested} fact(s) harvested`);let o=(await oe()).filter(a=>!a.archivedAt&&!a.transferredTo&&a.kind!=="setup").flatMap(a=>[a.cwd,...(a.boxes||[]).map(i=>i.cwd)]).filter(Boolean),s=await zw([...new Set(o)]);s.harvested&&console.log(`[memory] reconciler harvested ${s.harvested} fact(s) from ${o.length} live workspace(s)`)}catch{}};setTimeout(()=>{e(),setInterval(e,LU).unref()},FU).unref()}function HU(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{let t=await oe();for(let n of t)n.archivedAt||n.transferredTo||!n.boxes?.some(r=>r.state==="live")||(await Fl(n.id).catch(()=>{}),await Px(n).catch(()=>{}))}catch{}finally{e=!1}}},4e3)}
|