bostrat-node 1.7.11 → 1.7.12
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-loop +3 -3
- package/dist/cli.js +5 -5
- package/dist/index.js +191 -176
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,96 +1,96 @@
|
|
|
1
|
-
var
|
|
1
|
+
var zE=Object.defineProperty;var M=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var Wu=(e,t)=>{for(var n in t)zE(e,n,{get:t[n],enumerable:!0})};import{createHash as JE,randomBytes as YE,timingSafeEqual as XE}from"node:crypto";function xw(){return YE(32).toString("hex")}function Vs(e){let t=typeof e=="string"?e.trim():"";return t?JE("sha256").update(t).digest("hex"):null}function Sw(e,t){return typeof e!="string"||typeof t!="string"||e.length!==t.length||!e?!1:XE(Buffer.from(e),Buffer.from(t))}function zs(e,t={},{now:n=Date.now(),log:r=console.warn}={}){if(qu.set(e,(qu.get(e)||0)+1),Ku.has(e)&&n-Ku.get(e)<ZE)return!1;Ku.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=${qu.get(e)}${o?` ${o}`:""}`),!0}async function vw(e,{byToken:t,legacy:n,env:r=process.env}={}){let o=typeof e=="string"?e.trim():"";if(!o)return r.BOSTRAT_HOOK_LEGACY_DENY==="1"?{mode:"legacy-denied",target:null}:{mode:"legacy",target:await n()};let s=await t(o);return s?.track?{mode:"token",target:s}:{mode:"refused",target:null}}var Xi,Zi,qu,Ku,ZE,Js=M(()=>{Xi="BOSTRAT_HOOK_TOKEN",Zi="x-bostrat-hook";qu=new Map,Ku=new Map,ZE=6e4});import dt from"node:fs/promises";import Tw from"node:os";import st from"node:path";import{execFile as QE}from"node:child_process";import{promisify as e_}from"node:util";function Qi(){return/^(1|true|yes|on)$/i.test(process.env.BOSTRAT_CLAUDE_USE_API_KEY||"")&&process.env.ANTHROPIC_API_KEY?.trim()||null}function t_(){return process.env.CLAUDE_CONFIG_DIR?st.join(process.env.CLAUDE_CONFIG_DIR,".claude.json"):st.join(Tw.homedir(),".claude.json")}function n_(){return process.env.CLAUDE_CONFIG_DIR?st.join(process.env.CLAUDE_CONFIG_DIR,"settings.json"):st.join(Tw.homedir(),".claude","settings.json")}async function Wn(e){if(!e||typeof e!="string")return;let t=st.resolve(e),n=t_(),r=n_(),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 "${Zi}: $${Xi}" --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 r_(t)}async function r_(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 o_(t,["/.claude/settings.local.json"])}catch{}}async function o_(e,t){try{let{stdout:n}=await e_(QE)("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
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 Ys=M(()=>{Js()});import Gu from"node:fs/promises";import s_ from"node:os";import ec from"node:path";function a_(){let e=process.env.CODEX_HOME||ec.join(s_.homedir(),".codex");return ec.join(e,"config.toml")}function Aw(e){return`[projects."${e}"]`}function i_(e,t){let n=Aw(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 To(e){if(!e||typeof e!="string")return;let t=ec.resolve(e),n=a_(),r="";try{r=await Gu.readFile(n,"utf8")}catch{}if(i_(r,t))return;let o=Aw(t);if(r.includes(o)){let a=r.indexOf(o)+o.length,c=r.slice(a).search(/^\s*\[/m),l=c===-1?r.length:a+c,u=r.slice(a,l),d=/trust_level\s*=/.test(u)?u.replace(/trust_level\s*=\s*"[^"]*"/,'trust_level = "trusted"'):`${u.replace(/\s*$/,"")}
|
|
7
7
|
trust_level = "trusted"
|
|
8
8
|
`;r=r.slice(0,a)+d+r.slice(l)}else{let s=`
|
|
9
9
|
${o}
|
|
10
10
|
trust_level = "trusted"
|
|
11
11
|
`;r=r.trimEnd()+s+(r.length&&!r.endsWith(`
|
|
12
12
|
`)?`
|
|
13
|
-
`:"")}try{await
|
|
14
|
-
`,"utf8")}var
|
|
15
|
-
`),at.emit("event",i),at.emit(`track:${n}`,i),i}async function
|
|
16
|
-
`))if(a.trim())try{s.push(JSON.parse(a))}catch{}return s}function
|
|
13
|
+
`:"")}try{await Gu.mkdir(ec.dirname(n),{recursive:!0}),await Gu.writeFile(n,r)}catch(s){console.warn(`[codex-config] could not seed folder trust for ${t}: ${s?.message||s}`)}}var tc=M(()=>{});import Ew from"node:fs/promises";import c_ from"node:os";import Xs from"node:path";function l_(e){let n=Xs.resolve(e).slice(1).replace(/\//g,"-").replace(/\./g,""),r=process.env.CURSOR_CONFIG_DIR||Xs.join(c_.homedir(),".cursor");return Xs.join(r,"projects",n)}async function Ao(e){if(!e||typeof e!="string")return;let t=Xs.resolve(e),n=l_(t),r=Xs.join(n,".workspace-trusted");await Ew.mkdir(n,{recursive:!0}),await Ew.writeFile(r,`${JSON.stringify({trustedAt:new Date().toISOString(),workspacePath:t},null,2)}
|
|
14
|
+
`,"utf8")}var nc=M(()=>{});import zu from"node:fs/promises";import qn from"node:path";import{execFile as u_}from"node:child_process";import{promisify as d_}from"node:util";async function g_(e){try{return(await zu.stat(qn.join(e,".git"))).isDirectory()?"repo":"worktree"}catch{return null}}async function y_(e){let t=i=>zu.readFile(qn.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 fn(){let e=new Map;async function t(o,s){if(e.has(o))return;let a=await g_(o);if(a==="worktree")return;if(a==="repo"){e.set(o,{path:o,name:qn.basename(o),branch:await y_(o)});return}if(s>=h_)return;let i;try{i=await zu.readdir(o,{withFileTypes:!0})}catch{return}for(let c of i)!c.isDirectory()||c.name.startsWith(".")||w_.has(c.name)||await t(qn.join(o,c.name),s+1)}for(let o of m_)await t(qn.resolve(o),0);let n=new Set,r=[];for(let o of e.values()){let s=(await fe(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 b_(e){let t=String(e||"").trim().match(/github\.com[:/]+([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);return t?`${t[1]}/${t[2]}`:null}async function fe(e){if(!e)return null;if(Vu.has(e))return Vu.get(e);let t=null;try{let{stdout:n}=await f_("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});t=b_(n)}catch{t=null}return Vu.set(e,t),t}async function Eo(e){let t=String(e||"").toLowerCase();if(!t)return null;for(let n of await fn())if((await fe(n.path))?.toLowerCase()===t)return n.path;return null}var f_,_w,p_,m_,h_,w_,Vu,Kn=M(()=>{f_=d_(u_),_w=process.env.HOME||"/home/globo",p_=qn.join(process.env.BOSTRAT_STATE_DIR||qn.join(_w,".bostrat"),"repos"),m_=[...(process.env.BOSTRAT_REPOS_ROOT||qn.join(_w,"Documents")).split(","),p_].map(e=>e.trim()).filter(Boolean),h_=Number(process.env.BOSTRAT_REPOS_DEPTH)||3,w_=new Set(["node_modules",".git",".cache","dist","build",".next","vendor","__pycache__","venv",".venv","target"]);Vu=new Map});import k_ from"node:fs";import Iw from"node:fs/promises";import Zs from"node:path";import x_ from"node:os";import{fileURLToPath as S_}from"node:url";import{execFile as v_}from"node:child_process";import{promisify as T_}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 rc(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function oc(e,{env:t=process.env}={}){let n=String(e||"").toLowerCase();if(!$w.test(n))throw _o("repo must be owner/name",400);let r=Ju.get(n);if(r&&r.expiresAt-Date.now()>__)return r;if(!rc(t))throw _o("node is not plane-linked \u2014 GitHub auth unavailable",503);let o;try{o=await Ow(`${ke(t)}/v1/github/token?repo=${encodeURIComponent(e)}`,{headers:Cw(t)})}catch(i){if(r&&r.expiresAt>Date.now())return r;throw _o(`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 _o(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 Ju.set(n,a),a}async function Qs({env:e=process.env,force:t=!1}={}){if(!rc(e))return{configured:!1,repos:[],username:null};if(!t&&Nr&&Date.now()-Nr.at<I_)return Nr.data;try{let n=await Ow(`${ke(e)}/v1/github/repos`,{headers:Cw(e)});if(!n.ok)throw new Error(`plane replied ${n.status}`);let r=await n.json();return Nr={at:Date.now(),data:r},r}catch(n){return Nr?Nr.data:{configured:!0,repos:[],username:null,error:n?.message||String(n)}}}function Mw(){Ju.clear(),Nr=null}async function Kt({cwd:e=null,slug:t=null,env:n=process.env}={}){try{let r=t||(e?await fe(e):null);if(!r)return{};let{token:o}=await oc(r,{env:n});return o?{GH_TOKEN:o}:{}}catch{return{}}}async function Yu({env:e=process.env}={}){return(await Qs({env:e})).username??null}function C_(e){let[t,n]=String(e).split("/");return Zs.join(E_,t,n)}async function Xu(e,{env:t=process.env}={}){let n=(...r)=>Rw("git",["-C",e,...r],{timeout:1e4});await n("config","--replace-all","credential.helper",""),await n("config","--add","credential.helper",`!"${$_()}" ${O_(t)}`),await n("config","credential.useHttpPath","true")}async function sc(e,{env:t=process.env,baseUrl:n="https://github.com"}={}){if(!$w.test(String(e||"")))throw _o(`bad repo slug "${e}"`,400);let r=C_(e);if(k_.existsSync(Zs.join(r,".git")))return r;let{token:o}=await oc(e,{env:t});await Iw.mkdir(Zs.dirname(r),{recursive:!0});let s="!f() { echo username=x-access-token; echo password=$BOSTRAT_GIT_TOKEN; }; f";try{await Rw("git",["-c","credential.helper=","-c",`credential.helper=${s}`,"clone","--no-tags","-q",`${n}/${e}.git`,r],{timeout:R_,env:{...t,BOSTRAT_GIT_TOKEN:o,GIT_TERMINAL_PROMPT:"0"}})}catch(a){throw await Iw.rm(r,{recursive:!0,force:!0}).catch(()=>{}),_o(`clone of ${e} failed: ${a?.stderr?.trim?.()||a?.message||a}`,502)}return await Xu(r,{env:t}),r}var Rw,A_,E_,$w,__,I_,R_,Ow,Ju,Nr,Cw,_o,$_,O_,et=M(()=>{Kn();Rw=T_(v_),A_=process.env.BOSTRAT_STATE_DIR||Zs.join(process.env.HOME||x_.tmpdir(),".bostrat"),E_=Zs.join(A_,"repos"),$w=/^[^/\s]+\/[^/\s]+$/,__=5*6e4,I_=6e4,R_=10*6e4,Ow=fetch,Ju=new Map,Nr=null;Cw=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),_o=(e,t)=>Object.assign(new Error(e),{status:t});$_=()=>S_(new URL("../bin/bostrat-git-credential",import.meta.url)),O_=(e=process.env)=>`http://127.0.0.1:${Number(e.PORT)||3362}`});function Pw(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function Bw(e,t,n,r=Nw){let o=ke(n),s=new AbortController,a=setTimeout(()=>s.abort(),r);try{return await N_(`${o}${e}`,{...t,signal:s.signal})}finally{clearTimeout(a)}}async function jw(e,{env:t=process.env,force:n=!1,timeoutMs:r=Nw}={}){if(!Pw(t))return{facts:[],source:"unconfigured"};let o=Zu.get(e);if(!n&&o&&Date.now()-o.at<M_)return{facts:o.facts,source:"cache"};try{let s=await Bw(`/v1/memory/${encodeURIComponent(e)}`,{headers:Dw(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 Zu.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 ac(e,t,{env:n=process.env}={}){if(!Pw(n))return{pushed:0,source:"unconfigured"};if(!t?.length)return{pushed:0,source:"empty"};try{let r=await Bw(`/v1/memory/${encodeURIComponent(e)}`,{method:"POST",headers:{...Dw(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 Zu.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 M_,Nw,Zu,N_,Dw,Lw=M(()=>{et();M_=6e4,Nw=5e3,Zu=new Map,N_=(...e)=>fetch(...e);Dw=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`})});import Qu from"node:fs/promises";import Io from"node:path";import P_ from"node:os";import{EventEmitter as D_}from"node:events";import{randomUUID as B_}from"node:crypto";function Ro(e){return e?.cwd?Io.join(e.cwd,".bostrat","events"):Io.join(ed,"events")}async function U_(e,t){if(!ic.has(t)){let o=(await j(e,t)).reduce((s,a)=>a.seq>s?a.seq:s,0);ic.set(t,o)}let n=ic.get(t)+1;return ic.set(t,n),n}function H_(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 A(e,t){let n=typeof e=="string"?e:e?.id;if(!n)throw new Error("appendEvent: a track (or trackId) is required");if(!t||!j_.has(t.type))throw new Error(`appendEvent: unknown event type "${t?.type}"`);if(t.actor&&!L_.has(t.actor))throw new Error(`appendEvent: bad actor "${t.actor}"`);if(t.source&&!F_.has(t.source))throw new Error(`appendEvent: bad source "${t.source}"`);let r=typeof e=="string"?Io.join(ed,"events"):Ro(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=H_(c,Number(t.payload?.ts??t.ts)||Date.now())}let s=await U_(r,n),a=t.userId??process.env.BOSTRAT_OWNER??null,i={id:t.id||B_(),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 Qu.mkdir(r,{recursive:!0}),await Qu.appendFile(Fw(r,n),JSON.stringify(i)+`
|
|
15
|
+
`),at.emit("event",i),at.emit(`track:${n}`,i),i}async function j(e,t){let n,r;typeof e=="string"&&t?(n=e,r=t):(r=typeof e=="string"?e:e?.id,n=typeof e=="string"?Io.join(ed,"events"):Ro(e));let o;try{o=await Qu.readFile(Fw(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 Uw(e,t){let n=`track:${e}`;return at.on(n,t),()=>at.off(n,t)}var j_,L_,F_,at,ed,Fw,ic,ce=M(()=>{j_=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","loop.update.offered","loop.update.answered","loop.updated","loop.delete.offered","loop.delete.answered","loop.deleted","loop.paused","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"]),L_=new Set(["user","agent","orchestrator","system","peer"]),F_=new Set(["hook","otel","statusline","transcript","pane","orchestrator","vendor"]),at=new D_;at.setMaxListeners(0);ed=process.env.BOSTRAT_STATE_DIR||Io.join(process.env.HOME||P_.tmpdir(),".bostrat");Fw=(e,t)=>Io.join(e,`${t}.ndjson`),ic=new Map});import W from"node:fs/promises";import W_ from"node:os";import ae from"node:path";import{execFile as q_}from"node:child_process";import{promisify as K_}from"node:util";function lc(){return ae.join(uc,"memory","repos")}function Pr(e){return e===pn?ae.join(uc,"memory",pn):ae.join(lc(),e)}async function ta(e){try{return JSON.parse(await W.readFile(ae.join(e,zw),"utf8"))}catch{return{}}}async function nd(e,t){let n=await ta(e);for(let[r,o]of t)n[r]=Math.max(Number(n[r])||0,o);await W.mkdir(e,{recursive:!0}),await W.writeFile(ae.join(e,zw),JSON.stringify(n))}function Ww(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 qw(e){try{let{stdout:t}=await G_("git",["-C",e,"config","--get","remote.origin.url"]);return t.trim()||null}catch{return null}}async function rd(e){if(!e)return[];let t=Ww(await qw(e));if(t)return[t];let n=[],r=[];try{r=await W.readdir(e,{withFileTypes:!0})}catch{return[]}for(let o of r){if(!o.isDirectory()||o.name.startsWith("."))continue;let s=Ww(await qw(ae.join(e,o.name)));s&&!n.includes(s)&&n.push(s)}return n}function dc(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 Gn(e,t){let n=new RegExp(`^\\s*${t}:\\s*(.+?)\\s*$`,"m").exec(e||"");return n?n[1].replace(/^"|"$/g,""):null}async function Gt(e,t=null){let n=t??await W.readFile(e,"utf8").catch(()=>""),{fm:r}=dc(n),o=Gn(r,"modified"),s=o?Date.parse(o):NaN;if(Number.isFinite(s))return s;try{return(await W.stat(e)).mtimeMs}catch{return 0}}function Jw(e,t){let n=Gn(e,"scope"),r=/^repo:([a-z0-9][a-z0-9._-]*)$/i.exec(n||"");if(r)return r[1].toLowerCase();if((n||"").toLowerCase()===pn){let o=Gn(e,"trust");if(!o||/^operator/i.test(o))return pn}return t}function od(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=Oo(c);l&&c.trim().startsWith("-")&&s.set(l,c)}let a=new Set,i=o.map(c=>{let l=Oo(c);return l?(a.add(l),n.has(l)&&s.has(l)?s.get(l):c):c});for(let[c,l]of s)a.has(c)||!n.has(c)||(a.add(c),i.push(l));for(let c of n){if(a.has(c))continue;let l=r.get(c);l&&i.push(l)}return i.join(`
|
|
19
19
|
`).replace(/\n{3,}/g,`
|
|
20
20
|
|
|
21
|
-
`)}function
|
|
21
|
+
`)}function Yw(e,t){let n=Gn(t,"name")||e.replace(/\.md$/,""),r=Gn(t,"description")||"";return`- [${n}](${e})${r?` \u2014 ${r}`:""}`}async function na(e){let t=[];try{t=await W.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 Xw(e,t){let n=ae.join(e,".bostrat","memory"),r=await na(n);if(!r.length)return{harvested:0,changedByKey:new Map};let o=Kw.get(e)??0,s=Date.now(),a=new Map,i=new Map,c=0,l=new Map;for(let d of r){let f=ae.join(n,d);if(o)try{if((await W.stat(f)).mtimeMs<o-z_)continue}catch{continue}let p;try{p=await W.readFile(f,"utf8")}catch{continue}let{fm:m}=dc(p),w=Jw(m,t);if(!w)continue;let y=Pr(w),x=ae.join(y,d),k=await Gt(f,p);if(l.has(y)||l.set(y,await ta(y)),(l.get(y)[d]||0)>=k)continue;let T=await Gt(x).catch(()=>0);if(await W.access(x).then(()=>!0).catch(()=>!1)&&T>=k)continue;await W.mkdir(y,{recursive:!0}),await W.writeFile(x,p),c++;let P=a.get(y);P||a.set(y,P={won:new Set,generated:new Map}),P.won.add(d),P.generated.set(d,Yw(d,m)),i.has(w)||i.set(w,new Set),i.get(w).add(d)}let u=await W.readFile(ae.join(n,"MEMORY.md"),"utf8").catch(()=>"");for(let[d,f]of a){let p=ae.join(d,"MEMORY.md"),m=await W.readFile(p,"utf8").catch(()=>"");await W.writeFile(p,od(m,u,f.won,f.generated))}return Kw.set(e,s),{harvested:c,changedByKey:i}}async function Zw(e){let t=0,n=new Map;for(let r of e||[]){if(!r||!await W.access(ae.join(r,".bostrat","memory")).then(()=>!0).catch(()=>!1))continue;let[s]=await rd(r),a=await Xw(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 J_(){let e=ae.join(uc,"workspaces"),t=[];try{t=await W.readdir(e,{withFileTypes:!0})}catch{return{harvested:0,changedByKey:new Map}}return Zw(t.filter(n=>n.isDirectory()).map(n=>ae.join(e,n.name)))}async function Z_(e,t){let n=Pr(e),r=await ta(n),o=new Set,s=new Map,a=[],i=0;for(let c of t||[]){let l=String(c?.file||"");if(!Y_.test(l)||l==="MEMORY.md")continue;let u=ae.join(n,l),d=await W.access(u).then(()=>!0).catch(()=>!1),f=d?await Gt(u):0,p=Number(c.modified)||0;if(c.deleted){p>f&&(d&&await W.rm(u,{force:!0}),await nd(n,[[l,p]]),d&&(a.push(l),i++));continue}d&&f>=p||(r[l]||0)>=p||(await W.mkdir(n,{recursive:!0}),await W.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=ae.join(n,"MEMORY.md"),l=await W.readFile(c,"utf8").catch(()=>""),u=od(l,[...s.values()].join(`
|
|
22
22
|
`),o);if(a.length){let d=new Set(a);u=u.split(`
|
|
23
|
-
`).filter(f=>!d.has(
|
|
24
|
-
`)}await
|
|
25
|
-
`);t.length>
|
|
26
|
-
`);for(;Buffer.byteLength(n,"utf8")>
|
|
27
|
-
`);return n}async function
|
|
23
|
+
`).filter(f=>!d.has(Oo(f))).join(`
|
|
24
|
+
`)}await W.mkdir(n,{recursive:!0}),await W.writeFile(c,u)}return{changed:i,tombstoned:a.length}}async function fc(e,t=null){let n=Pr(e),r=t?.length?t:await na(n);if(!r.length)return{pushed:0,source:"empty"};let o=X_(await W.readFile(ae.join(n,"MEMORY.md"),"utf8").catch(()=>"")),s=[];for(let a of r){let i=ae.join(n,a),c;try{c=await W.readFile(i,"utf8")}catch{continue}s.push({file:a,content:c,modified:await Gt(i,c),indexLine:o.get(a)??null})}return ac(e,s)}async function Q_(e,{timeoutMs:t}={}){let{facts:n,source:r}=await jw(e,t?{timeoutMs:t}:{});n.length?await Z_(e,n):r==="plane"&&await fc(e)}function eI(e){let t=e.split(`
|
|
25
|
+
`);t.length>Hw&&(t=t.slice(0,Hw));let n=t.join(`
|
|
26
|
+
`);for(;Buffer.byteLength(n,"utf8")>V_&&t.length;)t.pop(),n=t.join(`
|
|
27
|
+
`);return n}async function nI(e,t){if(!e||!t?.length)return{materialized:!1,reason:"no-repo-key"};let n=ae.join(e,".bostrat","memory"),r=0,o=0,s=[];for(let u of t){let d=Pr(u),f=await ta(d);for(let[y,x]of Object.entries(f)){let k=ae.join(n,y);await W.access(k).then(()=>!0).catch(()=>!1)&&await Gt(k)<=x&&(await W.rm(k,{force:!0}),o++)}let p=await na(d);if(!p.length)continue;await W.mkdir(n,{recursive:!0});for(let y of p){let x=ae.join(d,y),k=ae.join(n,y),T=await Gt(x),_=await Gt(k).catch(()=>0);await W.access(k).then(()=>!0).catch(()=>!1)&&_>=T||(await W.copyFile(x,k),r++)}let m=await W.readFile(ae.join(d,"MEMORY.md"),"utf8").catch(()=>""),w=u===pn?"## operator memory":`## repo: ${u}`;m.trim()&&s.push(t.length>1?`${w}
|
|
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
|
|
35
|
-
`).filter(l=>
|
|
36
|
-
`)).catch(()=>{});let c=await
|
|
37
|
-
`)){let r=
|
|
32
|
+
`,c=eI(i);await W.mkdir(n,{recursive:!0}),await W.writeFile(ae.join(n,"MEMORY.md"),c);let l=ae.join(n,"claude");return await W.mkdir(l,{recursive:!0}),await W.writeFile(ae.join(l,"MEMORY.md"),tI,{flag:"wx"}).catch(()=>{}),{materialized:!0,facts:r,capped:c!==i}}async function Qw(e){try{let t=await rd(e);if(!t.length)return{materialized:!1,reason:"no-repo-key"};let n=[...t,pn];return await Promise.all(n.map(r=>Q_(r,{timeoutMs:rI}))),await nI(e,n)}catch(t){return{materialized:!1,reason:"error",error:t?.message||String(t)}}}function eg(){return{...Fe,pending:$o.size,queued:td.size}}function tg(e){if(cc.has(e))return cc.get(e);td.add(e);let t=Gw.then(async()=>{td.delete(e),cc.delete(e);let n=await ad(e);return Fe.runs++,Fe.lastRunAt=Date.now(),n.error?(Fe.errors++,Fe.lastError=n.error):Fe.harvested+=n.harvested||0,n}).catch(n=>({harvested:0,error:n?.message||String(n)}));return cc.set(e,t),Gw=t.then(()=>{}),t}function pc(e,{delayMs:t=oI}={}){if(!e||typeof e!="string")return;Fe.scheduled++;let n=$o.get(e);n&&clearTimeout(n);let r=setTimeout(()=>{$o.delete(e),tg(e)},t);r.unref?.(),$o.set(e,r)}async function sd(e,{timeoutMs:t=3e3}={}){if(!e)return{harvested:0,skipped:"no-cwd"};if(!await W.access(ae.join(e,".bostrat","memory")).then(()=>!0,()=>!1))return{harvested:0,skipped:"no-memory-dir"};let r=$o.get(e);r&&(clearTimeout(r),$o.delete(e));let o,s=new Promise(a=>{o=setTimeout(()=>a({harvested:0,timedOut:!0}),t),o.unref?.()});try{return await Promise.race([tg(e),s])}finally{clearTimeout(o)}}async function ng(e){return ea||(ea=(async()=>{try{Vw||(Vw=!0,await aI());let{harvested:t,changedByKey:n}=await Zw(e);for(let[r,o]of n)await fc(r,[...o]);return Fe.reconciles++,Fe.lastReconcileAt=Date.now(),Fe.harvested+=t,{harvested:t,keys:[...n.keys()]}}catch(t){return Fe.errors++,Fe.lastError=t?.message||String(t),{harvested:0,error:Fe.lastError}}finally{ea=null}})(),ea)}function sI(){return ae.join(uc,"memory",".full-sweep-done")}async function rg(){let e=sI();try{let t=JSON.parse(await W.readFile(e,"utf8"));return Fe.backfill={at:Number(t?.at)||0,harvested:Number(t?.harvested)||0,prior:!0},{ran:!1,...Fe.backfill}}catch{}try{let{harvested:t,changedByKey:n}=await J_();for(let[o,s]of n)await fc(o,[...s]);let r={at:Date.now(),harvested:t};return await W.mkdir(ae.dirname(e),{recursive:!0}),await W.writeFile(e,JSON.stringify(r)),Fe.backfill=r,{ran:!0,...r}}catch(t){return Fe.errors++,Fe.lastError=t?.message||String(t),{ran:!1,error:Fe.lastError}}}async function aI(){let e=0,t=[];try{t=await W.readdir(lc(),{withFileTypes:!0})}catch{return{moved:e}}for(let n of t){if(!n.isDirectory())continue;let r=ae.join(lc(),n.name),o=Pr(pn),s=new Set,a=new Map;for(let p of await na(r)){let m=ae.join(r,p),w;try{w=await W.readFile(m,"utf8")}catch{continue}let{fm:y}=dc(w);if(Jw(y,n.name)!==pn)continue;let x=ae.join(o,p),k=await Gt(x).catch(()=>0),T=await Gt(m,w);await W.mkdir(o,{recursive:!0}),T>k&&await W.writeFile(x,w),await W.rm(m,{force:!0}),s.add(p),a.set(p,Yw(p,y)),e++}if(!s.size)continue;let i=Date.now();await nd(r,[...s].map(p=>[p,i])),await ac(n.name,[...s].map(p=>({file:p,deleted:!0,modified:i})));let c=ae.join(r,"MEMORY.md"),l=await W.readFile(c,"utf8").catch(()=>""),u=l.split(`
|
|
33
|
+
`).filter(p=>!s.has(Oo(p))).join(`
|
|
34
|
+
`);await W.writeFile(c,u).catch(()=>{});let d=ae.join(o,"MEMORY.md"),f=await W.readFile(d,"utf8").catch(()=>"");await W.writeFile(d,od(f,l,s,a))}return{moved:e}}async function og(){let e=[],t=[pn];try{for(let n of await W.readdir(lc(),{withFileTypes:!0}))n.isDirectory()&&t.push(n.name)}catch{}for(let n of t){let r=Pr(n),o=await na(r),s=await ta(r);if(!o.length&&!Object.keys(s).length)continue;let a=[];for(let i of o){let c=await W.readFile(ae.join(r,i),"utf8").catch(()=>""),{fm:l}=dc(c);a.push({file:i,name:Gn(l,"name")||i.replace(/\.md$/,""),description:Gn(l,"description")||"",scope:Gn(l,"scope")||null,modified:await Gt(ae.join(r,i),c)})}e.push({key:n,facts:a,tombstones:Object.keys(s).length})}return e}async function sg(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=Pr(e),r=ae.join(n,t);if(!await W.access(r).then(()=>!0).catch(()=>!1))throw new Error(`fact ${t} not found in ${e}`);let s=Date.now();await W.rm(r,{force:!0}),await nd(n,[[t,s]]);let a=ae.join(n,"MEMORY.md"),i=await W.readFile(a,"utf8").catch(()=>"");await W.writeFile(a,i.split(`
|
|
35
|
+
`).filter(l=>Oo(l)!==t).join(`
|
|
36
|
+
`)).catch(()=>{});let c=await ac(e,[{file:t,deleted:!0,modified:s}]);return{deleted:t,key:e,plane:c.source}}async function ad(e){try{let t=await rd(e),{harvested:n,changedByKey:r}=await Xw(e,t[0]||null);for(let[o,s]of r)await fc(o,[...s]);return n>0&&await cI(e,r).catch(()=>{}),{harvested:n}}catch(t){return{harvested:0,error:t?.message||String(t)}}}async function cI(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,iI)}}await A({id:n,cwd:e},{type:"memory.harvested",actor:"orchestrator",payload:{harvested:o,keys:r,ts:Date.now()}})}var G_,uc,Hw,V_,pn,zw,Oo,Kw,z_,Y_,X_,tI,rI,oI,$o,td,Gw,Fe,cc,Vw,ea,iI,ra=M(()=>{Lw();ce();G_=K_(q_),uc=process.env.BOSTRAT_STATE_DIR||ae.join(process.env.HOME||W_.tmpdir(),".bostrat"),Hw=200,V_=25*1024;pn="operator";zw=".tombstones.json";Oo=e=>/\]\(([^)]+\.md)\)/.exec(e)?.[1]||null;Kw=new Map,z_=2e3;Y_=/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/,X_=e=>{let t=new Map;for(let n of(e||"").split(`
|
|
37
|
+
`)){let r=Oo(n);r&&n.trim().startsWith("-")&&t.set(r,n)}return t};tI=`# 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
|
+
`;rI=Number(process.env.BOSTRAT_MEMORY_SPAWN_PULL_MS)||1500;oI=Number(process.env.BOSTRAT_MEMORY_HARVEST_DEBOUNCE_MS)||2e3,$o=new Map,td=new Set,Gw=Promise.resolve(),Fe={scheduled:0,runs:0,harvested:0,errors:0,lastRunAt:null,lastError:null,reconciles:0,lastReconcileAt:null,backfill:null};cc=new Map;Vw=!1,ea=null;iI=20});import id from"node:fs/promises";import lI from"node:path";async function it(e,t,{mode:n}={}){await id.mkdir(lI.dirname(e),{recursive:!0});let r=`${e}.${process.pid}.${++uI}.tmp`;await id.writeFile(r,JSON.stringify(t,null,2)+`
|
|
43
|
+
`,n!=null?{mode:n}:void 0),await id.rename(r,e)}var uI,Vn=M(()=>{uI=0});import ug from"node:fs/promises";import dg from"node:os";import fg from"node:path";function Ae(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function aa(e,t={},n=process.env){let r=new AbortController,o=setTimeout(()=>r.abort(),dI);try{return await gI(`${ke(n)}${e}`,{...t,signal:r.signal})}finally{clearTimeout(o)}}async function mg(){return hc||(cd||=ug.readFile(pg(),"utf8").then(e=>JSON.parse(e)).catch(()=>({ops:[]})).then(e=>hc={ops:Array.isArray(e?.ops)?e.ops:[],parked:Array.isArray(e?.parked)?e.parked:[]}).finally(()=>cd=null),cd)}async function wc(){await it(pg(),hc).catch(()=>{})}function cg(e){return e.op==="blob"?`blob ${e.family} ${e.id} ${e.v} ${e.kind}`:`${e.op} ${e.family} ${e.id}`}async function ud(e){let t=await mg(),n=cg(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=>cg(o)===n);r>=0?t.ops[r]=e:t.ops.push(e)}t.ops.length>ag&&(t.ops=t.ops.slice(-ag)),await wc()}function lg(){mc||(mc=setTimeout(()=>{mc=null,Co().catch(()=>{})},yI),mc.unref?.())}function ld(e){let t=new Error(`plane ${e}`);return t.refused=!0,t}async function kI(e,t=process.env){if(e.op==="record"){let n=await aa(`/v1/content/${encodeURIComponent(e.family)}`,{method:"POST",headers:{...sa(t),"content-type":"application/json"},body:JSON.stringify({records:[{id:e.id,trackId:e.trackId??null,data:e.data,updatedAt:e.updatedAt,originNode:wI()}]})},t);if(!n.ok)throw ld(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 ug.readFile(e.path).catch(()=>null);if(!n)return{delivered:!0,dropped:"source file gone"};let r=await aa(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}/blob?v=${e.v}&kind=${encodeURIComponent(e.kind)}`,{method:"POST",headers:{...sa(t),"content-type":"application/octet-stream"},body:n},t);if(!r.ok&&r.status!==400)throw ld(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 aa(`/v1/content/${encodeURIComponent(e.family)}/${encodeURIComponent(e.id)}?deletedAt=${e.deletedAt}`,{method:"DELETE",headers:sa(t)},t);if(!n.ok)throw ld(n.status);return{delivered:!0}}return{delivered:!0,dropped:"unknown op"}}async function Co({env:e=process.env,log:t=console,now:n=Date.now()}={}){return Ae(e)?oa||(oa=(async()=>{let r=await mg();xI(r,n);let o=0,s=0;for(;r.ops.length;){let a=r.ops[0],i;try{i=await kI(a,e)}catch(c){if(!c?.refused)return lg(),{flushed:o,parked:s,pending:r.ops.length,error:c?.message||String(c)};if(a.refusals=(a.refusals||0)+1,a.refusals<pI)return lg(),await wc(),{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>ig&&(r.parked=r.parked.slice(-ig)),s++,await wc();continue}i.rejected&&t.error?.(`[content-plane] ALARM plane rejected ${a.op} ${a.family}/${a.id}: ${i.rejected}`),r.ops.shift(),o++,await wc()}return{flushed:o,parked:s,pending:0}})().finally(()=>{oa=null}),oa):{flushed:0,source:"unconfigured"}}function xI(e,t){if(!e.parked.length||e.ops.length)return;let n=e.parked.filter(r=>t-(r.parkedAt||0)>=bI);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 Ae(r)?(await ud({op:"record",family:e,id:t.id,trackId:n??t.trackId??null,data:t,updatedAt:Number(t.updatedAt)||Date.now()}),Co({env:r})):{queued:!1,source:"unconfigured"}}async function zn(e,t,n,r,o,{env:s=process.env}={}){return Ae(s)?(await ud({op:"blob",family:e,id:t,v:Number(n),kind:r,path:o}),Co({env:s})):{queued:!1,source:"unconfigured"}}async function Ot(e,t,{deletedAt:n=Date.now(),env:r=process.env}={}){return Ae(r)?(await ud({op:"delete",family:e,id:t,deletedAt:n}),Co({env:r})):{queued:!1,source:"unconfigured"}}async function We(e,{env:t=process.env}={}){if(!Ae(t))return{records:[],source:"unconfigured"};try{let n=await aa(`/v1/content/${encodeURIComponent(e)}?tombstones=1`,{headers:sa(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 ia(e,t,n,r,{env:o=process.env}={}){if(!Ae(o))return null;try{let s=await aa(`/v1/content/${encodeURIComponent(e)}/${encodeURIComponent(t)}/blob?v=${Number(n)}&kind=${encodeURIComponent(r)}`,{headers:sa(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 dI,fI,ag,pI,ig,mI,hI,pg,wI,gI,yI,bI,sa,hc,cd,mc,oa,Jn=M(()=>{et();Vn();dI=1e4,fI=3e4,ag=2e3,pI=5,ig=100,mI=60*6e4,hI=()=>process.env.BOSTRAT_STATE_DIR||fg.join(process.env.HOME||dg.tmpdir(),".bostrat"),pg=()=>fg.join(hI(),"content-outbox.json"),wI=()=>process.env.BOSTRAT_NODE_ID||dg.hostname(),gI=(...e)=>fetch(...e),yI=fI,bI=mI;sa=(e=process.env)=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`});hc=null,cd=null,mc=null,oa=null});import dd from"node:fs/promises";import hg from"node:path";async function wg(e,t,{box:n=null,agent:r=null}={}){if(!e||!Ae())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 zn("checkpoints",e,o,"md",t),ca=null,{pushed:!0,v:o}}catch{return{pushed:!1}}}async function EI(){if(ca&&Date.now()-ca.at<AI)return{records:ca.records,source:"plane"};let e=await We("checkpoints");return e.source==="plane"&&(ca={at:Date.now(),records:e.records}),e}async function gc(e){try{let t=TI(e);if(!t||!Ae())return{restored:!1};let n=hg.join(e,SI,vI);if(await dd.stat(n).then(()=>!0,()=>!1))return{restored:!1};let{records:r,source:o}=await EI();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 ia("checkpoints",t,s.data.v,"md");return typeof a!="string"||!a?{restored:!1}:(await dd.mkdir(hg.dirname(n),{recursive:!0}),await dd.writeFile(n,a),{restored:!0,v:s.data.v})}catch{return{restored:!1}}}var SI,vI,TI,AI,ca,yc=M(()=>{Jn();SI=".bostrat/handoff",vI="latest.md",TI=e=>/workspaces[/\\]([0-9a-f]{8})$/.exec(e||"")?.[1]??null;AI=6e4,ca=null});import la from"node:fs/promises";import _I from"node:os";import yg from"node:path";function Dr(e){let t=String(e??"");for(let n of II)t=t.replace(n,"\u2039redacted\u203A");return t}function bc(){let e=process.env.BOSTRAT_HANDOFF_DEBUG;return e==="1"||e==="true"}async function RI(e){try{await la.mkdir(fd,{recursive:!0}),await la.appendFile(Mo,`${JSON.stringify(e)}
|
|
44
|
+
`,"utf8");let n=(await la.readFile(Mo,"utf8")).split(`
|
|
45
|
+
`).filter(Boolean);n.length>gg&&await la.writeFile(Mo,`${n.slice(-gg).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,d,f,p,m]=r.split("|"),
|
|
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=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:
|
|
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 d=[];if(u.type==="function_call"&&
|
|
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
|
|
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
|
-
`),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(
|
|
75
|
-
`),r=-1;for(let o=n.length-1;o>=0;o--)if(
|
|
76
|
-
`)}function
|
|
77
|
-
`))
|
|
78
|
-
`).slice(-
|
|
79
|
-
`);return
|
|
47
|
+
`,"utf8")}catch(t){console.warn(`[handoff-debug] failed to write ${Mo}: ${t?.message||t}`)}}async function Z(e={}){let t={at:new Date().toISOString(),ts:Date.now(),...e},n=[t.flow||"handoff",t.phase||t.step||"?",t.trackId?`track=${t.trackId}`:"",t.sessionName?`box=${t.sessionName}`:"",t.error?`ERR=${t.error}`:""].filter(Boolean).join(" \xB7 ");if(console.log(`[handoff-debug] ${n}`),t.sessionName&&bc())try{t.paneTail=Dr((await ve(t.sessionName,{lines:35})).slice(-4e3))}catch{t.paneTail=null}(bc()||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 RI(t)}async function bg({limit:e=80}={}){try{return(await la.readFile(Mo,"utf8")).split(`
|
|
48
|
+
`).filter(Boolean).slice(-e).map(r=>{try{let o=JSON.parse(r);return typeof o?.paneTail=="string"&&(o.paneTail=Dr(o.paneTail)),o}catch{return{raw:Dr(r)}}})}catch{return[]}}var II,fd,Mo,gg,kg,Vt=M(()=>{ze();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];fd=process.env.BOSTRAT_DEBUG_DIR||yg.join(_I.homedir(),".bostrat","debug"),Mo=yg.join(fd,"handoff.jsonl"),gg=Number(process.env.BOSTRAT_HANDOFF_DEBUG_MAX)||500;kg={DEBUG_DIR:fd,LOG_FILE:Mo}});import{execFile as Sg}from"node:child_process";import{promisify as $I}from"node:util";import xc from"node:fs";import{randomUUID as vg}from"node:crypto";function Do(e){return new Promise(t=>setTimeout(t,e))}function kc(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 Tg(e){return`/tmp/tmux-${e}/default`}function OI({explicit:e,legacyExists:t,runtimeDir:n,uid:r}){if(e)return e;let o=Tg(r);return t?o:n?`${n}/bostrat/tmux-default`:o}function da(){return process.env.BOSTRAT_CLAUDE_ALT_SCREEN==="1"?[]:["-e",`${ua}=1`]}function wd(e,t,n){let r=Br(e),o=/(^|\s)(--session-id|--resume|--continue|-r|-c)(\s|=|$)/.test(e);if(!r||o)return e;let s=`${e} --session-id ${t}`,a=/(^|\s)(--name|-n)(\s|=)/.test(e);return n&&!a&&Ee(n)&&(s+=` --name ${n}`),s}function Br(e){return/(^|\/|\s)claude(\s|$)/.test(String(e||""))}function Yn(e){return/(^|\/|\s)claude(\s|$)/.test(e)?"claude":/cursor/.test(e)?"cursor":/codex/.test(e)?"codex":/gemini/.test(e)?"gemini":"agent"}function NI(e){let t=String(e||"").trim();return!t||/fake-|\.sh(\s|$)/.test(t)?!1:Br(t)||/(^|\/|\s)codex(\s|$)/.test(t)||/cursor-agent/.test(t)||/(^|\/|\s)gemini(\s|$)/.test(t)}async function Ag(e,{loginShell:t=!1,failOpen:n=!1}={}){let r=await Eg(e,{loginShell:t});return r===Sc?!0:r===No?!1:n}async function Eg(e,{loginShell:t=!1,timeoutMs:n=pd}={}){if(!e)return No;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}),Sc}catch(s){return s?.killed||s?.signal?Po:s?.code===1||s?.code===127?No:Po}}async function vc(e,{retries:t=PI,sleep:n=DI,probe:r=Eg}={}){let o=String(e||"").trim().split(/\s+/)[0],s=await r(o,{timeoutMs:pd});for(let a=0;s===Po&&a<t.length;a++)await n(t[a]),s=await r(o,{timeoutMs:pd*2});return s}function U(...e){return["-S",ft,...e]}async function Xn(){if(!xc.existsSync(ft))return!1;try{return await we(L,U("list-sessions")),!0}catch(e){let t=e&&e.stderr?String(e.stderr):"";return!/no server running/i.test(t)}}async function fa(){if(await Xn())return!0;try{xc.mkdirSync(ft.replace(/\/[^/]+$/,""),{recursive:!0}),await we(L,U("new-session","-d","-s","keeper","sleep 2147483647"))}catch{}return await Xn()}function Ee(e){return typeof e=="string"&&BI.test(e)}async function Ct(){if(!xc.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(L,U("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("|"),w=Number(i),y=Number(m);return{name:o,title:u||null,windows:Number(s),attached:Number(a),lastActivityMs:Number.isFinite(w)?n-w*1e3:null,cwd:c||null,isBostrat:l==="1",sessionId:d||null,agent:f||(l==="1"?"claude":null),alternateOn:p==="1",createdMs:Number.isFinite(y)&&y>0?y*1e3:null}}).filter(r=>!MI.has(r.name)&&!r.name.startsWith("bostrat-view-"))}catch{return[]}}async function ie(e){return(await Ct()).find(n=>n.name===e)||null}function FI(e){if(typeof e!="string")return!1;let t=e.trim();return t.length>=1&&t.length<=LI&&jI.test(t)}function Tc(e){return e.trim().replace(/\s+/g,"-").toLowerCase().replace(/[^A-Za-z0-9_.-]/g,"").replace(/^-+|-+$/g,"")}async function _g(e){try{let{stdout:t}=await we(L,U("list-sessions","-F","#{session_name}"));return t.split(`
|
|
50
|
+
`).map(n=>n.trim()).includes(e)}catch{return!1}}function WI(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 qI(e,t){let n=Date.now()+pa(),r=WI(t),o=!1,s=0;for(;;){if(await Do(UI),s+=1,!await _g(e))return;let a=await ve(e,{lines:15}),i=!o&&HI.test(a)&&!!r?.test(a);if(!i&&s>1&&!await mn(e))return;if(i){o=!0,await Z({flow:"spawn",phase:"launch-retry",sessionName:e,agent:Yn(t),promptHead:t.slice(0,120)}).catch(()=>{}),await we(L,U("send-keys","-t",e,"C-u")),await we(L,U("send-keys","-t",e,"-l",t)),await we(L,U("send-keys","-t",e,"Enter"));continue}if(Date.now()>n){await Z({flow:"spawn",phase:"launch-failed",sessionName:e,agent:Yn(t),retried:o,error:"agent CLI never started after launch was typed"}).catch(()=>{});return}}}async function Zn(e,t=CI,n={}){if(!FI(e))throw new Error("displayName must be 1\u201364 chars (letters, digits, space . _ -)");let r=e.trim(),o=Tc(r);if(!Ee(o))throw new Error("resulting tmux session name is invalid");if(!await fa())throw new Error(`host tmux server not running on ${ft} and auto-start failed \u2014 start one: ${L} -S ${ft} new-session -d -s keeper "sleep 2147483647"`);if(await _g(o))throw new Error(`tmux session "${o}" already exists`);let s=n.agentCmd||md,a=n.agent||Yn(s),i=!!n.resumeSessionId,c=n.resumeSessionId||vg(),l=i?s:wd(s,c,o),u=i?n.pinned===!0:/(^|\s)--session-id(\s|=)/.test(l),d=a==="claude"&&Br(s),f=n.firstRun===!0;f||(d&&await Wn(t),a==="codex"&&await To(t),a==="cursor"&&await Ao(t),await Qw(t),n.freshWorkspace!==!0&&await gc(t));let p=d&&!f?Qi():null,m=xw(),w=["-e",`${Xi}=${m}`];p&&w.push("-e",`ANTHROPIC_API_KEY=${p}`),d&&w.push(...da());try{await we(L,U("new-session","-d","-s",o,"-c",t,...w))}catch(y){throw kc(p?kc(y,p):y,m)}return await jr(o),await we(L,U("set-option","-t",o,"@bostrat-session","1")),await we(L,U("set-option","-t",o,"@bostrat-title",r)),await we(L,U("set-option","-t",o,"@bostrat-sid",c)),await we(L,U("set-option","-t",o,"@bostrat-agent",a)),await we(L,U("send-keys","-t",o,l,"Enter")),NI(l)&&qI(o,l).catch(()=>{}),{name:o,title:r,cwd:t,sessionId:c,agent:a,pinned:u,hookToken:m}}async function Mt(e){if(!Ee(e))throw new Error("invalid session name");await we(L,U("kill-session","-t",e))}async function Ig(e,t){if(Ee(e))try{let n=t?U("set-option","-t",e,"@bostrat-sid",t):U("set-option","-u","-t",e,"@bostrat-sid");await we(L,n)}catch{}}async function ve(e,{lines:t=40}={}){if(!Ee(e))return"";try{let n=U("capture-pane","-t",e,"-p");Number.isFinite(t)&&t>0&&n.push("-S",`-${Math.floor(t)}`);let{stdout:r}=await we(L,n);return String(r||"")}catch{return""}}async function Rg(e){if(!Ee(e))return 0;try{let{stdout:t}=await we(L,U("list-clients","-t",e,"-F","1"));return String(t||"").trim().split(`
|
|
51
|
+
`).filter(Boolean).length}catch{return 0}}async function jr(e,{cols:t=VI,rows:n=zI}={}){if(!Ee(e))return!0;if(await Rg(e))return!1;try{let r=await $g(e);if(r.cols===t&&r.rows===n)return!0;await we(L,U("resize-window","-t",e,"-x",String(t),"-y",String(n))),await we(L,U("set-window-option","-t",e,"window-size","latest")),await Do(80)}catch{}return!0}async function JI(e){if(!Ee(e))return 0;try{let{stdout:t}=await we(L,U("display-message","-t",e,"-p","#{pane_pid}"));return Number(String(t||"").trim())||0}catch{return 0}}async function mn(e){let t=await JI(e);if(!t)return!1;try{return await we("pgrep",["-P",String(t)]),!1}catch(n){return n?.code===1}}async function $g(e){if(!Ee(e))return{cols:0,rows:0};try{let{stdout:t}=await we(L,U("display-message","-t",e,"-p","#{window_width} #{window_height}")),[n,r]=String(t||"").trim().split(/\s+/).map(o=>Number(o)||0);return{cols:n,rows:r}}catch{return{cols:0,rows:0}}}async function Ac(e,{cols:t=KI,rows:n=GI}={}){if(Ee(e)&&!await Rg(e))try{let r=await $g(e),o=Math.max(r.cols,t),s=Math.max(r.rows,n);(o>r.cols||s>r.rows)&&(await we(L,U("resize-window","-t",e,"-x",String(o),"-y",String(s))),await we(L,U("set-window-option","-t",e,"window-size","latest")),await Do(80))}catch{}}async function hn(e){return await Ac(e),ve(e,{lines:Ue})}async function Ie(e,t,{settleMs:n=40,kind:r}={}){if(!Ee(e))throw new Error("invalid session name");let o=(Array.isArray(t)?t:[t]).filter(s=>YI.has(s));for(let s=0;s<o.length;s++)await we(L,U("send-keys","-t",e,o[s])),n>0&&s<o.length-1&&await Do(n);return Z({flow:"tmux",phase:"send-keys",sessionName:e,kind:r||"keys",keys:o.join(" ")}).catch(()=>{}),o.length}async function Bo(e,t,{kind:n}={}){if(!Ee(e))throw new Error("invalid session name");let r=Math.max(0,Math.floor(Number(t)||0));return r>0&&await we(L,U("send-keys","-t",e,"-N",String(r),"BSpace")),Z({flow:"tmux",phase:"send-keys",sessionName:e,kind:n||"wipe",keys:`BSpace x${r}`}).catch(()=>{}),r}async function _c(e,t,{perCharDelayMs:n=120,kind:r}={}){if(!Ee(e))throw new Error("invalid session name");let o=[...String(t)];for(let s of o)await we(L,U("send-keys","-t",e,"-l",s)),n>0&&await Do(n);Z({flow:"tmux",phase:"type",sessionName:e,kind:r||"type",textLen:o.length,textHead:String(t).slice(0,120)}).catch(()=>{})}function XI(e,t){return new Promise((n,r)=>{let o=Sg(L,U(...e),(s,a,i)=>{s?r(s):n({stdout:a,stderr:i})});o.stdin.on("error",()=>{}),o.stdin.end(t)})}async function ZI(e,t){let n=`bostrat-${vg().slice(0,8)}`;await XI(["load-buffer","-b",n,"-"],t),await we(L,U("paste-buffer","-p","-d","-b",n,"-t",e))}async function He(e,t,{enter:n=!0,submitDelayMs:r=0,kind:o,paste:s=!1}={}){if(!Ee(e))throw new Error("invalid session name");if(typeof t=="string"&&t.length>0&&(s?await ZI(e,t):await we(L,U("send-keys","-t",e,"-l",t))),n){let a=Number(r);Number.isFinite(a)&&a>0&&await Do(a),await we(L,U("send-keys","-t",e,"Enter"))}Z({flow:"tmux",phase:"send",sessionName:e,kind:o||"text",textLen:typeof t=="string"?t.length:0,textHead:typeof t=="string"?t.slice(0,120):"",enter:n,submitDelayMs:r,...s?{paste:!0}:{}}).catch(()=>{})}var we,L,xg,ft,CI,md,ua,hd,MI,Sc,No,Po,pd,PI,DI,gd,BI,jI,LI,pa,UI,HI,KI,GI,Ue,VI,zI,YI,Ec,ze=M(()=>{Ys();tc();nc();ra();yc();Vt();Js();we=$I(Sg);L=process.env.BOSTRAT_TMUX_BIN||"/usr/bin/tmux";xg=typeof process.getuid=="function"?process.getuid():0,ft=OI({explicit:process.env.BOSTRAT_TMUX_SOCKET,legacyExists:(()=>{try{return xc.existsSync(Tg(xg))}catch{return!1}})(),runtimeDir:process.env.XDG_RUNTIME_DIR,uid:xg}),CI=process.env.BOSTRAT_DEFAULT_CWD||process.env.HOME||"/",md=process.env.BOSTRAT_AGENT_CMD||"claude --dangerously-skip-permissions",ua="CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN";hd="bostrat-usage-probe",MI=new Set([hd]);Sc="present",No="absent",Po="unknown",pd=5e3,PI=[250,1e3,3e3],DI=e=>new Promise(t=>setTimeout(t,e));gd={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"};BI=/^[A-Za-z0-9_.-]{1,64}$/;jI=/^[A-Za-z0-9 ._↻-]+$/,LI=64;pa=()=>Number(process.env.BOSTRAT_LAUNCH_VERIFY_MS)||12e3,UI=1e3,HI=/not found|Unknown command/i;KI=Number(process.env.BOSTRAT_PROMPT_CAPTURE_COLS)||100,GI=Number(process.env.BOSTRAT_PROMPT_CAPTURE_ROWS)||40,Ue=Number(process.env.BOSTRAT_PROMPT_CAPTURE_LINES)||80,VI=Number(process.env.BOSTRAT_BOX_COLS)||120,zI=Number(process.env.BOSTRAT_BOX_ROWS)||40;YI=new Set(["Up","Down","Left","Right","Space","Enter","Tab","BTab","Escape","BSpace"]);Ec=1600});import ma from"node:fs";import oR from"node:fs/promises";import jo from"node:path";import sR from"node:os";function kd(e){return e.replace(/[^A-Za-z0-9]/g,"-")}function xd(e){let t=jo.join(bd,kd(e)),n;try{n=ma.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return null}let r=null;for(let o of n){let s=jo.join(t,o);try{let{mtimeMs:a}=ma.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=jo.join(bd,kd(e),`${t}.jsonl`);return ma.existsSync(n)?n:null}function Bg(e){if(!e)return[];let t=jo.join(bd,kd(e)),n;try{n=ma.readdirSync(t).filter(o=>o.endsWith(".jsonl"))}catch{return[]}let r=[];for(let o of n)try{let s=ma.statSync(jo.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 oR.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 iR(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 ha(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=xd(e),o=!1,!r)return null}else return null;let s;try{s=iR(await ct(r))}catch{return null}if(!s)return null;let a=Object.prototype.hasOwnProperty.call(Dg,s.model),i=Dg[s.model]||aR;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 Dg,aR,bd,Pt=M(()=>{Dg={"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},aR=2e5,bd=process.env.BOSTRAT_CLAUDE_PROJECTS||jo.join(sR.homedir(),".claude","projects")});function cR(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 wa(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 ga(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(Fg(o)){t.has(o)&&r++;continue}n.push(o)}return n}function Ug(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 dR(e){let t=Ug(e),n=Je(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 Sd(e){if(e.includes("--files")){let s=Je(xe(e,jg)[0]);return s&&s!=="."?`List files in ${Lo(s)}`:"List files"}let t=xe(e,jg),n=t[0];if(!n)return null;let r=Je(t.slice(1).find(s=>s!==".")),o=Fo(n,40);return r?`Search for \u201C${o}\u201D in ${Lo(r)}`:`Search for \u201C${o}\u201D`}function vd(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 Td(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 fR(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 Lg(e){return e.find(t=>/^https?:\/\//.test(t))||null}function hR(e){let t=ga(wa(e));if(!t.length)return null;if(/^(ba|z)?sh$/.test(Re(t[0]))&&(t[1]==="-lc"||t[1]==="-c")&&t[2])return Ad(t[2]);let n=Re(t[0]);if(mR.has(n))return null;if(t.includes("--version"))return`Check ${n} version`;if(n.endsWith(".sh"))return`Run ${n}`;let r=pR[n];return r?r(t.map((o,s)=>s===0?n:o)):null}function Ad(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=cR(t);for(let i=0;i<r.length-1;i++){let c=ga(wa(r[i])),l=ga(wa(r[i+1]));if(!(!c.length||!l.length)&&["nl","cat"].includes(Re(c[0]))&&Re(l[0])==="sed"){let u=Ug(l),d=xe(c)[0];if(u&&d)return Fo(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=hR(i);c&&o.push({d:c,head:Re(ga(wa(i))[0]||""),sub:xe(ga(wa(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":Fo(s?s.d:o[0].d,88)}var Fo,Je,Re,Lo,Fg,jg,lR,uR,pR,mR,Hg=M(()=>{Fo=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e,Je=e=>e&&!/[$`]/.test(e)?e:null,Re=e=>e.replace(/\/+$/,"").split("/").pop()||e,Lo=e=>{let t=e.replace(/\/+$/,"").split("/").filter(Boolean);return t.length>3?t.slice(-2).join("/"):e},Fg=e=>e.startsWith("-"),jg=new Set(["-g","--glob","-t","--type","-A","-B","-C","-m","--max-count","-e","--regexp","-f","--file"]);lR={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"},uR={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"};pR={git:e=>{let t=xe(e,new Set(["-C","-c"])),n=lR[t[0]];return n?n(t,e):null},gh:e=>{let t=xe(e),n=uR[t[0]];return n?n(t):null},sed:dR,rg:Sd,grep:Sd,egrep:Sd,nl:e=>{let t=Je(xe(e)[0]);return t?`Read ${Re(t)}`:null},cat:e=>{let t=Je(xe(e)[0]);return t?`Read ${Re(t)}`:null},head:e=>{let t=Je(xe(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the start of ${Re(t)}`:null},tail:e=>{let t=Je(xe(e,new Set(["-n","-c"])).find(n=>!/^\d+$/.test(n)));return t?`Read the end of ${Re(t)}`:null},wc:e=>{let t=Je(xe(e)[0]);return t?`Count lines in ${Re(t)}`:"Count input lines"},ls:e=>{let t=Je(xe(e)[0]);return t&&t!=="."?`List files in ${Lo(t)}`:"List files"},find:e=>Je(e[1])&&!Fg(e[1])&&e[1]!=="."?`Find files in ${Lo(e[1])}`:"Find files",tree:()=>"List the directory tree",mkdir:e=>{let t=Je(xe(e)[0]);return t?`Create directory ${Lo(t)}`:null},touch:e=>{let t=Je(xe(e)[0]);return t?`Create ${Re(t)}`:null},rm:e=>{let t=Je(xe(e)[0]);return t?`Delete ${Lo(t)}`:null},mv:e=>{let t=Je(xe(e)[0]);return t?`Move ${Re(t)}`:null},cp:e=>{let t=Je(xe(e)[0]);return t?`Copy ${Re(t)}`:null},chmod:e=>{let t=Je(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=Lg(e);return t?`Fetch ${Fo(t,56)}`:"Fetch a URL"},wget:e=>{let t=Lg(e);return t?`Fetch ${Fo(t,56)}`:"Fetch a URL"},npm:vd,pnpm:vd,yarn:vd,npx:e=>{let t=xe(e)[0];return t?`Run ${t}`:null},node:e=>Td(e,"Node"),python:e=>Td(e,"Python"),python3:e=>Td(e,"Python"),make:e=>{let t=xe(e)[0];return t?`Run make ${t}`:"Run make"},systemctl:fR,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}},mR=new Set(["pwd","echo","cd","sleep","true","false","test","[","set","export","exit","read"])});function Uo(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 Qn(e,t=""){let n=t.trim();return n||Ad(e)||Uo(e)}function Ed(e,t){if(e){if(Array.isArray(e)){for(let n of e)Ed(n,t);return}if(typeof e=="object"){if(e.type==="tool_use"&&_d.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))Ed(n,t)}}}async function Wg(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(Ed(c.content,l),!l.length)continue;let u=i.timestamp?Date.parse(i.timestamp):null;for(let d of l)o.push({...d,summary:Qn(d.command,d.description),ts:u})}return o.slice(-n)}function qg(e,t){let n=t.filter(l=>l.role==="assistant"&&(l.text||"").trim()).sort((l,u)=>(l.ts||0)-(u.ts||0)),r=[...e].sort((l,u)=>(l.ts||0)-(u.ts||0)),o=[],s=0,a=0,i=l=>{let u=l.n??a+1;return u>a&&(a=u),u};for(let l of n){let u=r.findIndex((m,w)=>w>=s&&(m.ts||0)>(l.ts||0)),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 _d,Ho=M(()=>{Pt();Hg();_d=new Set(["Bash"])});import Kg from"node:fs";import Rd from"node:fs/promises";import Id from"node:path";import wR from"node:os";async function Gg(e){try{return await Rd.realpath(e)}catch{return e}}function yR(e=gR){let t=[],n=r=>{let o;try{o=Kg.readdirSync(r,{withFileTypes:!0})}catch{return}for(let s of o){let a=Id.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:Kg.statSync(a).mtimeMs})}catch{}}}};return n(e),t.sort((r,o)=>o.mtimeMs-r.mtimeMs)}async function bR(e,t=1048576){let n;try{n=await Rd.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 kR(e){let t=await bR(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 Lr(e,t){if(!e&&!t)return null;let n=yR();if(t){let o=n.find(s=>s.id===t);return o||null}let r=await Gg(e);for(let o of n){let s=await kR(o.file);if(s&&(s===e||await Gg(s)===r))return o}return null}async function Vg(e,t,{limit:n=80,maxBytes:r=524288}={}){let o=await Lr(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:Fr,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 w=m.payload;if(!w)continue;if((w.type==="function_call"||w.type==="custom_tool_call")&&w.name){u=!0,$d.has(w.name)||c.push(w.name);continue}if(w.type!=="message"||w.role!=="assistant")continue;u=!0;let y=SR(w.content),x=c.length?c.slice():null;c=[],!(!y&&!x)&&a.push({role:"assistant",text:y,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 vR(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 TR(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 AR(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=TR(i.slice(c.index+c[0].length).trimStart());l&&l.trim()&&o.push(l.trim())}),o}async function zg(e,t,{limit:n=400,maxBytes:r=524288}={}){let o=await Lr(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"&&ya.has(u.name)){let p;try{p=JSON.parse(u.arguments||"{}")}catch{continue}let m=vR(p);m&&(d=[m])}else u.type==="custom_tool_call"&&u.name==="exec"&&(d=AR(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:Qn(p),ts:l.timestamp?Date.parse(l.timestamp):null})})}return a.slice(-n)}async function Jg(e,t){let n=await Lr(e,t);if(!n)return null;try{return Math.round((await Rd.stat(n.file)).mtimeMs)}catch{return n.mtimeMs||null}}var gR,xR,SR,ya,$d,Fr,Ur=M(()=>{Pt();Ho();gR=process.env.BOSTRAT_CODEX_SESSIONS||Id.join(process.env.CODEX_HOME||Id.join(wR.homedir(),".codex"),"sessions");xR=new Set(["output_text","text"]),SR=e=>typeof e=="string"?e.trim():Array.isArray(e)?e.filter(t=>t&&typeof t.text=="string"&&xR.has(t.type)).map(t=>t.text).join(`
|
|
61
|
+
`).trim():"";ya=new Set(["exec_command","shell","exec"]),$d=new Set(["wait","write_stdin"]),Fr="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 ba({error:e=null,status:t=null,text:n=""}={}){let r=String(e||"").trim().toLowerCase();if(r&&Yg.has(r))return Yg.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 Xg(e){if(!e||e.role!=="assistant")return null;if(e.apiError){let t=e.apiError;return{cls:t.cls||ba({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===Fr?"other":ba({text:t}),code:null,status:null,requestId:null}}return null}function Zg(e,{codexFailedText:t=null}={}){let n=String(e?.text||"").trim();return n?e?.model==="<synthetic>"?/^No response requested\.$/.test(n)?null:{cls:ba({text:n}),code:null,status:ER(n),requestId:null,text:n}:t&&n===t?{cls:"other",code:null,status:null,requestId:null,text:n}:null:null}function ER(e){let t=/^API Error:\s*(\d{3})\b/.exec(e);return t?Number(t[1]):null}var uH,Yg,Rc=M(()=>{Ur();uH=Object.freeze(["entitlement","auth","rate_limit","overloaded","server","other"]),Yg=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 _R(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 RR(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 $R(e){return Array.isArray(e)&&e.length>0&&e.every(t=>t&&t.type==="tool_result")}function Od(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 Qg(e,t){if(!e.uuid)return null;let n=Od(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 Cd(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 w=c.attachment;w?.type==="queued_command"&&w.commandMode==="task-notification"&&typeof w.prompt=="string"&&w.prompt.trim()&&o.push({role:"user",text:w.prompt.trim(),ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let l=c.message;if(!l)continue;let u=l.role||c.type;if(u!=="user"&&u!=="assistant"||u==="assistant"&&l.model==="<synthetic>"&&!c.isApiErrorMessage)continue;if(u==="assistant"&&Array.isArray(l.content))for(let w of l.content){let y=w?.type==="tool_use"?w.input?.file_path||w.input?.path:null;typeof y=="string"&&OR.test(y)&&s.set(w.id,y.split("/").pop())}if($R(l.content)){let w=Qg(c,s);w&&o.push({role:"assistant",text:"",images:w,ts:c.timestamp?Date.parse(c.timestamp):null,model:null});continue}let d=IR(l.content),f=u==="assistant"?RR(l.content):null,p=u==="user"?Qg(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:ba({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 ey(e,t,{turns:n=16}={}){let r=_R(e,t);if(!r)return"";let o=await Cd(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}:** ${CR(s.text||c,700)} ${i}`.trim()}).join(`
|
|
64
|
+
|
|
65
|
+
`):""}var OR,CR,$c=M(()=>{Rc();Pt();OR=/\.(png|jpe?g|gif|webp|bmp|svg|tiff?|avif|heic|heif|ico)$/i;CR=(e,t)=>e.length>t?`${e.slice(0,t).trimEnd()}\u2026`:e});import MR from"node:fs/promises";import{execFile as NR}from"node:child_process";import{promisify as PR}from"node:util";function oy(e){Nd=typeof e=="function"?e:null}function Pd(e){let t=String(e||""),n=t.indexOf("/lib/node_modules/");return n>0?t.slice(0,n):null}function sy({sessionName:e,from:t,to:n}={}){let r=String(n||"").trim();!r||ty.has(r)||(ty.add(r),ny=ny.then(()=>BR({sessionName:e,from:t,to:r})).catch(()=>{}))}async function BR({sessionName:e,from:t,to:n}){await Z({flow:"codex-update",phase:"start",sessionName:e,from:t,to:n}).catch(()=>{});try{let r=(await Md("/bin/sh",["-c","command -v codex"])).stdout.trim();if(!r)throw new Error("codex not on PATH");let o=await MR.realpath(r),s=Pd(o),a=s?{...process.env,PATH:`${s}/bin:${process.env.PATH||""}`}:process.env;await Md("npm",["install","-g","@openai/codex"],{env:a,timeout:DR});let i=(await Md(r,["--version"])).stdout.trim().replace(/^codex-cli\s*/,"")||n;await Z({flow:"codex-update",phase:"done",sessionName:e,from:t,to:n,version:i}).catch(()=>{}),await ry({sessionName:e,from:t,to:n,version:i,status:"updated"})}catch(r){let o=String(r?.message||r).slice(0,300);await Z({flow:"codex-update",phase:"failed",sessionName:e,from:t,to:n,error:o}).catch(()=>{}),await ry({sessionName:e,from:t,to:n,status:"failed",error:o})}}async function ry(e){if(Nd)try{await Nd(e)}catch{}}var Md,DR,ty,ny,Nd,Oc=M(()=>{Vt();Md=PR(NR),DR=Number(process.env.BOSTRAT_CODEX_UPDATE_TIMEOUT_MS)||5*6e4,ty=new Set,ny=Promise.resolve(),Nd=null});function LR(e,{provider:t,box:n}){let r=String(e).replace(/\r/g,"").split(`
|
|
66
|
+
`);if(!ay.test(e))return null;let o=[],s=!1;for(let l=r.length-1;l>=0;l--){let u=r[l].match(jR);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"&&ay.test(a)?a:"The agent is asking for approval";return{provider:t,box:n,promptId:`${t}:${c}:${i.map(l=>l.label).join("|")}`.slice(0,200),questions:[{question:c,header:"Approval",multiSelect:!1,options:i.map(l=>({label:l.label,description:""}))}]}}function Hr(e){let t=String(e??"").replace(/\r/g,""),n=t.split(`
|
|
67
|
+
`);for(let r=n.length-1;r>=0;r--)if(ly.test(n[r]))return n.slice(r).join(`
|
|
68
|
+
`);return t}function FR(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 UR(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 HR(e,t=""){let n=Hr(e);if(ka.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(iy);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(iy.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&&!ly.test(r[l])&&FR(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=UR(l.label);return{label:l.label,description:[l.desc,u].filter(Boolean).join(" \xB7 ")}})}]}}function KR(e,t=""){let n=Hr(e);if(ka.test(n)||!WR.test(n))return null;let r=n.trimEnd().split(`
|
|
70
|
+
`).pop()||"";return qR.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 VR(e,t=""){let n=Hr(e);if(ka.test(n)||!cy.test(n))return null;let r=n.split(`
|
|
71
|
+
`),o=r.findIndex(i=>cy.test(i)),s="";for(let i=o+1;i<r.length;i++){let c=r[i].match(GR);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 zR(e,t=""){let n=Hr(e);return ka.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 JR(e,t=""){let n=Hr(e);return ka.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 YR(e,t=""){let n=String(e);if(!/Press any key to log in/.test(n)||Dd.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 ZR(e,t=""){let n=String(e);if(!XR.test(n)||Dd.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 QR(e,t=""){let n=String(e);return!/Scan this QR code to log in/.test(n)||Dd.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 uy(e,t,n=""){if(e!=="cursor"&&e!=="codex")return null;if(e==="codex"){let r=KR(t,n)||VR(t,n)||zR(t,n)||JR(t,n)||HR(t,n);if(r)return r}if(e==="cursor"){let r=ZR(t,n)||QR(t,n)||YR(t,n);if(r)return r}return LR(t,{provider:e,box:n})}function dy(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 jR,ay,ka,ly,iy,WR,qR,cy,GR,Dd,XR,Cc=M(()=>{jR=/^\s*[›>*\-❯]?\s*(\d+)[.)]\s+(.+?)\s*$/,ay=/\b(allow|approve|apply (this )?patch|run (this )?command|do you want to run|grant)\b/i;ka=/(^|\n)\s*›/,ly=/^\s*Welcome to Codex\b/;iy=/^\s*(>)?\s*(\d+)\.\s+(.+?)\s*$/;WR=/Successfully logged in\./,qR=/^\s*Press Enter to continue\.\s*$/;cy=/Enter this one-time code/,GR=/^\s*([A-Z0-9]{2,10}-[A-Z0-9]{2,10})\s*$/;Dd=/Run Everything|Add a follow-up|Plan, search, build anything/;XR=/click this link to log in|Signing in with the browser/});function r$(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(t$.test(r)||n$.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 c$(e){let t=String(e||"");if(!Pe.codex.block.test(t))return null;let n=Hr(t);if(!Pe.codex.block.test(n))return null;let r=n.match(i$);return{from:r?.[1]||null,to:r?.[2]||null}}function zt(e){return new Promise(t=>setTimeout(t,e))}function Bd(e,t){let n=Date.now(),r=l$();for(let[s,a]of Nc)n-a>=r&&Nc.delete(s);let o=`${e}::${t}`;return Nc.has(o)?!1:(Nc.set(o,n),!0)}function hy(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(my.test(n[o])){r=o;break}return r<0?t:n.slice(r+1).join(`
|
|
76
|
+
`)}function d$(e){let t=0;for(let n of String(e).split(`
|
|
77
|
+
`))my.test(n)&&(t=Math.max(t,n.trimEnd().length));return t}function jd(e,t){let n=Pe[e];if(!n?.busy)return{state:"idle",reason:null};if(Fd(e,t))return{state:"unknown",reason:"blocked"};if(n.up&&!n.up.test(t))return{state:"idle",reason:null};if(n.busy.test(hy(n,t)))return{state:"busy",reason:null};if(n.busyAlt&&n.busyAlt.test(t))return{state:"busy",reason:null};let r=d$(t);return e==="claude"&&r>0&&r<u$?{state:"unknown",reason:"narrow"}:{state:"idle",reason:null}}function Ld(e,t){return jd(e,t).state}function wy(e,t){if(e!=="claude")return!1;let n=String(t||"").split(`
|
|
78
|
+
`).slice(-p$).join(`
|
|
79
|
+
`);return f$.test(n)}function qo(e,t){return Ld(e,t)==="busy"}function Pc(e,t){return Ld(e,t)!=="idle"}function Ko(e,t){let n=Pe[e];return n?.up?n.up.test(t):!0}function m$(e,t){let n=Pe[e];return n?Fd(e,t)?!1:n.up.test(t)&&Ld(e,t)==="idle":!0}function Fd(e,t){let n=Pe[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 Ud(e,t){let n=String(t||"").trim();if(!n)return e;let r=String(e||"").split(`
|
|
80
80
|
`);for(let o=r.length-1;o>=0;o--)if(r[o].includes(n))return r.slice(o+1).join(`
|
|
81
|
-
`);return e}function
|
|
82
|
-
`)?!0:Buffer.byteLength(e,"utf8")>=
|
|
83
|
-
`).map(c=>c.replace(/\s+$/,"")),r=-1;for(let c=n.length-1;c>=0;c--)if(
|
|
84
|
-
`).map(o=>o.replace(/\s+$/,"")),n=-1;for(let o=t.length-1;o>=0;o--)if(t[o]){(
|
|
85
|
-
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return
|
|
86
|
-
${o}`.trim()||t.slice(0,2e3)}function
|
|
87
|
-
`):""}async function
|
|
81
|
+
`);return e}function gy(e){let t=String(e||"").trim();return t?t.slice(0,32):null}function fy(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 py(e,t,n,{bailOnDialog:r=!1,launchEcho:o=null}={}){let s=Date.now()+n,a=0,i=null;for(await jr(e).catch(()=>{});;){let c=Ud(await ve(e),o);if(m$(t,c)){await zt(Wo);return}if(Pe[t]?.up&&!Pe[t].up.test(c))if(await mn(e)){if(i??=Date.now(),Date.now()-i>=pa()){let l=fy(t);throw Z({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"&&a$.test(c)&&!Pe.claude.up.test(c)){Z({flow:"agent-ready",phase:"dialog",sessionName:e,agent:t,dialog:"resume-confirm"}).catch(()=>{});return}if(t==="codex"&&s$.test(c)&&!Pe.codex.up.test(c)&&a<3){if(!Bd(e,"codex-trust")){await zt(Mc);continue}a+=1,await Z({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,gate:"codex-trust",attempt:a}).catch(()=>{}),await He(e,"",{enter:!0,kind:"codex-trust-accept"}),await zt(Wo);continue}if(t==="cursor"&&o$.test(c)&&!Pe.cursor.up.test(c)&&a<3){if(!Bd(e,"cursor-trust")){await zt(Mc);continue}a+=1,await Z({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:a}).catch(()=>{}),await He(e,"a",{enter:!1,kind:"cursor-trust-accept"}),await zt(Wo);continue}if(t==="codex"&&a<3){let l=c$(c);if(l){if(!Bd(e,`codex-update:${l.from}->${l.to}`)){await zt(Mc);continue}a+=1,await Z({flow:"agent-ready",phase:"gate-accept",sessionName:e,agent:t,attempt:a,update:`${l.from} -> ${l.to}`}).catch(()=>{}),await He(e,"3",{enter:!1,kind:"codex-update-skip"}),sy({sessionName:e,from:l.from,to:l.to}),await zt(Wo);continue}}if(Date.now()>s){if(Fd(t,c)){let l=`${t} is blocked on an interactive prompt`;throw t==="codex"?l=Pe.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"&&Pe.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"&&Pe.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"&&Pe.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'),Z({flow:"agent-ready",phase:"blocked",sessionName:e,agent:t,timeoutMs:n,gateAccepts:a,error:l}).catch(()=>{}),new Error(l)}if(await mn(e)){let l=fy(t);throw Z({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.`),Z({flow:"agent-ready",phase:"timeout",sessionName:e,agent:t,timeoutMs:n,gateAccepts:a,paneBlocked:!!Pe[t]?.block?.test(c),paneUp:!!Pe[t]?.up?.test(c),paneBusy:!!Pe[t]?.busy?.test(hy(Pe[t],c))}).catch(()=>{}),await zt(Wo);return}await zt(Mc)}}function Hd({agent:e,agentCmd:t}={}){let n=t?Yn(t):Yn(e||"");return{label:n,realClaude:t?Br(t):n==="claude",realCodex:t?/codex/.test(t)&&!/fake-codex|fake-lead/.test(t):n==="codex",realCursor:t?/cursor/.test(t)&&!/fake-cursor|fake-box/.test(t):n==="cursor"}}function yy({agent:e,agentCmd:t}={}){let{realClaude:n,realCodex:r,realCursor:o}=Hd({agent:e,agentCmd:t});return n||r||o||process.env.BOSTRAT_FAKE_POLL_READY==="1"}function xa({cwd:e,sessionName:t,agent:n,agentCmd:r}={}){let{realCodex:o,realCursor:s}=Hd({agent:n,agentCmd:r});return!t||!o&&!s?null:er({cwd:e,sessionName:t,agent:n,agentCmd:r}).catch(()=>{})}async function er({cwd:e,sessionId:t,sessionName:n,agent:r,agentCmd:o,timeoutMs:s=e$(),bailOnDialog:a=!1,launchEcho:i=null}={}){let{label:c,realClaude:l,realCodex:u,realCursor:d}=Hd({agent:r,agentCmd:o});if(l&&e&&await Wn(e),u&&e&&await To(e),d&&e&&await Ao(e),(l||u||d)&&n){await py(n,c,s,{bailOnDialog:a,launchEcho:i});return}let f=Pe[r]?r:Pe[c]?c:null;if(process.env.BOSTRAT_FAKE_POLL_READY==="1"&&n&&f){await py(n,f,s,{launchEcho:i});return}await zt(Wo)}var e$,Mc,Wo,t$,n$,Pe,o$,s$,a$,i$,l$,Nc,u$,my,f$,p$,Sa=M(()=>{ze();Ys();tc();nc();Oc();Cc();Vt();e$=()=>Number(process.env.BOSTRAT_AGENT_READY_TIMEOUT_MS)||9e4,Mc=Number(process.env.BOSTRAT_AGENT_READY_POLL_MS)||500,Wo=Number(process.env.BOSTRAT_AGENT_READY_SETTLE_MS)||800,t$=/^\s{0,4}[^\s›•■◦╭╰│]\S*(?: \S+){0,3} · \S/,n$=/^\s*tab to queue message\b/;Pe={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:r$},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/}},o$=/Trust this workspace|\[a\]\s*Trust/,s$=/Do you trust the contents of this directory\?[\s\S]*›\s*1\. Yes, continue[\s\S]*Press enter to continue/,a$=/Resume from summary/i,i$=/Update available!\s*([\d][\w.-]*)\s*->\s*([\d][\w.-]*)/;l$=()=>Number(process.env.BOSTRAT_GATE_RESEND_MS)||3e3,Nc=new Map;u$=68,my=/^\s*[─━]{10,}\s*$/;f$=/\bWaiting for \d+\b[^\n]*\bto finish\b|\/workflows to (?:stop|monitor)\b/,p$=15});function y$(e){return typeof e!="string"||!e?!1:e.includes(`
|
|
82
|
+
`)?!0:Buffer.byteLength(e,"utf8")>=g$}function Jt(e,t){return e==="codex"?{enter:!0,submitDelayMs:h$,...y$(t)?{paste:!0}:{}}:e==="claude"?{enter:!0,submitDelayMs:w$}:{enter:!0}}var h$,w$,g$,va=M(()=>{h$=(()=>{let e=Number(process.env.BOSTRAT_CODEX_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),w$=(()=>{let e=Number(process.env.BOSTRAT_CLAUDE_SUBMIT_SETTLE_MS??120);return Number.isFinite(e)&&e>=0?e:120})(),g$=(()=>{let e=Number(process.env.BOSTRAT_CODEX_PASTE_MIN_BYTES??512);return Number.isFinite(e)&&e>=0?e:512})()});function Ta(e,t){if(e==="codex")return v$(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(by.test(n[c])){r=c;break}if(r<=0)return null;let o=-1;for(let c=r-1;c>=0;c--)if(by.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(b$);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 v$(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]){(k$.test(t[o])||x$.test(t[o]))&&!Wd.test(t[o])?n=o:Wd.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(Wd);if(a){r.unshift(a[1]);let i=r.map(c=>c.trim()).filter(Boolean);return i.length?i.join(" "):null}if(S$.test(s))return null;r.unshift(s)}return null}function qd(e,t){let n=String(t||"");if(!/\[Pasted Content \d+ chars\]/.test(n))return!1;let r=0,o=Ce(n.replace(T$,(i,c)=>(r+=Number(c)," "))),s=[...String(e||"")].length;return o&&!Ce(e).includes(o)?!1:r+o.length<=s+Math.max(4,Math.round(s*.05))}function Ce(e){return String(e||"").replace(/\s+/g," ").trim()}function ky(e,t,{slackMs:n=2e4}={}){let r=Ce(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&&Ce(o.text).includes(r))}function xy(e,t,n,{busy:r=!1}={}){let o=Ta(e,n);return o&&Dc(t,o)||o&&e==="codex"&&qd(t,o)?"stuck":r&&e!=="codex"||o?"submitted":"unknown"}function Dc(e,t){let n=Ce(e),r=Ce(t).replace(/^…\s*/,"").replace(/\s*…$/,"");return!n||!r||r.length<Math.min(n.length,12)?!1:n===r||n.includes(r)}var by,b$,Wd,k$,x$,S$,T$,Bc=M(()=>{by=/^\s*─{8,}\s*$/,b$=/^❯[ ]?(.*)$/,Wd=/^\s*›[ ]?(.*)$/,k$=/\S+ · \S/,x$=/^\s*(?:\? for shortcuts|tab to queue message)\b/,S$=/^\s*(?:[•■◦✶✻⚠]|╭|╰|│)/;T$=/\[Pasted Content (\d+) chars\](?: #\d+)?/g});async function Ea({sessionName:e,agent:t,agentCmd:n=null,text:r,trackId:o=null,flow:s="say"},a={}){if(!A$.has(t))return!0;let i=a.waitForUp??yy({agent:t,agentCmd:n}),c=a.capture||(_=>ve(_,{lines:Ue})),l=a.pressEnter||(_=>Ie(_,["Enter"],{kind:"submit-retry"})),u=a.sleep||R$,d=a.now||Date.now,f=a.log||(_=>Z(_).catch(()=>{})),p=a.maxMs??I$(),m=d()+p,w={flow:s,trackId:o,sessionName:e,agent:t,textHead:String(r).slice(0,120)},y=0,x=0,k=0,T=!1;for(await u(Aa[0]);;){let _=await c(e).catch(()=>""),P=Ko(t,_),N=qo(t,_),O=xy(t,r,_,{busy:N});if(O==="submitted")return!0;if(i&&(!P||O==="stuck"&&N)){if(d()>=m)return O==="stuck"?(f({...w,phase:"submit-failed",reason:"stuck-at-deadline",retries:y}),!1):(f({...w,phase:"submit-unverified",reason:T?"cli-down":"cli-never-up",retries:y,waitedMs:p}),!0);await u(E$);continue}if(T=!0,O==="stuck"){if(y>=_$)return f({...w,phase:"submit-failed",retries:y}),!1;y+=1,x=0,k=0,await l(e),f({...w,phase:"submit-retry",attempt:y}),await u(Aa[0]);continue}if(x+=1,x>=Aa.length||d()>=m)return!0;k+=1,await u(Aa[Math.min(k,Aa.length-1)])}}var A$,Aa,E$,_$,I$,R$,Kd=M(()=>{ze();Sa();Bc();Vt();A$=new Set(["claude","codex"]),Aa=[250,450,700],E$=500,_$=2,I$=()=>{let e=Number(process.env.BOSTRAT_SUBMIT_VERIFY_MAX_MS);return Number.isFinite(e)&&e>0?e:3e4},R$=e=>new Promise(t=>setTimeout(t,e))});function Wr(e){let t=String(e||"").toLowerCase();if(!t.trim())return null;let n=null;for(let r of $$){let o=0;for(let s of O$[r])s.test(t)&&o++;o>0&&(!n||o>n.score)&&(n={category:r,score:o})}return n}function Sy(e){let t=0,n=0;for(let[r,o]of Object.entries(e||{}))C$.has(r)&&(t+=o||0),M$.has(r)&&(n+=o||0);return t>=5&&n===0?{category:"research",score:1}:n>=3?{category:"feature-build",score:1}:null}function _a(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 $$,O$,C$,M$,Gd=M(()=>{$$=["bug-fix","orchestration","design","ops-infra","docs-writing","research","feature-build"],O$={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/]};C$=new Set(["Read","Grep","Glob","LS","WebSearch","WebFetch","NotebookRead"]),M$=new Set(["Edit","Write","MultiEdit","NotebookEdit"])});function Ey(e){let t=_a(String(e||"")).toLowerCase(),n=[];for(let r of t.split(/[^a-z0-9]+/))r.length<2||N$.has(r)||n.push(r);return n}function P$(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(Ay(n,"user",o))}else if(n.type==="turn.agent"){if(!o)continue;t.push(Ay(n,"assistant",o))}}return t}function Ay(e,t,n){let r=Wr(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 Ia(e){let t=P$(e),n=new Map,r=new Array(t.length).fill(0),o=0;t.forEach((a,i)=>{let c=Ey(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 jc(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(Ey(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,w]of d){let y=e.lengths[m],x=w+vy*(1-Ty+Ty*(y/(e.avgdl||1))),k=p*(w*(vy+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 vy,Ty,N$,Vd=M(()=>{Gd();vy=1.5,Ty=.75,N$=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 Lc from"node:fs/promises";import _y from"node:path";async function D$(e){let t=_y.join(e,".bostrat","events"),n;try{n=await Lc.readdir(t)}catch{return Ia([])}let r=[];for(let o of n)if(o.endsWith(".ndjson"))try{let s=await Lc.readFile(_y.join(t,o),"utf8");for(let a of s.split(`
|
|
85
|
+
`))if(a.trim())try{r.push(JSON.parse(a))}catch{}}catch{}return Ia(r)}function B$(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 j$(e,t,{k:n=6,maxChars:r=4e3,exclude:o=null}={}){let s=jc(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?["",`## ${Iy}, 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 Ry(e,t,n={}){let{k:r=6,maxChars:o=4e3,exclude:s=null}=n;try{let a=await Lc.readFile(t,"utf8");if(a.includes(Iy))return{appended:!1,reason:"already-enriched"};let i=await D$(e);if(!i.N)return{appended:!1,reason:"empty-index"};let c=j$(i,B$(a),{k:r,maxChars:o,exclude:s});return c?(await Lc.appendFile(t,`
|
|
88
88
|
`+c),{appended:!0,chars:c.length,chunks:c.split(`
|
|
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
|
|
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
|
|
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 Iy,$y=M(()=>{Vd();Iy="Relevant prior discussion (verbatim, retrieved"});function q$(e){return L$.test(String(e||"").trimStart())}function My(e){let t=String(e||"");if(!q$(t.trimStart()))return null;let n=t.replace(F$,"").trim(),r=K$(n),o=[];for(let u of H$){let d=u.from.map(f=>Go(r.get(f))).find(f=>f&&!Oy(f));d&&o.push({h:u.h,text:Ra(d,U$)})}let s=Go(r.get("Current Work"))||Go(r.get("Primary Request and Intent"))||V$(n),a=Uc(s),i=Ra(Fc(Yd(a[0]||"")),W$)||"Context resumed from summary",c=[],l=a.slice(1).join(" ").trim();l&&c.push(Ra(Fc(Yd(l)),Jd));for(let u of["Pending Tasks","Optional Next Step"]){let d=Uc(Go(r.get(u)))[0]||"";if(!(!d||Oy(d))){c.push(Ra(`Left to do: ${Fc(Yd(d))}`,Jd));break}}if(!c.length){let u=Uc(Go(r.get("Problem Solving"))||"")[0];u&&c.push(Ra(Fc(u),Jd))}return{kind:"resume-summary",title:i,bullets:c.slice(0,2),sections:o,rawChars:t.length}}function K$(e){let t=new Map,n=[];zd.lastIndex=0;for(let r=zd.exec(e);r;r=zd.exec(e))n.push({name:G$(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 G$(e){return Cy.find(n=>n.toLowerCase()===e.toLowerCase())||e}function Go(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 V$(e){let t=e.replace(/^[\s\S]*?\bSummary:\s*/i,"");return Go(t.split(/\n{2,}/).slice(0,2).join(" "))}function Uc(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 Yd(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 Fc(e){return String(e||"").replace(/(?<![:\w/])(?:\/[\w.@+-]+){4,}/g,t=>`\u2026/${t.split("/").slice(-2).join("/")}`)}function Oy(e){return/^(none|nothing|n\/a|no pending|there are no)\b/i.test(String(e||"").trim())}function Ra(e,t){let n=String(e||"").trim();if(n.length<=t)return n;let r=Uc(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 L$,Cy,zd,F$,U$,H$,W$,Jd,Ny=M(()=>{L$=/^This session is being continued from a previous conversation that ran out of context\./,Cy=["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"],zd=new RegExp(String.raw`^[ \t]*#{0,4}[ \t]*(?:\d+[.)][ \t]*)?\*{0,2}(${Cy.join("|")})[:*\t ]*$`,"gim"),F$=/\n+If you need specific details from before compaction[\s\S]*$|\n+Continue the conversation from where it left off[\s\S]*$/,U$=420,H$=[{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"]}],W$=116,Jd=168});function Dy(e){return Xd.has(String(e||"").replace(/^\//,"").toLowerCase())}function Hc(e){let t=String(e||"").trim();if(!t)return null;let n=My(t);if(n)return n;let r=t.match(z$);if(r)return{kind:"skill",name:r[1].split("/").filter(Boolean).pop()||"skill"};let o=t.match(J$);if(o)return{kind:"skill",name:o[1]};let s=t.match(Y$);if(s){let i=s[1].toLowerCase();return Xd.has(i)?{kind:"suppress"}:{kind:"command",command:`/${i}`}}let a=t.match(aO);if(a&&Xd.has(a[1].toLowerCase()))return{kind:"suppress"};if(X$.test(t))return{kind:"suppress"};if(Q$.test(t))return{kind:"suppress"};if(Z$.test(t))return{kind:"suppress"};if(eO.test(t)){let i=l=>iO(t.match(l)?.[1].trim()||"")||null,c=i(sO);return{kind:"task",summary:i(tO)||"",taskId:i(nO),toolUseId:i(rO),status:i(oO)?.toLowerCase()||null,resultHead:c?c.length>Py?`${c.slice(0,Py).trimEnd()}\u2026`:c:null}}return null}function iO(e){return e.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&")}function By(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 jy(e){return e?.kind==="skill"?"skill-run":e?.kind==="task"?"task-done":e?.kind==="resume-summary"?"context-resumed":"command-run"}var z$,J$,Y$,X$,Z$,Q$,eO,tO,nO,rO,oO,sO,Py,Xd,aO,Zd=M(()=>{Ny();z$=/^Base directory for this skill:\s*(\S+)/,J$=/^Run the "([^"]+)" workflow\./,Y$=/<command-name>\s*\/?([a-z0-9:_-]+)\s*<\/command-name>/i,X$=/^<local-command-(?:stdout|caveat)>/i,Z$=/^\[Image: original \d+x\d+, displayed at \d+x\d+\.\s*Multiply coordinates by [\d.]+ to map to original image\.\]$/,Q$=/^Continue from where you left off\.$/,eO=/^<task-notification>/i,tO=/<summary>([\s\S]*?)<\/summary>/i,nO=/<task-id>([\s\S]*?)<\/task-id>/i,rO=/<tool-use-id>([\s\S]*?)<\/tool-use-id>/i,oO=/<status>([\s\S]*?)<\/status>/i,sO=/<result>([\s\S]*?)(?:<\/result>|$)/i,Py=600,Xd=new Set(["model","clear","compact","login","exit","quit"]);aO=/^\/([a-z0-9:_-]+)$/i});import cO from"node:crypto";function fO(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,Fy)){let o=$a(r?.question);if(!o)return null;let s=Array.isArray(r?.options)?r.options:[],a=[];for(let i of s.slice(0,lO)){let c=$a(i?.label);if(!c)return null;let l=$a(i?.description);a.push(l?{label:c,description:l}:{label:c})}if(a.length<uO)return null;n.push({question:o,header:$a(r?.header).slice(0,dO),multiSelect:r?.multiSelect===!0,options:a})}return n.length?n:null}function ef(e){if(typeof e!="string"||!e.includes("```bostrat-question"))return null;Ly.lastIndex=0;let t=null;for(let o;o=Ly.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=fO(n);return r?{text:e.slice(0,t.index).trimEnd(),questions:r}:null}function tf(e,t,n){let r=cO.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,8);return`${e}:ask:${t||"turn"}:${r}`}function nf(e,{leadBoxId:t,provider:n,isAnswered:r,maxQuestions:o=Fy}={}){if(!Array.isArray(e)||!t||!Qd.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||ef(s.text)?.questions;if(!a?.length)return null;let i=a.slice(0,Math.max(1,o)),c=tf(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 Uy(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=$a(n),s;return r.length?r.length===1?s=`${r[0].name}: ${r[0].picks}`:s=`Answers:
|
|
91
91
|
${r.map(a=>`- ${a.name}: ${a.picks}`).join(`
|
|
92
92
|
`)}`:s=o,r.length&&o&&(s+=`
|
|
93
|
-
${o}`),s||""}var
|
|
93
|
+
${o}`),s||""}var Qd,Fy,lO,uO,dO,Ly,$a,Hy,rf=M(()=>{Qd=new Set(["cursor","codex"]),Fy=4,lO=4,uO=2,dO=24,Ly=/^[ \t]{0,3}(`{3,})bostrat-question[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]{0,3}\1`*[ \t]*$/gm,$a=e=>typeof e=="string"?e.trim():"";Hy=`
|
|
94
94
|
When you are BLOCKED on a decision only the operator can make \u2014 an ambiguous
|
|
95
95
|
requirement, an irreversible choice, several valid approaches \u2014 do not bury the
|
|
96
96
|
question in prose. End your message with exactly ONE fenced code block tagged
|
|
@@ -110,67 +110,67 @@ your turn. The operator sees the questions as an interactive card in their chat
|
|
|
110
110
|
answer. Never use the block for rhetorical questions, progress updates, or
|
|
111
111
|
anything you can decide yourself; keep asking in prose for questions without
|
|
112
112
|
enumerable options.
|
|
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<=
|
|
115
|
-
|
|
116
|
-
[\u2026 brief truncated]`}var
|
|
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?
|
|
120
|
-
`).trim():"",
|
|
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(
|
|
122
|
-
`),r=n.length-1;for(;r>=0&&n[r].trim()==="";)r--;if(r<0||!
|
|
123
|
-
`),o=[];for(let c=0;c<r.length;c++){let l=r[c].match(
|
|
124
|
-
`),o=-1;for(let u=r.length-1;u>=0;u--)if(
|
|
125
|
-
`),o
|
|
126
|
-
`),o
|
|
127
|
-
`),o="";for(let s=0;s<r.length;s++)if(/^\s*https:\/\//.test(r[s])){o=
|
|
128
|
-
`)].reverse().find(a=>
|
|
129
|
-
`),o
|
|
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(
|
|
131
|
-
`),o=
|
|
132
|
-
`).pop()||"")?!0:
|
|
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
|
|
134
|
-
${String(t||"")}`;return/Try the new fullscreen renderer|Flicker-free output/i.test(o)},keys:["Escape"]}];
|
|
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
|
|
136
|
-
`).map(s=>s.trim()).filter(Boolean)}async function
|
|
137
|
-
`).map(n=>n.trim()).filter(Boolean)}catch{return[]}}var
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
113
|
+
`});function pO(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 hO(e,t,n){let r=Wy(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 tr(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=(b,h)=>{u.set(b,h),t.push(h)},f=b=>{let h=u.get(b);h&&(h.retired=!0)},p=/^Agent "([\s\S]+)" finished$/,m=new Map;for(let b of e||[]){if(b.type!=="tool.called"||b.source!=="transcript")continue;let h=String(b.payload?.command||"");if(/\bbostrat-mock\s+(?:add|update)\b/.test(h))for(let v of h.matchAll(/[\w./~-]+\.(?:png|jpe?g|gif|webp|bmp|svg)\b/gi)){let g=v[0].split("/").pop().toLowerCase();m.has(g)||m.set(g,[]),m.get(g).push(b.payload?.ts??b.ts)}}let w=3*6e4,y=20*6e4,x=(b,h)=>!!(b?.name&&m.get(String(b.name).toLowerCase()))?.some(g=>g>=h-w&&g<=h+y);for(let b of e||[])if(b.type==="turn.retracted"){r.add(`${b.boxId}|${b.payload?.saidTs}`);let h=Ce(b.payload?.text);h&&(o.has(b.boxId)||o.set(b.boxId,[]),o.get(b.boxId).push(h))}else if(b.type==="turn.user"&&b.source==="orchestrator"){let h=Ce(b.payload?.text);h&&(s.has(b.boxId)||s.set(b.boxId,new Set),s.get(b.boxId).add(h))}else if(b.type==="job.started"&&b.payload?.id)a.add(b.payload.id),(b.payload.kind||"agent")==="agent"&&b.payload.name&&c.add(b.payload.name);else if(b.type==="turn.user"&&b.payload?.note?.kind==="task"){let h=b.payload.note,v={status:h.status||null,summary:h.summary||null,resultHead:h.resultHead??null,ts:le(b)};h.toolUseId&&i.set(h.toolUseId,v);let g=String(h.summary||"").match(p);g&&l.set(g[1],v)}let k=new Map,T=new Map,_=null,P=new Map,N=new Map,O=new Map,$=new Map,R=new Map,ee=new Map,de=new Map,be=new Map,G=new Map,I=new Map,J=new Map,X=new Map,he=new Map,Se=new Map,Le=new Map,F=()=>{for(let b of Le.values())b.questions.length&&t.push(b);Le.clear()},D=null,Q=6e4,Ne=new Map,Un=(b,h)=>{let v=o.get(b);if(!v?.length)return!1;let g=Ce(h),C=s.get(b);return v.some(se=>{if(!g.startsWith(se))return!1;let _e=Ce(g.slice(se.length));return!!_e&&!!C?.has(_e)})};for(let b of e||[]){let h=b.payload||{};switch(b.type){case"turn.user":{if(b.source==="hook"||!String(h.text||"").trim()&&!h.images?.length&&!h.attachments?.length)break;{let v=h.note||Hc(h.text);if(v){if(v.kind==="task"){if(v.toolUseId&&a.has(v.toolUseId))break;let g=String(v.summary||"").match(p);if(!v.toolUseId&&g&&c.has(g[1]))break}if(v.kind==="command"&&Dy(v.command))break;if(v.kind==="resume-summary"){t.push({id:b.id,kind:"resume-summary",title:v.title||"Context resumed from summary",bullets:v.bullets||[],sections:v.sections||[],rawChars:v.rawChars??null,ts:le(b),box:b.boxId,agent:b.provider});break}v.kind!=="suppress"&&t.push({id:b.id,kind:"event",type:jy(v),name:v.name??null,command:v.command??null,summary:v.summary??null,status:v.status??null,ts:le(b),box:b.boxId,agent:b.provider});break}}if(r.has(`${b.boxId}|${le(b)}`)||b.source==="transcript"&&Un(b.boxId,h.text))break;if(h.kind==="merge-notify"||h.kind==="ship-notify"){t.push({id:b.id,kind:"event",type:h.kind==="ship-notify"?"ship-notified":"merge-notified",text:h.text,ts:le(b),box:b.boxId,agent:b.provider});break}if(h.kind==="mock-picked"||h.kind==="ci-fix"||h.kind==="reconcile"||h.kind==="pr-go"||h.kind==="push-go"||h.kind==="spin-up-view"||h.kind==="prompt-answer"){h.kind==="prompt-answer"&&(D=null);break}for(let v of P.values())v.status="stale";P.clear();for(let v of O.values())v.status="stale";O.clear(),D&&D.box===b.boxId&&(t.push(pO(D)),D=null),t.push({id:b.id,kind:"message",role:"user",text:h.text,ts:le(b),box:b.boxId,agent:b.provider,boxRole:h.boxRole,...b.actor==="orchestrator"?{orchestration:h.kind||!0}:{},...h.sourceId?{sourceId:h.sourceId}:{},...h.attachments?.length?{attachments:h.attachments}:{},...h.images?.length?{images:h.images}:{}});break}case"peer.message":{let v=h.from||{};t.push({id:b.id,kind:"event",type:"peer-message",text:h.text,from:v.trackName||v.boxId||v.name||null,fromTrackId:v.trackId||null,ts:le(b),box:b.boxId,agent:b.provider});break}case"turn.agent":{if(h.model==="<synthetic>"&&/^No response requested\.$/.test(String(h.text||"").trim()))break;let v=Zg(h,{codexFailedText:Fr});if(v){t.push({id:b.id,kind:"event",type:"provider-error",cls:v.cls,status:v.status??void 0,text:v.text,provider:b.provider,legacy:!0,at:le(b),ts:le(b),box:b.boxId,agent:b.provider});break}F();let g=(h.images||[]).filter(Oe=>!x(Oe,le(b)));if(!String(h.text||"").trim()&&!g.length)break;if(b.provider==="cursor"){let Oe=`${b.boxId}|${h.text}`;if(n.has(Oe))break;n.add(Oe)}(!D||D.box===b.boxId)&&(D=null);let C=b.provider==="codex"?(h.tools||[]).filter(Oe=>!$d.has(Oe)&&!ya.has(Oe)):h.tools||[],se=h.text,_e=null;if(Qd.has(b.provider)){let Oe=ef(String(h.text||""));Oe&&(se=Oe.text,_e={questions:Oe.questions})}t.push({id:b.id,kind:"message",role:"assistant",text:se,ts:le(b),box:b.boxId,agent:b.provider,boxRole:h.boxRole,..._e?{ask:_e}:{},...C.length?{tools:C}:{},...h.model?{model:h.model}:{},...h.sourceId?{sourceId:h.sourceId}:{},...g.length?{images:g}:{}}),_e&&(D={id:b.id,groupId:tf(b.provider,h.sourceId||b.id,_e.questions),provider:b.provider,box:b.boxId,questions:_e.questions,ts:le(b)});break}case"job.started":{if(!h.id)break;let v=le(b),g=i.get(h.id);if(!g&&(h.kind||"agent")==="agent"&&h.name){let V=l.get(h.name);V&&V.ts>=v&&(g=V)}let C={jobId:h.id,jobKind:h.kind||"agent",name:h.name||null,detail:h.detail||null,status:g?g.status==="killed"?"stopped":g.status&&g.status!=="completed"?"failed":"done":"running",startedTs:v,finishedTs:g?.ts??null,summary:g?.summary??null,resultHead:g?.resultHead??null},se=Ne.get(b.boxId),_e=se?.jobs[se.jobs.length-1]?.startedTs;if(se&&_e!=null&&v-_e<=Q)se.jobs.push(C);else{let V={id:b.id,kind:"job-dock",dockId:h.id,ts:v,box:b.boxId,agent:b.provider,jobs:[C]};Ne.set(b.boxId,V),t.push(V)}break}case"handoff.done":t.push(St(b,"lead-handoff"));break;case"subtask.delegated":t.push({...St(b,"subtask-delegated"),to:b.boxId,toAgent:b.provider});break;case"subtask.merged":t.push(St(b,"subtask-merged"));break;case"pr.state.changed":{if(!h.change)break;if(h.change==="opened"){for(let g of P.values())g.status="stale";P.clear();for(let g of O.values())g.status="stale";O.clear()}let v=St(b,`pr-${h.change}`);if(v.box=b.boxId??null,mO.has(h.change)&&h.number!=null){let g=hO(k,T,h);if(k.size>=2){for(let se of T.values())for(let _e of se)_e.grouped=!0;v.groupPrs=[...k.values()];let C=[v];for(let se of k.keys())T.set(se,C)}else{let C=T.get(g)??[];C.push(v),T.set(g,C)}}else if(h.change==="merged"||h.change==="closed"||h.change==="lost"){let g=Wy(h),C=_&&!_.complete||k.size>=2;if(k.has(g))k.delete(g),T.delete(g);else if(h.number!=null)for(let se of[...k.keys()])se.endsWith(`#${h.number}`)&&(k.delete(se),T.delete(se));if(C&&h.number!=null){(!_||_.complete)&&(_={id:b.id,kind:"event",type:"pr-group-settled",prs:[],remaining:[],complete:!1,at:le(b),ts:le(b),box:b.boxId,agent:b.provider},t.push(_)),_.prs.push({number:h.number,url:h.url??null,title:h.title??null,repo:h.repo??null,branch:h.branch??null,change:h.change,ts:le(b)}),_.remaining=[...k.values()],_.complete=k.size===0;break}}t.push(v);break}case"pr.offered":{if(!h.offerId)break;for(let g of P.values())g.status="stale";P.clear();for(let g of O.values())g.status="stale";O.clear();let v={id:b.id,kind:"pr-offer",offerId:h.offerId,excerpt:h.excerpt||null,branch:h.branch||null,base:h.base||null,offered:Array.isArray(h.offered)&&h.offered.length?h.offered:null,toPr:h.toPr&&typeof h.toPr=="object"?{number:h.toPr.number??null}:null,available:Array.isArray(h.available)?h.available:null,defaults:Array.isArray(h.defaults)?h.defaults:null,prState:h.prState||null,openPr:h.openPr&&typeof h.openPr=="object"?h.openPr:null,repos:Array.isArray(h.repos)&&h.repos.length?h.repos:null,status:"open",ts:le(b),box:b.boxId,agent:b.provider};P.set(h.offerId,v),N.set(h.offerId,v),t.push(v);break}case"pr.offer.answered":{let v=N.get(h.offerId);v&&(v.status=h.action||"dismiss",v.actions=Array.isArray(h.actions)?h.actions:null,v.notify=h.notify===!0,h.existingPr&&typeof h.existingPr=="object"&&(v.existingPr={number:h.existingPr.number??null}),P.delete(h.offerId));break}case"pr.exists.offered":{if(!h.offerId)break;for(let g of O.values())g.status="stale";O.clear();let v={id:b.id,kind:"pr-exists-offer",offerId:h.offerId,srcOfferId:h.srcOfferId??null,branch:h.branch||null,base:h.base||null,pr:h.pr&&typeof h.pr=="object"?{number:h.pr.number??null,url:h.pr.url??null,title:h.pr.title??null}:null,actions:Array.isArray(h.actions)&&h.actions.length?h.actions:null,notify:h.notify===!0,status:"open",ts:le(b),box:b.boxId,agent:b.provider};O.set(h.offerId,v),$.set(h.offerId,v),t.push(v);break}case"pr.exists.answered":{let v=$.get(h.offerId);v&&(v.status=h.choice||"dismiss",O.delete(h.offerId));break}case"box.state.changed":{let v=h.change==="model"?"model-change":h.change;if(!v)break;let g=St(b,v);if(v==="model-change"&&(g.to=h.model),v==="node-update"&&g.latest){let C=t.findIndex(se=>se.kind==="event"&&se.type==="node-update"&&se.latest===g.latest);if(C>=0){let se=t[C];t[C]={...g,ts:se.ts,at:se.at,firstAt:se.firstAt??se.ts,updatedAt:g.ts};break}}t.push(g);break}case"track.transferred":case"track.recovered":t.push(St(b,"track-moved"));break;case"view.state.changed":{if(h.change==="deleted"){f(`view:${h.viewId}`);break}if(h.change!=="created"||h.restarted)break;d(`view:${h.viewId}`,{id:b.id,kind:"view-card",viewId:h.viewId,name:h.name,slug:h.slug,port:h.port,origin:h.origin,ts:le(b),box:b.boxId,agent:b.provider});break}case"view.spinup.failed":{t.push({id:b.id,kind:"view-spinup-failed",name:h.name||null,reason:h.reason||"no-view",ts:le(b),box:b.boxId,agent:b.provider});break}case"mock.state.changed":{if(h.change==="picked"){t.push({id:b.id,kind:"mock-picked",mockId:h.mockId,name:h.name,group:h.group??null,version:h.version??null,siblings:h.siblings??[],ts:le(b),box:b.boxId,agent:b.provider});break}if(h.change==="deleted"){f(`mock:${h.mockId}`);break}if(h.change!=="created")break;d(`mock:${h.mockId}`,{id:b.id,kind:"mock-card",mockId:h.mockId,name:h.name,group:h.group??null,ts:le(b),box:b.boxId,agent:b.provider});break}case"flow.state.changed":{if(h.change==="deleted"){f(`flow:${h.flowId}`);break}if(h.change!=="created")break;d(`flow:${h.flowId}`,{id:b.id,kind:"flow-card",flowId:h.flowId,name:h.name,stepCount:h.stepCount??null,mockIds:h.mockIds??[],ts:le(b),box:b.boxId,agent:b.provider});break}case"runbook.state.changed":{if(h.change==="deleted"){f(`runbook:${h.runbookId}`);break}if(h.change!=="created")break;d(`runbook:${h.runbookId}`,{id:b.id,kind:"runbook-card",runbookId:h.runbookId,name:h.name,ts:le(b),box:b.boxId,agent:b.provider});break}case"ticket.linked":{t.push({id:b.id,kind:"ticket-card",provider:h.provider||"linear",key:h.key,url:h.url,title:h.title??null,linkSource:h.linkSource??null,ts:le(b),box:b.boxId,agent:b.provider});break}case"box.fullscreen.detected":{if(!h.offerId)break;for(let g of de.values())g.box===(h.box||b.boxId)&&(g.status="stale",de.delete(g.offerId));let v={id:b.id,kind:"fullscreen-offer",offerId:h.offerId,status:"open",ts:le(b),box:h.box||b.boxId,agent:h.agent||b.provider};de.set(h.offerId,v),be.set(h.offerId,v),t.push(v);break}case"box.fullscreen.answered":{let v=be.get(h.offerId);v&&(v.status=h.action==="restarted"?"restarted":h.action==="cleared"?"cleared":"dismiss",de.delete(h.offerId));break}case"ticket.move.offered":{if(!h.offerId)break;for(let g of R.values())g.key===h.key&&(g.status="stale",R.delete(g.offerId));let v={id:b.id,kind:"ticket-offer",offerId:h.offerId,provider:h.provider||"linear",key:h.key,url:h.url??null,phase:h.phase??null,from:h.from??null,to:h.to?.name??null,prNumber:h.prNumber??null,status:"open",ts:le(b),box:b.boxId,agent:b.provider};R.set(h.offerId,v),ee.set(h.offerId,v),t.push(v);break}case"ticket.move.answered":{let v=ee.get(h.offerId);v&&(v.status=h.action==="moved"?"moved":"dismiss",R.delete(h.offerId));break}case"ticket.state.changed":{for(let v of R.values())v.key===h.key&&v.to===h.to&&(v.status="stale",R.delete(v.offerId));if(h.mode==="offer")break;t.push(St(b,"ticket-state"));break}case"ticket.commented":{t.push({id:b.id,kind:"ticket-comment",key:h.key,url:h.url??null,excerpt:h.excerpt??null,ts:le(b),box:b.boxId,agent:b.provider});break}case"ticket.flow.changed":t.push(St(b,"ticket-flow"));break;case"ticket.create.offered":{if(!h.proposalId)break;for(let g of G.values())g.status="stale";G.clear();let v={id:b.id,kind:"ticket-create-offer",proposalId:h.proposalId,provider:h.provider||"linear",epic:h.epic??null,issues:Array.isArray(h.issues)?h.issues:[],team:h.team??null,needsTeam:!!h.needsTeam,linkCurrentTrack:!!h.linkCurrentTrack,status:"open",ts:le(b),box:b.boxId,agent:b.provider};G.set(h.proposalId,v),I.set(h.proposalId,v),t.push(v);break}case"ticket.create.answered":{let v=I.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",v.createdCount=h.createdCount??null,v.failedCount=h.failedCount??null,G.delete(h.proposalId));break}case"loop.create.offered":{if(!h.proposalId)break;for(let g of J.values())g.status="stale";J.clear();let v={id:b.id,kind:"loop-create-offer",proposalId:h.proposalId,name:h.name??"",schedule:h.schedule??"",prompt:h.prompt??"",expectation:h.expectation??null,notify:h.notify??"off",assignment:h.assignment??{provider:"claude"},repo:h.repo??null,nodeId:h.nodeId??null,nodeName:h.nodeName??null,status:"open",ts:le(b),box:b.boxId,agent:b.provider};J.set(h.proposalId,v),X.set(h.proposalId,v),t.push(v);break}case"loop.create.answered":{let v=X.get(h.proposalId);v&&(v.status=h.action==="created"?"created":"dismiss",J.delete(h.proposalId));break}case"loop.created":{let v=h.proposalId?X.get(h.proposalId):null;v&&(v.created={loopId:h.loopId??null,nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null});break}case"loop.update.offered":case"loop.delete.offered":{if(!h.proposalId||!h.loopId)break;for(let C of he.values())C.loopId===h.loopId&&(C.status="stale",C.staleReason="superseded",he.delete(C.proposalId));let v=b.type==="loop.update.offered",g={id:b.id,kind:v?"loop-update-offer":"loop-delete-offer",proposalId:h.proposalId,loopId:String(h.loopId),name:h.name??h.base?.name??"",base:h.base??null,...v?{patch:h.patch??{},changed:Array.isArray(h.changed)?h.changed:Object.keys(h.patch||{})}:{reason:h.reason??""},selfEdit:!!h.selfEdit,status:"open",staleReason:null,ts:le(b),box:b.boxId,agent:b.provider};he.set(h.proposalId,g),Se.set(h.proposalId,g),t.push(g);break}case"loop.update.answered":{let v=Se.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="applied"?"applied":"dismiss",he.delete(h.proposalId));break}case"loop.delete.answered":{let v=Se.get(h.proposalId);v&&(h.action==="moved"?(v.status="stale",v.staleReason="moved"):v.status=h.action==="delete"?"deleted":h.action==="pause"?"paused":"keep",he.delete(h.proposalId));break}case"loop.updated":{let v=h.proposalId?Se.get(h.proposalId):null;v&&(v.applied={changed:Array.isArray(h.changed)?h.changed:[],nextFireAt:Number.isFinite(Number(h.nextFireAt))?Number(h.nextFireAt):null});break}case"loop.deleted":case"loop.paused":break;case"ticket.created":{let v={items:Array.isArray(h.items)?h.items:[],failed:Array.isArray(h.failed)?h.failed:[],team:h.team??null},g=h.proposalId?I.get(h.proposalId):null;if(g){g.created=v;break}t.push({id:b.id,kind:"ticket-created",...v,ts:le(b),box:b.boxId,agent:b.provider});break}case"error":{if(!h.kind)break;t.push(St(b,h.kind));break}case"provider.error":t.push({...St(b,"provider-error"),provider:b.provider,box:b.boxId,agent:b.provider});break;case"usage.cap.hit":t.push(St(b,"usage-cap-hit"));break;case"usage.cap.warning":t.push(St(b,"usage-cap-warning"));break;case"provider.connected":{t.push({id:b.id,kind:"event",type:"provider-connected",provider:h.provider||b.provider,ts:le(b),at:le(b),box:b.boxId,agent:b.provider});break}case"prompt.answered":{if(D=null,h.control||!h.question)break;let v=h.groupId||h.promptId;if(!v)break;let g=Le.get(v);g||(g={id:b.id,kind:"prompt",groupId:v,provider:b.provider,box:b.boxId,agent:b.provider,questionTotal:h.questionTotal??1,questions:[],ts:le(b)},Le.set(v,g));let C=h.questionIndex??0,se={index:C,header:h.header||"",question:h.question,multiSelect:!!h.multiSelect,chosen:Array.isArray(h.chosen)?h.chosen:[]},_e=g.questions.findIndex(V=>V.index===C&&Ce(V.question)===Ce(se.question));_e>=0?g.questions[_e]=se:g.questions.push(se),g.questions.sort((V,K)=>V.index-K.index),g.questionTotal=Math.max(g.questionTotal,h.questionTotal??1),g.questions.length>=g.questionTotal&&(t.push(g),Le.delete(v));break}default:break}}let Wt=u.size?t.filter(b=>!b.retired):t;return gO(Wt.sort((b,h)=>(b.ts??0)-(h.ts??0)))}function gO(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<=wO){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 of(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:yO(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??tr(e)).filter(o=>o.kind==="message");return qg(n,r)}function yO(e){let t=String(e.summary||"").trim();return t&&t!==e.command&&t!==Uo(e.command)?t:Qn(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:le(e),ts:le(e)}}function qy(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)+bO,s=n;for(;s+1<t.length;){let a=t[s+1];if((a?.ts??0)>o||a?.kind==="event"&&kO.has(a.type))break;s++}s>n&&t.splice(s,0,...t.splice(n,1)),n=s}return t}function Ky(e){let t=[...e];for(let n=0;n<t.length;n++){let r=t[n];if(r?.kind!=="event"||!SO.has(r.type)||!r.box)continue;let o=(r.ts??0)+xO,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 Gy(e){return(e||[]).filter(t=>vO.has(t?.kind)&&t.status==="open")}var le,mO,Wy,wO,bO,kO,xO,SO,vO,sf=M(()=>{Ho();Ur();Rc();Bc();Zd();rf();le=e=>e.payload?.ts??e.ts;mO=new Set(["opened","ci-fixed","reconciled","pushed"]),Wy=e=>`${e.repo??""}#${e.number}`;wO=2*6e4;bO=12e4,kO=new Set(["archived","unarchived","opened"]);xO=2500,SO=new Set(["pr-opened","pr-merged","pr-auto-armed"]);vO=new Set(["pr-offer","pr-exists-offer","ticket-offer","ticket-create-offer","loop-create-offer","loop-update-offer","loop-delete-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 $O(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 OO(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<EO&&(o.add(c.path),n.push(c.path)),c.head&&!s.has(c.head)&&r.length<_O&&(s.add(c.head),r.push(c.head))}return{files:n,heads:r}}function Yy(e,{trackName:t="this Track",now:n=null,mode:r="failover"}={}){let o=IO(e),s=$O(e),a=s?.payload?.content??null,i=s?s.payload?.ts??s.ts??null:null,c=tr(e??[]).filter(f=>f?.kind==="message"&&typeof f.text=="string"&&f.text.trim()).slice(-TO);if(!a&&!c.length)return null;let l=[];if(l.push(`# Reconstructed resume brief \u2014 ${t}`),l.push(""),l.push(Jy[r]??Jy.failover),l.push(""),o.lastPush?.sha){let f=zy(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=zy(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=OO(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"}:** ${RO(f.text,AO)}`),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<=Vy?d:`${d.slice(0,Vy)}
|
|
115
|
+
|
|
116
|
+
[\u2026 brief truncated]`}var TO,AO,Vy,EO,_O,RO,zy,Jy,Xy=M(()=>{sf();TO=12,AO=2e3,Vy=48e3,EO=30,_O=10;RO=(e,t)=>{let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1)}\u2026`},zy=(e,t)=>e&&t?Math.max(1,Math.round((e-t)/6e4)):null;Jy={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 CO from"node:fs";import nr from"node:fs/promises";import rr from"node:path";function sb(e){let t=Dr(String(e??"")),n=Buffer.byteLength(t,"utf8");return n<=Zy?{content:t,bytes:n,truncated:!1}:{content:`${Buffer.from(t,"utf8").subarray(0,Zy).toString("utf8").replace(/�+$/,"")}
|
|
117
|
+
|
|
118
|
+
[\u2026 truncated by bostrat at 32 KiB \u2014 full artifact in the workspace]`,bytes:n,truncated:!0}}function UO(e){let t=String(e||"").split(/\r?\n/).map(s=>s.trim()).filter(Boolean),n=[],r=[];for(let s of t)if(eb.test(s)){if(r.length>=db)continue;let a=FO(s.replace(eb,"").replace(/[.;,:]+$/,""),fb,jO);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>Qy&&(o=`${o.slice(0,Qy-1).trimEnd()}\u2026`),{text:o,items:r.length>1?r:[]}}async function pb(e,t,{timeoutMs:n=9e4}={}){let r=rr.join(t,DO),o=rr.join(r,BO);await nr.mkdir(r,{recursive:!0});let s=await Wc(o),a=await ie(e);if(!a)throw new Error(`session "${e}" not found`);return await qc(e,a,LO(o)),await kb(o,s,n),UO(await nr.readFile(o,"utf8"))}function mb(e,t){let n=(t||"").trim();if(!n)return!1;if(e==="assistant")return n===ib||n===ub;let r=n.replace(/^\/[a-z0-9:_-]+\s+/i,"");return r.startsWith(ab)||n===MO||r.startsWith(cb)||r.startsWith(lb)}function hb(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-HO;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 wb(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 gb(e,{windows:t=[]}={}){if(mb(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 Kc(e){return af.get(e)||null}async function yb(e){let t=af.get(e);if(t&&!t.finishedAt)throw new Error("a handoff is already running for this session");let n=await ie(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:rr.join(n.cwd,rb,ob)};return af.set(e,r),KO(r,n.cwd).catch(o=>{r.error=o?.message||String(o),r.finishedAt=Date.now()}),r}async function KO(e,t){e.step="summarize",await bb(e.name,t),e.step="clear";let n=await ie(e.name);await qc(e.name,n,"/clear"),await uf(WO),e.step="resume",await qc(e.name,n,PO),e.step="done",e.finishedAt=Date.now()}async function bb(e,t,{timeoutMs:n=qO}={}){let r=rr.join(t,rb),o=rr.join(r,ob);await nr.mkdir(r,{recursive:!0});let s=await Wc(o),a=await ie(e);await Z({flow:"checkpoint",phase:"summarize-start",sessionName:e,cwd:t,file:o,beforeMtime:s}),await qc(e,a,NO(o)),await kb(o,s,n),await Z({flow:"checkpoint",phase:"summarize-done",sessionName:e,file:o,afterMtime:await Wc(o)});let i=await Ry(t,o,{exclude:u=>mb(u.role,u.text)});await Z({flow:"checkpoint",phase:"seed-enriched",sessionName:e,file:o,...i});let c=/workspaces[/\\]([0-9a-f]{8})$/.exec(t||"")?.[1];if(c){await A({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 nr.readFile(o,"utf8");await A({id:c,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...sb(u),ts:Date.now()}})}catch{}}await ad(t);let l=await GO(r,o);return c&&await wg(c,l||o,{box:e,agent:a?.agent??null}),o}async function cf(e,{timeoutMs:t}={}){let n=await ie(e);if(!n)throw new Error(`session "${e}" not found`);if(!n.cwd)throw new Error(`can't resolve cwd for "${e}"`);return bb(e,n.cwd,t?{timeoutMs:t}:void 0)}async function lf(e,t,{trackName:n="this Track"}={}){let r=await j({id:e,cwd:t}),o=Yy(r,{trackName:n,now:Date.now(),mode:"in-place"});if(!o)return null;let s=rr.join(t,tb);return await nr.mkdir(rr.dirname(s),{recursive:!0}),await nr.writeFile(s,o,"utf8"),await A({id:e,cwd:t},{type:"handoff.artifact",actor:"orchestrator",payload:{...sb(o),reconstructed:!0,ts:Date.now()}}).catch(()=>{}),tb}async function Wc(e){try{return(await nr.stat(e)).mtimeMs}catch{return 0}}async function kb(e,t,n){let r=Date.now()+n;for(;;){if(await Wc(e)>t)return;if(Date.now()>r)throw new Error(`timed out waiting for the handoff file to be written (${e})`);await uf(2e3)}}async function GO(e,t){try{if(!CO.existsSync(t))return null;let n=new Date().toISOString().replace(/[:.]/g,"-"),r=rr.join(e,`${n}.md`);return await nr.copyFile(t,r),r}catch{return null}}function uf(e){return new Promise(t=>setTimeout(t,e))}function nb(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 qc(e,t,n){if(t){let o=nb(t.agent);await er({cwd:t.cwd,sessionId:t.sessionId,sessionName:e,agent:t.agent,agentCmd:o})}let r=await ie(e);if(!r)throw new Error(`session "${e}" disappeared before the orchestration prompt could be sent`);await Ie(e,["Escape"],{kind:"drive-wipe"}),await uf(150),await Bo(e,Ec,{kind:"drive-wipe"}),await He(e,n,{kind:"orchestration-drive",...Jt(r.agent,n)}),await Ea({sessionName:e,agent:r.agent,agentCmd:nb(r.agent),text:n,flow:"orchestration-drive"})}var rb,ob,Zy,ab,ib,cb,MO,NO,PO,lb,ub,DO,BO,Qy,db,fb,jO,LO,eb,FO,HO,WO,qO,af,tb,df=M(()=>{ze();Sa();Vt();va();Kd();$y();ra();ce();yc();Xy();rb=".bostrat/handoff",ob="latest.md",Zy=32*1024;ab="Checkpoint: write a work-in-progress handoff",ib="HANDOFF WRITTEN",cb="Resume from .bostrat/handoff/latest.md",MO="/clear",NO=e=>`${ab} 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 ${ib} and take no further action.`,PO=`${cb}: read it, restate the Objective and the single next step in one line, then continue the work from 'Next steps'.`,lb="Beat: write a",ub="BEAT WRITTEN",DO=".bostrat/arc",BO="beat.txt",Qy=200,db=4,fb=5,jO=36,LO=e=>`${lb} 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 ${db} further lines, each starting with "- " and naming ONE piece in at most ${fb} 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 ${ub} and take no further action.`,eb=/^(?:[-–—*•·]|\d+[.)])\s+/,FO=(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};HO=6e5;WO=3500,qO=3e5,af=new Map;tb=".bostrat/handoff/status-latest.md"});function sr(e,t,n){if(e==null)return t==="from"?-1/0:1/0;if(!Gc.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 vb(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:sr(o.from,"from",n),toMs:sr(o.to,"to",n)}))}return t}function pf(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 zO(e){if(!e)return null;let t=String(e).trim();return or[t]?t:xb[t]?xb[t]:Object.keys(or).find(r=>t.startsWith(r))||null}function JO(e){if(!e||e===or)return VO;let t=Sb.get(e);return t||(t=vb(e),Sb.set(e,t)),t}function Tb(e,t,n={}){let{table:r,speed:o}=n,s=JO(r),a=r&&r!==or?s[e]?e:null:zO(e);if(!a)return null;let i=pf(t);if(i===null)return null;let c=s[a].find(l=>i>=l.fromMs&&i<=l.toMs)||null;return c?o==null||o==="standard"?c:o==="fast"&&c.fast?{...c.fast,fromMs:c.fromMs,toMs:c.toMs,verifiedAt:c.verifiedAt,note:c.note,speed:"fast"}:null:null}function Yt(e,t={},n,r){let o=Tb(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 YO(e=or){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:sr(a.from,"from",n),toMs:sr(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`),Gc.test(String(a.verifiedAt??""))||t.push(`${n}: every window needs a verifiedAt (YYYY-MM-DD) \u2014 the date its rates were checked against the vendor's published page`);for(let i of["input","output","cacheWrite","cacheRead"])(typeof a[i]!="number"||!Number.isFinite(a[i])||a[i]<0)&&t.push(`${n}: window ${i} must be a non-negative number, got ${JSON.stringify(a[i])}`),a.fast&&(typeof a.fast[i]!="number"||!Number.isFinite(a.fast[i])||a.fast[i]<0)&&t.push(`${n}: fast variant ${i} must be a non-negative number, got ${JSON.stringify(a.fast?.[i])}`)}}return t}function ZO({now:e=Date.now(),table:t=or,...n}={}){let{staleDays:r,leadDays:o,confirmDays:s}={...XO,...n},a=[],i=pf(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:sr(d.from,"from",c),toMs:sr(d.to,"to",c),verifiedMs:Gc.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*ff;f&&i-d.verifiedMs>r*ff&&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*ff&&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 Vo({now:e=Date.now(),table:t=or,...n}={}){let r=pf(e)??Date.now(),o=null,s=[];for(let[i,c]of Object.entries(t))for(let l of c){let u=sr(l.from,"from",i),d=sr(l.to,"to",i);r>=u&&r<=d&&Gc.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=[...YO(t),...ZO({now:r,table:t,...n})];return{tableVerifiedAt:o,upcomingChanges:s,problems:a,ok:a.length===0}}var or,xb,Gc,VO,Sb,Ab,XO,ff,wn=M(()=>{or={"claude-opus-5":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"GA rates; verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-opus-4-8":[{from:null,to:null,input:5,output:25,cacheWrite:10,cacheRead:.5,fast:{input:10,output:50,cacheWrite:20,cacheRead:1},verifiedAt:"2026-08-20",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page); fast-mode variant added 2026-08-20."}],"claude-sonnet-5":[{from:null,to:null,input:2,output:10,cacheWrite:4,cacheRead:.2,verifiedAt:"2026-08-17",note:"$2/$10 is the standard price (intro pricing made permanent; the 2026-09-01 $3/$15 increase was cancelled) \u2014 verified 2026-08-17 against platform.claude.com/docs/en/about-claude/pricing."}],"claude-sonnet-4-6":[{from:null,to:null,input:3,output:15,cacheWrite:6,cacheRead:.3,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-haiku-4-5":[{from:null,to:null,input:1,output:5,cacheWrite:2,cacheRead:.1,verifiedAt:"2026-08-17",note:"verified 2026-08-06 (claude-api skill), re-verified 2026-08-17 (live pricing page)."}],"claude-fable-5":[{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)."}]},xb={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"},Gc=/^\d{4}-\d{2}-\d{2}$/;VO=vb(or);Sb=new WeakMap;Ab=(e,t,n)=>Tb(e,t,n)!==null;XO=Object.freeze({staleDays:90,leadDays:14,confirmDays:30}),ff=864e5});function QO(e){return e?{input:e.input||0,output:e.output||0,cacheRead:e.cacheRead||0,cacheWrite:e.cacheWrite??e.cacheCreation??0}:null}function _b(e,t){t&&(e.input+=t.input,e.output+=t.output,e.cacheRead+=t.cacheRead,e.cacheCreation+=t.cacheWrite)}function gn(e){let t=e.payload?.model||"unknown",n=QO(e.payload?.tokens),r=Number(e.payload?.usd)||0,o=e.payload?.speed||void 0,s=e.source==="vendor",a=r,i=!1;if(n&&!s){let u=Yt(t,n,e.ts,o?{speed:o}:void 0);u>0&&(a=u,i=Math.abs(u-r)>1e-9)}let c=n?n.input+n.output+n.cacheRead+n.cacheWrite:0,l=!a&&c>0&&!Ab(t,e.ts,o?{speed:o}:void 0);return{usd:a,model:t,tokens:n,repriced:i,unpriced:l,asRecorded:r}}function ar(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=Eb();for(let f of n){let{usd:p,model:m,tokens:w,repriced:y,unpriced:x,asRecorded:k}=gn(f);i+=k;let T=f.provider||"unknown",_=f.boxId||"unattributed";if(y&&c++,x&&(l++,u.add(m)),a+=p,_b(d,w),r[m]||={usd:0,tokens:Eb(),events:0},r[m].usd+=p,r[m].events+=1,_b(r[m].tokens,w),o[T]||={usd:0,events:0},o[T].usd+=p,o[T].events+=1,s[_]||={usd:0,events:0,provider:T,models:new Set,label:t[_]||_},s[_].usd+=p,s[_].events+=1,m!=="unknown"&&s[_].models.add(m),f.epoch!=null){let P=(s[_].epochs||={})[f.epoch]||={usd:0,events:0,provider:T,models:new Set};P.usd+=p,P.events+=1,P.provider=T,m!=="unknown"&&P.models.add(m)}}return{totalUsd:zo(a),asRecordedUsd:zo(i),totalTokens:d,turns:n.length,repricedTurns:c,unpricedTurns:l,unpricedModels:[...u],byModel:Vc(r,f=>({...f,usd:zo(f.usd)})),byProvider:Vc(o,f=>({...f,usd:zo(f.usd)})),byBox:Vc(s,f=>({usd:zo(f.usd),events:f.events,provider:f.provider,label:f.label,models:[...f.models],...f.epochs?{epochs:Vc(f.epochs,p=>({usd:zo(p.usd),events:p.events,provider:p.provider,models:[...p.models]}))}:{}}))}}var Eb,zo,Vc,qr=M(()=>{wn();Eb=()=>({input:0,output:0,cacheRead:0,cacheCreation:0});zo=e=>Math.round(e*1e4)/1e4,Vc=(e,t)=>Object.fromEntries(Object.entries(e).map(([n,r])=>[n,t(r)]))});function eC(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 Ib(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(mf(s,"PR")+" opened");let a=Object.keys(e?.subtasks||{}).length||r.filter(u=>u.type==="subtask-delegated").length;a&&n.push(mf(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(`${mf(i.length,"handoff")} ${u}${i.length>3?"\u2026":""}`)}let c=ar(t);c.totalUsd>=.01&&n.push(`$${c.totalUsd.toFixed(2)}`);let l=Number.isFinite(e?.archivedAt)&&Number.isFinite(e?.createdAt)?eC(e.archivedAt-e.createdAt):null;return n.length&&l&&n.push(l),n.length?n.join(" \xB7 "):null}var mf,Rb=M(()=>{qr();mf=(e,t)=>`${e} ${t}${e===1?"":"s"}`});import{execFile as tC}from"node:child_process";import{promisify as nC}from"node:util";import{randomUUID as rC}from"node:crypto";async function hf(e,t){let n=Date.now()+t;for(;;){if(await mn(e))return!0;if(Date.now()>n)return!1;await Oa(sC)}}async function iC(e,t){let n=Ob[t]||Ob.default;n.escape&&(await Ie(e,["Escape"],{kind:"swap-exit"}),await Oa(150)),n.wipe&&await Bo(e,Ec,{kind:"swap-exit"}),await _c(e,n.text,{perCharDelayMs:90,kind:"swap-exit"});let r=Jt(t).submitDelayMs||0;await Oa(Math.max(r,250)),await Ie(e,["Enter"],{kind:"swap-exit"})}async function Cb(e,t){try{let{stdout:n}=await Kr(L,U("show-environment","-t",e,t));return!String(n||"").trimStart().startsWith("-")}catch{return!1}}async function Mb(e){await Kr(L,U("respawn-pane","-k","-t",e))}async function wf(e,t={}){if(!Ee(e))throw new Error("invalid session name");let n=String(t.agentCmd||"").trim();if(!n)throw new Error("agentCmd is required");let r=await ie(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||Yn(n),a=Number(t.exitTimeoutMs)||oC;await Z({flow:"swap",phase:"start",sessionName:e,from:o,to:s,cwd:r.cwd});let i=!1;if(!await mn(e)&&(await iC(e,o),!await hf(e,a)&&(i=!0,await Z({flow:"swap",phase:"respawn-forced",sessionName:e,from:o}),await Mb(e),!await hf(e,$b))))throw new Error(`pane shell did not come back after respawn-pane in "${e}"`);let c=s==="claude"&&Br(n),l=c?Qi():null,u=await Cb(e,"ANTHROPIC_API_KEY"),d=!1;try{l?(await Kr(L,U("set-environment","-t",e,"ANTHROPIC_API_KEY",l)),d=!0):u&&(await Kr(L,U("set-environment","-u","-t",e,"ANTHROPIC_API_KEY")),d=!0)}catch(y){throw l?kc(y,l):y}if(d&&!i&&(await Mb(e),!await hf(e,$b)))throw new Error(`pane shell did not come back after env respawn in "${e}"`);let f=!!t.resumeSessionId,p=t.resumeSessionId||rC(),m=f?n:wd(n,p,e),w=f?t.pinned===!0:/(^|\s)--session-id(\s|=)/.test(m);return c&&await Wn(r.cwd),s==="codex"&&await To(r.cwd),s==="cursor"&&await Ao(r.cwd),await jr(e),await Kr(L,U("set-option","-t",e,"@bostrat-sid",p)),await Kr(L,U("set-option","-t",e,"@bostrat-agent",s)),await Oa(aC),c&&da().length&&(await Cb(e,ua)||await Kr(L,U("set-environment","-t",e,ua,"1")).catch(()=>{}),await He(e,`export ${ua}=1`,{kind:"swap-env"}),await Oa(150)),await He(e,m,{kind:"swap-launch"}),await Z({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:w,forced:i,envRespawned:d,launchCmd:m}}var Kr,Oa,oC,$b,sC,aC,Ob,Nb=M(()=>{ze();Ys();tc();nc();va();Vt();Kr=nC(tC),Oa=e=>new Promise(t=>setTimeout(t,e)),oC=Number(process.env.BOSTRAT_SWAP_EXIT_TIMEOUT_MS)||15e3,$b=Number(process.env.BOSTRAT_SWAP_SHELL_TIMEOUT_MS)||8e3,sC=250,aC=400,Ob={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 Pb(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?Uo(p):null),detail:p?Uo(p):null,ts:d,proven:!0})}}else f.type==="tool_result"&&f.tool_use_id&&cC.test(lC(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 cC,lC,Db=M(()=>{Pt();Ho();cC=/^Async (?:agent|task) launched/i,lC=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 ir(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 uC(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 Bb(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 ${ir(r,n)}${o?` (lines ${o}\u2013${s??"end"})`:""}`,140),description:o?`Read lines ${o}\u2013${s??"end"} of ${Jo(r)}`:`Read ${Jo(r)}`}}case"Grep":{let r=t?.pattern;if(!r)return null;let o=t?.path?ir(t.path,n):null,s=t?.glob?` --glob ${gf(t.glob)}`:"",a=Dt(String(r),40);return{command:Dt(`grep ${gf(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?ir(t.target_directory,n):null;return{command:Dt(`glob ${gf(r)}${o&&o!=="."?` ${o}`:""}`,140),description:`Find files matching \u201C${Dt(String(r),40)}\u201D`}}case"StrReplace":return t?.path?{command:Dt(`edit ${ir(t.path,n)}`,140),description:`Edit ${Jo(t.path)}`}:null;case"Write":return t?.path?{command:Dt(`write ${ir(t.path,n)}`,140),description:`Write ${Jo(t.path)}`}:null;case"Delete":return t?.path?{command:Dt(`delete ${ir(t.path,n)}`,140),description:`Delete ${Jo(t.path)}`}:null;case"ReadLints":{let o=(Array.isArray(t?.paths)?t.paths:[]).map(s=>ir(s,n)).filter(Boolean);return{command:Dt(o.length?`lints ${o.join(" ")}`:"lints",140),description:"Check lints"}}case"ApplyPatch":{let r=uC(t);if(!r.length)return null;let o=r.length>1?` (+${r.length-1} more)`:"";return{command:Dt(`apply_patch ${ir(r[0],n)}${o}`,140),description:r.length>1?`Patch ${r.length} files`:`Patch ${Jo(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 zc,Dt,Jo,gf,jb=M(()=>{zc=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,Jo=e=>String(e).replace(/\/+$/,"").split("/").pop()||String(e);gf=e=>`"${String(e).replace(/"/g,'\\"')}"`});import Ca from"node:fs";import dC from"node:fs/promises";import Gr from"node:path";import fC from"node:os";import pC from"node:crypto";import mC from"node:zlib";function Ub(e){let t=pC.createHash("md5").update(String(e)).digest("hex");return Gr.join(hC,t)}function Hb(e){try{let t=JSON.parse(Ca.readFileSync(Gr.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 wC(e){let t=Ub(e),n;try{n=Ca.readdirSync(t,{withFileTypes:!0}).filter(o=>o.isDirectory())}catch{return null}let r=null;for(let o of n){let s=Gr.join(t,o.name),a=Gr.join(s,"store.db");if(!Ca.existsSync(a))continue;let i=Hb(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 Jc(e,t){if(!e)return null;if(t){let n=Gr.join(Ub(e),t),r=Gr.join(n,"store.db");if(Ca.existsSync(r))return{id:t,dir:n,store:r,...Hb(n)||{createdAtMs:null,updatedAtMs:null}}}return wC(e)}function Lb(e,t){let n=e.prepare("SELECT data FROM blobs WHERE id = ?").get(t);return n?Buffer.from(n.data):null}function gC(e){if(!e)return null;let t=e;if(t.length>1&&t[0]===31&&t[1]===139)try{t=mC.gunzipSync(t)}catch{}try{return JSON.parse(t.toString("utf8"))}catch{return null}}function yC(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 bC(e,t){if(!Array.isArray(e))return[];let n=[];for(let r of e){if(!r||r.type!=="tool-call"||!zc.has(r.toolName))continue;let o=Bb(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 yf(e,t){if(!Fb)return Yo;let{DatabaseSync:n}=Fb,r;try{r=new n(e,{readOnly:!0})}catch{return Yo}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 Yo;let s;try{s=JSON.parse(Buffer.from(String(o.value),"hex").toString("utf8"))}catch{return Yo}let a=s.lastUsedModel||null,i=s.latestRootBlobId?Lb(r,s.latestRootBlobId):null;if(!i)return{...Yo,model:a};let c=[],l=[],u=[],d=null,f=[];for(let p of yC(i)){let m=gC(Lb(r,p));if(!m)continue;let w=m.providerOptions?.cursor;if(w?.requestId&&f.push(w.requestId),w?.modelProviderMessageId&&f.push(w.modelProviderMessageId),m.role!=="assistant")continue;if(Array.isArray(m.content))for(let k of m.content){let T=k?.providerOptions?.cursor?.modelName;T&&(d=T)}l.push(...bC(m.content,t).map((k,T)=>({...k,id:k.id||`${p}:${T}`}))),u.push(...xC(m.content));let y=kC(m.content);if(!y)continue;let x=[...new Set(u)];u=[],c.push({id:p,text:y,tools:x.length?x:void 0,commands:l,model:d}),l=[]}return{turns:c,trailingCommands:l,model:a,requestIds:f}}catch{return Yo}finally{try{r.close()}catch{}}}function Wb(e,t){let n=e.updatedAtMs;if(!n)try{n=Math.round(Ca.statSync(e.store).mtimeMs)}catch{n=Date.now()}let r=e.createdAtMs;(!r||r>n)&&(r=n-t*1e3);let o=Math.max(0,n-r);return{end:n,tsAt:s=>Math.round(r+o*(s+1)/t)}}async function qb(e,t,{limit:n=80}={}){let r=Jc(e,t);if(!r)return[];let{turns:o,model:s}=await yf(r.store,e);if(!o.length)return[];let a=o.slice(-n),{tsAt:i}=Wb(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 Kb(e,t){let n=Jc(e,t);if(!n)return null;let{turns:r,requestIds:o,model:s}=await yf(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 Gb(e,t,{limit:n=80}={}){let r=Jc(e,t);if(!r)return[];let{turns:o,trailingCommands:s}=await yf(r.store,e);if(!o.length&&!s.length)return[];let a=o.slice(-n),{end:i,tsAt:c}=Wb(r,a.length||1),l=[];a.forEach((u,d)=>{for(let f of u.commands)l.push({...f,summary:Qn(f.command,f.description),ts:c(d)})});for(let u of s)l.push({...u,summary:Qn(u.command,u.description),ts:i+1});return l}async function Vb(e,t){let n=Jc(e,t);if(!n)return null;if(n.updatedAtMs)return n.updatedAtMs;try{return Math.round((await dC.stat(n.store)).mtimeMs)}catch{return null}}var hC,kC,xC,Fb,Yo,bf=M(()=>{Ho();jb();hC=process.env.BOSTRAT_CURSOR_CHATS||Gr.join(fC.homedir(),".cursor","chats");kC=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():"",xC=e=>Array.isArray(e)?e.filter(t=>t&&t.type==="tool-call"&&t.toolName).map(t=>t.toolName):[],Fb=process.getBuiltinModule?.("node:sqlite"),Yo={turns:[],trailingCommands:[],model:null,requestIds:[]}});import SC from"node:fs/promises";function vf(e){return zb.set(e.agent,e),e}function Ma(e){return zb.get(e)||vC}async function Af(e,t={}){return(await Ma(e.agent).readMessages(e,t)).map(Tf(e))}async function Jb(e,t={}){return(await Ma(e.agent).readCommands(e,t)).map(Tf(e))}async function Yb(e,t={}){return(await Ma(e.agent).readJobs(e,t)).map(Tf(e))}var Xo,kf,xf,Sf,vC,zb,Tf,Xb=M(()=>{Pt();$c();Ho();Db();bf();Ur();Xo=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}},kf=class extends Xo{transcript(t){return!t?.cwd||!t?.sessionId?null:Nt(t.cwd,t.sessionId)}async readMessages(t,n){let r=this.transcript(t);return r?Cd(r,n):[]}async readCommands(t,n){let r=this.transcript(t);return r?Wg(r,n):[]}async readJobs(t,n){let r=this.transcript(t);return r?Pb(r,n):[]}async updatedAt(t){let n=this.transcript(t);if(!n)return null;try{return Math.round((await SC.stat(n)).mtimeMs)}catch{return null}}},xf=class extends Xo{async readMessages(t,n){return qb(t.cwd,t.sessionId,n)}async readCommands(t,n){return Gb(t.cwd,t.sessionId,n)}async updatedAt(t){return Vb(t.cwd,t.sessionId)}},Sf=class extends Xo{sessionId(t){return t?.pinned?t.sessionId:null}async readMessages(t,n){return Vg(t.cwd,this.sessionId(t),n)}async readCommands(t,n){return zg(t.cwd,this.sessionId(t),n)}async updatedAt(t){return Jg(t.cwd,this.sessionId(t))}},vC=new Xo("unknown"),zb=new Map;vf(new kf("claude",_d));vf(new xf("cursor",zc));vf(new Sf("codex",ya));Tf=e=>t=>({...t,box:e.name,agent:e.agent,boxRole:e.role})});function Jr(e){let t=e.search(Rf);return(t>=0?e.slice(0,t):e).trim()}function TC(e){if(!e)return"";let t=e.replace(/[│┌└├─┘┤┬┴┼─✂].*$/,"").trim();return!t||/hidden|\d+\s+lines?/i.test(t)?"":t}function AC(e){let t=String(e).trim();return!!(!t||zr.test(e)||Rf.test(t)||/hidden|\d+\s+lines?/i.test(t)||/notespress|press n to add/i.test(t)||/^\s*notes?\s*$/i.test(t))}function EC(e,t=""){let n=[],r=/([☐☒])\s+([^☐☒←→✔]+)/g,o;for(;(o=r.exec(e))!==null;)n.push({label:o[2].trim(),answered:o[1]==="\u2612",current:!1});if(!n.length)return null;let s=_C(n,t);return n.forEach((a,i)=>{a.current=i===s}),{tabs:n,questionIndex:s,questionTotal:n.length,currentTabLabel:n[s].label}}function _C(e,t){let n=String(t).toLowerCase(),r=e.findIndex(c=>!c.answered),o=r>=0?r:e.length-1;if(!n)return o;let s=e.map(c=>{let l=c.label.toLowerCase(),u=l.split(/\s+/).filter(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 Qb(e){return e.replace(/[←→✔☐☒●▶]/g," ").replace(/\bSubmit\b/g," ").replace(/\s+/g," ").trim()}function IC(e,t=""){let n=String(e);if(!tk.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(Vr);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&&!zr.test(r[u])&&a.push(f)}}if(s.length<2)return null;let i=s.map(u=>({label:Jr(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 $C(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||!nk.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(Vr);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&&!(RC.test(n[c])||zr.test(n[c]));c--){let l=n[c].trim();l&&a.unshift(l)}let i=s.map(c=>({label:Jr(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 OC(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(Vr);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 MC(e,t=""){let n=String(e);if(!Ef.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
124
|
+
`),o=-1;for(let u=r.length-1;u>=0;u--)if(CC.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(Vr);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&&!zr.test(r[u])&&a.push(f)}}if(s.length<2||!s.some(u=>Ef.test(u.label)))return null;let i=s.map(u=>({label:Jr(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 $f(e){let t=[];for(let r=0;r<e.length;r++){let o=e[r].match(Vr);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 NC(e,t=""){let n=String(e);if(!rk.test(n)||!ok.test(n)||Yr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
125
|
+
`),o=$f(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:Jr(s.label),description:""}))}]}:null}function PC(e,t=""){let n=String(e);if(!sk.test(n)||Yr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
126
|
+
`),o=$f(r);if(!o)return null;let s=o.map(a=>{let[i,...c]=a.label.split("\xB7");return{label:Jr(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 BC(e,t){let n=e[t].trim();for(let r=t+1;r<e.length;r++){let o=e[r].trim();if(!o||!DC.test(o)||Of.test(e[r]))break;n+=o}return n}function jC(e,t=""){let n=String(e);if(!Of.test(n)||Yr.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=BC(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 FC(e,t=""){let n=String(e);if(!_f.test(n)||!LC.test(n)||Yr.test(n))return null;let s=([...n.replace(/\r/g,"").split(`
|
|
128
|
+
`)].reverse().find(a=>_f.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 UC(e,t=""){let n=String(e);if(!ak.test(n)||Yr.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
129
|
+
`),o=$f(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:Jr(s.label),description:""}))}]}:null}function HC(e,t=""){let n=String(e);if(Yr.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(ik);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(zr.test(r[l])||Rf.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 WC(e){let t=-1;for(let r=e.length-1;r>=0;r--)if(If.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 qC(e,t=""){let n=String(e);if(!ek.test(n)||!If.test(n))return null;let r=n.replace(/\r/g,"").split(`
|
|
131
|
+
`),o=WC(r);if(!o)return null;let{headerLine:s,chatLine:a,optStart:i,optEnd:c}=o,l=[],u=-1;for(let $=i;$<c;$++){let R=r[$].match(Vr);R&&(u<0&&(u=$),l.push({line:$,cursor:!!R[1],checked:R[3]!=null&&R[3]!==" ",hasCheckbox:R[3]!=null,label:R[4].trim(),desc:""}))}if(!l.length)return null;for(let $ of l)for(let R=$.line+1;R<Math.min(r.length,$.line+4)&&!(Vr.test(r[R])||zr.test(r[R]));R++){let ee=r[R].trim();if(!(!ee||AC(r[R])||Zb.test(ee))){$.desc=TC(ee);break}}let d=l.filter($=>!Zb.test($.label));if(!d.length)return null;let f=d.some($=>$.hasCheckbox),p=Math.max(0,d.findIndex($=>$.cursor)),m=s>=0?s+1:0,w=[];for(let $=m;$<u;$++){let R=r[$].trim();R&&!zr.test(r[$])&&!/[☐☒]/.test(r[$])&&w.push(R)}let y=w.join(" ")||(s>=0?Qb(r[s]):"")||"The agent is asking a question",x=s>=0?EC(r[s],y):null,k=x?.currentTabLabel||(s>=0?Qb(r[s]):""),T=d.map($=>({label:Jr($.label),description:$.desc})),_=T.map($=>$.label).join("|"),N=`claude:${x?`q${x.questionIndex+1}/${x.questionTotal}`:"q1/1"}:${k}:${y}:${_}`.slice(0,240),O=x?`claude:ask:${x.tabs.map($=>$.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:y,header:k,multiSelect:f,options:T}]}}function Xr(e,t){let n=String(t||"");return n?e==="claude"?ek.test(n)&&If.test(n)||tk.test(n)||Ef.test(n)||nk.test(n.trimEnd().split(`
|
|
132
|
+
`).pop()||"")?!0:Yr.test(n)?!1:rk.test(n)&&ok.test(n)||sk.test(n)||Of.test(n)||_f.test(n)||ak.test(n)||ik.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 cr(e,t){return!e||!t?null:e.agent==="claude"?OC(t,e.name)||qC(t,e.name)||IC(t,e.name)||MC(t,e.name)||FC(t,e.name)||jC(t,e.name)||PC(t,e.name)||NC(t,e.name)||UC(t,e.name)||HC(t,e.name)||$C(t,e.name):e.agent==="cursor"||e.agent==="codex"?uy(e.agent,t,e.name):null}async function Cf(e){if(!e)return null;let t=await hn(e.name).catch(()=>"");return cr(e,t)}function ck(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 lk(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 ek,If,Vr,Zb,zr,Rf,tk,nk,RC,Ef,CC,Yr,rk,ok,sk,Of,DC,_f,LC,ak,ik,Mf=M(()=>{ze();Cc();ek=/to navigate\b.*\bEsc to cancel|Enter to select\b/,If=/Chat about this/i,Vr=/^\s*(❯)?\s*(\d+)\.\s+(?:\[([ xX✔])\]\s+)?(.+?)\s*$/,Zb=/^(type something|chat about this|submit)\b/i,zr=/^[─—-]{6,}\s*$/,Rf=/[\u2500-\u257F\u2550-\u256F┌┐└┘├┤│┬┴┼─✂]/;tk=/^\s*Switch model\?\s*$/m;nk=/^\s*Enter to confirm\s*·\s*Esc to cancel\s*$/,RC=/^\s*[─—▔-]{6,}\s*$/;Ef=/Resume from summary/i,CC=/^\s*This session is\b/;Yr=/shift\+tab to cycle|⏵⏵ bypass permissions on|⏸ manual mode on/;rk=/Choose the text style that looks best with your terminal/,ok=/Let's get started\./;sk=/Select login method:/;Of=/Paste code here if prompted/,DC=/^[A-Za-z0-9%&=+._~#?/:\-]+$/;_f=/OAuth error:/,LC=/Press Enter to retry\./;ak=/Quick safety check: Is this a project you created or one you trust/;ik=/^(.{0,60}?)Press\s+Enter\s+to\s+continue\s*[.…]{0,3}\s*$/});function Yc(e,t,n){if(!e||!t)return null;for(let r of KC)if(r.provider===e.agent)try{if(r.match(t,n))return r}catch{}return null}function VC(e){let t=Nf.get(e);return t||(t={sentAt:[],last:0,emitted:new Set},Nf.set(e,t)),t}function fk(e,t,n=Date.now()){let r=VC(e);if(r.sentAt=r.sentAt.filter(s=>n-s<dk),r.sentAt.length>=uk)return{action:"give-up"};if(n-r.last<GC)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 pk(e,t=Date.now()){let n=Nf.get(e);return n?n.sentAt.filter(r=>t-r<dk).length>=uk:!1}var KC,GC,uk,dk,Nf,Pf=M(()=>{KC=[{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"]}];GC=5e3,uk=5,dk=3600*1e3,Nf=new Map});function Zo(e){return typeof e=="string"?e.trim():""}var mk=M(()=>{});import{execFile as zC}from"node:child_process";import{promisify as JC}from"node:util";async function $e(e,t,{input:n}={}){let r=Bf("git",["-C",e,...t],{maxBuffer:hk});return n!==void 0&&r.child.stdin&&(r.child.stdin.on("error",()=>{}),r.child.stdin.end(n)),r}async function Qo(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 wk(e){let{stdout:t}=await $e(e,["rev-parse","--show-toplevel"]);return t.trim()}async function YC(e){try{return await $e(e,["rev-parse","--verify","HEAD"]),!0}catch{return!1}}async function es(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)=>Bf("git",["-C",e,...a],{maxBuffer:hk,env:o,...i});try{let a=await YC(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 Bf("rm",["-f",r])}catch{}}}async function jf(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 Na(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 Pa(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 ts(e,t,n,r){return await $e(e,["worktree","add","--no-track","-b",n,t,r]),t}async function gk(e,t){try{return await $e(e,["rev-parse","--verify","--quiet",`refs/heads/${t}`]),!0}catch{return!1}}async function Xc(e,t,n){return await $e(e,["worktree","add",t,n]),t}async function ns(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 Lf(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 XC(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 Ff(e,t){return!t||!t.length?e:[...e,"--",".",...t.map(n=>`:(exclude)${n}`)]}async function Uf(e,t,n,r){let{stdout:o}=await $e(e,Ff(["diff","--shortstat",t,n],r));return XC(o)}async function Hf(e,t,n,r){let{stdout:o}=await $e(e,Ff(["diff","--name-only",t,n],r));return o.split(`
|
|
136
|
+
`).map(s=>s.trim()).filter(Boolean)}async function Wf(e,t,n,r){let{stdout:o}=await $e(e,Ff(["diff","--binary",t,n],r));if(!o.trim())return{applied:!0,empty:!0,conflicts:[]};if((await Df(e,o,["--whitespace=nowarn"])).ok)return{applied:!0,empty:!1,conflicts:[]};let s=await Df(e,o,["--3way","--whitespace=nowarn"]),a=await ZC(e);return a.length>0?{applied:!1,empty:!1,conflicts:a}:s.ok?{applied:!0,empty:!1,conflicts:[]}:(await Df(e,o,["--reject","--whitespace=nowarn"]),{applied:!1,empty:!1,conflicts:[`patch did not apply cleanly: ${s.err}`]})}async function Df(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 ZC(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 Bf,hk,Zc=M(()=>{Bf=JC(zC),hk=64*1024*1024});function Qc(e,{at:t=Date.now(),reason:n="node-transfer"}={}){let r=!1;for(let o of e?.boxes||[]){if(!o||typeof o!="object")continue;!QC.has(o.state)&&o.state!=="stopped"&&(o.state="stopped",r=!0);let s=Array.isArray(o.epochs)?o.epochs:[],a=s[s.length-1];a&&typeof a=="object"&&a.endedAt==null&&(a.endedAt=t,a.endReason==null&&(a.endReason=n),r=!0)}return r}var Be,QC,yn=M(()=>{Be=e=>!!(e&&(e.archivedAt||e.transferredTo)),QC=new Set(["merged","conflict","canceled","failed","handed-off"])});import qf from"node:fs/promises";import bk from"node:path";import eM from"node:os";function vk(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(t.getDate()-(t.getDay()+6)%7),t.getTime()}function Tk(e){let t=new Date(e);return t.setHours(0,0,0,0),t.setDate(1),t.getTime()}function Ak(e){let t=new Date(vk(e));return t.setDate(t.getDate()+7),t.getTime()}function Ek(e){let t=new Date(Tk(e));return t.setMonth(t.getMonth()+1),t.getTime()}function tl(e=Date.now()){for(let[t,n]of Object.entries(rs)){let r=n.startOf(e);if(r!==bn[t]){bn[t]=r;for(let o of Zr.values())o[t]=0;for(let o of os)o.includes(`:${t}:`)&&!o.endsWith(`:${r}`)&&os.delete(o)}}}function _k(e,t){let n=Bt?.caps?.[e]?.[rs[t].capKey];return Number.isFinite(n)&&n>0?n:null}function Qr(e,t=Date.now()){if(!e||!Gf||!Bt?.caps)return null;tl(t);let n=Zr.get(e);for(let r of Object.keys(rs)){let o=_k(e,r);if(o==null)continue;let s=n?.[r]||0;if(s>=o)return{provider:e,window:r,limitUsd:o,spentUsd:el(s),resetsAt:rs[r].nextStart(t)}}return null}function Ik(e,t){let n=`hit:${e}:${t.provider}:${t.window}:${bn[t.window]}`;return os.has(n)?!1:(os.add(n),!0)}function yk(e,t,n,r=Date.now()){if(!e||!t)return;tl(r);let o=tM(e);for(let[s,a]of Object.entries(rs))n>=bn[s]&&n<a.nextStart(r)&&(o[s]+=t)}async function nM(e,t,n=Date.now()){if(!Bt?.caps||!Kf)return;let r=Zr.get(e);if(r)for(let[o,s]of Object.entries(rs)){let a=_k(e,o);if(a==null)continue;let i=r[o]||0;if(i<a*Sk||i>=a)continue;let c=`warn:${e}:${o}:${bn[o]}`;if(!os.has(c)){os.add(c);try{let l=await Kf(t);if(!l)continue;await A(l,{type:"usage.cap.warning",actor:"system",provider:e,payload:{provider:e,window:o,limitUsd:a,spentUsd:el(i),pct:Math.round(i/a*100),resetsAt:s.nextStart(n)}})}catch{}}}}function Rk(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 nl(e,{source:t="local",updatedAt:n=Date.now()}={}){Bt={caps:Rk(e),updatedAt:n,source:t};try{await qf.mkdir(kk,{recursive:!0}),await qf.writeFile(xk(),JSON.stringify(Bt,null,2))}catch{}return Da()}function Da(e=Date.now()){tl(e);let t=new Set([...Zr.keys(),...Object.keys(Bt?.caps||{})]),n={};for(let r of t){let o=Zr.get(r);n[r]={weeklyUsd:el(o?.weekly||0),monthlyUsd:el(o?.monthly||0)}}return{caps:Bt?.caps||{},configured:!!Bt,seeded:Gf,updatedAt:Bt?.updatedAt??null,spent:n,windows:{weekly:{start:bn.weekly,resetsAt:Ak(e)},monthly:{start:bn.monthly,resetsAt:Ek(e)}},warnPct:Sk}}async function $k({listTracks:e,getTrack:t}={}){Kf=t||null,tl();try{let o=JSON.parse(await qf.readFile(xk(),"utf8"));Bt={caps:Rk(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}=gn(o);yk(o.provider||"unknown",s,o.ts??Date.now()),nM(o.provider||"unknown",o.trackId).catch(()=>{})});let r=Math.min(bn.weekly,bn.monthly);for(let o of await e?.()||[]){let s;try{s=await j(o)}catch{continue}for(let a of s){if(a.type!=="cost.recorded")continue;let i=a.ts??0;i<r||i>=n||yk(a.provider||"unknown",gn(a).usd,i,n)}}return Gf=!0,Da()}var kk,xk,Sk,rs,Bt,Gf,bn,Zr,os,Kf,tM,el,rl=M(()=>{ce();qr();kk=process.env.BOSTRAT_STATE_DIR||bk.join(process.env.HOME||eM.tmpdir(),".bostrat"),xk=()=>bk.join(kk,"usage-caps.json"),Sk=.8;rs={weekly:{startOf:vk,nextStart:Ak,capKey:"weeklyUsd"},monthly:{startOf:Tk,nextStart:Ek,capKey:"monthlyUsd"}},Bt=null,Gf=!1,bn={weekly:0,monthly:0},Zr=new Map,os=new Set,Kf=null,tM=e=>{let t=Zr.get(e);return t||(t={weekly:0,monthly:0},Zr.set(e,t)),t};el=e=>Math.round(e*1e4)/1e4});import rM from"node:fs/promises";import oM from"node:os";import Ok from"node:path";function jk(e){return e==="codex"?cM:e==="cursor"?Bk:Pk}function Ck(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 Mk(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 lM(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 Nk(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 Lk(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&&!Dk.has(o))throw new Error(`unknown codex effort "${o}" (low | medium | high | xhigh)`);return{model:r,effort:o||null}}if(e==="cursor"){let r=Bk.find(o=>o.alias===n);return{model:n,filter:r?r.label:n}}if(!iM.has(n))throw new Error(`unknown model "${n}"`);return{model:n}}async function Ba(e,t,{timeoutMs:n=sM}={}){let r=Date.now()+n;for(;;){let o=await ve(e,{lines:80}).catch(()=>""),s=t(o);if(s)return s;if(Date.now()>r)return null;await ol(aM)}}async function uM(e,t){return await He(e,`/model ${t.model}`,Jt("claude")),{verified:!1,note:"confirmed when the next turn's transcript model flips"}}async function dM(e,t){await Ac(e),await He(e,"/model",{enter:!1}),await ol(250),await Ie(e,["Enter"],{kind:"model-switch"});let n=await Ba(e,Ck);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=lM(i);return!c||c.model!==t.model||t.effort&&c.effort&&c.effort.toLowerCase()!==t.effort.toLowerCase()?null:c};await He(e,String(r.n),{enter:!1,kind:"model-switch"});let s=await Ba(e,i=>Mk(i)||!Ck(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?Dk.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 He(e,String(c.n),{enter:!1,kind:"model-switch"})}let a=s.done||await Ba(e,i=>!Mk(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 fM(){let e=Ok.join(process.env.CURSOR_CONFIG_DIR||Ok.join(oM.homedir(),".cursor"),"cli-config.json");try{return JSON.parse(await rM.readFile(e,"utf8"))?.model||null}catch{return null}}async function pM(e,t){await Ac(e),await _c(e,`/model ${t.filter}`,{kind:"model-switch"});let n=await Ba(e,Nk),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 Ba(e,Nk,{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 ol(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 ol(800);let a=await fM();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 Fk(e,t,n){return e==="codex"?dM(t,n):e==="cursor"?pM(t,n):uM(t,n)}var ol,sM,aM,Pk,iM,cM,Dk,Bk,Uk=M(()=>{ze();va();ol=e=>new Promise(t=>setTimeout(t,e)),sM=Number(process.env.BOSTRAT_MODEL_PICKER_TIMEOUT_MS)||8e3,aM=300,Pk=[{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"}],iM=new Set([...Pk.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"]),cM=[{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"}],Dk=new Map([["low","Low"],["medium","Medium"],["high","High"],["xhigh","Extra high"]]),Bk=[{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 al(e){let t=e||{},n=mM(t);return n&&(n.payload={ts:t.at,...n.payload}),n}function mM(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:{...lr(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",...lr(e)}};case"subtask-resumed":return{type:"box.state.changed",actor:"orchestrator",source:"orchestrator",boxId:e.box,provider:lt(e.agent),payload:{change:"subtask-resumed",...lr(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",...lr(e)}};case"opened":return{type:"box.state.changed",actor:"user",source:"orchestrator",boxId:e.box,payload:{change:"opened",...lr(e)}};case"archived":case"unarchived":case"track-deleted":case"renamed":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:e.type,...lr(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:{...lr(e)}};case"transferred-out":return null;case"retagged":return{type:"box.state.changed",actor:"user",source:"orchestrator",payload:{change:"retagged",...lr(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 qk(e,t){let n=al(t);if(!n)return;let r=t?.type==="track-deleted"?{id:e.id}:e;zf(()=>A(r,n))}async function Kk(e){if(!(!e?.id||Hk.has(e.id))){Hk.add(e.id);try{let t=await j(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=al(r);!o||n.has(`${o.type}|${o.payload?.ts??"?"}`)||await A(e,o)}}catch{}}}function lr(e){let{type:t,at:n,...r}=e;return r}function Gk(e,t,n){!n?.promptId||Vf.get(e.id)===n.promptId||(Vf.set(e.id,n.promptId),zf(()=>A(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 Jf(e,t,n,r={}){Vf.delete(e.id),zf(()=>A(e,{type:"prompt.answered",actor:"user",source:"orchestrator",boxId:t?.name,provider:lt(n?.provider||t?.agent),payload:{promptId:n?.promptId,...r}}))}var lt,sl,zf,Wk,Hk,Vf,Yf=M(()=>{ce();lt=e=>e||null;sl=Promise.resolve(),zf=e=>(sl=sl.then(e).catch(()=>{}),sl),Wk=()=>sl;Hk=new Set;Vf=new Map});function bM(e){return String(e).split(/(?<=[.?!])\s+|\n+/).map(t=>t.trim()).filter(Boolean)}function Zk(e){let t=String(e||"").trim();if(!t)return null;let n=t.slice(-600);for(let r of bM(n).reverse()){if(!hM.test(r))continue;let o=r.endsWith("?")&&r.length<=yM&&gM.test(r);if(wM.test(r)||o)return r.length>200?r.slice(0,197)+"\u2026":r}return null}function e0(e){let t=String(e||""),n=[];kM.test(t)&&n.push("commit"),Qk.test(t)&&n.push("rebase"),xM.test(t)&&n.push("push");let r=vM.test(t);!r&&SM.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 t0(e){return Qk.test(String(e||""))}function as(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 n0(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 r0(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<=TM&&n.push({box:r.box,from:r.from,to:t+1e12}),n}function o0(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+AM||(!o||s.ts>=o.ts)&&(o=s);o&&n.add(o)}return n}function s0(e){let t=(Array.isArray(e)?e:[]).filter(r=>Vk[r]).map(r=>Vk[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 zk,Jk,Yk,Xk,hM,wM,gM,yM,ss,kM,Qk,xM,SM,vM,TM,AM,Vk,is=M(()=>{zk=/(?<!\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,Jk=/(?<!\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,Yk=/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,Xk=/(?<!\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,hM=new RegExp(`\\b(?:${zk}|${Jk}|${Yk}|${Xk})\\b`,"i"),wM=/\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,gM=/^(?:commit|push|open|create|raise|rebase|ready|ok(?:ay)?\s+to|good\s+to)\b/i,yM=90;ss=["commit","rebase","push","pr"],kM=new RegExp(`\\b(?:${zk})\\b`,"i"),Qk=new RegExp(`\\b(?:${Xk})\\b`,"i"),xM=new RegExp(`\\b(?:${Jk})\\b`,"i"),SM=new RegExp(`\\b(?:${Yk})\\b`,"i"),vM=/\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;TM=3e4;AM=2500;Vk={commit:"commit",rebase:"rebase",push:"push",pr:"open a PR"}});import EM from"node:crypto";import il from"node:fs/promises";import a0 from"node:path";import _M from"node:os";async function Me(){return ur||(Xf??=il.readFile(Qf,"utf8").then(e=>({links:JSON.parse(e)?.links??{},existed:!0})).catch(()=>({links:{},existed:!1})).then(async({links:e,existed:t})=>{if(ur=e,!t&&Ae()){let{records:n,source:r}=await We("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;ur[te(i.repo,i.number)]=i}Object.keys(ur).length&&await ep().catch(()=>{})}}return ur}).finally(()=>Xf=null),Xf)}async function ep(){let e=JSON.stringify({links:ur},null,2),t=`${Qf}.${process.pid}.tmp`;await il.mkdir(i0,{recursive:!0}),await il.writeFile(t,e),await il.rename(t,Qf)}async function qe(e){Zf=Zf.then(ep,ep),await Zf.catch(()=>{});let t=ur?.[e];t&&tt("prlinks",{id:c0(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 Me(),c=te(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:IM.has(a),firstSeen:Date.now()},await qe(te(e,t)),{link:i[c],created:!0})}async function kn(e,t){if(!e||t==null)return null;let n=(await Me())[te(e,t)];return!n||n.announced?null:(n.announced=!0,await qe(te(e,t)),n)}async function Ke(e,t){return!e||t==null?null:(await Me())[te(e,t)]??null}async function dr(e,t,n){if(!e||t==null)return null;let r=(await Me())[te(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 qe(te(e,t)),r):null}async function l0(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Me())[te(e,t)];return o?(o.ciFix={delegatedAt:Date.now(),headSha:n,trackId:r},await qe(te(e,t)),o):null}async function u0(e,t,{headSha:n=null,trackId:r=null}={}){if(!e||t==null)return null;let o=(await Me())[te(e,t)];return o?(o.reconcile={delegatedAt:Date.now(),headSha:n,trackId:r},await qe(te(e,t)),o):null}async function cl(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Me())[te(e,t)];return!r||!r.ciFix?null:(r.ciFixAwait={since:Date.now(),trackId:r.ciFix.trackId??null,headSha:n},delete r.ciFix,await qe(te(e,t)),r)}async function ll(e,t){if(!e||t==null)return null;let n=(await Me())[te(e,t)];return!n||!n.ciFixAwait?null:(delete n.ciFixAwait,await qe(te(e,t)),n)}async function ul(e,t,{headSha:n=null}={}){if(!e||t==null)return null;let r=(await Me())[te(e,t)];return!r||!r.reconcile?null:(r.reconcileAwait={since:Date.now(),trackId:r.reconcile.trackId??null,headSha:n},delete r.reconcile,await qe(te(e,t)),r)}async function dl(e,t){if(!e||t==null)return null;let n=(await Me())[te(e,t)];return!n||!n.reconcileAwait?null:(delete n.reconcileAwait,await qe(te(e,t)),n)}async function d0(e,t,{at:n=Date.now()}={}){if(!e||t==null)return null;let r=(await Me())[te(e,t)];return r?(r.baseFoldAt=n,await qe(te(e,t)),r):null}async function f0(e,t){if(!e||t==null)return null;let n=(await Me())[te(e,t)];return!n||!n.baseFoldAt?null:(delete n.baseFoldAt,await qe(te(e,t)),n)}async function p0(e,t,n){if(!e||t==null||!n)return null;let r=(await Me())[te(e,t)];return r?(r.lastHeadSha=n,await qe(te(e,t)),r):null}async function m0(e,t,n){if(!e||t==null||!n)return{prev:null,link:null};let r=(await Me())[te(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 qe(te(e,t))),{prev:o,link:r}}async function ja(e,t,{merge:n=null}={}){if(!e||t==null)return null;let r=(await Me())[te(e,t)];return r?(r.armedAt=Date.now(),n&&(r.autoMerge={method:n.method||"merge",armedAt:r.armedAt}),await qe(te(e,t)),r):null}async function eo(e,t,n=!0){if(!e||t==null)return null;let r=(await Me())[te(e,t)];return r?(n?r.archiveOnMerge=!0:delete r.archiveOnMerge,await qe(te(e,t)),r):null}async function La(e,t,n=!0){if(!e||t==null)return null;let r=(await Me())[te(e,t)];return r?(n?r.notifyOnMerge=!0:delete r.notifyOnMerge,await qe(te(e,t)),r):null}async function h0(e,t,n){if(!e||t==null)return null;let r=(await Me())[te(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 qe(te(e,t)),r}async function Fa(e,t){if(!e||t==null)return null;let n=(await Me())[te(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 qe(te(e,t)),n)}async function w0(){return Object.values(await Me()).filter(e=>!RM.has(e.state))}async function to(){return Object.values(await Me()).sort((e,t)=>t.firstSeen-e.firstSeen)}async function g0(){if(!Ae())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await We("prlinks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Me(),r=0;for(let[o,s]of Object.entries(n))await tt("prlinks",{id:c0(o),updatedAt:Date.now(),...s},{trackId:s.trackId??null}).catch(()=>{}),r++;return{pushed:r,source:"plane"}}var i0,Qf,te,c0,ur,Xf,Zf,IM,RM,xn=M(()=>{Jn();i0=process.env.BOSTRAT_STATE_DIR||a0.join(process.env.HOME||_M.tmpdir(),".bostrat"),Qf=a0.join(i0,"pr-links.json"),te=(e,t)=>`${e}#${Number(t)}`,c0=e=>{let t=e.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^[^A-Za-z0-9]+/,"").slice(0,55),n=EM.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t||"pr"}-${n}`},ur=null,Xf=null,Zf=Promise.resolve();IM=new Set(["api","observed","reconciled"]);RM=new Set(["merged","closed","lost"])});import $M from"node:fs";import tp from"node:fs/promises";import x0 from"node:path";import OM from"node:os";function T0(e,t=!1){if(!t&&e-b0<CM)return;b0=e;let n=JSON.stringify({windowStart:cs,byCaller:Object.fromEntries(Sn),lastHour:fr});k0=k0.then(()=>tp.mkdir(S0,{recursive:!0})).then(()=>tp.writeFile(`${fl}.tmp`,n)).then(()=>tp.rename(`${fl}.tmp`,fl)).catch(()=>{})}function A0(e){e-cs<v0||(fr={start:cs,total:pl(Sn),byCaller:Object.fromEntries(Sn)},fr.total&&console.log(`[github] ${fr.total} calls last hour`,fr.byCaller),cs=e,Sn=new Map,T0(e,!0))}function vn(e="other",t=Date.now()){A0(t),Sn.set(e,(Sn.get(e)||0)+1),T0(t)}function E0(e=Date.now()){return A0(e),{windowStart:cs,total:pl(Sn),byCaller:Object.fromEntries(Sn),lastHour:fr,backoffUntil:no>e?no:null}}function pr(e){return MM.test(String(e?.message||e||""))}function mr(e,t=Date.now()){return no<=t&&console.warn(`[github] rate limited \u2014 backing off ${y0/6e4} min:`,String(e?.message||e||"").slice(0,200)),no=Math.max(no,t+y0),no}function ml(e=Date.now()){return Math.max(0,no-e)}var S0,fl,v0,y0,CM,cs,Sn,fr,no,b0,pl,k0,MM,Ua=M(()=>{S0=process.env.BOSTRAT_STATE_DIR||x0.join(process.env.HOME||OM.tmpdir(),".bostrat"),fl=x0.join(S0,"github-metrics.json"),v0=60*6e4,y0=5*6e4,CM=6e4,cs=Date.now(),Sn=new Map,fr=null,no=0,b0=0,pl=e=>[...e.values()].reduce((t,n)=>t+n,0);try{let e=JSON.parse($M.readFileSync(fl,"utf8"));e?.windowStart&&(Date.now()-e.windowStart<v0?(cs=e.windowStart,Sn=new Map(Object.entries(e.byCaller||{})),fr=e.lastHour||null):pl(new Map(Object.entries(e.byCaller||{})))&&(fr={start:e.windowStart,total:pl(new Map(Object.entries(e.byCaller||{}))),byCaller:e.byCaller||{}}))}catch{}k0=Promise.resolve();MM=/rate limit|too quickly|abuse detection|HTTP 429/i});import _0 from"node:fs/promises";import I0 from"node:path";import NM from"node:os";function wl({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};hl.push(l),hl.length>DM&&hl.shift();let u=np.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)),np.set(e,u),BM(l),l}async function BM(e){try{await _0.mkdir(R0,{recursive:!0}),await _0.appendFile(PM,JSON.stringify(e)+`
|
|
142
|
+
`)}catch{}}function $0(){let e={};for(let[t,n]of np)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:hl.slice(-20)}}var R0,PM,DM,hl,np,gl=M(()=>{R0=process.env.BOSTRAT_STATE_DIR||I0.join(process.env.HOME||NM.tmpdir(),".bostrat"),PM=I0.join(R0,"github-latency.ndjson"),DM=100,hl=[],np=new Map});import{execFile as jM}from"node:child_process";import{promisify as LM}from"node:util";async function FM(e){let t=/workspaces[/\\]([0-9a-f]{8})(?:[/\\]|$)/.exec(e||"")?.[1];if(t)try{let[{stdout:n},{stdout:r}]=await Promise.all([us("git",["-C",e,"rev-parse","HEAD"]),us("git",["-C",e,"rev-parse","--abbrev-ref","HEAD"])]);await A({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 Kt({cwd:e});vn(r);try{let{stdout:a}=await us("gh",t,{cwd:e,...UM,...o,env:{...process.env,...s,...o.env||{}}});return String(a||"")}catch(a){throw pr(a)&&mr(a),a}}async function sp(){if(yl&&Date.now()-yl.at<6e4)return yl.data;let e;try{vn("auth");let t=await us("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 yl={at:Date.now(),data:e},e}async function HM(e){try{let{stdout:t}=await us("git",["-C",e,"config","--get","remote.origin.url"],{timeout:5e3});return/github\.com/.test(t)}catch{return!1}}function C0(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 rp(e){let t=(e||[]).map(C0),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 ip(e){return/Resource not accessible by personal access token/.test(String(e?.stderr||e?.message||""))}async function N0(e,t){if(!t)return null;let n=await fe(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 P0(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 D0(e,t){let n=await N0(e,t);return n===null?{...rp([]),unavailable:!0}:rp(P0(n))}function bl(e,t=rp(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 qM(e,t,n){let r=new Set;for(let o of t){let s=`${e}#${o.number}`;if(o.mergeable!=="UNKNOWN"&&o.mergeStateStatus!=="UNKNOWN"){ls.delete(s);continue}r.add(s),ls.has(s)||ls.set(s,n),o.mergeabilityPending=n-ls.get(s)<WM}for(let o of ls.keys())o.startsWith(`${e}#`)&&!r.has(o)&&ls.delete(o);return t}async function ds(e,t){let n=await Tn(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",ap]);return bl(JSON.parse(o))}catch(o){if(!ip(o))throw o;let s=await vt(n.path,["pr","view",String(r),"--json",M0]),a=JSON.parse(s);return bl(a,await D0(n.path,a.headRefOid))}}async function Xt(e,{caller:t="prs"}={}){try{let n=await vt(e,["pr","list","--json",ap,"--limit","50"],{caller:t});return JSON.parse(n||"[]").map(r=>bl(r)).sort(O0)}catch(n){if(!ip(n))throw n;let r=await vt(e,["pr","list","--json",M0,"--limit","50"],{caller:t});return(await Promise.all(JSON.parse(r||"[]").map(async o=>bl(o,await D0(e,o.headRefOid))))).sort(O0)}}function Qt(){Ha.clear()}async function VM(e,t){if(!e)return t;for(let n of t){let r=await Ke(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 cl(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.ciFix={delegatedAt:r.ciFix.delegatedAt,trackId:r.ciFix.trackId??null}),r.reconcile&&(o(r.reconcile)?await ul(e,n.number,{headSha:n.headSha}).catch(()=>{}):n.reconcile={delegatedAt:r.reconcile.delegatedAt,trackId:r.reconcile.trackId??null})}return t}async function B0(){let e=await sp();if(!e.authenticated)return{auth:e,repos:[]};let t=[];for(let a of await fn())await HM(a.path)&&t.push(a);let n=Date.now(),r=ml(n)>0,o=await Promise.all(t.map(async a=>{let i=Ha.get(a.path),c=i?.pending?GM:KM;if(i&&(r||n-i.at<c))return i.entry;try{let l=await fe(a.path).catch(()=>null),u=qM(a.path,await Xt(a.path,{caller:"tab"}),n);if(await VM(l,u),i?.entry?.prs)for(let p of u)i.entry.prs.find(w=>w.number===p.number)?.checks?.pending>0&&p.checks.total>0&&p.checks.pending===0&&wl({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 Ha.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:Zt(l)}}})),s=new Set(t.map(a=>a.path));for(let a of Ha.keys())s.has(a)||Ha.delete(a);return{auth:e,repos:o}}async function Tn(e){let t=(await fn()).find(n=>n.path===e);if(!t)throw new Error(`unknown repo: ${e}`);return t}async function ro(e){return Eo(e)}async function fs(e,t,{method:n="squash"}={}){let r=await Tn(e);if(!Wa.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 Kt({cwd:r.path})).GH_TOKEN){let a=await Yu().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{Qt()}return{ok:!0,number:o}}async function qa(e,t){let n=await Tn(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{Qt()}let o=await fe(n.path).catch(()=>null);return o&&await d0(o,r).catch(()=>{}),{ok:!0,number:r}}async function cp(e,t){let n=await Tn(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{Qt()}return{ok:!0,number:r}}function lp(e){return e.green&&!e.isDraft}function XM(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 j0(e,{update:t=!0}={}){return e.isDraft?{action:"skip",reason:"draft"}:lp(e)?{action:"merge"}:t&&e.mergeStateStatus==="BEHIND"&&e.mergeable!=="CONFLICTING"&&e.checks.failed===0?{action:"update-arm"}:{action:"arm"}}function L0(e){return[...e.filter(t=>t.plan.action!=="merge"),...e.filter(t=>t.plan.action==="merge")]}function Ka(e,t){let n=a=>a?.firstSeen!=null&&Number.isFinite(Number(a.firstSeen))?Number(a.firstSeen):Number.MAX_SAFE_INTEGER,r=n(e),o=n(t);if(r!==o)return r-o;let s=String(e?.repo||"").localeCompare(String(t?.repo||""));return s||Number(e?.number||0)-Number(t?.number||0)}function F0(e){let t=new Map;for(let n of[...e].filter(r=>r?.autoMerge&&r?.trackId).sort(Ka))t.has(n.trackId)||t.set(n.trackId,`${n.repo}#${n.number}`);return t}function U0(){let e={};for(let[t,n]of op)e[t]={...n};return e}async function H0(e,{method:t="merge",onMerged:n=null}={}){let r=await Tn(e);if(!Wa.has(t))throw new Error(`invalid merge method: ${t}`);if(op.get(r.path)?.running)throw new Error("a merge-all is already running for this repo");let o=(await Xt(r.path)).filter(lp).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 op.set(r.path,s),QM(s,o,{listPrs:()=>Xt(r.path),merge:a=>fs(r.path,a,{method:t}),sleep:YM,onMerged:n}).catch(()=>{}),{ok:!0,repo:r.path,total:s.total,queue:o.map(a=>a.number)}}async function ZM(e,t){let n=Date.now()+(t.settleMaxMs??JM),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(zM)}}async function QM(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 ZM(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(!lp(a)){e.skipped.push({number:a.number,title:a.title,reason:XM(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(),Qt()}}async function up(e,{title:t,body:n="",base:r=null}={}){if(typeof t!="string"||!t.trim())throw new Error("title required");try{await us("git",["-C",e,"push","-u","origin","HEAD"],{timeout:12e4,maxBuffer:4*1024*1024})}catch(l){throw new Error(`push failed: ${Zt(l)}`)}await FM(e);let o=n||"";if((await Kt({cwd:e})).GH_TOKEN){let l=await Yu().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(Zt(l))}finally{Qt()}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 W0(e,t){let n=await Tn(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(Zt(c))}o=JSON.parse(i||"[]").filter(c=>c.bucket==="fail")}catch(i){if(!ip(i))throw i;let c=await vt(n.path,["pr","view",String(r),"--json","headRefOid"]),l=await N0(n.path,JSON.parse(c).headRefOid);if(l===null)throw new Error(Zt(i));o=P0(l).map(C0).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=eN(i.link),l=null;c&&!s.has(c)&&(s.add(c),l=await vt(n.path,["run","view",c,"--log-failed"]).then(u=>tN(u)).catch(()=>null)),a.push({name:i.name,link:i.link||null,log:l})}return a}function eN(e){return String(e||"").match(/\/actions\/runs\/(\d+)/)?.[1]??null}function tN(e,{maxLines:t=400,maxBytes:n=64*1024}={}){let r=String(e||""),o=r.split(`
|
|
145
145
|
`);return o.length>t&&(r=`\u2026 (${o.length-t} earlier lines truncated)
|
|
146
146
|
`+o.slice(-t).join(`
|
|
147
147
|
`)),Buffer.byteLength(r)>n&&(r=`\u2026 (truncated)
|
|
148
|
-
`+r.slice(-n)),r}function
|
|
149
|
-
`).find(n=>n.trim())||"gh failed"}var
|
|
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()===
|
|
148
|
+
`+r.slice(-n)),r}function Zt(e){return String(e?.stderr||e?.message||e||"").trim().split(`
|
|
149
|
+
`).find(n=>n.trim())||"gh failed"}var us,UM,yl,ap,M0,WM,ls,O0,KM,GM,Ha,Wa,zM,JM,YM,op,ps=M(()=>{Kn();et();xn();Ua();gl();ce();us=LM(jM),UM={timeout:3e4,maxBuffer:8*1024*1024};yl=null;ap="number,title,url,headRefName,headRefOid,baseRefName,isDraft,author,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup,autoMergeRequest,additions,deletions,createdAt,updatedAt",M0=ap.replace("statusCheckRollup,","");WM=9e4,ls=new Map;O0=(e,t)=>e.number-t.number;KM=3e4,GM=1500,Ha=new Map;Wa=new Set(["squash","merge","rebase"]);zM=2500,JM=9e4,YM=e=>new Promise(t=>setTimeout(t,e));op=new Map});import{execFile as nN}from"node:child_process";import{promisify as rN}from"node:util";async function sN(e,t){try{let{stdout:n}=await oN("git",["-C",e,"rev-list","--count",t]);return Number(n.trim())||0}catch{return null}}async function aN(e,t,n,{rebase:r=!1,listPrs:o=Xt}={}){let s=await Na(e.dir,{ignorePlumbing:!0}).catch(()=>!1),a=await Pa(e.dir,n).catch(()=>0),i=t?await sN(e.dir,`refs/remotes/origin/${t}..HEAD`):null,c=i==null?a>0:i>0,l=null,u="unknown";try{l=(await o(e.dir,{caller:"ship-state"})||[]).find(w=>w.headRefName===t)||null,u=l?"open":"none"}catch{}let 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:kl.filter(m=>f.includes(m)),defaults:kl.filter(m=>p.includes(m))}}async function q0(e,{branch:t,rebase:n=!1,listPrs:r=Xt}={}){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 Qo(s[0].dir).catch(()=>!1))return null;let a=[];for(let f of s)a.push(await aN(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=kl.filter(f=>c.some(p=>p.available.includes(f))),u=kl.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 oN,kl,K0=M(()=>{Zc();ps();oN=rN(nN),kl=["commit","rebase","push","pr"]});function xl(e,t,n){let{state:r,reason:o}=jd(t,n);if(r!=="unknown"){if(e){let a=Date.now();for(let[i,c]of Ga)a-c.at>=G0()&&Ga.delete(i);Ga.set(e,{busy:r==="busy",at:a})}return r==="busy"}if(o!=="narrow"||!e)return!1;let s=Ga.get(e);return s?Date.now()-s.at>=G0()?(Ga.delete(e),!1):s.busy:!1}async function dp(e,t){let n={working:!1,prompted:!1};if(!e)return n;let r=await ve(e,{lines:Ue}).catch(()=>"");if(!xl(e,t,r))return wy(t,r)?{working:!0,prompted:!1}:n;if(Xr(t,r)){let o=await hn(e).catch(()=>r),s=cr({name:e,agent:t},o);if(s)return!Yc({name:e,agent:t},s,o)||pk(e)?{working:!1,prompted:!0}:{working:!1,prompted:!1}}return{working:!0,prompted:!1}}async function oo(e,t){return(await dp(e,t)).working}var G0,Ga,Va=M(()=>{ze();Sa();Mf();Pf();G0=()=>Number(process.env.BOSTRAT_NARROW_CARRY_MS)||9e4,Ga=new Map});import{execFile as iN}from"node:child_process";import{promisify as cN}from"node:util";async function ms(e){if(!e)return null;let t=V0.get(e);if(t&&Date.now()-t.at<uN)return t.branch;let n=null;try{let{stdout:r}=await lN("git",["-C",e,"symbolic-ref","--short","-q","HEAD"],{timeout:5e3});n=r.trim()||null}catch{n=null}return V0.set(e,{at:Date.now(),branch:n}),n}function z0(e){return String(e||"").match(dN)?.[1]??null}function fN(e,t){let n=z0(e);return!!n&&!!t&&n===t}function J0(e,t){let n=z0(e);return!!n&&n!==t}function pN(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 hs(e){let t=[],n=r=>{r&&!t.includes(r)&&t.push(r)};n(e?.workspace?.branch??null);for(let r of pN(e))n(await ms(r));return t}async function Sl(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=>fN(t,a.id));if(s)return s;for(let a of r)if(!Be(a)&&(await hs(a)).includes(t))return a;return null}var lN,uN,V0,dN,ws=M(()=>{yn();lN=cN(iN),uN=5e3,V0=new Map;dN=/^track\/.+-([0-9a-f]{8})$/});import{execFile as mN}from"node:child_process";import{promisify as hN}from"node:util";async function fp(e,t,{midTurn:n=oo,pollMs:r=wN,capMs:o=gN}={}){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 tx(e){let t=String(e||""),n=0;for(let r=0;r<64;r++){Y0.lastIndex=n;let o=Y0.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*":"")+TN(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 pp(e,t){return e.slice(t).replace(/^[\s;&|)]+/,"").replace(/"[^"]*"|'[^']*'/g,'""').split(/[;&|\n]/)[0]}function rx(e){let t=tx(e),n=t.match(xN);if(!n)return null;let r=pp(t,n.index);return vN.test(r)?null:{repo:r.match(nx)?.[1]??null,head:r.match(AN)?.[1]??null}}function ox(e){let t=tx(e),n=t.match(SN);if(!n)return null;let r=pp(t,n.index),o=pp(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("-")){_N.has(c)&&i++;continue}s=c.replace(/^["']+|["']+$/g,"")||null;break}return{repo:r.match(nx)?.[1]??null,selector:s,auto:/\s--auto\b/.test(r)}}async function RN(e,t,{exclude:n=()=>!1}={}){let r=async i=>{vn("observer");let{stdout:c}=await X0("gh",["pr","list","--repo",e,"--author","@me","--state","all","--limit","10","--json",IN],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...i}}).catch(l=>{throw pr(l)&&mr(l),l});return JSON.parse(c||"[]")},o=i=>(i||[]).filter(c=>Date.parse(c.createdAt)>=t-yN&&!n(c)),s=await Kt({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 $N(e,t){if(t)return[t];let n=[];for(let r of sx(e)){let o=await fe(r).catch(()=>null);o&&!n.includes(o)&&n.push(o)}for(let r of await fn().catch(()=>[])){let o=await fe(r.path).catch(()=>null);o&&!n.includes(o)&&n.push(o)}return n}function sx(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 ON(e,t,n){let r=await hs(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)||EN(c.headRefName,e.id),i=[];for(let c of await $N(e,t?.repo??null)){let l;try{l=await RN(c,n,{exclude:a})}catch{continue}for(let u of l){let d=await Ke(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 ax(e,t,{delays:n=Z0,turnEnd:r}={}){let o=rx(t.command);if(!o)return null;let s=!!ox(t.command),a=`${e.id}|${t.id??t.ts??t.command}`;if(hr.has(a))return null;hr.add(a);let i=t.ts??Date.now(),c=(async()=>{try{for(let l of n){await new Promise(w=>setTimeout(w,l));let u=await ON(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 kn(d,f.number))?null:(await fp(t.box,t.agent,r),await A({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{hr.delete(a)}})();return c.catch(()=>hr.delete(a)),c}function ix(e,t,{delays:n=Z0,turnEnd:r,openedWait:o}={}){let s=ox(t.command);if(!s)return null;let a=`merge|${e.id}|${t.id??t.ts??t.command}`;if(hr.has(a))return null;hr.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=sx(e);if(m.length>1&&/^\d+$/.test(String(s.selector??"")))return null;for(let w of m){let y=await fe(w).catch(()=>null);y&&!d.includes(y)&&d.push(y)}}if(l||=(await hs(e).catch(()=>[])).filter(m=>m!==e.workspace?.branch)[0]||e.workspace?.branch,!d.length||!l)return null;c=d[0];let f=async(m,w)=>{let y=await Ke(c,m.number);return y&&y.trackId!==e.id?null:(y||await mt({repo:c,number:m.number,trackId:e.id,url:m.url,headRef:m.headRefName,title:m.title,source:"merge-observed"}),await ja(c,m.number),w&&((await j(e).catch(()=>[])).some(T=>T.type==="pr.state.changed"&&(T.payload?.change==="auto-armed"||T.payload?.change==="merged")&&T.payload?.number===m.number)||(await fp(t.box,t.agent,r),await A({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 w=[];for(let N of d)try{vn("observer");let{stdout:O}=await X0("gh",["pr","view",String(l),"--repo",N,"--json",CN],{timeout:3e4,maxBuffer:4*1024*1024,env:{...process.env,...await Kt({slug:N})}}),$=JSON.parse(O||"null");$?.number&&w.push({slug:N,pr:$})}catch(O){pr(O)&&mr(O)}let y=w.find(N=>N.pr.state==="MERGED")??(w.length===1?w[0]:null);if(!y)continue;c=y.slug;let x=y.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 Ke(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 fp(t.box,t.agent,r);let T=await j(e).catch(()=>[]);if(T.some(N=>N.type==="pr.state.changed"&&N.payload?.change==="merged"&&N.payload?.number===x.number))return null;let _=N=>N.some(O=>O.type==="pr.state.changed"&&O.payload?.change==="opened"&&O.payload?.number===x.number),P=0;if(!_(T)){if(rx(t.command)&&await kn(c,x.number)){let N=Date.now();P=N+1,await A({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 Ke(c,x.number))?.announced){let N=o?.capMs??bN,O=o?.pollMs??kN,$=Date.now()+N;for(;Date.now()<$&&(await new Promise(R=>setTimeout(R,O)),!_(await j(e).catch(()=>[]))););}}return await A({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(),P),change:"merged",number:x.number,url:x.url,title:x.title,branch:x.headRefName,repo:c}}),await En(c,x.number).catch(()=>{}),await An(c,x.number).catch(()=>{}),await dr(c,x.number,"merged"),x}return p?f(p,!1):null}finally{hr.delete(a)}})();return i.catch(()=>hr.delete(a)),i}var X0,Z0,wN,gN,yN,bN,kN,Q0,ex,xN,SN,vN,Y0,TN,nx,AN,EN,_N,IN,hr,CN,cx=M(()=>{ce();Kn();et();Ua();xn();wr();Va();ws();X0=hN(mN),Z0=[4e3,15e3,45e3],wN=2500,gN=5*6e4;yN=15e3,bN=9e4,kN=2e3,Q0=/(?:^|[;&|\n({`]\s*|\)\s*)(?:(?:then|do|else|elif|if|while|until|time)\s+)?/.source,ex=/gh\s+(?:[^\s;&|"'`$]+\s+)*?pr\s+/.source,xN=new RegExp(`${Q0}${ex}create\\b`),SN=new RegExp(`${Q0}${ex}merge\\b`),vN=/\s--web\b/,Y0=/<<(-?)\s*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][\w-]*))/g,TN=e=>e.replace(/[.*+?^${}()|[\]\\-]/g,"\\$&");nx=/(?:--repo|-R)[\s=]+([\w.-]+\/[\w.-]+)/,AN=/(?:--head|-H)[\s=]+([^\s"'`;&|]+)/,EN=J0;_N=new Set(["--subject","-t","--body","-b","--body-file","-F","--match-head-commit","--author-email","-A","--repo","-R"]);IN="number,url,title,headRefName,createdAt";hr=new Set;CN="number,url,title,headRefName,state"});import MN from"node:fs/promises";import hp from"node:path";import NN from"node:os";function mp(e,t){let n=new RegExp(`\\b${t}="([^"]*)"`).exec(e);return n?n[1]:null}function vl(e){let t=PN.exec(String(e||""));if(!t)return null;let[,n,r]=t,o=mp(n,"from"),s=o?o.replace(/^uds:/,""):null,a=lx(s);return{socket:s,pid:a,name:mp(n,"from-name"),mode:mp(n,"from-mode"),text:r.trim()}}function lx(e){let t=/(\d+)\.sock$/.exec(String(e||""));return t?Number(t[1]):null}function DN(){return process.env.CLAUDE_CONFIG_DIR?hp.join(process.env.CLAUDE_CONFIG_DIR,"sessions"):hp.join(NN.homedir(),".claude","sessions")}async function BN(e){let t=lx(e);if(!t)return null;try{let n=await MN.readFile(hp.join(DN(),`${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 ux(e,t){let n=await BN(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,wp=M(()=>{PN=/<cross-session-message\b([^>]*)>([\s\S]*?)<\/cross-session-message>/});function gp(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function yp({loopId:e,outcome:t,note:n=null,env:r=process.env}={}){if(!gp(r))return{ok:!1,source:"unconfigured"};let o=new AbortController,s=setTimeout(()=>o.abort(),dx);try{let a=await fx(`${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 px(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(!gp(s))return{ok:!1,source:"unconfigured"};let i=Tl.get(e.id)||0;if(a()-i<jN)return{ok:!1,source:"debounced"};Tl.set(e.id,a());let c=await za({env:s}),l=c.loops.find(f=>f.trackId===e.id);if(!l)return Tl.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 yp({loopId:l.id,outcome:"errored",note:u,env:s});return d.ok||Tl.delete(e.id),{...d,loop:{id:l.id,name:l.name}}}async function za({env:e=process.env}={}){if(!gp(e))return{loops:[],source:"unconfigured"};let t=new AbortController,n=setTimeout(()=>t.abort(),dx);try{let r=await fx(`${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 dx,fx,jN,Tl,bp=M(()=>{et();dx=1e4,fx=(...e)=>fetch(...e);jN=10*6e4,Tl=new Map});import{randomUUID as LN}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 mx(e){return e.id?`cmd|${e.box}|id:${e.id}`:`cmd|${e.box}|${e.ts??"?"}|${String(e.command||"").slice(0,40)}`}function hx(e){return`job|${e.box}|id:${e.id}`}async function xp(e){return kp.has(e.id)?kp.get(e.id):(Al.has(e.id)||Al.set(e.id,j(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(mx({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(hx({id:r.payload.id,box:r.boxId}));return kp.set(e.id,n),n}).finally(()=>Al.delete(e.id))),Al.get(e.id))}async function wx(e,t){let n=new Set;try{let r=(await xp(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"?vl(o.text):null;if(a){await A(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 ux(a,Ya),boxRole:o.boxRole,sourceId:o.sourceId||void 0}});continue}let i=Xg(o);if(i){await A(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}}),px(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 A(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=Zk(o.text);if(c){let l=t0(c),u;if(e.workspace?.kind==="multi"){let m=await gs(e).catch(()=>[]);u=m.length?m.map(w=>({name:w.name,slug:w.repo??null})):void 0}let d=(e.workspace?.kind==="multi"?null:await ms(e.cwd).catch(()=>null))||e.workspace.branch,f=await q0(e,{branch:d,rebase:l}).catch(()=>null),p=f?null:e0(c);if(f&&f.available.length===0)continue;await A(e,{type:"pr.offered",actor:"agent",source:"transcript",boxId:o.box,provider:o.agent||null,payload:{offerId:LN(),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 gx(e,t){try{let n=await xp(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=mx(o);n.keys.has(s)||(n.keys.add(s),n.cmdN+=1,await A(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}}),ax(e,o),ix(e,o))}}catch{}}async function yx(e,t){try{let n=await xp(e),r=[...t||[]].filter(o=>o&&o.id).sort((o,s)=>(o.ts||0)-(s.ts||0));for(let o of r){let s=hx(o);n.keys.has(s)||(n.keys.add(s),await A(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 kp,Al,bx=M(()=>{ce();is();K0();cx();ws();wp();wr();Rc();bp();kp=new Map,Al=new Map});function FN(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 UN(e,t){let n=`${e.id}:${t.name}`;if(!Sp.has(n)){let r=await j(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);Sp.set(n,o)}return Sp.get(n)}async function kx(e,t){try{if(t?.agent!=="codex"||!t.cwd)return;let n=await Lr(t.cwd,t.pinned?t.sessionId:null);if(!n)return;let r=await UN(e,t),o=FN(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 A(e,{type:"cost.recorded",actor:"agent",source:"transcript",boxId:t.name,provider:"codex",payload:{usd:Yt(c,i),model:c,tokens:i,reasoningOutput:a.last.reasoningOutput||void 0,codexTs:a.ts,ts:a.ts}}),a===s&&a.contextWindow&&await A(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 Sp,xx=M(()=>{ce();Ur();Pt();wn();Sp=new Map});import El from"node:fs";import Tp from"node:path";import HN from"node:os";async function KN({sinceMs:e,cookie:t,fetchImpl:n=fetch}){let r=[];for(let o=1;o<=20;o++){let s=await n(qN,{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:Sx})});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<Sx)break}return r}function GN(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 Tx(){try{return JSON.parse(El.readFileSync(_l(),"utf8"))}catch{return{sinceMs:0,seen:[]}}}function zN(e){try{El.mkdirSync(Tp.dirname(_l()),{recursive:!0});let t=_l()+".tmp";El.writeFileSync(t,JSON.stringify(e)),El.renameSync(t,_l())}catch{}}async function YN({fetchImpl:e,now:t=Date.now()}={}){if(gr.rows&&t-gr.at<VN)return gr.rows;let n=process.env.BOSTRAT_CURSOR_USAGE_COOKIE;if(!n)return null;let r=Tx(),o=Math.max(r.sinceMs-6e4,t-24*36e5);try{let s=await KN({sinceMs:o,cookie:n,fetchImpl:e});gr={at:t,rows:s.map(GN).filter(Boolean),error:null}}catch(s){gr={at:t,rows:null,error:String(s?.message||s)}}return gr.rows}async function XN(e,t){let n=`${e.id}:${t.name}`;if(!vp.has(n)){let r=await j(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);vp.set(n,o)}return vp.get(n)}async function Ax(e,t,{fetchImpl:n}={}){try{if(t?.agent!=="cursor"||!t.cwd||!vx())return;let r=await YN({fetchImpl:n});if(!r||!r.length)return;let o=await Kb(t.cwd,t.pinned?t.sessionId:null);if(!o?.chatId)return;let s=await XN(e,t),a=Tx(),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=Yt(u,d)),!f&&l.chargedCents!=null&&(f=l.chargedCents/100),await A(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}})}zN({sinceMs:c,seen:[...i].slice(-JN)})}catch{}}var WN,vx,qN,Sx,VN,gr,_l,JN,Ap,vp,Ep=M(()=>{ce();bf();wn();WN=process.env.BOSTRAT_STATE_DIR||Tp.join(process.env.HOME||HN.tmpdir(),".bostrat"),vx=(e=process.env)=>!!e.BOSTRAT_CURSOR_USAGE_COOKIE,qN="https://cursor.com/api/dashboard/get-filtered-usage-events",Sx=100;VN=5*6e4,gr={at:0,rows:null,error:null},_l=()=>Tp.join(WN,"cursor-usage.json");JN=5e3;Ap=()=>({enabled:vx(),lastPollAt:gr.at||null,error:gr.error}),vp=new Map});import Qa from"node:fs/promises";import ut from"node:path";import ZN from"node:os";import QN from"node:net";import{randomUUID as eP}from"node:crypto";import{execFile as tP}from"node:child_process";import{promisify as nP}from"node:util";function lP(e){for(let t of Mp(e))if(!Ox.test(t))return t;return null}function Cx(e){let t=Mp(e),n=t.indexOf("--");return n===-1?t:t.slice(0,n)}function uP(e,t){let n=Cx(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 fP(e){let t=!1,n=!1;for(let r of Mp(e)){if(!t){if(Ox.test(r))continue;t=!0;continue}if(r.startsWith("-")){let o=r.indexOf("="),s=o===-1?null:r.slice(o+1);if(s&&_p(s))return`serves the filesystem root ("${s}")`;n=o===-1;continue}if(n){if(n=!1,_p(r))return`serves the filesystem root ("${r}")`;continue}if(_p(r))return`serves the filesystem root ("${r}")`;if(dP(r))return`serves a path that escapes the workspace ("${r}")`}return null}async function mP(e,t){if(ut.resolve(e)===ut.resolve(t))return!0;let[n,r]=await Promise.all([Il(e),Il(t)]);return n===r}async function hP(e){let n=(await Qa.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 Mx(ut.join(e,o.name,$p))?`./${o.name}`:null))).filter(Boolean)}async function wP(e,t){let[n,r]=await Promise.all([Il(e),Il(t)]),o=ut.relative(n,r);if(o&&(o.startsWith("..")||ut.isAbsolute(o)))throw Xa(`serve directory escapes the Track workspace: ${t}`)}async function gP(e,t){let n=fP(t);if(n)throw Xa(`refusing this view: the command ${n} \u2014 serve a build dir, not the filesystem root`);if(!await pP(e))throw Xa(`serve directory does not exist: ${e}`);let r=lP(t);if(!r)return;if(!r.includes("/")&&!await Ag(r,{loginShell:!0,failOpen:!0}))throw Xa(`\`${r}\` is not on the box's PATH \u2014 install it in the box first`);if(aP.test(t)||iP.has(r)||!sP.has(r)||!Cx(t).includes("run")||cP.test(t))return;let o=uP(e,t);if(await Mx(ut.join(o,$p)))return;let s=await hP(o),a=s.length?` \u2014 found one in ${s.join(", ")}; run bostrat-view from there`:"";throw Xa(`no ${$p} in ${o}${a}`)}async function Rn(){return en||(Ip||=(async()=>{try{en=JSON.parse(await Qa.readFile($x,"utf8"))}catch{en={views:[],adoptWatermarks:{}}}finally{Ip=null}return en.views||=[],en.adoptWatermarks||={},en})(),Ip)}async function nn(){await it($x,en)}function In(e,t,n={}){return A({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 Pp(e){return Za.get(e)||null}function Px(e,{name:t,box:n}={}){let r=Za.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 Za.set(e,o),o}function Op(e,t){let n=Za.get(e);n&&!n.finishedAt&&!n.viewId&&(n.viewId=t,n.step="booting")}function Rl(e,{error:t=null}={}){let n=Za.get(e);return!n||n.finishedAt?null:(n.finishedAt=Date.now(),t&&(n.error=t),n)}function Dx(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 Cp(e){return String(e||"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40).replace(/-+$/g,"")||"view"}function yP(e,t){let n=Cp(t),r=new Set(e.views.map(o=>o.slug));if(!r.has(n)&&!Ix.has(n))return n;for(let o=2;;o++){let s=`${n}-${o}`;if(!r.has(s)&&!Ix.has(s))return s}}function ei(e,{timeoutMs:t=1200}={}){return new Promise(n=>{let r=QN.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 Dp(e){if(process.platform!=="darwin"){await tn("fuser",["-k",`${e}/tcp`]).catch(()=>{});return}let t="";try{({stdout:t}=await tn("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 Bx(e=process.env){return new Set([Number(e.PORT)||3362,Number(e.BOSTRAT_TLS_PORT)||3443])}async function bP(e,t){let n=new Set(e.views.map(o=>o.port)),r=Bx();if(t){let o=Number(t);if(!Number.isInteger(o)||o<1024||o>65535)throw new Error("invalid port");if(r.has(o))throw new Error(`port ${o} is the node's own API port`);if(n.has(o))throw new Error(`port ${o} is already owned by a view`);return o}for(let o=Ex;o<=_x;o++)if(!(n.has(o)||r.has(o))&&!await ei(o,{timeoutMs:250}))return o;throw new Error(`no free view port in ${Ex}-${_x}`)}async function kP(e){try{let{stdout:t}=await tn(L,U("list-sessions","-F","#{session_name}"));return t.split(`
|
|
152
|
+
`).some(n=>n.trim()===ys(e))}catch{return!1}}async function ti(e){try{await tn(L,U("kill-session","-t",ys(e)))}catch{}}async function Bp(e){if(!await fa())throw new Error("host tmux server not running and auto-start failed \u2014 cannot spawn a view server");let t=ys(e.slug),n=e.serveCwd??e.cwd,r=ut.dirname(process.execPath);await tn(L,U("new-session","-d","-s",t,"-c",n)),await tn(L,U("send-keys","-t",t,"-l",`export PORT=${e.port} PATH="${r}:$PATH"`)),await tn(L,U("send-keys","-t",t,"Enter")),await tn(L,U("send-keys","-t",t,"-l",e.cmd)),await tn(L,U("send-keys","-t",t,"Enter"))}async function Rx(e,t=12){try{let{stdout:n}=await tn(L,U("capture-pane","-t",ys(e),"-p"));return String(n||"").trimEnd().split(`
|
|
153
153
|
`).filter(Boolean).slice(-t).join(`
|
|
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}
|
|
154
|
+
`)}catch{return""}}async function SP(e){if(e.state==="stopped"||e.state==="error")return!1;let t=e.origin!=="detected",n=t?await kP(e.slug):!0;if(t&&!n){let s=e.state==="starting";return e.state=s?"error":"stopped",s&&(e.error=await Rx(e.slug)||"server exited before listening"),_n.delete(e.id),await In(e,e.state,e.error?{error:e.error}:{}),!0}if(await ei(e.port))return _n.delete(e.id),e.lastHealthyAt=Date.now(),e.state!=="live"?(e.state="live",e.error=null,await In(e,"live"),!0):!1;if(e.state==="starting")return Date.now()-e.createdAt>xP?(e.state="error",e.error=await Rx(e.slug)||"never started listening",await In(e,"error",{error:e.error}),!0):!1;let o=(_n.get(e.id)||0)+1;return _n.set(e.id,o),o>=3&&e.state==="live"?(e.state="unhealthy",!0):!1}async function $l(){if(!Rp){Rp=!0;try{let e=await Rn(),t=!1;for(let n of e.views)await SP(n)&&(t=!0);t&&await nn()}finally{Rp=!1}}}async function Ol({trackId:e}={}){await $l();let t=await Rn();return(e?t.views.filter(r=>r.trackId===e):t.views).map(r=>({...r}))}async function jx(e){let n=(await Rn()).views.find(r=>r.id===e);return n?{...n}:null}function jp(e){return en&&en.views.find(t=>t.slug===e)||null}async function Lp({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 wP(r,u),await gP(u,n)},f=await Rn();if(i!=="detected"){let w=Cp(t),y=String(n).trim(),x=f.views.find(k=>k.trackId===e&&(k.slug===w||Cp(k.name)===w||k.cmd===y&&ut.resolve(k.serveCwd??k.cwd)===u));if(x)return vP(f,x,{cmd:y,box:a,branch:c,serveCwd:l?u:void 0,validate:d})}if(d&&await d(),f.views.filter(w=>w.state==="live"||w.state==="starting").length>=yr)throw new Error(`view cap reached (${yr} live) \u2014 stop one first`);let m={id:eP().slice(0,8),trackId:e,box:a||null,name:t.trim(),slug:yP(f,t),cmd:n?String(n).trim():null,cwd:r,serveCwd:u,branch:c,port:await bP(f,s),origin:i,state:"starting",error:null,createdAt:Date.now(),lastHealthyAt:null};return i!=="detected"&&await Bp(m),f.views.push(m),await nn(),await In(m,"created",{origin:i}),Op(e,m.id),i==="detected"&&await ei(m.port)&&(m.state="live",m.lastHealthyAt=Date.now(),await nn(),await In(m,"live")),{...m}}async function vP(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 mP(t.serveCwd??t.cwd,s);if((t.state==="live"||t.state==="starting")&&!i&&!c&&await ei(t.port))return await nn(),Op(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>=yr)throw new Error(`view cap reached (${yr} live) \u2014 stop one first`);return t.origin==="detected"?(await Dp(t.port),t.origin="agent"):await ti(t.slug),t.cmd=n,s!==void 0&&(t.serveCwd=s),t.state="starting",t.error=null,t.createdAt=Date.now(),_n.delete(t.id),await Bp(t),await nn(),await In(t,"created",{restarted:!0,reused:!0}),Op(t.trackId,t.id),{...t,reused:!0}}async function Lx(e){let n=(await Rn()).views.find(r=>r.id===e);if(!n)throw new Error("view not found");return n.origin==="detected"?await Dp(n.port):await ti(n.slug),n.state!=="stopped"&&(n.state="stopped",_n.delete(n.id),await nn(),await In(n,"stopped")),{...n}}async function Fx(e){let t=await Rn(),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>=yr)throw new Error(`view cap reached (${yr} live) \u2014 stop one first`);return await ti(n.slug),n.state="starting",n.error=null,n.createdAt=Date.now(),_n.delete(n.id),await Bp(n),await nn(),await In(n,"created",{restarted:!0}),{...n}}async function Ux(e){let t=await Rn(),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 Dp(r.port):await ti(r.slug),t.views.splice(n,1),_n.delete(r.id),await nn(),await In(r,"deleted"),{ok:!0,id:e,slug:r.slug}}async function Hx(e){let t=await Rn(),n=t.views.filter(r=>r.trackId===e);for(let r of n)r.origin!=="detected"&&await ti(r.slug),_n.delete(r.id);return n.length&&(t.views=t.views.filter(r=>r.trackId!==e),await nn()),n.length}async function Wx(e,t){if(!e?.id||!Array.isArray(t)||!t.length)return[];let n=await Rn(),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 nn()),!o.length)return[];let a=new Set;for(let c of o)for(let l of String(c.text||"").matchAll(TP)){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(!Bx().has(c)&&!n.views.some(l=>l.port===c)&&await ei(c))try{i.push(await Lp({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 tn,rP,$x,Ex,_x,yr,oP,ys,Ix,Xa,sP,$p,aP,iP,cP,Mp,Ox,_p,dP,pP,Mx,Il,en,Ip,Za,Np,Nx,_n,xP,Rp,TP,Cl=M(()=>{ze();ce();Vn();tn=nP(tP),rP=process.env.BOSTRAT_STATE_DIR||ut.join(process.env.HOME||ZN.tmpdir(),".bostrat"),$x=ut.join(rP,"views.json"),[Ex,_x]=(process.env.BOSTRAT_VIEW_PORTS||"4300-4349").split("-").map(e=>Number(e)||0),yr=Number(process.env.BOSTRAT_VIEW_MAX)||3,oP="bostrat-view-",ys=e=>`${oP}${e}`,Ix=new Set(["bostrat","www","api","views","app"]),Xa=e=>Object.assign(new Error(e),{status:400}),sP=new Set(["npm","pnpm","yarn","bun"]),$p="package.json",aP=/[|;&`]|\$\(/,iP=new Set(["sh","bash","zsh","dash","env","exec","nohup","time"]),cP=/(^|\s)(-C|--cwd|--manifest-path|--dir)(=|\s)/,Mp=e=>String(e).trim().split(/\s+/).filter(Boolean),Ox=/^[A-Za-z_][A-Za-z0-9_]*=/;_p=e=>e==="/",dP=e=>e===".."||e.startsWith("../")||e.includes("/../")||e.endsWith("/..");pP=e=>Qa.stat(e).then(t=>t.isDirectory(),()=>!1),Mx=e=>Qa.stat(e).then(t=>t.isFile(),()=>!1),Il=e=>Qa.realpath(e).catch(()=>ut.resolve(e));en=null,Ip=null;Za=new Map,Np=Number(process.env.BOSTRAT_VIEW_JOB_TIMEOUT)||3e5,Nx=12e3;_n=new Map,xP=12e4;Rp=!1;TP=/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::(\d{2,5}))?(?:\/|\b)/g});function Ml(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>EP?null:n}function _P(e,t){return e?.firstSeen==null||t?.ts==null||e.firstSeen<t.ts}async function qx(e){let t=e?.workspace?.branch;if(!t)return;let n;try{n=await j(e)}catch{return}let r=Ml(n);if(r==null)return;let o=(e.boxes||[]).find(u=>u.name===e.leadBox);if(await oo(e.leadBox,o?.agent).catch(()=>!1))return;let s=Date.now();if(s-(Fp.get(e.id)||0)<AP)return;Fp.set(e.id,s);let a;try{a=await Xt(e.cwd,{caller:"reconcile"})}catch{return}let i=await hs(e).catch(()=>[t]),c=(a||[]).find(u=>i.includes(u.headRefName));if(!c)return;let l=await fe(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 kn(l,c.number))){if(!_P(u,r))return;let f=await j(e).catch(()=>null);if(f&&Ml(f)==null)return;await A(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 A(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(()=>{}),Fp.delete(e.id)}var AP,Fp,EP,Up=M(()=>{ce();ps();Kn();xn();Va();ws();AP=6e3,Fp=new Map,EP=30*6e4});import Hp from"node:os";import{execFile as IP}from"node:child_process";import{promisify as RP}from"node:util";async function Nl(){if(process.platform!=="darwin")return Hp.freemem();try{let{stdout:e}=await $P("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:Hp.freemem()}catch{return Hp.freemem()}}var $P,Wp=M(()=>{$P=RP(IP)});import De from"node:fs/promises";import je from"node:path";import zx from"node:os";import{randomUUID as OP}from"node:crypto";import{execFile as CP}from"node:child_process";import{promisify as MP}from"node:util";import{chromium as NP}from"playwright-core";async function sn(){return so||(qp||=(async()=>{try{so=JSON.parse(await De.readFile(Yx,"utf8"))}catch{so={mocks:[]}}finally{qp=null}return so.mocks||=[],so})(),qp)}async function Tt(){await it(Yx,so)}function eS(e,t){on(e,t.v,t.videoRel?.endsWith(".webm")?"webm":"mp4"),t.posterRel&&on(e,t.v,"png")}function nt(e,t,n={}){return A({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=ri(e.cwd);return{html:je.join(n,`${e.id}.v${t}.html`),png:je.join(n,`${e.id}.v${t}.png`)}}function ni(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?je.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 Vp(e){return e.length<24||e.readUInt32BE(12)!==1229472850?null:{width:e.readUInt32BE(16),height:e.readUInt32BE(20)}}function Pl(e){return Buffer.isBuffer(e)&&e.length>24&&e.subarray(0,8).equals(UP)}function HP(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 WP(e){let t=Buffer.alloc(24),n=await De.open(e,"r");try{await n.read(t,0,24,0)}finally{await n.close()}return t}function qP({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 KP(e,t){return e.evaluate(async n=>{let r=new Image;await new Promise((m,w)=>{r.onload=m,r.onerror=()=>w(new Error("decode failed")),r.src=`data:image/png;base64,${n}`});let o=r.naturalWidth,s=r.naturalHeight;if(!o||!s||o*s>24e6)return null;let a=document.createElement("canvas");a.width=o,a.height=s;let i=a.getContext("2d",{willReadFrequently:!0});i.drawImage(r,0,0);let c=i.getImageData(0,0,o,s).data,l=[c[0],c[1],c[2]],u=o,d=s,f=-1,p=-1;for(let m=0;m<s;m++)for(let w=m*o*4,y=0;y<o;y++,w+=4)Math.abs(c[w]-l[0])+Math.abs(c[w+1]-l[1])+Math.abs(c[w+2]-l[2])>24&&(y<u&&(u=y),y>f&&(f=y),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 GP(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 VP({htmlPath:e,outPath:t,width:n}){let r=Date.now(),o;try{o=await NP.launch({executablePath:Xx,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:bs})).newPage();a.setDefaultTimeout(ks),await a.goto(`file://${e}`,{waitUntil:"load",timeout:ks}),await a.waitForTimeout(250);let i;for(let l=1;;l++)try{i=await a.screenshot({fullPage:!0,timeout:ks});break}catch(u){if(l>=3||!/captureScreenshot|Unable to capture/i.test(u?.message||""))throw u;await a.waitForTimeout(500)}try{let l=await KP(a,i),u=l&&qP({...l,pad:BP*bs,align:bs});u&&(i=await GP(a,i,{bg:l.bg,crop:u}))}catch{}if(i.length<Zx)throw new ht(`capture produced a ${i.length}-byte image`);let c=Vp(i);if(!c)throw new ht("output is not a PNG");return await De.writeFile(t,i),{width:Math.round(c.width/bs),height:Math.round(c.height/bs),dpr:bs,renderMs:Date.now()-r}}catch(s){throw s instanceof ht?s:new ht(/timeout/i.test(s?.message||"")?`render timed out after ${ks}ms`:"chromium render failed",s?.message||"")}finally{await o.close().catch(()=>{})}}async function Vx({htmlPath:e,outPath:t,width:n}){let r=await De.mkdtemp(je.join(zx.tmpdir(),"bostrat-mock-profile-")),o=Date.now(),s="";try{try{s=(await Gp(Kx,["--headless","--no-remote","--profile",r,`--window-size=${n}`,"--screenshot",t,`file://${e}`],{env:{...process.env,MOZ_HEADLESS:"1"},timeout:ks,killSignal:"SIGKILL",maxBuffer:4194304})).stderr||""}catch(c){throw s=c?.stderr||"",c?.code==="ENOENT"?new ht(`firefox not found (${Kx})`,""):new ht(c?.killed?`render timed out after ${ks}ms`:"firefox exited non-zero",s)}let a=await De.stat(t).catch(()=>null);if(!a||a.size<Zx)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=Vp(await WP(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 De.rm(r,{recursive:!0,force:!0}).catch(()=>{})}}async function zP(e){if(PP==="firefox")return Vx(e);try{return await VP(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 ${Xx} 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 tS(e){return Kp=Kp.then(e,e),Kp}async function nS(e,t){let n=await sn(),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 Nl();if(o<jP)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 JP({htmlPath:s.html,outPath:s.png,width:r.width});if(!n.mocks.some(i=>i.id===e)){await De.rm(s.png,{force:!0}).catch(()=>{});return}zp(r,{v:t,source:"html",htmlRel:je.relative(r.cwd,s.html),pngRel:je.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}),rn(r),on(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}),rn(r)}}}function zp(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 YP({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?DP:Number(r);if(!Number.isInteger(o)||o<320||o>4e3)throw new Error("invalid width (320-4000)");return o}async function oi({trackId:e,group:t}={}){let n=await sn(),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 $n(e){let n=(await sn()).mocks.find(r=>r.id===e);return n?{...n}:null}function rS({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&&!Pl(t))throw new Error("image must be a PNG");if(n){let o=HP(n);if(!o)throw new Error("video must be an mp4 or webm (magic-byte check failed)");if(n.length>Gx)throw new Error(`video must be ${Gx/(1024*1024)} MB or smaller (got ${Math.round(n.length/(1024*1024))} MB)`);return o}return null}async function oS({trackId:e,name:t,html:n,image:r,video:o,cwd:s,width:a,group:i,box:c,agent:l,prompt:u}){let d=YP({trackId:e,name:t,cwd:s,width:a}),f=typeof n=="string"&&n.trim(),p=rS({html:n,image:r,video:o}),m=await sn(),w={id:OP().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 De.mkdir(ri(s),{recursive:!0}),m.mocks.push(w),f?(await De.writeFile(At(w,1).html,n),await Tt(),await nt(w,"created"),rn(w),on(w,1,"html"),tS(()=>nS(w.id,1))):o?(await aS(w,1,o,p),await Tt(),await nt(w,"created"),await nt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr,durationS:w.versions[0]?.durationS??null}),rn(w),eS(w,w.versions[0])):(await iS(w,1,r),await Tt(),await nt(w,"created"),await nt(w,"ready",{version:1,width:w.width,height:w.height,dpr:w.dpr}),rn(w),on(w,1,"png")),{...w}}async function sS(e,{html:t,image:n,video:r,width:o}={}){let a=(await sn()).mocks.find(u=>u.id===e);if(!a)throw new Error("mock not found");let i=typeof t=="string"&&t.trim(),c=rS({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 De.mkdir(ri(a.cwd),{recursive:!0}),i?(a.state="queued",await De.writeFile(At(a,l).html,t),await Tt(),await nt(a,"updated",{version:l}),rn(a),on(a,l,"html"),tS(()=>nS(a.id,l))):r?(await aS(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}),rn(a),eS(a,a.versions.find(u=>u.v===l))):(await iS(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}),rn(a),on(a,l,"png")),{...a}}async function XP(e){try{let{stdout:t}=await Gp(LP,["-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 ZP(e,t){try{await Gp(FP,["-y","-v","error","-i",e,"-frames:v","1","-f","image2",t],{timeout:Qx,killSignal:"SIGKILL",maxBuffer:4*1024*1024});let n=await De.stat(t).catch(()=>null);return!!n&&n.size>0}catch{return!1}}async function aS(e,t,n,r){let o=ri(e.cwd),s=je.join(o,`${e.id}.v${t}.${r.ext}`),a=je.join(o,`${e.id}.v${t}.poster.png`);await De.writeFile(s,n);let i=await XP(s),c=await ZP(s,a);zp(e,{v:t,source:"video",htmlRel:null,pngRel:null,videoRel:je.relative(e.cwd,s),posterRel:c?je.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 iS(e,t,n){let r=Vp(n);if(!r)throw new Error("image must be a PNG");let o=At(e,t);await De.writeFile(o.png,n),zp(e,{v:t,source:"image",htmlRel:null,pngRel:je.relative(e.cwd,o.png),width:r.width,height:r.height,dpr:1,renderMs:null,createdAt:Date.now()}),e.width=r.width}async function cS(e){let t=await sn(),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 lS(e){let t=await sn(),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 De.rm(s.html,{force:!0}).catch(()=>{}),await De.rm(s.png,{force:!0}).catch(()=>{})}for(let o of r.versions||[])for(let s of[o.videoRel,o.posterRel])s&&await De.rm(je.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 uS(e){let n=(await sn()).mocks.filter(i=>i.trackId===e.id);if(!n.length)return 0;let r=je.join(Jx,"mock-archive",e.id),o=ri(e.cwd),s=je.join(r,".bostrat","mocks");await De.mkdir(s,{recursive:!0});let a=await De.readdir(o).catch(()=>[]);for(let i of a)await De.copyFile(je.join(o,i),je.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 De.stat(je.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(),rn(i)}return await Tt(),n.length}async function dS(e){let t=await sn(),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 fS(){if(!Ae())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await We("mocks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await sn();for(let r of n.mocks){await rn(r);for(let o of r.versions||[])o.htmlRel&&await on(r,o.v,"html"),(o.pngRel||o.posterRel)&&await on(r,o.v,"png"),o.videoRel&&await on(r,o.v,o.videoRel.endsWith(".webm")?"webm":"mp4")}return{pushed:n.mocks.length,source:"plane"}}var Gp,Jx,Yx,PP,Xx,Kx,DP,bs,BP,Zx,ks,jP,Gx,LP,FP,Qx,ht,so,qp,rn,on,ri,UP,JP,Kp,xs=M(()=>{ce();Vn();Wp();Jn();Gp=MP(CP),Jx=process.env.BOSTRAT_STATE_DIR||je.join(process.env.HOME||zx.tmpdir(),".bostrat"),Yx=je.join(Jx,"mocks.json"),PP=process.env.BOSTRAT_MOCK_ENGINE||"chromium",Xx=process.env.BOSTRAT_CHROMIUM_BIN||"/usr/bin/chromium",Kx=process.env.BOSTRAT_FIREFOX_BIN||"firefox",DP=1300,bs=2,BP=32,Zx=1500,ks=Number(process.env.BOSTRAT_MOCK_TIMEOUT_MS)||75e3,jP=700*1024*1024,Gx=100*1024*1024,LP=process.env.BOSTRAT_FFPROBE_BIN||"ffprobe",FP=process.env.BOSTRAT_FFMPEG_BIN||"ffmpeg",Qx=2e4,ht=class extends Error{constructor(t,n=""){super(n?`${t}
|
|
155
155
|
${n.trim().split(`
|
|
156
156
|
`).slice(-6).join(`
|
|
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
|
|
159
|
-
`)[0]}`),{pushed:!1,reason:"push-failed"}}}function
|
|
157
|
+
`)}`:t),this.name="MockRenderError"}},so=null,qp=null;rn=e=>tt("mocks",{...e},{trackId:e.trackId}).catch(()=>{}),on=(e,t,n)=>{let r=(e.versions||[]).find(s=>s.v===t),o=n==="mp4"||n==="webm"?ni(e,r,"video"):r&&ni(e,r,n)||(n==="html"?At(e,t).html:null);return o?zn("mocks",e.id,t,n,o).catch(()=>{}):Promise.resolve()};ri=e=>je.join(e,".bostrat","mocks");UP=Buffer.from([137,80,78,71,13,10,26,10]);JP=zP,Kp=Promise.resolve()});import QP from"node:fs/promises";import gS from"node:path";import eD from"node:os";import{randomUUID as yS}from"node:crypto";async function Cn(){return On||(Jp||=(async()=>{let e=!0;try{On=JSON.parse(await QP.readFile(bS,"utf8"))}catch{On={flows:[]},e=!1}finally{Jp=null}if(On.flows||=[],!e&&Ae()){let{records:t,source:n}=await We("flows").catch(()=>({records:[],source:"error"}));if(n==="plane"){for(let r of t)!r.deletedAt&&r.data?.id&&On.flows.push(r.data);On.flows.length&&await Ss().catch(()=>{})}}return On})(),Jp)}async function Ss(){await it(bS,On)}function Yp(e,t,n={}){return A({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 kS({trackId:e,steps:t,prevSteps:n=[]}){if(!Array.isArray(t)||t.length<1||t.length>pS)throw new Error(`steps must be an array of 1-${pS}`);let r=await oi({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>hS)throw new Error(`${l}: caption over ${hS} chars`);let d=c.title==null?null:String(c.title).trim()||null;if(d&&d.length>wS)throw new Error(`${l}: title over ${wS} 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],w=null;if(c.v!=null&&(w=Number(c.v),!Number.isInteger(w)||!(m.versions||[]).some(k=>k.v===w)))throw new Error(`${l}: "${m.name}" has no captured v${c.v} to pin`);let y=o.findIndex(k=>k.mockId===m.id),x=y>=0?o.splice(y,1)[0].id:yS().slice(0,8);s.push({id:x,mockId:m.id,caption:u,title:d,v:w})}let a={};for(let i of s)i.mockId in a||(a[i.mockId]=r.find(c=>c.id===i.mockId)?.readyVersion??null);return{steps:s,resolved:a}}function nD(e){if(typeof e!="string"||!e.trim())throw new Error("name (string) required");if(e.trim().length>mS)throw new Error(`name over ${mS} chars`);return e.trim()}async function xS({trackId:e}={}){let t=await Cn();return(e?t.flows.filter(r=>r.trackId===e):t.flows).map(r=>({...r}))}async function rD(e){let n=(await Cn()).flows.find(r=>r.id===e);return n?{...n}:null}async function SS(e){let t=await rD(e);if(!t)return null;let n=new Map((await oi({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 vS({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=nD(t),i=await Cn(),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 kS({trackId:e,steps:n}),d=Date.now(),f={id:yS().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 Ss(),await Yp(f,"created"),Dl(f),{...f}}async function TS(e,{steps:t,note:n}={}){let o=(await Cn()).flows.find(i=>i.id===e);if(!o)throw new Error("flow not found");let{steps:s,resolved:a}=await kS({trackId:o.trackId,steps:t,prevSteps:o.steps});return o.version+=1,o.versions.push({v:o.version,steps:s,resolved:a,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),o.steps=s,o.updatedAt=Date.now(),await Ss(),await Yp(o,"updated",{version:o.version,...n?{note:String(n).slice(0,200)}:{}}),Dl(o),{...o}}async function AS(e){let t=await Cn(),n=t.flows.findIndex(o=>o.id===e);if(n===-1)throw new Error("flow not found");let r=t.flows[n];return t.flows.splice(n,1),await Ss(),await Yp(r,"deleted"),Ot("flows",r.id).catch(()=>{}),{ok:!0,id:e,name:r.name}}async function ES(e){return(await Cn()).flows.filter(n=>n.steps.some(r=>r.mockId===e)).map(n=>({...n}))}async function _S(e){let n=(await Cn()).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(),Dl(r);return n.length&&await Ss(),n.length}async function IS(e){let t=await Cn(),n=t.flows.filter(r=>r.trackId===e);if(n.length){t.flows=t.flows.filter(r=>r.trackId!==e),await Ss();for(let r of n)Ot("flows",r.id).catch(()=>{})}return n.length}async function RS(){if(!Ae())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await We("flows");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Cn();for(let r of n.flows)await Dl(r);return{pushed:n.flows.length,source:"plane"}}var tD,bS,pS,mS,hS,wS,On,Jp,Dl,Xp=M(()=>{ce();Vn();xs();Jn();tD=process.env.BOSTRAT_STATE_DIR||gS.join(process.env.HOME||eD.tmpdir(),".bostrat"),bS=gS.join(tD,"flows.json"),pS=20,mS=80,hS=500,wS=80,On=null,Jp=null;Dl=e=>tt("flows",{...e},{trackId:e.trackId}).catch(()=>{})});import Pn from"node:fs/promises";import Dn from"node:path";import oD from"node:os";import{randomUUID as sD}from"node:crypto";function si(e){return aD.filter(({re:t})=>t.test(e)).map(({label:t})=>t)}async function an(){return Mn||(Zp||=(async()=>{let e=!0;try{Mn=JSON.parse(await Pn.readFile(OS,"utf8"))}catch{Mn={runbooks:[]},e=!1}finally{Zp=null}return Mn.runbooks||=[],!e&&Ae()&&await iD().catch(()=>{}),Mn})(),Zp)}async function io(){await it(OS,Mn)}function jl(e){return ai(tt("runbooks",{...e},{trackId:e.trackId}))}async function iD(){let{records:e,source:t}=await We("runbooks");if(!(t!=="plane"||!e.length)){await Pn.mkdir(Bl,{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 ia("runbooks",r.id,o.v,"md");typeof s=="string"&&await Pn.writeFile(Nn(r,o.v),s).catch(()=>{})}Mn.runbooks.push(r)}Mn.runbooks.length&&await io()}}function vs(e,t,n={}){return A({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 Nn(e,t){return Dn.join(Bl,`${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 MS(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=Buffer.byteLength(e,"utf8");if(t>$S)throw new Error(`runbook is ${Math.round(t/1024)} KB \u2014 over the ${$S/1024} KB cap; a doc that size belongs in the repo, not a runbook`);let n=si(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 cD(e,t){if(e.trackDeletedAt)throw new Error(`runbook is orphaned (its Track was deleted) \u2014 cannot ${t} it`)}async function lD({trackId:e,status:t}={}){let n=await an(),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 NS({trackId:e,status:t}={}){let n=await an(),r=await lD({trackId:e,status:t});if(!Ae())return{runbooks:r,fleetDegraded:!1};let{records:o,source:s}=await We("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 Qp(e){let t=await uD(e);if(t)return t;if(!Ae())return null;let{records:n,source:r}=await We("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 PS(e,t){let n=await ia("runbooks",e.id,t,"md");return typeof n=="string"?n:null}async function uD(e){let n=(await an()).runbooks.find(r=>r.id===e);return n?{...n}:null}async function DS({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=MS(r),c=await an(),l={id:sD().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 Pn.mkdir(Bl,{recursive:!0}),await Pn.writeFile(Nn(l,1),r),l.versions.push({v:1,mdRel:Dn.relative(ao,Nn(l,1)),bytes:i,note:null,createdAt:Date.now()}),c.runbooks.push(l),await io(),await vs(l,"created",{version:1}),jl(l),ai(zn("runbooks",l.id,1,"md",Nn(l,1))),{...l}}async function BS(e,{md:t,note:n}={}){let o=(await an()).runbooks.find(i=>i.id===e);if(!o)throw new Error("runbook not found");cD(o,"update");let s=MS(t),a=o.version+1;return o.version=a,o.updatedAt=Date.now(),await Pn.mkdir(Bl,{recursive:!0}),await Pn.writeFile(Nn(o,a),t),o.versions.push({v:a,mdRel:Dn.relative(ao,Nn(o,a)),bytes:s,note:n?String(n).slice(0,200):null,createdAt:Date.now()}),await io(),await vs(o,"updated",{version:a,...n?{note:String(n).slice(0,200)}:{}}),jl(o),ai(zn("runbooks",o.id,a,"md",Nn(o,a))),{...o}}async function jS(e,t){if(t!=="open"&&t!=="done")throw new Error('status must be "open" or "done"');let r=(await an()).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 io(),await vs(r,t==="done"?"done":"reopened"),jl(r),{...r})}async function LS(e){let t=await an(),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 Pn.rm(Dn.join(ao,o.mdRel),{force:!0}).catch(()=>{});return await io(),r.trackDeletedAt||await vs(r,"deleted"),ai(Ot("runbooks",r.id)),{ok:!0,id:e,name:r.name}}async function FS(e){let n=(await an()).runbooks.filter(r=>r.trackId===e&&!r.trackDeletedAt);for(let r of n)r.trackDeletedAt=Date.now(),r.updatedAt=Date.now(),await vs(r,"orphaned"),r.cwd=null,jl(r);return n.length&&await io(),n.length}async function US(e){let t=await an(),n=t.runbooks.filter(r=>r.trackId===e);for(let r of n){await vs(r,"deleted");for(let o of r.versions||[])await Pn.rm(Dn.join(ao,o.mdRel),{force:!0}).catch(()=>{});ai(Ot("runbooks",r.id))}return n.length&&(t.runbooks=t.runbooks.filter(r=>r.trackId!==e),await io()),n.length}async function HS(){if(!Ae())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await We("runbooks");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await an();for(let r of n.runbooks){await tt("runbooks",{...r},{trackId:r.trackId}).catch(()=>{});for(let o of r.versions||[])await zn("runbooks",r.id,o.v,"md",Dn.join(ao,o.mdRel)).catch(()=>{})}return{pushed:n.runbooks.length,source:"plane"}}var ao,OS,Bl,$S,aD,Mn,Zp,ai,Ll=M(()=>{Vn();ce();Jn();ao=process.env.BOSTRAT_STATE_DIR||Dn.join(process.env.HOME||oD.tmpdir(),".bostrat"),OS=Dn.join(ao,"runbooks.json"),Bl=Dn.join(ao,"runbooks"),$S=256*1024,aD=[{label:"Anthropic API key",re:/\bsk-ant-[A-Za-z0-9_-]{20,}/},{label:"OpenAI-style API key",re:/\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{32,}\b/},{label:"Stripe secret key",re:/\bsk_(?:live|test)_[A-Za-z0-9]{16,}/},{label:"GitHub token",re:/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}|\bgithub_pat_[A-Za-z0-9_]{22,}/},{label:"AWS access key id",re:/\bAKIA[0-9A-Z]{16}\b/},{label:"Slack token",re:/\bxox[baprs]-[A-Za-z0-9-]{10,}/},{label:"private key block",re:/-----BEGIN [A-Z ]*PRIVATE KEY-----/},{label:"JWT",re:/\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/}];Mn=null,Zp=null;ai=e=>Promise.resolve(e).catch(()=>{})});import em from"node:fs/promises";import ii from"node:path";import{randomUUID as dD}from"node:crypto";function nm(e){let t=String(e||"").toLowerCase().replace(/^\./,"");return fD.has(t)?"image":pD.has(t)?"doc":null}function mD(e){let r=ii.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 KS(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>WS)throw new Error(`file too large \u2014 max ${Math.floor(WS/(1024*1024))} MB`);let r=mD(t),o=ii.extname(r).slice(1),s=nm(o);if(!s)throw new Error(`unsupported file type "${o||"unknown"}" \u2014 attach an image or a document`);let a=dD().slice(0,8),i=tm(e);await em.mkdir(i,{recursive:!0});let c=`${a}-${r}`;return await em.writeFile(ii.join(i,c),n,{mode:384}),await em.chmod(ii.join(i,c),384).catch(()=>{}),{id:a,name:r,rel:`${qS}/${c}`,kind:s,size:n.length}}function GS(e){let t=e.map(r=>`\`${r}\``).join(", ");return`[Attached \u2014 please read ${e.length===1?"this file":`these ${e.length} files`}: ${t}]`}function rm(e){return e.role==="user"&&hD.test((e.text||"").trim())}var qS,tm,WS,fD,pD,hD,om=M(()=>{qS=".bostrat/uploads",tm=e=>ii.join(e,qS),WS=Number(process.env.BOSTRAT_MAX_UPLOAD_BYTES)||24*1024*1024,fD=new Set(["png","jpg","jpeg","gif","webp","svg","bmp","tif","tiff","heic","heif","avif","ico"]),pD=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"]);hD=/\[Attached — please read (?:this file|these \d+ files): [^\]]*\]$/});function co(e,t,n=Date.now()){return t?e.idleSince==null?(e.idleSince=n,!1):n-e.idleSince>=6500:(e.idleSince=null,!1)}var VS=M(()=>{});import{execFile as wD}from"node:child_process";import{promisify as gD}from"node:util";function sm(e){return`refs/bostrat/wip/${e}`}function kD(e,t=Date.now()){return`refs/bostrat/rescue/${e}-${t}`}function YS(e=process.env){let t=String(e.BOSTRAT_WIP_SYNC_MS??"").trim().toLowerCase();return t!=="0"&&t!=="off"&&t!=="false"}function xD(e=process.env){let t=Number(e.BOSTRAT_WIP_SYNC_MS);return Number.isFinite(t)&&t>0?t:bD}async function am(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await br(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await br(r,["remote","get-url","origin"])}catch{return zS.has(r)||(zS.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 es(r,"bostrat: wip snapshot"),{stdout:s}=await br(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(Fl.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=sm(n);return await br(r,["push","--force","origin",`${o}:${i}`],JS),Fl.set(r,a),await A({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 im(e,{log:t=console}={}){let{id:n,cwd:r}=e??{};if(!n||!r)return{pushed:!1,reason:"no-workspace"};try{await br(r,["rev-parse","--git-dir"])}catch{return{pushed:!1,reason:"not-a-repo"}}try{await br(r,["remote","get-url","origin"])}catch{return{pushed:!1,reason:"no-origin"}}try{let o=await es(r,"bostrat: transfer rescue snapshot"),{stdout:s}=await br(r,["rev-parse",`${o}^{tree}`]),a=s.trim();if(Fl.get(r)===a)return{pushed:!1,reason:"unchanged"};let i=kD(n);return await br(r,["push","origin",`${o}:${i}`],JS),Fl.set(r,a),await A({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 XS({listTracks:e,sweepMs:t=xD(),log:n=console}){let r=!1,o=async()=>{if(!r){r=!0;try{let a=await e()??[];for(let i of a)Be(i)||i?.kind==="setup"||await am(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 yD,bD,JS,br,Fl,zS,cm=M(()=>{Zc();ce();yn();yD=gD(wD),bD=12e4,JS=6e4;br=(e,t,n=15e3)=>yD("git",["-C",e,...t],{timeout:n,maxBuffer:4*1024*1024}),Fl=new Map,zS=new Set});import SD from"node:fs";import lm from"node:fs/promises";import vD from"node:path";function _D(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function ZS(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(!_D(t))return{restored:!1,reason:"plane not configured"};let s=Ro(e),a=vD.join(s,`${o}.ndjson`);try{if(SD.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(),ED),m;try{m=await n(`${i}/v1/spine/${encodeURIComponent(o)}?after=${u}&limit=${TD}`,{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 w=await m.json();for(let y of w.events||[])l.push(JSON.stringify(y));if(l.length>AD)return{restored:!1,reason:"event count over safety cap"};if(w.next==null)break;u=w.next}if(!l.length)return{restored:!1,reason:"unknown to plane"};await lm.mkdir(s,{recursive:!0});let d=`${a}.pull-${process.pid}-${Date.now()}`;return await lm.writeFile(d,l.join(`
|
|
160
160
|
`)+`
|
|
161
|
-
`),await
|
|
161
|
+
`),await lm.rename(d,a),r.log?.(`[spine-pull] rehydrated ${o}: ${l.length} event(s) \u2192 ${a}`),{restored:!0,rows:l.length}}var TD,AD,ED,QS=M(()=>{et();ce();TD=1e3,AD=5e5,ED=3e4});var Cv={};Wu(Cv,{HANDOFF_LATEST_REL:()=>lo,KNOWN_AGENTS:()=>yi,TRACK_NAME_MAX:()=>Sm,addTrackRepo:()=>jm,adoptTrack:()=>nh,agentAvailability:()=>ql,announceNodeUpdate:()=>ln,answerFullscreenOffer:()=>Jm,answerPrompt:()=>Gl,archiveTrack:()=>po,archiveTrackForMergedPr:()=>An,augmentFirstPromptAfterHandoff:()=>lv,buildAgentCmd:()=>iv,canResumeSubtask:()=>Rv,cancelQueued:()=>ki,cancelQueuedToDraft:()=>Om,cancelStopJob:()=>xi,cancelSubtask:()=>nu,changedRepos:()=>gs,commitTransferOut:()=>eu,createSetupTrack:()=>Um,createTrack:()=>Jl,deleteTrack:()=>Wm,demoteTransferredTracks:()=>th,ensureLeadEpochs:()=>Zl,forceMergeSubtask:()=>ah,getHandoffJob:()=>Ms,getSubtaskJob:()=>RB,getTrack:()=>ne,getTransferOutJob:()=>Zm,healDeadLead:()=>wi,interruptLead:()=>zl,isArchiving:()=>bB,isHandoffNoteEcho:()=>ym,isInterruptEcho:()=>bm,isModelSwitchEcho:()=>km,leadCliDead:()=>qm,listSubtaskJobs:()=>mo,listTracks:()=>re,matchTranscriptsToBoxes:()=>fv,mergeNotifyMessage:()=>Av,navigatePrompt:()=>$m,notifyTrackForMergedPr:()=>En,notifyTrackForResolvedPr:()=>Yl,pendingOffers:()=>Im,queueShipCompleteNotify:()=>Mm,queuedMessages:()=>uo,reapArchivedOrphans:()=>zm,reconcileDemotedTracks:()=>Vm,reconcileHandoffOutcome:()=>Ev,reconcileInterruptedHandoffs:()=>Xm,reconcileInterruptedSubtasks:()=>rh,reconcileNodeUpdateNotices:()=>Em,reconcileSubtaskOutcome:()=>Iv,refreshStandingInstructions:()=>Fm,refuseIfTransferred:()=>eh,rehydrateLead:()=>vi,removeTrackRepo:()=>Lm,renameTrack:()=>Gm,reposOf:()=>gt,resolveTrackBox:()=>Ya,resolveTrackBoxByHookToken:()=>_m,restartLead:()=>Xl,resumeAgentCmd:()=>Am,sayToLead:()=>It,sendOptionsForAgent:()=>Jt,setTrackDraft:()=>Dm,setTrackModel:()=>Bm,startLeadHandoff:()=>Ql,startSpinUpView:()=>Nm,startSubtask:()=>sh,startTransferOut:()=>Qm,stopJobActive:()=>hv,subtaskMessages:()=>Rm,trackMessages:()=>Kl,unarchiveTrack:()=>Km});import H from"node:fs/promises";import q from"node:path";import{randomUUID as Os}from"node:crypto";import{execFile as ID}from"node:child_process";import{promisify as RD}from"node:util";function iv(e,{skipPermissions:t=!0}={}){let n=Wl[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 Am(e,t,{skipPermissions:n=!0,cwd:r=null,peerName:o=null}={}){let s=Wl[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&&Ee(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 Cs(e){let t=Wl[e];if(t)return process.env[t.envVar]||t.base}async function ql({maxAgeMs:e=6e4}={}){if(kr.value&&Date.now()-kr.at<e)return kr.value;let t=kr.value,n=await Promise.all(yi.map(async r=>{let o=await vc(Cs(r),{retries:[]});return o===Po?[r,t?.[r]??!0]:[r,o===Sc]}));return kr.value=Object.fromEntries(n),kr.at=Date.now(),kr.value}async function bi(e,t=!0){let n=iv(e,{skipPermissions:t});if(await vc(n)===No){kr.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.":gd[r]?`Install it on this node: ${gd[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 lv(e,t=lo){return`${e.trim()}
|
|
162
162
|
|
|
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
|
|
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()}
|
|
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 ym(e){return e.role==="user"&&CD.test((e.text||"").trim())}async function z(){return Et||(um||=(async()=>{try{Et=JSON.parse(await H.readFile(av,"utf8"))}catch{Et={tracks:[]}}finally{um=null}return Et})(),um)}async function Y(){await it(av,Et)}function PD(e,t){let n=JSON.stringify({trackId:e,...t})+`
|
|
164
|
+
`;return dm=dm.then(()=>H.mkdir(cn,{recursive:!0})).then(()=>H.appendFile($D,n)).catch(()=>{}),dm}async function ln({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 H.readFile(fm,"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)Be(d)||!d.leadBox||(pe(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 Y().catch(()=>{}),s==="available")try{await H.mkdir(q.dirname(fm),{recursive:!0}),await H.writeFile(fm,JSON.stringify({current:e,latest:t,at:Date.now()}))}catch{}return{announced:u}}async function Em({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)||DD(e,c)||(pe(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 Y().catch(()=>{}),{reconciled:o>0,announced:o,targets:[...s]}}function DD(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 uv(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=>BD.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 dv(e,t,n,{kind:r}={}){let o=await ie(t);if(!o)throw new Error(`box "${t}" is not running`);let s=Cs(o.agent);await er({cwd:o.cwd,sessionId:o.sessionId,sessionName:t,agent:o.agent,agentCmd:s}),await He(t,n,{...Jt(o.agent,n),kind:r||"orchestration"});let a=await Ea({sessionName:t,agent:o.agent,agentCmd:s,text:n,trackId:e,flow:"orchestration"});await Z({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=oe(i,e);if(!c)return;let l=c.boxes.find(u=>u.name===t);pe(c,"orchestration-prompt",{box:t,agent:l?.agent||o.agent,boxRole:l?.role,prompt:n,kind:r||"orchestration"}),await Y()}async function jD(e){let t=q.join(Ro(e),`${e.id}.ndjson`),n;try{n=await H.stat(t)}catch{return null}let r=ev.get(e.id);if(r&&r.mtimeMs===n.mtimeMs)return r.at;let o=null;for(let s of await j(e)){if(s.type!=="turn.agent")continue;let a=s.payload?.ts??s.ts;a&&(!o||a>o)&&(o=a)}return ev.set(e.id,{mtimeMs:n.mtimeMs,at:o}),o}async function LD(e,t){let n=t.find(r=>r.name===e.leadBox);return n?.state!=="live"?{working:!1,prompted:!1}:dp(n.name,n.agent)}async function _t(e,t=null){let n=t??new Map((await Ct()).map(c=>[c.name,c])),r=!!e.transferredTo,o=e.boxes.map(c=>{let l=r?null:n.get(c.name),u=xr.has(c.state)?c.state:l?c.role==="lead"?"live":c.state:"stopped";return{...c,state:u,cwd:l?.cwd||c.cwd,agent:l?.agent||c.agent,sessionId:l?.sessionId||c.sessionId}}),[s,{working:a,prompted:i}]=await Promise.all([jD(e),LD(e,o)]);return{...e,boxes:o,lastResponseAt:s,working:a,prompted:i,archiving:hi.has(e.id)}}function fv(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-FD,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 pv(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=Bg(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=fv(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 Ig(f.box,f.sessionId),n=!0}}return n}async function re(){let e=await z();await pv(e)&&await Y();let t=new Map((await Ct()).map(n=>[n.name,n]));return Promise.all(e.tracks.map(n=>_t(n,t)))}async function Ya(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 _m(e){let t=Vs(e);if(!t)return null;let n=await z();for(let r of n.tracks||[])for(let o of r.boxes||[])if(o.hookTokenHash&&Sw(o.hookTokenHash,t))return{track:r,box:o};return null}async function ne(e){let t=await z(),n=oe(t,e);return n?(await pv(t)&&await Y(),_t(n)):null}async function Im(e){let t=await j(e).catch(()=>[]),n;try{n=tr(t)}catch{return[]}return Gy(n).map(r=>({kind:r.kind,id:r.offerId??r.proposalId??r.id,ts:r.ts??null}))}function bm(e){if(e.role!=="user")return!1;let t=(e.text||"").trim();return(UD[e.agent]||[]).some(n=>n.test(t))}function km(e){let t=(e.text||"").trim();return t?HD.some(n=>n.test(t)):!1}async function Kl(e,t={}){let n=await ne(e);if(!n)return null;let r=new Set([...n.events.filter(g=>g.type==="orchestration-prompt"&&g.prompt).map(g=>String(g.prompt).trim()),...n.events.filter(g=>g.type==="said"&&g.text).map(g=>String(g.text).trim())]),o=Ms(n.id),s=!!(o&&!o.finishedAt&&o.step!=="done"&&!o.error),a=s&&o?.startedAt?[...o.fromBox?[{box:o.fromBox,from:o.startedAt,to:Date.now()+1e12}]:[],...o.toBox?[{box:o.toBox,from:o.startedAt,to:Date.now()+1e12}]:[]]:[],i=Pp(n.id),c=!!(i&&!i.finishedAt),l=c&&i.startedAt&&i.box?[{box:i.box,from:i.startedAt,to:Date.now()+1e12}]:[],u=[...Dx(n),...l],f={windows:[...hb(n),...wb(n,Kc),...a,...u]},m=(await Promise.all(n.boxes.map(g=>Af(g,t)))).flat(),w=m.map(g=>{let C={kind:"message",...g};if(C.role==="assistant"&&(C.text||"").trim()&&C.tools?.length){let se=Ma(C.agent).cliTools,_e=C.tools.filter(Oe=>!se.has(Oe));return{...C,tools:_e.length?_e:void 0}}return C}).map(g=>{if(g.role!=="user")return g;let C=Hc(g.text);return C?C.kind==="suppress"?null:{...g,note:C,text:By(C)}:g}).filter(Boolean).filter(g=>!(g.role==="user"&&r.has((g.text||"").trim()))).filter(g=>!bm(g)).filter(g=>!km(g)).filter(g=>!ym(g)).filter(g=>!rm(g)).filter(g=>g.role!=="assistant"||!!(g.text||"").trim()||!!g.images?.length).filter(g=>!gb(g,f)),y=w.filter(g=>g.failed&&g.text===Fr);if(y.length){let g=new Map;for(let C of y){if(!g.has(C.box)){let Oe=[...(await ve(C.box,{lines:Ue}).catch(()=>"")).matchAll(/^\s*■\s+(.+?)\s*$/gm)].map(V=>V[1]);g.set(C.box,Oe.length?Oe[Oe.length-1]:null)}let se=g.get(C.box);se&&(C.text=`\u25A0 ${se}`)}}let x=await Promise.all(n.boxes.map(g=>Yb(g,t)));await yx(n,x.flat());let{agentTurnBoxes:k}=await wx(n,w);for(let g of n.boxes)k.has(g.name)&&g.cwd&&pc(g.cwd);await Kk(n),await Wx(n,w).catch(()=>{});for(let g of n.boxes)g.agent==="codex"&&g.state==="live"&&await kx(n,g),g.agent==="cursor"&&g.state==="live"&&await Ax(n,g);let T=await Promise.all(n.boxes.map(g=>Jb(g,t)));await gx(n,T.flat().filter(g=>!pm(g.box,g.ts,u)));let _=await j(n),P=c&&i.viewId?i.viewId:null,N=r0(_),O=tr(_).filter(g=>!(g.kind==="message"&&(bm(g)||km(g)||ym(g)||rm(g)))&&!(g.kind==="view-card"&&P&&g.viewId===P)),$=o0(O,N),R=O.filter(g=>!(g.kind==="message"&&g.role==="assistant"&&!$.has(g)&&pm(g.box,g.ts,N))),ee=of(_,R).map(g=>({kind:"command-batch",id:g.id,ts:g.pending?g.ts:(g.anchorTs??g.ts)-1,anchorTs:g.anchorTs,anchorBox:g.anchorBox,agent:g.agent,boxRole:g.boxRole,commands:g.commands,pending:g.pending})).filter(g=>!pm(g.anchorBox,g.anchorTs??g.ts,u)),de=qy(Ky([...R,...ee].filter(g=>g.ts!=null).sort((g,C)=>g.ts-C.ts))),be=qD(n),G=be.size?de.filter(g=>g.kind==="event"||!be.has(g.box??g.anchorBox)):de,I=n.boxes.find(g=>g.name===n.leadBox)||null,J=I?await ha(I.cwd,I.sessionId,{fallbackNewest:!1}):null;if(!J&&I){let g=[..._].reverse().find(C=>C.type==="context.usage"&&C.boxId===I.name);g?.payload?.window&&(J={pct:g.payload.pct??(g.payload.occupancy!=null?g.payload.occupancy/g.payload.window:null),occupancy:g.payload.occupancy??null,window:g.payload.window,windowKnown:!0,model:g.payload.model||null})}let X=[...m].reverse().find(g=>g.role==="assistant"&&g.model),he=X?.model||null,Se=[...n.events||[]].reverse().find(g=>g.type==="model-change"),Le=J?.model||he;Se&&(!X?.ts||Se.at>=X.ts)&&(Le=Se.to);let F=I?await ve(I.name,{lines:Ue}).catch(()=>""):"",D=I&&Xr(I.agent,F)?await hn(I.name).catch(()=>F):F,Q=I?cr(I,D):null;if(Q&&I){let g=Yc(I,Q,D);if(g){let C=fk(I.name,Q.promptId);C.action!=="give-up"&&(C.action==="send"&&(Ie(I.name,g.keys,{kind:"auto-reject"}).catch(()=>{}),C.first&&A(n,{type:"prompt.autoRejected",actor:"system",source:"pane",boxId:I.name,provider:I.agent,payload:{ts:Date.now(),promptId:Q.promptId,rule:g.id}}).catch(()=>{})),Q=null)}}!Q&&I&&!qo(I.agent,F)&&!uo(n.id).length&&(Q=nf(G,{leadBoxId:I.name,provider:I.agent,isAnswered:g=>_.some(C=>C.type==="prompt.answered"&&C.payload?.promptId===g)})),Q&&Gk(n,I,Q);let Ne=G.filter(g=>!(g.kind==="message"&&g.role==="assistant"&&g.ask&&!String(g.text||"").trim()&&!g.images?.length)),Wt=!!I&&xl(I.name,I.agent,F)&&!Q;I&&!I.readyAt&&(Wt||Q||F&&Ko(I.agent,F))&&(I.readyAt=Date.now(),await Y());let b=!!I&&!I.readyAt&&Date.now()-(n.createdAt||0)<WD;if(n.kind==="setup"&&n.setup?.provider&&!n.setup.connectedAt&&I&&Ko(I.agent,F)&&!Q){let g=await z(),C=oe(g,n.id);C?.setup&&!C.setup.connectedAt&&(C.setup.connectedAt=Date.now(),n.setup.connectedAt=C.setup.connectedAt,await A(C,{type:"provider.connected",actor:"system",source:"pane",boxId:I.name,provider:I.agent,payload:{provider:I.agent,ts:C.setup.connectedAt}}).catch(()=>{}),await Y())}let h=s?{running:!0,step:o.step,toAgent:o.toAgent||null}:null,v=null;if(c){let g=Date.now()-i.startedAt;if(i.viewId&&!Wt){let C=Rl(n.id);C&&(pe(n,"view-spinup",{box:i.box,startedAt:i.startedAt,endedAt:C.finishedAt}),await Y())}else if(Q||g>Nx&&!Wt&&!i.viewId||g>Np){let C=Q?"needs-input":g>Np?"timeout":"no-view";Rl(n.id,{error:C})&&await A(n,{type:"view.spinup.failed",actor:"orchestrator",source:"orchestrator",boxId:i.box,provider:I?.agent||null,payload:{ts:i.startedAt,name:i.name,reason:C}}).catch(()=>{})}else v={running:!0,step:i.step,name:i.name}}return{trackId:n.id,leadBox:n.leadBox,items:Ne,context:J?{pct:J.pct,occupancy:J.occupancy,window:J.window,windowKnown:J.windowKnown,model:J.model}:null,models:{current:Le,options:jk(I?.agent)},pendingPrompt:Q,working:Wt,stopping:hv(n.id),leadStarting:b,handoff:h,view:v,queued:uo(n.id).filter(g=>g.kind!=="ship-notify"&&g.kind!=="prompt-answer"),draft:n.draft||null,draftRev:n.draftRev||0}}function qD(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 Rm(e,t){let n=await ne(e);if(!n)return null;let r=mo(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 j(n),a=tr(s).filter(d=>d.box===o&&!(d.kind==="message"&&d.role==="assistant"&&d.ask&&!String(d.text||"").trim()&&!d.images?.length)),i=of(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 ve(o,{lines:Ue}).catch(()=>"");u=xl(o,l.agent||r.agent,d)}return{items:c,working:u,box:o,agent:r.agent,task:r.task}}async function Gl(e,{promptId:t,answers:n,text:r}={}){let o=oe(await z(),e);if(!o)throw new Error(`track "${e}" not found`);xi(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 ie(o.leadBox))throw new Error(`lead box "${o.leadBox}" is not running`);let i=await Cf(s);if(i||(i=await KD(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 He(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=Uy(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=ck(i,n||[]);await Ie(o.leadBox,m,{kind:"answer-prompt"}),c={mode:"keys",value:m.join(" "),count:m.length}}else{let m=dy(i,n||[]);await He(o.leadBox,m,{enter:!0,kind:"answer-prompt"}),c={mode:"text",value:m,count:1}}if(i.answerMode==="say"){let m=i.questions||[];for(let w=0;w<m.length;w++){let y=m[w],x=new Set(Array.isArray(n?.[w])?n[w]:[]),k=[...x].map(_=>y.options?.[_]).filter(Boolean).map(_=>({label:_.label,description:_.description||""})),T=(y.options||[]).map((_,P)=>x.has(P)?null:_?.label).filter(Boolean);Jf(o,s,i,{...c,groupId:i.groupId||i.promptId,questionIndex:w,questionTotal:i.questionTotal??m.length,header:y.header||"",question:y.question||"",multiSelect:!!y.multiSelect,chosen:k,rejected:T})}return await Z({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),{ok:!0,promptId:i.promptId,sent:c.count}}let l=i.questions?.[0]||{},u=new Set(Array.isArray(n?.[0])?n[0]:[]),d=[...u].map(m=>l.options?.[m]).filter(Boolean).map(m=>({label:m.label,description:m.description||""})),f=(l.options||[]).map((m,w)=>u.has(w)?null:m?.label).filter(Boolean),p=i.review?"review":/^claude:model-confirm:/.test(i.promptId||"")?"model-confirm":/^claude:resume-confirm:/.test(i.promptId||"")?"resume-confirm":void 0;return c={...c,groupId:i.groupId||i.promptId,questionIndex:i.questionIndex??0,questionTotal:i.questionTotal??1,header:l.header||"",question:l.question||"",multiSelect:!!l.multiSelect,chosen:d,rejected:f,...p?{control:p}:{}},await Z({flow:"prompt",phase:"answered",sessionName:o.leadBox,agent:s.agent,provider:i.provider,promptId:i.promptId,...c}).catch(()=>{}),Jf(o,s,i,c),{ok:!0,promptId:i.promptId,sent:c.count}}async function KD(e,t){if(!t)return null;let n=await j(e).catch(()=>[]);return nf(tr(n),{leadBoxId:t.name,provider:t.agent,isAnswered:r=>n.some(o=>o.type==="prompt.answered"&&o.payload?.promptId===r)})}async function $m(e,{promptId:t,questionIndex:n}={}){let r=oe(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 ie(r.leadBox))throw new Error(`lead box "${r.leadBox}" is not running`);let a=await Cf(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=lk(a,n);return c.length?(await Ie(r.leadBox,c,{kind:"navigate-prompt"}),await Z({flow:"prompt",phase:"navigated",sessionName:r.leadBox,agent:o.agent,provider:a.provider,promptId:a.promptId,fromIndex:a.questionIndex??0,toIndex:n,mode:"keys",value:c.join(" "),count:c.length}).catch(()=>{}),{ok:!0,promptId:a.promptId,sent:c.length}):{ok:!0,promptId:a.promptId,sent:0}}function Vl(e){let t=di.get(e);return t||(t={items:[],timer:null,flushing:!1,idleSince:null},di.set(e,t)),t}function uo(e){return(di.get(e)?.items??[]).map(t=>({...t}))}function ki(e){let t=di.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 Om(e){let t=ki(e),n=t.filter(o=>o.text&&!o.kind).map(o=>o.text),r=null;if(n.length){let o=oe(await z(),e);if(o)return r=Is(o,n),await Y(),{removed:t,restored:r,draftRev:o.draftRev||0}}return{removed:t,restored:r,draftRev:null}}function Cm(e){let t=Vl(e);t.timer||(t.timer=setInterval(()=>VD(e).catch(()=>{}),GD))}async function VD(e){let t=di.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=oe(await z(),e);if(!n||n.archivedAt){ki(e);return}let r=Ms(e);if(r&&!r.finishedAt&&r.step!=="done"&&!r.error){co(t,!1);return}let o=await ie(n.leadBox),s=n.boxes.find(c=>c.name===n.leadBox);if(o&&await qm(o)&&(o=await wi(n,s,"queue")?await ie(n.leadBox):null,await Y()),!o){co(t,!1);return}if(Qr(s?.agent)){co(t,!1);return}let a=await ve(n.leadBox,{lines:Ue}).catch(()=>"");if(Pc(s?.agent,a)){co(t,!1);return}if(Xr(s?.agent,a)){let c=await hn(n.leadBox).catch(()=>a);if(cr(s,c)){co(t,!1);return}}if(!co(t,!0))return;let i=t.items.shift();t.idleSince=null;try{await xm(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 wi(n,s,"queue-gate")}await Y()}finally{t.flushing=!1}}}async function mv(e,t,n,r){let o=Qr(t?.agent);if(!o)return;if(!r&&typeof n=="string"&&n.trim()&&(Is(e,[n]),await Y()),Ik(e.id,o))try{await A(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 zD(e,t){let n=await ve(e.leadBox,{lines:Ue}).catch(()=>"");if(!Xr(t?.agent,n))return!1;let r=await hn(e.leadBox).catch(()=>n);return!!cr(t,r)}async function xm(e,t,n,r,o=null,s=null){await mv(e,t,r,o);let a=fi.get(e.id)||0,i=r,c=!1,l=e.pendingHandoffHydrate||null;e.pendingHandoffHydrate&&(i=lv(r,e.pendingHandoffHydrate.artifact||lo),delete e.pendingHandoffHydrate,c=!0);let u=Array.isArray(s)?s.filter(f=>f&&f.rel):[];if(u.length){let f=GS(u.map(p=>p.rel));i=i.trim()?`${i.trim()}
|
|
165
165
|
|
|
166
|
-
${f}`:f}let d=
|
|
166
|
+
${f}`:f}let d=pe(e,"said",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,text:r,...o?{kind:o}:{},...u.length?{attachments:u}:{}});o||_s(e,null);try{if(await er({cwd:n.cwd,sessionId:n.sessionId,sessionName:e.leadBox,agent:n.agent,agentCmd:Cs(n.agent),bailOnDialog:!0}),(fi.get(e.id)||0)!==a){!o&&r.trim()&&(Is(e,[r]),await Y());let f=new Error("delivery stopped \u2014 the message was handed back as the draft");throw f.interrupted=!0,f}if(await zD(e,t)){let f=Vl(e.id),p={id:Os(),text:r,ts:Date.now(),...o?{kind:o}:{},...u.length?{attachments:u}:{}};f.items.unshift(p),Cm(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 He(e.leadBox,i,{...Jt(t?.agent,i),kind:c?"lead-handoff-first-prompt":"say"})}catch(f){if(l&&!e.pendingHandoffHydrate&&(e.pendingHandoffHydrate=l),pe(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 cB(e,t,i)||(await wv(e.leadBox,t?.agent,i.length+8,i).catch(()=>{}),pe(e,"unsaid",{box:e.leadBox,agent:t?.agent,boxRole:t?.role,saidTs:d.at,text:r,reason:"unsubmitted"}),o||Is(e,[r])),c&&await Z({flow:"lead-handoff",phase:"first-prompt",trackId:e.id,sessionName:e.leadBox,agent:t?.agent,userTextLen:r.length})}function Mm(e,{actions:t}={}){Vl(e).items.push({id:Os(),text:s0(t),ts:Date.now(),kind:"ship-notify"}),Cm(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=oe(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`);eh(s);let a=s.boxes.find(p=>p.name===s.leadBox),i=Zo(t);await mv(s,a,i,n),xi(e,"say");let c=await ie(s.leadBox);if(c&&await qm(c)?await wi(s,a,"say")?(await Y(),c=await ie(s.leadBox)):c=null:!c&&await vi(s)&&(await Y(),c=await ie(s.leadBox)),!c)throw new Error(`lead box "${s.leadBox}" is not running`);let l=Vl(e),u=await ve(s.leadBox,{lines:Ue}).catch(()=>""),d=l.items.length>0||Pc(a?.agent,u);if(!d&&Xr(a?.agent,u)){let p=await hn(s.leadBox).catch(()=>u);d=!!cr(a,p)}if(d){let p={id:Os(),text:i,ts:Date.now(),...n?{kind:n}:{},...o.length?{attachments:o}:{}};return l.items.push(p),Cm(e),n||_s(s,null),await Y(),{ok:!0,leadBox:s.leadBox,queued:!0,id:p.id,position:l.items.length}}let f;try{f=await xm(s,a,c,i,n,o)}catch(p){if(p?.code!=="cli-not-running"||!await wi(s,a,"say-gate")||(await Y(),c=await ie(s.leadBox),!c))throw p;f=await xm(s,a,c,i,n,o)}return await Y(),f?.queued?{ok:!0,leadBox:s.leadBox,queued:!0,id:f.id,position:f.position}:{ok:!0,leadBox:s.leadBox}}async function Nm(e,{hint:t}={}){let n=oe(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=Pp(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";Px(n.id,{name:s,box:n.leadBox});let a=String(t||"").trim()?`${tv}
|
|
167
167
|
|
|
168
|
-
What to preview: ${String(t).trim()}`:
|
|
168
|
+
What to preview: ${String(t).trim()}`:tv;try{await It(e,a,{kind:"spin-up-view"})}catch(i){throw Rl(n.id,{error:"drive-failed"}),i}return{ok:!0,running:!0}}async function zl(e){let t=oe(await z(),e);if(!t)throw new Error(`track "${e}" not found`);if(t.archivedAt)throw new Error(`track "${e}" is archived`);fi.set(e,(fi.get(e)||0)+1);let n=ki(e),r=n.filter(i=>i.text&&!i.kind).map(i=>i.text),o=Es.get(e);if(o&&!o.canceled){o.clicks++;let i=null;return r.length&&(i=Is(t,r),await Y()),{ok:!0,interrupted:!0,stopped:!1,stopping:!0,dropped:n.length,restored:i,draftRev:t.draftRev||0}}let s=eB(e,{droppedCount:n.length,droppedTexts:r});await Promise.race([s.settled,fo(JD)]);let a=oe(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 hv(e){let t=Es.get(e);return!!t&&!t.canceled}function xi(e,t){let n=Es.get(e);n&&!n.canceled&&(n.canceled=!0,n.cancelReason=t||null)}function Pm(e){xi(e,"track-ended"),fi.delete(e),ki(e)}function eB(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}),Es.set(e,r),(async()=>{let s=oe(await z(),e),a=s?.boxes.find(y=>y.name===s.leadBox),i=s?await ie(s.leadBox):null,c=!1,l=!1;if(i){e:for(let y=0;y<YD&&!r.canceled;y++){await Ie(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.escSends++;for(let x of XD){await fo(x);let k=await ve(s.leadBox,{lines:Ue}).catch(()=>"");if(!qo(a?.agent,k)){c=!0;break e}}}if(!c){l=!0;for(let y=0;y<QD&&!r.canceled&&(await fo(ZD),!r.canceled);y++){let x=await ve(s.leadBox,{lines:Ue}).catch(()=>"");if(!qo(a?.agent,x)){c=!0;break}await Ie(s.leadBox,["Escape"],{kind:"interrupt"}).catch(()=>{}),r.bgSends++}}}let u=oe(await z(),e)||s,d=u?.boxes.find(y=>y.name===u.leadBox)||a,f=null,p=null;u&&d&&i&&c&&!r.canceled&&(f=await oB(u,d).catch(()=>null),f||(p=await iB(u,d).catch(()=>null)));let m=u&&i&&!r.canceled?await nB(u).catch(()=>null):null,w=[f||p,...r.queuedTexts].filter(Boolean);u&&w.length&&(r.restored=Is(u,w),await Y()),r.result=i?r.canceled?"canceled":c?l?"stopped-late":"stopped":"gave-up":"no-session",await tB(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 Z({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"),Es.get(e)===r&&Es.delete(e),o()}),r}async function tB(e,t,n){await A(e,{type:"turn.interrupted",actor:"user",source:"orchestrator",boxId:e.leadBox,provider:t?.agent||null,payload:n}).catch(()=>{})}async function nB(e){let t=Ml(await j(e));if(!t?.offerId)return null;let n=t.offerType==="exists";return await A(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 oB(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 rB){await fo(r);let o=await ve(e.leadBox,{lines:Ue}).catch(()=>""),s=Ta(t.agent,o);if(s&&Dc(n.text,s))return await wv(e.leadBox,t.agent,n.text.length+8),pe(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:n.at,text:n.text}),n.text}return null}async function iB(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>sB||n.some(s=>s.type==="unsaid"&&s.saidTs===r.at)||!Ce(r.text))return null;for(let s of aB){await fo(s);let a;try{a=await Af(t,{})}catch{return null}if(ky(a,r))return null;let i=await ve(e.leadBox,{lines:Ue}).catch(()=>"");if(Ta(t.agent,i))return null}return pe(e,"unsaid",{box:e.leadBox,agent:t.agent,boxRole:t.role,saidTs:r.at,text:r.text,reason:"lost"}),r.text}async function cB(e,t,n){return Ea({sessionName:e.leadBox,agent:t?.agent,agentCmd:Cs(t?.agent),text:n,trackId:e.id,flow:"say"})}async function wv(e,t,n,r=null){for(let o=0;o<3;o++){await Bo(e,n,{kind:"unsend-wipe"}).catch(()=>{}),await fo(200);let s=await ve(e,{lines:Ue}).catch(()=>""),a=Ta(t,s);if(!a||r&&!Dc(r,a)&&!(t==="codex"&&qd(r,a)))return!0;n=a.length+8}return!1}function _s(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 Is(e,t){let n=[],r=o=>n.some(s=>Ce(s).includes(Ce(o)));for(let o of[e.draft?.text,...t])o&&o.trim()&&!r(o)&&n.push(o);return n.length?_s(e,n.join(`
|
|
169
169
|
|
|
170
|
-
`),{source:"restore"})?.text??null:null}function
|
|
170
|
+
`),{source:"restore"})?.text??null:null}function uB(e,t){let n=Ce(t);if(!n)return!1;if(uo(e.id).some(o=>Ce(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>lB)break;if(!(s.type!=="said"||!s.text||Ce(s.text)!==n))return!r.some(a=>a.type==="unsaid"&&a.saidTs===s.at)}return!1}async function Dm(e,t,{rev:n=null}={}){if(typeof t!="string")throw new Error("text (string) required");let r=oe(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&&uB(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;Ce(o).includes(Ce(i))?_s(r,o):_s(r,`${i}
|
|
171
171
|
|
|
172
|
-
${o}`,{source:"restore"})}else
|
|
173
|
-
`);return`${
|
|
172
|
+
${o}`,{source:"restore"})}else _s(r,o);return await Y(),{ok:!0,draft:r.draft||null,rev:r.draftRev||0}}async function Bm(e,t){if(typeof t!="string"||!t.trim())throw new Error("model (string) required");let n=t.trim(),r=oe(await z(),e);if(!r)throw new Error(`track "${e}" not found`);let o=await ie(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=Lk(a,n),l=[...r.events||[]].reverse().find(d=>d.type==="model-change")?.to??null;l||(l=(await ha(o.cwd,o.sessionId,{fallbackNewest:!1}).catch(()=>null))?.model??null),await er({cwd:o.cwd,sessionId:o.sessionId,sessionName:r.leadBox,agent:o.agent,agentCmd:Cs(o.agent)});let u=await Fk(a,r.leadBox,i);return pe(r,"model-change",{to:n,from:l,box:r.leadBox,agent:a,verified:!!u.verified}),await Y(),{ok:!0,requested:n,leadBox:r.leadBox,...u}}async function gv(e){for(let t of e){if(!t||!await Qo(t))continue;let n=await wk(t).catch(()=>null);if(!n)continue;if((await H.stat(q.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:q.basename(t.base),dir:t.dir??null,base:t.base,baseRef:t.baseRef??null,repo:t.repo??null}]:[]:[]}async function gs(e){let t=[];for(let n of gt(e?.workspace)){if(!n?.dir)continue;let r=await Na(n.dir,{ignorePlumbing:!0}).catch(()=>!1),o=await Pa(n.dir,n.baseRef).catch(()=>0);(r||o>0)&&t.push({...n,dirty:r,ahead:o})}return t}async function yv(e){let t=e;typeof t=="string"&&!q.isAbsolute(t)&&/^[^/\s]+\/[^/\s]+$/.test(t)&&(t=await Eo(t)??await sc(t));let n=await gv([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 dB(e,t,n,r){let o=`track/${oh(t)}-${e}`,s=[];try{await H.mkdir(n,{recursive:!0});let a=new Set([".bostrat"]);for(let i of r){let c=await yv(i);if(s.some(p=>p.base===c))throw new Error(`repo tagged twice: ${i}`);let l=await fe(c).catch(()=>null),u=q.basename(c);a.has(u)&&l&&(u=l.replace("/","-"));for(let p=2;a.has(u);p++)u=`${q.basename(c)}-${p}`;a.add(u);let d=q.join(n,u),f=await ns(c);await ts(c,d,o,f),s.push({name:u,dir:d,base:c,baseRef:f,repo:l})}return await Si(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 H.rm(n,{recursive:!0,force:!0}).catch(()=>{}),a}}async function jm(e,t){let n=await z(),r=oe(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 yv(typeof t=="string"?t.trim():t);if((o.repos??[]).some(f=>f?.base===s))throw new Error(`repo already tagged: ${t}`);let a=await fe(s).catch(()=>null),i=new Set([".bostrat",...(o.repos??[]).map(f=>f?.name).filter(Boolean)]),c=q.basename(s);i.has(c)&&a&&(c=a.replace("/","-"));for(let f=2;i.has(c);f++)c=`${q.basename(s)}-${f}`;let l=q.join(o.dir,c),u=await ns(s);await gk(s,o.branch)?await Xc(s,l,o.branch):await ts(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 Si(o.dir,o.repos,o.branch),pe(r,"retagged",{action:"add",repo:a??s,name:c,workspace:bv(o)}),await Y(),d}async function Lm(e,t){let n=await z(),r=oe(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 Na(s.dir,{ignorePlumbing:!0}).catch(()=>!1),i=await Pa(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 H.rm(s.dir,{recursive:!0,force:!0}).catch(()=>{}),o.repos=(o.repos??[]).filter(c=>c&&c!==s),await Si(o.dir,o.repos,o.branch),pe(r,"retagged",{action:"remove",repo:s.repo??s.base,name:s.name,workspace:bv(o)}),await Y(),{removed:s.name}}async function fB(e,t,{repo:n,repos:r,cwd:o}={}){await H.mkdir(li,{recursive:!0});let s=q.join(li,e),a=Array.isArray(r)?r.map(c=>typeof c=="string"?c.trim():"").filter(Boolean):[];if(a.length>=2)return dB(e,t,s,a);a.length===1&&!n&&(n=a[0]),typeof n=="string"&&!q.isAbsolute(n)&&/^[^/\s]+\/[^/\s]+$/.test(n)&&(n=await Eo(n)??await sc(n));let i=await gv([n,o,OD]);if(i){let c=`track/${oh(t)}-${e}`;try{let l=await ns(i);await ts(i,s,c,l),await mi(s);let u=await fe(i).catch(()=>null);return{dir:s,base:i,branch:c,kind:"worktree",baseRef:l,repo:u}}catch{await H.rm(s,{recursive:!0,force:!0}).catch(()=>{})}}return await H.mkdir(s,{recursive:!0}),await Lf(s),await mi(s),{dir:s,base:null,branch:null,kind:"repo"}}async function mi(e){try{await H.writeFile(q.join(e,"AGENTS.md"),ci,{flag:"wx"})}catch{return}await wB(e,["/AGENTS.md"])}function hB(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`${mB}
|
|
174
174
|
|
|
175
175
|
This directory is NOT a git repo. It is a bostrat Track workspace holding one
|
|
176
176
|
git worktree per tagged repo, ALL on the same branch \`${t}\`:
|
|
@@ -182,20 +182,20 @@ push / open PRs) in each repo separately, always on that shared branch. Work
|
|
|
182
182
|
only needs to land in the repos it actually touches \u2014 an untouched repo's
|
|
183
183
|
branch simply stays at its fork point and never gets pushed. When asked to run
|
|
184
184
|
or preview an app, run it from that app's own repo directory.
|
|
185
|
-
`}async function
|
|
186
|
-
`).includes(s));o.length&&await
|
|
185
|
+
`}async function Si(e,t,n){let r=hB(t,n);for(let o of["CLAUDE.md","AGENTS.md"])await H.writeFile(q.join(e,o),r).catch(()=>{})}async function xv(e){let{stdout:t}=await sv("git",["-C",e,"rev-parse","--git-path","info/exclude"]);return q.resolve(e,t.trim())}async function wB(e,t){try{let n=await xv(e);await H.mkdir(q.dirname(n),{recursive:!0});let r=await H.readFile(n,"utf8").catch(()=>""),o=t.filter(s=>!r.split(`
|
|
186
|
+
`).includes(s));o.length&&await H.appendFile(n,(r.endsWith(`
|
|
187
187
|
`)||!r?"":`
|
|
188
188
|
`)+o.join(`
|
|
189
189
|
`)+`
|
|
190
|
-
`)}catch{}}async function
|
|
190
|
+
`)}catch{}}async function gB(e,t){try{let n=await xv(e),r=await H.readFile(n,"utf8"),o=r.split(`
|
|
191
191
|
`).filter(s=>s!==t);o.length!==r.split(`
|
|
192
|
-
`).length&&await
|
|
193
|
-
`))}catch{}}async function
|
|
192
|
+
`).length&&await H.writeFile(n,o.join(`
|
|
193
|
+
`))}catch{}}async function Fm(){let e={refreshed:0,removed:0,codexGlobal:null};try{await H.mkdir(cn,{recursive:!0}),await H.writeFile(q.join(cn,"CLAUDE.md"),pB).catch(()=>{}),await H.writeFile(q.join(cn,"AGENTS.md"),ci).catch(()=>{});let t=process.env.CODEX_HOME||q.join(process.env.HOME||"/tmp",".codex");if((await H.stat(t).catch(()=>null))?.isDirectory()){let r=q.join(t,"AGENTS.md"),o=await H.readFile(r,"utf8").catch(()=>null);o===null?(await H.writeFile(r,hm,{flag:"wx"}).catch(()=>{}),e.codexGlobal="created"):mm(o)&&o!==hm&&(await H.writeFile(r,hm).catch(()=>{}),e.codexGlobal="refreshed")}for(let r of[li,$s]){let o=await H.readdir(r,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(!s.isDirectory())continue;let a=q.join(r,s.name),i=q.join(a,"AGENTS.md"),c=await H.readFile(i,"utf8").catch(()=>null);c!==null&&mm(c)&&c!==ci&&(await H.writeFile(i,ci).catch(()=>{}),e.refreshed++);let l=q.join(a,"CLAUDE.md"),u=await H.readFile(l,"utf8").catch(()=>null);u!==null&&mm(u)&&(await H.rm(l,{force:!0}).catch(()=>{}),await gB(a,"/CLAUDE.md"),e.removed++)}}}catch{}return e}async function Sv(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 H.rm(t.dir,{recursive:!0,force:!0})}else t.kind==="worktree"&&t.base?await pt(t.base,t.dir):await H.rm(t.dir,{recursive:!0,force:!0})}catch{}}async function Jl({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 bi(o,s),i=await z(),c=Os().slice(0,8),l=await fB(c,e,{repo:n,repos:r,cwd:t}),u=new Set(i.tracks.flatMap(m=>(m.boxes||[]).map(w=>w.name))),d=`${e.trim()} lead`;u.has(Tc(d))&&(d=`${e.trim().slice(0,50).trim()} ${c} lead`);let f=await Zn(d,l.dir,{agentCmd:a,agent:o,freshWorkspace:!0});xa({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:[Ym(f,"lead","live")],events:[],createdAt:Date.now()};return pe(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:w,baseRef:y,repo:x})=>({name:m,base:w,baseRef:y,repo:x}))}:{}}}),i.tracks.push(p),await Y(),_t(p)}async function Um({provider:e="claude",fresh:t=!1}={}){if(!yB.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 po(r.id).catch(()=>{})}let o=(await bi(e,!1)).replace(/\s+--(?:dangerously-skip-permissions|dangerously-bypass-approvals-and-sandbox)\b/g,""),s=Os().slice(0,8),a=q.join(cn,"setup",s);await H.mkdir(a,{recursive:!0});let i=await Zn(`Connect ${e} ${s}`,a,{agentCmd:o,agent:e,firstRun:!0});e==="codex"&&xa({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:[Ym(i,"lead","live")],events:[],createdAt:Date.now()};return pe(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 Y(),_t(c)}async function Hm(e,t,n){await sd(t.cwd).catch(()=>{}),await Hx(e).catch(()=>{});let r=Rs.get(e);r&&!r.finishedAt&&(r.error=n,r.finishedAt=Date.now()),Rs.delete(e),t.handoff&&!t.handoff.finishedAt&&(t.handoff.step="error",t.handoff.error=n,t.handoff.finishedAt=Date.now());for(let a of mo(e))a.finishedAt||(a.canceled=!0,a.finishedAt=Date.now()),await ih(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 Wm(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 uS(s),Pm(e),await Hm(e,s,"track deleted"),n?await US(e).catch(()=>{}):await FS(e).catch(()=>{}),await Wk(),await Sv(s),t&&await dS(e).catch(()=>{}),t?await IS(e).catch(()=>{}):await _S(s).catch(()=>{}),pe(s,"track-deleted",{name:s.name,boxes:s.boxes.length,ageMs:s.createdAt?Date.now()-s.createdAt:null}),r.tracks.splice(o,1),await Y(),{ok:!0,id:e,name:s.name,boxes:s.boxes.length}}async function po(e){let t=await z(),n=oe(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(),Pm(e),pe(n,"archived",{boxes:n.boxes.length}),await Y(),n.kind==="setup"?(await vv(e),_t(n)):(hi.add(e),xB(e).catch(()=>{}).finally(()=>hi.delete(e)),{...await _t(n),archiving:!0})}async function xB(e){try{await SB(e)}catch{}await vv(e).catch(()=>{})}async function vv(e){let t=await z(),n=oe(t,e);if(!(!n||!n.archivedAt)){await Hm(e,n,"track archived");for(let r of n.boxes)xr.has(r.state)||(r.state="stopped");await Y()}}async function SB(e){let t=await z(),n=oe(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 ie(n.leadBox).catch(()=>null):null;if(i?.cwd&&a)try{({text:r,items:o}=await pb(n.leadBox,i.cwd,{timeoutMs:kB})),s="lead-cli"}catch{}if(r||(r=Ib(n,await j(n).catch(()=>[]))),!r)return;let c={text:r,source:s,at:Date.now()};o.length&&(c.items=o),n.beat=c,await A(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 Y()}async function An(e,t){let n=await Ke(e,t).catch(()=>null);if(!n?.archiveOnMerge||!n.trackId)return!1;let r=await Tv(n);if(r.length){for(let o of r)await eo(o.repo,o.number,!0).catch(()=>{});return!1}try{return await po(n.trackId),!0}catch(o){if(/already archived/i.test(String(o?.message||o)))return!1;throw o}}async function Tv(e){return(await to().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 En(e,t){return Yl(e,t,"merged")}async function Yl(e,t,n="merged"){let r=await Ke(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 Tv(r);if(s.length){let i=[...r.notifyPending||[],o];for(let c of s)await h0(c.repo,c.number,i).catch(()=>{});return!1}let a=[...r.notifyPending||[],o];try{return await It(r.trackId,Av(a),{kind:"merge-notify"}),!0}catch{return!1}}function Av(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 ${nv[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 ${nv[i.outcome]??i.outcome}`).join(`
|
|
194
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}
|
|
195
195
|
${s}
|
|
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
|
|
198
|
-
`)[0]}else l?.sha&&(
|
|
196
|
+
${a}`}async function qm(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<pa()?!1:mn(e.name)}async function wi(e,t,n){return!t||e.archivedAt||xr.has(t.state)?!1:(await Z({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",vi(e))}async function vi(e){let t=e.boxes.find(o=>o.name===e.leadBox);if(!t)return!1;let n=wm.get(t.name);if(n)return n;let r=vB(e,t).finally(()=>wm.delete(t.name));return wm.set(t.name,r),r}async function vB(e,t){if(e.archivedAt||e.transferredTo||xr.has(t.state)||await ie(t.name))return!1;let n=Am(t.agent,t.sessionId,{cwd:t.cwd,peerName:t.name}),r=await vc(n);if(r===No)return pe(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:`agent "${t.agent}" is not installed on this box`}),!1;r===Po&&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=Vs(o.hookToken),xa({cwd:t.cwd,sessionName:t.name,agent:t.agent,agentCmd:n}),t.state="live",pe(e,"rehydrated",{box:t.name,agent:t.agent}),!0}catch(o){return pe(e,"rehydrate-failed",{box:t.name,agent:t.agent,reason:String(o?.message||o)}),!1}}async function Km(e){let t=await z(),n=oe(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,hi.delete(e),pe(n,"unarchived",{}),await vi(n),await Y(),_t(n)}async function Gm(e,t){let n=typeof t=="string"?t.trim():"";if(!n)throw new Error("name (non-empty string) required");if(n.length>Sm)throw new Error(`name too long (max ${Sm} chars)`);let r=await z(),o=oe(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,pe(o,"renamed",{from:s,to:n}),await Y(),_t(o)}async function Vm(){let e=await z(),t=0;for(let n of e.tracks||[])n.transferredTo&&Qc(n,{at:n.transferredAt??Date.now()})&&t++;return t&&await Y(),t}async function zm(){let e=await z(),t=new Set((await Ct()).map(s=>s.name)),n=new Set((e.tracks||[]).filter(s=>!Be(s)).flatMap(s=>(s.boxes||[]).map(a=>a.name))),r=0,o=!1;for(let s of e.tracks||[])if(Be(s))for(let a of s.boxes)t.has(a.name)&&!n.has(a.name)&&(await Mt(a.name).catch(()=>{}),s.transferredTo?console.log(`[track] reaped orphan box ${a.name} on transferred-away track ${s.id} (\u2192 ${s.transferredTo})`):pe(s,"orphan-reaped",{box:a.name,agent:a.agent}),r++,o=!0),!xr.has(a.state)&&a.state!=="stopped"&&(a.state="stopped",o=!0);return o&&await Y(),r}async function Xl(e){let t=await z(),n=oe(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`);eh(n);let r=n.boxes.find(a=>a.name===n.leadBox);if(!r)throw new Error(`track "${e}" has no lead box`);if(xr.has(r.state))throw new Error("lead box is terminal \u2014 nothing to restart");if(await ie(n.leadBox)){let a=await ve(n.leadBox,{lines:Ue}).catch(()=>"");if(Pc(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 vi(n);if(await Y(),!s)throw new Error("rehydrate failed \u2014 see the track's rehydrate-failed event");return _t(n)}async function Jm(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 j(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 Xl(e),await A(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 Zl(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 Hl(e,t){let n=await z(),r=oe(n,e);r&&(r.handoff={...r.handoff||{},...t},await Y())}function Ev(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 Xm(){let e=await z(),t=!1;for(let n of e.tracks||[]){let r=n.handoff,o=Ev(n);if(!o)continue;t=!0;let s=!!r.toBox&&r.toBox===r.fromBox;if(s&&o.step==="error"){let c=await ie(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=Zl(l),f=d[d.length-1],p=f.agent,m=f.sessionId??null;f.endedAt=u,r.reason&&(f.endReason=r.reason);let w=c.agent==="claude"&&!!c.sessionId;d.push({agent:r.toAgent,sessionId:c.sessionId||null,pinned:w,startedAt:u,endedAt:null}),l.agent=r.toAgent,l.sessionId=c.sessionId||null,l.pinned=w,l.state="live",n.pendingHandoffHydrate={artifact:lo,at:u},pe(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 Z({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:"done",recovered:n.handoff.recovered,fromBox:r.fromBox,toBox:r.toBox}).catch(()=>{});continue}}let a=!s&&o.recovered==="rolled-back"&&r.toBox&&r.toBox!==n.leadBox?r.toBox:null,i=!s&&o.step==="done"?r.fromBox:null;n.handoff=o,await Z({flow:"lead-handoff",phase:"reconciled",trackId:n.id,outcome:o.step,recovered:o.recovered,fromBox:o.fromBox,toBox:o.toBox}).catch(()=>{}),i&&await Mt(i).catch(()=>{}),a&&await Mt(a).catch(()=>{})}return t&&await Y(),t}async function Ql(e,t,n=!0,r=null){let o=Rs.get(e);if(o&&!o.finishedAt)throw new Error("a lead handoff is already running for this Track");let s=oe(await z(),e);if(!s)throw new Error(`track "${e}" not found`);xi(e,"handoff");let a=await bi(t,n),i=Qr(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 ie(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:uv(r),toBox:s.leadBox,startedAt:Date.now(),finishedAt:null,error:null};return Rs.set(e,l),await Hl(e,{step:l.step,fromBox:l.fromBox,toAgent:t,toBox:l.toBox,skipPermissions:n,startedAt:l.startedAt,finishedAt:null,error:null}),TB(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 Hl(e,{step:l.step,error:l.error,finishedAt:l.finishedAt}).catch(()=>{}),Z({flow:"lead-handoff",phase:"failed",trackId:e,step:l.step,error:l.error,fromBox:l.fromBox,toAgent:l.toAgent,toBox:l.toBox}).catch(()=>{})}),Z({flow:"lead-handoff",phase:"started",trackId:e,fromBox:l.fromBox,toAgent:t,cwd:c.cwd}).catch(()=>{}),l}async function TB(e,t,n,r,o){let s=async(P,N={})=>{e.step=P,console.log(`[lead-handoff ${e.trackId}] step \u2192 ${P}`),await Hl(e.trackId,{step:P,toBox:e.toBox}),await Z({flow:"lead-handoff",phase:P,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=oe(a,e.trackId)?.boxes.find(P=>P.name===e.fromBox)||null,c=i?.agent||n.agent,l=i?.sessionId??n.sessionId??null,u=i?.pinned===!0;if(await s("checkpoint",{sessionName:e.fromBox}),e.checkpoint="llm",Qr(c))e.checkpoint="skipped-capped";else try{await cf(e.fromBox,{timeoutMs:cv})}catch(P){e.checkpoint="reconstructed",await Z({flow:"lead-handoff",phase:"checkpoint-fallback",trackId:e.trackId,fromBox:e.fromBox,error:P?.message||String(P)}).catch(()=>{})}e.checkpoint!=="llm"&&(e.statusArtifact=await lf(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 wf(e.fromBox,{agentCmd:r,agent:o});await s("hydrate",{sessionName:e.fromBox});let p=gy(f.launchCmd);await er({cwd:n.cwd,sessionId:f.sessionId,sessionName:e.fromBox,agent:o,agentCmd:Cs(o),launchEcho:p}).catch(P=>{if(P?.code!=="cli-not-running")throw P});let m=Ud(await ve(e.fromBox,{lines:Ue}).catch(()=>""),p);if(!Ko(o,m)){await s("rollback",{sessionName:e.fromBox,fromAgent:c});let P=Am(c,l,{skipPermissions:e.skipPermissions,cwd:n.cwd,peerName:e.fromBox});await wf(e.fromBox,{agentCmd:P,agent:c,resumeSessionId:l,pinned:u});let N=await z(),O=oe(N,e.trackId);if(O){let $=O.boxes.find(R=>R.name===e.fromBox);$&&($.state="live"),pe(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 Y()}throw new Error(`incoming ${o} CLI never drew its composer \u2014 rolled back to ${c} (--resume, context intact)`)}await s("commit");let w=await z(),y=oe(w,e.trackId);if(!y)throw new Error(`track "${e.trackId}" not found during handoff commit \u2014 was it deleted or archived mid-flight?`);let x=y.boxes.find(P=>P.name===e.fromBox);if(!x)throw new Error(`lead box row "${e.fromBox}" missing during handoff commit`);let k=Date.now(),T=Zl(x),_=T[T.length-1];_.endedAt=k,e.reason&&(_.endReason=e.reason),T.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",y.pendingHandoffHydrate={artifact:e.statusArtifact||lo,at:k},pe(y,"lead-handoff",{from:e.fromBox,to:e.fromBox,fromAgent:c,toAgent:o,fromSessionId:l,toSessionId:f.sessionId,epoch:T.length-1,singleBox:!0,forced:f.forced||void 0,skipPermissions:e.skipPermissions,reason:e.reason,handoffStartedAt:e.startedAt,checkpoint:e.checkpoint}),await Y(),await s("done",{sessionName:e.fromBox}),await Hl(e.trackId,{finishedAt:Date.now(),error:null}),e.finishedAt=Date.now()}async function Qm(e){let t=Tm.get(e);if(t&&!t.finishedAt)throw new Error("a transfer is already running for this Track");let n=await z(),r=oe(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=Rs.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 Tm.set(e,s),AB(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 AB(e,t){let n=t.boxes.find(u=>u.name===t.leadBox)||null,r=n?await ie(n.name).catch(()=>null):null;if(e.checkpoint="llm",!r||n&&Qr(n.agent))e.checkpoint=r?"skipped-capped":"skipped-dead";else try{await cf(t.leadBox,{timeoutMs:cv})}catch{e.checkpoint="reconstructed"}e.checkpoint!=="llm"&&await lf(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 Bn(u.dir,["push","origin",o],vm);let{stdout:f}=await Bn(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 am({id:t.id,cwd:t.cwd}),i=await z(),c=oe(i,t.id),l=c?.boxes.find(u=>u.name===c.leadBox)||null;l&&Zl(l),await Y(),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 eh(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 eu(e,{toNode:t,mode:n="transfer"}={}){if(!t||typeof t!="string")throw new Error("toNode required");let r=await z(),o=oe(r,e);if(!o)throw new Error(`track "${e}" not found`);if(o.transferredTo)return{ok:!0,alreadyTransferred:o.transferredTo};Pm(e),await Hm(e,o,n==="recover"?"track recovered onto another node":"track transferred to another node");let s=Date.now();Qc(o,{at:s,reason:n==="recover"?"node-offline":"node-transfer"});let a=await im({id:o.id,cwd:o.cwd});return o.transferredTo=t,o.transferredAt=s,pe(o,"transferred-out",{toNode:t,mode:n,box:o.leadBox,...a.pushed?{rescueRef:a.ref,rescueSha:a.sha}:{}}),await Y(),{ok:!0,toNode:t,rescue:a.pushed?{ref:a.ref,sha:a.sha}:null}}async function th(e){let t=[];for(let n of Array.isArray(e)?e:[])if(!(!n?.id||!n?.toNode))try{let r=await eu(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 rv(e,t,n){try{await Xc(e,t,n)}catch{await Bn(e,["worktree","prune"]).catch(()=>{}),await Xc(e,t,n)}return t}async function nh(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(gm.has(t))throw new Error("an adopt is already running for this Track");gm.set(t,{startedAt:Date.now()});try{return await EB({...e,trackId:t,name:n,agent:r,skipPermissions:o,mode:s})}finally{gm.delete(t)}}async function EB({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 w=await bi(n,r),y=await z(),x=oe(y,e);if(x&&!x.transferredTo)throw new Error(`track "${e}" is already hosted on this node`);x&&(await im({id:x.id,cwd:x.cwd}).catch(()=>{}),await Sv(x),y.tracks.splice(y.tracks.indexOf(x),1),await Y()),await H.mkdir(li,{recursive:!0});let k=q.join(li,e);await H.rm(k,{recursive:!0,force:!0}).catch(()=>{});let T=null,_=!1,P=[];try{let N=[];if(i)for(let F of Array.isArray(c)?c:[]){let D=typeof F=="string"?F:F?.repo;if(!D||!/^[^/\s]+\/[^/\s]+$/.test(D))continue;let Q=await Eo(D)??await sc(D);await Bn(Q,["fetch","origin",`+refs/heads/${i}:refs/heads/${i}`],vm),N.push({slug:D,base:Q,name:typeof F=="object"?F?.name??null:null})}if(i&&N.length===1){let{base:F,slug:D}=N[0];await rv(F,k,i),P.push({base:F,dir:k}),await mi(k);let Q=await ns(F);T={dir:k,base:F,branch:i,kind:"worktree",baseRef:Q,repo:D}}else if(i&&N.length>=2){await H.mkdir(k,{recursive:!0});let F=new Set([".bostrat"]),D=[];for(let Q of N){let Ne=Q.name||q.basename(Q.base);F.has(Ne)&&(Ne=Q.slug.replace("/","-"));for(let b=2;F.has(Ne);b++)Ne=`${q.basename(Q.base)}-${b}`;F.add(Ne);let Un=q.join(k,Ne);await rv(Q.base,Un,i),P.push({base:Q.base,dir:Un});let Wt=await ns(Q.base);D.push({name:Ne,dir:Un,base:Q.base,baseRef:Wt,repo:Q.slug})}await Si(k,D,i),T={dir:k,kind:"multi",branch:i,repos:D}}else _=!0,await H.mkdir(k,{recursive:!0}),await Lf(k),await mi(k),T={dir:k,base:null,branch:null,kind:"repo"};let O=!1,$=null;if(l?.sha&&T.kind==="worktree")try{await Bn(k,["fetch","origin",l.ref||sm(e)],vm);let{stdout:F}=await Bn(k,["rev-parse","FETCH_HEAD^"]),{stdout:D}=await Bn(k,["rev-parse","HEAD"]);F.trim()===D.trim()?(await Bn(k,["read-tree","--reset","-u","FETCH_HEAD"]),await Bn(k,["reset","-q"]),O=!0):$="snapshot does not parent the fetched branch head"}catch(F){$=String(F?.message||F).split(`
|
|
198
|
+
`)[0]}else l?.sha&&($="wip snapshots cover single-repo worktrees only");await ZS({id:e,cwd:k}).catch(()=>({restored:!1}));let R=q.join(k,lo),ee=null;(await gc(k)).restored?ee="checkpoint":typeof u=="string"&&u.trim()&&(await H.mkdir(q.dirname(R),{recursive:!0}),await H.writeFile(R,u),ee="brief");let be=new Set(y.tracks.flatMap(F=>(F.boxes||[]).map(D=>D.name))),G=null;p&&!be.has(p)&&!await ie(p).catch(()=>null)?G=p:(G=`${t.trim()} lead`,be.has(Tc(G))&&(G=`${t.trim().slice(0,50).trim()} ${e} lead`));let I=await Zn(G,k,{agentCmd:w,agent:n});xa({cwd:I.cwd,sessionName:I.name,agent:n,agentCmd:w});let J=Date.now(),X=Ym(I,"lead","live"),he=(Array.isArray(d)?d:[]).filter(F=>F&&typeof F=="object").map(F=>({...F})),Se=he[he.length-1];Se&&Se.endedAt==null&&(Se.endedAt=J,Se.endReason==null&&(Se.endReason=o==="recover"?"node-offline":"node-transfer")),he.push({agent:n,sessionId:I.sessionId??null,pinned:I.pinned===!0,startedAt:J,endedAt:null}),X.epochs=he;let Le={id:e,name:t.trim(),cwd:I.cwd,workspace:T,leadBox:I.name,boxes:[X],events:[],createdAt:Number.isFinite(f)?f:J,...ee?{pendingHandoffHydrate:{artifact:lo,at:J}}:{}};return pe(Le,"transferred-in",{box:I.name,agent:n,fromNode:s,toNode:a,mode:o,reason:m,branch:T.branch??null,wip:O,epoch:he.length-1,name:Le.name,cwd:Le.cwd,workspace:{base:T.base??null,branch:T.branch??null,kind:T.kind,...T.baseRef?{baseRef:T.baseRef}:{},...T.repo?{repo:T.repo}:{},...T.repos?{repos:T.repos.map(({name:F,base:D,baseRef:Q,repo:Ne})=>({name:F,base:D,baseRef:Q,repo:Ne}))}:{}}}),y.tracks.push(Le),await Y(),{...await _t(Le),adopt:{wipApplied:O,wipSkipped:$,codeless:_,artifactSource:ee}}}catch(N){for(let O of P)await pt(O.base,O.dir).catch(()=>{});throw await H.rm(k,{recursive:!0,force:!0}).catch(()=>{}),N}}async function _v(e,t,n){let r=await z(),o=oe(r,e);o&&(o.subtasks=o.subtasks||{},o.subtasks[t]={...o.subtasks[t]||{},...n},await Y())}async function As(e,t){e.step=t,await Ti(e)}function Iv(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 Rv(e){return!!e&&!e.finishedAt&&!e.canceled&&e.step==="working"&&!!e.box&&!!e.dir&&!!e.base&&!!e.branch}async function $B(e,t,n){if(!Rv(n)||wt.has(t)||!await ie(n.box).catch(()=>null))return!1;try{await H.access(n.dir)}catch{return!1}let o=await ie(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),Ov(s,o).catch(a=>$v(s,a)),!0}async function rh(){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 $B(n,r,o)){t=!0,pe(n,"subtask-resumed",{box:o.box,agent:o.agent,task:o.task,subtaskId:r,resumedStep:o.step}),await Z({flow:"subtask",phase:"resumed",trackId:n.id,subtaskId:r,box:o.box,branch:o.branch}).catch(()=>{});continue}let s=o.step,a=Iv(o);if(!a)continue;t=!0,n.subtasks[r]=a;let i=n.boxes.find(c=>c.subtaskId===r);i&&!xr.has(i.state)&&(i.state="canceled"),pe(n,"subtask-failed",{from:o.box,fromAgent:o.agent,task:o.task,subtaskId:r,failedStep:s,error:a.error,recovered:a.recovered,durationMs:o.startedAt?a.finishedAt-o.startedAt:null}),await Z({flow:"subtask",phase:"reconciled",trackId:n.id,subtaskId:r,outcome:a.step,recovered:a.recovered,box:o.box,branch:o.branch}).catch(()=>{}),await ov(n,o.box,o.dir)}for(let r of n.boxes){if(r.role!=="subtask"||xr.has(r.state)||wt.has(r.subtaskId)||n.subtasks?.[r.subtaskId])continue;t=!0,r.state="canceled";let o=r.subtaskId?q.join($s,`${n.id}-${r.subtaskId}`):null;await Z({flow:"subtask",phase:"reaped-legacy-orphan",trackId:n.id,subtaskId:r.subtaskId,box:r.name}).catch(()=>{}),await ov(n,r.name,o)}}return t=await OB(e)||t,t&&await Y(),t}async function ov(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=q.join(n,o.name);await pt(o.base,s).then(()=>!0,()=>!1)||await pt(o.dir,s).catch(()=>{})}await H.rm(n,{recursive:!0,force:!0}).catch(()=>{});return}let r=await ie(e.leadBox).catch(()=>null);r?.cwd&&await pt(r.cwd,n).catch(()=>{})}async function OB(e){let t;try{t=await H.readdir($s)}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(q.basename(o.dir));let r=!1;for(let o of t){if(n.has(o))continue;let s=q.join($s,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,q.join(s,i.name)).catch(()=>{});await H.rm(s,{recursive:!0,force:!0}).catch(()=>{})}else{let i=a?await ie(a.leadBox).catch(()=>null):null;i?.cwd?await pt(i.cwd,s).catch(()=>{}):await H.rm(s,{recursive:!0,force:!0}).catch(()=>{})}await Z({flow:"subtask",phase:"swept-orphan-worktree",dir:s}).catch(()=>{}),r=!0}return r}async function sh(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 bi(t,r),a=Qr(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=oe(await z(),e);if(!i)throw new Error(`track "${e}" not found`);let c=await ie(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 Qo(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 Qo(c.cwd))throw new Error(`subtask delegation needs the lead's workspace to be a git repo (got ${c.cwd})`);let d=Os().slice(0,8),f={id:d,trackId:e,trackName:i.name,agent:t,task:Zo(n),skipPermissions:r,reason:uv(o),step:"snapshot",leadBox:i.leadBox,box:null,branch:`bostrat/${oh(i.name)}-${d}`,base:null,repos:u,dir:q.join($s,`${e}-${d}`),stat:null,conflicts:[],applied:null,startedAt:Date.now(),finishedAt:null,error:null,canceled:!1,forceMerge:!1};return wt.set(d,f),await Ti(f),CB(f,c,s).catch(p=>$v(f,p)),f}async function $v(e,t){e.failedStep=e.step,e.error=t instanceof gi?"canceled":t?.message||String(t),e.step=t instanceof gi?"canceled":"error",e.finishedAt=Date.now(),await ih(e).catch(()=>{}),await Ti(e).catch(()=>{}),await DB(e).catch(()=>{})}function nu(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 ah(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 CB(e,t,n){if(await As(e,"snapshot"),e.repos){for(let s of e.repos)s.base=await es(s.leadDir,`bostrat: snapshot for subtask ${e.id}`);await Ti(e)}else e.base=await es(t.cwd,`bostrat: snapshot for subtask ${e.id}`);if(await As(e,"spawn"),await H.mkdir($s,{recursive:!0}),e.repos){await H.mkdir(e.dir,{recursive:!0});for(let a of e.repos)await ts(a.leadDir,q.join(e.dir,a.name),e.branch,a.base);let s=gt((oe(await z(),e.trackId)||{}).workspace);await Si(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 ts(t.cwd,e.dir,e.branch,e.base),await mi(e.dir);await MB(e,t);let r=e.trackName.slice(0,38).trim(),o=await Zn(`${r} ${e.agent} sub ${e.id}`,e.dir,{agentCmd:n,agent:e.agent});e.box=o.name,await NB(e,o),await dv(e.trackId,o.name,MD(e.task,!!e.repos),{kind:"subtask"}),await Ov(e,t)}async function Ov(e,t){await As(e,"working");let n=q.join(e.dir,Ul);if(await jB(n,e),e.canceled)throw new gi;if(await As(e,"merge"),e.repos){let s={files:0,insertions:0,deletions:0},a=[],i=[],c=!0;for(let l of e.repos){let u=q.join(e.dir,l.name);await jf(u,`bostrat: subtask ${e.id} result (auto-committed)`),l.stat=await Uf(l.leadDir,l.base,e.branch,Ts),l.files=await Hf(l.leadDir,l.base,e.branch,Ts);let d=await Wf(l.leadDir,l.base,e.branch,Ts);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=>q.join(l.name,f))),i.push(...l.conflicts.map(f=>f.startsWith("patch did not apply cleanly")?`${l.name}: ${f}`:q.join(l.name,f))),d.applied||(c=!1)}e.stat=s,e.files=a,e.conflicts=i,e.applied=c}else{await jf(e.dir,`bostrat: subtask ${e.id} result (auto-committed)`),e.stat=await Uf(t.cwd,e.base,e.branch,Ts),e.files=await Hf(t.cwd,e.base,e.branch,Ts);let s=await Wf(t.cwd,e.base,e.branch,Ts);e.applied=s.applied,e.conflicts=s.conflicts}await Ti(e);let r=await BB(e,t,n);await As(e,"resume"),await dv(e.trackId,e.leadBox,ND(e.task,e.stat,e.conflicts,r),{kind:"subtask-resume"}),await As(e,"reap"),await ih(e),e.step="done";let o=Date.now();await _v(e.trackId,e.id,{...tu(e),finishedAt:o}),await PB(e,o),e.finishedAt=o}async function MB(e,t){let n=q.join(e.dir,q.dirname(ui));await H.mkdir(n,{recursive:!0});let r="";try{r=await ey(t.cwd,t.sessionId)}catch{}if(!r)try{r=await H.readFile(q.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}
|
|
199
199
|
|
|
200
200
|
**Delegated task:** ${e.task}
|
|
201
201
|
|
|
@@ -206,15 +206,15 @@ ${a}`}async function Lm(e){return!e?.name||!e.createdMs||Date.now()-e.createdMs<
|
|
|
206
206
|
## Conversation so far (lead \u2192 you)
|
|
207
207
|
|
|
208
208
|
${r}
|
|
209
|
-
`;await
|
|
209
|
+
`;await H.writeFile(q.join(e.dir,ui),o)}async function NB(e,t){let n=await z(),r=oe(n,e.trackId);r&&(r.boxes.push({name:t.name,agent:t.agent,role:"subtask",state:"live",cwd:t.cwd,sessionId:t.sessionId,hookTokenHash:Vs(t.hookToken),createdAt:Date.now(),subtaskId:e.id,branch:e.branch,task:e.task}),pe(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]||{},...tu(e)},await Y())}async function PB(e,t=e.finishedAt){let n=await z(),r=oe(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),pe(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 Y()}async function DB(e){let t=await z(),n=oe(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"),pe(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]||{},...tu(e)},await Y()}async function BB(e,t,n){let r=`.bostrat/handoff/subtask-${e.id}.md`;try{let o=q.join(t.cwd,r);await H.mkdir(q.dirname(o),{recursive:!0}),await H.copyFile(n,o)}catch{}return r}async function ih(e){if(e.dir&&await sd(e.dir).catch(()=>{}),e.box&&await Mt(e.box).catch(()=>{}),e.repos&&e.dir){for(let t of e.repos)await pt(t.leadDir,q.join(e.dir,t.name)).catch(()=>{});await H.rm(e.dir,{recursive:!0,force:!0}).catch(()=>{})}else if(e.dir){let n=(await ie(e.leadBox).catch(()=>null))?.cwd;n&&await pt(n,e.dir).catch(()=>{})}}async function jB(e,t){let n=(t.startedAt||Date.now())+_B;for(;;){if(t.canceled||t.forceMerge)return;try{await H.access(e);return}catch{}if(Date.now()>n)throw new Error("subtask timed out without signaling completion (no result digest written)");await fo(IB)}}function fo(e){return new Promise(t=>setTimeout(t,e))}var sv,cn,av,$D,li,OD,Wl,yi,kr,lo,cv,CD,Ul,ui,MD,ND,Et,um,oe,dm,pe,fm,pm,BD,xr,ev,FD,UD,HD,WD,GD,di,tv,JD,YD,XD,ZD,QD,Es,fi,rB,sB,aB,lB,bv,pi,kv,pB,ci,mm,mB,hm,yB,hi,bB,kB,nv,wm,Sm,Ym,Rs,Ms,vm,Bn,Tm,Zm,gm,_B,IB,$s,Ts,wt,RB,mo,tu,Ti,oh,gi,wr=M(()=>{ra();ze();df();Rb();Sa();Vn();Oc();Nb();et();Vt();Js();$c();Xb();Ur();Mf();Pf();mk();Bc();Kd();Cc();rf();Pt();Zc();Kn();yn();va();rl();Uk();Yf();bx();sf();Zd();xx();Ep();ce();Cl();Va();is();Up();xs();Xp();Ll();om();xn();VS();cm();QS();yc();sv=RD(ID),cn=process.env.BOSTRAT_STATE_DIR||q.join(process.env.HOME||"/tmp",".bostrat"),av=q.join(cn,"tracks.json"),$D=q.join(cn,"events.jsonl"),li=q.join(cn,"workspaces"),OD=process.env.BOSTRAT_DEFAULT_REPO||null,Wl={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"}},yi=Object.keys(Wl);kr={at:0,value:null};lo=".bostrat/handoff/latest.md",cv=Number(process.env.BOSTRAT_HANDOFF_CHECKPOINT_TIMEOUT_MS)||9e4;CD=/\(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\.\)$/;Ul=".bostrat/handoff/subtask-result.md",ui=".bostrat/handoff/lead-context.md",MD=(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 ${ui} \u2014 read that first for context. Your scoped task:
|
|
210
210
|
|
|
211
211
|
${e}
|
|
212
212
|
|
|
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 ${
|
|
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 ${Ul} (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 ${ui} \u2014 read that first for context. Your scoped task:
|
|
214
214
|
|
|
215
215
|
${e}
|
|
216
216
|
|
|
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 ${
|
|
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 ${Ul} 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.`,ND=(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,um=null;oe=(e,t)=>e.tracks.find(n=>n.id===t)||null,dm=Promise.resolve();pe=(e,t,n={})=>{let r={at:Date.now(),type:t,...n};return e.events.push(r),PD(e.id,r),qk(e,r),r};oy(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&&(pe(i,"codex-update",{box:e,from:t,to:r||n,status:o,...s?{error:s}:{}}),await Y().catch(()=>{}))});fm=q.join(cn,"plane-link","update-notice.json");pm=(e,t,n)=>t!=null&&n.some(r=>e===r.box&&t>=r.from&&t<=r.to),BD=new Set(["stuck","wrong-approach","too-slow","want-different-model","context-refresh","cost","parallelize","other"]);xr=new Set(["merged","conflict","canceled","failed","handed-off"]),ev=new Map;FD=5e3;UD={claude:[/^\[Request interrupted by user(?: for tool use)?\]$/i],codex:[/^\[?Request interrupted/i,/^Conversation interrupted/i],cursor:[/^\[?Request interrupted/i]};HD=[/^\/model(?:\s|$)/i,/<command-(?:name|message|args)>[^<]*\bmodel\b/i,/^Model changed to\b/i];WD=10*6e4;GD=1500,di=new Map;tv="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.";JD=4e3,YD=3,XD=[300,350,400],ZD=1e3,QD=12,Es=new Map,fi=new Map;rB=[250,400,500,600];sB=3e4,aB=[350,650,900];lB=12e4;bv=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}))});pi="# bostrat workspace",kv=`${pi}
|
|
218
218
|
|
|
219
219
|
TRACK MEMORY: if \`.bostrat/memory/MEMORY.md\` exists in your workspace, read it
|
|
220
220
|
before substantive work \u2014 it is the shared cross-provider memory for this repo
|
|
@@ -423,9 +423,24 @@ When asked about scheduled or routine jobs ("loops"), check \`bostrat-loop ls\`
|
|
|
423
423
|
it lists every Loop for this owner across ALL nodes (\`bostrat-loop show
|
|
424
424
|
<name-or-id>\` prints one loop's prompt, schedule and recent runs; a loop's run
|
|
425
425
|
Track is named "\u21BB <loop name>"). Loops are operator-managed from the Loops tab:
|
|
426
|
-
you can read them but never edit, fire, or delete one
|
|
427
|
-
|
|
428
|
-
|
|
426
|
+
you can read them but never edit, fire, or delete one yourself. To CHANGE an
|
|
427
|
+
existing loop the operator asked about ("this loop is too noisy", "widen that
|
|
428
|
+
sweep's prompt", "that watch can go now"), propose it \u2014 write the fields to
|
|
429
|
+
change as JSON and run:
|
|
430
|
+
|
|
431
|
+
bostrat-loop propose-update <loop-id-or-name> <patch.json>
|
|
432
|
+
bostrat-loop propose-delete <loop-id-or-name> --reason "<why it should go>"
|
|
433
|
+
|
|
434
|
+
A patch names only the fields to change (\`name\`, \`schedule\`, \`prompt\` \u2014 the
|
|
435
|
+
FULL new prompt, \`expectation\`, \`notify\`, \`assignment\`, \`enabled\`; never repo,
|
|
436
|
+
node or owner). The card shows the change as a diff (or, for a delete, the
|
|
437
|
+
agent's reason + Delete / Pause / Keep); nothing changes until the operator
|
|
438
|
+
taps, and a loop edited in the Loops tab since you proposed makes the card go
|
|
439
|
+
stale rather than overwriting. Both are BLOCKING prompts like \`propose\` below.
|
|
440
|
+
Propose a change only when the operator asked for one; a run must NOT propose
|
|
441
|
+
deleting or rewriting ITSELF because it "finished" \u2014 report \`attention\` and say
|
|
442
|
+
so. \`fire\`/run-now stays Loops-tab only. Never claim a loop does or doesn't
|
|
443
|
+
exist from Track names or memory alone; \`bostrat-loop ls\` is the authority.
|
|
429
444
|
|
|
430
445
|
When the operator asks you to SET UP a recurring job ("check X every morning",
|
|
431
446
|
"make this a nightly job"), do NOT hand-write instructions or a cron entry \u2014
|
|
@@ -447,7 +462,7 @@ Proposing is a BLOCKING prompt: once the CLI reports the card was posted, STOP
|
|
|
447
462
|
and end your turn \u2014 do not start or resume other work until the operator's
|
|
448
463
|
decision comes back to you as a chat message. Propose only when the operator has
|
|
449
464
|
asked for a recurring job; never volunteer one as a bonus, and never propose a
|
|
450
|
-
second loop to "fix" one they already have (
|
|
465
|
+
second loop to "fix" one they already have (\`propose-update\` it instead).
|
|
451
466
|
|
|
452
467
|
If THIS Track is a loop's run Track (its name starts with "\u21BB"), you MUST end
|
|
453
468
|
every run by reporting its outcome: \`bostrat-loop report ok\` when it went as
|
|
@@ -485,15 +500,15 @@ which steps to run (and which to skip). An offer buried mid-message, phrased as
|
|
|
485
500
|
statement, or omitted entirely renders no controls \u2014 so close EVERY completed
|
|
486
501
|
implementation turn this way, every time. When the operator has already asked you
|
|
487
502
|
to commit / push / open the PR, just do it; do not re-offer.
|
|
488
|
-
`,
|
|
489
|
-
`)),
|
|
490
|
-
`,`${
|
|
503
|
+
`,pB=kv,ci=kv+Hy;mm=e=>typeof e=="string"&&(e===pi||e.startsWith(`${pi}
|
|
504
|
+
`)),mB="# bostrat multi-repo workspace";hm=ci.replace(`${pi}
|
|
505
|
+
`,`${pi}
|
|
491
506
|
|
|
492
507
|
(Global copy managed by bostrat \u2014 rewritten on service start; edits will be lost.)
|
|
493
508
|
SCOPE: these instructions apply ONLY when your working directory is under
|
|
494
509
|
~/.bostrat/workspaces/ or ~/.bostrat/worktrees/ \u2014 a bostrat-orchestrated box.
|
|
495
510
|
When working anywhere else, ignore everything below.
|
|
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=`
|
|
511
|
+
`);yB=new Set(["claude","codex","cursor"]);hi=new Set,bB=e=>hi.has(e);kB=9e4;nv={merged:"merged",closed:"closed on GitHub WITHOUT merging",lost:"no longer found on GitHub (deleted, or its repo left the grant)"};wm=new Map;Sm=120;Ym=(e,t,n)=>({name:e.name,agent:e.agent,role:t,state:n,cwd:e.cwd,sessionId:e.sessionId,pinned:e.pinned===!0,hookTokenHash:Vs(e.hookToken),createdAt:Date.now()});Rs=new Map,Ms=e=>Rs.get(e)||(Et?oe(Et,e)?.handoff:null)||null;vm=6e4,Bn=(e,t,n=15e3)=>sv("git",["-C",e,...t],{timeout:n,maxBuffer:8*1024*1024}),Tm=new Map,Zm=e=>Tm.get(e)||null;gm=new Map;_B=Number(process.env.BOSTRAT_SUBTASK_TIMEOUT_MS)||18e5,IB=Number(process.env.BOSTRAT_SUBTASK_POLL_MS)||3e3,$s=q.join(cn,"worktrees"),Ts=[ui,Ul],wt=new Map,RB=e=>wt.get(e)||(Et?Et.tracks.map(t=>t.subtasks?.[e]).find(Boolean):null)||null,mo=e=>{let t=new Map([...wt.values()].filter(r=>r.trackId===e).map(r=>[r.id,r])),n=Et?oe(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)},tu=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});Ti=e=>_v(e.trackId,e.id,tu(e));oh=e=>e.trim().replace(/\s+/g,"-").replace(/[^A-Za-z0-9._-]/g,"").toLowerCase()||"track",gi=class extends Error{}});ze();import me from"node:path";import Ze from"node:fs";import GU from"node:readline";import{fileURLToPath as VU}from"node:url";import $t from"express";import zU from"node:http";import JU from"node:https";import gw from"node:os";import{WebSocketServer as YU}from"ws";ze();import QI from"node-pty";async function Cg(e,t,n={}){if(!Ee(t)){Ic(e,`invalid session name: ${t}`),e.close(1008,"invalid session name");return}if(!await Xn()){Ic(e,`host tmux server not running on ${ft}`),e.close(1011,"tmux server unavailable");return}if(!await ie(t)){Ic(e,`session "${t}" not found`),e.close(1008,"session not found");return}let r=Og(n.cols,120,Mg),o=Og(n.rows,32,Ng),s;try{s=QI.spawn(L,["-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){Ic(e,`failed to spawn tmux attach: ${a.message}`),e.close(1011,"spawn failed");return}s.onData(a=>{e.readyState===e.OPEN&&yd(e,{type:"data",data:a})}),s.onExit(({exitCode:a,signal:i})=>{e.readyState===e.OPEN&&(yd(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"&&rR(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{}nR(t)})}var eR=6,tR=250;async function nR(e,t=eR,n=tR){for(let r=0;r<t;r++){if(await jr(e).catch(()=>!0))return!0;await new Promise(o=>setTimeout(o,n))}return!1}var Mg=20,Ng=6;function rR(e,t){return Number.isFinite(Number(e))&&Number.isFinite(Number(t))&&Number(e)>=Mg&&Number(t)>=Ng}function Og(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<n?t:Math.min(500,r|0)}function yd(e,t){try{e.send(JSON.stringify(t))}catch{}}function Ic(e,t){yd(e,{type:"error",message:t})}function Pg(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();$c();df();wr();import{createRequire as XU}from"node:module";var LB=e=>!!e&&!e.finishedAt&&!e.canceled&&!e.error;function ch({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(LB)&&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 Lv from"node:fs";import Sr from"node:fs/promises";import e1 from"node:os";import Ve from"node:path";import{spawn as t1}from"node:child_process";import{createRequire as n1}from"node:module";ce();rl();import Bv from"node:fs";import uh from"node:fs/promises";import HB from"node:http";import jn from"node:path";import su from"node:os";import{createRequire as WB}from"node:module";import dh from"ws";import FB from"node:path";import UB from"node:os";var Ai=process.env.BOSTRAT_STATE_DIR||FB.join(process.env.HOME||UB.tmpdir(),".bostrat");function lh(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 Mv(e=process.env){if(!e.BOSTRAT_PLANE_URL)return null;let t=lh(e);return t==="127.0.0.1"||t==="::1"||t==="localhost"?null:{host:t}}function Nv(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 WB(import.meta.url)("../package.json").version||null}catch{return null}})(),qB=process.env.BOSTRAT_STATE_DIR||jn.join(process.env.HOME||su.tmpdir(),".bostrat");function ho({env:e=process.env,pkgDir:t=jn.resolve(jn.dirname(new URL(import.meta.url).pathname),".."),exists:n=Bv.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(jn.join(t,"..",".git"))?"git-checkout":"dev"}function Ei(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 ru(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 KB({home:e=process.env.HOME||su.homedir(),env:t=process.env}={}){let n=r=>{try{return Bv.statSync(r),!0}catch{return!1}};return{claude:n(jn.join(e,".claude",".credentials.json")),codex:n(jn.join(t.CODEX_HOME||jn.join(e,".codex"),"auth.json"))}}var Ge=1,Pv=1e3,Dv=3e4,GB=6e4,VB=1500,zB=500,ou=100,JB=2e3,YB=25e3,XB=6e4,ZB=6e4,QB=6e4;function jv({url:e,token:t,nodeId:n=su.hostname(),name:r=su.hostname(),listTracks:o,detectAgents:s=null,onTracksTransferred:a=null,sampleMetrics:i=null,metricsMs:c=ZB,onUpdateAvailable:l=null,onUpdatePolicy:u=null,onHelloRefused:d=null,updatePolicy:f=null,installKind:p=ho(),selfUrl:m=`http://127.0.0.1:${process.env.PORT||3362}`,log:w=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=Nv(e);let y=m.replace(/^http/,"ws"),x=new Set,k=new Map,T=new Map,_=jn.join(qB,"plane-link","cursor.json"),P={},N={},O=new Map,$=null,R=!1,ee=!1,de=Pv,be=null,G=null,I=null,J=null,X=null,he=!1,Se=0;async function Le(){try{P=JSON.parse(await uh.readFile(_,"utf8"))}catch{P={}}}function F(){he=!0,!I&&(I=setTimeout(async()=>{if(I=null,!!he){he=!1;try{await uh.mkdir(jn.dirname(_),{recursive:!0}),await uh.writeFile(_,JSON.stringify(P))}catch(V){w.error("[plane-link] cursor flush failed:",V?.message||V)}}},JB),I.unref?.())}function D(V){return!$||$.readyState!==dh.OPEN?!1:($.send(JSON.stringify(V)),!0)}function Q(V){let K=Date.now();for(let{trackId:E,event:B}of V){let ge=O.get(B.id);O.set(B.id,{trackId:E,seq:B.seq,event:B,sentAt:K,tries:(ge?.tries??0)+1})}return D({v:Ge,ch:"events",type:"append",events:V})}function Ne(V){let K=null;for(let E of O.values())E.trackId===V&&(K==null||E.seq<K)&&(K=E.seq);return K}function Un(V){let K=N[V];if(K==null)return;let E=Ne(V),B=E==null?K:Math.min(K,E-1);B>(P[V]??0)&&(P[V]=B,F())}function Wt(){if(!R)return;let V=Date.now(),K=[...O.values()].filter(E=>E.event&&V-E.sentAt>=XB);if(K.length){K.sort((E,B)=>E.trackId===B.trackId?E.seq-B.seq:E.trackId<B.trackId?-1:1),w.log(`[plane-link] re-sending ${K.length} unacked event(s) the plane never confirmed`);for(let E=0;E<K.length;E+=ou){let B=K.slice(E,E+ou).map(ge=>({trackId:ge.trackId,event:ge.event}));if(!Q(B))return}}}async function b(){if(!i)return;let V;try{V=await i()}catch(K){w.error("[plane-link] metrics sample failed:",K?.message||K);return}V&&D({v:Ge,ch:"ctl",type:"metrics",metrics:V})}function h(){i&&(X&&clearInterval(X),b(),X=setInterval(b,c),X.unref?.())}async function v(){let V;try{V=await o()}catch(K){w.error("[plane-link] backfill listTracks failed:",K?.message||K);return}D({v:Ge,ch:"ctl",type:"tracks",ids:V.filter(K=>!K.transferredTo).map(K=>K.id).filter(K=>typeof K=="string")});for(let K of V){if(!R)return;let E;try{E=await j(K)}catch{continue}let B=P[K.id]??0,ge=E.filter(ye=>ye.seq>B&&typeof ye.id=="string");for(let ye=0;ye<ge.length;ye+=ou){let Qe=ge.slice(ye,ye+ou).map(kt=>({trackId:K.id,event:kt}));if(!Q(Qe))return}}}function g(V){!R||!V?.trackId||typeof V.id!="string"||Q([{trackId:V.trackId,event:V}])}let C=new Map;async function se(V){let{reqId:K,method:E,path:B,headers:ge={},body:ye}=V,Qe=Object.fromEntries(Object.entries(ge).filter(([,qt])=>qt!=null)),kt=new AbortController;C.set(K,kt);try{if(V.host){await _e(V,Qe,kt);return}let qt=await fetch(m+B,{method:E,headers:Qe,body:ye?Buffer.from(ye,"base64"):void 0,signal:kt.signal}),Hn=Object.fromEntries(qt.headers);for(let xt of["connection","keep-alive","transfer-encoding","upgrade","content-encoding","content-length"])delete Hn[xt];if(Hn["content-type"]||(Hn["content-type"]="application/octet-stream"),D({v:Ge,ch:"control",type:"http_head",reqId:K,status:qt.status,headers:Hn}),qt.body){for await(let xt of qt.body)if(!D({v:Ge,ch:"control",type:"http_data",reqId:K,data:Buffer.from(xt).toString("base64")}))break}D({v:Ge,ch:"control",type:"http_end",reqId:K,error:null})}finally{C.delete(K)}}function _e(V,K,E){let{reqId:B,method:ge,path:ye,body:Qe}=V,kt=new URL(m);return new Promise(qt=>{let Hn=HB.request({host:kt.hostname,port:kt.port,method:ge,path:ye,headers:{...K,host:V.host},signal:E.signal},xt=>{let{connection:h4,"keep-alive":w4,"transfer-encoding":g4,upgrade:y4,...GE}=xt.headers;D({v:Ge,ch:"control",type:"http_head",reqId:B,status:xt.statusCode||502,headers:GE}),xt.on("data",VE=>{D({v:Ge,ch:"control",type:"http_data",reqId:B,data:Buffer.from(VE).toString("base64")})||xt.destroy()}),xt.on("end",()=>{D({v:Ge,ch:"control",type:"http_end",reqId:B,error:null}),qt()}),xt.on("error",()=>{D({v:Ge,ch:"control",type:"http_end",reqId:B,error:"view upstream error"}),qt()})});Hn.on("error",xt=>{D({v:Ge,ch:"control",type:"http_end",reqId:B,error:xt?.message||"view request failed"}),qt()}),Qe&&Hn.write(Buffer.from(Qe,"base64")),Hn.end()})}function Oe(){if(ee)return;$=new dh(e,{headers:{authorization:`Bearer ${t}`}}),$.on("open",async()=>{let K=null;try{K=await s?.()||null}catch{K=null}D({v:Ge,ch:"ctl",type:"hello",nodeId:n,name:r,platform:process.platform,version:Lt,installKind:p,...typeof f=="function"?{updatePolicy:f()}:f?{updatePolicy:f}:{},providers:KB(),...K?{agents:K}:{}})}),$.on("message",K=>{let E;try{E=JSON.parse(K)}catch{return}if(E.ch==="ctl"&&E.type==="hello_ok"){R=!0,de=Pv,be=null,w.log(`[plane-link] connected to plane as ${n}`),E.minNodeVersion&&Lt&&Ei(Lt,E.minNodeVersion)&&w.error(`[plane-link] this node is bostrat-node ${Lt}; the plane asks for \u2265${E.minNodeVersion} \u2014 update with: ${ru(p)}`);try{u?.(typeof E.updatePolicy=="string"?E.updatePolicy:null)}catch{}let B=typeof E.latestNodeVersion=="string"?E.latestNodeVersion:null;if(B&&Lt&&Ei(Lt,B)&&!x.has(B)){x.add(B),w.log(`[plane-link] bostrat-node ${B} is available (this node runs ${Lt}) \u2014 ${ru(p)}`);try{l?.({current:Lt,latest:B,min:E.minNodeVersion??null,installKind:p,required:!!(E.minNodeVersion&&Ei(Lt,E.minNodeVersion))})}catch(ge){w.error(`[plane-link] update notice failed: ${ge?.message||ge}`)}}J&&clearInterval(J),J=setInterval(()=>{D({v:Ge,ch:"ctl",type:"ping"}),Wt()},YB),J.unref?.(),h(),v().catch(ge=>w.error("[plane-link] backfill failed:",ge?.message||ge));return}if(E.ch==="ctl"&&E.type==="hello_refused"){let B=typeof E.minNodeVersion=="string"?E.minNodeVersion:null,ge=typeof E.latestNodeVersion=="string"?E.latestNodeVersion:null;w.error(`[plane-link] plane REFUSED this node: bostrat-node ${Lt??"?"} is below its minimum ${B??"?"} \u2014 ${E.fix||ru(p)}`),de=Dv,be=0;try{d?.({current:Lt,min:B,latest:ge,fix:E.fix||ru(p),installKind:p})}catch(ye){w.error(`[plane-link] refusal handler failed: ${ye?.message||ye}`)}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"){nl(E.caps??{},{source:"plane",updatedAt:E.updatedAt??Date.now()}).then(B=>w.log(`[plane-link] spend caps ${Object.keys(B.caps).length?`updated (${Object.keys(B.caps).join(", ")})`:"cleared"} from plane`)).catch(B=>w.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=>w.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 ge of E.ids){let ye=O.get(ge);ye&&(O.delete(ge),(N[ye.trackId]??0)<ye.seq&&(N[ye.trackId]=ye.seq),B.add(ye.trackId))}for(let ge of B)Un(ge);return}if(E.ch==="control"&&E.type==="http_open"){se(E).catch(B=>D({v:Ge,ch:"control",type:"http_end",reqId:E.reqId,error:B?.message||"tunnel error"}));return}if(E.ch==="control"&&E.type==="http_cancel"){C.get(E.reqId)?.abort();return}if(E.ch==="attach"&&E.type==="open"){let B=new dh(y+E.path,Array.isArray(E.protocols)?E.protocols:[],E.host?{headers:{host:E.host}}:void 0);k.set(E.streamId,B),T.set(E.streamId,[]),B.on("open",()=>{let ye=T.get(E.streamId)||[];T.delete(E.streamId);for(let{buf:Qe,bin:kt}of ye)B.send(Qe,{binary:kt})}),B.on("message",(ye,Qe)=>{let kt=Qe?ye:Buffer.from(String(ye));D({v:Ge,ch:"attach",type:"data",streamId:E.streamId,data:kt.toString("base64"),...Qe?{bin:!0}:{}})});let ge=()=>{T.delete(E.streamId),k.delete(E.streamId)&&D({v:Ge,ch:"attach",type:"close",streamId:E.streamId})};B.on("close",ge),B.on("error",ge);return}if(E.ch==="attach"&&E.type==="data"){let B=k.get(E.streamId);if(!B)return;let ge=Buffer.from(E.data,"base64"),ye=E.bin===!0,Qe=T.get(E.streamId);Qe?Qe.push({buf:ge,bin:ye}):B.send(ge,{binary:ye});return}if(E.ch==="attach"&&E.type==="close"){let B=k.get(E.streamId);k.delete(E.streamId),T.delete(E.streamId),B?.close();return}});let V=()=>{R&&w.log("[plane-link] disconnected from plane"),R=!1,J&&(clearInterval(J),J=null),X&&(clearInterval(X),X=null),O.clear(),N={};for(let B of k.values())B.close();k.clear(),T.clear();for(let B of C.values())B.abort();if(C.clear(),ee||G)return;be==null&&(be=Date.now());let K=Date.now()-be<GB,E=K?VB+Math.random()*zB:de;G=setTimeout(()=>{G=null,Oe()},E),G.unref?.(),K||(de=Math.min(de*2,Dv))};$.on("close",V),$.on("error",K=>{let E=/Unexpected server response: (\d+)/.exec(K?.message||"")?.[1];(E==="401"||E==="403")&&Date.now()-Se>=QB&&(Se=Date.now(),w.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)`)),V()})}return at.on("event",g),Le().then(Oe),{stop:()=>{ee=!0,at.off("event",g),G&&clearTimeout(G),I&&clearTimeout(I),J&&clearInterval(J),X&&clearInterval(X),D({v:Ge,ch:"ctl",type:"bye",reason:"shutdown"}),$?.close()},status:()=>({connected:R,nodeId:n,cursor:{...P},inflight:O.size})}}Oc();var au=(()=>{try{return n1(import.meta.url)("../package.json").version||null}catch{return null}})(),r1=process.env.BOSTRAT_STATE_DIR||Ve.join(process.env.HOME||e1.tmpdir(),".bostrat"),Fv=Ve.join(r1,"self-update"),iu=Ve.join(Fv,"status.json"),o1=1440*6e4,s1=6e4,_i=new Set(["npm-global","git-checkout"]);function Uv({kind:e=ho(),env:t=process.env}={}){let n=String(t.BOSTRAT_UPDATE_POLICY||"").trim();return["auto","prompt","off"].includes(n)?n:_i.has(e)?e==="git-checkout"||a1(t)?"auto":"prompt":"off"}function a1(e=process.env){return!!(e.INVOCATION_ID||e.XPC_SERVICE_NAME&&e.XPC_SERVICE_NAME!=="0")}var yt=e=>`'${String(e).replace(/'/g,"'\\''")}'`;function i1({kind:e,to:t,from:n=au,statusFile:r=iu,logFile:o=Ve.join(Fv,"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||Ve.resolve(Ve.dirname(new URL(import.meta.url).pathname),"..",".."),startedAt:u=Date.now()}={}){if(!_i.has(e))throw new Error(`self-update: install kind "${e}" is not actionable here`);if(!/^\d+\.\d+\.\d+$/.test(String(t)))throw new Error(`self-update: bad target version "${t}"`);if(n&&!/^\d+\.\d+\.\d+$/.test(String(n)))throw new Error(`self-update: bad current version "${n}"`);let d=(y,x="")=>`write_status ${yt(y)} ${x}`,f=process.platform==="darwin"?`launchctl kickstart -k gui/${c}/${i}`:`systemctl --user restart ${yt(i)}`,p=`
|
|
497
512
|
health() {
|
|
498
513
|
for i in $(seq 1 45); do
|
|
499
514
|
sleep 1
|
|
@@ -516,10 +531,10 @@ write_status() { # <status> [error]
|
|
|
516
531
|
"\${err:+,\\"error\\":\\"$err\\"}" > "$STATUS_FILE.tmp" && mv "$STATUS_FILE.tmp" "$STATUS_FILE"
|
|
517
532
|
}
|
|
518
533
|
${p}
|
|
519
|
-
`;if(e==="npm-global"){let
|
|
520
|
-
`:"",k=a?` --prefix ${yt(a)}`:"",
|
|
534
|
+
`;if(e==="npm-global"){let y=a?Ve.join(a,"bin","npm"):"npm",x=a?`export PATH=${yt(Ve.join(a,"bin"))}:"$PATH"
|
|
535
|
+
`:"",k=a?` --prefix ${yt(a)}`:"",T=_=>`${yt(y)} install -g "bostrat-node@${_}" --no-audit --no-fund${k}`;return m+x+`
|
|
521
536
|
${d("updating",'"" ')}
|
|
522
|
-
if ! ${
|
|
537
|
+
if ! ${T("$TO")}; then
|
|
523
538
|
${d("failed",'"npm install failed"')}
|
|
524
539
|
exit 1
|
|
525
540
|
fi
|
|
@@ -534,13 +549,13 @@ if health; then
|
|
|
534
549
|
fi
|
|
535
550
|
# \u2500\u2500 rollback: the same primitive with the previous version \u2500\u2500
|
|
536
551
|
${d("rolling-back",'"agent did not answer on :'+s+' after the update"')}
|
|
537
|
-
if [ -n "$FROM" ] && ${
|
|
552
|
+
if [ -n "$FROM" ] && ${T("$FROM")} && ${f} && health; then
|
|
538
553
|
${d("rolled-back",'"agent did not answer on :'+s+' after the update; rolled back"')}
|
|
539
554
|
exit 1
|
|
540
555
|
fi
|
|
541
556
|
${d("failed",'"agent did not answer after the update AND the rollback"')}
|
|
542
557
|
exit 1
|
|
543
|
-
`}let
|
|
558
|
+
`}let w=y=>`( cd ${yt(l)} && git fetch -q origin main --tags && git show origin/main:scripts/deploy-pi.sh > "$TMP_SCRIPT" && bash "$TMP_SCRIPT" "v${y}" )`;return m+`
|
|
544
559
|
LIVE=${yt(l)}
|
|
545
560
|
TMP_SCRIPT="$(mktemp)"
|
|
546
561
|
trap 'rm -f "$TMP_SCRIPT"' EXIT
|
|
@@ -548,72 +563,72 @@ trap 'rm -f "$TMP_SCRIPT"' EXIT
|
|
|
548
563
|
# restart \u2192 health in one call): the agent it restarts settles "done" on boot,
|
|
549
564
|
# and nothing here may write after that boot or the settle would repeat.
|
|
550
565
|
${d("restarting")}
|
|
551
|
-
if ${
|
|
566
|
+
if ${w("$TO")}; then
|
|
552
567
|
exit 0
|
|
553
568
|
fi
|
|
554
569
|
${d("rolling-back",'"deploy-pi.sh v$TO failed"')}
|
|
555
|
-
if [ -n "$FROM" ] && ${
|
|
570
|
+
if [ -n "$FROM" ] && ${w("$FROM")}; then
|
|
556
571
|
${d("rolled-back",'"deploy-pi.sh v$TO failed; rolled back to v$FROM"')}
|
|
557
572
|
exit 1
|
|
558
573
|
fi
|
|
559
574
|
${d("failed",'"deploy-pi.sh v$TO failed AND the rollback failed"')}
|
|
560
575
|
exit 1
|
|
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(
|
|
576
|
+
`}async function Ii(e=iu){try{return JSON.parse(await Sr.readFile(e,"utf8"))}catch{return null}}var c1=new Set(["updating","restarting","rolling-back"]),cu=e=>!!(e&&c1.has(e.status));async function lu({to:e,from:t=au,kind:n=ho(),reason:r="manual",statusFile:o=iu,spawnImpl:s=t1,now:a=Date.now,npmPrefix:i=void 0,log:c=console}={}){if(!e)return{ok:!1,reason:"no target version"};if(!_i.has(n))return{ok:!1,reason:`install kind "${n}" updates elsewhere`};if(t&&!Ei(t,e))return{ok:!1,reason:`already on ${t} (target ${e})`};let l=await Ii(o);if(cu(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 Sr.mkdir(Ve.dirname(o),{recursive:!0});let u=i!==void 0?i:n==="npm-global"?Pd(await l1())||Ve.resolve(Ve.dirname(process.execPath),".."):null,d=i1({kind:n,to:e,from:t,statusFile:o,npmPrefix:u,startedAt:a()}),f=Ve.join(Ve.dirname(o),`update-${e}.sh`),p=Ve.join(Ve.dirname(o),"last.log");await Sr.writeFile(f,d,{mode:448}),await Sr.writeFile(o,JSON.stringify({status:"updating",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a()}));let m=Lv.existsSync("/run/systemd/system")&&await u1("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(w){return await Sr.writeFile(o,JSON.stringify({status:"failed",from:t,to:e,kind:n,reason:r,startedAt:a(),updatedAt:a(),error:`could not spawn updater: ${w?.message||w}`})),{ok:!1,reason:`could not spawn updater: ${w?.message||w}`}}return c.log?.(`[self-update] ${t??"?"} \u2192 ${e} (${n}, ${r}) started \u2014 status ${o}`),{ok:!0,to:e,from:t,kind:n,scriptFile:f,statusFile:o}}async function l1(){try{return await Sr.realpath(new URL(import.meta.url).pathname)}catch{return""}}async function u1(e){for(let t of String(process.env.PATH||"").split(Ve.delimiter))try{return await Sr.access(Ve.join(t,e),Lv.constants.X_OK),!0}catch{}return!1}var d1=45*6e4;async function Hv({statusFile:e=iu,current:t=au,now:n=Date.now,staleMs:r=d1}={}){let o=await Ii(e);if(!o)return null;let s=a=>Sr.writeFile(e,JSON.stringify(a)).catch(()=>{});if(cu(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 Wv({isBusy:e,start:t=lu,onStuck:n=null,onStarted:r=null,tickMs:o=s1,stuckMs:s=o1,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 w=await t({to:m.to,from:m.from,kind:m.kind,reason:"auto"});l.log?.(`[self-update] stasis reached \u2192 ${w.ok?"started":`not started: ${w.reason}`}`);try{await r?.({...m,result:w})}catch{}};return{arm({to:p,from:m=au,kind:w=ho()}){return u?.to===p?!1:(d(),u={to:p,from:m,kind:w,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}}}ce();et();var f1=(...e)=>fetch(...e);function p1(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}async function fh(e=process.env){if(!p1(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 f1(`${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 Ns(e,t=process.env){let n=String(e??"").trim();if(!n)throw new Error("a circuit name or id is required");let r=await fh(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 uu(e,t,{source:n="chat"}={}){return A(e,{type:"track.circuit.tagged",actor:"orchestrator",source:"orchestrator",payload:t?{circuitId:t.id,circuitName:t.name??null,source:n}:{circuitId:null,source:n}})}ce();et();var m1=(...e)=>fetch(...e);async function du(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 m1(`${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 h1(e,t=process.env){return du("POST","/v1/circuits",{name:e},t).then(n=>n.circuit)}async function qv({circuitRef:e,newCircuitName:t,name:n,markdown:r},o=process.env){let s=t?await h1(t,o):await Ns(e,o),a=await du("POST","/v1/blueprints",{circuitId:s.id,name:n,markdown:r},o);return{circuit:s,...a}}async function ph(e,t=process.env){let n=await Ns(e,t),r=await du("GET",`/v1/blueprints/${encodeURIComponent(n.id)}`,null,t);return{circuit:n,...r}}async function mh(e,t=process.env){return(await du("GET","/v1/circuits",null,t)).memberships?.[e]??null}async function Kv(e,t,n,r=process.env){let{blueprint:o,items:s}=await ph(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 A(e,{type:"blueprint.item.bound",actor:"orchestrator",source:"orchestrator",payload:{itemIds:n,blueprintId:o.id,source:"cli"}}),{blueprint:o,itemIds:n}}ce();et();var w1=(...e)=>fetch(...e);async function g1(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 w1(`${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 Gv=72,hh=160;async function Vv(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>Gv)throw new Error(`the title is the choice as a LABEL \u2014 ${Gv} 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 ${hh} chars) a reader can act on without opening the record`);if(c.length>hh)throw new Error(`the summary is one sentence \u2014 ${hh} 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 A(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 zv(e,t=process.env){return g1("GET",`/v1/decisions?track=${encodeURIComponent(e)}`,null,t)}om();ce();import Ni from"node:fs/promises";import ku from"node:path";import ij from"node:os";Vn();import y1 from"node:fs/promises";import Jv from"node:path";import b1 from"node:os";var Yv=()=>Jv.join(process.env.BOSTRAT_STATE_DIR||Jv.join(process.env.HOME||b1.tmpdir(),".bostrat"),"integrations.json");async function Ln(){try{return JSON.parse(await y1.readFile(Yv(),"utf8"))||{}}catch{return{}}}var fu=["linked","prOpened","prMerged","create"],wh=["prompt","auto","off"],k1={create:["prompt","off"]},Xv=e=>k1[e]??wh;function pu(e){let t=e.linear||{},n=t.transitions===!0?"auto":"prompt",r={};for(let o of fu){let s=Xv(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 wo(){let e=await Ln(),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:pu(e),defaultTeam:n}:null}async function mu(){let e=await Ln(),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:pu(e)}}async function gh(){return pu(await Ln())}async function Zv(e){let t=Object.entries(e||{});if(!t.length)throw new Error(`nothing to set \u2014 phases: ${fu.join(", ")}`);for(let[r,o]of t){if(!fu.includes(r))throw new Error(`unknown phase "${r}" \u2014 configurable phases: ${fu.join(", ")}`);let s=Xv(r);if(!s.includes(o))throw new Error(s.length<wh.length?`mode "${o}" not allowed for "${r}" \u2014 ${r} is always confirmed (modes: ${s.join(", ")})`:`unknown mode "${o}" \u2014 modes: ${wh.join(", ")}`)}let n=await Ln();return n.ticketFlow={...pu(n),...Object.fromEntries(t)},n.linear&&(delete n.linear.ticketFlow,delete n.linear.transitions),await it(Yv(),n,{mode:384}),n.ticketFlow}async function Ri(){let e=(await Ln()).deepgram||{},t=process.env.DEEPGRAM_API_KEY||e.apiKey;return t?{apiKey:t}:null}async function x1(){let e=(await Ln()).openai||{},t=process.env.BOSTRAT_OPENAI_API_KEY||e.apiKey;return t?{apiKey:t}:null}var S1={openai:{model:"gpt-realtime-2.1-mini",voice:"marin"}};async function hu(){let t=(await Ln()).voiceChat||{},n=t.provider||"openai",r=S1[n];if(!r)return null;let o=n==="openai"?await x1():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 Ln()).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 go(){let e=(await Ln()).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}}ce();Jn();qr();xs();import Oi from"node:fs/promises";import cT from"node:path";import V1 from"node:os";import{randomUUID as lT}from"node:crypto";var bh={};Wu(bh,{createComment:()=>O1,findIssueUrl:()=>E1,issueStates:()=>N1,mdToAdf:()=>nT,moveIssue:()=>P1,parseIssueRef:()=>A1,resolveIssue:()=>_1,uploadImage:()=>C1,upsertCard:()=>I1,whoami:()=>yh});var Ps=e=>String(e.baseUrl||"").replace(/\/$/,""),tT=e=>`Basic ${Buffer.from(`${e.email}:${e.apiToken}`).toString("base64")}`;async function yo(e,t,n,r){let o=await fetch(`${Ps(e)}${n}`,{method:t,headers:{authorization:tT(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 v1=/\/browse\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,T1=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function A1(e){let t=String(e||"").trim();if(/^https?:\/\//i.test(t)){let r=t.match(v1);return r?r[1].toUpperCase():null}let n=t.match(T1);return n?n[1]:null}function E1(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 _1(e,t){let n;try{n=await yo(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:`${Ps(e)}/browse/${n.key}`,state:n.fields?.status?.name??null}}async function I1(e,t,{url:n,title:r,subtitle:o}){await yo(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 R1=/(!\[[^\]]*\]\([^)\s]+\)|\*\*[^*\n]+\*\*|`[^`\n]+`|_[^_\n]+_|https?:\/\/[^\s)]+)/g;function $1(e){let t=[],n=0;for(let r of e.matchAll(R1)){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 nT(e){let t=String(e??"").split(/\n{2,}/).map(n=>n.trim()).filter(Boolean).map(n=>{let r=[];return n.split(`
|
|
577
|
+
`).forEach((o,s)=>{s>0&&r.push({type:"hardBreak"}),r.push(...$1(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 O1(e,t,n){let r=await yo(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/comment`,{body:nT(n)});return r?.id?`${Ps(e)}/browse/${t.key}?focusedCommentId=${r.id}`:null}async function yh(e){let t=await yo(e,"GET","/rest/api/3/myself"),n=null;try{n=new URL(Ps(e)).host}catch{n=e.baseUrl??null}return{user:t?.displayName??t?.emailAddress??null,org:n}}async function C1(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(`${Ps(e)}/rest/api/3/issue/${encodeURIComponent(t)}/attachments`,{method:"POST",headers:{authorization:tT(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??`${Ps(e)}/rest/api/3/attachment/content/${c.id}`}var M1={new:"unstarted",indeterminate:"started",done:"completed"},eT=e=>M1[e?.key]??"started";async function N1(e,t){let n=encodeURIComponent(t.key),[r,o]=await Promise.all([yo(e,"GET",`/rest/api/3/issue/${n}?fields=status`),yo(e,"GET",`/rest/api/3/issue/${n}/transitions`)]),s=r?.fields?.status??null,a=s?{id:`status:${s.id}`,name:s.name,type:eT(s.statusCategory)}:null,i=(o?.transitions??[]).map((c,l)=>({id:c.id,name:c.to?.name??c.name,type:eT(c.to?.statusCategory),position:l}));return{current:a,states:i}}async function P1(e,t,n){await yo(e,"POST",`/rest/api/3/issue/${encodeURIComponent(t.key)}/transitions`,{transition:{id:String(n)}})}var vh={};Wu(vh,{createComment:()=>W1,createIssue:()=>Sh,createProject:()=>xh,findIssueUrl:()=>F1,issueStates:()=>K1,moveIssue:()=>G1,parseIssueRef:()=>L1,resolveIssue:()=>U1,teams:()=>Ds,uploadImage:()=>q1,upsertCard:()=>H1,whoami:()=>kh});var D1=()=>process.env.BOSTRAT_LINEAR_API_URL||"https://api.linear.app/graphql";async function un(e,t,n={}){let r=await fetch(D1(),{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 B1=/linear\.app\/[^/\s]+\/issue\/([A-Za-z][A-Za-z0-9]{0,9}-\d+)/,j1=/^([A-Z][A-Z0-9]{0,9}-\d+)$/;function L1(e){let t=String(e||"").trim(),n=t.match(B1);if(n)return n[1].toUpperCase();let r=t.match(j1);return r?r[1]:null}function F1(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 U1(e,t){let r=(await un(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 H1(e,t,{url:n,title:r,subtitle:o}){let s=await un(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 W1(e,t,n){let r=await un(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 kh(e){let t=await un(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 q1(e,{filename:t,contentType:n,buffer:r}){let o=await un(e,`mutation($contentType: String!, $filename: String!, $size: Int!) {
|
|
563
578
|
fileUpload(contentType: $contentType, filename: $filename, size: $size) {
|
|
564
579
|
success
|
|
565
580
|
uploadFile { uploadUrl assetUrl headers { key value } }
|
|
566
581
|
}
|
|
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?`
|
|
582
|
+
}`,{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 K1(e,t){let n=await un(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 G1(e,t,n){if(!(await un(e,"mutation($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success } }",{id:t.issueId,input:{stateId:n}})).issueUpdate?.success)throw new Error("linear: issueUpdate did not succeed")}async function Ds(e){return((await un(e,"query { teams(first: 100) { nodes { id key name } } }")).teams?.nodes??[]).map(n=>({id:n.id,key:n.key,name:n.name}))}var rT=255;async function xh(e,{teamId:t,name:n,description:r}){let o=r||null,s=!!o&&o.length>rT,a=s?`${o.slice(0,rT-1).trimEnd()}\u2026`:o,i=await un(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 un(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 uT={linear:{api:vh,config:wo},jira:{api:bh,config:mu}};async function $h(){let e=[];for(let[t,{api:n,config:r}]of Object.entries(uT)){let o=await r();o&&e.push({name:t,api:n,cfg:o})}return e}async function bo(e){let t=uT[e?.provider];if(!t)return null;let n=await t.config();return n?{name:e.provider,api:t.api,cfg:n}:null}function Oh(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 dT=()=>process.env.BOSTRAT_STATE_DIR||cT.join(process.env.HOME||V1.tmpdir(),".bostrat"),_h=()=>cT.join(dT(),"tickets.json"),fT=()=>(process.env.BOSTRAT_PUBLIC_URL||"http://bostrat.globopi").replace(/\/$/,""),wu=e=>`${fT()}/track/${e}`,vr=null,Th=null,Ah=Promise.resolve();async function Mi(){return vr||(Th??=Oi.readFile(_h(),"utf8").then(e=>({tickets:JSON.parse(e)?.tickets??{},existed:!0})).catch(()=>({tickets:{},existed:!1})).then(async({tickets:e,existed:t})=>{if(vr=e,!t&&Ae()){let{records:n,source:r}=await We("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;vr[s]=i}Object.keys(vr).length&&await Ih().catch(()=>{})}}return vr}).finally(()=>Th=null),Th)}async function Ih(){let e=JSON.stringify({tickets:vr},null,2),t=`${_h()}.${process.pid}.tmp`;await Oi.mkdir(dT(),{recursive:!0}),await Oi.writeFile(t,e),await Oi.rename(t,_h())}async function Tr(e=null){if(Ah=Ah.then(Ih,Ih),await Ah.catch(()=>{}),!e)return;let t=vr?.[e];t?tt("tickets",{id:e,updatedAt:Date.now(),...t},{trackId:e}).catch(()=>{}):Ot("tickets",e).catch(()=>{})}async function dn(e){return(await Mi())[e]??null}async function pT(){return{...await Mi()}}async function mT(){return(await $h()).length>0}async function Bs(e,t,{source:n="api"}={}){let r=await $h();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 Mi())[e.id]=c,await Tr(e.id),await A({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 Ci(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 $i(c,`**Bostrat** \u2014 Track **${c.track.name}** linked${l.length?` (${l.join(", ")})`:""}.`),await gT(e.id,c,"linked"),c}async function Ch(e){let t=await Mi();return t[e]?(delete t[e],await Tr(e),!0):!1}var Rh=e=>`$${e>=100?Math.round(e):e.toFixed(2)}`;async function hT(e,t){if(!e.track?.cwd)return null;let n=ar(await j({id:t,cwd:e.track.cwd}));return!n.totalUsd||n.totalUsd<.01?null:n}function z1(e){let t=Object.entries(e.byProvider).sort((r,o)=>o[1].usd-r[1].usd).map(([r,o])=>`${r} ${Rh(o.usd)}`).join(" \xB7 "),n=Object.keys(e.byBox).length;return`${Rh(e.totalUsd)}${t?` (${t})`:""}${n>1?` across ${n} boxes`:""}`}function J1(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(Rh(t.totalUsd)),n.join(" \xB7 ")||"linked"}async function Ci(e,t){let n=await bo(t);if(!n)return;let r=await hT(t,e).catch(()=>null);await n.api.upsertCard(n.cfg,t,{url:wu(e),title:`Bostrat Track: ${t.track.name}`,subtitle:J1(t,r)})}function gu(e){let t=new URL(fT());return`${t.protocol}//${e}.${t.host}/`}var Y1={linked:"linked",opened:"prOpened",merged:"prMerged"};function X1(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 wT(e,t,{from:n,to:r,mode:o,phase:s=null,actor:a="user"}){t.state=r,await Tr(e),await A({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 gT(e,t,n){let r=await bo(t);if(!r)return;let o=r.cfg.ticketFlow?.[Y1[n]]??"prompt";if(o==="off")return;let{current:s,states:a}=await r.api.issueStates(r.cfg,t),i=Oh(s,a),c=X1(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 wT(e,t,{from:l,to:c.name,mode:"auto",phase:n,actor:"system"});return}await A({id:e,cwd:t.track?.cwd},{type:"ticket.move.offered",actor:"system",source:"orchestrator",payload:{ts:Date.now(),offerId:lT(),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 yT(e){let t=await dn(e);if(!t)throw new Error("no ticket linked to this Track");let n=await bo(t);if(!n)throw new Error("no ticketing integration configured");let{current:r,states:o}=await n.api.issueStates(n.cfg,t),s=Oh(r,o);return s?.name&&t.state!==s.name&&(t.state=s.name,await Tr(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 Mh(e,t,{mode:n="manual"}={}){if(!t)throw new Error("stateId required");let r=await dn(e);if(!r)throw new Error("no ticket linked to this Track");let o=await bo(r);if(!o)throw new Error("no ticketing integration configured");let{current:s,states:a}=await o.api.issueStates(o.cfg,r),i=Oh(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 Tr(e)),{key:r.key,from:l,to:c.name,moved:!1}):(await o.api.moveIssue(o.cfg,r,c.id),await wT(e,r,{from:l,to:c.name,mode:n}),{key:r.key,from:l,to:c.name,moved:!0})}async function bT(e,t){let n=await dn(e);if(!n)throw new Error("no ticket linked to this Track");let r=await bo(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 A({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 kT(e,t,n){if(!t)throw new Error("offerId required");let r=await j({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 Mh(e.id,o.payload.to?.id,{mode:"offer"})),await A({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 oT=50;async function xT(){let e=await wo();if(!e)throw new Error("no ticketing integration configured");return Ds(e)}function Z1(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>oT)throw new Error(`too many issues (${n.length}); cap is ${oT} per proposal`);return{epic:r,issues:n,team:t.team?String(t.team).trim():null,linkCurrentTrack:t.linkCurrentTrack===!0}}async function Q1(e,t,n,r){let o=r??await Ds(e),s=i=>o.find(c=>c.id===i||c.key.toLowerCase()===String(i).toLowerCase()||c.name.toLowerCase()===String(i).toLowerCase())??null;if(t.team){let i=s(t.team);if(i)return i}let a=await dn(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 ST(e,t){let n=await wo();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=Z1(t),o=await Ds(n),s=await Q1(n,r,e,o),a=s?{id:s.id,key:s.key,name:s.name}:null,i=lT();return await A({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 vT(e,t,n,{teamId:r=null}={}){if(!t)throw new Error("proposalId required");let o=await j({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 A({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 wo();if(!a)throw new Error("no ticketing integration configured");let i=s.payload,c=i.team??null;if(!c&&r){let m=(await Ds(a)).find(w=>w.id===r);if(!m)throw new Error("unknown team");c={id:m.id,key:m.key,name:m.name}}if(!c)throw new Error("a team is required to create \u2014 pick one");let l=[],u=[],d=null;if(i.epic){let m=await xh(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 w=await Sh(a,{teamId:c.id,title:m.title,description:m.description,projectId:d,priority:m.priority});l.push({kind:"issue",key:w.key,title:w.title,url:w.url})}catch(w){u.push({title:m.title,message:w?.message||String(w)})}await A({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 A({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 dn(e.id)&&(p=await Bs(e,f[0].key,{source:"created"}).catch(()=>null)),{proposalId:t,action:"created",created:l,failed:u,linked:p?p.key:null}}async function $i(e,t){let n=await bo(e);n&&await n.api.createComment(n.cfg,e,t)}async function ej(e,t,n){let r=await bo(e);if(!r)return null;let o=await $n(t);if(!o)return null;let s=jt(o,n);if(!s)return null;let a=await Oi.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 sT=()=>Number(process.env.BOSTRAT_TICKET_COST_SYNC_MS??12e4),Eh=new Map;function tj(e,t){if(sT()<=0)return Ci(e,t);if(Eh.has(e))return;let n=setTimeout(()=>{Eh.delete(e),dn(e).then(r=>r?Ci(e,r):null).catch(()=>{})},sT());n.unref?.(),Eh.set(e,n)}var aT=new Map;async function iT(e,t,n){let r=aT.get(e)??0;Date.now()-r<600*1e3||(aT.set(e,Date.now()),await A({id:e,cwd:t},{type:"ticket.sync.failed",actor:"system",source:"orchestrator",payload:{ts:Date.now(),message:n?.message||String(n)}}).catch(()=>{}))}async function nj(e,{resolveTrack:t=null}={}){let n=await dn(e.trackId),r=e.payload||{};if(!n){if(e.type!=="turn.user"||e.actor!=="user")return;let o=null;for(let a of await $h())if(o=a.api.findIssueUrl(r.text,a.cfg),o)break;if(!o)return;let s=t?await t(e.trackId):null;if(!s)return;try{await Bs(s,o,{source:"chat"})}catch(a){await iT(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 Tr(e.trackId))}try{switch(e.type){case"handoff.done":n.track.leadAgent=r.toAgent??n.track.leadAgent,await Tr(e.trackId),await Ci(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 Tr(e.trackId),await Ci(e.trackId,n),r.change==="opened"||r.change==="merged"){let o=r.change==="merged"?await hT(n,e.trackId).catch(()=>null):null;await $i(n,`**Bostrat** \u2014 PR ${r.change} for **${n.track.name}**${r.url?`: ${r.url}`:r.number?` (#${r.number})`:""}`+(o?`
|
|
568
583
|
|
|
569
|
-
Agent cost for this Track: ${
|
|
584
|
+
Agent cost for this Track: ${z1(o)}.`:"")),await gT(e.trackId,n,r.change)}break}case"mock.state.changed":{if(r.change==="ready"&&r.mockId){let o=await ej(n,r.mockId,r.version??null);if(!o)break;let s=(r.version??1)>1?` revised (v${r.version})`:"";await $i(n,`**Bostrat** \u2014 mock **${r.name}**${s}${r.group?` (group \`${r.group}\`)`:""}:
|
|
570
585
|
|
|
571
|
-
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await
|
|
572
|
-
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await
|
|
573
|
-
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${
|
|
586
|
+
`)}else if(r.change==="picked"){let o=(r.siblings??[]).map(s=>`**${s.name}**`).join(", ");await $i(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 $i(n,`**Bostrat** \u2014 live preview **${r.name}**: ${gu(r.slug)}
|
|
587
|
+
_(runs on the Track's branch; reachable on the operator's private network only)_`);break}case"cost.recorded":await tj(e.trackId,n);break;default:break}}catch(o){let s=t?await t(e.trackId).catch(()=>null):null;await iT(e.trackId,s?.cwd??null,o)}}function TT({resolveTrack:e}){at.on("event",t=>{nj(t,{resolveTrack:e}).catch(()=>{})})}async function AT(){if(!Ae())return{pushed:0,source:"unconfigured"};let{records:e,source:t}=await We("tickets");if(t!=="plane"||e.length)return{pushed:0,source:t};let n=await Mi(),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"}}is();xs();import rj from"ws";var ET=()=>process.env.BOSTRAT_SLACK_API_BASE||"https://slack.com/api";async function yu(e,t,n={}){let r=await fetch(`${ET()}/${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 Nh=(e,t)=>yu("chat.postMessage",e,t),ko=(e,t)=>yu("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 oj(e,t,n={}){let r=await fetch(`${ET()}/${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 _T(e,{filename:t,buffer:n,title:r,channelId:o,threadTs:s,initialComment:a}){let{upload_url:i,file_id:c}=await oj("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 yu("files.completeUploadExternal",e,{files:[{id:c,title:r}],channel_id:o,thread_ts:s,...a?{initial_comment:a}:{}})}var sj=2200;function IT({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 yu("apps.connections.open",u))}catch(u){return n(`connections.open failed: ${u?.message||u}`),c()}o=new rj(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=w=>{try{o.send(JSON.stringify(w?{envelope_id:d.envelope_id,payload:w}:{envelope_id:d.envelope_id}))}catch{}},p=!1,m=setTimeout(()=>{p=!0,f()},sj);Promise.resolve().then(()=>t(d)).then(w=>{clearTimeout(m),!p&&(p=!0,f(d.accepts_response_payload?w:void 0))},w=>{clearTimeout(m),p||(p=!0,f()),n(`envelope handler failed: ${w?.message||w}`)})}),o.on("close",c),o.on("error",u=>{n(`socket error: ${u?.message||u}`);try{o.close()}catch{}})}function c(){if(r||s)return;let l=a;a=Math.min(a*2,6e4),s=setTimeout(()=>{s=null,i()},l)}return i(),()=>{r=!0,s&&clearTimeout(s);try{o?.close()}catch{}}}var OT=()=>process.env.BOSTRAT_STATE_DIR||ku.join(process.env.HOME||ij.tmpdir(),".bostrat"),Lh=()=>ku.join(OT(),"slack.json"),bu=null,Ph=null,Dh=Promise.resolve();async function Uh(){return bu||(Ph??=Ni.readFile(Lh(),"utf8").then(e=>JSON.parse(e)?.threads??{}).catch(()=>({})).then(e=>bu=e).finally(()=>Ph=null),Ph)}async function RT(){let e=JSON.stringify({threads:bu},null,2),t=`${Lh()}.${process.pid}.tmp`;await Ni.mkdir(OT(),{recursive:!0}),await Ni.writeFile(t,e),await Ni.rename(t,Lh())}async function Ft(){Dh=Dh.then(RT,RT),await Dh.catch(()=>{})}async function Hh(e){return(await Uh())[e]??null}async function cj(e,t){let n=await Uh();for(let[r,o]of Object.entries(n))if(o.channel===e&&o.ts===t)return{trackId:r,rec:o};return null}var ue=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");function Pi(e,t){let n=String(e??"").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function lj(e,t=500){let n=String(e??"").trim().split(/\n\s*\n/)[0]??"";return Pi(n.replace(/\n+/g," "),t)}function CT(e){let t=`*${ue(e.name)}*${e.archivedAt?" \xB7 _archived_":""}`,n=`${ue(e.repoLabel)} \xB7 \`${ue(e.branch||"?")}\` \xB7 lead *${ue(e.lead)}*`,r=e.kickoff?`> ${ue(Pi(e.kickoff,300))}`:"_no kickoff yet \u2014 reply in this thread to steer the lead_";return[{type:"section",text:{type:"mrkdwn",text:`${t}
|
|
588
|
+
${n}`}},{type:"section",text:{type:"mrkdwn",text:r}},{type:"context",elements:[{type:"mrkdwn",text:`<${wu(e.trackId)}|open the Track> \xB7 reachable on the operator's private network only`}]}]}async function Fh(e,t){await ko(e.botToken,{channel:t.channel,ts:t.ts,text:`Track: ${t.name}`,blocks:CT(t)})}async function Er(e,t,n,r=void 0){return Nh(e.botToken,{channel:t.channel,thread_ts:t.ts,text:n,...r?{blocks:r}:{},unfurl_links:!1})}var $T=new Set;function Ar(e){$T.add(e),e.catch(()=>{}).finally(()=>$T.delete(e))}var Te=e=>({response_type:"ephemeral",text:e}),uj="*/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._",dj=["claude","cursor","codex"];function Bh(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 jh=(e,t)=>Te(`No live Track matches \u201C${ue(t)}\u201D. Live: ${e.map(n=>`\`${ue(n.name)}\``).join(" \xB7 ")||"none"}`);async function MT(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,{...Te(`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,{...Te("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?ku.basename(c.base):"fresh workspace"),lead:"claude"},u=await Nh(t.botToken,{channel:t.channel,text:`Track started: ${l.name}`,blocks:CT(l),unfurl_links:!1});l.ts=u.ts,(await Uh())[i.id]=l,await Ft(),await rt(o,{...Te(`\u2713 *${ue(l.name)}* started \u2014 root message posted.`),replace_original:!0})}catch(a){await rt(o,{...Te(`Start failed: ${a?.message||a}`),replace_original:!0})}}async function fj(e,t,{track:n,responseUrl:r}){let o=e.deps;try{await o.archiveTrack(n.id);let s=await Hh(n.id);s&&(s.archivedAt=Date.now(),await Ft(),await Fh(t,s).catch(()=>{})),await rt(r,{...Te(`\u2713 *${ue(n.name)}* archived.`),replace_original:!0})}catch(s){await rt(r,{...Te(`Archive failed: ${s?.message||s}`),replace_original:!0})}}async function pj(e,t,{track:n,agent:r,responseUrl:o}){try{await e.deps.startLeadHandoff(n.id,r),await rt(o,{...Te(`\u2713 Handing *${ue(n.name)}* to ${ue(r)} \u2014 the root message updates when the baton lands.`),replace_original:!0})}catch(s){await rt(o,{...Te(`Handoff failed: ${s?.message||s}`),replace_original:!0})}}async function mj(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,{...Te(`\u2713 Stopped *${ue(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,{...Te(`Stop failed: ${s?.message||s}`),replace_original:!0})}}function hj(e,t){let n=[...t.slice(0,99).map(r=>({text:{type:"plain_text",text:Pi(`${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 *${ue(e)}*:`},accessory:{type:"static_select",action_id:"bostrat-start-repo",placeholder:{type:"plain_text",text:"Choose a repo"},options:n}}]}async function wj(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?ku.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 *${ue(s.name)}* \u2014 ${ue(i)}${a.branch?` \`${ue(a.branch)}\``:""} \xB7 lead ${ue(c)} \xB7 ${l}`}),o=t.length-n.length;return r.join(`
|
|
574
589
|
`)+(o?`
|
|
575
|
-
_${o} archived_`:"")}async function
|
|
576
|
-
\u2713 *${
|
|
577
|
-
\u2713 _answered_`}}]})}async function
|
|
578
|
-
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return
|
|
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
|
|
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!==
|
|
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
|
|
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
|
|
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
|
|
585
|
-
`,{mode:384}),
|
|
590
|
+
_${o} archived_`:"")}async function gj(e,t){let n=await go();if(!n)return Te("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 Te("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?(Ar(MT(e,n,{name:u,repo:l.path,responseUrl:t.response_url})),Te(`Starting *${ue(u)}* on ${ue(l.name)}\u2026`)):{response_type:"ephemeral",text:`Pick a repo for \u201C${u}\u201D`,blocks:hj(u,i)}}case"archive":{if(!a.length)return Te("Usage: `/bostrat archive <track>`");let{match:i,live:c}=Bh(await r.listTracks(),a.join(" "));return i?(Ar(fj(e,n,{track:i,responseUrl:t.response_url})),Te(`Archiving *${ue(i.name)}*\u2026`)):jh(c,a.join(" "))}case"handoff":{let i=r.knownAgents??dj,c=a.at(-1)?.toLowerCase();if(a.length<2||!i.includes(c))return Te(`Usage: \`/bostrat handoff <track> <${i.join("|")}>\``);let l=a.slice(0,-1).join(" "),{match:u,live:d}=Bh(await r.listTracks(),l);return u?(Ar(pj(e,n,{track:u,agent:c,responseUrl:t.response_url})),Te(`Handing *${ue(u.name)}* to ${ue(c)}\u2026`)):jh(d,l)}case"stop":{if(!a.length)return Te("Usage: `/bostrat stop <track>`");let{match:i,live:c}=Bh(await r.listTracks(),a.join(" "));return i?(Ar(mj(e,n,{track:i,responseUrl:t.response_url})),Te(`Stopping *${ue(i.name)}*\u2026`)):jh(c,a.join(" "))}case"list":return Te(await wj(r));default:return Te(uj)}}async function yj(e,t,n,r){let{trackId:o,promptId:s,label:a}=r,i=await Hh(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 ko(t.botToken,{channel:i.channel,ts:c,text:`\u2713 ${a}`,blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ue(l||"question")}~
|
|
591
|
+
\u2713 *${ue(a)}*`}}]}).catch(()=>{})}}catch(c){await rt(n.response_url,Te(`Couldn't answer: ${c?.message||c}`))}}async function bj(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)?ss.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,as(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,Te(`Couldn't act on the offer: ${c?.message||c}`))}}async function kj(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,Te(`Couldn't act on the offer: ${i?.message||i}`))}}async function xj(e,t){let n=t.actions?.[0];if(!n?.action_id)return;let r=await go();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,{...Te(`Starting *${ue(s)}*\u2026`),replace_original:!0}),Ar(MT(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"?Ar(yj(e,r,t,o)):n.action_id==="bostrat-ship"?Ar(bj(e,r,t,o)):n.action_id==="bostrat-pr-exists"&&Ar(kj(e,r,t,o))}async function Sj(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 cj(t.channel,t.thread_ts);if(!n)return;let r=await go();if(r)try{await e.deps.sayToLead(n.trackId,t.text.trim())}catch(o){await Er(r,n.rec,`Couldn't deliver that to the lead: ${o?.message||o}`).catch(()=>{})}}function vj(e){return async t=>{if(t.type==="slash_commands")return gj(e,t.payload);if(t.type==="interactive"){await xj(e,t.payload);return}if(t.type==="events_api"){await Sj(e,t.payload?.event);return}}}var Tj=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 Aj(e,t){let n=[{type:"section",text:{type:"mrkdwn",text:`\u2753 ${ue(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:Pi(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 Ej(e,t,n){let r=n.payload||{};if(!r.promptId||t.prompt?.promptId===r.promptId)return;t.prompt&&await ko(e.botToken,{channel:t.channel,ts:t.prompt.ts,text:"superseded",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ue(t.prompt.question||"question")}~ _superseded_`}}]}).catch(()=>{});let o=await Er(e,t,r.question||"The lead needs an answer",Aj(n.trackId,r));t.prompt={promptId:r.promptId,ts:o.ts,question:r.question||null},await Ft()}async function _j(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 ko(e.botToken,{channel:t.channel,ts:o,text:"answered",blocks:[{type:"section",text:{type:"mrkdwn",text:`~${ue(s||"question")}~
|
|
592
|
+
\u2713 _answered_`}}]})}async function Ij(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 Er(e,t,r.excerpt||"Ship it?",[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${ue(Pi(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 Rj(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 ko(e.botToken,{channel:t.channel,ts:o,text:s,blocks:[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${s}`}}]})}async function $j(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 Er(e,t,`PR ${o} already open \u2014 push or new PR?`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F6A2} ${o}${r.branch?` (${ue(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 Oj(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 ko(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,r){let o=n.payload||{};if(o.change==="ready"&&o.mockId){let s=await(r.readMockPng??Mj)(o.mockId,o.version??null);if(!s)return;let a=(o.version??1)>1?` (v${o.version})`:"";await _T(e.botToken,{filename:s.filename,buffer:s.buffer,title:`${o.name}${a}`,channelId:t.channel,threadTs:t.ts,initialComment:`\u{1F3A8} mock *${ue(o.name)}*${a}${o.group?` (group \`${ue(o.group)}\`)`:""}`})}else if(o.change==="picked"){let s=(o.siblings??[]).map(a=>`*${ue(a.name)}*`).join(", ");await Er(e,t,`Design decision: ${o.name} picked`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F3A8} design decision: *${ue(o.name)}* picked${s?` over ${s}`:""}`}}])}}async function Mj(e,t){let n=await $n(e);if(!n)return null;let r=jt(n,t);if(!r)return null;let o=await Ni.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 Nj(e,t){if(!Tj.has(t.type))return;let n=await Hh(t.trackId);if(!n)return;let r=await go();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 Fh(r,n);return;case"turn.agent":{if(!o.text)return;let s=lj(o.text);if(!s)return;await Er(r,n,s,[{type:"section",text:{type:"mrkdwn",text:ue(s)}},{type:"context",elements:[{type:"mrkdwn",text:`<${wu(t.trackId)}|full turn \u2192>`}]}]);return}case"prompt.raised":return Ej(r,n,t);case"prompt.answered":return _j(r,n,t);case"pr.offered":return Ij(r,n,t);case"pr.offer.answered":return Rj(r,n,t);case"pr.exists.offered":return $j(r,n,t);case"pr.exists.answered":return Oj(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 Er(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 Fh(r,n);return;case"view.state.changed":{if(o.change!=="created"||o.restarted||!o.slug)return;await Er(r,n,`Live preview ${o.name}: ${gu(o.slug)}`,[{type:"section",text:{type:"mrkdwn",text:`\u{1F50D} live preview *${ue(o.name)}*: ${gu(o.slug)}
|
|
593
|
+
_runs on the Track's branch; reachable on the operator's private network only_`}}]);return}case"mock.state.changed":return Cj(r,n,t,e.deps)}}function NT(e){let t={deps:e};at.on("event",s=>{Nj(t,s).catch(()=>{})});let n=null,r=null,o=async()=>{n||!await go()||(r&&clearInterval(r),r=null,n=IT({getToken:async()=>(await go())?.appToken,onEnvelope:vj(t),log:s=>console.log(`[slack] ${s}`)}))};return r=setInterval(()=>o().catch(()=>{}),3e4),r.unref?.(),o().catch(()=>{}),()=>{r&&clearInterval(r),n?.()}}Pt();ce();Vt();wp();wn();import Pj from"node:fs/promises";function Dj(e){let t=new Map;for(let n of String(e||"").split(`
|
|
594
|
+
`)){let r=n.trim();if(!r||r[0]!=="{")continue;let o;try{o=JSON.parse(r)}catch{continue}let s=o?.message?.usage;if(!s)continue;let a={input:s.input_tokens||0,output:s.output_tokens||0,cacheWrite:s.cache_creation_input_tokens||0,cacheRead:s.cache_read_input_tokens||0};if(a.input+a.output+a.cacheWrite+a.cacheRead===0)continue;let i=o.message?.id||o.uuid||null;if(!i)continue;let c=s.cache_creation,l=c&&typeof c=="object"?{m5:c.ephemeral_5m_input_tokens||0,h1:c.ephemeral_1h_input_tokens||0}:null,u=typeof s.speed=="string"&&s.speed!=="standard"?s.speed:null;t.set(i,{messageId:i,tokens:a,cacheWriteTtl:l,speed:u,model:o.message?.model||o.model||null})}return[...t.values()]}function Bj(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 jj=new Set(["Write","Edit","NotebookEdit"]),Lj=220,Fj=160;function Uj(e,t){let n=e?.tool_name??null,r={tool:n},o=e?.tool_input;if(!o||typeof o!="object")return r;if(jj.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,Lj)}else n==="Bash"&&typeof o.command=="string"&&o.command.trim()&&(r.head=Dr(o.command.trim().replace(/\s+/g," ")).slice(0,Fj));return r}function Hj(e,t){return e?.transcript_path?e.transcript_path:t?.cwd&&t?.sessionId?Nt(t.cwd,t.sessionId):t?.cwd?xd(t.cwd):null}async function PT(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 A(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=vl(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:Uj(e,o)});break;case"Stop":await Wj(r,o,e,i);break;default:break}return a}async function Wj(e,t,n,r){let o=[],s=Hj(n,t);if(s)try{o=Dj(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=Bj(await j(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"}\0${d.speed||""}`,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),p.speed=d.speed||null,u.set(f,p)}for(let[d,f]of u){let p=d.split("\0")[0];await r({type:"cost.recorded",actor:"system",source:"transcript",payload:{model:p,usd:Yt(p,f.tokens,void 0,f.speed?{speed:f.speed}:void 0),tokens:f.tokens,...f.speed?{speed:f.speed}:{},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}})}Js();import qj from"node:fs";import DT from"node:fs/promises";import Di from"node:os";import Kj from"node:path";Wp();ze();var Gj=1024*1024,xu=e=>Math.round(e/Gj);async function Vj(e){try{let t=await DT.statfs(e),n=t.blocks*t.bsize,r=n-t.bavail*t.bsize;return{diskUsedMb:xu(r),diskTotalMb:xu(n)}}catch{return{diskUsedMb:null,diskTotalMb:null}}}async function zj(){try{return(await Ct()).filter(t=>t.isBostrat).length}catch{return null}}async function Jj(){let e=0,t=!1;for(let n of["workspaces","worktrees"])try{let r=await DT.readdir(Kj.join(Ai,n),{withFileTypes:!0});e+=r.filter(o=>o.isDirectory()).length,t=!0}catch{}return t?e:null}async function BT({stateDir:e=Ai,now:t=Date.now}={}){let n=Di.totalmem(),[r,o,s,a]=await Promise.all([Nl().catch(()=>null),Vj(qj.existsSync(e)?e:Di.tmpdir()),zj(),Jj()]),i=Di.cpus()?.length||null,c=Di.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?xu(n-r):null,memTotalMb:xu(n),...o,boxes:s,worktrees:a,procUptimeS:Math.round(process.uptime()),osUptimeS:Math.round(Di.uptime())}}ce();Vd();ra();ce();qr();wn();function Wh(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 jT=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),LT=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},Su=e=>Math.round(e*1e4)/1e4;async function qh(e){let t=[],n={},r={},o=0,s=0,a=0,i=0,c=0,l=new Set,u=jT(),d=0,f=[];for(let p of e||[]){let m=await j(p),w=ar(m,{boxLabels:Wh(p)});if(w.turns===0){p.archivedAt?d++:f.push(p.name||p.id);continue}o+=w.totalUsd,s+=w.turns,a+=w.repricedTurns||0,i+=w.asRecordedUsd||0,c+=w.unpricedTurns||0;for(let y of w.unpricedModels||[])l.add(y);LT(u,w.totalTokens);for(let[y,x]of Object.entries(w.byModel))n[y]||={usd:0,events:0,tokens:jT()},n[y].usd=Su(n[y].usd+x.usd),n[y].events+=x.events,LT(n[y].tokens,x.tokens);for(let[y,x]of Object.entries(w.byProvider))r[y]||={usd:0,events:0},r[y].usd=Su(r[y].usd+x.usd),r[y].events+=x.events;t.push({id:p.id,name:p.name,usd:w.totalUsd,rollup:w})}return t.sort((p,m)=>m.usd-p.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:Vo(),fleet:{totalUsd:Su(o),asRecordedUsd:Su(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 _r=e=>`$${e.toFixed(4)}`,js=(e,t)=>String(e).padEnd(t);function FT(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 ${_r(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 ${_r(t.asRecordedUsd)} \xB7 current-rates total ${_r(t.totalUsd)} (${t.totalUsd>=t.asRecordedUsd?"+":""}${_r(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(` ${js(a,26)} ${js(_r(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(` ${js(a.name||a.id,30)} ${_r(a.usd)}`);let i=Object.values(a.rollup.byBox).sort((c,l)=>l.usd-c.usd);for(let c of i)if(r.push(` ${js(c.label,40)} ${js(_r(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))}${js(_r(u.usd),10)} ${u.models.join(",")||"?"}`)}return r.join(`
|
|
595
|
+
`)}wn();ce();wn();var Yj=864e5;function Xj(e,{tolerance:t=.005}={}){if(e.type!=="cost.recorded"||e.source==="vendor")return null;let n=e.payload?.tokens,r=Number(e.payload?.usd)||0;if(!(n?(n.input||0)+(n.output||0)+(n.cacheRead||0)+(n.cacheWrite??n.cacheCreation??0):0)||!r)return null;let s=e.payload?.model||"unknown",a=e.payload?.speed||void 0,i={input:n.input||0,output:n.output||0,cacheRead:n.cacheRead||0,cacheWrite:n.cacheWrite??n.cacheCreation??0},c=Yt(s,i,e.ts,a?{speed:a}:void 0);if(!c)return null;let l=r-c,u=Math.abs(l)>Math.max(c*t,.001);return{model:s,speed:a??null,ts:e.ts,asRecorded:r,expected:c,delta:l,divergent:u}}async function UT(e,{days:t=14,tolerance:n=.005,now:r=Date.now()}={}){let o=r-t*Yj,s={},a=0,i=0,c=[];for(let u of e||[]){let d=await j(u);for(let f of d){if(!f.ts||f.ts<o)continue;let p=Xj(f,{tolerance:n});if(!p)continue;a++;let m=p.speed?`${p.model} (${p.speed})`:p.model,w=s[m]||={rows:0,divergent:0,asRecordedUsd:0,expectedUsd:0};w.rows++,w.asRecordedUsd+=p.asRecorded,w.expectedUsd+=p.expected,p.divergent&&(i++,w.divergent++,c.length<10&&c.push({track:u.name||u.id,...p}))}}let l=u=>Math.round(u*1e4)/1e4;for(let u of Object.values(s))u.asRecordedUsd=l(u.asRecordedUsd),u.expectedUsd=l(u.expectedUsd);return{sinceMs:o,days:t,tolerance:n,rows:a,divergent:i,byModel:s,examples:c,ok:i===0}}Ep();rl();ce();Yf();import nL from"node:fs/promises";import rL from"node:path";Gd();qr();var vu=e=>e.payload?.ts??e.ts,Zj=12e4,Ir=e=>Math.round(e*1e4)/1e4,Qj=()=>({usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{},byTrack:new Map});function eL(e,{since:t}){let{track:n,events:r,brief:o}=e,s=k=>vu(k)>=t,a=Wr([_a(n?.name),_a(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 T=Wr(k.payload.task);T&&i.set(k.boxId,T)}let c=new Map,l=k=>{let T=k||"unattributed";return c.has(T)||c.set(T,{segments:[u(null)],lastTs:null,provider:null}),c.get(T)},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,T)=>vu(k)-vu(T)),f=0,p=0,m=0,w={turns:0};for(let k of d){let T=l(k.boxId),_=T.segments[T.segments.length-1],P=vu(k);switch(T.lastTs!=null&&P>T.lastTs&&s(k)&&(_.activeMs+=Math.min(P-T.lastTs,Zj)),T.lastTs=P,k.provider&&(T.provider=k.provider),k.type){case"turn.user":{let N=k.payload?.text;if(k.source==="hook"||!String(N||"").trim())break;let O=u(Wr(N));T.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(Wr(N));T.segments.push(O),s(k)&&(O.turns+=1);break}case"tool.called":{if(k.source==="transcript")break;let N=k.payload?.tool;N&&(_.tools[N]=(_.tools[N]||0)+1),s(k)&&(_.toolCalls+=1);break}case"cost.recorded":{if(!s(k))break;let{usd:N,tokens:O,model:$,unpriced:R}=gn(k);R&&(w.turns+=1),_.usd+=N,O&&(_.tokens.input+=O.input,_.tokens.output+=O.output,_.tokens.cacheRead+=O.cacheRead,_.tokens.cacheWrite+=O.cacheWrite),_.models[$]=(_.models[$]||0)+N,k.provider&&(_.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 y=new Map,x=!1;for(let[k,T]of c)for(let _ of T.segments){if(!(_.usd>0||_.activeMs>0||_.turns>0||_.toolCalls>0))continue;let N=_.textCat?.category??i.get(k)?.category??Sy(_.tools)?.category??a?.category??"uncategorized";y.has(N)||y.set(N,{usd:0,activeMs:0,turns:0,toolCalls:0,models:{},providers:{}});let O=y.get(N);O.usd+=_.usd,O.activeMs+=_.activeMs,O.turns+=_.turns,O.toolCalls+=_.toolCalls;for(let[R,ee]of Object.entries(_.models))O.models[R]=(O.models[R]||0)+ee;let $=_.provider||T.provider;$&&(O.providers[$]=(O.providers[$]||0)+_.usd),$==="cursor"&&_.usd===0&&(x=!0)}return{perCategory:y,handoffs:f,merged:p,failed:m,sawCursorActivity:x,unpricedTurns:w.turns}}function WT(e,{windowMs:t=null,now:n=Date.now()}={}){let r=t==null?0:n-t,o=new Map,s=[],a=0,i=!1,c=0;for(let w of e||[]){let{track:y,events:x}=w;if(!x?.length){a+=1;continue}let{perCategory:k,handoffs:T,merged:_,failed:P,sawCursorActivity:N,unpricedTurns:O}=eL(w,{since:r});N&&(i=!0),c+=O;let $={trackId:y?.id,name:y?.name||y?.id,archived:!!y?.archivedAt,usd:0,activeMs:0,turns:0,categories:{},models:{},providers:{},handoffs:T,subtasksMerged:_,subtasksFailed:P};for(let[R,ee]of k){$.usd+=ee.usd,$.activeMs+=ee.activeMs,$.turns+=ee.turns,$.categories[R]={usd:Ir(ee.usd),activeMs:ee.activeMs};for(let[G,I]of Object.entries(ee.models))$.models[G]=Ir(($.models[G]||0)+I);for(let[G,I]of Object.entries(ee.providers))$.providers[G]=Ir(($.providers[G]||0)+I);o.has(R)||o.set(R,Qj());let de=o.get(R);de.usd+=ee.usd,de.activeMs+=ee.activeMs,de.turns+=ee.turns,de.toolCalls+=ee.toolCalls;for(let[G,I]of Object.entries(ee.models))de.models[G]=(de.models[G]||0)+I;for(let[G,I]of Object.entries(ee.providers))de.providers[G]=(de.providers[G]||0)+I;let be=de.byTrack.get(y?.id)||{usd:0,activeMs:0};be.usd+=ee.usd,be.activeMs+=ee.activeMs,de.byTrack.set(y?.id,be)}($.usd>0||$.activeMs>0||$.turns>0)&&($.usd=Ir($.usd),$.dominantCategory=tL($.categories),s.push($))}let l=[...o.values()].reduce((w,y)=>w+y.usd,0),u=[...o.values()].reduce((w,y)=>w+y.activeMs,0),d=l>0?"usd":"time",f=d==="usd"?l:u,p=new Map((e||[]).map(w=>[w.track?.id,w.track?.name||w.track?.id])),m=[...o.entries()].map(([w,y])=>({category:w,sharePct:f>0?Math.round((d==="usd"?y.usd:y.activeMs)/f*100):0,shareBasis:d,usd:Ir(y.usd),activeMs:y.activeMs,turns:y.turns,toolCalls:y.toolCalls,models:HT(y.models),providers:HT(y.providers),topWorkstreams:[...y.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:Ir(k.usd),activeMs:k.activeMs}))})).sort((w,y)=>y.usd-w.usd||y.activeMs-w.activeMs);return s.sort((w,y)=>y.usd-w.usd||y.activeMs-w.activeMs),{since:r,capturedAt:n,totalUsd:Ir(l),activeMs:u,turns:[...o.values()].reduce((w,y)=>w+y.turns,0),bullets:m,workstreams:s,gaps:{tracksWithoutEvents:a,cursorCostUnknown:i,unpricedTurns:c}}}var tL=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},HT=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Ir(n)]));var Ls={"24h":24*36e5,"7d":168*36e5,"30d":720*36e5,all:null};function oL(e){let t=String(e||"7d");return t in Ls?{key:t,ms:Ls[t]}:{key:"7d",ms:Ls["7d"]}}function sL(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=al(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 aL(e){if(!e?.cwd)return null;try{let n=(await nL.readFile(rL.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 qT(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}:oL(t),d=[];for(let f of e||[]){let p=await j(f),m=sL(f,p);i&&c<o&&(m=m.filter(w=>(w.ts??0)<=c)),d.push({track:f,events:m,brief:await aL(f)})}return{window:l,...WT(d,{windowMs:u,now:c})}}ce();qr();wn();var Rr=36e5,zT=24*Rr,iL=400,cL=12,xo=e=>Math.round(e*1e4)/1e4;function lL({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 Ls?String(e||"7d"):"7d",i=Ls[a];return{key:a,since:i==null?null:r-i,until:r}}var uL=(e,t)=>e!=null&&t-e<=48*Rr?Rr:zT;function KT(e,t){if(t===Rr)return Math.floor(e/Rr)*Rr;let n=new Date(e);return n.setHours(0,0,0,0),n.getTime()}function dL(e,t){if(t===Rr)return e+Rr;let n=new Date(e);return n.setDate(n.getDate()+1),n.setHours(0,0,0,0),n.getTime()}var GT=()=>({input:0,output:0,cacheRead:0,cacheCreation:0}),VT=(e,t)=>{for(let n of Object.keys(e))e[n]+=t?.[n]||0},fL=e=>Object.fromEntries(Object.entries(e).map(([t,n])=>[t,xo(n)]));async function JT(e,t={}){let n=t.now??Date.now(),r=lL({...t,now:n}),o=r.since==null?zT:uL(r.since,r.until),s=R=>(r.since==null||R>=r.since)&&R<r.until,a=r.since!=null?r.until-r.since:null,i=R=>a!=null&&R>=r.since-a&&R<r.since,c={},l={},u=GT(),d=0,f=0,p=0,m=0,w=0,y=0,x=new Set,k=new Map,T=[],_=[],P=[],N=null;for(let R of e||[]){let de=(await j(R)).filter(I=>I.type==="cost.recorded");if(!de.length){R.archivedAt||P.push(R.name||R.id);continue}let be=[];for(let I of de){let J=I.ts??0;s(J)?be.push(I):i(J)&&(y+=gn(I).usd)}if(!be.length)continue;let G=ar(be,{boxLabels:Wh(R)});d+=G.totalUsd,f+=G.turns,p+=G.repricedTurns||0,m+=G.asRecordedUsd||0,w+=G.unpricedTurns||0;for(let I of G.unpricedModels||[])x.add(I);VT(u,G.totalTokens);for(let[I,J]of Object.entries(G.byModel))c[I]||={usd:0,events:0,tokens:GT()},c[I].usd=xo(c[I].usd+J.usd),c[I].events+=J.events,VT(c[I].tokens,J.tokens);for(let[I,J]of Object.entries(G.byProvider))l[I]||={usd:0,events:0},l[I].usd=xo(l[I].usd+J.usd),l[I].events+=J.events;for(let I of be){let{usd:J}=gn(I);if(!J)continue;let X=KT(I.ts??0,o),he=k.get(X)??{t:X,total:0,byProvider:{}};he.total+=J;let Se=I.provider||"unknown";he.byProvider[Se]=(he.byProvider[Se]||0)+J,k.set(X,he),(N==null||X<N)&&(N=X)}T.push({id:R.id,name:R.name||R.id,archived:!!R.archivedAt,usd:G.totalUsd,turns:G.turns,providers:Object.keys(G.byProvider)});for(let[I,J]of Object.entries(G.byBox))_.push({trackId:R.id,trackName:R.name||R.id,boxId:I,...J})}let O=r.since!=null?KT(r.since,o):N,$=[];for(let R=O,ee=0;R!=null&&R<r.until&&ee<iL;R=dL(R,o),ee++){let de=k.get(R);$.push(de?{t:R,total:xo(de.total),byProvider:fL(de.byProvider)}:{t:R,total:0,byProvider:{}})}return T.sort((R,ee)=>ee.usd-R.usd),_.sort((R,ee)=>ee.usd-R.usd),{generatedAt:new Date().toISOString(),pricingBasis:"api-equivalent (subscription usage priced at public API rates)",pricing:Vo(),window:r.key,since:r.since,until:r.until,bucketMs:o,series:$,fleet:{totalUsd:xo(d),asRecordedUsd:xo(m),turns:f,repricedTurns:p,unpricedTurns:w,unpricedModels:[...x],totalTokens:u,byModel:c,byProvider:l,tracksWithCost:T.length,tracksSilentLive:P},prior:a!=null?{totalUsd:xo(y),since:r.since-a,until:r.since}:null,tracks:T.slice(0,50),boxes:_.slice(0,cL)}}et();yn();import Fs from"node:fs/promises";import pL from"node:fs";import Ye from"node:path";import mL from"node:os";import{execFile as hL}from"node:child_process";import{promisify as wL}from"node:util";var gL=wL(hL),YT=process.env.BOSTRAT_STATE_DIR||Ye.join(process.env.HOME||mL.tmpdir(),".bostrat"),Tu=!1,Kh=null;async function Gh(e,t){let n=await Fs.stat(e).catch(()=>null);if(!n?.isFile())return!1;let r=await Fs.stat(t).catch(()=>null);return r&&r.size===n.size&&r.mtimeMs>=n.mtimeMs?!1:(await Fs.mkdir(Ye.dirname(t),{recursive:!0}),await Fs.copyFile(e,t),!0)}var yL=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 Au(e,t,{skip:n=null}={}){let r=0,o=await Fs.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(let s of o){if(yL.has(s.name)||n?.has(s.name))continue;let a=Ye.join(e,s.name);s.isDirectory()?r+=await Au(a,Ye.join(t,s.name)):s.isFile()&&(r+=await Gh(a,Ye.join(t,s.name))?1:0)}return r}async function XT({stateDir:e=YT,dest:t=process.env.BOSTRAT_SYNC_DIR||`${YT}-sync`,remote:n=process.env.BOSTRAT_STATE_REMOTE||null,tracks:r=null}={}){try{await Fs.mkdir(t,{recursive:!0});let o=0;o+=await Gh(Ye.join(e,"tracks.json"),Ye.join(t,"tracks.json"))?1:0,o+=await Gh(Ye.join(e,"events.jsonl"),Ye.join(t,"events.jsonl"))?1:0,o+=await Au(Ye.join(e,"events"),Ye.join(t,"events")),o+=await Au(Ye.join(e,"memory"),Ye.join(t,"memory"));let s=new Set(["mocks"]);for(let u of r||[])!u?.cwd||!u?.id||(o+=await Au(Ye.join(u.cwd,".bostrat"),Ye.join(t,"workspaces",u.id,".bostrat"),{skip:s}));let a=(...u)=>gL("git",["-C",t,...u]);pL.existsSync(Ye.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 Xu(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||Tu))try{await a("push","-q",n,"HEAD:refs/heads/main"),l=!0,Tu&&console.log("[state-sync] push recovered \u2014 off-box copy is current again"),Tu=!1,Kh=null}catch(u){Tu=!0;let d=(u?.stderr||u?.message||String(u)).trim().split(`
|
|
596
|
+
`).slice(-3).join(" | ").slice(0,300);d!==Kh&&(Kh=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)}}}cm();Pt();Ur();ze();Ys();import{execFile as bL}from"node:child_process";import{promisify as kL}from"node:util";import{randomUUID as xL}from"node:crypto";import SL from"node:fs";import vL from"node:os";import TL from"node:path";var Or=kL(bL),Eu=e=>new Promise(t=>setTimeout(t,e)),Cr=hd,Vh=process.env.BOSTRAT_USAGE_PROBE_CWD||TL.join(vL.homedir(),".bostrat","usage-probe"),AL=Number(process.env.BOSTRAT_USAGE_IDLE_REAP_MS)||5*6e4,EL=Number(process.env.BOSTRAT_USAGE_PANEL_TIMEOUT_MS)||15e3,_L=Number(process.env.BOSTRAT_USAGE_READY_TIMEOUT_MS)||45e3,ZT=Number(process.env.BOSTRAT_USAGE_CACHE_MS)||2e4,$r=null,Bi=null,ji=null;async function IL(){try{return await Or(L,U("has-session","-t",Cr)),!0}catch{return!1}}function RL(){$r&&clearTimeout($r),$r=setTimeout(()=>$L().catch(()=>{}),AL),$r.unref&&$r.unref()}async function $L(){$r&&(clearTimeout($r),$r=null);try{await Or(L,U("kill-session","-t",Cr))}catch{}}async function OL(){let e=Date.now()+_L;for(;;){let t=await zh().catch(()=>"");if(/for agents|for shortcuts|bypass permissions on/i.test(t)){await Eu(500);return}if(Date.now()>e)throw new Error("usage probe didn't reach the prompt (trust/onboarding may be blocking)");await Eu(500)}}async function CL(){if(await IL())return;if(!await Xn())throw new Error("host tmux server not running \u2014 start globo-tmux.service");SL.mkdirSync(Vh,{recursive:!0}),await Wn(Vh);let e=xL();await Or(L,U("new-session","-d","-s",Cr,"-c",Vh,"-x","200","-y","60",...da()));let t=`${md} --session-id ${e}`;await Or(L,U("send-keys","-t",Cr,t,"Enter")),await OL()}async function zh(){let{stdout:e}=await Or(L,U("capture-pane","-t",Cr,"-p"));return e}async function ML(){await CL(),await Or(L,U("send-keys","-t",Cr,"-l","/usage")),await Or(L,U("send-keys","-t",Cr,"Enter"));let e=Date.now()+EL,t="";for(;await Eu(400),t=await zh(),!(/Current\s+(session|week)/i.test(t)||/\d+%\s*used/i.test(t)||Date.now()>e););return await Eu(900),t=await zh(),await Or(L,U("send-keys","-t",Cr,"Escape")).catch(()=>{}),RL(),t}function NL(e){let t=String(e||"").split(`
|
|
597
|
+
`).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 PL(e){let t=String(e||"").split(`
|
|
598
|
+
`).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 QT({fresh:e=!1}={}){return!e&&Bi&&Date.now()-Bi.at<ZT?Bi.data:ji||(ji=(async()=>{try{let t=await ML(),n=NL(t),r={...n,contributors:PL(t),subscription:!!(n.session||n.week||n.weekModels),capturedAt:Date.now()};return Bi={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 Bi={at:Date.now()-(ZT-3e3),data:n},n}finally{ji=null}})(),ji)}var DL=262144;function BL(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 jL=new Set([300]),LL=new Set([10080]);function FL(e){let t=Number(e);return!Number.isFinite(t)||t<=0?null:jL.has(t)?"session":LL.has(t)?"week":"other"}function UL(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 HL(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?BL((o-n)/1e3):null,resetsAt:o}}var Li=e=>({session:null,week:null,other:null,planType:null,credits:null,limitReached:null,sawTokenCount:e});function WL(e,t=Date.now()){let n=String(e||"").split(`
|
|
599
|
+
`),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 Li(r);if(c.primary||c.secondary){let l=Date.parse(a.timestamp)||null,u=Li(r);[c.primary,c.secondary].forEach((p,m)=>{let w=HL(p,l,t);if(!w)return;let y=FL(p.window_minutes);y===null&&(y=m===0?"session":"week"),y==="session"&&!u.session?u.session=w:y==="week"&&!u.week?u.week=w:(u.other??=[]).push({label:UL(p.window_minutes),...w})}),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{...Li(r),session:l(c.primary_used_percent),week:l(c.secondary_used_percent)}}return Li(r)}return Li(r)}async function Jh(e){let t={provider:"codex",supported:!0,contributors:null};try{let n=await Lr(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,DL),o=WL(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 eA(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 _u({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 Jh(i);if(c.subscription){a=c;break}(!a||a.error&&!c.error)&&(a=c)}r.push(a||await Jh(null))}return n.has("cursor")&&r.push(await _u({agent:"cursor"})),{providers:r,capturedAt:Date.now()}}async function _u(e,{fresh:t=!1}={}){let n=e?.agent||"claude";return n==="codex"?Jh(e):n==="cursor"?{provider:"cursor",supported:!1,subscription:!1,session:null,week:null,contributors:null,capturedAt:Date.now()}:{provider:"claude",supported:!0,...await QT({fresh:t})}}Kn();et();et();function qL(e=process.env){return!!(ke(e)&&e.BOSTRAT_PLANE_TOKEN)}var KL=e=>({authorization:`Bearer ${e.BOSTRAT_PLANE_TOKEN||""}`}),Iu=(e,t)=>Object.assign(new Error(e),{status:t});async function tA(e,t){if(!qL(t))throw Iu("node is not plane-linked \u2014 Gmail unavailable",503);let n;try{n=await fetch(`${ke(t)}${e}`,{headers:KL(t)})}catch(o){throw Iu(`plane unreachable \u2014 Gmail unavailable (${o?.message||o})`,503)}let r=await n.json().catch(()=>({}));if(!n.ok)throw Iu(r?.error||`plane replied ${n.status}`,n.status);return r}async function nA(e,{max:t=10,env:n=process.env}={}){let r=new URLSearchParams({q:e||""});return t&&r.set("max",String(t)),(await tA(`/v1/gmail/messages?${r.toString()}`,n)).messages||[]}async function rA(e,{env:t=process.env}={}){if(!e)throw Iu("message id required",400);return(await tA(`/v1/gmail/messages/${encodeURIComponent(e)}`,t)).message}Ua();gl();function Yh(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 GL(e){let[t,n]=e.split("/"),r=Yh(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 oA(e){return(e||"").split(",").map(t=>t.trim()).filter(Boolean).map(GL).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 sA(e){let t=oA(e),n=String(e||"").trim().toLowerCase()==="open";return{open:n,clientIp:VL,allowed(r){if(n)return!0;let o=Yh((r||"").replace(/^::ffff:/,""));return o==null?!1:t.some(s=>s(o))}}}function aA(e,t){let n=oA(e);return{active:n.length>0,allows(r){if(!n.length)return!1;let o=(r.socket&&r.socket.remoteAddress||"").replace(/^::ffff:/,""),s=Yh(o);if(s==null||!n.some(i=>i(s)))return!1;let a=t(r);return!!(a&&a.view)}}}import iA from"node:crypto";import Ru from"node:fs";import zL from"node:path";function cA(e=process.env,{stateDir:t=Ai,log:n=console}={}){let r=String(e.BOSTRAT_NODE_API_TOKEN||"").trim();if(r)return r;let o=zL.join(t,"api-token");try{let a=Ru.readFileSync(o,"utf8").trim();if(a)return a}catch{}let s=iA.randomBytes(32).toString("base64url");try{Ru.mkdirSync(t,{recursive:!0}),Ru.writeFileSync(o,s+`
|
|
600
|
+
`,{mode:384}),Ru.chmodSync(o,384)}catch(a){n.error?.(`[bostrat-agent] could not persist the node API token: ${a?.message||a}`)}return s}function Fi(e){let t=e?.socket?.remoteAddress||"";if(!t)return!1;let n=t.replace(/^::ffff:/,"");return n==="::1"||n==="127.0.0.1"||/^127\./.test(n)}function lA(e,t){if(!e||!t)return!1;let n=Buffer.from(String(e)),r=Buffer.from(String(t));return n.length!==r.length?!1:iA.timingSafeEqual(n,r)}function uA(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 JL=[/^\/api\/github\/token\b/,/^\/api\/gmail\b/],YL=e=>JL.some(t=>t.test(e));function dA({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=uA(o);return lA(c,e)?a():YL(i)?(n.log?.(`[perimeter] refused ${o.method} ${i} \u2014 sensitive route requires the node API token`),s.status(401).json({error:"this route requires the node API token"})):!t||Fi(o)?a():(n.log?.(`[perimeter] refused ${o.method} ${i} from ${o.socket?.remoteAddress||"?"} \u2014 no node API token`),s.status(401).json({error:"unauthorized"}))}}function fA(e,t,{enforce:n=!0}={}){return!n||lA(uA(e),t)?!0:Fi(e)}Vt();Cl();Cl();import XL from"node:http";import ZL from"node:net";function $u(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 Ou(e){let t=$u(e.headers.host);return t?{slug:t,view:jp(t)}:null}var QL=e=>String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),eF=e=>{let t=QL(e.name);return`<!doctype html><meta charset="utf-8">
|
|
586
601
|
<meta http-equiv="refresh" content="3">
|
|
587
602
|
<title>${t} \u2014 starting</title>
|
|
588
603
|
<body style="font-family:system-ui;background:#0b0e14;color:#dce3f0;display:grid;place-items:center;height:100vh;margin:0">
|
|
589
604
|
<div style="text-align:center"><p style="font-size:2rem;margin:0">\u23F3</p>
|
|
590
|
-
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function
|
|
605
|
+
<p><b>${t}</b> is waking up\u2026</p><p style="opacity:.6">auto-refreshing</p></div>`};function pA(e,t,n){let r=Ou(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(eF(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=XL.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 mA(e,t,n){let r=Ou(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
|
|
591
606
|
\r
|
|
592
607
|
bostrat: no live view "${s}"\r
|
|
593
|
-
`),t.destroy(),!0;let a=
|
|
608
|
+
`),t.destroy(),!0;let a=ZL.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
|
|
594
609
|
`)+`\r
|
|
595
610
|
\r
|
|
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-
|
|
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
|
|
611
|
+
`),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 hA(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}.${tF(n.host)}/`}function tF(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 $u(t)&&jp(n[0])?n.slice(1).join("."):t}xs();Xp();Ll();bp();ce();import wA from"node:os";import{randomUUID as Zh}from"node:crypto";var nF=(e=process.env)=>({nodeId:e.BOSTRAT_NODE_ID||wA.hostname(),nodeName:e.BOSTRAT_NODE_NAME||wA.hostname()});var gA=32768,yA=120,bA=1e3,kA=new Set(["claude","codex","cursor"]),rF=new Set(["off","transition","nag"]),oF=[[0,59],[0,23],[1,31],[1,12],[0,6]];function sF(e){let t=String(e??"").trim().split(/\s+/);return t.length!==5?!1:t.every((n,r)=>{let[o,s]=oF[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 Ut=e=>String(e??"").trim(),So={name(e){let t=Ut(e);if(!t)throw new Error("a loop needs a name");if(t.length>yA)throw new Error(`name is too long (${t.length}); cap is ${yA}`);return t},schedule(e){let t=Ut(e);if(!t)throw new Error('a loop needs a schedule (5-field UTC crontab, e.g. "0 7 * * *")');if(!sF(t))throw new Error(`invalid cron schedule: "${t}" \u2014 5 UTC fields, e.g. "0 7 * * *"`);return t},prompt(e){let t=Ut(e);if(!t)throw new Error("a loop needs a prompt \u2014 it is what the run does");if(t.length>gA)throw new Error(`prompt is too long (${t.length} chars); cap is ${gA}`);return t},expectation(e){let t=Ut(e)||null;if(t&&t.length>bA)throw new Error(`expectation is too long (${t.length}); cap is ${bA}`);return t},notify(e){let t=Ut(e)||"off";if(!rF.has(t))throw new Error(`notify must be one of off | transition | nag, got "${e}"`);return t},assignment(e){let t=e??null,n=Ut(typeof t=="string"?t:t?.provider)||"claude";if(!kA.has(n))throw new Error(`unknown provider "${n}" \u2014 one of ${[...kA].join(", ")}`);let r=typeof t=="object"&&t&&Ut(t.model)||null;return r?{provider:n,model:r}:{provider:n}},enabled(e){if(typeof e=="boolean")return e;if(e==="true"||e==="false")return e==="true";throw new Error(`enabled must be true or false, got "${e}"`)}};function aF(e,t){let n=e&&typeof e=="object"?e:{},r=So.name(n.name),o=So.schedule(n.schedule),s=So.prompt(n.prompt),a=So.expectation(n.expectation),i=So.notify(n.notify),c=So.assignment(n.assignment),l=Ut(t?.workspace?.repo)||Ut(t?.workspace?.base)||null;if(!l)throw new Error("this Track isn't based on a repo, so there's nothing for a loop to run in \u2014 propose from a repo-backed Track");let{nodeId:u,nodeName:d}=nF();return{name:r,schedule:o,prompt:s,expectation:a,notify:i,assignment:c,repo:l,nodeId:u||null,nodeName:d||null}}async function SA(e,t){let n=aF(t,e),r=Zh();return await A({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 vA(e,t,n,{loopId:r=null,nextFireAt:o=null}={}){if(!t)throw new Error("proposalId required");let s=await j({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 A({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 A({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 A({id:e.id,cwd:e.cwd},{type:"loop.create.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:"created",loopId:String(r)}}),{proposalId:t,action:"created",loopId:String(r)}}var Xh=["name","schedule","prompt","expectation","notify","assignment","enabled"],iF=(e,t)=>(e?.provider??"claude")===(t?.provider??"claude")&&(e?.model??null)===(t?.model??null);function TA(e){let t=Array.isArray(e.runs)?e.runs:[],n=t[t.length-1]??null;return{definitionUpdatedAt:Number(e.definitionUpdatedAt??e.updatedAt)||null,name:e.name??"",schedule:e.schedule??"",prompt:e.prompt??"",expectation:e.expectation??null,notify:e.notify??"off",assignment:e.assignment&&typeof e.assignment=="object"?e.assignment:{provider:String(e.assignment||"claude")},enabled:e.enabled!==!1,repo:e.repo??null,nodeId:e.nodeId??null,nodeName:e.nodeName??null,trackId:e.trackId??null,nextFireAt:Number.isFinite(Number(e.nextFireAt))?Number(e.nextFireAt):null,createdAt:Number(e.createdAt)||null,runCount:t.length,lastRun:n?{firedAt:Number(n.firedAt)||null,outcome:n.outcome??(n.error?"errored":null)}:null}}function AA(e,t){let n=Ut(t);if(!n)throw new Error("which loop? give an id or a name \u2014 see `bostrat-loop ls`");let r=e.find(a=>a.id===n)||e.find(a=>a.name===n);if(r)return r;let o=n.toLowerCase(),s=e.filter(a=>String(a.id).toLowerCase().startsWith(o)||String(a.name).toLowerCase().startsWith(o));if(s.length===1)return s[0];throw s.length>1?new Error(`"${n}" matches ${s.length} loops: ${s.map(a=>`${a.name} (${a.id})`).join(", ")} \u2014 use the id`):new Error(`no loop matching "${n}" \u2014 use \`bostrat-loop ls\``)}function cF(e,t){let n=e&&typeof e=="object"&&!Array.isArray(e)?e:null;if(!n)throw new Error("a patch must be a JSON object of the fields to change");let r=Object.keys(n).filter(a=>!Xh.includes(a));if(r.length){let a=r.filter(i=>["repo","nodeId","cwd","userId","owner","id","trackId"].includes(i));throw a.length?new Error(`a proposal can't change ${a.join(", ")} \u2014 retargeting a loop is a Loops-tab edit (scope D5)`):new Error(`unknown field(s) ${r.join(", ")} \u2014 patchable: ${Xh.join(", ")}`)}let o={};for(let a of Xh){if(!(a in n))continue;let i=So[a](n[a]);(a==="assignment"?iF(i,t.assignment):i===(t[a]??(a==="notify"?"off":a==="enabled"?!0:null)))||(o[a]=i)}let s=Object.keys(o);if(!s.length)throw new Error("nothing would change \u2014 the patch equals the loop's current definition");return{patch:o,changed:s}}async function EA(e,t,n){let r=TA(t),{patch:o,changed:s}=cF(n,r),a=Zh(),i={ts:Date.now(),proposalId:a,loopId:String(t.id),name:r.name,base:r,patch:o,changed:s,selfEdit:!!(r.trackId&&r.trackId===e.id)};return await A({id:e.id,cwd:e.cwd},{type:"loop.update.offered",actor:"system",source:"orchestrator",payload:i}),i}var xA=600;async function _A(e,t,n){let r=Ut(n);if(!r)throw new Error('a delete proposal needs a reason \u2014 bostrat-loop propose-delete <loop> --reason "<why this loop should go>"');if(r.length>xA)throw new Error(`reason is too long (${r.length}); cap is ${xA}`);let o=TA(t),s=Zh(),a={ts:Date.now(),proposalId:s,loopId:String(t.id),name:o.name,base:o,reason:r,selfEdit:!!(o.trackId&&o.trackId===e.id)};return await A({id:e.id,cwd:e.cwd},{type:"loop.delete.offered",actor:"system",source:"orchestrator",payload:a}),a}var IA=async(e,t,n)=>{if(!n)throw new Error("proposalId required");let r=await j({id:e.id,cwd:e.cwd}),o=r.find(a=>a.type===t&&a.payload?.proposalId===n);if(!o)throw new Error("unknown proposal");let s=t.replace(".offered",".answered");if(r.some(a=>a.type===s&&a.payload?.proposalId===n))throw new Error("proposal already answered");return o};async function RA(e,t,n,{nextFireAt:r=null}={}){if(!["applied","dismiss","moved"].includes(n))throw new Error("action must be one of applied | dismiss | moved");let s=(await IA(e,"loop.update.offered",t)).payload,a={id:e.id,cwd:e.cwd};return n==="applied"&&await A(a,{type:"loop.updated",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:s.loopId,name:s.patch?.name??s.name,changed:s.changed??Object.keys(s.patch||{}),nextFireAt:Number.isFinite(Number(r))?Number(r):null}}),await A(a,{type:"loop.update.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:s.loopId}}),{proposalId:t,action:n,loopId:s.loopId}}async function $A(e,t,n){if(!["delete","pause","keep","moved"].includes(n))throw new Error("action must be one of delete | pause | keep | moved");let o=(await IA(e,"loop.delete.offered",t)).payload,s={id:e.id,cwd:e.cwd};return(n==="delete"||n==="pause")&&await A(s,{type:n==="delete"?"loop.deleted":"loop.paused",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,loopId:o.loopId,name:o.name,trackId:o.base?.trackId??null}}),await A(s,{type:"loop.delete.answered",actor:"user",source:"orchestrator",payload:{ts:Date.now(),proposalId:t,action:n,loopId:o.loopId}}),{proposalId:t,action:n,loopId:o.loopId}}Jn();ce();xn();Ll();var OA=10,CA=200,lF=120,uF=24,NA=/^\d{4}-\d{2}-\d{2}$/;function Qh(e=process.env){return(e.CHANGELOG_REPOS||"globocodes/bostrat").split(",").map(t=>t.trim()).filter(Boolean)}var dF=(e=Date.now())=>new Date(e).toISOString().slice(0,10),Xe=(e,t="i")=>new RegExp(e,t),fF=[{re:Xe("^(business|docs|chore|ci|refactor|test|security|sec)(\\([^)]*\\))?!?:"),reason:"non-product PR prefix"},{re:Xe("\\bSPC\\b",""),reason:"business (SPC)"},{re:Xe("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),reason:"business"},{re:Xe("competitor|cloudcli|\\bkiro\\b"),reason:"competitor research"},{re:Xe("\\bReplicas\\b",""),reason:"competitor research"},{re:Xe("tenanc|tenant|perimeter|ip[- ]gate|access[- ]code|vulnerab|\\bCVE\\b|audit|hardening|security|\\bsweep\\b"),reason:"security / tenancy"},{re:Xe("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),reason:"internal codename"},{re:Xe("^track/(business|security|spc|sec)-"),reason:"non-product branch"}],pF=[Xe("\\bSPC\\b",""),Xe("kickstarter|pitch deck|investor|fundrais|commercial ladder|/seat\\b|price[- ]test"),Xe("competitor|cloudcli|\\bkiro\\b"),Xe("\\bReplicas\\b",""),Xe("tenanc|\\btenants?\\b|cross-tenant|perimeter|ip[- ]gate|vulnerab|\\bCVE\\b|hardening|security register|audit finding"),Xe("\\bF-\\d+\\b|\\bBOS-\\d+\\b|\\bAC-D\\d"),Xe("(^|[^\\w&])#\\d+\\b"),Xe("\\btrack/[\\w.-]+")];function mF({title:e="",headRef:t=""}={}){let n=String(e||""),r=String(t||"");for(let{re:o,reason:s}of fF)if(o.test(n)||o.test(r))return s;return null}function MA(e){let t=String(e||"");for(let n of pF){let r=n.exec(t);if(r)return r[0].trim()}return null}function PA(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 hF=/^\*\*([^*]+?)\*\*\s*[:.]?\s*[—–-]?\s*(.*)$/;function wF(e){if(typeof e!="string"||!e.trim())throw new Error("md (markdown string) required");let t=si(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(`
|
|
612
|
+
`)){let s=o.trim();if(!s)continue;if(s.startsWith("#")){n==null&&(n=s.replace(/^#+\s*/,"").trim().slice(0,lF)||null);continue}let a=/^[-*]\s+(.*)$/.exec(s);if(!a)continue;let i=a[1].trim(),c=null,l=new RegExp(`^\\[([^\\]]{1,${uF}})\\]\\s+(.*)$`).exec(i);if(l&&(c=l[1].trim(),i=l[2].trim()),!i)continue;let u=hF.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=MA(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>CA)throw new Error(`bullet ${r.length+1} is ${i.length} chars \u2014 over the ${CA}-char cap; a changelog bullet is one line`);r.push({text:i,...c?{area:c}:{}})}if(n){let o=MA(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>OA)throw new Error(`${r.length} bullets \u2014 over the ${OA}-bullet cap; group related changes into one line`);return{title:n||null,bullets:r}}async function gF(e,{repos:t=Qh()}={}){let n=new Set(t);return(await to()).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:mF({title:r.title,headRef:r.headRef})}))}async function DA(e,t){let n=await gF(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=wF(t),o=n==null||n===""?dF():String(n);if(!NA.test(o)||Number.isNaN(Date.parse(o)))throw new Error(`invalid day "${n}" \u2014 expected YYYY-MM-DD`);let s=await A({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 jA(e,{day:t}){let n=String(t||"");if(!NA.test(n)||Number.isNaN(Date.parse(n)))throw new Error(`invalid day "${t}" \u2014 expected YYYY-MM-DD`);let r=await A({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}}ps();xn();ws();ce();ps();et();wr();xn();Ua();gl();Va();yn();import{execFile as yF}from"node:child_process";import{promisify as bF}from"node:util";var tw=bF(yF),LA=3e4,kF=30*6e4,xF=LA,SF=10*6e4,vF=10080*6e4,TF=60*6e4,AF=8,EF=1440*6e4,_F=3,FA="number,url,title,headRefName,headRefOid,state,mergedAt,closedAt",ew=60*6e4,IF=15*6e4,RF=9e4,$F=1440*6e4,Cu=new Map,Mu=new Map,OF=/could not resolve|not found|no pull requests?/i,CF=e=>OF.test(String(e?.message||e||""));function MF(e,t){return[e.ciFix?.delegatedAt,e.reconcile?.delegatedAt,e.ciFixAwait?.since,e.reconcileAwait?.since].some(n=>n&&t-n<ew)}var UA={fast:xF,slow:SF,decay:TF};function HA(e,t){return e.armedAt&&t-e.armedAt<kF||MF(e,t)||WA(e,t,{grace:0})?"fast":t-(e.firstSeen||0)>vF?"decay":"slow"}function NF(e,t){return UA[HA(e,t)]}function Hi(e,t,n,r){let o=e.lanes?.get(te(t.repo,t.number))||{};return wl({change:n,repo:t.repo,number:Number(t.number),githubAt:r||null,lane:o.lane||null,intervalMs:o.intervalMs||null,source:"watch"})}async function PF(e,t){vn("watch");try{let{stdout:n}=await tw("gh",["pr","view",String(t),"--repo",e,"--json",FA],{timeout:3e4,maxBuffer:4194304,env:{...process.env,...await Kt({slug:e})}});return JSON.parse(n||"null")}catch(n){throw pr(n)&&mr(n),n}}async function DF(e){vn("watch");try{let{stdout:t}=await tw("gh",["pr","list","--repo",e,"--state","all","--limit","100","--json",FA],{timeout:3e4,maxBuffer:8388608,env:{...process.env,...await Kt({slug:e})}}),n=new Map;for(let r of JSON.parse(t||"[]"))n.set(Number(r.number),r);return n}catch(t){throw pr(t)&&mr(t),t}}async function BF(){let e=new Map;for(let t of await re().catch(()=>[]))e.set(t.id,t);return e}async function jF(e,t){await A({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 LF(e,t){let n=await ro(e);return n?ds(n,t):null}async function FF(e,t,n){let r=await ro(e);if(!r)throw new Error(`no local checkout for ${e}`);return fs(r,t,{method:n})}async function UF(e,t){let n=await ro(e);if(!n)throw new Error(`no local checkout for ${e}`);return qa(n,t)}async function HF(e,t){await A({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 Us(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 WF=10*6e4;async function qF(e,t,n){let r=e.autoMerge?.armedAt||0;if(r&&(n.now??Date.now())-r>EF){await Us(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 Us(e,o,t,n,`check${o.checks.failed===1?"":"s"} failed: ${a}`);return}if(o.mergeable==="CONFLICTING"){await Us(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<WF)return;await Us(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 Us(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 Us(e,o,t,n,String(a?.message||a).trim()||"merge failed");return}await qA(e,{...o,state:"MERGED"},t,n,{change:"auto-merged",githubAt:o.checks.settledAt||null})}}async function Hs(e,t,n){await A({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 tw("git",["-C",e.cwd,"cat-file","-e",`${t}^{commit}`],{timeout:1e4}),!0}catch{return!1}}async function Ui(e,t,n,r=()=>!0){return(await j(e).catch(()=>[])).some(s=>s.type==="pr.state.changed"&&s.payload?.change===t&&s.payload?.number===Number(n)&&r(s))}var Ws=(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 WA(e,t,{grace:n=RF}={}){if(e.announced!==!1)return!1;let r=t-(e.firstSeen||0);return r>=n&&r<=$F}async function GF(e,t,n,r){let o=r.now??Date.now();if(!WA(e,o))return!1;let s=n.get(e.trackId)||null;if(!s||Be(s))return!1;let a=(s.boxes||[]).find(d=>d.name===s.leadBox);return await(r.midTurn??oo)(s.leadBox,a?.agent).catch(()=>!1)?!1:(await j(s).catch(()=>[])).some(d=>d.type==="pr.state.changed"&&d.payload?.number===Number(e.number))?(await(r.claim??kn)(e.repo,e.number).catch(()=>null),!1):await(r.claim??kn)(e.repo,e.number).catch(()=>null)?(await Hs(s,"opened",{...Ws(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 VF(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 cl(e.repo,e.number,{headSha:s}).catch(()=>{}),e.reconcile?.headSha&&s!==e.reconcile.headSha&&await ul(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<IF;await p0(e.repo,e.number,s).catch(()=>{}),e.baseFoldAt&&await f0(e.repo,e.number).catch(()=>{}),!i&&!c&&!l&&a&&!await r.localHasCommit(a,s).catch(()=>!1)&&!await Ui(a,"pushed",e.number,d=>d.payload?.sha===s)&&await Hs(a,"pushed",{...Ws(e,t),sha:s}).catch(()=>{})}if(e.ciFixAwait)if(o-(e.ciFixAwait.since||0)>ew)await ll(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 ll(e.repo,e.number).catch(()=>{});else if(i.checks.total>0&&i.checks.pending===0){let c=Hi(r,e,"ci-fixed",i.checks.settledAt||null),l=e.ciFixAwait.since||0;a&&!await Ui(a,"ci-fixed",e.number,u=>(u.payload?.ts||0)>=l)&&await Hs(a,"ci-fixed",{...Ws(e,t),sha:s,delegated:!0,passed:i.checks.passed,githubAt:c.githubAt,latencyMs:c.latencyMs,lane:c.lane}).catch(()=>{}),await ll(e.repo,e.number).catch(()=>{})}}}if(e.reconcileAwait)if(o-(e.reconcileAwait.since||0)>ew)await dl(e.repo,e.number).catch(()=>{});else{let i=await r.detail(e.repo,e.number).catch(()=>null);if(i){if(i.mergeable==="CONFLICTING")await dl(e.repo,e.number).catch(()=>{});else if(i.mergeable==="MERGEABLE"){Hi(r,e,"reconciled",null);let c=e.reconcileAwait.since||0;a&&!await Ui(a,"reconciled",e.number,l=>(l.payload?.ts||0)>=c)&&await Hs(a,"reconciled",{...Ws(e,t),sha:s,delegated:!0,base:i.baseRefName||null}).catch(()=>{}),await dl(e.repo,e.number).catch(()=>{})}}}}async function zF(e,t,n,r){let o=Hi(r,e,"closed",Date.parse(t?.closedAt||"")||null),s=n.get(e.trackId);s&&!await Ui(s,"closed",e.number)&&await Hs(s,"closed",{...Ws(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 JF(e,t,n){Hi(n,e,"lost",null);let r=t.get(e.trackId);r&&!await Ui(r,"lost",e.number)&&await Hs(r,"lost",Ws(e,null)).catch(()=>{}),await n.notifyIfResolved(e.repo,e.number,"lost").catch(()=>{}),await n.mark(e.repo,e.number,"lost").catch(()=>{})}async function qA(e,t,n,r,o={}){let s=Hi(r,e,o.change||"merged",o.githubAt??(Date.parse(t.mergedAt||"")||null)),a=n.get(e.trackId);a&&((await j(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 YF(e={}){let{now:t=Date.now(),auth:n=sp,view:r=PF,list:o=DF,links:s=w0,trackMap:a=BF,emitMerged:i=jF,mark:c=dr,archiveIfArmed:l=An,notifyIfArmed:u=En,notifyIfResolved:d=Yl,batchThreshold:f=_F,detail:p=LF,merge:m=FF,updateBranch:w=UF,lookup:y=Ke,disarm:x=Fa,emitDisarmed:k=HF,localHasCommit:T=KF,claim:_=kn,midTurn:P=oo}=e;if(!(await n().catch(()=>null))?.authenticated||ml()>0)return 0;let O=await s().catch(()=>[]),$=F0(O),R=O.filter(X=>t-(Cu.get(te(X.repo,X.number))||0)>=NF(X,t));if(!R.length)return 0;let ee=Date.now(),de=new Map,be=new Map;for(let X of[...R].sort(Ka)){let he=HA(X,t);de.set(te(X.repo,X.number),{lane:he,intervalMs:UA[he]}),Cu.set(te(X.repo,X.number),t),be.has(X.repo)||be.set(X.repo,[]),be.get(X.repo).push(X)}let G=await a().catch(()=>new Map),I={now:t,lanes:de,emitMerged:i,mark:c,archiveIfArmed:l,notifyIfArmed:u,notifyIfResolved:d,detail:p,merge:m,updateBranch:w,lookup:y,disarm:x,emitDisarmed:k,localHasCommit:T,claim:_,midTurn:P};for(let[X,he]of be){let Se,Le=new Set;if(he.length>f)Se=await o(X).catch(()=>null);else{Se=new Map;for(let F of he)try{let D=await r(X,F.number);D?.number!=null&&Se.set(Number(D.number),D)}catch(D){CF(D)&&Le.add(te(F.repo,F.number))}}if(Se)for(let F of he){let D=te(F.repo,F.number),Q=Se.get(Number(F.number));if(!Q){if(Le.has(D)){let Ne=(Mu.get(D)||0)+1;Mu.set(D,Ne),Ne>=AF&&(await JF(F,G,I),Mu.delete(D),Cu.delete(D))}continue}if(Mu.delete(D),Q.state==="MERGED"||Q.state==="CLOSED")Q.state==="MERGED"?await qA(F,Q,G,I):await zF(F,Q,G,I),Cu.delete(D);else{await GF(F,Q,G,I).catch(()=>{}),await VF(F,Q,G,I).catch(()=>{});let Ne=!F.trackId||$.get(F.trackId)===D;F.autoMerge&&Ne&&await qF(F,G,I).catch(()=>{})}}}let J={};for(let{lane:X}of de.values())J[X]=(J[X]||0)+1;return console.log(`[pr-watch] probed ${R.length} link(s) (${Object.entries(J).map(([X,he])=>`${X} ${he}`).join(", ")}) in ${Date.now()-ee}ms`),R.length}function KA(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{await YF()}catch{}finally{e=!1}}},LA)}ce();xn();Kn();wr();yn();function GA(e){return(e?.checks?.failed??0)>0?"red":e?.mergeable==="CONFLICTING"?"conflict":e?.mergeStateStatus==="BEHIND"?"behind":null}function XF(e){return!(!e?.checks||e.checks.pending>0||e.mergeabilityPending||e.mergeable==="UNKNOWN"||e.mergeStateStatus==="UNKNOWN")}var ZF=new Set(["merged","closed","lost"]);function QF(e,t){if(!e?.blocked)return null;let n=GA(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 eU(e,t){return e.autoMerge?!0:t==="ci-fixed"?!!(e.ciFix||e.ciFixAwait):t==="reconciled"?!!(e.reconcile||e.reconcileAwait):!1}async function tU(e,t,n,r,o){return(await j(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 nU(e,t,n){await A({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 VA(e,t={}){let{now:n=Date.now(),slugOf:r=fe,lookup:o=Ke,note:s=m0,tracks:a=async()=>(await re()).filter(d=>!Be(d)),asked:i=tU,emitChange:c=nU}=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(!XF(p))continue;let m=await o(f,p.number).catch(()=>null);if(!m?.trackId||ZF.has(m.state))continue;let{prev:w}=await s(f,p.number,{blocked:GA(p),sha:p.headSha??null,at:n});if(!w)continue;let y=QF(w,p);if(!y||eU(m,y.change))continue;u??=new Map((await a()).map(_=>[_.id,_]));let x=u.get(m.trackId);if(!x)continue;let k=p.headSha??null;if(await i(x,y.change,p.number,k,w.at||0))continue;let T={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:y.reason};y.change==="ci-fixed"?T.passed=p.checks.passed:T.base=p.baseRefName||null,await c(x,y.change,T),l.push({change:y.change,number:p.number,reason:y.reason})}catch{}}return l}ze();ce();wr();yn();import{randomUUID as rU}from"node:crypto";var oU=2e4,zA=3,qs=new Map,nw=new Set;async function JA(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 sU(e={}){let{sessions:t=null,tracks:n=null,append:r=A,read:o=j,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 re()}catch{return}let c=new Map(a.map(u=>[u.name,u])),l=new Set;for(let u of i){if(Be(u)||!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=(qs.get(d.name)||0)+1;qs.set(d.name,p),p===zA&&(nw.add(d.name),await JA(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:rU(),box:d.name,agent:d.agent}}).catch(()=>{}))}else{let p=(qs.get(d.name)||0)>=zA;if(qs.delete(d.name),p||!nw.has(d.name)){nw.add(d.name);let m=await JA(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[...qs.keys()])l.has(u)||qs.delete(u)}var Wi=null,rw=!1;function YA(){return Wi?()=>{}:(Wi=setInterval(()=>{rw||(rw=!0,sU().catch(()=>{}).finally(()=>{rw=!1}))},oU),Wi.unref?.(),()=>{clearInterval(Wi),Wi=null})}is();Up();ce();ps();ws();is();import{randomUUID as aU}from"node:crypto";async function iU(){let e=await Promise.resolve().then(()=>(wr(),Cv));return{sayToLead:e.sayToLead,queueShipCompleteNotify:e.queueShipCompleteNotify}}var ow={listRepoPrs:Xt,readEvents:j,appendEvent:A,sayDeps:iU,currentBranch:ms};async function cU(e,t=ow,{branch:n=null}={}){if(!e?.cwd)return null;let r=n||await(t.currentBranch??ms)(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 sw(e,t,{actions:n,notify:r=!1}={},o=ow){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 cU(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=aU();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 aw(e,{offerId:t,choice:n}={},r=ow){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=ss.filter(d=>d!=="pr"&&(i.includes(d)||d==="push")),c=await u(e.id,as(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,n0(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 Fn from"node:fs/promises";import Ks from"node:path";import rE from"node:os";import XA from"node:fs/promises";import Nu from"node:path";import{execFile as lU}from"node:child_process";import{promisify as uU}from"node:util";import{fileURLToPath as dU}from"node:url";var fU=uU(lU),QA=Nu.dirname(dU(import.meta.url)),pU=Nu.resolve(QA,"..","..","client","dist"),mU="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",hU=new Map([...mU].map((e,t)=>[e,t]));function wU(e){let t=[],n=0,r=0;for(let o of e){let s=hU.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 gU(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=wU(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 yU(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 ZA=new Map;async function bU(e){let t;try{t=await XA.stat(e)}catch{return null}let n=ZA.get(e);if(n&&n.mtimeMs===t.mtimeMs)return n;try{let r=JSON.parse(await XA.readFile(e,"utf8")),o={mtimeMs:t.mtimeMs,sources:r.sources||[],lines:gU(r.mappings||"")};return ZA.set(e,o),o}catch{return null}}var iw=null;function kU(){return iw||(iw=process.env.BOSTRAT_RELEASE?Promise.resolve(process.env.BOSTRAT_RELEASE):fU("git",["rev-parse","--short","HEAD"],{cwd:Nu.resolve(QA,"..","..")}).then(e=>e.stdout.trim()).catch(()=>null)),iw}var xU=/(?:https?:\/\/[^\s()]*)?\/assets\/([^\s()/]+\.js):(\d+):(\d+)/g;async function SU(e,{distDir:t=pU}={}){let n=[],r=0,o=!1;for(let s of e.matchAll(xU)){let[a,i,c,l]=s,u=await bU(Nu.join(t,"assets",`${i}.map`)),d=u?yU(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 eE(e,t={}){try{if(!e?.error?.stack||!e.release||e.release==="unknown")return e;let n=t.release??await kU();if(!n||e.release!==n)return e;let{stack:r,symbolicated:o}=await SU(e.error.stack,t);o&&(e.error.stack=r,e.error.symbolicated=!0)}catch{}return e}var vU=()=>process.env.BOSTRAT_STATE_DIR||Ks.join(process.env.HOME||rE.tmpdir(),".bostrat"),Mr=()=>Ks.join(vU(),"telemetry"),oE=()=>Ks.join(Mr(),".relay-offsets.json"),TU=50,AU=14,EU=3e4,tE=500,bt=(e,t)=>typeof e=="string"?e.slice(0,t):"";function _U(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 sE=e=>new Date(e).toISOString().slice(0,10),nE=0;async function aE(e,t=Date.now()){let r=(Array.isArray(e?.events)?e.events.slice(0,TU):[]).map(s=>_U(s,t)).filter(Boolean);if(!r.length)return 0;for(let s of r)s.error?.stack&&await eE(s);await Fn.mkdir(Mr(),{recursive:!0});let o=Ks.join(Mr(),`${sE(t)}.ndjson`);return await Fn.appendFile(o,r.map(s=>JSON.stringify(s)+`
|
|
613
|
+
`).join(""),"utf8"),t-nE>6*3600*1e3&&(nE=t,IU(t).catch(()=>{})),r.length}async function IU(e=Date.now()){let t=sE(e-AU*24*3600*1e3),n=[];try{n=await Fn.readdir(Mr())}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 Fn.rm(Ks.join(Mr(),s),{force:!0});let o=await cE();for(let s of r)delete o[s];await lE(o)}var Pu=new Map;function iE(e,t=Date.now()){let n=e||"unknown",r=Pu.get(n);return r||(Pu.size>512&&Pu.clear(),r={tokens:40,at:t},Pu.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 cE(){try{return JSON.parse(await Fn.readFile(oE(),"utf8"))||{}}catch{return{}}}async function lE(e){await Fn.mkdir(Mr(),{recursive:!0}),await Fn.writeFile(oE(),JSON.stringify(e),"utf8")}function RU(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||rE.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 $U({fetchImpl:e=fetch}={}){let t=await Qv();if(!t)return{sent:0,disabled:!0};let n;try{n=(await Fn.readdir(Mr())).filter(s=>s.endsWith(".ndjson")).sort()}catch{return{sent:0,disabled:!1}}let r=await cE(),o=0;for(let s of n){let a=Ks.join(Mr(),s),i=r[s]||0,c;try{let f=await Fn.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(`
|
|
614
|
+
`).filter(Boolean),d=[];for(let f of u)try{d.push(JSON.parse(f))}catch{}for(let f=0;f<d.length;f+=tE){let p=d.slice(f,f+tE).map(w=>RU(w)),m=await e(`${t.url.replace(/\/$/,"")}/v1/logs`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.ingestKey}`},body:JSON.stringify({events:p})}).catch(()=>null);if(!m||!m.ok)return{sent:o,disabled:!1};o+=p.length}r[s]=i+l+1,await lE(r)}return{sent:o,disabled:!1}}var cw=null,lw=!1;function uE(){cw||(cw=setInterval(async()=>{if(!lw){lw=!0;try{await $U()}catch(e){console.error("[telemetry] relay failed:",e?.message||e)}finally{lw=!1}}},EU),cw.unref?.())}import Bu from"node:fs/promises";import hE from"node:path";import OU from"node:os";var wE=()=>process.env.BOSTRAT_STATE_DIR||hE.join(process.env.HOME||OU.tmpdir(),".bostrat"),gE=()=>hE.join(wE(),"dictation-glossary.json"),CU=100,MU=500,NU=["bostrat","globopi","codex","tmux","worktree","Deepgram","Linear","Firecracker","Tigris","WireGuard","misous","postyx","cedar","gerbil","Anthropic","Opus","Haiku","Sonnet","keyterm","monorepo","dogfood"],ju=null,uw=null,dw=Promise.resolve();function dE(){let e={},t=Date.now();for(let n of NU)e[n.toLowerCase()]={term:n,count:1,lastUsed:t,source:"seed"};return e}async function yE(){return ju||(uw??=Bu.readFile(gE(),"utf8").then(e=>JSON.parse(e)?.terms??dE()).catch(()=>dE()).then(e=>ju=e).finally(()=>uw=null),uw)}async function fE(){let e=JSON.stringify({terms:ju},null,2),t=gE(),n=`${t}.${process.pid}.tmp`;await Bu.mkdir(wE(),{recursive:!0}),await Bu.writeFile(n,e),await Bu.rename(n,t)}function PU(){return dw=dw.then(fE,fE),dw.catch(()=>{})}async function fw(){return Object.values(await yE()).sort((t,n)=>n.count-t.count||n.lastUsed-t.lastUsed)}var DU=e=>Math.max(1,Math.ceil(e.length/4));async function BU(){let e=[],t=0;for(let{term:n}of await fw()){let r=DU(n);if(!(t+r>MU)&&(e.push(n),t+=r,e.length>=CU))break}return e}async function jU(e,t){let n=await yE(),r=e.toLowerCase(),o=n[r];n[r]={term:e,count:(o?.count??0)+1,lastUsed:Date.now(),source:o?.source??t}}var pE=e=>(e||"").split(/\s+/).map(t=>t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"")).filter(Boolean),Du=e=>e.toLowerCase();function mE(e){return!new RegExp("\\p{L}","u").test(e)||e.length<3?!1:/^[a-z]+$/.test(e)?e.length>=4:!0}async function bE(e,t){let n=pE(e),r=pE(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]=Du(n[u])===Du(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;)Du(n[c])===Du(r[l])?(c++,l++):a[c+1][l]>=a[c][l+1]?c++:(mE(r[l])&&i.add(r[l]),l++);for(;l<s;l++)mE(r[l])&&i.add(r[l]);if(!i.size)return[];for(let u of i)await jU(u,"learned");return await PU(),[...i]}var qi=null;function LU(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 kE(){if(!qi)return null;let e=await Ri();if(!e||e.apiKey.slice(-6)!==qi.keyTail)return null;let{keyTail:t,...n}=qi;return n}async function xE(e,t){let n=await Ri();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 BU())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=LU(o.status,i);c.persistent&&(qi={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 qi=null,{transcript:(await o.json())?.results?.channels?.[0]?.alternatives?.[0]?.transcript??""}}ce();var pw={"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 FU(e){if(!e)return null;let t=String(e).trim();if(pw[t])return t;let n=null;for(let r of Object.keys(pw))t.startsWith(r)&&(!n||r.length>n.length)&&(n=r);return n}function UU(e,t={}){let n=pw[FU(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 HU(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 Ki=null;async function WU(){if(!Ki)return null;let e=await hu();if(!e||e.apiKey.slice(-6)!==Ki.keyTail)return null;let{keyTail:t,...n}=Ki;return n}async function qU(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=HU(t.status,o);s.persistent&&(Ki={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}Ki=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:qU};async function SE(){let e=await hu();return e?{configured:!0,provider:e.provider,model:e.model,voice:e.voice,problem:await WU()}:{configured:!1}}async function vE(){let e=await hu();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 TE(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=UU(t,s),i=await A(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}}yn();var $E=me.dirname(VU(import.meta.url)),AE=Number(process.env.PORT)||3362,Lu=lh(process.env),OE=Number(process.env.BOSTRAT_AUTO_TRIGGER_PCT)||80,Gs=sA(process.env.BOSTRAT_ALLOW_IPS),bw=aA(process.env.BOSTRAT_VIEW_FRONT_CIDRS,Ou),CE=cA(),ME=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"),$u(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(Fi(e)||Gs.allowed(Gs.clientIp(e))||bw.allows(e))return n();t.status(403).type("text/plain").send("bostrat: access restricted to the allowlist")});S.use(pA);S.use(dA({token:CE,enforce:ME}));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 Xn(),autoTriggerPct:OE})});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 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(!Ee(n))return t.status(400).json({error:"invalid session name"});if(!await ie(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(!Ee(n))return t.status(400).json({error:"invalid session name"});let r=await ie(n);if(!r)return t.status(404).json({error:"session not found"});let o=await ha(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(!Ee(n))return t.status(400).json({error:"invalid session name"});try{let r=await yb(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=Kc(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 EE=10;S.get("/api/idle-guard",async(e,t)=>{try{t.json(ch({tracks:await re(),views:await Ol(),queuedOf:n=>uo(n)}))}catch{t.status(500).json({busy:!0,reasons:["error"]})}});var Rt=ho(),ZU=Uv({kind:Rt}),Gi=null,ot=()=>Gi&&["auto","prompt","off"].includes(Gi)?Gi:ZU,yw=null,QU=_i.has(Rt),NE=async({to:e,from:t})=>!QU||cu(await Ii())?!1:(Vi.arm({to:e,from:t,kind:Rt}),!0);async function e4(e){let t=ot();Gi=e??null;let n=ot();if(t!==n){if(console.log(`[self-update] policy ${t} \u2192 ${n} (${e?"plane row":"node default"})`),n!=="auto"){Vi.cancel();return}yw&&await NE(yw)}}var PE=(()=>{try{return XU(import.meta.url)("../package.json").version||null}catch{return null}})(),Vi=Wv({isBusy:async()=>ch({tracks:await re(),views:await Ol(),queuedOf:e=>uo(e)}).busy,onStuck:({to:e,from:t})=>ln({current:t,latest:e,installKind:Rt,policy:ot(),status:"stuck"}).catch(()=>{}),onStarted:({to:e,from:t,result:n})=>(n?.ok?ln({current:t,latest:e,installKind:Rt,policy:ot(),status:"updating",reason:"auto"}):ln({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:Gi?"plane":"node",current:PE,status:await Ii(),armed:Vi.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 lu({to:n,kind:Rt,reason:"manual"});if(r.ok)return Vi.cancel(),ln({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 DE(){try{let e=await Hv();e&&await ln({current:e.from,latest:e.to,installKind:e.kind||Rt,policy:ot(),status:e.status,error:e.error,reason:e.reason}),await Em({current:PE,installKind:Rt,policy:ot()})}catch{}}DE();setInterval(DE,3e4).unref();S.get("/api/tracks",async(e,t)=>{let n=(await re()).filter(s=>!s.transferredTo),r=n.filter(s=>s.archivedAt&&!s.archiving),o=n;if(e.query.archived!=="all"&&r.length>EE){let s=new Set([...r].sort((a,i)=>(i.archivedAt||0)-(a.archivedAt||0)).slice(0,EE).map(a=>a.id));o=n.filter(a=>!a.archivedAt||a.archiving||s.has(a.id))}t.json({tracks:o,archivedTotal:r.length,agents:yi,agentAvailability:await ql(),tickets:await pT()})});S.get("/api/cost",async(e,t)=>{try{let n=await re();e.query.track&&(n=n.filter(o=>o.id===e.query.track));let r=await qh(n);if(r.cursorUsage=Ap(),e.query.format==="text")return t.type("text/plain").send(FT(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 qT(await re(),{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 JT(await re(),{window:e.query.window,from:e.query.from,to:e.query.to});n.cursorUsage=Ap(),n.subscriptionMonthlyUsd=Number(process.env.BOSTRAT_SUBSCRIPTION_MONTHLY_USD)||null,n.caps=Da(),t.json(n)}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/usage/caps",(e,t)=>{t.json(Da())});S.post("/api/usage/caps",async(e,t)=>{try{t.json(await nl(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 eA(await re(),{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 vw(e.get(Zi),{byToken:_m,legacy:()=>Ya(e.body?.session_id,e.body?.cwd)});if(n==="refused")return zs("refused",{reason:"unknown-token",hook:e.body?.hook_event_name}),t.json({ok:!1,error:"unknown hook token"});if(n==="legacy-denied")return zs("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&&zs("mismatch",{track:r.track.id,box:r.box.name}),zs(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 PT(e.body||{},()=>r);e.body?.hook_event_name==="Stop"&&r?.box?.cwd&&pc(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(Gs.clientIp(e)))return t.json({ok:!0,dropped:!0});let n=await aE(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 fn(),r=new Set((await Promise.all(n.map(a=>fe(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 oc(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:rc(),calls:E0(),latency:$0()})});S.post("/api/github/refresh",(e,t)=>{Mw(),t.json({ok:!0})});S.get("/api/gmail/search",async(e,t)=>{try{let n=await nA(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 rA(String(e.params.id))})}catch(n){t.status(n?.status||502).json({error:n?.message||String(n)})}});function kw(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 Fu({repo:e=null,number:t=null,branch:n=null}){let r=e?await fe(e):null;if(r&&t!=null){let s=await Ke(r,t),a=s&&await ne(s.trackId);if(a)return a}if(!n)return null;let o=await re();return Sl(o,n,s=>kw(s,e,r))}async function BE({repo:e=null,number:t=null,branch:n=null}){let r=await Fu({repo:e,number:t,branch:n}).catch(()=>null);if(r&&!Be(r))return r;if(!n)return null;let o=e?await fe(e).catch(()=>null):null,s=await re();return Sl(s,n,a=>!Be(a)&&kw(a,e,o))}async function Ht(e,{number:t,url:n=null,title:r=null,branch:o=null,repo:s=null,track:a=null,extra:i={}}){let c=a||await Fu({repo:s,number:t,branch:o}).catch(()=>null);if(!c)return;let l=i.repo??(s?await fe(s).catch(()=>null):null);await A({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 t4(e){let t=(await re()).filter(n=>!Be(n));for(let n of e??[]){if(!n.prs?.length)continue;let r=await fe(n.path);if(r)for(let o of n.prs){if(await Ke(r,o.number))continue;let s=await Sl(t,o.headRefName,a=>kw(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 B0();await t4(n.repos).catch(()=>{}),await VA(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 w=o||"merge";if(!Wa.has(w))throw new Error(`invalid merge method: ${w}`);let y=Number(r);if(!Number.isInteger(y)||y<=0)throw new Error("invalid PR number");let x=await fe((await Tn(n)).path);if(!x)throw new Error("repo has no GitHub remote");let k=await Fu({repo:n,number:y,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 qa(n,y),await mt({repo:x,number:y,trackId:k.id,url:d,headRef:l,title:u,source:"matched"}).catch(()=>{}),!await ja(x,y,{merge:{method:w}}))throw new Error("couldn't record the auto-merge arm");return await eo(x,y,i===!0),await La(x,y,c===!0&&i!==!0),Qt(),await Ht("auto-armed",{number:y,url:d,title:u,branch:l,repo:n,track:k,...a?{extra:{updated:!0}}:{}}),t.json({ok:!0,number:y,auto:!0})}let f=await fs(n,r,{method:o||"squash"});await Ht("merged",{number:f.number,url:d,title:u,branch:l,repo:n});let p=await fe(n).catch(()=>null),m=!1;if(p&&(i===!0&&await eo(p,f.number,!0).catch(()=>{}),c===!0&&i!==!0&&await La(p,f.number,!0).catch(()=>{}),await En(p,f.number).catch(()=>{}),m=await An(p,f.number).catch(()=>!1),await dr(p,f.number,"merged").catch(()=>{})),i===!0&&!m){let w=await Fu({repo:n,number:f.number,branch:l}).catch(()=>null);w&&!w.archivedAt&&await po(w.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 fe((await Tn(n)).path).catch(()=>null),l=c?await Fa(c,i).catch(()=>null):null;try{await cp(n,i)}catch(u){if(!l)throw u}Qt(),await Ht("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 fe(n).catch(()=>null),s=await H0(n,{method:r||"merge",onMerged:a=>{Ht("merged",{number:a.number,url:a.url,title:a.title,branch:a.headRefName,repo:n}),o&&En(o,a.number).catch(()=>{}).then(()=>An(o,a.number)).catch(()=>{}).then(()=>dr(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:U0()})});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(!Wa.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 to()).filter(f=>f.trackId===c.id&&f.state!=="merged"&&f.state!=="closed").sort(Ka);if(!l.length)return t.status(409).json({error:"no open PRs on this Track"});for(let f of l)await eo(f.repo,f.number,o===!0).catch(()=>{}),await La(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 ro(f.repo);if(!m)throw new Error(`no local checkout for ${f.repo}`);let w=await ds(m,f.number);d.push({r:p,l:f,repoPath:m,pr:w,plan:j0(w,{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:w,plan:y}of L0(d))try{if(y.action==="skip")f.action="skipped",f.reason=y.reason;else if(y.action==="merge")await fs(m,p.number,{method:i}),await Ht("merged",{number:p.number,url:w.url,title:w.title,branch:w.headRefName,repo:m,track:c}),await En(p.repo,p.number).catch(()=>{}),await An(p.repo,p.number).catch(()=>{}),await dr(p.repo,p.number,"merged").catch(()=>{}),f.action="merged";else{if(y.action==="update-arm"&&(await qa(m,p.number).catch(()=>{}),f.action="updated-armed"),!await ja(p.repo,p.number,{merge:{method:i}}))throw new Error("couldn't record the auto-merge arm");await Ht("auto-armed",{number:p.number,url:w.url,title:w.title,branch:w.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(Qt(),!u.some(f=>f.ok)){if(o===!0)for(let f of l)await eo(f.repo,f.number,!1).catch(()=>{});if(s===!0&&o!==!0)for(let f of l)await La(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 to()).filter(a=>a.trackId===r.id&&a.state!=="merged"&&a.state!=="closed"),s=[];for(let a of o){let i=await Fa(a.repo,a.number).catch(()=>null),c=await ro(a.repo).catch(()=>null);c&&await cp(c,a.number).catch(()=>{}),i&&(await Ht("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}))}Qt(),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(w=>{let y=u.find(x=>x.name===w||x.repo===w||x.base===w||x.dir===w);if(!y)throw new Error(`invalid repo "${w}" \u2014 not tagged on this Track`);return y});else if(d=await gs(i),!d.length)return t.status(409).json({error:"no tagged repo has work to ship"});let p=[];for(let w of d)try{let y=await up(w.dir,{title:r,body:o,base:s}),x=w.repo||await fe(w.dir).catch(()=>null);x&&y.number&&await mt({repo:x,number:y.number,trackId:i.id,url:y.url,headRef:i.workspace?.branch??null,title:r,source:"api"}),await Ht("opened",{number:y.number,url:y.url,title:r,track:i,branch:i.workspace?.branch,extra:{repo:x??w.name}}),p.push({repo:w.name,slug:x??null,ok:!0,url:y.url,number:y.number})}catch(y){p.push({repo:w.name,slug:w.repo??null,ok:!1,error:y?.message||String(y)})}let m=p.filter(w=>w.ok);return m.length?t.json({ok:!0,multi:!0,results:p,url:m[0].url,number:m[0].number}):t.status(502).json({error:p.map(w=>`${w.repo}: ${w.error}`).join("; "),results:p})}let c=await up(i.cwd,{title:r,body:o,base:s}),l=await fe(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 Ht("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 gs(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 jm(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 Lm(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 ds(n,r),s=await fe(n).catch(()=>null),a=await BE({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 W0(n,o.number);if(!l.length)return t.status(409).json({error:"no failing checks on this PR"});let u=c&&i?me.join("ci",i.name):"ci",d=me.join(a.cwd,".bostrat",u),f=me.join(".bostrat",u,`pr-${o.number}-fail.log`);await Ze.promises.mkdir(d,{recursive:!0}),await Ze.promises.writeFile(me.join(a.cwd,f),l.map(x=>`\u2550\u2550\u2550\u2550 ${x.name}${x.link?` \xB7 ${x.link}`:""} \u2550\u2550\u2550\u2550
|
|
600
615
|
${x.log??"(no fetchable log \u2014 see link)"}
|
|
601
616
|
`).join(`
|
|
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
|
|
617
|
+
`));let p=l.map(x=>x.name).join(", "),m=c&&i?` in the ${i.name}/ checkout`:"",w=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"}),y=s;y&&(await mt({repo:y,number:o.number,trackId:a.id,url:o.url,headRef:o.headRefName,title:o.title,source:"matched"}),await l0(y,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Ht("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:w.leadBox,queued:!!w.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 ds(n,r);if(o.mergeable!=="CONFLICTING")return t.status(409).json({error:"this PR does not conflict with its base branch"});let s=await fe(n).catch(()=>null),a=await BE({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 u0(u,o.number,{headSha:o.headSha,trackId:a.id}).catch(()=>{})),await Ht("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=ss.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 j(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 sw(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,as(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 A({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&&Mm(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 aw(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 zi=e=>t=>({...t,url:hA(t,e.headers)});S.get("/api/views",async(e,t)=>{try{let n=await Ol({trackId:e.query.track||void 0});t.json({views:n.map(zi(e)),maxLive:yr})}catch(n){t.status(500).json({error:n?.message||String(n)})}});S.get("/api/views/:id",async(e,t)=>{let n=await jx(e.params.id);if(!n)return t.status(404).json({error:"view not found"});t.json({view:zi(e)(n),session:ys(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 re()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+me.sep)))||null),!i)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let c=await Lp({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:zi(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:zi(e)(await Lx(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:zi(e)(await Fx(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 Ux(e.params.id))}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:500).json({error:r})}});var n4=[["linear",wo,kh],["jira",mu,yh]],mw=new Map;S.get("/api/integrations/ticketing",async(e,t)=>{try{let n=await gh(),r={};for(let[o,s,a]of n4){let i=await s();if(!i){mw.delete(o),r[o]={configured:!1};continue}let c=mw.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},mw.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 gh()})}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 Zv(o),i=n?await ne(n):null;!i&&r&&(i=(await re()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+me.sep)))||null),i&&await A({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 vo=async(e,t)=>{let n=e?await ne(e):null;return!n&&t&&(n=(await re()).find(o=>o.cwd&&(t===o.cwd||t.startsWith(o.cwd+me.sep)))||null),n};S.get("/api/circuits",async(e,t)=>{try{t.json({circuits:await fh()})}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 vo(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 Ns(o);await uu(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 vo(n,r);if(!o)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});await uu(o,null,{source:"chat"}),t.json({ok:!0,trackId:o.id})}catch(o){t.status(502).json({error:o?.message||String(o)})}});var jE=async(e,t,n)=>{if(e)return{ref:e,track:await vo(t,n)};let r=await vo(t,n),o=r?await mh(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 jE(o,n,r),l=await qv({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 jE(e.query.circuit,e.query.track,e.query.cwd);t.json(await ph(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 vo(n,r);if(!u)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let d=await Vv(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 vo(e.query.track,e.query.cwd);if(!n)return t.status(400).json({error:"run from inside a Track workspace"});t.json(await zv(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 vo(n,r);if(!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=await mh(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 Kv(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 re()).find(c=>c.cwd&&(r===c.cwd||r.startsWith(c.cwd+me.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});if(!o)return t.status(400).json({error:"ref (issue URL or key) required"});let a=await Bs(s,o,{source:n?"api":"cli"});t.json({ok:!0,trackId:s.id,ticket:a})}catch(s){let a=s?.message||String(s);t.status(/not a Linear|not found|no ticketing|required/i.test(a)?400:502).json({error:a})}});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 re()).find(a=>a.cwd&&(n===a.cwd||String(n).startsWith(a.cwd+me.sep)))||null),!o)return t.status(400).json({error:"track (or a cwd inside a Track workspace) required"});t.json({trackId:o.id,configured:await mT(),ticket:await dn(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 dn(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 Bs(n,(e.body||{}).ref,{source:"api"});t.json({ok:!0,ticket:r})}catch(n){let r=n?.message||String(n);t.status(/not a Linear|not found|no ticketing|required/i.test(r)?400:502).json({error:r})}});S.delete("/api/tracks/:id/ticket",async(e,t)=>{t.json({ok:!0,removed:await Ch(e.params.id)})});var Hu=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 yT(e.params.id))}catch(n){let r=n?.message||String(n);t.status(Hu(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/state",async(e,t)=>{try{t.json({ok:!0,...await Mh(e.params.id,(e.body||{}).stateId)})}catch(n){let r=n?.message||String(n);t.status(Hu(r)).json({error:r})}});S.post("/api/tracks/:id/ticket/comment",async(e,t)=>{try{t.json({ok:!0,...await bT(e.params.id,(e.body||{}).body)})}catch(n){let r=n?.message||String(n);t.status(Hu(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 kT(o,n,r===!0)})}catch(o){let s=o?.message||String(o);t.status(Hu(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 Jm(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 re()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+me.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await ST(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 xT()})}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 vT(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 oi({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 $n(e.params.id);if(!n)return t.status(404).json({error:"mock not found"});t.json({mock:n})});var Ji={dotfiles:"allow"};S.get("/api/mocks/:id/png",async(e,t)=>{let n=await $n(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=ni(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,Ji,s=>{s&&!t.headersSent&&t.status(404).json({error:"rendered artifact missing"})})});S.get("/api/mocks/:id/media",async(e,t)=>{let n=await $n(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=ni(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,Ji,s=>{s&&!t.headersSent&&t.status(404).json({error:"video artifact missing"})})});S.get("/api/mocks/:id/html",async(e,t)=>{let n=await $n(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,Ji,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&&Pl(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 re()).find(w=>w.cwd&&(c===w.cwd||c.startsWith(w.cwd+me.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 oS({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&&Pl(n)?n:null;try{let o=await sS(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 cS(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 ES(e.params.id)).map(r=>({id:r.id,name:r.name}));t.json({...await lS(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 xS({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 SS(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 re()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+me.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await vS({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 TS(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 AS(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 re(),a=String(e.query.cwd);if(n=s.find(i=>i.cwd&&(a===i.cwd||a.startsWith(i.cwd+me.sep)))?.id,!n)return t.json({runbooks:[]})}let{runbooks:r,fleetDegraded:o}=await NS({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 Qp(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 Qp(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 PS(n,r.v);return typeof o!="string"?t.status(404).json({error:"runbook artifact missing"}):t.send(o)}t.sendFile(Nn(n,r.v),Ji,o=>{o&&!t.headersSent&&t.status(404).json({error:"runbook artifact missing"})})});S.get("/api/loops",async(e,t)=>{t.json(await za())});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 re()).find(u=>u.cwd&&(o===u.cwd||o.startsWith(u.cwd+me.sep)))||null;if(!a)return t.status(400).json({error:"cwd is not inside a Track workspace"});let i=await za();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 yp({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 re()).find(i=>i.cwd&&(r===i.cwd||r.startsWith(i.cwd+me.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});t.json({ok:!0,trackId:s.id,proposal:await SA(s,o)})}catch(s){t.status(400).json({error:s?.message||String(s)})}});var LE=async(e,t,{trackId:n,cwd:r,loop:o})=>{let s=n?await ne(n):null;if(!s&&r&&(s=(await re()).find(l=>l.cwd&&(r===l.cwd||r.startsWith(l.cwd+me.sep)))||null),!s)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"}),null;let a=await za();if(a.source!=="plane")return t.status(503).json({error:a.source==="unconfigured"?"this node isn't linked to a plane \u2014 loops live in the plane":`plane unreachable (${a.error||"no detail"})`}),null;let i;try{i=AA(a.loops,o)}catch(c){return t.status(/matches \d+ loops/.test(c.message)?409:404).json({error:c.message}),null}return{track:s,loop:i}};S.post("/api/loops/propose-update",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await LE(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await EA(s.track,s.loop,e.body?.patch)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});S.post("/api/loops/propose-delete",async(e,t)=>{let{trackId:n,cwd:r,loop:o}=e.body||{},s=await LE(e,t,{trackId:n,cwd:r,loop:o});if(s)try{t.json({ok:!0,trackId:s.track.id,proposal:await _A(s.track,s.loop,e.body?.reason)})}catch(a){t.status(400).json({error:a?.message||String(a)})}});var FE=e=>/unknown proposal/i.test(e)?404:/already answered/i.test(e)?409:400;S.post("/api/tracks/:id/loop-update-offer",async(e,t)=>{let{proposalId:n,action:r,nextFireAt: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 RA(s,n,r,{nextFireAt:o??null})})}catch(s){let a=s?.message||String(s);t.status(FE(a)).json({error:a})}});S.post("/api/tracks/:id/loop-delete-offer",async(e,t)=>{let{proposalId:n,action: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 $A(o,n,r)})}catch(o){let s=o?.message||String(o);t.status(FE(s)).json({error: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 vA(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 re()).find(u=>u.cwd&&(s===u.cwd||s.startsWith(u.cwd+me.sep)))||null),!a)return t.status(400).json({error:"trackId (or a cwd inside a Track workspace) required"});let i=(a.boxes||[]).find(l=>l.name===a.leadBox),c=await DS({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 BS(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 jS(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 LS(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=PA(e.query.since||null);t.json({since:n,repos:Qh(),...await DA(n)})}catch(n){let r=n?.message||String(n);t.status(/unparseable/i.test(r)?400:500).json({error:r})}});var UE=async({trackId:e,cwd:t})=>{let n=e?await ne(e):null;return!n&&t&&(n=(await re()).find(o=>o.cwd&&(t===o.cwd||String(t).startsWith(o.cwd+me.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 UE({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 UE({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 jA(s,{day:n})})}catch(s){let a=s?.message||String(s);t.status(/required|invalid/i.test(a)?400:500).json({error:a})}});var r4=120,hw=400,Uu=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>hw)return t.status(400).json({error:`message is ${i.length} chars \u2014 over the ${hw} cap; a notification is a nudge, put the detail in chat`});let c=si(`${n||""}
|
|
618
|
+
${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 re()).find(m=>m.cwd&&(s===m.cwd||s.startsWith(m.cwd+me.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,r4):l.name,body:d(i,hw),urgency:o==="high"?"high":"normal",trackName:l.name,ts:Date.now()};await A(l,{type:"notify.raised",actor:"agent",boxId:l.leadBox??null,provider:u?.agent||null,payload:f}),t.json({ok:!0,notification:f,delivered:Uu?Uu.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 xE(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 Ri(),terms:await fw(),problem:await kE()})});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 bE(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 SE())});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 vE())}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 TE(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 Jl({name:n,cwd:r,repo:o,repos:s,agent:a,skipPermissions:i!==!1}),d=null;if(c)try{await Bs(u,c,{source:"create"})}catch(p){d=p?.message||String(p)}let f=null;if(l)try{await uu(u,await Ns(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:HE(Ms(e.params.id)),subtasks:mo(e.params.id).map(Yi),pendingOffers:await Im(n)})});S.delete("/api/tracks/:id",async(e,t)=>{try{let n=await Wm(e.params.id,{cascadeMocks:e.query.mocks!=="0",cascadeRunbooks:e.query.runbooks==="1"});await Ch(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 po(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 Gm(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 Km(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 Kl(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}
|
|
604
619
|
data: ${JSON.stringify(i)}
|
|
605
620
|
|
|
606
|
-
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await
|
|
621
|
+
`),o=Number(e.query.since??e.headers["last-event-id"])||0;for(let i of await j(n))i.seq>o&&r(i);let s=Uw(n.id,r),a=setInterval(()=>t.write(`: hb
|
|
607
622
|
|
|
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=
|
|
609
|
-
`);S.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await It(e.params.id,
|
|
623
|
+
`),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=Ia(await j(n)),a=jc(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 og(),harvest:eg()})});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 sg(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 o4=["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(`
|
|
624
|
+
`);S.post("/api/tracks/:id/memory/consolidate",async(e,t)=>{try{t.json({ok:!0,...await It(e.params.id,o4)})}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 Xl(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 KS(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=me.basename(String(e.params.file||""));if(!r||!nm(me.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=tm(n.cwd),s,a;try{s=await Ze.promises.realpath(o),a=await Ze.promises.realpath(me.join(o,r))}catch{return t.status(404).json({error:"upload not found"})}if(!a.startsWith(s+me.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 s4=/^[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(!s4.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=GU.createInterface({input:Ze.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?Od(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 zl(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 Om(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 Gl(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 Um({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 $m(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 Dm(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 _u(r,{fresh:o}))});S.post("/api/tracks/:id/model",async(e,t)=>{let{model:n}=e.body||{};try{t.json(await Bm(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 sh(e.params.id,{agent:n,task:r,skipPermissions:o!==!1,reason:s});t.json({ok:!0,subtask:Yi(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:mo(e.params.id).map(Yi)})});S.get("/api/tracks/:id/subtask/:sid/messages",async(e,t)=>{let n=await Rm(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:Yi(nu(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:Yi(ah(e.params.sid))})}catch(n){let r=n?.message||String(n);t.status(/not found/i.test(r)?404:409).json({error:r})}});function Yi(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 Ql(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 Nm(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(HE(Ms(e.params.id))??{running:!1})});S.post("/api/tracks/:id/transfer-out",async(e,t)=>{try{let n=await Qm(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=Zm(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 eu(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 nh({...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 HE(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:bc(),paths:kg,trace:await bg({limit:n})})});var ww=me.resolve($E,"..","..","client","dist");Ze.existsSync(ww)&&(S.use($t.static(ww)),S.get(/^(?!\/api\/).*/,(e,t)=>t.sendFile(me.join(ww,"index.html"),Ji)));var WE=zU.createServer(S),a4=new YU({noServer:!0}),qE=(e,t,n)=>{if(!(Fi(e)||Gs.allowed(Gs.clientIp(e))||bw.allows(e))){t.write(`HTTP/1.1 403 Forbidden\r
|
|
610
625
|
\r
|
|
611
|
-
`),t.destroy();return}if(
|
|
626
|
+
`),t.destroy();return}if(mA(e,t,n))return;if(!fA(e,CE,{enforce:ME})){t.write(`HTTP/1.1 401 Unauthorized\r
|
|
612
627
|
\r
|
|
613
|
-
`),t.destroy();return}let r=
|
|
628
|
+
`),t.destroy();return}let r=Pg(e.url);if(!r.ok){t.write(`HTTP/1.1 ${r.status} ${r.status===400?"Bad Request":"Not Found"}\r
|
|
614
629
|
\r
|
|
615
|
-
`),t.destroy();return}let o=r.sessionName;if(!
|
|
630
|
+
`),t.destroy();return}let o=r.sessionName;if(!Ee(o)){t.write(`HTTP/1.1 400 Bad Request\r
|
|
616
631
|
\r
|
|
617
|
-
`),t.destroy();return}let{cols:s,rows:a}=r;
|
|
618
|
-
`);if(!
|
|
619
|
-
`).slice(0,900)}).catch(()=>{}),e=
|
|
632
|
+
`),t.destroy();return}let{cols:s,rows:a}=r;a4.handleUpgrade(e,t,n,i=>Cg(i,o,{cols:s,rows:a}))};WE.on("upgrade",qE);var KE=process.env.BOSTRAT_CERT_DIR||me.join(process.env.HOME||gw.tmpdir(),".bostrat","certs"),_E=Number(process.env.BOSTRAT_TLS_PORT)||3443,IE=me.join(KE,"bostrat.globopi.key"),RE=me.join(KE,"bostrat.globopi.crt");if(Ze.existsSync(IE)&&Ze.existsSync(RE)){let e=JU.createServer({key:Ze.readFileSync(IE),cert:Ze.readFileSync(RE)},S);e.on("upgrade",qE),e.on("error",t=>{console.error(`[bostrat-agent] https listener failed (${t?.code||t?.message}) \u2014 continuing HTTP-only`)}),e.listen(_E,Lu,()=>{console.log(`[bostrat-agent] https listening on ${Lu||"0.0.0.0"}:${_E}`)})}WE.listen(AE,Lu,()=>{console.log(`[bostrat-agent] listening on ${Lu||"0.0.0.0"}:${AE}`);let e=Mv(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: ${OE}%`),console.log(`[bostrat-agent] ip allowlist: ${Gs.open?"OPEN":process.env.BOSTRAT_ALLOW_IPS||"CLOSED (BOSTRAT_ALLOW_IPS unset)"}`+(bw.active?` \xB7 view front-door: ${process.env.BOSTRAT_VIEW_FRONT_CIDRS} (registered views only)`:"")),Xm().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted handoff(s)")).catch(t=>console.error("[bostrat-agent] handoff reconcile failed:",t?.message||t)),rh().then(t=>t&&console.log("[bostrat-agent] reconciled interrupted subtask(s)")).catch(t=>console.error("[bostrat-agent] subtask reconcile failed:",t?.message||t)),Vm().then(t=>t&&console.log(`[bostrat-agent] healed ${t} half-demoted transferred track(s)`)).catch(t=>console.error("[bostrat-agent] demoted-track reconcile failed:",t?.message||t)).then(()=>zm()).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)),Fm().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)),m4(),l4(),u4(),p4(),$k({listTracks:re,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)),YS()&&XS({listTracks:re}),KA(),YA(),TT({resolveTrack:ne}),NT({listTracks:re,createTrack:Jl,archiveTrack:po,listRepos:fn,resolveTrack:ne,sayToLead:It,answerPrompt:Gl,startLeadHandoff:Ql,interruptLead:zl,listSubtaskJobs:mo,cancelSubtask:nu,readEvents:j,appendEvent:A,divertShip:sw,answerPrExists:aw,knownAgents:yi}),i4(),fa().then(t=>{t||console.warn(`[boot] no host tmux server on ${ft} and auto-start failed \u2014 boxes cannot spawn until one runs`)}),uE(),process.env.BOSTRAT_PLANE_URL&&process.env.BOSTRAT_PLANE_TOKEN&&(Uu=jv({url:process.env.BOSTRAT_PLANE_URL,token:process.env.BOSTRAT_PLANE_TOKEN,nodeId:process.env.BOSTRAT_NODE_ID||gw.hostname(),name:process.env.BOSTRAT_NODE_NAME||gw.hostname(),listTracks:re,detectAgents:ql,sampleMetrics:BT,onTracksTransferred:th,onUpdateAvailable:async t=>{try{yw={latest:t.latest,current:t.current},await ln({...t,policy:ot()}),ot()==="auto"&&await NE({to:t.latest,from:t.current})}catch(n){console.error(`[plane-link] update notice failed: ${n?.message||n}`)}},onUpdatePolicy:t=>e4(t).catch(()=>{}),onHelloRefused:async({current:t,min:n,latest:r,installKind:o,fix:s})=>{try{let a=r||n;if(await ln({current:t,latest:a,min:n,installKind:o,required:!0,policy:ot(),status:"available",reason:"refused"}),ot()!=="off"&&a){let i=await lu({to:a,kind:Rt,reason:"hard-floor"});i.ok?(Vi.cancel(),await ln({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}`),Co().catch(()=>{}),HS().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} runbooks to the plane`),()=>{}),AT().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} ticket links to the plane`),()=>{}),fS().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} mocks to the plane`),()=>{}),RS().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} flows to the plane`),()=>{}),g0().then(t=>t.pushed&&console.log(`[content-plane] seeded ${t.pushed} PR links to the plane`),()=>{}))});process.on("SIGTERM",()=>{try{Uu?.stop()}catch{}setTimeout(()=>process.exit(0),150)});function i4(){c4(),$l().catch(()=>{}),setInterval(()=>$l().catch(()=>{}),1e4)}function c4(){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=me.resolve($E,"..","bin",e),n=me.join(process.env.HOME||"",".local","bin");if(!process.env.HOME||!Ze.existsSync(t))continue;Ze.mkdirSync(n,{recursive:!0});let r=me.join(n,e);(Ze.existsSync(r)?Ze.readlinkSync(r):null)!==t&&(Ze.rmSync(r,{force:!0}),Ze.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 l4(){let e=!1,t=async()=>{if(!e){e=!0;try{let n=await re(),r=await XT({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 u4(){let e=null,t=async()=>{try{let n=Vo(),r=0,o=[],s=null;try{let l=await re(),u=await qh(l);r=u.fleet.unpricedTurns||0,o=u.fleet.unpricedModels||[],s=await UT(l,{days:7})}catch{}let a=[...n.problems];s&&!s.ok&&a.push(`${s.divergent} recent row(s) diverge from the table (implied-rate audit) \u2014 run \`npm run cost -- --audit\``);let i=o.filter(l=>l!=="unknown");i.length&&a.push(`${r} unpriced turn(s) \u2014 add rates for: ${i.join(", ")}`);let c=a.join(`
|
|
633
|
+
`);if(!a.length){e!==""&&console.log(`[pricing] table ok \u2014 rates verified ${n.tableVerifiedAt??"never"}`),e="";return}for(let l of a)console.warn(`[pricing] \u26A0 ${l}`);c!==e&&process.env.BOSTRAT_NTFY_URL&&fetch(process.env.BOSTRAT_NTFY_URL,{method:"POST",headers:{Title:"bostrat: cost instrument needs attention",Tags:"warning"},body:a.join(`
|
|
634
|
+
`).slice(0,900)}).catch(()=>{}),e=c}catch(n){console.warn(`[pricing] watch failed: ${n?.message||n}`)}};setInterval(t,1440*60*1e3),setTimeout(t,3e4)}var d4=Number(process.env.BOSTRAT_MEMORY_RECONCILE_MS)||5*6e4,f4=Number(process.env.BOSTRAT_MEMORY_RECONCILE_FIRST_MS)||5*6e4;function p4(){let e=async()=>{try{let n=await rg();n.ran&&console.log(`[memory] one-time full backfill sweep: ${n.harvested} fact(s) harvested`);let o=(await re()).filter(a=>!Be(a)&&a.kind!=="setup").flatMap(a=>[a.cwd,...(a.boxes||[]).map(i=>i.cwd)]).filter(Boolean),s=await ng([...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,d4).unref()},f4).unref()}function m4(){let e=!1;setInterval(async()=>{if(!e){e=!0;try{let t=await re();for(let n of t)Be(n)||!n.boxes?.some(r=>r.state==="live")||(await Kl(n.id).catch(()=>{}),await qx(n).catch(()=>{}))}catch{}finally{e=!1}}},4e3)}
|